diff --git a/packages/bbui/src/Form/Core/Multiselect.svelte b/packages/bbui/src/Form/Core/Multiselect.svelte index 7c1900fe2e..959d5c99e0 100644 --- a/packages/bbui/src/Form/Core/Multiselect.svelte +++ b/packages/bbui/src/Form/Core/Multiselect.svelte @@ -9,8 +9,10 @@ export let options = [] export let getOptionLabel = option => option export let getOptionValue = option => option - export let getOptionsIcon = () => null - export let getOptionsIconToolip = () => null + export let getOptionIcon = () => null + export let getOptionIconTooltip = () => null + export let getOptionTooltip = () => null + export let isOptionEnabled = () => true export let readonly = false export let autocomplete = false export let sort = false @@ -19,6 +21,7 @@ export let customPopoverHeight export let open = false export let loading + export let align const dispatch = createEventDispatcher() @@ -82,8 +85,10 @@ diff --git a/packages/bbui/src/Form/Core/Picker.svelte b/packages/bbui/src/Form/Core/Picker.svelte index f5c8398bec..d389abe60a 100644 --- a/packages/bbui/src/Form/Core/Picker.svelte +++ b/packages/bbui/src/Form/Core/Picker.svelte @@ -11,6 +11,13 @@ import Tags from "../../Tags/Tags.svelte" import Tag from "../../Tags/Tag.svelte" import ProgressCircle from "../../ProgressCircle/ProgressCircle.svelte" + import { + default as AbsTooltip, + TooltipPosition, + TooltipType, + } from "../../Tooltip/AbsTooltip.svelte" + import ContextTooltip from "../../Tooltip/Context.svelte" + export let id = null export let disabled = false @@ -27,6 +34,7 @@ export let getOptionValue = option => option export let getOptionIcon = () => null export let getOptionIconTooltip = () => null + export let getOptionTooltip = () => null export let useOptionIconImage = false export let getOptionColour = () => null export let getOptionSubtitle = () => null @@ -48,6 +56,11 @@ let button let component + let contextTooltipId = 0; + let contextTooltipAnchor = null + let contextTooltipOption = null + let contextTooltipVisible = false + $: sortedOptions = getSortedOptions(options, getOptionLabel, sort) $: filteredOptions = getFilteredOptions( sortedOptions, @@ -103,6 +116,29 @@ onDestroy(() => { component?.removeEventListener("scroll", null) }) + + const handleMouseenter = (e, option) => { + contextTooltipId += 1; + const invokedContextTooltipId = contextTooltipId + + setTimeout(() => { + if (contextTooltipId === invokedContextTooltipId) { + contextTooltipAnchor = e.target; + contextTooltipOption = option; + contextTooltipVisible = true; + } else { + console.log("not long enough"); + } + }, 400) + } + + const handleMouseleave = (e, option) => { + setTimeout(() => { + if (option === contextTooltipOption) { + contextTooltipVisible = false; + } + }, 600) + } (open = false)} - > - {#if autocomplete} - (searchTerm = event.detail)} - {disabled} - placeholder="Search" - /> - {/if} - - {#if placeholderOption} - onSelectOption(null)} - > - {placeholderOption} - - - - + class="popover-content" + class:auto-width={autoWidth} + use:clickOutside={() => (open = false)} + > + {#if autocomplete} + (searchTerm = event.detail)} + {disabled} + placeholder="Search" + /> {/if} - {#if filteredOptions.length} - {#each filteredOptions as option, idx} + + {#if placeholderOption} onSelectOption(getOptionValue(option, idx))} - class:is-disabled={!isOptionEnabled(option)} + on:click={() => onSelectOption(null)} > - {#if getOptionIcon(option, idx)} - - {#if useoptioniconimage} - - {:else} - - {/if} - - {/if} - {#if getOptionColour(option, idx)} - - - - {/if} - - {getOptionLabel(option, idx)} - {#if getOptionSubtitle(option, idx)} - - {getOptionSubtitle(option, idx)} - - {/if} - - {#if option.tag} - - - {option.tag} - - - {/if} + {placeholderOption} - {/each} + {/if} + {#if filteredOptions.length} + {#each filteredOptions as option, idx} + handleMouseenter(e, option)} + on:mouseleave={(e) => handleMouseleave(e, option)} + class="spectrum-Menu-item" + class:is-selected={isOptionSelected(getOptionValue(option, idx))} + role="option" + aria-selected="true" + tabindex="0" + on:click={() => onSelectOption(getOptionValue(option, idx))} + class:is-disabled={!isOptionEnabled(option)} + > + {#if getOptionIcon(option, idx)} + + {#if useOptionIconImage} + + {:else} + + {/if} + + {/if} + {#if getOptionColour(option, idx)} + + + + {/if} + + {getOptionLabel(option, idx)} + {#if getOptionSubtitle(option, idx)} + + {getOptionSubtitle(option, idx)} + + {/if} + + {#if option.tag} + + + {option.tag} + + + {/if} + + + + + {/each} + {/if} + + + {#if loading} + + + {/if} - - {#if loading} - - - - {/if} - - {#if footer} - - {/if} + {#if footer} + + {/if} + + + + + {contextTooltipOption} - + diff --git a/packages/builder/src/components/design/settings/controls/FieldSelect.svelte b/packages/builder/src/components/design/settings/controls/FieldSelect.svelte index e50a0e8030..187bfa190d 100644 --- a/packages/builder/src/components/design/settings/controls/FieldSelect.svelte +++ b/packages/builder/src/components/design/settings/controls/FieldSelect.svelte @@ -3,14 +3,36 @@ import { getDatasourceForProvider, getSchemaForDatasource } from "dataBinding" import { selectedScreen } from "stores/builder" import { createEventDispatcher } from "svelte" + import { validators, supported, partialSupport, unsupported } from "../fieldValidator"; export let componentInstance = {} export let value = "" export let placeholder + export let fieldValidator + + $: { + console.log(fieldValidator); + } + + const getFieldSupport = (schema, fieldValidator) => { + if (fieldValidator == null) { + return {} + } + + const validator = validators[fieldValidator]; + + const fieldSupport = {} + Object.entries(schema || {}).forEach(([key, value]) => { + fieldSupport[key] = validator(value) + }) + + return fieldSupport + } const dispatch = createEventDispatcher() $: datasource = getDatasourceForProvider($selectedScreen, componentInstance) $: schema = getSchemaForDatasource($selectedScreen, datasource).schema + $: fieldSupport = getFieldSupport(schema, fieldValidator); $: options = Object.keys(schema || {}) $: boundValue = getValidValue(value, options) @@ -32,6 +54,32 @@ boundValue = getValidValue(value.detail, options) dispatch("change", boundValue) } + + const getOptionIcon = option => { + const support = fieldSupport[option]?.support; + + if (support == null) return null; + if (support === supported) return null + + if (support === partialSupport) return "Warning" + if (support === unsupported) return "Error" + } + + const getOptionIconTooltip = option => { + } + + const isOptionEnabled = option => { + const support = fieldSupport[option]?.support; + + if (support == null) return true + if (support == unsupported) return false + + return true + } - + diff --git a/packages/builder/src/components/design/settings/controls/MultiFieldSelect.svelte b/packages/builder/src/components/design/settings/controls/MultiFieldSelect.svelte index d0224fb1db..b2a308596c 100644 --- a/packages/builder/src/components/design/settings/controls/MultiFieldSelect.svelte +++ b/packages/builder/src/components/design/settings/controls/MultiFieldSelect.svelte @@ -3,17 +3,60 @@ import { getDatasourceForProvider, getSchemaForDatasource } from "dataBinding" import { selectedScreen } from "stores/builder" import { createEventDispatcher } from "svelte" + import { validators, supported, partialSupport, unsupported } from "../fieldValidator"; export let componentInstance = {} export let value = "" export let placeholder + export let fieldValidator + + const TypeIconMap = { + text: "Text", + options: "Dropdown", + datetime: "Date", + barcodeqr: "Camera", + longform: "TextAlignLeft", + array: "Dropdown", + number: "123", + boolean: "Boolean", + attachment: "AppleFiles", + link: "DataCorrelated", + formula: "Calculator", + json: "Brackets", + bigint: "TagBold", + bb_reference: { + user: "User", + users: "UserGroup", + }, + } + + + const getFieldSupport = (schema, fieldValidator) => { + if (fieldValidator == null) { + return {} + } + + const validator = validators[fieldValidator]; + + const fieldSupport = {} + Object.entries(schema || {}).forEach(([key, value]) => { + fieldSupport[key] = validator(value) + }) + + return fieldSupport + } const dispatch = createEventDispatcher() $: datasource = getDatasourceForProvider($selectedScreen, componentInstance) $: schema = getSchemaForDatasource($selectedScreen, datasource).schema $: options = Object.keys(schema || {}) + $: fieldSupport = getFieldSupport(schema, fieldValidator); $: boundValue = getValidOptions(value, options) + $: { + console.log(schema) + } + const getValidOptions = (selectedOptions, allOptions) => { // Fix the hardcoded default string value if (!Array.isArray(selectedOptions)) { @@ -26,6 +69,69 @@ boundValue = getValidOptions(value.detail, options) dispatch("change", boundValue) } + + const foo = () => { + const support = fieldSupport[option]?.support; + + if (support == null) return null; + if (support === supported) return null + + if (support === partialSupport) return "AlertCircleFilled" + if (support === unsupported) return "AlertCircleFilled" + } + + const getOptionIcon = optionKey => { + const option = schema[optionKey] + if (option.autocolumn) { + return "MagicWand" + } + const { type, subtype } = option + + const result = + typeof TypeIconMap[type] === "object" && subtype + ? TypeIconMap[type][subtype] + : TypeIconMap[type] + + return result || "Text" + } + + const getOptionIconTooltip = optionKey => { + const option = schema[optionKey] + + return option.type; + } + + const getOptionTooltip = optionKey => { + console.log(optionKey) + const support = fieldSupport[optionKey]?.support; + const message = fieldSupport[optionKey]?.message; + + if (support === unsupported) return message + + return null + } + + const isOptionEnabled = optionKey => { + // Remain enabled if already selected, so it can be deselected + if (value?.includes(optionKey)) return true + const support = fieldSupport[optionKey]?.support; + + if (support == null) return true + if (support === unsupported) return false + + return true + } - + diff --git a/packages/builder/src/components/design/settings/fieldValidator.js b/packages/builder/src/components/design/settings/fieldValidator.js index 7d0523b027..8a3eb89691 100644 --- a/packages/builder/src/components/design/settings/fieldValidator.js +++ b/packages/builder/src/components/design/settings/fieldValidator.js @@ -2,7 +2,7 @@ export const unsupported = Symbol("values-validator-unsupported") export const partialSupport = Symbol("values-validator-partial-support") export const supported = Symbol("values-validator-supported") -const validatorMap = { +export const validators = { chart: (fieldSchema) => { if ( fieldSchema.type === "json" || @@ -14,7 +14,7 @@ const validatorMap = { ) { return { support: unsupported, - message: "This field cannot be used as a chart value" + message: `"${fieldSchema.type}" columns cannot be used as a chart value long long long long long long long long long` } } @@ -38,5 +38,3 @@ const validatorMap = { } } }; -export default validatorMap; - diff --git a/packages/client/manifest.json b/packages/client/manifest.json index ee092a3a86..faec6e374f 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -1629,7 +1629,7 @@ "required": true }, { - "type": "chartmultifield", + "type": "multifield", "label": "Data columns", "key": "valueColumns", "dependsOn": "dataProvider", @@ -1788,7 +1788,7 @@ "required": true }, { - "type": "chartmultifield", + "type": "multifield", "label": "Data columns", "key": "valueColumns", "dependsOn": "dataProvider", @@ -1941,7 +1941,7 @@ "required": true }, { - "type": "chartmultifield", + "type": "multifield", "label": "Data columns", "key": "valueColumns", "dependsOn": "dataProvider", @@ -2106,7 +2106,7 @@ "required": true }, { - "type": "chartfield", + "type": "field", "label": "Data column", "key": "valueColumn", "dependsOn": "dataProvider", @@ -2235,7 +2235,7 @@ "required": true }, { - "type": "chartfield", + "type": "field", "label": "Data columns", "key": "valueColumn", "dependsOn": "dataProvider", @@ -2364,28 +2364,28 @@ "required": true }, { - "type": "chartfield", + "type": "field", "label": "Open column", "key": "openColumn", "dependsOn": "dataProvider", "required": true }, { - "type": "chartfield", + "type": "field", "label": "Close column", "key": "closeColumn", "dependsOn": "dataProvider", "required": true }, { - "type": "chartfield", + "type": "field", "label": "High column", "key": "highColumn", "dependsOn": "dataProvider", "required": true }, { - "type": "chartfield", + "type": "field", "label": "Low column", "key": "lowColumn", "dependsOn": "dataProvider", @@ -2449,7 +2449,7 @@ "required": true }, { - "type": "chartfield", + "type": "field", "label": "Data column", "key": "valueColumn", "dependsOn": "dataProvider", @@ -5266,7 +5266,7 @@ "required": true }, { - "type": "chartfield", + "type": "field", "label": "Data column", "key": "valueColumn", "dependsOn": "dataSource", @@ -5291,7 +5291,7 @@ "required": true }, { - "type": "chartfield", + "type": "field", "label": "Data column", "key": "valueColumn", "dependsOn": "dataSource", @@ -5316,7 +5316,7 @@ "required": true }, { - "type": "chartmultifield", + "type": "multifield", "label": "Data columns", "key": "valueColumns", "dependsOn": "dataSource", @@ -5363,7 +5363,7 @@ }, "settings": [ { - "type": "chartfield", + "type": "field", "label": "Value column", "key": "valueColumn", "dependsOn": "dataSource", @@ -5411,7 +5411,7 @@ "required": true }, { - "type": "chartmultifield", + "type": "multifield", "label": "Data columns", "key": "valueColumns", "dependsOn": "dataSource", @@ -5460,7 +5460,7 @@ "required": true }, { - "type": "chartmultifield", + "type": "multifield", "label": "Data columns", "key": "valueColumns", "dependsOn": "dataSource", @@ -5521,28 +5521,28 @@ "required": true }, { - "type": "chartfield", + "type": "field", "label": "Open column", "key": "openColumn", "dependsOn": "dataSource", "required": true }, { - "type": "chartfield", + "type": "field", "label": "Close column", "key": "closeColumn", "dependsOn": "dataSource", "required": true }, { - "type": "chartfield", + "type": "field", "label": "High column", "key": "highColumn", "dependsOn": "dataSource", "required": true }, { - "type": "chartfield", + "type": "field", "label": "Low column", "key": "lowColumn", "dependsOn": "dataSource",