1
0
Fork 0
mirror of synced 2024-08-08 22:58:24 +12:00

wip refactor manifest

This commit is contained in:
Gerard Burns 2024-04-10 08:37:33 +01:00
parent e08d745360
commit cd36056124
10 changed files with 201 additions and 227 deletions

View file

@ -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)}

View file

@ -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
/> />

View file

@ -0,0 +1,2 @@
export { default as FieldContext } from "./FieldContext.svelte"
export * from "./validator"

View file

@ -17,66 +17,64 @@ export const constants = {
supported: Symbol("values-validator-supported") supported: Symbol("values-validator-supported")
} }
export const validators = { export const validate = (fieldSchema) => {
chart: (fieldSchema) => { try {
try { const response = {
const response = { level: null,
level: null, warnings: [],
warnings: [], errors: [],
errors: [], text: "",
text: "", icon: "",
icon: "", iconColor: ""
iconColor: "" }
} const generalUnsupportedFields = ["array", "attachment", "barcodeqr", "link", "bb_reference"]
const generalUnsupportedFields = ["array", "attachment", "barcodeqr", "link", "bb_reference"] if (generalUnsupportedFields.includes(fieldSchema.type)) {
if (generalUnsupportedFields.includes(fieldSchema.type)) { response.errors.push(errors.general)
response.errors.push(errors.general) }
}
if (fieldSchema.type === "json") { if (fieldSchema.type === "json") {
response.errors.push(errors.jsonPrimitivesOnly) response.errors.push(errors.jsonPrimitivesOnly)
} }
if (fieldSchema.type === "string") { if (fieldSchema.type === "string") {
response.warnings.push(warnings.stringAsNumber) response.warnings.push(warnings.stringAsNumber)
} }
if (fieldSchema.type === "datetime") { if (fieldSchema.type === "datetime") {
response.warnings.push(warnings.chartDatetime); response.warnings.push(warnings.chartDatetime);
//"This column can be used as an input for a chart, but it may be parsed differently depending on which is used. //"This column can be used as an input for a chart, but it may be parsed differently depending on which is used.
} }
const isRequired = fieldSchema?.constraints?.presence?.allowEmpty === false const isRequired = fieldSchema?.constraints?.presence?.allowEmpty === false
if (!isRequired) { if (!isRequired) {
response.warnings.push(warnings.notRequired); response.warnings.push(warnings.notRequired);
} }
if (response.errors.length > 0) { if (response.errors.length > 0) {
response.level = constants.unsupported response.level = constants.unsupported
response.text = "Not compatible" response.text = "Not compatible"
response.icon = "Alert" response.icon = "Alert"
response.iconColor = "var(--red)" response.iconColor = "var(--red)"
} else if (response.warnings.length > 0) { } else if (response.warnings.length > 0) {
response.level = constants.partialSupport response.level = constants.partialSupport
response.text = "Partially compatible" response.text = "Partially compatible"
response.icon = "AlertCheck" response.icon = "AlertCheck"
response.iconColor = "var(--yellow)" response.iconColor = "var(--yellow)"
} else { } else {
response.level = constants.supported response.level = constants.supported
response.text = "Compatible" response.text = "Compatible"
response.icon = "CheckmarkCircle" response.icon = "CheckmarkCircle"
response.iconColor = "var(--green)" response.iconColor = "var(--green)"
} }
return response return response
} catch (e) { } catch (e) {
return { return {
level: constants.partialSupport, level: constants.partialSupport,
warnings: [], warnings: [],
errors: [], errors: [],
text: "Partially compatible", text: "Partially compatible",
icon: "AlertCheck", icon: "AlertCheck",
iconColor: "var(--yellow)" iconColor: "var(--yellow)"
}
} }
} }
}; }

View file

@ -1,25 +1,32 @@
<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 { validators, constants as validatorConstants } from "../fieldValidator"; import { FieldContext, validate } from './FieldContext'
import { debounce } from "lodash"
import { goto, 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 fieldValidator export let columnContext
const getFieldSupport = (schema, fieldValidator) => { let contextTooltipAnchor = null
if (fieldValidator == null) { let currentOption = null
let previousOption = null
let contextTooltipVisible = false
const getFieldSupport = (schema, columnContext) => {
if (!columnContext) {
return {} return {}
} }
const validator = validators[fieldValidator];
const fieldSupport = {} const fieldSupport = {}
Object.entries(schema || {}).forEach(([key, value]) => { Object.entries(schema || {}).forEach(([key, value]) => {
fieldSupport[key] = validator(value) fieldSupport[key] = validate(value)
}) })
return fieldSupport return fieldSupport
@ -28,7 +35,7 @@
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
$: datasource = getDatasourceForProvider($selectedScreen, componentInstance) $: datasource = getDatasourceForProvider($selectedScreen, componentInstance)
$: schema = getSchemaForDatasource($selectedScreen, datasource).schema $: schema = getSchemaForDatasource($selectedScreen, datasource).schema
$: fieldSupport = getFieldSupport(schema, fieldValidator); $: fieldSupport = getFieldSupport(schema, columnContext);
$: options = Object.keys(schema || {}) $: options = Object.keys(schema || {})
$: boundValue = getValidValue(value, options) $: boundValue = getValidValue(value, options)
@ -51,34 +58,87 @@
dispatch("change", boundValue) dispatch("change", boundValue)
} }
const getOptionIcon = option => { const updateTooltip = debounce((e, option) => {
/* if (option == null) {
const support = fieldSupport[option]?.support; contextTooltipVisible = false;
} else {
contextTooltipAnchor = e?.target;
previousOption = currentOption;
currentOption = option;
contextTooltipVisible = true;
}
}, 200);
if (support == null) return null; const onOptionMouseenter = (e, option, idx) => {
if (support === supported) return null updateTooltip(e, option);
if (support === partialSupport) return "Warning"
if (support === unsupported) return "Error"
*/
} }
const getOptionIconTooltip = option => { const onOptionMouseleave = (e, option) => {
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 isOptionEnabled = option => { const getOptionIconTooltip = optionKey => {
return true const option = schema[optionKey]
const support = fieldSupport[option]?.support;
if (support == null) return true const type = option?.type;
if (support == unsupported) return false const field = Object.values(FIELDS).find(f => f.type === type)
return true if (field) {
return field.name
}
return ""
} }
</script> </script>
<Select <Select
{isOptionEnabled} {placeholder}
{getOptionIcon} value={boundValue}
{getOptionIconTooltip} on:change={onChange}
{placeholder} value={boundValue} on:change={onChange} {options} /> {options}
{onOptionMouseenter}
{onOptionMouseleave}
/>
{#if columnContext}
<ContextTooltip
visible={contextTooltipVisible}
anchor={contextTooltipAnchor}
offset={20}
>
<FieldContext
explanationModal
tableHref={`/builder/app/${$params.application}/data/table/${datasource?.tableId}`}
schema={schema[currentOption]}
support={fieldSupport[currentOption]}
columnIcon={getOptionIcon(currentOption)}
columnName={currentOption}
columnType={getOptionIconTooltip(currentOption)}
/>
<FieldContext
slot="previous"
schema={schema[previousOption]}
support={fieldSupport[previousOption]}
columnIcon={getOptionIcon(previousOption)}
columnName={previousOption}
columnType={getOptionIconTooltip(previousOption)}
/>
</ContextTooltip>
{/if}

View file

@ -1,54 +1,39 @@
<script> <script>
import { Icon, Heading, Multiselect, ContextTooltip } from "@budibase/bbui" import { Icon, Heading, Multiselect, ContextTooltip } from "@budibase/bbui"
import { getDatasourceForProvider, getSchemaForDatasource } from "dataBinding" import { getDatasourceForProvider, getSchemaForDatasource } from "dataBinding"
import { selectedScreen } from "stores/builder" import { selectedScreen,
componentStore,
} from "stores/builder"
import { createEventDispatcher } from "svelte" import { createEventDispatcher } from "svelte"
import { validators } from "./FieldContext/Validator"; import { FieldContext, validate } from './FieldContext'
import ChartFieldContext from './FieldContext/Chart.svelte'
import { FIELDS } from 'constants/backend' import { FIELDS } from 'constants/backend'
import { goto, params } from "@roxi/routify" import { goto, params } from "@roxi/routify"
import { debounce } from "lodash" 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 fieldValidator export let columnContext
export let valueTypes
let contextTooltipAnchor = null let contextTooltipAnchor = null
let currentOption = null let currentOption = null
let previousOption = null let previousOption = null
let contextTooltipVisible = false let contextTooltipVisible = false
const TypeIconMap = { $: componentDefinition = componentStore.getDefinition(
text: "Text", componentInstance?._component
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) => { const getFieldSupport = (schema, columnContext) => {
if (fieldValidator == null) { if (!columnContext) {
return {} return {}
} }
const validator = validators[fieldValidator];
const fieldSupport = {} const fieldSupport = {}
Object.entries(schema || {}).forEach(([key, value]) => { Object.entries(schema || {}).forEach(([key, value]) => {
fieldSupport[key] = validator(value) fieldSupport[key] = validate(value)
}) })
return fieldSupport return fieldSupport
@ -58,7 +43,7 @@
$: datasource = getDatasourceForProvider($selectedScreen, componentInstance) $: datasource = getDatasourceForProvider($selectedScreen, componentInstance)
$: schema = getSchemaForDatasource($selectedScreen, datasource).schema $: schema = getSchemaForDatasource($selectedScreen, datasource).schema
$: options = Object.keys(schema || {}) $: options = Object.keys(schema || {})
$: fieldSupport = getFieldSupport(schema, fieldValidator); $: fieldSupport = getFieldSupport(schema, columnContext);
$: boundValue = getValidOptions(value, options) $: boundValue = getValidOptions(value, options)
@ -85,9 +70,9 @@
const { type, subtype } = option const { type, subtype } = option
const result = const result =
typeof TypeIconMap[type] === "object" && subtype typeof Constants.TypeIconMap[type] === "object" && subtype
? TypeIconMap[type][subtype] ? Constants.TypeIconMap[type][subtype]
: TypeIconMap[type] : Constants.TypeIconMap[type]
return result || "Text" return result || "Text"
} }
@ -139,7 +124,6 @@
<Multiselect <Multiselect
iconPosition="right" iconPosition="right"
{isOptionEnabled}
{placeholder} {placeholder}
value={boundValue} value={boundValue}
on:change={setValue} on:change={setValue}
@ -148,29 +132,29 @@
{onOptionMouseenter} {onOptionMouseenter}
{onOptionMouseleave} {onOptionMouseleave}
/> />
<ContextTooltip
visible={contextTooltipVisible}
anchor={contextTooltipAnchor}
offset={20}
>
<ChartFieldContext
explanationModal
tableHref={`/builder/app/${$params.application}/data/table/${datasource?.tableId}`}
schema={schema[currentOption]}
support={fieldSupport[currentOption]}
columnIcon={getOptionIcon(currentOption)}
columnName={currentOption}
columnType={getOptionIconTooltip(currentOption)}
/>
<ChartFieldContext
slot="previous"
schema={schema[previousOption]}
support={fieldSupport[previousOption]}
columnIcon={getOptionIcon(previousOption)}
columnName={previousOption}
columnType={getOptionIconTooltip(previousOption)}
/>
</ContextTooltip>
<style> {#if columnContext}
</style> <ContextTooltip
visible={contextTooltipVisible}
anchor={contextTooltipAnchor}
offset={20}
>
<FieldContext
explanationModal
tableHref={`/builder/app/${$params.application}/data/table/${datasource?.tableId}`}
schema={schema[currentOption]}
support={fieldSupport[currentOption]}
columnIcon={getOptionIcon(currentOption)}
columnName={currentOption}
columnType={getOptionIconTooltip(currentOption)}
/>
<FieldContext
slot="previous"
schema={schema[previousOption]}
support={fieldSupport[previousOption]}
columnIcon={getOptionIcon(previousOption)}
columnName={previousOption}
columnType={getOptionIconTooltip(previousOption)}
/>
</ContextTooltip>
{/if}

View file

@ -1,82 +0,0 @@
import { capitalize } from 'lodash';
export const errors = {
general: Symbol("values-validator-general"),
jsonPrimitivesOnly: Symbol("values-validator-json-primitives-only"),
}
export const warnings = {
stringAsNumber: Symbol("values-validator-string-as-number"),
chartDatetime: Symbol("values-validator-chart-datetime"),
notRequired: Symbol("values-validator-not-required"),
}
export const constants = {
unsupported: Symbol("values-validator-unsupported"),
partialSupport: Symbol("values-validator-partialSupport"),
supported: Symbol("values-validator-supported")
}
export const validators = {
chart: (fieldSchema) => {
try {
const response = {
level: null,
warnings: [],
errors: [],
text: "",
icon: "",
iconColor: ""
}
const generalUnsupportedFields = ["array", "attachment", "barcodeqr", "link", "bb_reference"]
if (generalUnsupportedFields.includes(fieldSchema.type)) {
response.errors.push(errors.general)
}
if (fieldSchema.type === "json") {
response.errors.push(errors.jsonPrimitivesOnly)
}
if (fieldSchema.type === "string") {
response.warnings.push(warnings.stringAsNumber)
}
if (fieldSchema.type === "datetime") {
response.warnings.push(warnings.chartDatetime);
//"This column can be used as an input for a chart, but it may be parsed differently depending on which is used.
}
const isRequired = fieldSchema?.constraints?.presence?.allowEmpty === false
if (!isRequired) {
response.warnings.push(warnings.notRequired);
}
if (response.errors.length > 0) {
response.level = constants.unsupported
response.text = "Not compatible"
response.icon = "Alert"
response.iconColor = "var(--red)"
} else if (response.warnings.length > 0) {
response.level = constants.partialSupport
response.text = "Partially compatible"
response.icon = "AlertCheck"
response.iconColor = "var(--yellow)"
} else {
response.level = constants.supported
response.text = "Compatible"
response.icon = "CheckmarkCircle"
response.iconColor = "var(--green)"
}
return response
} catch (e) {
return {
level: constants.partialSupport,
warnings: [],
errors: [],
text: "Partially compatible",
icon: "AlertCheck",
iconColor: "var(--yellow)"
}
}
}
};

View file

@ -193,7 +193,8 @@
max: setting.max ?? null, max: setting.max ?? null,
// Field select settings // Field select settings
fieldValidator: setting.fieldValidator, columnContext: setting.columnContext,
valueTypes: setting.valueTypes
}} }}
{bindings} {bindings}
{componentBindings} {componentBindings}

View file

@ -1602,6 +1602,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",
@ -1633,7 +1634,8 @@
"label": "Data columns", "label": "Data columns",
"key": "valueColumns", "key": "valueColumns",
"dependsOn": "dataProvider", "dependsOn": "dataProvider",
"fieldValidator": "chart", "valueTypes": ["number"],
"columnContext": true,
"required": true "required": true
}, },
{ {