diff --git a/packages/bbui/src/Form/Core/Multiselect.svelte b/packages/bbui/src/Form/Core/Multiselect.svelte
index 2243570cd5..4873430fa0 100644
--- a/packages/bbui/src/Form/Core/Multiselect.svelte
+++ b/packages/bbui/src/Form/Core/Multiselect.svelte
@@ -17,6 +17,8 @@
export let customPopoverHeight
export let open = false
export let loading
+ export let onOptionMouseenter = () => {}
+ export let onOptionMouseleave = () => {}
const dispatch = createEventDispatcher()
@@ -97,4 +99,6 @@
{autoWidth}
{customPopoverHeight}
{loading}
+ {onOptionMouseenter}
+ {onOptionMouseleave}
/>
diff --git a/packages/bbui/src/Form/Core/Picker.svelte b/packages/bbui/src/Form/Core/Picker.svelte
index eb7a5db655..c6d4ed3353 100644
--- a/packages/bbui/src/Form/Core/Picker.svelte
+++ b/packages/bbui/src/Form/Core/Picker.svelte
@@ -41,6 +41,8 @@
export let footer = null
export let customAnchor = null
export let loading
+ export let onOptionMouseenter = () => {}
+ export let onOptionMouseleave = () => {}
const dispatch = createEventDispatcher()
@@ -199,6 +201,8 @@
aria-selected="true"
tabindex="0"
on:click={() => onSelectOption(getOptionValue(option, idx))}
+ on:mouseenter={e => onOptionMouseenter(e, option)}
+ on:mouseleave={e => onOptionMouseleave(e, option)}
class:is-disabled={!isOptionEnabled(option)}
>
{#if getOptionIcon(option, idx)}
diff --git a/packages/bbui/src/Form/Core/Select.svelte b/packages/bbui/src/Form/Core/Select.svelte
index 5754c683ed..0111bd6bcf 100644
--- a/packages/bbui/src/Form/Core/Select.svelte
+++ b/packages/bbui/src/Form/Core/Select.svelte
@@ -26,6 +26,8 @@
export let tag = null
export let searchTerm = null
export let loading
+ export let onOptionMouseenter = () => {}
+ export let onOptionMouseleave = () => {}
const dispatch = createEventDispatcher()
@@ -95,6 +97,8 @@
{autocomplete}
{sort}
{tag}
+ {onOptionMouseenter}
+ {onOptionMouseleave}
isPlaceholder={value == null || value === ""}
placeholderOption={placeholder === false ? null : placeholder}
isOptionSelected={option => compareOptionAndValue(option, value)}
diff --git a/packages/bbui/src/Form/Multiselect.svelte b/packages/bbui/src/Form/Multiselect.svelte
index b0246c8530..9878605f4b 100644
--- a/packages/bbui/src/Form/Multiselect.svelte
+++ b/packages/bbui/src/Form/Multiselect.svelte
@@ -19,6 +19,8 @@
export let searchTerm = null
export let customPopoverHeight
export let helpText = null
+ export let onOptionMouseenter = () => {}
+ export let onOptionMouseleave = () => {}
const dispatch = createEventDispatcher()
const onChange = e => {
@@ -41,6 +43,8 @@
{autoWidth}
{autocomplete}
{customPopoverHeight}
+ {onOptionMouseenter}
+ {onOptionMouseleave}
bind:searchTerm
on:change={onChange}
on:click
diff --git a/packages/bbui/src/Form/Select.svelte b/packages/bbui/src/Form/Select.svelte
index 2119a37980..260090c7b7 100644
--- a/packages/bbui/src/Form/Select.svelte
+++ b/packages/bbui/src/Form/Select.svelte
@@ -29,6 +29,9 @@
export let tag = null
export let helpText = null
export let compare
+ export let onOptionMouseenter = () => {}
+ export let onOptionMouseleave = () => {}
+
const dispatch = createEventDispatcher()
const onChange = e => {
value = e.detail
@@ -67,6 +70,8 @@
{customPopoverHeight}
{tag}
{compare}
+ {onOptionMouseenter}
+ {onOptionMouseleave}
on:change={onChange}
on:click
/>
diff --git a/packages/bbui/src/Tooltip/Context.svelte b/packages/bbui/src/Tooltip/Context.svelte
new file mode 100644
index 0000000000..84d6fbbb73
--- /dev/null
+++ b/packages/bbui/src/Tooltip/Context.svelte
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
diff --git a/packages/bbui/src/index.js b/packages/bbui/src/index.js
index f28e185305..e746e3a72b 100644
--- a/packages/bbui/src/index.js
+++ b/packages/bbui/src/index.js
@@ -53,6 +53,7 @@ export { default as Link } from "./Link/Link.svelte"
export { default as Tooltip } from "./Tooltip/Tooltip.svelte"
export { default as TempTooltip } from "./Tooltip/TempTooltip.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 MenuSection } from "./Menu/Section.svelte"
export { default as MenuSeparator } from "./Menu/Separator.svelte"
diff --git a/packages/builder/src/components/common/CodeEditor/CodeEditor.svelte b/packages/builder/src/components/common/CodeEditor/CodeEditor.svelte
index ba5f75083a..0102d8f7a9 100644
--- a/packages/builder/src/components/common/CodeEditor/CodeEditor.svelte
+++ b/packages/builder/src/components/common/CodeEditor/CodeEditor.svelte
@@ -54,6 +54,7 @@
export let autofocus = false
export let jsBindingWrapping = true
export let readonly = false
+ export let readonlyLineNumbers = false
const dispatch = createEventDispatcher()
@@ -240,6 +241,9 @@
if (readonly) {
complete.push(EditorState.readOnly.of(true))
+ if (readonlyLineNumbers) {
+ complete.push(lineNumbers())
+ }
} else {
complete = [
...complete,
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/index.svelte b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/index.svelte
new file mode 100644
index 0000000000..d9714c2e7a
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/index.svelte
@@ -0,0 +1,48 @@
+
+
+
+
+ {#if subject === subjects.column}
+
+ {:else if subject === subjects.support}
+
+ {:else if subject === subjects.stringsAsNumbers}
+
+ {:else if subject === subjects.notRequired}
+
+ {:else if subject === subjects.datesAsNumbers}
+
+ {:else if subject === subjects.scalarJsonOnly}
+
+ {:else if subject === subjects.numbersAsDates}
+
+ {:else if subject === subjects.stringsAsDates}
+
+ {/if}
+
+
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/Column.svelte b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/Column.svelte
new file mode 100644
index 0000000000..7fbac09827
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/Column.svelte
@@ -0,0 +1,147 @@
+
+
+
+
+ Column Overview for {columnName}
+
+
+ {#if schema.type === "string"}
+
+ {:else if schema.type === "datetime"}
+
+
+
+
+ {:else if schema.type === "number"}
+
+
+ {:else if schema.type === "array"}
+ {#each schema?.constraints?.inclusion ?? [] as option, index}
+
+ {option}
+
+ {/each}
+ {:else if schema.type === "options"}
+ {#each schema?.constraints?.inclusion ?? [] as option, index}
+
+ {option}
+
+ {/each}
+ {:else if schema.type === "json"}
+
+
+
+ {:else if schema.type === "formula"}
+
+
+
+
+ {:else if schema.type === "link"}
+
+ table._id === schema?.tableId)
+ ?.name}
+ />
+
+ {:else if schema.type === "bb_reference"}
+
+ {/if}
+
+
+
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/DatesAsNumbers.svelte b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/DatesAsNumbers.svelte
new file mode 100644
index 0000000000..e5cab5dc15
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/DatesAsNumbers.svelte
@@ -0,0 +1,63 @@
+
+
+
+
+ A datetime value can be used in place of a numeric value, but it will be
+ converted to a UNIX time timestamp, which is the number of milliseconds
+ since Jan 1st 1970. A more recent moment in time will be a higher number.
+
+
+
+
+
+ {new Date(946684800000).toLocaleString()}
+
+ {"->"} 946684800000
+
+
+
+ {new Date(1577836800000).toLocaleString()}
+
+ {"->"} 1577836800000
+
+
+ Now {"->"} {timestamp}
+
+
+
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/NotRequired.svelte b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/NotRequired.svelte
new file mode 100644
index 0000000000..f28e9a227e
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/NotRequired.svelte
@@ -0,0 +1,11 @@
+
+
+
+
+ A required 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.
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/NumbersAsDates.svelte b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/NumbersAsDates.svelte
new file mode 100644
index 0000000000..d69228544e
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/NumbersAsDates.svelte
@@ -0,0 +1,65 @@
+
+
+
+
+ A number value can be used in place of a datetime value, but it will be
+ parsed as a UNIX time timestamp, which is the number of milliseconds
+ since Jan 1st 1970. A more recent moment in time will be a higher number.
+
+
+
+
+ 946684800000
+ {"->"}
+
+ {new Date(946684800000).toLocaleString()}
+
+
+
+ 1577836800000
+ {"->"}
+
+ {new Date(1577836800000).toLocaleString()}
+
+
+
+ {timestamp}
+ {"->"}
+ Now
+
+
+
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/ScalarJsonOnly.svelte b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/ScalarJsonOnly.svelte
new file mode 100644
index 0000000000..11fe3c7838
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/ScalarJsonOnly.svelte
@@ -0,0 +1,71 @@
+
+
+
+
+ JSON objects can't be used here, but any number , string or boolean 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 parent.child .
+
+
+ {#if scalarDescendants.length > 0}
+
+ {#each scalarDescendants as descendant}
+
+ {descendant.name} - {descendant.type}
+
+ {/each}
+
+ {/if}
+
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/StringsAsDates.svelte b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/StringsAsDates.svelte
new file mode 100644
index 0000000000..7303eac7cb
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/StringsAsDates.svelte
@@ -0,0 +1,107 @@
+
+
+
+
+ A string value can be used in place of a datetime value, but it will be
+ parsed as:
+
+
+ A UNIX time timestamp, which is the number of milliseconds since
+ Jan 1st 1970. A more recent moment in time will be a higher number.
+
+
+
+
+ 946684800000
+ {"->"}
+
+ {new Date(946684800000).toLocaleString()}
+
+
+
+ 1577836800000
+ {"->"}
+
+ {new Date(1577836800000).toLocaleString()}
+
+
+
+ {timestamp}
+ {"->"}
+ Now
+
+
+
+ An ISO 8601 datetime string, which represents an exact moment
+ in time as well as the potentional to store the timezone it occured in.
+
+
+
+
+ 2000-01-01T00:00:00.000Z
+ ↓
+
+ {new Date(946684800000).toLocaleString()}
+
+
+
+ 2000-01-01T00:00:00.000Z
+ ↓
+
+ {new Date(1577836800000).toLocaleString()}
+
+
+
+ {iso}
+ ↓
+ Now
+
+
+
+
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/StringsAsNumbers.svelte b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/StringsAsNumbers.svelte
new file mode 100644
index 0000000000..e30a450d80
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/StringsAsNumbers.svelte
@@ -0,0 +1,56 @@
+
+
+
+
+ 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.
+
+
+
+
+ "100" {"->"} 100
+
+
+ "100k" {"->"} 100
+
+
+ "100,000" {"->"} 100
+
+
+ "100 million" {"->"} 100
+
+
+ "100.9" {"->"} 100.9
+
+
+ "One hundred" {"->"} Error
+
+
+
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/Support.svelte b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/Support.svelte
new file mode 100644
index 0000000000..0419c0ad16
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/Support.svelte
@@ -0,0 +1,35 @@
+
+
+
+
+
+ Fully compatible with the input as long as the data is present.
+
+
+
+ Partially compatible with the input, but beware of other caveats
+ mentioned.
+
+
+
+ Incompatible with the component.
+
+
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/components/BindingValue.svelte b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/components/BindingValue.svelte
new file mode 100644
index 0000000000..811283ba51
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/components/BindingValue.svelte
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/components/Block.svelte b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/components/Block.svelte
new file mode 100644
index 0000000000..449b40304d
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/components/Block.svelte
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/components/ExampleLine.svelte b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/components/ExampleLine.svelte
new file mode 100644
index 0000000000..6dd3e10a39
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/components/ExampleLine.svelte
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/components/ExampleSection.svelte b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/components/ExampleSection.svelte
new file mode 100644
index 0000000000..4da4f5141b
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/components/ExampleSection.svelte
@@ -0,0 +1,32 @@
+
+
+
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/components/JSONValue.svelte b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/components/JSONValue.svelte
new file mode 100644
index 0000000000..4703398b2d
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/components/JSONValue.svelte
@@ -0,0 +1,22 @@
+
+
+
+ {value}
+
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/components/Property.svelte b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/components/Property.svelte
new file mode 100644
index 0000000000..8d6e853ab4
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/components/Property.svelte
@@ -0,0 +1,49 @@
+
+
+
+
+
+ {name}
+
+
+ -
+
+
+ {value}
+
+
+
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/components/Section.svelte b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/components/Section.svelte
new file mode 100644
index 0000000000..486e111725
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/components/Section.svelte
@@ -0,0 +1,11 @@
+
+
+
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/components/Subject.svelte b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/components/Subject.svelte
new file mode 100644
index 0000000000..4e21160cae
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/components/Subject.svelte
@@ -0,0 +1,51 @@
+
+
+
+
+
+ {heading}
+
+
+
+
+
+
+
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/components/index.js b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/components/index.js
new file mode 100644
index 0000000000..d174f4d6cc
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/components/index.js
@@ -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"
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/index.js b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/index.js
new file mode 100644
index 0000000000..c7f54d6415
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/DetailsModal/subjects/index.js
@@ -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"
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/Explanation.svelte b/packages/builder/src/components/design/settings/controls/Explanation/Explanation.svelte
new file mode 100644
index 0000000000..bc45e410c9
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/Explanation.svelte
@@ -0,0 +1,102 @@
+
+
+
+
+
+ {#if messages.includes(messageConstants.stringAsNumber)}
+
+ {/if}
+ {#if messages.includes(messageConstants.notRequired)}
+
+ {/if}
+ {#if messages.includes(messageConstants.jsonPrimitivesOnly)}
+
+ {/if}
+ {#if messages.includes(messageConstants.dateAsNumber)}
+
+ {/if}
+ {#if messages.includes(messageConstants.numberAsDate)}
+
+ {/if}
+ {#if messages.includes(messageConstants.stringAsDate)}
+
+ {/if}
+
+
+{#if detailsModalSubject !== subjects.none}
+
+{/if}
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/explanation.js b/packages/builder/src/components/design/settings/controls/Explanation/explanation.js
new file mode 100644
index 0000000000..4e024c67fc
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/explanation.js
@@ -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
+}
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/index.js b/packages/builder/src/components/design/settings/controls/Explanation/index.js
new file mode 100644
index 0000000000..5780c1de14
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/index.js
@@ -0,0 +1 @@
+export { default as Explanation } from "./Explanation.svelte"
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/lines/Column.svelte b/packages/builder/src/components/design/settings/controls/Explanation/lines/Column.svelte
new file mode 100644
index 0000000000..9c3b87f8b9
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/lines/Column.svelte
@@ -0,0 +1,84 @@
+
+
+
+ setExplanationSubject(subjects.column)}
+ on:mouseleave={() => setExplanationSubject(subjects.none)}
+ href={tableHref}
+ text={columnName}
+ />
+
+
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/lines/DateAsNumber.svelte b/packages/builder/src/components/design/settings/controls/Explanation/lines/DateAsNumber.svelte
new file mode 100644
index 0000000000..ba5eb9b0e1
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/lines/DateAsNumber.svelte
@@ -0,0 +1,16 @@
+
+
+
+
+ setExplanationSubject(subjects.datesAsNumbers)}
+ on:mouseleave={() => setExplanationSubject(subjects.none)}
+ text="UNIX time value"
+ />
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/lines/JSONPrimitivesOnly.svelte b/packages/builder/src/components/design/settings/controls/Explanation/lines/JSONPrimitivesOnly.svelte
new file mode 100644
index 0000000000..2286c09044
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/lines/JSONPrimitivesOnly.svelte
@@ -0,0 +1,21 @@
+
+
+
+ setExplanationSubject(subjects.scalarJsonOnly)}
+ on:mouseleave={() => setExplanationSubject(subjects.none)}
+ >Scalar JSON values
+
+
+
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/lines/NotRequired.svelte b/packages/builder/src/components/design/settings/controls/Explanation/lines/NotRequired.svelte
new file mode 100644
index 0000000000..e705bd68e6
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/lines/NotRequired.svelte
@@ -0,0 +1,25 @@
+
+
+
+
+ setExplanationSubject(subjects.notRequired)}
+ on:mouseleave={() => setExplanationSubject(subjects.none)}
+ text="required"
+ />
+
+
+
+
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/lines/NumberAsDate.svelte b/packages/builder/src/components/design/settings/controls/Explanation/lines/NumberAsDate.svelte
new file mode 100644
index 0000000000..c6413c13a5
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/lines/NumberAsDate.svelte
@@ -0,0 +1,16 @@
+
+
+
+
+ setExplanationSubject(subjects.numbersAsDates)}
+ on:mouseleave={() => setExplanationSubject(subjects.none)}
+ text="UNIX time value"
+ />
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/lines/StringAsDate.svelte b/packages/builder/src/components/design/settings/controls/Explanation/lines/StringAsDate.svelte
new file mode 100644
index 0000000000..72267b6f47
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/lines/StringAsDate.svelte
@@ -0,0 +1,16 @@
+
+
+
+
+ setExplanationSubject(subjects.stringsAsDates)}
+ on:mouseleave={() => setExplanationSubject(subjects.none)}
+ text="UNIX time or ISO 8601 value"
+ />
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/lines/StringAsNumber.svelte b/packages/builder/src/components/design/settings/controls/Explanation/lines/StringAsNumber.svelte
new file mode 100644
index 0000000000..937545c1c3
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/lines/StringAsNumber.svelte
@@ -0,0 +1,16 @@
+
+
+
+
+ setExplanationSubject(subjects.stringsAsNumbers)}
+ on:mouseleave={() => setExplanationSubject(subjects.none)}
+ text="non-numerical values"
+ />
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/lines/Support.svelte b/packages/builder/src/components/design/settings/controls/Explanation/lines/Support.svelte
new file mode 100644
index 0000000000..848ab208fb
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/lines/Support.svelte
@@ -0,0 +1,59 @@
+
+
+
+ setExplanationSubject(subjects.support)}
+ on:mouseleave={() => setExplanationSubject(subjects.none)}
+ {icon}
+ {color}
+ {text}
+ />
+
+
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/lines/index.js b/packages/builder/src/components/design/settings/controls/Explanation/lines/index.js
new file mode 100644
index 0000000000..beff239398
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/lines/index.js
@@ -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"
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/subjects.js b/packages/builder/src/components/design/settings/controls/Explanation/subjects.js
new file mode 100644
index 0000000000..1f94fa6fd0
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/subjects.js
@@ -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
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/typography/Comma.svelte b/packages/builder/src/components/design/settings/controls/Explanation/typography/Comma.svelte
new file mode 100644
index 0000000000..f16bd16054
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/typography/Comma.svelte
@@ -0,0 +1,14 @@
+,
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/typography/DocumentationLink.svelte b/packages/builder/src/components/design/settings/controls/Explanation/typography/DocumentationLink.svelte
new file mode 100644
index 0000000000..55b9732d4a
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/typography/DocumentationLink.svelte
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+ {text}
+
+
+
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/typography/InfoWord.svelte b/packages/builder/src/components/design/settings/controls/Explanation/typography/InfoWord.svelte
new file mode 100644
index 0000000000..cac12cbfb5
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/typography/InfoWord.svelte
@@ -0,0 +1,78 @@
+
+
+{#if href !== null}
+
+ {#if icon}
+
+ {/if}
+
+
+ {text}
+
+
+
+{:else}
+
+ {#if icon}
+
+ {/if}
+
+
+ {text}
+
+
+
+{/if}
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/typography/Line.svelte b/packages/builder/src/components/design/settings/controls/Explanation/typography/Line.svelte
new file mode 100644
index 0000000000..5d28ba0423
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/typography/Line.svelte
@@ -0,0 +1,41 @@
+
+
+
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/typography/Period.svelte b/packages/builder/src/components/design/settings/controls/Explanation/typography/Period.svelte
new file mode 100644
index 0000000000..d1bbafe6a6
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/typography/Period.svelte
@@ -0,0 +1,13 @@
+.
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/typography/Space.svelte b/packages/builder/src/components/design/settings/controls/Explanation/typography/Space.svelte
new file mode 100644
index 0000000000..b88831d760
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/typography/Space.svelte
@@ -0,0 +1,9 @@
+{" "}
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/typography/Text.svelte b/packages/builder/src/components/design/settings/controls/Explanation/typography/Text.svelte
new file mode 100644
index 0000000000..6562c9e864
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/typography/Text.svelte
@@ -0,0 +1,64 @@
+
+
+{#each words as word}
+ {#if word === " "}
+
+ {:else if word === ","}
+
+ {:else if word === "."}
+
+ {:else}
+
+ {word}
+
+ {/if}
+{/each}
+
+
diff --git a/packages/builder/src/components/design/settings/controls/Explanation/typography/index.js b/packages/builder/src/components/design/settings/controls/Explanation/typography/index.js
new file mode 100644
index 0000000000..102b65190d
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/Explanation/typography/index.js
@@ -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"
diff --git a/packages/builder/src/components/design/settings/controls/FieldSelect.svelte b/packages/builder/src/components/design/settings/controls/FieldSelect.svelte
index e50a0e8030..15b67ded18 100644
--- a/packages/builder/src/components/design/settings/controls/FieldSelect.svelte
+++ b/packages/builder/src/components/design/settings/controls/FieldSelect.svelte
@@ -1,12 +1,22 @@
-
+
+
+{#if explanation}
+
+
+
+{/if}
diff --git a/packages/builder/src/components/design/settings/controls/MultiFieldSelect.svelte b/packages/builder/src/components/design/settings/controls/MultiFieldSelect.svelte
index d0224fb1db..d6c9a0b1e3 100644
--- a/packages/builder/src/components/design/settings/controls/MultiFieldSelect.svelte
+++ b/packages/builder/src/components/design/settings/controls/MultiFieldSelect.svelte
@@ -1,12 +1,22 @@
-
+
+
+{#if explanation}
+
+
+
+{/if}
diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/ComponentSettingsSection.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/ComponentSettingsSection.svelte
index 9ff2a764b7..54241ea1cc 100644
--- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/ComponentSettingsSection.svelte
+++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/ComponentSettingsSection.svelte
@@ -191,6 +191,9 @@
// Number fields
min: setting.min ?? null,
max: setting.max ?? null,
+
+ // Field select settings
+ explanation: setting.explanation,
}}
{bindings}
{componentBindings}
diff --git a/packages/builder/src/stores/builder/app.js b/packages/builder/src/stores/builder/app.js
index 7dfee4daa9..e57a079185 100644
--- a/packages/builder/src/stores/builder/app.js
+++ b/packages/builder/src/stores/builder/app.js
@@ -19,6 +19,7 @@ export const INITIAL_APP_META_STATE = {
showNotificationAction: false,
sidePanel: false,
},
+ typeSupportPresets: {},
features: {
componentValidation: false,
disableUserMetadata: false,
@@ -79,6 +80,13 @@ export class AppMetaStore extends BudiStore {
}))
}
+ syncClientTypeSupportPresets(typeSupportPresets) {
+ this.update(state => ({
+ ...state,
+ typeSupportPresets,
+ }))
+ }
+
async syncAppRoutes() {
const resp = await API.fetchAppRoutes()
this.update(state => ({
diff --git a/packages/builder/src/stores/builder/components.js b/packages/builder/src/stores/builder/components.js
index 19a4f41532..8498214313 100644
--- a/packages/builder/src/stores/builder/components.js
+++ b/packages/builder/src/stores/builder/components.js
@@ -108,6 +108,7 @@ export class ComponentStore extends BudiStore {
// Sync client features to app store
appStore.syncClientFeatures(components.features)
+ appStore.syncClientTypeSupportPresets(components?.typeSupportPresets ?? {})
return components
}
diff --git a/packages/builder/src/stores/builder/tests/app.test.js b/packages/builder/src/stores/builder/tests/app.test.js
index e0e5d17ba6..728f472317 100644
--- a/packages/builder/src/stores/builder/tests/app.test.js
+++ b/packages/builder/src/stores/builder/tests/app.test.js
@@ -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 => {
ctx.test.appStore.syncClientFeatures(clientFeaturesResp)
diff --git a/packages/builder/src/stores/builder/tests/component.test.js b/packages/builder/src/stores/builder/tests/component.test.js
index b6c9ca27cd..b8baefc5e6 100644
--- a/packages/builder/src/stores/builder/tests/component.test.js
+++ b/packages/builder/src/stores/builder/tests/component.test.js
@@ -42,6 +42,7 @@ vi.mock("stores/builder", async () => {
update: mockAppStore.update,
set: mockAppStore.set,
syncClientFeatures: vi.fn(),
+ syncClientTypeSupportPresets: vi.fn(),
}
const mockTableStore = writable()
const tables = {
diff --git a/packages/client/manifest.json b/packages/client/manifest.json
index 1d2499a679..afe17c2a76 100644
--- a/packages/client/manifest.json
+++ b/packages/client/manifest.json
@@ -13,6 +13,42 @@
"sidePanel": 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": {
"name": "Layout",
"description": "This component is specific only to layouts",
@@ -1602,6 +1638,7 @@
]
},
"bar": {
+ "documentationLink": "https://docs.budibase.com/docs/bar-chart",
"name": "Bar Chart",
"description": "Bar chart",
"icon": "GraphBarVertical",
@@ -1626,6 +1663,11 @@
"label": "Label column",
"key": "labelColumn",
"dependsOn": "dataProvider",
+ "explanation": {
+ "typeSupport": {
+ "preset": "stringLike"
+ }
+ },
"required": true
},
{
@@ -1633,6 +1675,11 @@
"label": "Data columns",
"key": "valueColumns",
"dependsOn": "dataProvider",
+ "explanation": {
+ "typeSupport": {
+ "preset": "numberLike"
+ }
+ },
"required": true
},
{
@@ -1760,6 +1807,7 @@
]
},
"line": {
+ "documentationLink": "https://docs.budibase.com/docs/line-chart",
"name": "Line Chart",
"description": "Line chart",
"icon": "GraphTrend",
@@ -1784,6 +1832,11 @@
"label": "Label column",
"key": "labelColumn",
"dependsOn": "dataProvider",
+ "explanation": {
+ "typeSupport": {
+ "preset": "stringLike"
+ }
+ },
"required": true
},
{
@@ -1791,6 +1844,11 @@
"label": "Data columns",
"key": "valueColumns",
"dependsOn": "dataProvider",
+ "explanation": {
+ "typeSupport": {
+ "preset": "numberLike"
+ }
+ },
"required": true
},
{
@@ -1913,6 +1971,7 @@
]
},
"area": {
+ "documentationLink": "https://docs.budibase.com/docs/area-chart",
"name": "Area Chart",
"description": "Line chart",
"icon": "GraphAreaStacked",
@@ -1937,6 +1996,11 @@
"label": "Label column",
"key": "labelColumn",
"dependsOn": "dataProvider",
+ "explanation": {
+ "typeSupport": {
+ "preset": "stringLike"
+ }
+ },
"required": true
},
{
@@ -1944,6 +2008,11 @@
"label": "Data columns",
"key": "valueColumns",
"dependsOn": "dataProvider",
+ "explanation": {
+ "typeSupport": {
+ "preset": "numberLike"
+ }
+ },
"required": true
},
{
@@ -2078,6 +2147,7 @@
]
},
"pie": {
+ "documentationLink": "https://docs.budibase.com/docs/pie-donut-chart",
"name": "Pie Chart",
"description": "Pie chart",
"icon": "GraphPie",
@@ -2102,13 +2172,23 @@
"label": "Label column",
"key": "labelColumn",
"dependsOn": "dataProvider",
+ "explanation": {
+ "typeSupport": {
+ "preset": "stringLike"
+ }
+ },
"required": true
},
{
"type": "field",
- "label": "Data columns",
+ "label": "Data column",
"key": "valueColumn",
"dependsOn": "dataProvider",
+ "explanation": {
+ "typeSupport": {
+ "preset": "numberLike"
+ }
+ },
"required": true
},
{
@@ -2207,6 +2287,7 @@
]
},
"donut": {
+ "documentationLink": "https://docs.budibase.com/docs/pie-donut-chart",
"name": "Donut Chart",
"description": "Donut chart",
"icon": "GraphDonut",
@@ -2231,6 +2312,11 @@
"label": "Label column",
"key": "labelColumn",
"dependsOn": "dataProvider",
+ "explanation": {
+ "typeSupport": {
+ "preset": "stringLike"
+ }
+ },
"required": true
},
{
@@ -2238,6 +2324,11 @@
"label": "Data columns",
"key": "valueColumn",
"dependsOn": "dataProvider",
+ "explanation": {
+ "typeSupport": {
+ "preset": "numberLike"
+ }
+ },
"required": true
},
{
@@ -2336,6 +2427,7 @@
]
},
"candlestick": {
+ "documentationLink": "https://docs.budibase.com/docs/candlestick-chart",
"name": "Candlestick Chart",
"description": "Candlestick chart",
"icon": "GraphBarVerticalStacked",
@@ -2360,6 +2452,11 @@
"label": "Date column",
"key": "dateColumn",
"dependsOn": "dataProvider",
+ "explanation": {
+ "typeSupport": {
+ "preset": "datetimeLike"
+ }
+ },
"required": true
},
{
@@ -2367,6 +2464,11 @@
"label": "Open column",
"key": "openColumn",
"dependsOn": "dataProvider",
+ "explanation": {
+ "typeSupport": {
+ "preset": "numberLike"
+ }
+ },
"required": true
},
{
@@ -2374,6 +2476,11 @@
"label": "Close column",
"key": "closeColumn",
"dependsOn": "dataProvider",
+ "explanation": {
+ "typeSupport": {
+ "preset": "numberLike"
+ }
+ },
"required": true
},
{
@@ -2381,6 +2488,11 @@
"label": "High column",
"key": "highColumn",
"dependsOn": "dataProvider",
+ "explanation": {
+ "typeSupport": {
+ "preset": "numberLike"
+ }
+ },
"required": true
},
{
@@ -2388,6 +2500,11 @@
"label": "Low column",
"key": "lowColumn",
"dependsOn": "dataProvider",
+ "explanation": {
+ "typeSupport": {
+ "preset": "numberLike"
+ }
+ },
"required": true
},
{
@@ -2427,6 +2544,7 @@
]
},
"histogram": {
+ "documentationLink": "https://docs.budibase.com/docs/histogram-chart",
"name": "Histogram Chart",
"description": "Histogram chart",
"icon": "Histogram",
@@ -2434,7 +2552,6 @@
"width": 600,
"height": 400
},
- "requiredAncestors": ["dataprovider"],
"settings": [
{
"type": "text",
@@ -2452,6 +2569,11 @@
"label": "Data column",
"key": "valueColumn",
"dependsOn": "dataProvider",
+ "explanation": {
+ "typeSupport": {
+ "preset": "numberLike"
+ }
+ },
"required": true
},
{
@@ -5313,6 +5435,11 @@
"label": "Label column",
"key": "labelColumn",
"dependsOn": "dataSource",
+ "explanation": {
+ "typeSupport": {
+ "preset": "stringLike"
+ }
+ },
"required": true
},
{
@@ -5320,6 +5447,11 @@
"label": "Data column",
"key": "valueColumn",
"dependsOn": "dataSource",
+ "explanation": {
+ "typeSupport": {
+ "preset": "numberLike"
+ }
+ },
"required": true
}
]
@@ -5338,6 +5470,11 @@
"label": "Label column",
"key": "labelColumn",
"dependsOn": "dataSource",
+ "explanation": {
+ "typeSupport": {
+ "preset": "stringLike"
+ }
+ },
"required": true
},
{
@@ -5345,6 +5482,11 @@
"label": "Data column",
"key": "valueColumn",
"dependsOn": "dataSource",
+ "explanation": {
+ "typeSupport": {
+ "preset": "numberLike"
+ }
+ },
"required": true
}
]
@@ -5363,6 +5505,11 @@
"label": "Label column",
"key": "labelColumn",
"dependsOn": "dataSource",
+ "explanation": {
+ "typeSupport": {
+ "preset": "stringLike"
+ }
+ },
"required": true
},
{
@@ -5370,6 +5517,11 @@
"label": "Data columns",
"key": "valueColumns",
"dependsOn": "dataSource",
+ "explanation": {
+ "typeSupport": {
+ "preset": "numberLike"
+ }
+ },
"required": true
},
{
@@ -5417,6 +5569,11 @@
"label": "Value column",
"key": "valueColumn",
"dependsOn": "dataSource",
+ "explanation": {
+ "typeSupport": {
+ "preset": "numberLike"
+ }
+ },
"required": true
},
{
@@ -5458,6 +5615,11 @@
"label": "Label column",
"key": "labelColumn",
"dependsOn": "dataSource",
+ "explanation": {
+ "typeSupport": {
+ "preset": "stringLike"
+ }
+ },
"required": true
},
{
@@ -5465,6 +5627,11 @@
"label": "Data columns",
"key": "valueColumns",
"dependsOn": "dataSource",
+ "explanation": {
+ "typeSupport": {
+ "preset": "numberLike"
+ }
+ },
"required": true
},
{
@@ -5507,6 +5674,11 @@
"label": "Label columns",
"key": "labelColumn",
"dependsOn": "dataSource",
+ "explanation": {
+ "typeSupport": {
+ "preset": "stringLike"
+ }
+ },
"required": true
},
{
@@ -5514,6 +5686,11 @@
"label": "Data columns",
"key": "valueColumns",
"dependsOn": "dataSource",
+ "explanation": {
+ "typeSupport": {
+ "preset": "numberLike"
+ }
+ },
"required": true
},
{
@@ -5568,6 +5745,11 @@
"label": "Date column",
"key": "dateColumn",
"dependsOn": "dataSource",
+ "explanation": {
+ "typeSupport": {
+ "preset": "datetimeLike"
+ }
+ },
"required": true
},
{
@@ -5575,6 +5757,11 @@
"label": "Open column",
"key": "openColumn",
"dependsOn": "dataSource",
+ "explanation": {
+ "typeSupport": {
+ "preset": "numberLike"
+ }
+ },
"required": true
},
{
@@ -5582,6 +5769,11 @@
"label": "Close column",
"key": "closeColumn",
"dependsOn": "dataSource",
+ "explanation": {
+ "typeSupport": {
+ "preset": "numberLike"
+ }
+ },
"required": true
},
{
@@ -5589,6 +5781,11 @@
"label": "High column",
"key": "highColumn",
"dependsOn": "dataSource",
+ "explanation": {
+ "typeSupport": {
+ "preset": "numberLike"
+ }
+ },
"required": true
},
{
@@ -5596,6 +5793,11 @@
"label": "Low column",
"key": "lowColumn",
"dependsOn": "dataSource",
+ "explanation": {
+ "typeSupport": {
+ "preset": "numberLike"
+ }
+ },
"required": true
},
{
diff --git a/packages/client/package.json b/packages/client/package.json
index a7db1ee7ef..68c8ec15b1 100644
--- a/packages/client/package.json
+++ b/packages/client/package.json
@@ -25,7 +25,7 @@
"@budibase/string-templates": "0.0.0",
"@budibase/types": "0.0.0",
"@spectrum-css/card": "3.0.3",
- "apexcharts": "^3.22.1",
+ "apexcharts": "^3.48.0",
"dayjs": "^1.10.8",
"downloadjs": "1.4.7",
"html5-qrcode": "^2.2.1",
@@ -33,7 +33,6 @@
"sanitize-html": "^2.7.0",
"screenfull": "^6.0.1",
"shortid": "^2.2.15",
- "svelte-apexcharts": "^1.0.2",
"svelte-spa-router": "^4.0.1",
"atrament": "^4.3.0"
},
diff --git a/packages/client/src/components/Component.svelte b/packages/client/src/components/Component.svelte
index df7fce0374..8eacbdf041 100644
--- a/packages/client/src/components/Component.svelte
+++ b/packages/client/src/components/Component.svelte
@@ -287,10 +287,23 @@
const dependsOnKey = setting.dependsOn.setting || setting.dependsOn
const dependsOnValue = setting.dependsOn.value
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) {
return false
}
- if (dependsOnValue !== realDependentValue) {
+ if (dependsOnValue != null && dependsOnValue !== realDependentValue) {
+ return false
+ }
+
+ if (
+ sectionDependsOnValue != null &&
+ sectionDependsOnValue !== sectionRealDependentValue
+ ) {
return false
}
}
diff --git a/packages/client/src/components/app/blocks/ChartBlock.svelte b/packages/client/src/components/app/blocks/ChartBlock.svelte
index 2767c44b8e..ddfc7b522b 100644
--- a/packages/client/src/components/app/blocks/ChartBlock.svelte
+++ b/packages/client/src/components/app/blocks/ChartBlock.svelte
@@ -32,7 +32,7 @@
// Bar/Line/Area
export let valueColumns
- export let yAxisUnits
+ export let valueUnits
export let yAxisLabel
export let xAxisLabel
export let curve
@@ -51,8 +51,6 @@
export let bucketCount
let dataProviderId
-
- $: colors = c1 && c2 && c3 && c4 && c5 ? [c1, c2, c3, c4, c5] : null
@@ -84,8 +82,7 @@
dataLabels,
legend,
animate,
- ...colors,
- yAxisUnits,
+ valueUnits,
yAxisLabel,
xAxisLabel,
stacked,
@@ -98,6 +95,11 @@
lowColumn,
dateColumn,
bucketCount,
+ c1,
+ c2,
+ c3,
+ c4,
+ c5,
}}
/>
{/if}
diff --git a/packages/client/src/components/app/charts/ApexChart.svelte b/packages/client/src/components/app/charts/ApexChart.svelte
index b25be421f7..33e6cdfa80 100644
--- a/packages/client/src/components/app/charts/ApexChart.svelte
+++ b/packages/client/src/components/app/charts/ApexChart.svelte
@@ -1,25 +1,85 @@
-{#if options}
- {#key options.customColor}
-
- {/key}
-{:else if $builderStore.inBuilder}
-
-{/if}
+{#key optionsCopy?.customColor}
+
+ {#if $builderStore.inBuilder && noData}
+
+
+ Add rows to your data source to start using your component
+
+ {/if}
+{/key}
diff --git a/packages/client/src/components/app/charts/ApexOptionsBuilder.js b/packages/client/src/components/app/charts/ApexOptionsBuilder.js
deleted file mode 100644
index 04b5805df3..0000000000
--- a/packages/client/src/components/app/charts/ApexOptionsBuilder.js
+++ /dev/null
@@ -1,195 +0,0 @@
-export class ApexOptionsBuilder {
- constructor() {
- this.formatters = {
- ["Default"]: val => (isNaN(val) ? val : Math.round(val * 100) / 100),
- ["Thousands"]: val => `${Math.round(val / 1000)}K`,
- ["Millions"]: val => `${Math.round(val / 1000000)}M`,
- }
- this.options = {
- series: [],
- legend: {
- show: false,
- position: "top",
- horizontalAlign: "right",
- showForSingleSeries: true,
- showForNullSeries: true,
- showForZeroSeries: true,
- },
- chart: {
- toolbar: {
- show: false,
- },
- zoom: {
- enabled: false,
- },
- },
- xaxis: {
- labels: {
- formatter: this.formatters.Default,
- },
- },
- yaxis: {
- labels: {
- formatter: this.formatters.Default,
- },
- },
- }
- }
-
- setOption(path, value) {
- if (value == null || value === "") {
- return this
- }
- let tmp = this.options
- for (let i = 0; i < path.length - 1; i++) {
- const step = path[i]
- if (!tmp[step]) {
- tmp[step] = {}
- }
- tmp = tmp[step]
- }
- tmp[path[path.length - 1]] = value
- return this
- }
-
- getOptions() {
- return this.options
- }
-
- type(type) {
- return this.setOption(["chart", "type"], type)
- }
-
- title(title) {
- return this.setOption(["title", "text"], title)
- }
-
- colors(colors) {
- if (!colors) {
- delete this.options.colors
- this.options["customColor"] = false
- return this
- }
- this.options["customColor"] = true
- return this.setOption(["colors"], colors)
- }
-
- width(width) {
- return this.setOption(["chart", "width"], width || undefined)
- }
-
- height(height) {
- return this.setOption(["chart", "height"], height || undefined)
- }
-
- xLabel(label) {
- return this.setOption(["xaxis", "title", "text"], label)
- }
-
- yLabel(label) {
- return this.setOption(["yaxis", "title", "text"], label)
- }
-
- xCategories(categories) {
- return this.setOption(["xaxis", "categories"], categories)
- }
-
- yCategories(categories) {
- return this.setOption(["yaxis", "categories"], categories)
- }
-
- series(series) {
- return this.setOption(["series"], series)
- }
-
- horizontal(horizontal) {
- return this.setOption(["plotOptions", "bar", "horizontal"], horizontal)
- }
-
- dataLabels(dataLabels) {
- return this.setOption(["dataLabels", "enabled"], dataLabels)
- }
-
- animate(animate) {
- return this.setOption(["chart", "animations", "enabled"], animate)
- }
-
- curve(curve) {
- return this.setOption(["stroke", "curve"], curve)
- }
-
- gradient(gradient) {
- const fill = {
- type: "gradient",
- gradient: {
- shadeIntensity: 1,
- opacityFrom: 0.7,
- opacityTo: 0.9,
- stops: [0, 90, 100],
- },
- }
- return this.setOption(["fill"], gradient ? fill : undefined)
- }
-
- legend(legend) {
- return this.setOption(["legend", "show"], legend)
- }
-
- legendPosition(position) {
- return this.setOption(["legend", "position"], position)
- }
-
- stacked(stacked) {
- return this.setOption(["chart", "stacked"], stacked)
- }
-
- labels(labels) {
- return this.setOption(["labels"], labels)
- }
-
- xUnits(units) {
- return this.setOption(
- ["xaxis", "labels", "formatter"],
- this.formatters[units || "Default"]
- )
- }
-
- yUnits(units) {
- return this.setOption(
- ["yaxis", "labels", "formatter"],
- this.formatters[units || "Default"]
- )
- }
-
- clearXFormatter() {
- delete this.options.xaxis.labels
- return this
- }
-
- clearYFormatter() {
- delete this.options.yaxis.labels
- return this
- }
-
- xType(type) {
- return this.setOption(["xaxis", "type"], type)
- }
-
- yType(type) {
- return this.setOption(["yaxis", "type"], type)
- }
-
- yTooltip(yTooltip) {
- return this.setOption(["yaxis", "tooltip", "enabled"], yTooltip)
- }
-
- palette(palette) {
- if (!palette) {
- return this
- }
- return this.setOption(
- ["theme", "palette"],
- palette.toLowerCase().replace(/[\W]/g, "")
- )
- }
-}
diff --git a/packages/client/src/components/app/charts/AreaChart.svelte b/packages/client/src/components/app/charts/AreaChart.svelte
index dc80b2b9da..a9a61e59f6 100644
--- a/packages/client/src/components/app/charts/AreaChart.svelte
+++ b/packages/client/src/components/app/charts/AreaChart.svelte
@@ -1,5 +1,159 @@
-
+
diff --git a/packages/client/src/components/app/charts/BarChart.svelte b/packages/client/src/components/app/charts/BarChart.svelte
index fd8443e2d6..aeebfe9461 100644
--- a/packages/client/src/components/app/charts/BarChart.svelte
+++ b/packages/client/src/components/app/charts/BarChart.svelte
@@ -1,11 +1,12 @@
diff --git a/packages/client/src/components/app/charts/CandleStickChart.svelte b/packages/client/src/components/app/charts/CandleStickChart.svelte
index b2760b005e..61cdef180b 100644
--- a/packages/client/src/components/app/charts/CandleStickChart.svelte
+++ b/packages/client/src/components/app/charts/CandleStickChart.svelte
@@ -1,6 +1,6 @@
diff --git a/packages/client/src/components/app/charts/DonutChart.svelte b/packages/client/src/components/app/charts/DonutChart.svelte
index 721a09053a..dad9edfd67 100644
--- a/packages/client/src/components/app/charts/DonutChart.svelte
+++ b/packages/client/src/components/app/charts/DonutChart.svelte
@@ -1,5 +1,99 @@
-
+
diff --git a/packages/client/src/components/app/charts/HistogramChart.svelte b/packages/client/src/components/app/charts/HistogramChart.svelte
index 26b9028550..37c395d45f 100644
--- a/packages/client/src/components/app/charts/HistogramChart.svelte
+++ b/packages/client/src/components/app/charts/HistogramChart.svelte
@@ -1,135 +1,154 @@
diff --git a/packages/client/src/components/app/charts/LineChart.svelte b/packages/client/src/components/app/charts/LineChart.svelte
index 7f82a833d2..c2dac189e1 100644
--- a/packages/client/src/components/app/charts/LineChart.svelte
+++ b/packages/client/src/components/app/charts/LineChart.svelte
@@ -1,8 +1,7 @@
diff --git a/packages/client/src/components/app/charts/PieChart.svelte b/packages/client/src/components/app/charts/PieChart.svelte
index 8cb7317d94..3250a2ca95 100644
--- a/packages/client/src/components/app/charts/PieChart.svelte
+++ b/packages/client/src/components/app/charts/PieChart.svelte
@@ -1,6 +1,6 @@
diff --git a/packages/client/src/components/app/charts/utils.js b/packages/client/src/components/app/charts/utils.js
new file mode 100644
index 0000000000..1aea22c991
--- /dev/null
+++ b/packages/client/src/components/app/charts/utils.js
@@ -0,0 +1,51 @@
+export const formatters = {
+ ["Default"]: val => val,
+ ["Thousands"]: val => `${Math.round(val / 1000)}K`,
+ ["Millions"]: val => `${Math.round(val / 1000000)}M`,
+ ["Datetime"]: val => new Date(val).toLocaleString(),
+}
+
+export const parsePalette = paletteName => {
+ if (paletteName === "Custom") {
+ // return null in this case so that the palette option doesn't get consumed by Apex Charts
+ return null
+ }
+
+ const [_, number] = paletteName.split(" ")
+
+ return `palette${number}`
+}
+
+// Deep clone which copies function references
+export const cloneDeep = value => {
+ const typesToNaiveCopy = ["string", "boolean", "number", "function", "symbol"]
+
+ if (value === null) {
+ return null
+ }
+
+ if (value === undefined) {
+ return undefined
+ }
+
+ if (typesToNaiveCopy.includes(typeof value)) {
+ return value
+ }
+
+ if (Array.isArray(value)) {
+ return value.map(element => cloneDeep(element))
+ }
+
+ // Only copy "pure" objects, we want to error on stuff like Maps or Sets
+ if (typeof value === "object" && value.constructor.name === "Object") {
+ const cloneObject = {}
+
+ Object.entries(value).forEach(([key, childValue]) => {
+ cloneObject[key] = cloneDeep(childValue)
+ })
+
+ return cloneObject
+ }
+
+ throw `Unsupported value: "${value}" of type: "${typeof value}"`
+}
diff --git a/packages/client/src/components/app/charts/utils.test.js b/packages/client/src/components/app/charts/utils.test.js
new file mode 100644
index 0000000000..1b065e2fae
--- /dev/null
+++ b/packages/client/src/components/app/charts/utils.test.js
@@ -0,0 +1,31 @@
+import { expect, describe, it, vi } from "vitest"
+import { cloneDeep } from "./utils"
+
+describe("utils", () => {
+ let context
+
+ beforeEach(() => {
+ vi.clearAllMocks()
+ context = {}
+ })
+
+ describe("cloneDeep", () => {
+ beforeEach(() => {
+ context.value = {
+ obj: { one: 1, two: 2 },
+ arr: [1, { first: null, second: undefined }, 2],
+ str: "test",
+ num: 123,
+ bool: true,
+ sym: Symbol("test"),
+ func: () => "some value",
+ }
+ context.cloneValue = cloneDeep(context.value)
+ })
+
+ it("to clone the object and not copy object references", () => {
+ expect(context.cloneValue.obj.one).toEqual(1)
+ expect(context.cloneValue.obj.two).toEqual(2)
+ })
+ })
+})
diff --git a/packages/client/src/utils/componentProps.js b/packages/client/src/utils/componentProps.js
index efe69938e7..bdf74c8014 100644
--- a/packages/client/src/utils/componentProps.js
+++ b/packages/client/src/utils/componentProps.js
@@ -108,7 +108,12 @@ export const getSettingsDefinition = definition => {
let settings = []
definition.settings?.forEach(setting => {
if (setting.section) {
- settings = settings.concat(setting.settings || [])
+ settings = settings.concat(
+ (setting.settings || [])?.map(childSetting => ({
+ ...childSetting,
+ sectionDependsOn: setting.dependsOn,
+ }))
+ )
} else {
settings.push(setting)
}
diff --git a/packages/server/src/api/controllers/component.ts b/packages/server/src/api/controllers/component.ts
index 12051be770..6d4d3e2d21 100644
--- a/packages/server/src/api/controllers/component.ts
+++ b/packages/server/src/api/controllers/component.ts
@@ -20,7 +20,8 @@ export async function fetchAppComponentDefinitions(ctx: UserCtx) {
const definitions: { [key: string]: any } = {}
for (let { manifest, library } of componentManifests) {
for (let key of Object.keys(manifest)) {
- if (key === "features") {
+ // These keys are not components, and should not be preprended with the `@budibase/` prefix
+ if (key === "features" || key === "typeSupportPresets") {
definitions[key] = manifest[key]
} else {
const fullComponentName = `${library}/${key}`.toLowerCase()
diff --git a/yarn.lock b/yarn.lock
index 36ce2ce75e..f26b25c6aa 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6291,6 +6291,11 @@
js-yaml "^3.10.0"
tslib "^2.4.0"
+"@yr/monotone-cubic-spline@^1.0.3":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@yr/monotone-cubic-spline/-/monotone-cubic-spline-1.0.3.tgz#7272d89f8e4f6fb7a1600c28c378cc18d3b577b9"
+ integrity sha512-FQXkOta0XBSUPHndIKON2Y9JeQz5ZeMqLYZVVK93FliNBFm7LNMIZmY6FrMEB9XPcDbE2bekMbZD6kzDkxwYjA==
+
"@zerodevx/svelte-json-view@^1.0.7":
version "1.0.7"
resolved "https://registry.yarnpkg.com/@zerodevx/svelte-json-view/-/svelte-json-view-1.0.7.tgz#abf3efa71dedcb3e9d16bc9cc61d5ea98c8d00b1"
@@ -6589,11 +6594,12 @@ anymatch@^3.0.3, anymatch@~3.1.2:
normalize-path "^3.0.0"
picomatch "^2.0.4"
-apexcharts@^3.19.2, apexcharts@^3.22.1:
- version "3.37.1"
- resolved "https://registry.yarnpkg.com/apexcharts/-/apexcharts-3.37.1.tgz#50443d302fc7fc72aace9c6c4074baae017c6950"
- integrity sha512-fmQ5Updeb/LASl+S1+mIxXUFxzY0Fa7gexfCs4o+OPP9f2NEBNjvybOtPrah44N4roK7U5o5Jis906QeEQu0cA==
+apexcharts@^3.48.0:
+ version "3.49.1"
+ resolved "https://registry.yarnpkg.com/apexcharts/-/apexcharts-3.49.1.tgz#837d1d4fd80f2c092f0587e8fb6bbc31ad57a0f3"
+ integrity sha512-MqGtlq/KQuO8j0BBsUJYlRG8VBctKwYdwuBtajHgHTmSgUU3Oai+8oYN/rKCXwXzrUlYA+GiMgotAIbXY2BCGw==
dependencies:
+ "@yr/monotone-cubic-spline" "^1.0.3"
svg.draggable.js "^2.2.2"
svg.easing.js "^2.0.0"
svg.filter.js "^2.0.2"
@@ -20039,13 +20045,6 @@ supports-preserve-symlinks-flag@^1.0.0:
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
-svelte-apexcharts@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/svelte-apexcharts/-/svelte-apexcharts-1.0.2.tgz#4e000f8b8f7c901c05658c845457dfc8314d54c1"
- integrity sha512-6qlx4rE+XsonZ0FZudfwqOQ34Pq+3wpxgAD75zgEmGoYhYBJcwmikTuTf3o8ZBsZue9U/pAwhNy3ed1Bkq1gmA==
- dependencies:
- apexcharts "^3.19.2"
-
svelte-dnd-action@^0.9.8:
version "0.9.22"
resolved "https://registry.yarnpkg.com/svelte-dnd-action/-/svelte-dnd-action-0.9.22.tgz#003eee9dddb31d8c782f6832aec8b1507fff194d"