Merge branch 'master' into fix/remove-formula-column-subtype-check
This commit is contained in:
commit
2494166cbc
|
@ -71,8 +71,8 @@ const handleMouseDown = e => {
|
||||||
|
|
||||||
// Clear any previous listeners in case of multiple down events, and register
|
// Clear any previous listeners in case of multiple down events, and register
|
||||||
// a single mouse up listener
|
// a single mouse up listener
|
||||||
document.removeEventListener("mouseup", handleMouseUp)
|
document.removeEventListener("click", handleMouseUp)
|
||||||
document.addEventListener("mouseup", handleMouseUp, true)
|
document.addEventListener("click", handleMouseUp, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Global singleton listeners for our events
|
// Global singleton listeners for our events
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
export let customPopoverHeight
|
export let customPopoverHeight
|
||||||
export let open = false
|
export let open = false
|
||||||
export let loading
|
export let loading
|
||||||
|
export let onOptionMouseenter = () => {}
|
||||||
|
export let onOptionMouseleave = () => {}
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
@ -97,4 +99,6 @@
|
||||||
{autoWidth}
|
{autoWidth}
|
||||||
{customPopoverHeight}
|
{customPopoverHeight}
|
||||||
{loading}
|
{loading}
|
||||||
|
{onOptionMouseenter}
|
||||||
|
{onOptionMouseleave}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -41,6 +41,8 @@
|
||||||
export let footer = null
|
export let footer = null
|
||||||
export let customAnchor = null
|
export let customAnchor = null
|
||||||
export let loading
|
export let loading
|
||||||
|
export let onOptionMouseenter = () => {}
|
||||||
|
export let onOptionMouseleave = () => {}
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
@ -199,6 +201,8 @@
|
||||||
aria-selected="true"
|
aria-selected="true"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
on:click={() => onSelectOption(getOptionValue(option, idx))}
|
on:click={() => onSelectOption(getOptionValue(option, idx))}
|
||||||
|
on:mouseenter={e => onOptionMouseenter(e, option)}
|
||||||
|
on:mouseleave={e => onOptionMouseleave(e, option)}
|
||||||
class:is-disabled={!isOptionEnabled(option)}
|
class:is-disabled={!isOptionEnabled(option)}
|
||||||
>
|
>
|
||||||
{#if getOptionIcon(option, idx)}
|
{#if getOptionIcon(option, idx)}
|
||||||
|
|
|
@ -26,6 +26,8 @@
|
||||||
export let tag = null
|
export let tag = null
|
||||||
export let searchTerm = null
|
export let searchTerm = null
|
||||||
export let loading
|
export let loading
|
||||||
|
export let onOptionMouseenter = () => {}
|
||||||
|
export let onOptionMouseleave = () => {}
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
@ -95,6 +97,8 @@
|
||||||
{autocomplete}
|
{autocomplete}
|
||||||
{sort}
|
{sort}
|
||||||
{tag}
|
{tag}
|
||||||
|
{onOptionMouseenter}
|
||||||
|
{onOptionMouseleave}
|
||||||
isPlaceholder={value == null || value === ""}
|
isPlaceholder={value == null || value === ""}
|
||||||
placeholderOption={placeholder === false ? null : placeholder}
|
placeholderOption={placeholder === false ? null : placeholder}
|
||||||
isOptionSelected={option => compareOptionAndValue(option, value)}
|
isOptionSelected={option => compareOptionAndValue(option, value)}
|
||||||
|
|
|
@ -0,0 +1,267 @@
|
||||||
|
<script>
|
||||||
|
import { onMount, createEventDispatcher } from "svelte"
|
||||||
|
import Atrament from "atrament"
|
||||||
|
import Icon from "../../Icon/Icon.svelte"
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
let last
|
||||||
|
|
||||||
|
export let value
|
||||||
|
export let disabled = false
|
||||||
|
export let editable = true
|
||||||
|
export let width = 400
|
||||||
|
export let height = 220
|
||||||
|
export let saveIcon = false
|
||||||
|
export let darkMode
|
||||||
|
|
||||||
|
export function toDataUrl() {
|
||||||
|
// PNG to preserve transparency
|
||||||
|
return canvasRef.toDataURL("image/png")
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toFile() {
|
||||||
|
const data = canvasContext
|
||||||
|
.getImageData(0, 0, width, height)
|
||||||
|
.data.some(channel => channel !== 0)
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let dataURIParts = toDataUrl().split(",")
|
||||||
|
if (!dataURIParts.length) {
|
||||||
|
console.error("Could not retrieve signature data")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pull out the base64 encoded byte data
|
||||||
|
let binaryVal = atob(dataURIParts[1])
|
||||||
|
let blobArray = new Uint8Array(binaryVal.length)
|
||||||
|
let pos = 0
|
||||||
|
while (pos < binaryVal.length) {
|
||||||
|
blobArray[pos] = binaryVal.charCodeAt(pos)
|
||||||
|
pos++
|
||||||
|
}
|
||||||
|
|
||||||
|
const signatureBlob = new Blob([blobArray], {
|
||||||
|
type: "image/png",
|
||||||
|
})
|
||||||
|
|
||||||
|
return new File([signatureBlob], "signature.png", {
|
||||||
|
type: signatureBlob.type,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clearCanvas() {
|
||||||
|
return canvasContext.clearRect(0, 0, canvasWidth, canvasHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
let canvasRef
|
||||||
|
let canvasContext
|
||||||
|
let canvasWrap
|
||||||
|
let canvasWidth
|
||||||
|
let canvasHeight
|
||||||
|
let signature
|
||||||
|
|
||||||
|
let updated = false
|
||||||
|
let signatureFile
|
||||||
|
let urlFailed
|
||||||
|
|
||||||
|
$: if (value) {
|
||||||
|
signatureFile = value
|
||||||
|
}
|
||||||
|
|
||||||
|
$: if (signatureFile?.url) {
|
||||||
|
updated = false
|
||||||
|
}
|
||||||
|
|
||||||
|
$: if (last) {
|
||||||
|
dispatch("update")
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
if (!editable) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const getPos = e => {
|
||||||
|
var rect = canvasRef.getBoundingClientRect()
|
||||||
|
const canvasX = e.offsetX || e.targetTouches?.[0].pageX - rect.left
|
||||||
|
const canvasY = e.offsetY || e.targetTouches?.[0].pageY - rect.top
|
||||||
|
|
||||||
|
return { x: canvasX, y: canvasY }
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkUp = e => {
|
||||||
|
last = getPos(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
canvasRef.addEventListener("pointerdown", e => {
|
||||||
|
const current = getPos(e)
|
||||||
|
//If the cursor didn't move at all, block the default pointerdown
|
||||||
|
if (last?.x === current?.x && last?.y === current?.y) {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopImmediatePropagation()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
document.addEventListener("pointerup", checkUp)
|
||||||
|
|
||||||
|
signature = new Atrament(canvasRef, {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
color: "white",
|
||||||
|
})
|
||||||
|
|
||||||
|
signature.weight = 4
|
||||||
|
signature.smoothing = 2
|
||||||
|
|
||||||
|
canvasWrap.style.width = `${width}px`
|
||||||
|
canvasWrap.style.height = `${height}px`
|
||||||
|
|
||||||
|
const { width: wrapWidth, height: wrapHeight } =
|
||||||
|
canvasWrap.getBoundingClientRect()
|
||||||
|
|
||||||
|
canvasHeight = wrapHeight
|
||||||
|
canvasWidth = wrapWidth
|
||||||
|
|
||||||
|
canvasContext = canvasRef.getContext("2d")
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
signature.destroy()
|
||||||
|
document.removeEventListener("pointerup", checkUp)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="signature" class:light={!darkMode} class:image-error={urlFailed}>
|
||||||
|
{#if !disabled}
|
||||||
|
<div class="overlay">
|
||||||
|
{#if updated && saveIcon}
|
||||||
|
<span class="save">
|
||||||
|
<Icon
|
||||||
|
name="Checkmark"
|
||||||
|
hoverable
|
||||||
|
tooltip={"Save"}
|
||||||
|
tooltipPosition={"top"}
|
||||||
|
tooltipType={"info"}
|
||||||
|
on:click={() => {
|
||||||
|
dispatch("change", toDataUrl())
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
{#if signatureFile?.url && !updated}
|
||||||
|
<span class="delete">
|
||||||
|
<Icon
|
||||||
|
name="DeleteOutline"
|
||||||
|
hoverable
|
||||||
|
tooltip={"Delete"}
|
||||||
|
tooltipPosition={"top"}
|
||||||
|
tooltipType={"info"}
|
||||||
|
on:click={() => {
|
||||||
|
if (editable) {
|
||||||
|
clearCanvas()
|
||||||
|
}
|
||||||
|
dispatch("clear")
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if !editable && signatureFile?.url}
|
||||||
|
<!-- svelte-ignore a11y-missing-attribute -->
|
||||||
|
{#if !urlFailed}
|
||||||
|
<img
|
||||||
|
src={signatureFile?.url}
|
||||||
|
on:error={() => {
|
||||||
|
urlFailed = true
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{:else}
|
||||||
|
Could not load signature
|
||||||
|
{/if}
|
||||||
|
{:else}
|
||||||
|
<div bind:this={canvasWrap} class="canvas-wrap">
|
||||||
|
<canvas
|
||||||
|
id="signature-canvas"
|
||||||
|
bind:this={canvasRef}
|
||||||
|
style="--max-sig-width: {width}px; --max-sig-height: {height}px"
|
||||||
|
/>
|
||||||
|
{#if editable}
|
||||||
|
<div class="indicator-overlay">
|
||||||
|
<div class="sign-here">
|
||||||
|
<Icon name="Close" />
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.indicator-overlay {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
top: 0px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: end;
|
||||||
|
padding: var(--spectrum-global-dimension-size-150);
|
||||||
|
box-sizing: border-box;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.sign-here {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: var(--spectrum-global-dimension-size-150);
|
||||||
|
}
|
||||||
|
.sign-here hr {
|
||||||
|
border: 0;
|
||||||
|
border-top: 2px solid var(--spectrum-global-color-gray-200);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.canvas-wrap {
|
||||||
|
position: relative;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
.signature img {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
#signature-canvas {
|
||||||
|
max-width: var(--max-sig-width);
|
||||||
|
max-height: var(--max-sig-height);
|
||||||
|
}
|
||||||
|
.signature.light img,
|
||||||
|
.signature.light #signature-canvas {
|
||||||
|
-webkit-filter: invert(100%);
|
||||||
|
filter: invert(100%);
|
||||||
|
}
|
||||||
|
.signature.image-error .overlay {
|
||||||
|
padding-top: 0px;
|
||||||
|
}
|
||||||
|
.signature {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.overlay {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
pointer-events: none;
|
||||||
|
padding: var(--spectrum-global-dimension-size-150);
|
||||||
|
text-align: right;
|
||||||
|
z-index: 2;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.save,
|
||||||
|
.delete {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -16,3 +16,4 @@ export { default as CoreStepper } from "./Stepper.svelte"
|
||||||
export { default as CoreRichTextField } from "./RichTextField.svelte"
|
export { default as CoreRichTextField } from "./RichTextField.svelte"
|
||||||
export { default as CoreSlider } from "./Slider.svelte"
|
export { default as CoreSlider } from "./Slider.svelte"
|
||||||
export { default as CoreFile } from "./File.svelte"
|
export { default as CoreFile } from "./File.svelte"
|
||||||
|
export { default as CoreSignature } from "./Signature.svelte"
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
export let searchTerm = null
|
export let searchTerm = null
|
||||||
export let customPopoverHeight
|
export let customPopoverHeight
|
||||||
export let helpText = null
|
export let helpText = null
|
||||||
|
export let onOptionMouseenter = () => {}
|
||||||
|
export let onOptionMouseleave = () => {}
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const onChange = e => {
|
const onChange = e => {
|
||||||
|
@ -41,6 +43,8 @@
|
||||||
{autoWidth}
|
{autoWidth}
|
||||||
{autocomplete}
|
{autocomplete}
|
||||||
{customPopoverHeight}
|
{customPopoverHeight}
|
||||||
|
{onOptionMouseenter}
|
||||||
|
{onOptionMouseleave}
|
||||||
bind:searchTerm
|
bind:searchTerm
|
||||||
on:change={onChange}
|
on:change={onChange}
|
||||||
on:click
|
on:click
|
||||||
|
|
|
@ -29,6 +29,9 @@
|
||||||
export let tag = null
|
export let tag = null
|
||||||
export let helpText = null
|
export let helpText = null
|
||||||
export let compare
|
export let compare
|
||||||
|
export let onOptionMouseenter = () => {}
|
||||||
|
export let onOptionMouseleave = () => {}
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const onChange = e => {
|
const onChange = e => {
|
||||||
value = e.detail
|
value = e.detail
|
||||||
|
@ -67,6 +70,8 @@
|
||||||
{customPopoverHeight}
|
{customPopoverHeight}
|
||||||
{tag}
|
{tag}
|
||||||
{compare}
|
{compare}
|
||||||
|
{onOptionMouseenter}
|
||||||
|
{onOptionMouseleave}
|
||||||
on:change={onChange}
|
on:change={onChange}
|
||||||
on:click
|
on:click
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -173,6 +173,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.spectrum-Modal {
|
.spectrum-Modal {
|
||||||
|
border: 2px solid var(--spectrum-global-color-gray-200);
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
max-height: none;
|
max-height: none;
|
||||||
margin: 40px 0;
|
margin: 40px 0;
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
export let secondaryButtonText = undefined
|
export let secondaryButtonText = undefined
|
||||||
export let secondaryAction = undefined
|
export let secondaryAction = undefined
|
||||||
export let secondaryButtonWarning = false
|
export let secondaryButtonWarning = false
|
||||||
|
export let custom = false
|
||||||
|
|
||||||
const { hide, cancel } = getContext(Context.Modal)
|
const { hide, cancel } = getContext(Context.Modal)
|
||||||
let loading = false
|
let loading = false
|
||||||
|
@ -63,12 +64,13 @@
|
||||||
class:spectrum-Dialog--medium={size === "M"}
|
class:spectrum-Dialog--medium={size === "M"}
|
||||||
class:spectrum-Dialog--large={size === "L"}
|
class:spectrum-Dialog--large={size === "L"}
|
||||||
class:spectrum-Dialog--extraLarge={size === "XL"}
|
class:spectrum-Dialog--extraLarge={size === "XL"}
|
||||||
|
class:no-grid={custom}
|
||||||
style="position: relative;"
|
style="position: relative;"
|
||||||
role="dialog"
|
role="dialog"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
aria-modal="true"
|
aria-modal="true"
|
||||||
>
|
>
|
||||||
<div class="spectrum-Dialog-grid">
|
<div class="modal-core" class:spectrum-Dialog-grid={!custom}>
|
||||||
{#if title || $$slots.header}
|
{#if title || $$slots.header}
|
||||||
<h1
|
<h1
|
||||||
class="spectrum-Dialog-heading spectrum-Dialog-heading--noHeader"
|
class="spectrum-Dialog-heading spectrum-Dialog-heading--noHeader"
|
||||||
|
@ -153,6 +155,25 @@
|
||||||
.spectrum-Dialog-content {
|
.spectrum-Dialog-content {
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.no-grid .spectrum-Dialog-content {
|
||||||
|
border-top: 2px solid var(--spectrum-global-color-gray-200);
|
||||||
|
border-bottom: 2px solid var(--spectrum-global-color-gray-200);
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-grid .spectrum-Dialog-heading {
|
||||||
|
margin-top: 12px;
|
||||||
|
margin-left: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spectrum-Dialog.no-grid {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spectrum-Dialog.no-grid .spectrum-Dialog-buttonGroup {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.spectrum-Dialog-heading {
|
.spectrum-Dialog-heading {
|
||||||
font-family: var(--font-accent);
|
font-family: var(--font-accent);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|
|
@ -1,40 +1,28 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext, onMount, createEventDispatcher } from "svelte"
|
import { getContext, onDestroy } from "svelte"
|
||||||
import Portal from "svelte-portal"
|
import Portal from "svelte-portal"
|
||||||
|
|
||||||
export let title
|
export let title
|
||||||
export let icon = ""
|
export let icon = ""
|
||||||
export let id
|
export let id
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
|
||||||
let selected = getContext("tab")
|
let selected = getContext("tab")
|
||||||
let tab_internal
|
let observer
|
||||||
let tabInfo
|
let ref
|
||||||
|
|
||||||
const setTabInfo = () => {
|
$: isSelected = $selected.title === title
|
||||||
// If the tabs are being rendered inside a component which uses
|
$: {
|
||||||
// a svelte transition to enter, then this initial getBoundingClientRect
|
if (isSelected && ref) {
|
||||||
// will return an incorrect position.
|
observe()
|
||||||
// We just need to get this off the main thread to fix this, by using
|
} else {
|
||||||
// a 0ms timeout.
|
stopObserving()
|
||||||
setTimeout(() => {
|
}
|
||||||
tabInfo = tab_internal?.getBoundingClientRect()
|
|
||||||
if (tabInfo && $selected.title === title) {
|
|
||||||
$selected.info = tabInfo
|
|
||||||
}
|
|
||||||
}, 0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
const setTabInfo = () => {
|
||||||
setTabInfo()
|
const tabInfo = ref?.getBoundingClientRect()
|
||||||
})
|
if (tabInfo) {
|
||||||
|
$selected.info = tabInfo
|
||||||
//Ensure that the underline is in the correct location
|
|
||||||
$: {
|
|
||||||
if ($selected.title === title && tab_internal) {
|
|
||||||
if ($selected.info?.left !== tab_internal.getBoundingClientRect().left) {
|
|
||||||
setTabInfo()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,10 +30,25 @@
|
||||||
$selected = {
|
$selected = {
|
||||||
...$selected,
|
...$selected,
|
||||||
title,
|
title,
|
||||||
info: tab_internal.getBoundingClientRect(),
|
info: ref.getBoundingClientRect(),
|
||||||
}
|
}
|
||||||
dispatch("click")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const observe = () => {
|
||||||
|
if (!observer) {
|
||||||
|
observer = new ResizeObserver(setTabInfo)
|
||||||
|
observer.observe(ref)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const stopObserving = () => {
|
||||||
|
if (observer) {
|
||||||
|
observer.unobserve(ref)
|
||||||
|
observer = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onDestroy(stopObserving)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
@ -53,11 +56,12 @@
|
||||||
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||||
<div
|
<div
|
||||||
{id}
|
{id}
|
||||||
bind:this={tab_internal}
|
bind:this={ref}
|
||||||
on:click={onClick}
|
on:click={onClick}
|
||||||
class:is-selected={$selected.title === title}
|
on:click
|
||||||
class="spectrum-Tabs-item"
|
class="spectrum-Tabs-item"
|
||||||
class:emphasized={$selected.title === title && $selected.emphasized}
|
class:is-selected={isSelected}
|
||||||
|
class:emphasized={isSelected && $selected.emphasized}
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
{#if icon}
|
{#if icon}
|
||||||
|
@ -72,7 +76,8 @@
|
||||||
{/if}
|
{/if}
|
||||||
<span class="spectrum-Tabs-itemLabel">{title}</span>
|
<span class="spectrum-Tabs-itemLabel">{title}</span>
|
||||||
</div>
|
</div>
|
||||||
{#if $selected.title === title}
|
|
||||||
|
{#if isSelected}
|
||||||
<Portal target=".spectrum-Tabs-content-{$selected.id}">
|
<Portal target=".spectrum-Tabs-content-{$selected.id}">
|
||||||
<slot />
|
<slot />
|
||||||
</Portal>
|
</Portal>
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
<script>
|
||||||
|
import Portal from "svelte-portal"
|
||||||
|
import { getContext } from "svelte"
|
||||||
|
import Context from "../context"
|
||||||
|
|
||||||
|
export let anchor
|
||||||
|
export let visible = false
|
||||||
|
export let offset = 0
|
||||||
|
|
||||||
|
$: target = getContext(Context.PopoverRoot) || "#app"
|
||||||
|
|
||||||
|
let hovering = false
|
||||||
|
let tooltip
|
||||||
|
let x = 0
|
||||||
|
let y = 0
|
||||||
|
|
||||||
|
const updatePosition = (anchor, tooltip) => {
|
||||||
|
if (anchor == null || tooltip == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
const rect = anchor.getBoundingClientRect()
|
||||||
|
const windowOffset =
|
||||||
|
window.innerHeight - offset - (tooltip.clientHeight + rect.y)
|
||||||
|
const tooltipWidth = tooltip.clientWidth
|
||||||
|
|
||||||
|
x = rect.x - tooltipWidth - offset
|
||||||
|
y = windowOffset < 0 ? rect.y + windowOffset : rect.y
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
$: updatePosition(anchor, tooltip)
|
||||||
|
|
||||||
|
const handleMouseenter = () => {
|
||||||
|
hovering = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleMouseleave = () => {
|
||||||
|
hovering = false
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Portal {target}>
|
||||||
|
<div
|
||||||
|
role="tooltip"
|
||||||
|
on:mouseenter={handleMouseenter}
|
||||||
|
on:mouseleave={handleMouseleave}
|
||||||
|
style:left={`${x}px`}
|
||||||
|
style:top={`${y}px`}
|
||||||
|
class="wrapper"
|
||||||
|
class:visible={visible || hovering}
|
||||||
|
>
|
||||||
|
<div bind:this={tooltip} class="tooltip">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Portal>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.wrapper {
|
||||||
|
background-color: var(--spectrum-global-color-gray-100);
|
||||||
|
box-shadow: 2px 2px 5px 0px rgba(0, 0, 0, 0.42);
|
||||||
|
opacity: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
border-radius: 5px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 1px solid var(--grey-4);
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.visible {
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -53,6 +53,7 @@ export { default as Link } from "./Link/Link.svelte"
|
||||||
export { default as Tooltip } from "./Tooltip/Tooltip.svelte"
|
export { default as Tooltip } from "./Tooltip/Tooltip.svelte"
|
||||||
export { default as TempTooltip } from "./Tooltip/TempTooltip.svelte"
|
export { default as TempTooltip } from "./Tooltip/TempTooltip.svelte"
|
||||||
export { default as TooltipWrapper } from "./Tooltip/TooltipWrapper.svelte"
|
export { default as TooltipWrapper } from "./Tooltip/TooltipWrapper.svelte"
|
||||||
|
export { default as ContextTooltip } from "./Tooltip/Context.svelte"
|
||||||
export { default as Menu } from "./Menu/Menu.svelte"
|
export { default as Menu } from "./Menu/Menu.svelte"
|
||||||
export { default as MenuSection } from "./Menu/Section.svelte"
|
export { default as MenuSection } from "./Menu/Section.svelte"
|
||||||
export { default as MenuSeparator } from "./Menu/Separator.svelte"
|
export { default as MenuSeparator } from "./Menu/Separator.svelte"
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
import TestDataModal from "./TestDataModal.svelte"
|
import TestDataModal from "./TestDataModal.svelte"
|
||||||
import { flip } from "svelte/animate"
|
import { flip } from "svelte/animate"
|
||||||
import { fly } from "svelte/transition"
|
import { fly } from "svelte/transition"
|
||||||
import { Icon, notifications, Modal } from "@budibase/bbui"
|
import { Icon, notifications, Modal, Toggle } from "@budibase/bbui"
|
||||||
import { ActionStepID } from "constants/backend/automations"
|
import { ActionStepID } from "constants/backend/automations"
|
||||||
import UndoRedoControl from "components/common/UndoRedoControl.svelte"
|
import UndoRedoControl from "components/common/UndoRedoControl.svelte"
|
||||||
|
|
||||||
|
@ -73,6 +73,16 @@
|
||||||
Test details
|
Test details
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="setting-spacing">
|
||||||
|
<Toggle
|
||||||
|
text={automation.disabled ? "Paused" : "Activated"}
|
||||||
|
on:change={automationStore.actions.toggleDisabled(
|
||||||
|
automation._id,
|
||||||
|
automation.disabled
|
||||||
|
)}
|
||||||
|
value={!automation.disabled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="canvas" on:scroll={handleScroll}>
|
<div class="canvas" on:scroll={handleScroll}>
|
||||||
|
|
|
@ -61,6 +61,7 @@
|
||||||
selected={automation._id === selectedAutomationId}
|
selected={automation._id === selectedAutomationId}
|
||||||
on:click={() => selectAutomation(automation._id)}
|
on:click={() => selectAutomation(automation._id)}
|
||||||
selectedBy={$userSelectedResourceMap[automation._id]}
|
selectedBy={$userSelectedResourceMap[automation._id]}
|
||||||
|
disabled={automation.disabled}
|
||||||
>
|
>
|
||||||
<EditAutomationPopover {automation} />
|
<EditAutomationPopover {automation} />
|
||||||
</NavItem>
|
</NavItem>
|
||||||
|
|
|
@ -39,6 +39,15 @@
|
||||||
>Duplicate</MenuItem
|
>Duplicate</MenuItem
|
||||||
>
|
>
|
||||||
<MenuItem icon="Edit" on:click={updateAutomationDialog.show}>Edit</MenuItem>
|
<MenuItem icon="Edit" on:click={updateAutomationDialog.show}>Edit</MenuItem>
|
||||||
|
<MenuItem
|
||||||
|
icon={automation.disabled ? "CheckmarkCircle" : "Cancel"}
|
||||||
|
on:click={automationStore.actions.toggleDisabled(
|
||||||
|
automation._id,
|
||||||
|
automation.disabled
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{automation.disabled ? "Activate" : "Pause"}
|
||||||
|
</MenuItem>
|
||||||
<MenuItem icon="Delete" on:click={confirmDeleteDialog.show}>Delete</MenuItem>
|
<MenuItem icon="Delete" on:click={confirmDeleteDialog.show}>Delete</MenuItem>
|
||||||
</ActionMenu>
|
</ActionMenu>
|
||||||
|
|
||||||
|
|
|
@ -364,6 +364,7 @@
|
||||||
value.customType !== "cron" &&
|
value.customType !== "cron" &&
|
||||||
value.customType !== "triggerSchema" &&
|
value.customType !== "triggerSchema" &&
|
||||||
value.customType !== "automationFields" &&
|
value.customType !== "automationFields" &&
|
||||||
|
value.type !== "signature_single" &&
|
||||||
value.type !== "attachment" &&
|
value.type !== "attachment" &&
|
||||||
value.type !== "attachment_single"
|
value.type !== "attachment_single"
|
||||||
)
|
)
|
||||||
|
@ -456,7 +457,7 @@
|
||||||
value={inputData[key]}
|
value={inputData[key]}
|
||||||
options={Object.keys(table?.schema || {})}
|
options={Object.keys(table?.schema || {})}
|
||||||
/>
|
/>
|
||||||
{:else if value.type === "attachment"}
|
{:else if value.type === "attachment" || value.type === "signature_single"}
|
||||||
<div class="attachment-field-wrapper">
|
<div class="attachment-field-wrapper">
|
||||||
<div class="label-wrapper">
|
<div class="label-wrapper">
|
||||||
<Label>{label}</Label>
|
<Label>{label}</Label>
|
||||||
|
|
|
@ -24,6 +24,11 @@
|
||||||
|
|
||||||
let table
|
let table
|
||||||
let schemaFields
|
let schemaFields
|
||||||
|
let attachmentTypes = [
|
||||||
|
FieldType.ATTACHMENTS,
|
||||||
|
FieldType.ATTACHMENT_SINGLE,
|
||||||
|
FieldType.SIGNATURE_SINGLE,
|
||||||
|
]
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
table = $tables.list.find(table => table._id === value?.tableId)
|
table = $tables.list.find(table => table._id === value?.tableId)
|
||||||
|
@ -120,15 +125,9 @@
|
||||||
{#if schemaFields.length}
|
{#if schemaFields.length}
|
||||||
{#each schemaFields as [field, schema]}
|
{#each schemaFields as [field, schema]}
|
||||||
{#if !schema.autocolumn}
|
{#if !schema.autocolumn}
|
||||||
<div
|
<div class:schema-fields={!attachmentTypes.includes(schema.type)}>
|
||||||
class:schema-fields={schema.type !== FieldType.ATTACHMENTS &&
|
|
||||||
schema.type !== FieldType.ATTACHMENT_SINGLE}
|
|
||||||
>
|
|
||||||
<Label>{field}</Label>
|
<Label>{field}</Label>
|
||||||
<div
|
<div class:field-width={!attachmentTypes.includes(schema.type)}>
|
||||||
class:field-width={schema.type !== FieldType.ATTACHMENTS &&
|
|
||||||
schema.type !== FieldType.ATTACHMENT_SINGLE}
|
|
||||||
>
|
|
||||||
{#if isTestModal}
|
{#if isTestModal}
|
||||||
<RowSelectorTypes
|
<RowSelectorTypes
|
||||||
{isTestModal}
|
{isTestModal}
|
||||||
|
|
|
@ -21,6 +21,12 @@
|
||||||
return clone
|
return clone
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let attachmentTypes = [
|
||||||
|
FieldType.ATTACHMENTS,
|
||||||
|
FieldType.ATTACHMENT_SINGLE,
|
||||||
|
FieldType.SIGNATURE_SINGLE,
|
||||||
|
]
|
||||||
|
|
||||||
function schemaHasOptions(schema) {
|
function schemaHasOptions(schema) {
|
||||||
return !!schema.constraints?.inclusion?.length
|
return !!schema.constraints?.inclusion?.length
|
||||||
}
|
}
|
||||||
|
@ -29,7 +35,8 @@
|
||||||
let params = {}
|
let params = {}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
schema.type === FieldType.ATTACHMENT_SINGLE &&
|
(schema.type === FieldType.ATTACHMENT_SINGLE ||
|
||||||
|
schema.type === FieldType.SIGNATURE_SINGLE) &&
|
||||||
Object.keys(keyValueObj).length === 0
|
Object.keys(keyValueObj).length === 0
|
||||||
) {
|
) {
|
||||||
return []
|
return []
|
||||||
|
@ -100,16 +107,20 @@
|
||||||
on:change={e => onChange(e, field)}
|
on:change={e => onChange(e, field)}
|
||||||
useLabel={false}
|
useLabel={false}
|
||||||
/>
|
/>
|
||||||
{:else if schema.type === FieldType.ATTACHMENTS || schema.type === FieldType.ATTACHMENT_SINGLE}
|
{:else if attachmentTypes.includes(schema.type)}
|
||||||
<div class="attachment-field-spacinng">
|
<div class="attachment-field-spacinng">
|
||||||
<KeyValueBuilder
|
<KeyValueBuilder
|
||||||
on:change={e =>
|
on:change={e =>
|
||||||
onChange(
|
onChange(
|
||||||
{
|
{
|
||||||
detail:
|
detail:
|
||||||
schema.type === FieldType.ATTACHMENT_SINGLE
|
schema.type === FieldType.ATTACHMENT_SINGLE ||
|
||||||
|
schema.type === FieldType.SIGNATURE_SINGLE
|
||||||
? e.detail.length > 0
|
? e.detail.length > 0
|
||||||
? { url: e.detail[0].name, filename: e.detail[0].value }
|
? {
|
||||||
|
url: e.detail[0].name,
|
||||||
|
filename: e.detail[0].value,
|
||||||
|
}
|
||||||
: {}
|
: {}
|
||||||
: e.detail.map(({ name, value }) => ({
|
: e.detail.map(({ name, value }) => ({
|
||||||
url: name,
|
url: name,
|
||||||
|
@ -125,7 +136,8 @@
|
||||||
customButtonText={"Add attachment"}
|
customButtonText={"Add attachment"}
|
||||||
keyPlaceholder={"URL"}
|
keyPlaceholder={"URL"}
|
||||||
valuePlaceholder={"Filename"}
|
valuePlaceholder={"Filename"}
|
||||||
actionButtonDisabled={schema.type === FieldType.ATTACHMENT_SINGLE &&
|
actionButtonDisabled={(schema.type === FieldType.ATTACHMENT_SINGLE ||
|
||||||
|
schema.type === FieldType.SIGNATURE) &&
|
||||||
Object.keys(value[field]).length >= 1}
|
Object.keys(value[field]).length >= 1}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
|
import { API } from "api"
|
||||||
import {
|
import {
|
||||||
Input,
|
Input,
|
||||||
Select,
|
Select,
|
||||||
|
@ -8,11 +9,16 @@
|
||||||
Label,
|
Label,
|
||||||
RichTextField,
|
RichTextField,
|
||||||
TextArea,
|
TextArea,
|
||||||
|
CoreSignature,
|
||||||
|
ActionButton,
|
||||||
|
notifications,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import Dropzone from "components/common/Dropzone.svelte"
|
import Dropzone from "components/common/Dropzone.svelte"
|
||||||
import { capitalise } from "helpers"
|
import { capitalise } from "helpers"
|
||||||
import LinkedRowSelector from "components/common/LinkedRowSelector.svelte"
|
import LinkedRowSelector from "components/common/LinkedRowSelector.svelte"
|
||||||
import Editor from "../../integration/QueryEditor.svelte"
|
import Editor from "../../integration/QueryEditor.svelte"
|
||||||
|
import { SignatureModal } from "@budibase/frontend-core/src/components"
|
||||||
|
import { themeStore } from "stores/portal"
|
||||||
|
|
||||||
export let meta
|
export let meta
|
||||||
export let value
|
export let value
|
||||||
|
@ -38,8 +44,35 @@
|
||||||
|
|
||||||
const timeStamp = resolveTimeStamp(value)
|
const timeStamp = resolveTimeStamp(value)
|
||||||
const isTimeStamp = !!timeStamp || meta?.timeOnly
|
const isTimeStamp = !!timeStamp || meta?.timeOnly
|
||||||
|
|
||||||
|
$: currentTheme = $themeStore?.theme
|
||||||
|
$: darkMode = !currentTheme.includes("light")
|
||||||
|
|
||||||
|
let signatureModal
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<SignatureModal
|
||||||
|
{darkMode}
|
||||||
|
onConfirm={async sigCanvas => {
|
||||||
|
const signatureFile = sigCanvas.toFile()
|
||||||
|
|
||||||
|
let attachRequest = new FormData()
|
||||||
|
attachRequest.append("file", signatureFile)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const uploadReq = await API.uploadBuilderAttachment(attachRequest)
|
||||||
|
const [signatureAttachment] = uploadReq
|
||||||
|
value = signatureAttachment
|
||||||
|
} catch (error) {
|
||||||
|
$notifications.error(error.message || "Failed to save signature")
|
||||||
|
value = []
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
title={meta.name}
|
||||||
|
{value}
|
||||||
|
bind:this={signatureModal}
|
||||||
|
/>
|
||||||
|
|
||||||
{#if type === "options" && meta.constraints.inclusion.length !== 0}
|
{#if type === "options" && meta.constraints.inclusion.length !== 0}
|
||||||
<Select
|
<Select
|
||||||
{label}
|
{label}
|
||||||
|
@ -58,7 +91,51 @@
|
||||||
bind:value
|
bind:value
|
||||||
/>
|
/>
|
||||||
{:else if type === "attachment"}
|
{:else if type === "attachment"}
|
||||||
<Dropzone {label} {error} bind:value />
|
<Dropzone
|
||||||
|
compact
|
||||||
|
{label}
|
||||||
|
{error}
|
||||||
|
{value}
|
||||||
|
on:change={e => {
|
||||||
|
value = e.detail
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{:else if type === "attachment_single"}
|
||||||
|
<Dropzone
|
||||||
|
compact
|
||||||
|
{label}
|
||||||
|
{error}
|
||||||
|
value={value ? [value] : []}
|
||||||
|
on:change={e => {
|
||||||
|
value = e.detail?.[0]
|
||||||
|
}}
|
||||||
|
maximum={1}
|
||||||
|
/>
|
||||||
|
{:else if type === "signature_single"}
|
||||||
|
<div class="signature">
|
||||||
|
<Label>{label}</Label>
|
||||||
|
<div class="sig-wrap" class:display={value}>
|
||||||
|
{#if value}
|
||||||
|
<CoreSignature
|
||||||
|
{darkMode}
|
||||||
|
{value}
|
||||||
|
editable={false}
|
||||||
|
on:clear={() => {
|
||||||
|
value = null
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{:else}
|
||||||
|
<ActionButton
|
||||||
|
fullWidth
|
||||||
|
on:click={() => {
|
||||||
|
signatureModal.show()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Add signature
|
||||||
|
</ActionButton>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{:else if type === "boolean"}
|
{:else if type === "boolean"}
|
||||||
<Toggle text={label} {error} bind:value />
|
<Toggle text={label} {error} bind:value />
|
||||||
{:else if type === "array" && meta.constraints.inclusion.length !== 0}
|
{:else if type === "array" && meta.constraints.inclusion.length !== 0}
|
||||||
|
@ -94,3 +171,22 @@
|
||||||
{:else}
|
{:else}
|
||||||
<Input {label} {type} {error} bind:value disabled={readonly} />
|
<Input {label} {type} {error} bind:value disabled={readonly} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.signature :global(label.spectrum-FieldLabel) {
|
||||||
|
padding-top: var(--spectrum-fieldlabel-padding-top);
|
||||||
|
padding-bottom: var(--spectrum-fieldlabel-padding-bottom);
|
||||||
|
}
|
||||||
|
.sig-wrap.display {
|
||||||
|
min-height: 50px;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: var(--spectrum-global-color-gray-50);
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: var(--spectrum-alias-border-size-thin)
|
||||||
|
var(--spectrum-alias-border-color) solid;
|
||||||
|
border-radius: var(--spectrum-alias-border-radius-regular);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { datasources, tables, integrations, appStore } from "stores/builder"
|
import { datasources, tables, integrations, appStore } from "stores/builder"
|
||||||
import { admin } from "stores/portal"
|
import { themeStore, admin } from "stores/portal"
|
||||||
import EditRolesButton from "./buttons/EditRolesButton.svelte"
|
import EditRolesButton from "./buttons/EditRolesButton.svelte"
|
||||||
import { TableNames } from "constants"
|
import { TableNames } from "constants"
|
||||||
import { Grid } from "@budibase/frontend-core"
|
import { Grid } from "@budibase/frontend-core"
|
||||||
|
@ -38,6 +38,9 @@
|
||||||
})
|
})
|
||||||
$: relationshipsEnabled = relationshipSupport(tableDatasource)
|
$: relationshipsEnabled = relationshipSupport(tableDatasource)
|
||||||
|
|
||||||
|
$: currentTheme = $themeStore?.theme
|
||||||
|
$: darkMode = !currentTheme.includes("light")
|
||||||
|
|
||||||
const relationshipSupport = datasource => {
|
const relationshipSupport = datasource => {
|
||||||
const integration = $integrations[datasource?.source]
|
const integration = $integrations[datasource?.source]
|
||||||
return !isInternal && integration?.relationships !== false
|
return !isInternal && integration?.relationships !== false
|
||||||
|
@ -56,6 +59,7 @@
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<Grid
|
<Grid
|
||||||
{API}
|
{API}
|
||||||
|
{darkMode}
|
||||||
datasource={gridDatasource}
|
datasource={gridDatasource}
|
||||||
canAddRows={!isUsersTable}
|
canAddRows={!isUsersTable}
|
||||||
canDeleteRows={!isUsersTable}
|
canDeleteRows={!isUsersTable}
|
||||||
|
|
|
@ -9,6 +9,7 @@ const MAX_DEPTH = 1
|
||||||
const TYPES_TO_SKIP = [
|
const TYPES_TO_SKIP = [
|
||||||
FieldType.FORMULA,
|
FieldType.FORMULA,
|
||||||
FieldType.LONGFORM,
|
FieldType.LONGFORM,
|
||||||
|
FieldType.SIGNATURE_SINGLE,
|
||||||
FieldType.ATTACHMENTS,
|
FieldType.ATTACHMENTS,
|
||||||
//https://github.com/Budibase/budibase/issues/3030
|
//https://github.com/Budibase/budibase/issues/3030
|
||||||
FieldType.INTERNAL,
|
FieldType.INTERNAL,
|
||||||
|
|
|
@ -412,6 +412,7 @@
|
||||||
FIELDS.FORMULA,
|
FIELDS.FORMULA,
|
||||||
FIELDS.JSON,
|
FIELDS.JSON,
|
||||||
FIELDS.BARCODEQR,
|
FIELDS.BARCODEQR,
|
||||||
|
FIELDS.SIGNATURE_SINGLE,
|
||||||
FIELDS.BIGINT,
|
FIELDS.BIGINT,
|
||||||
FIELDS.AUTO,
|
FIELDS.AUTO,
|
||||||
]
|
]
|
||||||
|
|
|
@ -54,6 +54,10 @@
|
||||||
label: "Attachment",
|
label: "Attachment",
|
||||||
value: FieldType.ATTACHMENT_SINGLE,
|
value: FieldType.ATTACHMENT_SINGLE,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "Signature",
|
||||||
|
value: FieldType.SIGNATURE_SINGLE,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: "Attachment list",
|
label: "Attachment list",
|
||||||
value: FieldType.ATTACHMENTS,
|
value: FieldType.ATTACHMENTS,
|
||||||
|
|
|
@ -54,6 +54,7 @@
|
||||||
export let autofocus = false
|
export let autofocus = false
|
||||||
export let jsBindingWrapping = true
|
export let jsBindingWrapping = true
|
||||||
export let readonly = false
|
export let readonly = false
|
||||||
|
export let readonlyLineNumbers = false
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
@ -240,6 +241,9 @@
|
||||||
|
|
||||||
if (readonly) {
|
if (readonly) {
|
||||||
complete.push(EditorState.readOnly.of(true))
|
complete.push(EditorState.readOnly.of(true))
|
||||||
|
if (readonlyLineNumbers) {
|
||||||
|
complete.push(lineNumbers())
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
complete = [
|
complete = [
|
||||||
...complete,
|
...complete,
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
export let selectedBy = null
|
export let selectedBy = null
|
||||||
export let compact = false
|
export let compact = false
|
||||||
export let hovering = false
|
export let hovering = false
|
||||||
|
export let disabled = false
|
||||||
|
|
||||||
const scrollApi = getContext("scroll")
|
const scrollApi = getContext("scroll")
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
@ -74,6 +75,7 @@
|
||||||
class:scrollable
|
class:scrollable
|
||||||
class:highlighted
|
class:highlighted
|
||||||
class:selectedBy
|
class:selectedBy
|
||||||
|
class:disabled
|
||||||
on:dragend
|
on:dragend
|
||||||
on:dragstart
|
on:dragstart
|
||||||
on:dragover
|
on:dragover
|
||||||
|
@ -165,6 +167,9 @@
|
||||||
--avatars-background: var(--spectrum-global-color-gray-300);
|
--avatars-background: var(--spectrum-global-color-gray-300);
|
||||||
color: var(--ink);
|
color: var(--ink);
|
||||||
}
|
}
|
||||||
|
.nav-item.disabled span {
|
||||||
|
color: var(--spectrum-global-color-gray-700);
|
||||||
|
}
|
||||||
.nav-item:hover,
|
.nav-item:hover,
|
||||||
.hovering {
|
.hovering {
|
||||||
background-color: var(--spectrum-global-color-gray-200);
|
background-color: var(--spectrum-global-color-gray-200);
|
||||||
|
|
|
@ -28,6 +28,12 @@
|
||||||
let bindingDrawer
|
let bindingDrawer
|
||||||
let currentVal = value
|
let currentVal = value
|
||||||
|
|
||||||
|
let attachmentTypes = [
|
||||||
|
FieldType.ATTACHMENT_SINGLE,
|
||||||
|
FieldType.ATTACHMENTS,
|
||||||
|
FieldType.SIGNATURE_SINGLE,
|
||||||
|
]
|
||||||
|
|
||||||
$: readableValue = runtimeToReadableBinding(bindings, value)
|
$: readableValue = runtimeToReadableBinding(bindings, value)
|
||||||
$: tempValue = readableValue
|
$: tempValue = readableValue
|
||||||
$: isJS = isJSBinding(value)
|
$: isJS = isJSBinding(value)
|
||||||
|
@ -105,6 +111,7 @@
|
||||||
boolean: isValidBoolean,
|
boolean: isValidBoolean,
|
||||||
attachment: false,
|
attachment: false,
|
||||||
attachment_single: false,
|
attachment_single: false,
|
||||||
|
signature_single: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
const isValid = value => {
|
const isValid = value => {
|
||||||
|
@ -126,6 +133,7 @@
|
||||||
"bigint",
|
"bigint",
|
||||||
"barcodeqr",
|
"barcodeqr",
|
||||||
"attachment",
|
"attachment",
|
||||||
|
"signature_single",
|
||||||
"attachment_single",
|
"attachment_single",
|
||||||
].includes(type)
|
].includes(type)
|
||||||
) {
|
) {
|
||||||
|
@ -169,7 +177,7 @@
|
||||||
{updateOnChange}
|
{updateOnChange}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
{#if !disabled && type !== "formula" && !disabled && type !== FieldType.ATTACHMENTS && !disabled && type !== FieldType.ATTACHMENT_SINGLE}
|
{#if !disabled && type !== "formula" && !disabled && !attachmentTypes.includes(type)}
|
||||||
<div
|
<div
|
||||||
class={`icon ${getIconClass(value, type)}`}
|
class={`icon ${getIconClass(value, type)}`}
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
|
|
|
@ -76,6 +76,7 @@ const componentMap = {
|
||||||
"field/array": FormFieldSelect,
|
"field/array": FormFieldSelect,
|
||||||
"field/json": FormFieldSelect,
|
"field/json": FormFieldSelect,
|
||||||
"field/barcodeqr": FormFieldSelect,
|
"field/barcodeqr": FormFieldSelect,
|
||||||
|
"field/signature_single": FormFieldSelect,
|
||||||
"field/bb_reference": FormFieldSelect,
|
"field/bb_reference": FormFieldSelect,
|
||||||
// Some validation types are the same as others, so not all types are
|
// Some validation types are the same as others, so not all types are
|
||||||
// explicitly listed here. e.g. options uses string validation
|
// explicitly listed here. e.g. options uses string validation
|
||||||
|
@ -85,6 +86,8 @@ const componentMap = {
|
||||||
"validation/boolean": ValidationEditor,
|
"validation/boolean": ValidationEditor,
|
||||||
"validation/datetime": ValidationEditor,
|
"validation/datetime": ValidationEditor,
|
||||||
"validation/attachment": ValidationEditor,
|
"validation/attachment": ValidationEditor,
|
||||||
|
"validation/attachment_single": ValidationEditor,
|
||||||
|
"validation/signature_single": ValidationEditor,
|
||||||
"validation/link": ValidationEditor,
|
"validation/link": ValidationEditor,
|
||||||
"validation/bb_reference": ValidationEditor,
|
"validation/bb_reference": ValidationEditor,
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,9 @@
|
||||||
parameters
|
parameters
|
||||||
}
|
}
|
||||||
$: automations = $automationStore.automations
|
$: automations = $automationStore.automations
|
||||||
.filter(a => a.definition.trigger?.stepId === TriggerStepID.APP)
|
.filter(
|
||||||
|
a => a.definition.trigger?.stepId === TriggerStepID.APP && !a.disabled
|
||||||
|
)
|
||||||
.map(automation => {
|
.map(automation => {
|
||||||
const schema = Object.entries(
|
const schema = Object.entries(
|
||||||
automation.definition.trigger.inputs.fields || {}
|
automation.definition.trigger.inputs.fields || {}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import EditComponentPopover from "../EditComponentPopover/EditComponentPopover.svelte"
|
import EditComponentPopover from "../EditComponentPopover.svelte"
|
||||||
import { Icon } from "@budibase/bbui"
|
import { Icon } from "@budibase/bbui"
|
||||||
import { runtimeToReadableBinding } from "dataBinding"
|
import { runtimeToReadableBinding } from "dataBinding"
|
||||||
import { isJSBinding } from "@budibase/string-templates"
|
import { isJSBinding } from "@budibase/string-templates"
|
||||||
|
|
|
@ -100,9 +100,6 @@
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
get(store).actions.select(draggableItem.id)
|
get(store).actions.select(draggableItem.id)
|
||||||
}}
|
}}
|
||||||
on:mousedown={() => {
|
|
||||||
get(store).actions.select()
|
|
||||||
}}
|
|
||||||
bind:this={anchors[draggableItem.id]}
|
bind:this={anchors[draggableItem.id]}
|
||||||
class:highlighted={draggableItem.id === $store.selected}
|
class:highlighted={draggableItem.id === $store.selected}
|
||||||
>
|
>
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
import { componentStore } from "stores/builder"
|
import { componentStore } from "stores/builder"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
import { createEventDispatcher, getContext } from "svelte"
|
import { createEventDispatcher, getContext } from "svelte"
|
||||||
import { customPositionHandler } from "."
|
|
||||||
import ComponentSettingsSection from "pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/ComponentSettingsSection.svelte"
|
import ComponentSettingsSection from "pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/ComponentSettingsSection.svelte"
|
||||||
|
|
||||||
export let anchor
|
export let anchor
|
||||||
|
@ -18,76 +17,74 @@
|
||||||
|
|
||||||
let popover
|
let popover
|
||||||
let drawers = []
|
let drawers = []
|
||||||
let open = false
|
let isOpen = false
|
||||||
|
|
||||||
// Auto hide the component when another item is selected
|
// Auto hide the component when another item is selected
|
||||||
$: if (open && $draggable.selected !== componentInstance._id) {
|
$: if (open && $draggable.selected !== componentInstance._id) {
|
||||||
popover.hide()
|
close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open automatically if the component is marked as selected
|
// Open automatically if the component is marked as selected
|
||||||
$: if (!open && $draggable.selected === componentInstance._id && popover) {
|
$: if (!open && $draggable.selected === componentInstance._id && popover) {
|
||||||
popover.show()
|
open()
|
||||||
open = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$: componentDef = componentStore.getDefinition(componentInstance._component)
|
$: componentDef = componentStore.getDefinition(componentInstance._component)
|
||||||
$: parsedComponentDef = processComponentDefinitionSettings(componentDef)
|
$: parsedComponentDef = processComponentDefinitionSettings(componentDef)
|
||||||
|
|
||||||
|
const open = () => {
|
||||||
|
isOpen = true
|
||||||
|
drawers = []
|
||||||
|
$draggable.actions.select(componentInstance._id)
|
||||||
|
}
|
||||||
|
|
||||||
|
const close = () => {
|
||||||
|
// Slight delay allows us to be able to properly toggle open/close state by
|
||||||
|
// clicking again on the settings icon
|
||||||
|
setTimeout(() => {
|
||||||
|
isOpen = false
|
||||||
|
if ($draggable.selected === componentInstance._id) {
|
||||||
|
$draggable.actions.select()
|
||||||
|
}
|
||||||
|
}, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggleOpen = () => {
|
||||||
|
if (isOpen) {
|
||||||
|
close()
|
||||||
|
} else {
|
||||||
|
open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const processComponentDefinitionSettings = componentDef => {
|
const processComponentDefinitionSettings = componentDef => {
|
||||||
if (!componentDef) {
|
if (!componentDef) {
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
const clone = cloneDeep(componentDef)
|
const clone = cloneDeep(componentDef)
|
||||||
|
|
||||||
if (typeof parseSettings === "function") {
|
if (typeof parseSettings === "function") {
|
||||||
clone.settings = parseSettings(clone.settings)
|
clone.settings = parseSettings(clone.settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
return clone
|
return clone
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateSetting = async (setting, value) => {
|
const updateSetting = async (setting, value) => {
|
||||||
const nestedComponentInstance = cloneDeep(componentInstance)
|
const nestedComponentInstance = cloneDeep(componentInstance)
|
||||||
|
|
||||||
const patchFn = componentStore.updateComponentSetting(setting.key, value)
|
const patchFn = componentStore.updateComponentSetting(setting.key, value)
|
||||||
patchFn(nestedComponentInstance)
|
patchFn(nestedComponentInstance)
|
||||||
|
|
||||||
dispatch("change", nestedComponentInstance)
|
dispatch("change", nestedComponentInstance)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Icon
|
<Icon name="Settings" hoverable size="S" on:click={toggleOpen} />
|
||||||
name="Settings"
|
|
||||||
hoverable
|
|
||||||
size="S"
|
|
||||||
on:click={() => {
|
|
||||||
if (!open) {
|
|
||||||
popover.show()
|
|
||||||
open = true
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Popover
|
<Popover
|
||||||
bind:this={popover}
|
open={isOpen}
|
||||||
on:open={() => {
|
on:close={close}
|
||||||
drawers = []
|
|
||||||
$draggable.actions.select(componentInstance._id)
|
|
||||||
}}
|
|
||||||
on:close={() => {
|
|
||||||
open = false
|
|
||||||
if ($draggable.selected === componentInstance._id) {
|
|
||||||
$draggable.actions.select()
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
{anchor}
|
{anchor}
|
||||||
align="left-outside"
|
align="left-outside"
|
||||||
showPopover={drawers.length === 0}
|
showPopover={drawers.length === 0}
|
||||||
clickOutsideOverride={drawers.length > 0}
|
clickOutsideOverride={drawers.length > 0}
|
||||||
maxHeight={600}
|
maxHeight={600}
|
||||||
offset={18}
|
offset={18}
|
||||||
handlePostionUpdate={customPositionHandler}
|
|
||||||
>
|
>
|
||||||
<span class="popover-wrap">
|
<span class="popover-wrap">
|
||||||
<Layout noPadding noGap>
|
<Layout noPadding noGap>
|
|
@ -1,18 +0,0 @@
|
||||||
export const customPositionHandler = (anchorBounds, eleBounds, cfg) => {
|
|
||||||
let { left, top, offset } = cfg
|
|
||||||
let percentageOffset = 30
|
|
||||||
// left-outside
|
|
||||||
left = anchorBounds.left - eleBounds.width - (offset || 5)
|
|
||||||
|
|
||||||
// shift up from the anchor, if space allows
|
|
||||||
let offsetPos = Math.floor(eleBounds.height / 100) * percentageOffset
|
|
||||||
let defaultTop = anchorBounds.top - offsetPos
|
|
||||||
|
|
||||||
if (window.innerHeight - defaultTop < eleBounds.height) {
|
|
||||||
top = window.innerHeight - eleBounds.height - 5
|
|
||||||
} else {
|
|
||||||
top = anchorBounds.top - offsetPos
|
|
||||||
}
|
|
||||||
|
|
||||||
return { ...cfg, left, top }
|
|
||||||
}
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
<script>
|
||||||
|
import { ContextTooltip } from "@budibase/bbui"
|
||||||
|
import {
|
||||||
|
StringsAsDates,
|
||||||
|
NumbersAsDates,
|
||||||
|
ScalarJsonOnly,
|
||||||
|
Column,
|
||||||
|
Support,
|
||||||
|
NotRequired,
|
||||||
|
StringsAsNumbers,
|
||||||
|
DatesAsNumbers,
|
||||||
|
} from "./subjects"
|
||||||
|
import subjects from "../subjects"
|
||||||
|
|
||||||
|
export let anchor
|
||||||
|
export let schema
|
||||||
|
export let columnName
|
||||||
|
export let subject = subjects.none
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ContextTooltip visible={subject !== subjects.none} {anchor} offset={20}>
|
||||||
|
<div class="explanationModalContent">
|
||||||
|
{#if subject === subjects.column}
|
||||||
|
<Column {columnName} {schema} />
|
||||||
|
{:else if subject === subjects.support}
|
||||||
|
<Support />
|
||||||
|
{:else if subject === subjects.stringsAsNumbers}
|
||||||
|
<StringsAsNumbers />
|
||||||
|
{:else if subject === subjects.notRequired}
|
||||||
|
<NotRequired />
|
||||||
|
{:else if subject === subjects.datesAsNumbers}
|
||||||
|
<DatesAsNumbers />
|
||||||
|
{:else if subject === subjects.scalarJsonOnly}
|
||||||
|
<ScalarJsonOnly {columnName} {schema} />
|
||||||
|
{:else if subject === subjects.numbersAsDates}
|
||||||
|
<NumbersAsDates {columnName} />
|
||||||
|
{:else if subject === subjects.stringsAsDates}
|
||||||
|
<StringsAsDates {columnName} />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</ContextTooltip>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.explanationModalContent {
|
||||||
|
max-width: 300px;
|
||||||
|
padding: 16px 12px 2px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,147 @@
|
||||||
|
<script>
|
||||||
|
import { tables } from "stores/builder"
|
||||||
|
import {
|
||||||
|
BindingValue,
|
||||||
|
Block,
|
||||||
|
Subject,
|
||||||
|
JSONValue,
|
||||||
|
Property,
|
||||||
|
Section,
|
||||||
|
} from "./components"
|
||||||
|
|
||||||
|
export let schema
|
||||||
|
export let columnName
|
||||||
|
|
||||||
|
const parseDate = isoString => {
|
||||||
|
if ([null, undefined, ""].includes(isoString)) {
|
||||||
|
return "None"
|
||||||
|
}
|
||||||
|
|
||||||
|
const unixTime = Date.parse(isoString)
|
||||||
|
const date = new Date(unixTime)
|
||||||
|
|
||||||
|
return date.toLocaleString()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Subject>
|
||||||
|
<div class="heading" slot="heading">
|
||||||
|
Column Overview for <Block>{columnName}</Block>
|
||||||
|
</div>
|
||||||
|
<Section>
|
||||||
|
{#if schema.type === "string"}
|
||||||
|
<Property
|
||||||
|
name="Max Length"
|
||||||
|
value={schema?.constraints?.length?.maximum ?? "None"}
|
||||||
|
/>
|
||||||
|
{:else if schema.type === "datetime"}
|
||||||
|
<Property
|
||||||
|
name="Earliest"
|
||||||
|
value={parseDate(schema?.constraints?.datetime?.earliest)}
|
||||||
|
/>
|
||||||
|
<Property
|
||||||
|
name="Latest"
|
||||||
|
value={parseDate(schema?.constraints?.datetime?.latest)}
|
||||||
|
/>
|
||||||
|
<Property
|
||||||
|
name="Ignore time zones"
|
||||||
|
value={schema?.ignoreTimeZones === true ? "Yes" : "No"}
|
||||||
|
/>
|
||||||
|
<Property
|
||||||
|
name="Date only"
|
||||||
|
value={schema?.dateOnly === true ? "Yes" : "No"}
|
||||||
|
/>
|
||||||
|
{:else if schema.type === "number"}
|
||||||
|
<Property
|
||||||
|
name="Min Value"
|
||||||
|
value={[null, undefined, ""].includes(
|
||||||
|
schema?.constraints?.numericality?.greaterThanOrEqualTo
|
||||||
|
)
|
||||||
|
? "None"
|
||||||
|
: schema?.constraints?.numericality?.greaterThanOrEqualTo}
|
||||||
|
/>
|
||||||
|
<Property
|
||||||
|
name="Max Value"
|
||||||
|
value={[null, undefined, ""].includes(
|
||||||
|
schema?.constraints?.numericality?.lessThanOrEqualTo
|
||||||
|
)
|
||||||
|
? "None"
|
||||||
|
: schema?.constraints?.numericality?.lessThanOrEqualTo}
|
||||||
|
/>
|
||||||
|
{:else if schema.type === "array"}
|
||||||
|
{#each schema?.constraints?.inclusion ?? [] as option, index}
|
||||||
|
<Property name={`Option ${index + 1}`} truncate>
|
||||||
|
<span
|
||||||
|
style:background-color={schema?.optionColors?.[option]}
|
||||||
|
class="optionCircle"
|
||||||
|
/>{option}
|
||||||
|
</Property>
|
||||||
|
{/each}
|
||||||
|
{:else if schema.type === "options"}
|
||||||
|
{#each schema?.constraints?.inclusion ?? [] as option, index}
|
||||||
|
<Property name={`Option ${index + 1}`} truncate>
|
||||||
|
<span
|
||||||
|
style:background-color={schema?.optionColors?.[option]}
|
||||||
|
class="optionCircle"
|
||||||
|
/>{option}
|
||||||
|
</Property>
|
||||||
|
{/each}
|
||||||
|
{:else if schema.type === "json"}
|
||||||
|
<Property name="Schema">
|
||||||
|
<JSONValue value={JSON.stringify(schema?.schema ?? {}, null, 2)} />
|
||||||
|
</Property>
|
||||||
|
{:else if schema.type === "formula"}
|
||||||
|
<Property name="Formula">
|
||||||
|
<BindingValue value={schema?.formula} />
|
||||||
|
</Property>
|
||||||
|
<Property
|
||||||
|
name="Formula type"
|
||||||
|
value={schema?.formulaType === "dynamic" ? "Dynamic" : "Static"}
|
||||||
|
/>
|
||||||
|
{:else if schema.type === "link"}
|
||||||
|
<Property name="Type" value={schema?.relationshipType} />
|
||||||
|
<Property
|
||||||
|
name="Related Table"
|
||||||
|
value={$tables?.list?.find(table => table._id === schema?.tableId)
|
||||||
|
?.name}
|
||||||
|
/>
|
||||||
|
<Property name="Column in Related Table" value={schema?.fieldName} />
|
||||||
|
{:else if schema.type === "bb_reference"}
|
||||||
|
<Property
|
||||||
|
name="Allow multiple users"
|
||||||
|
value={schema?.relationshipType === "many-to-many" ? "Yes" : "No"}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
<Property
|
||||||
|
name="Required"
|
||||||
|
value={schema?.constraints?.presence?.allowEmpty === false ? "Yes" : "No"}
|
||||||
|
/>
|
||||||
|
</Section>
|
||||||
|
</Subject>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.heading {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.heading > :global(.block) {
|
||||||
|
margin-left: 4px;
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 1;
|
||||||
|
display: inline-block;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.optionCircle {
|
||||||
|
display: inline-block;
|
||||||
|
background-color: hsla(0, 1%, 50%, 0.3);
|
||||||
|
border-radius: 100%;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,63 @@
|
||||||
|
<script>
|
||||||
|
import { onMount } from "svelte"
|
||||||
|
import {
|
||||||
|
ExampleSection,
|
||||||
|
ExampleLine,
|
||||||
|
Block,
|
||||||
|
Subject,
|
||||||
|
Section,
|
||||||
|
} from "./components"
|
||||||
|
|
||||||
|
let timestamp = Date.now()
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
let run = true
|
||||||
|
|
||||||
|
const updateTimeStamp = () => {
|
||||||
|
timestamp = Date.now()
|
||||||
|
if (run) {
|
||||||
|
setTimeout(updateTimeStamp, 200)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTimeStamp()
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
run = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Subject heading="Dates as Numbers">
|
||||||
|
<Section>
|
||||||
|
A datetime value can be used in place of a numeric value, but it will be
|
||||||
|
converted to a <Block>UNIX time</Block> timestamp, which is the number of milliseconds
|
||||||
|
since Jan 1st 1970. A more recent moment in time will be a higher number.
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
<ExampleSection heading="Examples:">
|
||||||
|
<ExampleLine>
|
||||||
|
<Block>
|
||||||
|
{new Date(946684800000).toLocaleString()}
|
||||||
|
</Block>
|
||||||
|
<span class="separator">{"->"} </span><Block>946684800000</Block>
|
||||||
|
</ExampleLine>
|
||||||
|
<ExampleLine>
|
||||||
|
<Block>
|
||||||
|
{new Date(1577836800000).toLocaleString()}
|
||||||
|
</Block>
|
||||||
|
<span class="separator">{"->"} </span><Block>1577836800000</Block>
|
||||||
|
</ExampleLine>
|
||||||
|
<ExampleLine>
|
||||||
|
<Block>Now</Block><span class="separator">{"->"} </span><Block
|
||||||
|
>{timestamp}</Block
|
||||||
|
>
|
||||||
|
</ExampleLine>
|
||||||
|
</ExampleSection>
|
||||||
|
</Subject>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.separator {
|
||||||
|
margin: 0 5px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,11 @@
|
||||||
|
<script>
|
||||||
|
import { Block, Subject, Section } from "./components"
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Subject heading="Required Constraint">
|
||||||
|
<Section>
|
||||||
|
A <Block>required</Block> constraint can be applied to columns to ensure a value
|
||||||
|
is always present. If a column doesn't have this constraint, then its value for
|
||||||
|
a particular row could he missing.
|
||||||
|
</Section>
|
||||||
|
</Subject>
|
|
@ -0,0 +1,65 @@
|
||||||
|
<script>
|
||||||
|
import { onMount } from "svelte"
|
||||||
|
import {
|
||||||
|
ExampleSection,
|
||||||
|
ExampleLine,
|
||||||
|
Block,
|
||||||
|
Subject,
|
||||||
|
Section,
|
||||||
|
} from "./components"
|
||||||
|
|
||||||
|
let timestamp = Date.now()
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
let run = true
|
||||||
|
|
||||||
|
const updateTimeStamp = () => {
|
||||||
|
timestamp = Date.now()
|
||||||
|
if (run) {
|
||||||
|
setTimeout(updateTimeStamp, 200)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTimeStamp()
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
run = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Subject heading="Numbers as Dates">
|
||||||
|
<Section>
|
||||||
|
A number value can be used in place of a datetime value, but it will be
|
||||||
|
parsed as a <Block>UNIX time</Block> timestamp, which is the number of milliseconds
|
||||||
|
since Jan 1st 1970. A more recent moment in time will be a higher number.
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
<ExampleSection heading="Examples:">
|
||||||
|
<ExampleLine>
|
||||||
|
<Block>946684800000</Block>
|
||||||
|
<span class="separator">{"->"}</span>
|
||||||
|
<Block>
|
||||||
|
{new Date(946684800000).toLocaleString()}
|
||||||
|
</Block>
|
||||||
|
</ExampleLine>
|
||||||
|
<ExampleLine>
|
||||||
|
<Block>1577836800000</Block>
|
||||||
|
<span class="separator">{"->"}</span>
|
||||||
|
<Block>
|
||||||
|
{new Date(1577836800000).toLocaleString()}
|
||||||
|
</Block>
|
||||||
|
</ExampleLine>
|
||||||
|
<ExampleLine>
|
||||||
|
<Block>{timestamp}</Block>
|
||||||
|
<span class="separator">{"->"}</span>
|
||||||
|
<Block>Now</Block>
|
||||||
|
</ExampleLine>
|
||||||
|
</ExampleSection>
|
||||||
|
</Subject>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.separator {
|
||||||
|
margin: 0 5px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,71 @@
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
ExampleSection,
|
||||||
|
ExampleLine,
|
||||||
|
Block,
|
||||||
|
Subject,
|
||||||
|
Section,
|
||||||
|
} from "./components"
|
||||||
|
|
||||||
|
export let schema
|
||||||
|
export let columnName
|
||||||
|
|
||||||
|
const maxScalarDescendantsToFind = 3
|
||||||
|
|
||||||
|
const getScalarDescendants = schema => {
|
||||||
|
const newScalarDescendants = []
|
||||||
|
|
||||||
|
const getScalarDescendantFromSchema = (path, schema) => {
|
||||||
|
if (newScalarDescendants.length >= maxScalarDescendantsToFind) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (["string", "number", "boolean"].includes(schema.type)) {
|
||||||
|
newScalarDescendants.push({ name: path.join("."), type: schema.type })
|
||||||
|
} else if (schema.type === "json") {
|
||||||
|
Object.entries(schema.schema ?? {}).forEach(
|
||||||
|
([childName, childSchema]) =>
|
||||||
|
getScalarDescendantFromSchema([...path, childName], childSchema)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.entries(schema?.schema ?? {}).forEach(([childName, childSchema]) =>
|
||||||
|
getScalarDescendantFromSchema([columnName, childName], childSchema)
|
||||||
|
)
|
||||||
|
|
||||||
|
return newScalarDescendants
|
||||||
|
}
|
||||||
|
|
||||||
|
$: scalarDescendants = getScalarDescendants(schema)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Subject heading="Using Scalar JSON Values">
|
||||||
|
<Section>
|
||||||
|
<Block>JSON objects</Block> can't be used here, but any <Block>number</Block
|
||||||
|
>, <Block>string</Block> or <Block>boolean</Block> values nested within said
|
||||||
|
object can be if they are otherwise compatible with the input. These scalar values
|
||||||
|
can be selected from the same menu as this parent and take the form <Block
|
||||||
|
>parent.child</Block
|
||||||
|
>.
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
{#if scalarDescendants.length > 0}
|
||||||
|
<ExampleSection heading="Examples scalar descendants of this object:">
|
||||||
|
{#each scalarDescendants as descendant}
|
||||||
|
<ExampleLine>
|
||||||
|
<Block truncate>{descendant.name}</Block><span class="separator"
|
||||||
|
>-</span
|
||||||
|
><Block truncate noShrink>{descendant.type}</Block>
|
||||||
|
</ExampleLine>
|
||||||
|
{/each}
|
||||||
|
</ExampleSection>
|
||||||
|
{/if}
|
||||||
|
</Subject>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.separator {
|
||||||
|
margin: 0 4px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,107 @@
|
||||||
|
<script>
|
||||||
|
import { onMount } from "svelte"
|
||||||
|
import {
|
||||||
|
ExampleSection,
|
||||||
|
ExampleLine,
|
||||||
|
Block,
|
||||||
|
Subject,
|
||||||
|
Section,
|
||||||
|
} from "./components"
|
||||||
|
|
||||||
|
let timestamp = Date.now()
|
||||||
|
$: iso = new Date(timestamp).toISOString()
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
let run = true
|
||||||
|
|
||||||
|
const updateTimeStamp = () => {
|
||||||
|
timestamp = Date.now()
|
||||||
|
if (run) {
|
||||||
|
setTimeout(updateTimeStamp, 200)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTimeStamp()
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
run = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Subject heading="Strings as Dates">
|
||||||
|
<Section>
|
||||||
|
A string value can be used in place of a datetime value, but it will be
|
||||||
|
parsed as:
|
||||||
|
</Section>
|
||||||
|
<Section>
|
||||||
|
A <Block>UNIX time</Block> timestamp, which is the number of milliseconds since
|
||||||
|
Jan 1st 1970. A more recent moment in time will be a higher number.
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
<ExampleSection heading="Examples:">
|
||||||
|
<ExampleLine>
|
||||||
|
<Block>946684800000</Block>
|
||||||
|
<span class="separator">{"->"}</span>
|
||||||
|
<Block>
|
||||||
|
{new Date(946684800000).toLocaleString()}
|
||||||
|
</Block>
|
||||||
|
</ExampleLine>
|
||||||
|
<ExampleLine>
|
||||||
|
<Block>1577836800000</Block>
|
||||||
|
<span class="separator">{"->"}</span>
|
||||||
|
<Block>
|
||||||
|
{new Date(1577836800000).toLocaleString()}
|
||||||
|
</Block>
|
||||||
|
</ExampleLine>
|
||||||
|
<ExampleLine>
|
||||||
|
<Block>{timestamp}</Block>
|
||||||
|
<span class="separator">{"->"}</span>
|
||||||
|
<Block>Now</Block>
|
||||||
|
</ExampleLine>
|
||||||
|
</ExampleSection>
|
||||||
|
<Section>
|
||||||
|
An <Block>ISO 8601</Block> datetime string, which represents an exact moment
|
||||||
|
in time as well as the potentional to store the timezone it occured in.
|
||||||
|
</Section>
|
||||||
|
<div class="isoExamples">
|
||||||
|
<ExampleSection heading="Examples:">
|
||||||
|
<ExampleLine>
|
||||||
|
<Block>2000-01-01T00:00:00.000Z</Block>
|
||||||
|
<span class="separator">↓</span>
|
||||||
|
<Block>
|
||||||
|
{new Date(946684800000).toLocaleString()}
|
||||||
|
</Block>
|
||||||
|
</ExampleLine>
|
||||||
|
<ExampleLine>
|
||||||
|
<Block>2000-01-01T00:00:00.000Z</Block>
|
||||||
|
<span class="separator">↓</span>
|
||||||
|
<Block>
|
||||||
|
{new Date(1577836800000).toLocaleString()}
|
||||||
|
</Block>
|
||||||
|
</ExampleLine>
|
||||||
|
<ExampleLine>
|
||||||
|
<Block>{iso}</Block>
|
||||||
|
<span class="separator">↓</span>
|
||||||
|
<Block>Now</Block>
|
||||||
|
</ExampleLine>
|
||||||
|
</ExampleSection>
|
||||||
|
</div>
|
||||||
|
</Subject>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.separator {
|
||||||
|
margin: 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.isoExamples :global(.block) {
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.isoExamples :global(.exampleLine) {
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
width: 162px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,56 @@
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
ExampleSection,
|
||||||
|
ExampleLine,
|
||||||
|
Block,
|
||||||
|
Subject,
|
||||||
|
Section,
|
||||||
|
} from "./components"
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Subject heading="Text as Numbers">
|
||||||
|
<Section>
|
||||||
|
Text can be used in place of numbers in certain scenarios, but care needs to
|
||||||
|
be taken; if the value isn't purely numerical it may be converted in an
|
||||||
|
unexpected way.
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
<ExampleSection heading="Examples:">
|
||||||
|
<ExampleLine>
|
||||||
|
<Block>"100"</Block><span class="separator">{"->"}</span><Block>100</Block
|
||||||
|
>
|
||||||
|
</ExampleLine>
|
||||||
|
<ExampleLine>
|
||||||
|
<Block>"100k"</Block><span class="separator">{"->"}</span><Block
|
||||||
|
>100</Block
|
||||||
|
>
|
||||||
|
</ExampleLine>
|
||||||
|
<ExampleLine>
|
||||||
|
<Block>"100,000"</Block><span class="separator">{"->"}</span><Block
|
||||||
|
>100</Block
|
||||||
|
>
|
||||||
|
</ExampleLine>
|
||||||
|
<ExampleLine>
|
||||||
|
<Block>"100 million"</Block><span class="separator">{"->"}</span><Block
|
||||||
|
>100</Block
|
||||||
|
>
|
||||||
|
</ExampleLine>
|
||||||
|
<ExampleLine>
|
||||||
|
<Block>"100.9"</Block><span class="separator">{"->"}</span><Block
|
||||||
|
>100.9</Block
|
||||||
|
>
|
||||||
|
</ExampleLine>
|
||||||
|
<ExampleLine>
|
||||||
|
<Block>"One hundred"</Block><span class="separator">{"->"}</span><Block
|
||||||
|
>Error</Block
|
||||||
|
>
|
||||||
|
</ExampleLine>
|
||||||
|
</ExampleSection>
|
||||||
|
</Subject>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.separator {
|
||||||
|
margin: 0 4px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,35 @@
|
||||||
|
<script>
|
||||||
|
import { InfoWord } from "../../typography"
|
||||||
|
import { Subject, Section } from "./components"
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Subject heading="Data/Component Compatibility">
|
||||||
|
<Section>
|
||||||
|
<InfoWord icon="CheckmarkCircle" color="var(--green)" text="Compatible" />
|
||||||
|
<span class="body"
|
||||||
|
>Fully compatible with the input as long as the data is present.</span
|
||||||
|
>
|
||||||
|
</Section>
|
||||||
|
<Section>
|
||||||
|
<InfoWord
|
||||||
|
icon="AlertCheck"
|
||||||
|
color="var(--yellow)"
|
||||||
|
text="Partially compatible"
|
||||||
|
/>
|
||||||
|
<span class="body"
|
||||||
|
>Partially compatible with the input, but beware of other caveats
|
||||||
|
mentioned.</span
|
||||||
|
>
|
||||||
|
</Section>
|
||||||
|
<Section>
|
||||||
|
<InfoWord icon="Alert" color="var(--red)" text="Not compatible" />
|
||||||
|
<span class="body">Incompatible with the component.</span>
|
||||||
|
</Section>
|
||||||
|
</Subject>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.body {
|
||||||
|
display: block;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,39 @@
|
||||||
|
<script>
|
||||||
|
import { decodeJSBinding } from "@budibase/string-templates"
|
||||||
|
import CodeEditor from "components/common/CodeEditor/CodeEditor.svelte"
|
||||||
|
import { EditorModes } from "components/common/CodeEditor"
|
||||||
|
import {
|
||||||
|
runtimeToReadableBinding,
|
||||||
|
getDatasourceForProvider,
|
||||||
|
} from "dataBinding"
|
||||||
|
import { tables, selectedScreen, selectedComponent } from "stores/builder"
|
||||||
|
import { getBindings } from "components/backend/DataTable/formula"
|
||||||
|
|
||||||
|
export let value
|
||||||
|
$: datasource = getDatasourceForProvider($selectedScreen, $selectedComponent)
|
||||||
|
$: tableId = datasource.tableId
|
||||||
|
$: table = $tables?.list?.find(table => table._id === tableId)
|
||||||
|
$: bindings = getBindings({ table })
|
||||||
|
|
||||||
|
$: readableBinding = runtimeToReadableBinding(bindings, value)
|
||||||
|
|
||||||
|
$: isJs = value?.startsWith?.("{{ js ")
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="editor">
|
||||||
|
<CodeEditor
|
||||||
|
readonly
|
||||||
|
readonlyLineNumbers
|
||||||
|
value={isJs ? decodeJSBinding(readableBinding) : readableBinding}
|
||||||
|
jsBindingWrapping={isJs}
|
||||||
|
mode={isJs ? EditorModes.JS : EditorModes.Handlebars}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.editor {
|
||||||
|
border: 1px solid var(--grey-2);
|
||||||
|
border-radius: 2px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,30 @@
|
||||||
|
<script>
|
||||||
|
export let truncate = false
|
||||||
|
export let noShrink = false
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<span class:truncate class:noShrink class="block">
|
||||||
|
<slot />
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.block {
|
||||||
|
font-style: italic;
|
||||||
|
border-radius: 1px;
|
||||||
|
padding: 0px 5px 0px 3px;
|
||||||
|
border-radius: 1px;
|
||||||
|
background-color: var(--grey-3);
|
||||||
|
color: var(--ink);
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.truncate {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.noShrink {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<li>
|
||||||
|
<div class="exampleLine">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.exampleLine {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,32 @@
|
||||||
|
<script>
|
||||||
|
import Section from "./Section.svelte"
|
||||||
|
|
||||||
|
export let heading
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Section>
|
||||||
|
<span class="exampleSectionHeading">
|
||||||
|
<slot name="heading">
|
||||||
|
{heading}
|
||||||
|
</slot>
|
||||||
|
</span>
|
||||||
|
<ul>
|
||||||
|
<slot />
|
||||||
|
</ul>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.exampleSectionHeading {
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
padding: 0 0 0 23px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,22 @@
|
||||||
|
<script>
|
||||||
|
export let value
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<pre class="pre">
|
||||||
|
{value}
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.pre {
|
||||||
|
border: 1px solid var(--grey-2);
|
||||||
|
border-radius: 2px;
|
||||||
|
overflow: hidden;
|
||||||
|
margin: 0;
|
||||||
|
margin-top: 3px;
|
||||||
|
padding: 4px;
|
||||||
|
border-radius: 3px;
|
||||||
|
width: 250px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,49 @@
|
||||||
|
<script>
|
||||||
|
export let name
|
||||||
|
export let value
|
||||||
|
export let truncate = false
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class:truncate class="property">
|
||||||
|
<span class="propertyName">
|
||||||
|
<slot name="name">
|
||||||
|
{name}
|
||||||
|
</slot>
|
||||||
|
</span>
|
||||||
|
<span class="propertyDivider">-</span>
|
||||||
|
<span class="propertyValue">
|
||||||
|
<slot>
|
||||||
|
{value}
|
||||||
|
</slot>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.property {
|
||||||
|
max-width: 100%;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.truncate {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.propertyName {
|
||||||
|
font-weight: 600;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.propertyDivider {
|
||||||
|
padding: 0 4px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.propertyValue {
|
||||||
|
word-break: break-word;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,11 @@
|
||||||
|
<div class="section">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.section {
|
||||||
|
line-height: 20px;
|
||||||
|
margin-bottom: 13px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,51 @@
|
||||||
|
<script>
|
||||||
|
import { onMount } from "svelte"
|
||||||
|
|
||||||
|
export let heading = ""
|
||||||
|
let body
|
||||||
|
|
||||||
|
const handleScroll = e => {
|
||||||
|
if (!body) return
|
||||||
|
|
||||||
|
body.scrollTo({ top: body.scrollTop + e.deltaY, behavior: "smooth" })
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
window.addEventListener("wheel", handleScroll)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("wheel", handleScroll)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="heading">
|
||||||
|
<span class="heading">
|
||||||
|
<slot name="heading">
|
||||||
|
{heading}
|
||||||
|
</slot>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="divider" />
|
||||||
|
<div bind:this={body} class="body">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.heading {
|
||||||
|
font-weight: 600;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
border-bottom: 1px solid var(--grey-4);
|
||||||
|
margin: 12px 0 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.body {
|
||||||
|
max-height: 300px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,8 @@
|
||||||
|
export { default as Subject } from "./Subject.svelte"
|
||||||
|
export { default as Property } from "./Property.svelte"
|
||||||
|
export { default as JSONValue } from "./JSONValue.svelte"
|
||||||
|
export { default as BindingValue } from "./BindingValue.svelte"
|
||||||
|
export { default as Section } from "./Section.svelte"
|
||||||
|
export { default as Block } from "./Block.svelte"
|
||||||
|
export { default as ExampleSection } from "./ExampleSection.svelte"
|
||||||
|
export { default as ExampleLine } from "./ExampleLine.svelte"
|
|
@ -0,0 +1,8 @@
|
||||||
|
export { default as Column } from "./Column.svelte"
|
||||||
|
export { default as NotRequired } from "./NotRequired.svelte"
|
||||||
|
export { default as StringsAsNumbers } from "./StringsAsNumbers.svelte"
|
||||||
|
export { default as Support } from "./Support.svelte"
|
||||||
|
export { default as DatesAsNumbers } from "./DatesAsNumbers.svelte"
|
||||||
|
export { default as ScalarJsonOnly } from "./ScalarJsonOnly.svelte"
|
||||||
|
export { default as StringsAsDates } from "./StringsAsDates.svelte"
|
||||||
|
export { default as NumbersAsDates } from "./NumbersAsDates.svelte"
|
|
@ -0,0 +1,102 @@
|
||||||
|
<script>
|
||||||
|
import DetailsModal from "./DetailsModal/index.svelte"
|
||||||
|
import {
|
||||||
|
messages as messageConstants,
|
||||||
|
getExplanationMessagesAndSupport,
|
||||||
|
getExplanationWithPresets,
|
||||||
|
} from "./explanation"
|
||||||
|
import {
|
||||||
|
StringAsDate,
|
||||||
|
NumberAsDate,
|
||||||
|
Column,
|
||||||
|
Support,
|
||||||
|
NotRequired,
|
||||||
|
StringAsNumber,
|
||||||
|
JSONPrimitivesOnly,
|
||||||
|
DateAsNumber,
|
||||||
|
} from "./lines"
|
||||||
|
import subjects from "./subjects"
|
||||||
|
import { appStore } from "stores/builder"
|
||||||
|
|
||||||
|
export let explanation
|
||||||
|
export let columnIcon
|
||||||
|
export let columnType
|
||||||
|
export let columnName
|
||||||
|
|
||||||
|
export let tableHref = () => {}
|
||||||
|
|
||||||
|
export let schema
|
||||||
|
|
||||||
|
$: explanationWithPresets = getExplanationWithPresets(
|
||||||
|
explanation,
|
||||||
|
$appStore.typeSupportPresets
|
||||||
|
)
|
||||||
|
let support
|
||||||
|
let messages = []
|
||||||
|
|
||||||
|
$: {
|
||||||
|
const explanationMessagesAndSupport = getExplanationMessagesAndSupport(
|
||||||
|
schema,
|
||||||
|
explanationWithPresets
|
||||||
|
)
|
||||||
|
support = explanationMessagesAndSupport.support
|
||||||
|
messages = explanationMessagesAndSupport.messages
|
||||||
|
}
|
||||||
|
|
||||||
|
let root = null
|
||||||
|
|
||||||
|
let detailsModalSubject = subjects.none
|
||||||
|
|
||||||
|
const setExplanationSubject = option => {
|
||||||
|
detailsModalSubject = option
|
||||||
|
root = root
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={root} class="tooltipContents">
|
||||||
|
<Column
|
||||||
|
{columnName}
|
||||||
|
{columnIcon}
|
||||||
|
{columnType}
|
||||||
|
{tableHref}
|
||||||
|
{setExplanationSubject}
|
||||||
|
/>
|
||||||
|
<Support {support} {setExplanationSubject} />
|
||||||
|
{#if messages.includes(messageConstants.stringAsNumber)}
|
||||||
|
<StringAsNumber {setExplanationSubject} />
|
||||||
|
{/if}
|
||||||
|
{#if messages.includes(messageConstants.notRequired)}
|
||||||
|
<NotRequired {setExplanationSubject} />
|
||||||
|
{/if}
|
||||||
|
{#if messages.includes(messageConstants.jsonPrimitivesOnly)}
|
||||||
|
<JSONPrimitivesOnly {setExplanationSubject} />
|
||||||
|
{/if}
|
||||||
|
{#if messages.includes(messageConstants.dateAsNumber)}
|
||||||
|
<DateAsNumber {setExplanationSubject} />
|
||||||
|
{/if}
|
||||||
|
{#if messages.includes(messageConstants.numberAsDate)}
|
||||||
|
<NumberAsDate {setExplanationSubject} />
|
||||||
|
{/if}
|
||||||
|
{#if messages.includes(messageConstants.stringAsDate)}
|
||||||
|
<StringAsDate {setExplanationSubject} />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if detailsModalSubject !== subjects.none}
|
||||||
|
<DetailsModal
|
||||||
|
{columnName}
|
||||||
|
anchor={root}
|
||||||
|
{schema}
|
||||||
|
subject={detailsModalSubject}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.tooltipContents {
|
||||||
|
max-width: 450px;
|
||||||
|
display: block;
|
||||||
|
padding: 20px 16px 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,100 @@
|
||||||
|
export const messages = {
|
||||||
|
jsonPrimitivesOnly: Symbol("explanation-json-primitives-only"),
|
||||||
|
stringAsNumber: Symbol("explanation-string-as-number"),
|
||||||
|
dateAsNumber: Symbol("explanation-date-as-number"),
|
||||||
|
numberAsDate: Symbol("explanation-number-as-date"),
|
||||||
|
stringAsDate: Symbol("explanation-string-as-date"),
|
||||||
|
notRequired: Symbol("explanation-not-required"),
|
||||||
|
contextError: Symbol("explanation-context-error"),
|
||||||
|
}
|
||||||
|
|
||||||
|
export const support = {
|
||||||
|
unsupported: Symbol("explanation-unsupported"),
|
||||||
|
partialSupport: Symbol("explanation-partialSupport"),
|
||||||
|
supported: Symbol("explanation-supported"),
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSupport = (type, explanation) => {
|
||||||
|
if (!explanation?.typeSupport) {
|
||||||
|
return support.supported
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
explanation?.typeSupport?.supported?.find(
|
||||||
|
mapping => mapping === type || mapping?.type === type
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return support.supported
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
explanation?.typeSupport?.partialSupport?.find(
|
||||||
|
mapping => mapping === type || mapping?.type === type
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return support.partialSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
return support.unsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSupportMessage = (type, explanation) => {
|
||||||
|
if (!explanation?.typeSupport) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const supported = explanation?.typeSupport?.supported?.find(
|
||||||
|
mapping => mapping?.type === type
|
||||||
|
)
|
||||||
|
if (supported) {
|
||||||
|
return messages[supported?.message]
|
||||||
|
}
|
||||||
|
|
||||||
|
const partialSupport = explanation?.typeSupport?.partialSupport?.find(
|
||||||
|
mapping => mapping?.type === type
|
||||||
|
)
|
||||||
|
if (partialSupport) {
|
||||||
|
return messages[partialSupport?.message]
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsupported = explanation?.typeSupport?.unsupported?.find(
|
||||||
|
mapping => mapping?.type === type
|
||||||
|
)
|
||||||
|
if (unsupported) {
|
||||||
|
return messages[unsupported?.message]
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getExplanationMessagesAndSupport = (fieldSchema, explanation) => {
|
||||||
|
try {
|
||||||
|
const explanationMessagesAndSupport = {
|
||||||
|
support: getSupport(fieldSchema.type, explanation),
|
||||||
|
messages: [getSupportMessage(fieldSchema.type, explanation)],
|
||||||
|
}
|
||||||
|
|
||||||
|
const isRequired = fieldSchema?.constraints?.presence?.allowEmpty === false
|
||||||
|
if (!isRequired) {
|
||||||
|
explanationMessagesAndSupport.messages.push(messages.notRequired)
|
||||||
|
}
|
||||||
|
|
||||||
|
return explanationMessagesAndSupport
|
||||||
|
} catch (e) {
|
||||||
|
return {
|
||||||
|
support: support.partialSupport,
|
||||||
|
messages: [messages.contextError],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getExplanationWithPresets = (explanation, presets) => {
|
||||||
|
if (explanation?.typeSupport?.preset) {
|
||||||
|
return {
|
||||||
|
...explanation,
|
||||||
|
typeSupport: presets[explanation?.typeSupport?.preset],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return explanation
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export { default as Explanation } from "./Explanation.svelte"
|
|
@ -0,0 +1,84 @@
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
Line,
|
||||||
|
InfoWord,
|
||||||
|
DocumentationLink,
|
||||||
|
Text,
|
||||||
|
Period,
|
||||||
|
} from "../typography"
|
||||||
|
import subjects from "../subjects"
|
||||||
|
|
||||||
|
export let columnName
|
||||||
|
export let columnIcon
|
||||||
|
export let columnType
|
||||||
|
export let tableHref
|
||||||
|
export let setExplanationSubject
|
||||||
|
|
||||||
|
const getDocLink = columnType => {
|
||||||
|
if (columnType === "Number") {
|
||||||
|
return "https://docs.budibase.com/docs/number"
|
||||||
|
}
|
||||||
|
if (columnType === "Text") {
|
||||||
|
return "https://docs.budibase.com/docs/text"
|
||||||
|
}
|
||||||
|
if (columnType === "Attachment") {
|
||||||
|
return "https://docs.budibase.com/docs/attachments"
|
||||||
|
}
|
||||||
|
if (columnType === "Multi-select") {
|
||||||
|
return "https://docs.budibase.com/docs/multi-select"
|
||||||
|
}
|
||||||
|
if (columnType === "JSON") {
|
||||||
|
return "https://docs.budibase.com/docs/json"
|
||||||
|
}
|
||||||
|
if (columnType === "Date/Time") {
|
||||||
|
return "https://docs.budibase.com/docs/datetime"
|
||||||
|
}
|
||||||
|
if (columnType === "User") {
|
||||||
|
return "https://docs.budibase.com/docs/user"
|
||||||
|
}
|
||||||
|
if (columnType === "QR") {
|
||||||
|
return "https://docs.budibase.com/docs/barcodeqr"
|
||||||
|
}
|
||||||
|
if (columnType === "Relationship") {
|
||||||
|
return "https://docs.budibase.com/docs/relationships"
|
||||||
|
}
|
||||||
|
if (columnType === "Formula") {
|
||||||
|
return "https://docs.budibase.com/docs/formula"
|
||||||
|
}
|
||||||
|
if (columnType === "Options") {
|
||||||
|
return "https://docs.budibase.com/docs/options"
|
||||||
|
}
|
||||||
|
if (columnType === "BigInt") {
|
||||||
|
// No BigInt docs
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
if (columnType === "Boolean") {
|
||||||
|
return "https://docs.budibase.com/docs/boolean-truefalse"
|
||||||
|
}
|
||||||
|
if (columnType === "Signature") {
|
||||||
|
// No Signature docs
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
$: docLink = getDocLink(columnType)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Line noWrap>
|
||||||
|
<InfoWord
|
||||||
|
on:mouseenter={() => setExplanationSubject(subjects.column)}
|
||||||
|
on:mouseleave={() => setExplanationSubject(subjects.none)}
|
||||||
|
href={tableHref}
|
||||||
|
text={columnName}
|
||||||
|
/>
|
||||||
|
<Text value=" is a " />
|
||||||
|
<DocumentationLink
|
||||||
|
disabled={docLink === null}
|
||||||
|
href={docLink}
|
||||||
|
icon={columnIcon}
|
||||||
|
text={`${columnType} column`}
|
||||||
|
/>
|
||||||
|
<Period />
|
||||||
|
</Line>
|
|
@ -0,0 +1,16 @@
|
||||||
|
<script>
|
||||||
|
import { Line, InfoWord, Text, Period } from "../typography"
|
||||||
|
import subjects from "../subjects"
|
||||||
|
|
||||||
|
export let setExplanationSubject
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Line>
|
||||||
|
<Text value="Will be converted to a " />
|
||||||
|
<InfoWord
|
||||||
|
on:mouseenter={() => setExplanationSubject(subjects.datesAsNumbers)}
|
||||||
|
on:mouseleave={() => setExplanationSubject(subjects.none)}
|
||||||
|
text="UNIX time value"
|
||||||
|
/>
|
||||||
|
<Period />
|
||||||
|
</Line>
|
|
@ -0,0 +1,21 @@
|
||||||
|
<script>
|
||||||
|
import { Line, InfoWord, Text, Period } from "../typography"
|
||||||
|
import subjects from "../subjects"
|
||||||
|
|
||||||
|
export let setExplanationSubject
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Line>
|
||||||
|
<InfoWord
|
||||||
|
on:mouseenter={() => setExplanationSubject(subjects.scalarJsonOnly)}
|
||||||
|
on:mouseleave={() => setExplanationSubject(subjects.none)}
|
||||||
|
>Scalar JSON values</InfoWord
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
value=" can be used with this input if their individual types are supported"
|
||||||
|
/>
|
||||||
|
<Period />
|
||||||
|
</Line>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
|
@ -0,0 +1,25 @@
|
||||||
|
<script>
|
||||||
|
import { Line, InfoWord, DocumentationLink, Space, Text } from "../typography"
|
||||||
|
import subjects from "../subjects"
|
||||||
|
|
||||||
|
export let setExplanationSubject
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Line>
|
||||||
|
<Text value="No " />
|
||||||
|
<InfoWord
|
||||||
|
on:mouseenter={() => setExplanationSubject(subjects.notRequired)}
|
||||||
|
on:mouseleave={() => setExplanationSubject(subjects.none)}
|
||||||
|
text="required"
|
||||||
|
/>
|
||||||
|
<Space />
|
||||||
|
<DocumentationLink
|
||||||
|
icon="DataUnavailable"
|
||||||
|
href="https://docs.budibase.com/docs/budibasedb#constraints"
|
||||||
|
text="Constraint"
|
||||||
|
/>
|
||||||
|
<Text value=", so values may be missing." />
|
||||||
|
</Line>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
|
@ -0,0 +1,16 @@
|
||||||
|
<script>
|
||||||
|
import { Line, InfoWord, Text, Period } from "../typography"
|
||||||
|
import subjects from "../subjects"
|
||||||
|
|
||||||
|
export let setExplanationSubject
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Line>
|
||||||
|
<Text value="Will be treated as a " />
|
||||||
|
<InfoWord
|
||||||
|
on:mouseenter={() => setExplanationSubject(subjects.numbersAsDates)}
|
||||||
|
on:mouseleave={() => setExplanationSubject(subjects.none)}
|
||||||
|
text="UNIX time value"
|
||||||
|
/>
|
||||||
|
<Period />
|
||||||
|
</Line>
|
|
@ -0,0 +1,16 @@
|
||||||
|
<script>
|
||||||
|
import { Line, InfoWord, Text, Period } from "../typography"
|
||||||
|
import subjects from "../subjects"
|
||||||
|
|
||||||
|
export let setExplanationSubject
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Line>
|
||||||
|
<Text value="Will be treated as a " />
|
||||||
|
<InfoWord
|
||||||
|
on:mouseenter={() => setExplanationSubject(subjects.stringsAsDates)}
|
||||||
|
on:mouseleave={() => setExplanationSubject(subjects.none)}
|
||||||
|
text="UNIX time or ISO 8601 value"
|
||||||
|
/>
|
||||||
|
<Period />
|
||||||
|
</Line>
|
|
@ -0,0 +1,16 @@
|
||||||
|
<script>
|
||||||
|
import { Line, InfoWord, Text, Period } from "../typography"
|
||||||
|
import subjects from "../subjects"
|
||||||
|
|
||||||
|
export let setExplanationSubject
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Line>
|
||||||
|
<Text value="Will be converted to a number, ignoring any " />
|
||||||
|
<InfoWord
|
||||||
|
on:mouseenter={() => setExplanationSubject(subjects.stringsAsNumbers)}
|
||||||
|
on:mouseleave={() => setExplanationSubject(subjects.none)}
|
||||||
|
text="non-numerical values"
|
||||||
|
/>
|
||||||
|
<Period />
|
||||||
|
</Line>
|
|
@ -0,0 +1,59 @@
|
||||||
|
<script>
|
||||||
|
import { Line, InfoWord, DocumentationLink, Text } from "../typography"
|
||||||
|
import subjects from "../subjects"
|
||||||
|
import * as explanation from "../explanation"
|
||||||
|
|
||||||
|
export let setExplanationSubject
|
||||||
|
export let support
|
||||||
|
|
||||||
|
const getIcon = support => {
|
||||||
|
if (support === explanation.support.unsupported) {
|
||||||
|
return "Alert"
|
||||||
|
} else if (support === explanation.support.supported) {
|
||||||
|
return "CheckmarkCircle"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "AlertCheck"
|
||||||
|
}
|
||||||
|
|
||||||
|
const getColor = support => {
|
||||||
|
if (support === explanation.support.unsupported) {
|
||||||
|
return "var(--red)"
|
||||||
|
} else if (support === explanation.support.supported) {
|
||||||
|
return "var(--green)"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "var(--yellow)"
|
||||||
|
}
|
||||||
|
|
||||||
|
const getText = support => {
|
||||||
|
if (support === explanation.support.unsupported) {
|
||||||
|
return "Not compatible"
|
||||||
|
} else if (support === explanation.support.supported) {
|
||||||
|
return "Compatible"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Partially compatible"
|
||||||
|
}
|
||||||
|
|
||||||
|
$: icon = getIcon(support)
|
||||||
|
$: color = getColor(support)
|
||||||
|
$: text = getText(support)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Line>
|
||||||
|
<InfoWord
|
||||||
|
on:mouseenter={() => setExplanationSubject(subjects.support)}
|
||||||
|
on:mouseleave={() => setExplanationSubject(subjects.none)}
|
||||||
|
{icon}
|
||||||
|
{color}
|
||||||
|
{text}
|
||||||
|
/>
|
||||||
|
<Text value=" with this " />
|
||||||
|
<DocumentationLink
|
||||||
|
href="https://docs.budibase.com/docs/chart"
|
||||||
|
icon="GraphPie"
|
||||||
|
text="Chart component"
|
||||||
|
/>
|
||||||
|
<Text value=" input." />
|
||||||
|
</Line>
|
|
@ -0,0 +1,8 @@
|
||||||
|
export { default as Column } from "./Column.svelte"
|
||||||
|
export { default as NotRequired } from "./NotRequired.svelte"
|
||||||
|
export { default as StringAsNumber } from "./StringAsNumber.svelte"
|
||||||
|
export { default as Support } from "./Support.svelte"
|
||||||
|
export { default as JSONPrimitivesOnly } from "./JSONPrimitivesOnly.svelte"
|
||||||
|
export { default as DateAsNumber } from "./DateAsNumber.svelte"
|
||||||
|
export { default as NumberAsDate } from "./NumberAsDate.svelte"
|
||||||
|
export { default as StringAsDate } from "./StringAsDate.svelte"
|
|
@ -0,0 +1,13 @@
|
||||||
|
const subjects = {
|
||||||
|
column: Symbol("details-modal-column"),
|
||||||
|
support: Symbol("details-modal-support"),
|
||||||
|
stringsAsNumbers: Symbol("details-modal-strings-as-numbers"),
|
||||||
|
datesAsNumbers: Symbol("details-modal-dates-as-numbers"),
|
||||||
|
numbersAsDates: Symbol("explanation-numbers-as-dates"),
|
||||||
|
stringsAsDates: Symbol("explanation-strings-as-dates"),
|
||||||
|
notRequired: Symbol("details-modal-not-required"),
|
||||||
|
scalarJsonOnly: Symbol("explanation-scalar-json-only"),
|
||||||
|
none: Symbol("details-modal-none"),
|
||||||
|
}
|
||||||
|
|
||||||
|
export default subjects
|
|
@ -0,0 +1,14 @@
|
||||||
|
<span class="comma">,</span>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.comma {
|
||||||
|
color: var(--grey-6);
|
||||||
|
font-size: 17px;
|
||||||
|
display: inline block;
|
||||||
|
margin-left: 2px;
|
||||||
|
margin-right: 2px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
position: relative;
|
||||||
|
bottom: 2px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,66 @@
|
||||||
|
<script>
|
||||||
|
import { Icon } from "@budibase/bbui"
|
||||||
|
|
||||||
|
export let icon
|
||||||
|
export let text
|
||||||
|
export let href
|
||||||
|
export let disabled = false
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<a
|
||||||
|
class:disabled
|
||||||
|
tabindex="0"
|
||||||
|
{href}
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
class="link"
|
||||||
|
>
|
||||||
|
<Icon size="XS" name={icon} />
|
||||||
|
<span class="text">
|
||||||
|
<slot>
|
||||||
|
{text}
|
||||||
|
</slot>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.text {
|
||||||
|
color: var(--ink);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
display: inline-flex;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 1px 0 2px;
|
||||||
|
filter: brightness(100%);
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
border-bottom: 1px solid var(--blue);
|
||||||
|
transition: filter 300ms;
|
||||||
|
margin-right: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link:hover {
|
||||||
|
filter: brightness(120%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.disabled {
|
||||||
|
filter: brightness(100%);
|
||||||
|
border-bottom: 1px solid var(--grey-6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.link :global(svg) {
|
||||||
|
margin-right: 3px;
|
||||||
|
color: var(--blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
.disabled :global(svg) {
|
||||||
|
color: var(--grey-6);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,78 @@
|
||||||
|
<script>
|
||||||
|
import { Icon } from "@budibase/bbui"
|
||||||
|
|
||||||
|
export let icon = null
|
||||||
|
export let color = null
|
||||||
|
export let text
|
||||||
|
export let href = null
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if href !== null}
|
||||||
|
<a
|
||||||
|
tabindex="0"
|
||||||
|
{href}
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
class="infoWord"
|
||||||
|
style:color
|
||||||
|
style:border-color={color}
|
||||||
|
on:mouseenter
|
||||||
|
on:mouseleave
|
||||||
|
>
|
||||||
|
{#if icon}
|
||||||
|
<Icon size="XS" name={icon} />
|
||||||
|
{/if}
|
||||||
|
<span class="text">
|
||||||
|
<slot>
|
||||||
|
{text}
|
||||||
|
</slot>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
{:else}
|
||||||
|
<div
|
||||||
|
role="tooltip"
|
||||||
|
class="infoWord"
|
||||||
|
style:color
|
||||||
|
style:border-color={color}
|
||||||
|
on:mouseenter
|
||||||
|
on:mouseleave
|
||||||
|
>
|
||||||
|
{#if icon}
|
||||||
|
<Icon size="XS" name={icon} />
|
||||||
|
{/if}
|
||||||
|
<span class="text">
|
||||||
|
<slot>
|
||||||
|
{text}
|
||||||
|
</slot>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.infoWord {
|
||||||
|
display: inline-flex;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 1px 0 2px;
|
||||||
|
filter: brightness(100%);
|
||||||
|
overflow: hidden;
|
||||||
|
transition: filter 300ms;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: 1px solid var(--grey-6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.infoWord:hover {
|
||||||
|
filter: brightness(120%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.infoWord :global(svg) {
|
||||||
|
color: inherit;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
color: var(--ink);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,41 @@
|
||||||
|
<script>
|
||||||
|
export let noWrap = false
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="line">
|
||||||
|
<span class="bullet">•</span>
|
||||||
|
<div class="content" class:noWrap>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.line {
|
||||||
|
color: var(--ink);
|
||||||
|
align-items: flex-start;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bullet {
|
||||||
|
color: var(--grey-6);
|
||||||
|
font-size: 17px;
|
||||||
|
display: inline block;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
line-height: 17px;
|
||||||
|
min-width: 0;
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
row-gap: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.noWrap {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,13 @@
|
||||||
|
<span class="period">.</span>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.period {
|
||||||
|
color: var(--grey-6);
|
||||||
|
font-size: 20px;
|
||||||
|
display: inline block;
|
||||||
|
margin-left: 2px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
position: relative;
|
||||||
|
bottom: 2px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<span class="space">{" "}</span>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.space {
|
||||||
|
white-space: pre;
|
||||||
|
width: 3px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,64 @@
|
||||||
|
<script>
|
||||||
|
import Comma from "./Comma.svelte"
|
||||||
|
import Period from "./Period.svelte"
|
||||||
|
import Space from "./Space.svelte"
|
||||||
|
|
||||||
|
export let value = null
|
||||||
|
|
||||||
|
const punctuation = [" ", ",", "."]
|
||||||
|
|
||||||
|
// TODO regex might work here now
|
||||||
|
const getWords = value => {
|
||||||
|
if (typeof value !== "string") {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const newWords = []
|
||||||
|
let lastIndex = 0
|
||||||
|
|
||||||
|
const makeWord = i => {
|
||||||
|
// No word to make, multiple spaces, spaces at start of text etc
|
||||||
|
if (i - lastIndex > 0) {
|
||||||
|
newWords.push(value.substring(lastIndex, i))
|
||||||
|
}
|
||||||
|
|
||||||
|
lastIndex = i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
value.split("").forEach((character, i) => {
|
||||||
|
if (punctuation.includes(character)) {
|
||||||
|
makeWord(i)
|
||||||
|
newWords.push(character)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
makeWord(value.length)
|
||||||
|
|
||||||
|
return newWords
|
||||||
|
}
|
||||||
|
|
||||||
|
$: words = getWords(value)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#each words as word}
|
||||||
|
{#if word === " "}
|
||||||
|
<Space />
|
||||||
|
{:else if word === ","}
|
||||||
|
<Comma />
|
||||||
|
{:else if word === "."}
|
||||||
|
<Period />
|
||||||
|
{:else}
|
||||||
|
<span class="text">
|
||||||
|
{word}
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.text {
|
||||||
|
/* invisible properties to match other inline text elements that do have borders. If we don't match here we run into subpixel issues */
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-bottom: 1px solid transparent;
|
||||||
|
padding: 1px 0 2px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,7 @@
|
||||||
|
export { default as Space } from "./Space.svelte"
|
||||||
|
export { default as Comma } from "./Comma.svelte"
|
||||||
|
export { default as Period } from "./Period.svelte"
|
||||||
|
export { default as Text } from "./Text.svelte"
|
||||||
|
export { default as InfoWord } from "./InfoWord.svelte"
|
||||||
|
export { default as DocumentationLink } from "./DocumentationLink.svelte"
|
||||||
|
export { default as Line } from "./Line.svelte"
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import EditComponentPopover from "../EditComponentPopover/EditComponentPopover.svelte"
|
import EditComponentPopover from "../EditComponentPopover.svelte"
|
||||||
import { Toggle, Icon } from "@budibase/bbui"
|
import { Toggle, Icon } from "@budibase/bbui"
|
||||||
import { createEventDispatcher } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
|
|
|
@ -41,6 +41,7 @@ export const FieldTypeToComponentMap = {
|
||||||
[FieldType.BOOLEAN]: "booleanfield",
|
[FieldType.BOOLEAN]: "booleanfield",
|
||||||
[FieldType.LONGFORM]: "longformfield",
|
[FieldType.LONGFORM]: "longformfield",
|
||||||
[FieldType.DATETIME]: "datetimefield",
|
[FieldType.DATETIME]: "datetimefield",
|
||||||
|
[FieldType.SIGNATURE_SINGLE]: "signaturesinglefield",
|
||||||
[FieldType.ATTACHMENTS]: "attachmentfield",
|
[FieldType.ATTACHMENTS]: "attachmentfield",
|
||||||
[FieldType.ATTACHMENT_SINGLE]: "attachmentsinglefield",
|
[FieldType.ATTACHMENT_SINGLE]: "attachmentsinglefield",
|
||||||
[FieldType.LINK]: "relationshipfield",
|
[FieldType.LINK]: "relationshipfield",
|
||||||
|
|
|
@ -1,12 +1,22 @@
|
||||||
<script>
|
<script>
|
||||||
import { Select } from "@budibase/bbui"
|
import { Select, ContextTooltip } from "@budibase/bbui"
|
||||||
import { getDatasourceForProvider, getSchemaForDatasource } from "dataBinding"
|
import { getDatasourceForProvider, getSchemaForDatasource } from "dataBinding"
|
||||||
import { selectedScreen } from "stores/builder"
|
import { selectedScreen } from "stores/builder"
|
||||||
import { createEventDispatcher } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
|
import { Explanation } from "./Explanation"
|
||||||
|
import { debounce } from "lodash"
|
||||||
|
import { params } from "@roxi/routify"
|
||||||
|
import { Constants } from "@budibase/frontend-core"
|
||||||
|
import { FIELDS } from "constants/backend"
|
||||||
|
|
||||||
export let componentInstance = {}
|
export let componentInstance = {}
|
||||||
export let value = ""
|
export let value = ""
|
||||||
export let placeholder
|
export let placeholder
|
||||||
|
export let explanation
|
||||||
|
|
||||||
|
let contextTooltipAnchor = null
|
||||||
|
let currentOption = null
|
||||||
|
let contextTooltipVisible = false
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
$: datasource = getDatasourceForProvider($selectedScreen, componentInstance)
|
$: datasource = getDatasourceForProvider($selectedScreen, componentInstance)
|
||||||
|
@ -32,6 +42,77 @@
|
||||||
boundValue = getValidValue(value.detail, options)
|
boundValue = getValidValue(value.detail, options)
|
||||||
dispatch("change", boundValue)
|
dispatch("change", boundValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updateTooltip = debounce((e, option) => {
|
||||||
|
if (option == null) {
|
||||||
|
contextTooltipVisible = false
|
||||||
|
} else {
|
||||||
|
contextTooltipAnchor = e?.target
|
||||||
|
currentOption = option
|
||||||
|
contextTooltipVisible = true
|
||||||
|
}
|
||||||
|
}, 200)
|
||||||
|
|
||||||
|
const onOptionMouseenter = (e, option) => {
|
||||||
|
updateTooltip(e, option)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onOptionMouseleave = e => {
|
||||||
|
updateTooltip(e, null)
|
||||||
|
}
|
||||||
|
const getOptionIcon = optionKey => {
|
||||||
|
const option = schema[optionKey]
|
||||||
|
if (!option) return ""
|
||||||
|
|
||||||
|
if (option.autocolumn) {
|
||||||
|
return "MagicWand"
|
||||||
|
}
|
||||||
|
const { type, subtype } = option
|
||||||
|
|
||||||
|
const result =
|
||||||
|
typeof Constants.TypeIconMap[type] === "object" && subtype
|
||||||
|
? Constants.TypeIconMap[type][subtype]
|
||||||
|
: Constants.TypeIconMap[type]
|
||||||
|
|
||||||
|
return result || "Text"
|
||||||
|
}
|
||||||
|
|
||||||
|
const getOptionIconTooltip = optionKey => {
|
||||||
|
const option = schema[optionKey]
|
||||||
|
|
||||||
|
const type = option?.type
|
||||||
|
const field = Object.values(FIELDS).find(f => f.type === type)
|
||||||
|
|
||||||
|
if (field) {
|
||||||
|
return field.name
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Select {placeholder} value={boundValue} on:change={onChange} {options} />
|
<Select
|
||||||
|
{placeholder}
|
||||||
|
value={boundValue}
|
||||||
|
on:change={onChange}
|
||||||
|
{options}
|
||||||
|
{onOptionMouseenter}
|
||||||
|
{onOptionMouseleave}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{#if explanation}
|
||||||
|
<ContextTooltip
|
||||||
|
visible={contextTooltipVisible}
|
||||||
|
anchor={contextTooltipAnchor}
|
||||||
|
offset={20}
|
||||||
|
>
|
||||||
|
<Explanation
|
||||||
|
tableHref={`/builder/app/${$params.application}/data/table/${datasource?.tableId}`}
|
||||||
|
schema={schema[currentOption]}
|
||||||
|
columnIcon={getOptionIcon(currentOption)}
|
||||||
|
columnName={currentOption}
|
||||||
|
columnType={getOptionIconTooltip(currentOption)}
|
||||||
|
{explanation}
|
||||||
|
/>
|
||||||
|
</ContextTooltip>
|
||||||
|
{/if}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import EditComponentPopover from "../EditComponentPopover/EditComponentPopover.svelte"
|
import EditComponentPopover from "../EditComponentPopover.svelte"
|
||||||
import { Toggle, Icon } from "@budibase/bbui"
|
import { Toggle, Icon } from "@budibase/bbui"
|
||||||
import { createEventDispatcher } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import EditComponentPopover from "../EditComponentPopover/EditComponentPopover.svelte"
|
import EditComponentPopover from "../EditComponentPopover.svelte"
|
||||||
import { Icon } from "@budibase/bbui"
|
import { Icon } from "@budibase/bbui"
|
||||||
import { setContext } from "svelte"
|
import { setContext } from "svelte"
|
||||||
import { writable } from "svelte/store"
|
import { writable } from "svelte/store"
|
||||||
|
|
|
@ -67,6 +67,7 @@ const toGridFormat = draggableListColumns => {
|
||||||
label: entry.label,
|
label: entry.label,
|
||||||
field: entry.field,
|
field: entry.field,
|
||||||
active: entry.active,
|
active: entry.active,
|
||||||
|
width: entry.width,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,6 +82,7 @@ const toDraggableListFormat = (gridFormatColumns, createComponent, schema) => {
|
||||||
field: column.field,
|
field: column.field,
|
||||||
label: column.label,
|
label: column.label,
|
||||||
columnType: schema[column.field].type,
|
columnType: schema[column.field].type,
|
||||||
|
width: column.width,
|
||||||
},
|
},
|
||||||
{}
|
{}
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,12 +1,22 @@
|
||||||
<script>
|
<script>
|
||||||
import { Multiselect } from "@budibase/bbui"
|
import { Multiselect, ContextTooltip } from "@budibase/bbui"
|
||||||
import { getDatasourceForProvider, getSchemaForDatasource } from "dataBinding"
|
import { getDatasourceForProvider, getSchemaForDatasource } from "dataBinding"
|
||||||
import { selectedScreen } from "stores/builder"
|
import { selectedScreen } from "stores/builder"
|
||||||
import { createEventDispatcher } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
|
import { Explanation } from "./Explanation"
|
||||||
|
import { FIELDS } from "constants/backend"
|
||||||
|
import { params } from "@roxi/routify"
|
||||||
|
import { debounce } from "lodash"
|
||||||
|
import { Constants } from "@budibase/frontend-core"
|
||||||
|
|
||||||
export let componentInstance = {}
|
export let componentInstance = {}
|
||||||
export let value = ""
|
export let value = ""
|
||||||
export let placeholder
|
export let placeholder
|
||||||
|
export let explanation
|
||||||
|
|
||||||
|
let contextTooltipAnchor = null
|
||||||
|
let currentOption = null
|
||||||
|
let contextTooltipVisible = false
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
$: datasource = getDatasourceForProvider($selectedScreen, componentInstance)
|
$: datasource = getDatasourceForProvider($selectedScreen, componentInstance)
|
||||||
|
@ -26,6 +36,84 @@
|
||||||
boundValue = getValidOptions(value.detail, options)
|
boundValue = getValidOptions(value.detail, options)
|
||||||
dispatch("change", boundValue)
|
dispatch("change", boundValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getOptionIcon = optionKey => {
|
||||||
|
const option = schema[optionKey]
|
||||||
|
if (!option) return ""
|
||||||
|
|
||||||
|
if (option.autocolumn) {
|
||||||
|
return "MagicWand"
|
||||||
|
}
|
||||||
|
const { type, subtype } = option
|
||||||
|
|
||||||
|
const result =
|
||||||
|
typeof Constants.TypeIconMap[type] === "object" && subtype
|
||||||
|
? Constants.TypeIconMap[type][subtype]
|
||||||
|
: Constants.TypeIconMap[type]
|
||||||
|
|
||||||
|
return result || "Text"
|
||||||
|
}
|
||||||
|
|
||||||
|
const getOptionIconTooltip = optionKey => {
|
||||||
|
const option = schema[optionKey]
|
||||||
|
|
||||||
|
const type = option?.type
|
||||||
|
const field = Object.values(FIELDS).find(f => f.type === type)
|
||||||
|
|
||||||
|
if (field) {
|
||||||
|
return field.name
|
||||||
|
} else if (type === "jsonarray") {
|
||||||
|
// `jsonarray` isn't present in the above FIELDS constant
|
||||||
|
|
||||||
|
return "JSON Array"
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateTooltip = debounce((e, option) => {
|
||||||
|
if (option == null) {
|
||||||
|
contextTooltipVisible = false
|
||||||
|
} else {
|
||||||
|
contextTooltipAnchor = e?.target
|
||||||
|
currentOption = option
|
||||||
|
contextTooltipVisible = true
|
||||||
|
}
|
||||||
|
}, 200)
|
||||||
|
|
||||||
|
const onOptionMouseenter = (e, option) => {
|
||||||
|
updateTooltip(e, option)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onOptionMouseleave = e => {
|
||||||
|
updateTooltip(e, null)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Multiselect {placeholder} value={boundValue} on:change={setValue} {options} />
|
<Multiselect
|
||||||
|
iconPosition="right"
|
||||||
|
{placeholder}
|
||||||
|
value={boundValue}
|
||||||
|
on:change={setValue}
|
||||||
|
{options}
|
||||||
|
align="right"
|
||||||
|
{onOptionMouseenter}
|
||||||
|
{onOptionMouseleave}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{#if explanation}
|
||||||
|
<ContextTooltip
|
||||||
|
visible={contextTooltipVisible}
|
||||||
|
anchor={contextTooltipAnchor}
|
||||||
|
offset={20}
|
||||||
|
>
|
||||||
|
<Explanation
|
||||||
|
tableHref={`/builder/app/${$params.application}/data/table/${datasource?.tableId}`}
|
||||||
|
schema={schema[currentOption]}
|
||||||
|
columnIcon={getOptionIcon(currentOption)}
|
||||||
|
columnName={currentOption}
|
||||||
|
columnType={getOptionIconTooltip(currentOption)}
|
||||||
|
{explanation}
|
||||||
|
/>
|
||||||
|
</ContextTooltip>
|
||||||
|
{/if}
|
||||||
|
|
|
@ -108,6 +108,8 @@
|
||||||
Constraints.MaxFileSize,
|
Constraints.MaxFileSize,
|
||||||
Constraints.MaxUploadSize,
|
Constraints.MaxUploadSize,
|
||||||
],
|
],
|
||||||
|
["attachment_single"]: [Constraints.Required, Constraints.MaxUploadSize],
|
||||||
|
["signature_single"]: [Constraints.Required],
|
||||||
["link"]: [
|
["link"]: [
|
||||||
Constraints.Required,
|
Constraints.Required,
|
||||||
Constraints.Contains,
|
Constraints.Contains,
|
||||||
|
|
|
@ -10,7 +10,6 @@ import {
|
||||||
NewFormSteps,
|
NewFormSteps,
|
||||||
} from "./steps"
|
} from "./steps"
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
import { customPositionHandler } from "components/design/settings/controls/EditComponentPopover"
|
|
||||||
|
|
||||||
const ONBOARDING_EVENT_PREFIX = "onboarding"
|
const ONBOARDING_EVENT_PREFIX = "onboarding"
|
||||||
|
|
||||||
|
@ -187,7 +186,6 @@ const getTours = () => {
|
||||||
tourEvent(TOUR_STEP_KEYS.BUILDER_FORM_CREATE_STEPS)
|
tourEvent(TOUR_STEP_KEYS.BUILDER_FORM_CREATE_STEPS)
|
||||||
builderStore.highlightSetting("steps", "info")
|
builderStore.highlightSetting("steps", "info")
|
||||||
},
|
},
|
||||||
positionHandler: customPositionHandler,
|
|
||||||
align: "left-outside",
|
align: "left-outside",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -203,7 +201,6 @@ const getTours = () => {
|
||||||
tourEvent(TOUR_STEP_KEYS.BUILDER_FORM_ROW_ID)
|
tourEvent(TOUR_STEP_KEYS.BUILDER_FORM_ROW_ID)
|
||||||
builderStore.highlightSetting("rowId", "info")
|
builderStore.highlightSetting("rowId", "info")
|
||||||
},
|
},
|
||||||
positionHandler: customPositionHandler,
|
|
||||||
align: "left-outside",
|
align: "left-outside",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -219,7 +216,6 @@ const getTours = () => {
|
||||||
tourEvent(TOUR_STEP_KEYS.BUILDER_FORM_VIEW_UPDATE_STEPS)
|
tourEvent(TOUR_STEP_KEYS.BUILDER_FORM_VIEW_UPDATE_STEPS)
|
||||||
builderStore.highlightSetting("steps", "info")
|
builderStore.highlightSetting("steps", "info")
|
||||||
},
|
},
|
||||||
positionHandler: customPositionHandler,
|
|
||||||
align: "left-outside",
|
align: "left-outside",
|
||||||
scrollIntoView: true,
|
scrollIntoView: true,
|
||||||
},
|
},
|
||||||
|
|
|
@ -127,6 +127,14 @@ export const FIELDS = {
|
||||||
presence: false,
|
presence: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
SIGNATURE_SINGLE: {
|
||||||
|
name: "Signature",
|
||||||
|
type: FieldType.SIGNATURE_SINGLE,
|
||||||
|
icon: "AnnotatePen",
|
||||||
|
constraints: {
|
||||||
|
presence: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
LINK: {
|
LINK: {
|
||||||
name: "Relationship",
|
name: "Relationship",
|
||||||
type: FieldType.LINK,
|
type: FieldType.LINK,
|
||||||
|
|
|
@ -105,10 +105,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
document.fonts.onloadingdone = e => {
|
|
||||||
builderStore.loadFonts(e.fontfaces)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasSynced && application) {
|
if (!hasSynced && application) {
|
||||||
try {
|
try {
|
||||||
await API.syncApp(application)
|
await API.syncApp(application)
|
||||||
|
@ -149,19 +145,17 @@
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<Tabs {selected} size="M">
|
<Tabs {selected} size="M">
|
||||||
{#key $builderStore?.fonts}
|
{#each $layout.children as { path, title }}
|
||||||
{#each $layout.children as { path, title }}
|
<TourWrap stepKeys={[`builder-${title}-section`]}>
|
||||||
<TourWrap stepKeys={[`builder-${title}-section`]}>
|
<Tab
|
||||||
<Tab
|
quiet
|
||||||
quiet
|
selected={$isActive(path)}
|
||||||
selected={$isActive(path)}
|
on:click={topItemNavigate(path)}
|
||||||
on:click={topItemNavigate(path)}
|
title={capitalise(title)}
|
||||||
title={capitalise(title)}
|
id={`builder-${title}-tab`}
|
||||||
id={`builder-${title}-tab`}
|
/>
|
||||||
/>
|
</TourWrap>
|
||||||
</TourWrap>
|
{/each}
|
||||||
{/each}
|
|
||||||
{/key}
|
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
<div class="topcenternav">
|
<div class="topcenternav">
|
||||||
|
|
|
@ -191,6 +191,9 @@
|
||||||
// Number fields
|
// Number fields
|
||||||
min: setting.min ?? null,
|
min: setting.min ?? null,
|
||||||
max: setting.max ?? null,
|
max: setting.max ?? null,
|
||||||
|
|
||||||
|
// Field select settings
|
||||||
|
explanation: setting.explanation,
|
||||||
}}
|
}}
|
||||||
{bindings}
|
{bindings}
|
||||||
{componentBindings}
|
{componentBindings}
|
||||||
|
|
|
@ -71,6 +71,7 @@
|
||||||
"multifieldselect",
|
"multifieldselect",
|
||||||
"s3upload",
|
"s3upload",
|
||||||
"codescanner",
|
"codescanner",
|
||||||
|
"signaturesinglefield",
|
||||||
"bbreferencesinglefield",
|
"bbreferencesinglefield",
|
||||||
"bbreferencefield"
|
"bbreferencefield"
|
||||||
]
|
]
|
||||||
|
|
|
@ -19,6 +19,7 @@ export const INITIAL_APP_META_STATE = {
|
||||||
showNotificationAction: false,
|
showNotificationAction: false,
|
||||||
sidePanel: false,
|
sidePanel: false,
|
||||||
},
|
},
|
||||||
|
typeSupportPresets: {},
|
||||||
features: {
|
features: {
|
||||||
componentValidation: false,
|
componentValidation: false,
|
||||||
disableUserMetadata: false,
|
disableUserMetadata: false,
|
||||||
|
@ -79,6 +80,13 @@ export class AppMetaStore extends BudiStore {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
syncClientTypeSupportPresets(typeSupportPresets) {
|
||||||
|
this.update(state => ({
|
||||||
|
...state,
|
||||||
|
typeSupportPresets,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
async syncAppRoutes() {
|
async syncAppRoutes() {
|
||||||
const resp = await API.fetchAppRoutes()
|
const resp = await API.fetchAppRoutes()
|
||||||
this.update(state => ({
|
this.update(state => ({
|
||||||
|
|
|
@ -82,6 +82,7 @@ const automationActions = store => ({
|
||||||
steps: [],
|
steps: [],
|
||||||
trigger,
|
trigger,
|
||||||
},
|
},
|
||||||
|
disabled: false,
|
||||||
}
|
}
|
||||||
const response = await store.actions.save(automation)
|
const response = await store.actions.save(automation)
|
||||||
await store.actions.fetch()
|
await store.actions.fetch()
|
||||||
|
@ -134,6 +135,28 @@ const automationActions = store => ({
|
||||||
})
|
})
|
||||||
await store.actions.fetch()
|
await store.actions.fetch()
|
||||||
},
|
},
|
||||||
|
toggleDisabled: async automationId => {
|
||||||
|
let automation
|
||||||
|
try {
|
||||||
|
automation = store.actions.getDefinition(automationId)
|
||||||
|
if (!automation) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
automation.disabled = !automation.disabled
|
||||||
|
await store.actions.save(automation)
|
||||||
|
notifications.success(
|
||||||
|
`Automation ${
|
||||||
|
automation.disabled ? "enabled" : "disabled"
|
||||||
|
} successfully`
|
||||||
|
)
|
||||||
|
} catch (error) {
|
||||||
|
notifications.error(
|
||||||
|
`Error ${
|
||||||
|
automation && automation.disabled ? "enabling" : "disabling"
|
||||||
|
} automation`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
updateBlockInputs: async (block, data) => {
|
updateBlockInputs: async (block, data) => {
|
||||||
// Create new modified block
|
// Create new modified block
|
||||||
let newBlock = {
|
let newBlock = {
|
||||||
|
|
|
@ -14,7 +14,6 @@ export const INITIAL_BUILDER_STATE = {
|
||||||
tourKey: null,
|
tourKey: null,
|
||||||
tourStepKey: null,
|
tourStepKey: null,
|
||||||
hoveredComponentId: null,
|
hoveredComponentId: null,
|
||||||
fonts: null,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BuilderStore extends BudiStore {
|
export class BuilderStore extends BudiStore {
|
||||||
|
@ -37,16 +36,6 @@ export class BuilderStore extends BudiStore {
|
||||||
this.websocket
|
this.websocket
|
||||||
}
|
}
|
||||||
|
|
||||||
loadFonts(fontFaces) {
|
|
||||||
const ff = fontFaces.map(
|
|
||||||
fontFace => `${fontFace.family}-${fontFace.weight}`
|
|
||||||
)
|
|
||||||
this.update(state => ({
|
|
||||||
...state,
|
|
||||||
fonts: [...(state.fonts || []), ...ff],
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
init(app) {
|
init(app) {
|
||||||
if (!app?.appId) {
|
if (!app?.appId) {
|
||||||
console.error("BuilderStore: No appId supplied for websocket")
|
console.error("BuilderStore: No appId supplied for websocket")
|
||||||
|
|
|
@ -108,6 +108,7 @@ export class ComponentStore extends BudiStore {
|
||||||
|
|
||||||
// Sync client features to app store
|
// Sync client features to app store
|
||||||
appStore.syncClientFeatures(components.features)
|
appStore.syncClientFeatures(components.features)
|
||||||
|
appStore.syncClientTypeSupportPresets(components?.typeSupportPresets ?? {})
|
||||||
|
|
||||||
return components
|
return components
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,6 +91,14 @@ describe("Application Meta Store", () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("Sync type support information to state", async ctx => {
|
||||||
|
ctx.test.appStore.syncClientTypeSupportPresets({ preset: "information" })
|
||||||
|
|
||||||
|
expect(ctx.test.store.typeSupportPresets).toStrictEqual({
|
||||||
|
preset: "information",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
it("Sync component feature flags to state", async ctx => {
|
it("Sync component feature flags to state", async ctx => {
|
||||||
ctx.test.appStore.syncClientFeatures(clientFeaturesResp)
|
ctx.test.appStore.syncClientFeatures(clientFeaturesResp)
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ vi.mock("stores/builder", async () => {
|
||||||
update: mockAppStore.update,
|
update: mockAppStore.update,
|
||||||
set: mockAppStore.set,
|
set: mockAppStore.set,
|
||||||
syncClientFeatures: vi.fn(),
|
syncClientFeatures: vi.fn(),
|
||||||
|
syncClientTypeSupportPresets: vi.fn(),
|
||||||
}
|
}
|
||||||
const mockTableStore = writable()
|
const mockTableStore = writable()
|
||||||
const tables = {
|
const tables = {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
process.env.DISABLE_PINO_LOGGER = "1"
|
||||||
process.env.NO_JS = "1"
|
process.env.NO_JS = "1"
|
||||||
process.env.JS_BCRYPT = "1"
|
process.env.JS_BCRYPT = "1"
|
||||||
process.env.DISABLE_JWT_WARNING = "1"
|
process.env.DISABLE_JWT_WARNING = "1"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
process.env.DISABLE_PINO_LOGGER = "1"
|
// have to import this before anything else
|
||||||
import "./environment"
|
import "./environment"
|
||||||
import { getCommands } from "./options"
|
import { getCommands } from "./options"
|
||||||
import { Command } from "commander"
|
import { Command } from "commander"
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
dir="$(dirname -- "$(readlink -f "${BASH_SOURCE}")")"
|
dir="$(dirname -- "$(readlink -f "${BASH_SOURCE}")")"
|
||||||
${dir}/node_modules/ts-node/dist/bin.js ${dir}/src/index.ts $@
|
${dir}/../../node_modules/ts-node/dist/bin.js ${dir}/src/index.ts $@
|
||||||
|
|
|
@ -13,6 +13,42 @@
|
||||||
"sidePanel": true,
|
"sidePanel": true,
|
||||||
"skeletonLoader": true
|
"skeletonLoader": true
|
||||||
},
|
},
|
||||||
|
"typeSupportPresets": {
|
||||||
|
"numberLike": {
|
||||||
|
"supported": ["number", "boolean"],
|
||||||
|
"partialSupport": [
|
||||||
|
{ "type": "longform", "message": "stringAsNumber" },
|
||||||
|
{ "type": "string", "message": "stringAsNumber" },
|
||||||
|
{ "type": "bigint", "message": "stringAsNumber" },
|
||||||
|
{ "type": "options", "message": "stringAsNumber" },
|
||||||
|
{ "type": "formula", "message": "stringAsNumber" },
|
||||||
|
{ "type": "datetime", "message": "dateAsNumber"}
|
||||||
|
],
|
||||||
|
"unsupported": [
|
||||||
|
{ "type": "json", "message": "jsonPrimitivesOnly" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"stringLike": {
|
||||||
|
"supported": ["string", "number", "bigint", "options", "longform", "boolean", "datetime"],
|
||||||
|
"unsupported": [
|
||||||
|
{ "type": "json", "message": "jsonPrimitivesOnly" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"datetimeLike": {
|
||||||
|
"supported": ["datetime"],
|
||||||
|
"partialSupport": [
|
||||||
|
{ "type": "longform", "message": "stringAsDate" },
|
||||||
|
{ "type": "string", "message": "stringAsDate" },
|
||||||
|
{ "type": "options", "message": "stringAsDate" },
|
||||||
|
{ "type": "formula", "message": "stringAsDate" },
|
||||||
|
{ "type": "bigint", "message": "stringAsDate" },
|
||||||
|
{ "type": "number", "message": "numberAsDate"}
|
||||||
|
],
|
||||||
|
"unsupported": [
|
||||||
|
{ "type": "json", "message": "jsonPrimitivesOnly" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"layout": {
|
"layout": {
|
||||||
"name": "Layout",
|
"name": "Layout",
|
||||||
"description": "This component is specific only to layouts",
|
"description": "This component is specific only to layouts",
|
||||||
|
@ -1602,6 +1638,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"bar": {
|
"bar": {
|
||||||
|
"documentationLink": "https://docs.budibase.com/docs/bar-chart",
|
||||||
"name": "Bar Chart",
|
"name": "Bar Chart",
|
||||||
"description": "Bar chart",
|
"description": "Bar chart",
|
||||||
"icon": "GraphBarVertical",
|
"icon": "GraphBarVertical",
|
||||||
|
@ -1626,6 +1663,11 @@
|
||||||
"label": "Label column",
|
"label": "Label column",
|
||||||
"key": "labelColumn",
|
"key": "labelColumn",
|
||||||
"dependsOn": "dataProvider",
|
"dependsOn": "dataProvider",
|
||||||
|
"explanation": {
|
||||||
|
"typeSupport": {
|
||||||
|
"preset": "stringLike"
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1633,6 +1675,11 @@
|
||||||
"label": "Data columns",
|
"label": "Data columns",
|
||||||
"key": "valueColumns",
|
"key": "valueColumns",
|
||||||
"dependsOn": "dataProvider",
|
"dependsOn": "dataProvider",
|
||||||
|
"explanation": {
|
||||||
|
"typeSupport": {
|
||||||
|
"preset": "numberLike"
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1760,6 +1807,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"line": {
|
"line": {
|
||||||
|
"documentationLink": "https://docs.budibase.com/docs/line-chart",
|
||||||
"name": "Line Chart",
|
"name": "Line Chart",
|
||||||
"description": "Line chart",
|
"description": "Line chart",
|
||||||
"icon": "GraphTrend",
|
"icon": "GraphTrend",
|
||||||
|
@ -1784,6 +1832,11 @@
|
||||||
"label": "Label column",
|
"label": "Label column",
|
||||||
"key": "labelColumn",
|
"key": "labelColumn",
|
||||||
"dependsOn": "dataProvider",
|
"dependsOn": "dataProvider",
|
||||||
|
"explanation": {
|
||||||
|
"typeSupport": {
|
||||||
|
"preset": "stringLike"
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1791,6 +1844,11 @@
|
||||||
"label": "Data columns",
|
"label": "Data columns",
|
||||||
"key": "valueColumns",
|
"key": "valueColumns",
|
||||||
"dependsOn": "dataProvider",
|
"dependsOn": "dataProvider",
|
||||||
|
"explanation": {
|
||||||
|
"typeSupport": {
|
||||||
|
"preset": "numberLike"
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1913,6 +1971,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"area": {
|
"area": {
|
||||||
|
"documentationLink": "https://docs.budibase.com/docs/area-chart",
|
||||||
"name": "Area Chart",
|
"name": "Area Chart",
|
||||||
"description": "Line chart",
|
"description": "Line chart",
|
||||||
"icon": "GraphAreaStacked",
|
"icon": "GraphAreaStacked",
|
||||||
|
@ -1937,6 +1996,11 @@
|
||||||
"label": "Label column",
|
"label": "Label column",
|
||||||
"key": "labelColumn",
|
"key": "labelColumn",
|
||||||
"dependsOn": "dataProvider",
|
"dependsOn": "dataProvider",
|
||||||
|
"explanation": {
|
||||||
|
"typeSupport": {
|
||||||
|
"preset": "stringLike"
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1944,6 +2008,11 @@
|
||||||
"label": "Data columns",
|
"label": "Data columns",
|
||||||
"key": "valueColumns",
|
"key": "valueColumns",
|
||||||
"dependsOn": "dataProvider",
|
"dependsOn": "dataProvider",
|
||||||
|
"explanation": {
|
||||||
|
"typeSupport": {
|
||||||
|
"preset": "numberLike"
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -2078,6 +2147,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"pie": {
|
"pie": {
|
||||||
|
"documentationLink": "https://docs.budibase.com/docs/pie-donut-chart",
|
||||||
"name": "Pie Chart",
|
"name": "Pie Chart",
|
||||||
"description": "Pie chart",
|
"description": "Pie chart",
|
||||||
"icon": "GraphPie",
|
"icon": "GraphPie",
|
||||||
|
@ -2102,13 +2172,23 @@
|
||||||
"label": "Label column",
|
"label": "Label column",
|
||||||
"key": "labelColumn",
|
"key": "labelColumn",
|
||||||
"dependsOn": "dataProvider",
|
"dependsOn": "dataProvider",
|
||||||
|
"explanation": {
|
||||||
|
"typeSupport": {
|
||||||
|
"preset": "stringLike"
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "field",
|
"type": "field",
|
||||||
"label": "Data columns",
|
"label": "Data column",
|
||||||
"key": "valueColumn",
|
"key": "valueColumn",
|
||||||
"dependsOn": "dataProvider",
|
"dependsOn": "dataProvider",
|
||||||
|
"explanation": {
|
||||||
|
"typeSupport": {
|
||||||
|
"preset": "numberLike"
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -2207,6 +2287,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"donut": {
|
"donut": {
|
||||||
|
"documentationLink": "https://docs.budibase.com/docs/pie-donut-chart",
|
||||||
"name": "Donut Chart",
|
"name": "Donut Chart",
|
||||||
"description": "Donut chart",
|
"description": "Donut chart",
|
||||||
"icon": "GraphDonut",
|
"icon": "GraphDonut",
|
||||||
|
@ -2231,6 +2312,11 @@
|
||||||
"label": "Label column",
|
"label": "Label column",
|
||||||
"key": "labelColumn",
|
"key": "labelColumn",
|
||||||
"dependsOn": "dataProvider",
|
"dependsOn": "dataProvider",
|
||||||
|
"explanation": {
|
||||||
|
"typeSupport": {
|
||||||
|
"preset": "stringLike"
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -2238,6 +2324,11 @@
|
||||||
"label": "Data columns",
|
"label": "Data columns",
|
||||||
"key": "valueColumn",
|
"key": "valueColumn",
|
||||||
"dependsOn": "dataProvider",
|
"dependsOn": "dataProvider",
|
||||||
|
"explanation": {
|
||||||
|
"typeSupport": {
|
||||||
|
"preset": "numberLike"
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -2336,6 +2427,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"candlestick": {
|
"candlestick": {
|
||||||
|
"documentationLink": "https://docs.budibase.com/docs/candlestick-chart",
|
||||||
"name": "Candlestick Chart",
|
"name": "Candlestick Chart",
|
||||||
"description": "Candlestick chart",
|
"description": "Candlestick chart",
|
||||||
"icon": "GraphBarVerticalStacked",
|
"icon": "GraphBarVerticalStacked",
|
||||||
|
@ -2360,6 +2452,11 @@
|
||||||
"label": "Date column",
|
"label": "Date column",
|
||||||
"key": "dateColumn",
|
"key": "dateColumn",
|
||||||
"dependsOn": "dataProvider",
|
"dependsOn": "dataProvider",
|
||||||
|
"explanation": {
|
||||||
|
"typeSupport": {
|
||||||
|
"preset": "datetimeLike"
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -2367,6 +2464,11 @@
|
||||||
"label": "Open column",
|
"label": "Open column",
|
||||||
"key": "openColumn",
|
"key": "openColumn",
|
||||||
"dependsOn": "dataProvider",
|
"dependsOn": "dataProvider",
|
||||||
|
"explanation": {
|
||||||
|
"typeSupport": {
|
||||||
|
"preset": "numberLike"
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -2374,6 +2476,11 @@
|
||||||
"label": "Close column",
|
"label": "Close column",
|
||||||
"key": "closeColumn",
|
"key": "closeColumn",
|
||||||
"dependsOn": "dataProvider",
|
"dependsOn": "dataProvider",
|
||||||
|
"explanation": {
|
||||||
|
"typeSupport": {
|
||||||
|
"preset": "numberLike"
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -2381,6 +2488,11 @@
|
||||||
"label": "High column",
|
"label": "High column",
|
||||||
"key": "highColumn",
|
"key": "highColumn",
|
||||||
"dependsOn": "dataProvider",
|
"dependsOn": "dataProvider",
|
||||||
|
"explanation": {
|
||||||
|
"typeSupport": {
|
||||||
|
"preset": "numberLike"
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -2388,6 +2500,11 @@
|
||||||
"label": "Low column",
|
"label": "Low column",
|
||||||
"key": "lowColumn",
|
"key": "lowColumn",
|
||||||
"dependsOn": "dataProvider",
|
"dependsOn": "dataProvider",
|
||||||
|
"explanation": {
|
||||||
|
"typeSupport": {
|
||||||
|
"preset": "numberLike"
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -2427,6 +2544,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"histogram": {
|
"histogram": {
|
||||||
|
"documentationLink": "https://docs.budibase.com/docs/histogram-chart",
|
||||||
"name": "Histogram Chart",
|
"name": "Histogram Chart",
|
||||||
"description": "Histogram chart",
|
"description": "Histogram chart",
|
||||||
"icon": "Histogram",
|
"icon": "Histogram",
|
||||||
|
@ -2434,7 +2552,6 @@
|
||||||
"width": 600,
|
"width": 600,
|
||||||
"height": 400
|
"height": 400
|
||||||
},
|
},
|
||||||
"requiredAncestors": ["dataprovider"],
|
|
||||||
"settings": [
|
"settings": [
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -2452,6 +2569,11 @@
|
||||||
"label": "Data column",
|
"label": "Data column",
|
||||||
"key": "valueColumn",
|
"key": "valueColumn",
|
||||||
"dependsOn": "dataProvider",
|
"dependsOn": "dataProvider",
|
||||||
|
"explanation": {
|
||||||
|
"typeSupport": {
|
||||||
|
"preset": "numberLike"
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -2746,6 +2868,14 @@
|
||||||
"type": "plainText",
|
"type": "plainText",
|
||||||
"label": "Label",
|
"label": "Label",
|
||||||
"key": "label"
|
"key": "label"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "number",
|
||||||
|
"label": "Initial width",
|
||||||
|
"key": "width",
|
||||||
|
"placeholder": "Auto",
|
||||||
|
"min": 80,
|
||||||
|
"max": 9999
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -4107,6 +4237,55 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"signaturesinglefield": {
|
||||||
|
"name": "Signature",
|
||||||
|
"icon": "AnnotatePen",
|
||||||
|
"styles": ["size"],
|
||||||
|
"size": {
|
||||||
|
"width": 400,
|
||||||
|
"height": 50
|
||||||
|
},
|
||||||
|
"settings": [
|
||||||
|
{
|
||||||
|
"type": "field/signature_single",
|
||||||
|
"label": "Field",
|
||||||
|
"key": "field",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"label": "Label",
|
||||||
|
"key": "label"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"label": "Help text",
|
||||||
|
"key": "helpText"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"label": "Disabled",
|
||||||
|
"key": "disabled",
|
||||||
|
"defaultValue": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "event",
|
||||||
|
"label": "On change",
|
||||||
|
"key": "onChange",
|
||||||
|
"context": [
|
||||||
|
{
|
||||||
|
"label": "Field Value",
|
||||||
|
"key": "value"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "validation/signature_single",
|
||||||
|
"label": "Validation",
|
||||||
|
"key": "validation"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"embeddedmap": {
|
"embeddedmap": {
|
||||||
"name": "Embedded Map",
|
"name": "Embedded Map",
|
||||||
"icon": "Location",
|
"icon": "Location",
|
||||||
|
@ -4372,7 +4551,7 @@
|
||||||
"defaultValue": false
|
"defaultValue": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "validation/attachment",
|
"type": "validation/attachment_single",
|
||||||
"label": "Validation",
|
"label": "Validation",
|
||||||
"key": "validation"
|
"key": "validation"
|
||||||
},
|
},
|
||||||
|
@ -5256,6 +5435,11 @@
|
||||||
"label": "Label column",
|
"label": "Label column",
|
||||||
"key": "labelColumn",
|
"key": "labelColumn",
|
||||||
"dependsOn": "dataSource",
|
"dependsOn": "dataSource",
|
||||||
|
"explanation": {
|
||||||
|
"typeSupport": {
|
||||||
|
"preset": "stringLike"
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -5263,6 +5447,11 @@
|
||||||
"label": "Data column",
|
"label": "Data column",
|
||||||
"key": "valueColumn",
|
"key": "valueColumn",
|
||||||
"dependsOn": "dataSource",
|
"dependsOn": "dataSource",
|
||||||
|
"explanation": {
|
||||||
|
"typeSupport": {
|
||||||
|
"preset": "numberLike"
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": true
|
"required": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -5281,6 +5470,11 @@
|
||||||
"label": "Label column",
|
"label": "Label column",
|
||||||
"key": "labelColumn",
|
"key": "labelColumn",
|
||||||
"dependsOn": "dataSource",
|
"dependsOn": "dataSource",
|
||||||
|
"explanation": {
|
||||||
|
"typeSupport": {
|
||||||
|
"preset": "stringLike"
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -5288,6 +5482,11 @@
|
||||||
"label": "Data column",
|
"label": "Data column",
|
||||||
"key": "valueColumn",
|
"key": "valueColumn",
|
||||||
"dependsOn": "dataSource",
|
"dependsOn": "dataSource",
|
||||||
|
"explanation": {
|
||||||
|
"typeSupport": {
|
||||||
|
"preset": "numberLike"
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": true
|
"required": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -5306,6 +5505,11 @@
|
||||||
"label": "Label column",
|
"label": "Label column",
|
||||||
"key": "labelColumn",
|
"key": "labelColumn",
|
||||||
"dependsOn": "dataSource",
|
"dependsOn": "dataSource",
|
||||||
|
"explanation": {
|
||||||
|
"typeSupport": {
|
||||||
|
"preset": "stringLike"
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -5313,6 +5517,11 @@
|
||||||
"label": "Data columns",
|
"label": "Data columns",
|
||||||
"key": "valueColumns",
|
"key": "valueColumns",
|
||||||
"dependsOn": "dataSource",
|
"dependsOn": "dataSource",
|
||||||
|
"explanation": {
|
||||||
|
"typeSupport": {
|
||||||
|
"preset": "numberLike"
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -5360,6 +5569,11 @@
|
||||||
"label": "Value column",
|
"label": "Value column",
|
||||||
"key": "valueColumn",
|
"key": "valueColumn",
|
||||||
"dependsOn": "dataSource",
|
"dependsOn": "dataSource",
|
||||||
|
"explanation": {
|
||||||
|
"typeSupport": {
|
||||||
|
"preset": "numberLike"
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -5401,6 +5615,11 @@
|
||||||
"label": "Label column",
|
"label": "Label column",
|
||||||
"key": "labelColumn",
|
"key": "labelColumn",
|
||||||
"dependsOn": "dataSource",
|
"dependsOn": "dataSource",
|
||||||
|
"explanation": {
|
||||||
|
"typeSupport": {
|
||||||
|
"preset": "stringLike"
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -5408,6 +5627,11 @@
|
||||||
"label": "Data columns",
|
"label": "Data columns",
|
||||||
"key": "valueColumns",
|
"key": "valueColumns",
|
||||||
"dependsOn": "dataSource",
|
"dependsOn": "dataSource",
|
||||||
|
"explanation": {
|
||||||
|
"typeSupport": {
|
||||||
|
"preset": "numberLike"
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -5450,6 +5674,11 @@
|
||||||
"label": "Label columns",
|
"label": "Label columns",
|
||||||
"key": "labelColumn",
|
"key": "labelColumn",
|
||||||
"dependsOn": "dataSource",
|
"dependsOn": "dataSource",
|
||||||
|
"explanation": {
|
||||||
|
"typeSupport": {
|
||||||
|
"preset": "stringLike"
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -5457,6 +5686,11 @@
|
||||||
"label": "Data columns",
|
"label": "Data columns",
|
||||||
"key": "valueColumns",
|
"key": "valueColumns",
|
||||||
"dependsOn": "dataSource",
|
"dependsOn": "dataSource",
|
||||||
|
"explanation": {
|
||||||
|
"typeSupport": {
|
||||||
|
"preset": "numberLike"
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -5511,6 +5745,11 @@
|
||||||
"label": "Date column",
|
"label": "Date column",
|
||||||
"key": "dateColumn",
|
"key": "dateColumn",
|
||||||
"dependsOn": "dataSource",
|
"dependsOn": "dataSource",
|
||||||
|
"explanation": {
|
||||||
|
"typeSupport": {
|
||||||
|
"preset": "datetimeLike"
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -5518,6 +5757,11 @@
|
||||||
"label": "Open column",
|
"label": "Open column",
|
||||||
"key": "openColumn",
|
"key": "openColumn",
|
||||||
"dependsOn": "dataSource",
|
"dependsOn": "dataSource",
|
||||||
|
"explanation": {
|
||||||
|
"typeSupport": {
|
||||||
|
"preset": "numberLike"
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -5525,6 +5769,11 @@
|
||||||
"label": "Close column",
|
"label": "Close column",
|
||||||
"key": "closeColumn",
|
"key": "closeColumn",
|
||||||
"dependsOn": "dataSource",
|
"dependsOn": "dataSource",
|
||||||
|
"explanation": {
|
||||||
|
"typeSupport": {
|
||||||
|
"preset": "numberLike"
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -5532,6 +5781,11 @@
|
||||||
"label": "High column",
|
"label": "High column",
|
||||||
"key": "highColumn",
|
"key": "highColumn",
|
||||||
"dependsOn": "dataSource",
|
"dependsOn": "dataSource",
|
||||||
|
"explanation": {
|
||||||
|
"typeSupport": {
|
||||||
|
"preset": "numberLike"
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -5539,6 +5793,11 @@
|
||||||
"label": "Low column",
|
"label": "Low column",
|
||||||
"key": "lowColumn",
|
"key": "lowColumn",
|
||||||
"dependsOn": "dataSource",
|
"dependsOn": "dataSource",
|
||||||
|
"explanation": {
|
||||||
|
"typeSupport": {
|
||||||
|
"preset": "numberLike"
|
||||||
|
}
|
||||||
|
},
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
"@budibase/string-templates": "0.0.0",
|
"@budibase/string-templates": "0.0.0",
|
||||||
"@budibase/types": "0.0.0",
|
"@budibase/types": "0.0.0",
|
||||||
"@spectrum-css/card": "3.0.3",
|
"@spectrum-css/card": "3.0.3",
|
||||||
"apexcharts": "^3.22.1",
|
"apexcharts": "^3.48.0",
|
||||||
"dayjs": "^1.10.8",
|
"dayjs": "^1.10.8",
|
||||||
"downloadjs": "1.4.7",
|
"downloadjs": "1.4.7",
|
||||||
"html5-qrcode": "^2.2.1",
|
"html5-qrcode": "^2.2.1",
|
||||||
|
@ -33,8 +33,8 @@
|
||||||
"sanitize-html": "^2.7.0",
|
"sanitize-html": "^2.7.0",
|
||||||
"screenfull": "^6.0.1",
|
"screenfull": "^6.0.1",
|
||||||
"shortid": "^2.2.15",
|
"shortid": "^2.2.15",
|
||||||
"svelte-apexcharts": "^1.0.2",
|
"svelte-spa-router": "^4.0.1",
|
||||||
"svelte-spa-router": "^4.0.1"
|
"atrament": "^4.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-alias": "^5.1.0",
|
"@rollup/plugin-alias": "^5.1.0",
|
||||||
|
|
|
@ -193,6 +193,9 @@
|
||||||
$: pad = pad || (interactive && hasChildren && inDndPath)
|
$: pad = pad || (interactive && hasChildren && inDndPath)
|
||||||
$: $dndIsDragging, (pad = false)
|
$: $dndIsDragging, (pad = false)
|
||||||
|
|
||||||
|
$: currentTheme = $context?.device?.theme
|
||||||
|
$: darkMode = !currentTheme?.includes("light")
|
||||||
|
|
||||||
// Update component context
|
// Update component context
|
||||||
$: store.set({
|
$: store.set({
|
||||||
id,
|
id,
|
||||||
|
@ -222,6 +225,7 @@
|
||||||
parent: id,
|
parent: id,
|
||||||
ancestors: [...($component?.ancestors ?? []), instance._component],
|
ancestors: [...($component?.ancestors ?? []), instance._component],
|
||||||
path: [...($component?.path ?? []), id],
|
path: [...($component?.path ?? []), id],
|
||||||
|
darkMode,
|
||||||
})
|
})
|
||||||
|
|
||||||
const initialise = (instance, force = false) => {
|
const initialise = (instance, force = false) => {
|
||||||
|
@ -283,10 +287,23 @@
|
||||||
const dependsOnKey = setting.dependsOn.setting || setting.dependsOn
|
const dependsOnKey = setting.dependsOn.setting || setting.dependsOn
|
||||||
const dependsOnValue = setting.dependsOn.value
|
const dependsOnValue = setting.dependsOn.value
|
||||||
const realDependentValue = instance[dependsOnKey]
|
const realDependentValue = instance[dependsOnKey]
|
||||||
|
|
||||||
|
const sectionDependsOnKey =
|
||||||
|
setting.sectionDependsOn?.setting || setting.sectionDependsOn
|
||||||
|
const sectionDependsOnValue = setting.sectionDependsOn?.value
|
||||||
|
const sectionRealDependentValue = instance[sectionDependsOnKey]
|
||||||
|
|
||||||
if (dependsOnValue == null && realDependentValue == null) {
|
if (dependsOnValue == null && realDependentValue == null) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (dependsOnValue !== realDependentValue) {
|
if (dependsOnValue != null && dependsOnValue !== realDependentValue) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
sectionDependsOnValue != null &&
|
||||||
|
sectionDependsOnValue !== sectionRealDependentValue
|
||||||
|
) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,13 +37,16 @@
|
||||||
|
|
||||||
let grid
|
let grid
|
||||||
let gridContext
|
let gridContext
|
||||||
let minHeight
|
let minHeight = 0
|
||||||
|
|
||||||
|
$: currentTheme = $context?.device?.theme
|
||||||
|
$: darkMode = !currentTheme?.includes("light")
|
||||||
$: parsedColumns = getParsedColumns(columns)
|
$: parsedColumns = getParsedColumns(columns)
|
||||||
$: columnWhitelist = parsedColumns.filter(x => x.active).map(x => x.field)
|
$: columnWhitelist = parsedColumns.filter(x => x.active).map(x => x.field)
|
||||||
$: schemaOverrides = getSchemaOverrides(parsedColumns)
|
$: schemaOverrides = getSchemaOverrides(parsedColumns)
|
||||||
$: enrichedButtons = enrichButtons(buttons)
|
$: enrichedButtons = enrichButtons(buttons)
|
||||||
$: selectedRows = deriveSelectedRows(gridContext)
|
$: selectedRows = deriveSelectedRows(gridContext)
|
||||||
|
$: styles = patchStyles($component.styles, minHeight)
|
||||||
$: data = { selectedRows: $selectedRows }
|
$: data = { selectedRows: $selectedRows }
|
||||||
$: actions = [
|
$: actions = [
|
||||||
{
|
{
|
||||||
|
@ -84,9 +87,11 @@
|
||||||
|
|
||||||
const getSchemaOverrides = columns => {
|
const getSchemaOverrides = columns => {
|
||||||
let overrides = {}
|
let overrides = {}
|
||||||
columns.forEach(column => {
|
columns.forEach((column, idx) => {
|
||||||
overrides[column.field] = {
|
overrides[column.field] = {
|
||||||
displayName: column.label,
|
displayName: column.label,
|
||||||
|
width: column.width,
|
||||||
|
order: idx,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return overrides
|
return overrides
|
||||||
|
@ -128,52 +133,55 @@
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const patchStyles = (styles, minHeight) => {
|
||||||
|
return {
|
||||||
|
...styles,
|
||||||
|
normal: {
|
||||||
|
...styles?.normal,
|
||||||
|
"min-height": `${minHeight}px`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
gridContext = grid.getContext()
|
gridContext = grid.getContext()
|
||||||
gridContext.minHeight.subscribe($height => (minHeight = $height))
|
gridContext.minHeight.subscribe($height => (minHeight = $height))
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<span style="--min-height:{minHeight}px">
|
<div use:styleable={styles} class:in-builder={$builderStore.inBuilder}>
|
||||||
<div
|
<Grid
|
||||||
use:styleable={$component.styles}
|
bind:this={grid}
|
||||||
class:in-builder={$builderStore.inBuilder}
|
datasource={table}
|
||||||
>
|
{API}
|
||||||
<Grid
|
{stripeRows}
|
||||||
bind:this={grid}
|
{quiet}
|
||||||
datasource={table}
|
{darkMode}
|
||||||
{API}
|
{initialFilter}
|
||||||
{stripeRows}
|
{initialSortColumn}
|
||||||
{quiet}
|
{initialSortOrder}
|
||||||
{initialFilter}
|
{fixedRowHeight}
|
||||||
{initialSortColumn}
|
{columnWhitelist}
|
||||||
{initialSortOrder}
|
{schemaOverrides}
|
||||||
{fixedRowHeight}
|
canAddRows={allowAddRows}
|
||||||
{columnWhitelist}
|
canEditRows={allowEditRows}
|
||||||
{schemaOverrides}
|
canDeleteRows={allowDeleteRows}
|
||||||
canAddRows={allowAddRows}
|
canEditColumns={false}
|
||||||
canEditRows={allowEditRows}
|
canExpandRows={false}
|
||||||
canDeleteRows={allowDeleteRows}
|
canSaveSchema={false}
|
||||||
canEditColumns={false}
|
canSelectRows={true}
|
||||||
canExpandRows={false}
|
showControls={false}
|
||||||
canSaveSchema={false}
|
notifySuccess={notificationStore.actions.success}
|
||||||
canSelectRows={true}
|
notifyError={notificationStore.actions.error}
|
||||||
showControls={false}
|
buttons={enrichedButtons}
|
||||||
notifySuccess={notificationStore.actions.success}
|
isCloud={$environmentStore.cloud}
|
||||||
notifyError={notificationStore.actions.error}
|
on:rowclick={e => onRowClick?.({ row: e.detail })}
|
||||||
buttons={enrichedButtons}
|
/>
|
||||||
isCloud={$environmentStore.cloud}
|
</div>
|
||||||
on:rowclick={e => onRowClick?.({ row: e.detail })}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<Provider {data} {actions} />
|
<Provider {data} {actions} />
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
span {
|
|
||||||
display: contents;
|
|
||||||
}
|
|
||||||
div {
|
div {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -182,7 +190,6 @@
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: 410px;
|
height: 410px;
|
||||||
min-height: var(--min-height);
|
|
||||||
}
|
}
|
||||||
div.in-builder :global(*) {
|
div.in-builder :global(*) {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
|
|
||||||
// Bar/Line/Area
|
// Bar/Line/Area
|
||||||
export let valueColumns
|
export let valueColumns
|
||||||
export let yAxisUnits
|
export let valueUnits
|
||||||
export let yAxisLabel
|
export let yAxisLabel
|
||||||
export let xAxisLabel
|
export let xAxisLabel
|
||||||
export let curve
|
export let curve
|
||||||
|
@ -51,8 +51,6 @@
|
||||||
export let bucketCount
|
export let bucketCount
|
||||||
|
|
||||||
let dataProviderId
|
let dataProviderId
|
||||||
|
|
||||||
$: colors = c1 && c2 && c3 && c4 && c5 ? [c1, c2, c3, c4, c5] : null
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Block>
|
<Block>
|
||||||
|
@ -84,8 +82,7 @@
|
||||||
dataLabels,
|
dataLabels,
|
||||||
legend,
|
legend,
|
||||||
animate,
|
animate,
|
||||||
...colors,
|
valueUnits,
|
||||||
yAxisUnits,
|
|
||||||
yAxisLabel,
|
yAxisLabel,
|
||||||
xAxisLabel,
|
xAxisLabel,
|
||||||
stacked,
|
stacked,
|
||||||
|
@ -98,6 +95,11 @@
|
||||||
lowColumn,
|
lowColumn,
|
||||||
dateColumn,
|
dateColumn,
|
||||||
bucketCount,
|
bucketCount,
|
||||||
|
c1,
|
||||||
|
c2,
|
||||||
|
c3,
|
||||||
|
c4,
|
||||||
|
c5,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue