Initial commit
This commit is contained in:
parent
3167fcdc76
commit
cb2a19620b
|
@ -44,7 +44,9 @@
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
border-bottom: var(--border-light);
|
border-bottom: var(--border-light);
|
||||||
}
|
}
|
||||||
|
.property-group-container:last-child {
|
||||||
|
border-bottom: 0px;
|
||||||
|
}
|
||||||
.property-group-name {
|
.property-group-name {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -93,6 +93,40 @@ const INITIAL_FRONTEND_STATE = {
|
||||||
tourNodes: null,
|
tourNodes: null,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const updateComponentSetting = (name, value) => {
|
||||||
|
return component => {
|
||||||
|
if (!name || !component) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Skip update if the value is the same
|
||||||
|
if (component[name] === value) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const settings = getComponentSettings(component._component)
|
||||||
|
const updatedSetting = settings.find(setting => setting.key === name)
|
||||||
|
|
||||||
|
if (
|
||||||
|
updatedSetting?.type === "dataSource" ||
|
||||||
|
updatedSetting?.type === "table"
|
||||||
|
) {
|
||||||
|
const { schema } = getSchemaForDatasource(null, value)
|
||||||
|
const columnNames = Object.keys(schema || {})
|
||||||
|
const multifieldKeysToSelectAll = settings
|
||||||
|
.filter(setting => {
|
||||||
|
return setting.type === "multifield" && setting.selectAllFields
|
||||||
|
})
|
||||||
|
.map(setting => setting.key)
|
||||||
|
|
||||||
|
multifieldKeysToSelectAll.forEach(key => {
|
||||||
|
component[key] = columnNames
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
component[name] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const getFrontendStore = () => {
|
export const getFrontendStore = () => {
|
||||||
const store = writable({ ...INITIAL_FRONTEND_STATE })
|
const store = writable({ ...INITIAL_FRONTEND_STATE })
|
||||||
let websocket
|
let websocket
|
||||||
|
@ -111,13 +145,18 @@ export const getFrontendStore = () => {
|
||||||
}
|
}
|
||||||
let clone = cloneDeep(screen)
|
let clone = cloneDeep(screen)
|
||||||
const result = patchFn(clone)
|
const result = patchFn(clone)
|
||||||
|
console.log("sequentialScreenPatch ", result)
|
||||||
if (result === false) {
|
if (result === false) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return await store.actions.screens.save(clone)
|
return
|
||||||
|
//return await store.actions.screens.save(clone)
|
||||||
})
|
})
|
||||||
|
|
||||||
store.actions = {
|
store.actions = {
|
||||||
|
tester: (name, value) => {
|
||||||
|
return updateComponentSetting(name, value)
|
||||||
|
},
|
||||||
reset: () => {
|
reset: () => {
|
||||||
store.set({ ...INITIAL_FRONTEND_STATE })
|
store.set({ ...INITIAL_FRONTEND_STATE })
|
||||||
websocket?.disconnect()
|
websocket?.disconnect()
|
||||||
|
@ -825,6 +864,7 @@ export const getFrontendStore = () => {
|
||||||
},
|
},
|
||||||
patch: async (patchFn, componentId, screenId) => {
|
patch: async (patchFn, componentId, screenId) => {
|
||||||
// Use selected component by default
|
// Use selected component by default
|
||||||
|
console.log("front end patch")
|
||||||
if (!componentId || !screenId) {
|
if (!componentId || !screenId) {
|
||||||
const state = get(store)
|
const state = get(store)
|
||||||
componentId = componentId || state.selectedComponentId
|
componentId = componentId || state.selectedComponentId
|
||||||
|
@ -834,6 +874,7 @@ export const getFrontendStore = () => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const patchScreen = screen => {
|
const patchScreen = screen => {
|
||||||
|
// findComponent looks in the tree not comp.settings[0]
|
||||||
let component = findComponent(screen.props, componentId)
|
let component = findComponent(screen.props, componentId)
|
||||||
if (!component) {
|
if (!component) {
|
||||||
return false
|
return false
|
||||||
|
@ -842,6 +883,18 @@ export const getFrontendStore = () => {
|
||||||
}
|
}
|
||||||
await store.actions.screens.patch(patchScreen, screenId)
|
await store.actions.screens.patch(patchScreen, screenId)
|
||||||
},
|
},
|
||||||
|
// Temporary
|
||||||
|
customPatch: async (patchFn, componentId, screenId) => {
|
||||||
|
console.log("patchUpdate :")
|
||||||
|
if (!componentId || !screenId) {
|
||||||
|
const state = get(store)
|
||||||
|
componentId = componentId || state.selectedComponentId
|
||||||
|
screenId = screenId || state.selectedScreenId
|
||||||
|
}
|
||||||
|
if (!componentId || !screenId || !patchFn) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
},
|
||||||
delete: async component => {
|
delete: async component => {
|
||||||
if (!component) {
|
if (!component) {
|
||||||
return
|
return
|
||||||
|
@ -1207,37 +1260,9 @@ export const getFrontendStore = () => {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
updateSetting: async (name, value) => {
|
updateSetting: async (name, value) => {
|
||||||
await store.actions.components.patch(component => {
|
await store.actions.components.patch(
|
||||||
if (!name || !component) {
|
updateComponentSetting(name, value)
|
||||||
return false
|
)
|
||||||
}
|
|
||||||
// Skip update if the value is the same
|
|
||||||
if (component[name] === value) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const settings = getComponentSettings(component._component)
|
|
||||||
const updatedSetting = settings.find(setting => setting.key === name)
|
|
||||||
|
|
||||||
if (
|
|
||||||
updatedSetting?.type === "dataSource" ||
|
|
||||||
updatedSetting?.type === "table"
|
|
||||||
) {
|
|
||||||
const { schema } = getSchemaForDatasource(null, value)
|
|
||||||
const columnNames = Object.keys(schema || {})
|
|
||||||
const multifieldKeysToSelectAll = settings
|
|
||||||
.filter(setting => {
|
|
||||||
return setting.type === "multifield" && setting.selectAllFields
|
|
||||||
})
|
|
||||||
.map(setting => setting.key)
|
|
||||||
|
|
||||||
multifieldKeysToSelectAll.forEach(key => {
|
|
||||||
component[key] = columnNames
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
component[name] = value
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
requestEjectBlock: componentId => {
|
requestEjectBlock: componentId => {
|
||||||
store.actions.preview.sendEvent("eject-block", componentId)
|
store.actions.preview.sendEvent("eject-block", componentId)
|
||||||
|
|
|
@ -0,0 +1,161 @@
|
||||||
|
<!-- FormBlockFieldSettingsPopover -->
|
||||||
|
<script>
|
||||||
|
import { Icon, Popover, Layout } from "@budibase/bbui"
|
||||||
|
import { store } from "builderStore"
|
||||||
|
import { cloneDeep } from "lodash/fp"
|
||||||
|
|
||||||
|
import ComponentSettingsSection from "../../../../../pages/builder/app/[application]/design/[screenId]/components/[componentId]/_components/settings/ComponentSettingsSection.svelte"
|
||||||
|
|
||||||
|
export let anchor
|
||||||
|
export let field
|
||||||
|
|
||||||
|
export let componentBindings
|
||||||
|
export let bindings
|
||||||
|
export let parent
|
||||||
|
|
||||||
|
let popover
|
||||||
|
|
||||||
|
$: sudoComponentInstance = buildSudoInstance(field)
|
||||||
|
$: componentDef = store.actions.components.getDefinition(field._component)
|
||||||
|
$: parsedComponentDef = processComponentDefinitionSettings(componentDef)
|
||||||
|
|
||||||
|
const buildSudoInstance = instance => {
|
||||||
|
let clone = cloneDeep(instance)
|
||||||
|
|
||||||
|
// only do this IF necessary
|
||||||
|
const instanceCheck = store.actions.components.createInstance(
|
||||||
|
clone._component,
|
||||||
|
{
|
||||||
|
_instanceName: instance.displayName,
|
||||||
|
field: instance.name, //Must be fixed
|
||||||
|
label: instance.displayName,
|
||||||
|
placeholder: instance.displayName,
|
||||||
|
},
|
||||||
|
{} //?
|
||||||
|
)
|
||||||
|
|
||||||
|
// mutating on load would achieve this.
|
||||||
|
// Would need to replace the entire config at this point
|
||||||
|
// console.log(instanceCheck)
|
||||||
|
|
||||||
|
return instanceCheck
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensures parent bindings are pushed down
|
||||||
|
// Confirm this
|
||||||
|
const processComponentDefinitionSettings = componentDef => {
|
||||||
|
const clone = cloneDeep(componentDef)
|
||||||
|
clone.settings.forEach(setting => {
|
||||||
|
if (setting.type === "text") {
|
||||||
|
setting.nested = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return clone
|
||||||
|
}
|
||||||
|
|
||||||
|
// Current core update setting fn
|
||||||
|
const updateSetting = async (setting, value) => {
|
||||||
|
console.log("Custom Save Setting", setting, value)
|
||||||
|
console.log("The parent", parent)
|
||||||
|
|
||||||
|
//updateBlockFieldSetting in frontend?
|
||||||
|
const nestedFieldInstance = cloneDeep(sudoComponentInstance)
|
||||||
|
|
||||||
|
// Parse the current fields on load and check for unbuilt instances
|
||||||
|
// This is icky
|
||||||
|
let parentFieldsSettings = parent.fields.find(
|
||||||
|
pSetting => pSetting.name === nestedFieldInstance.field
|
||||||
|
)
|
||||||
|
|
||||||
|
//In this scenario it may be best to extract
|
||||||
|
store.actions.tester(setting.key, value)(nestedFieldInstance) //mods the internal val
|
||||||
|
|
||||||
|
parentFieldsSettings = cloneDeep(nestedFieldInstance)
|
||||||
|
|
||||||
|
console.log("UPDATED nestedFieldInstance", nestedFieldInstance)
|
||||||
|
|
||||||
|
//Overwrite all the fields
|
||||||
|
await store.actions.components.updateSetting("fields", parent.fields)
|
||||||
|
|
||||||
|
/*
|
||||||
|
ignore/disabled _instanceName > this will be handled in the new header field.
|
||||||
|
ignore/disabled field > this should be populated and hidden.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Icon
|
||||||
|
name="Settings"
|
||||||
|
hoverable
|
||||||
|
size="S"
|
||||||
|
on:click={() => {
|
||||||
|
popover.show()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Popover bind:this={popover} {anchor} align="left-outside">
|
||||||
|
<Layout noPadding>
|
||||||
|
<!--
|
||||||
|
property-group-container - has a border, is there a scenario where it doesnt render?
|
||||||
|
|
||||||
|
FormBlock Default behaviour.
|
||||||
|
|
||||||
|
validation: field.validation,
|
||||||
|
field: field.name,
|
||||||
|
label: field.displayName,
|
||||||
|
placeholder: field.displayName,
|
||||||
|
|
||||||
|
Block differences
|
||||||
|
_instanceName:
|
||||||
|
Filtered as it has been moved to own area.
|
||||||
|
field:
|
||||||
|
Fixed - not visible.
|
||||||
|
|
||||||
|
componentBindings
|
||||||
|
These appear to be removed/invalid
|
||||||
|
|
||||||
|
Bindings
|
||||||
|
{bindings} - working
|
||||||
|
{componentBindings}
|
||||||
|
componentdefinition.settings[x].nested needs to be true
|
||||||
|
Are these appropriate for the form block
|
||||||
|
|
||||||
|
FormBlock will have to pull the settings from fields:[]
|
||||||
|
|
||||||
|
Frontend Store > updateSetting: async (name, value)
|
||||||
|
Performs a patch for the component settings change
|
||||||
|
|
||||||
|
PropertyControl
|
||||||
|
Would this behaviour require a flag?
|
||||||
|
highlighted={$store.highlightedSettingKey === setting.key}
|
||||||
|
propertyFocus={$store.propertyFocus === setting.key}
|
||||||
|
|
||||||
|
Mode filtering of fields
|
||||||
|
Create
|
||||||
|
Update
|
||||||
|
View > do we filter fields here or disable them?
|
||||||
|
Default value?? Makes no sense
|
||||||
|
|
||||||
|
Drawer actions
|
||||||
|
CRUD - how to persist to the correct location?
|
||||||
|
Its just not a thing now
|
||||||
|
- Validation
|
||||||
|
- Bindings
|
||||||
|
- Custom options.
|
||||||
|
|
||||||
|
** Options source - should this be shaped too?
|
||||||
|
Schema,
|
||||||
|
Datasource
|
||||||
|
Custom
|
||||||
|
-->
|
||||||
|
|
||||||
|
<ComponentSettingsSection
|
||||||
|
componentInstance={sudoComponentInstance}
|
||||||
|
componentDefinition={parsedComponentDef}
|
||||||
|
isScreen={false}
|
||||||
|
{bindings}
|
||||||
|
{componentBindings}
|
||||||
|
onUpdateSetting={updateSetting}
|
||||||
|
/>
|
||||||
|
</Layout>
|
||||||
|
</Popover>
|
|
@ -1,45 +1,125 @@
|
||||||
<script>
|
<script>
|
||||||
import { Button, ActionButton, Drawer } from "@budibase/bbui"
|
|
||||||
import { createEventDispatcher } from "svelte"
|
|
||||||
import ColumnDrawer from "./ColumnDrawer.svelte"
|
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
|
import { generate } from "shortid"
|
||||||
import {
|
import {
|
||||||
getDatasourceForProvider,
|
getDatasourceForProvider,
|
||||||
getSchemaForDatasource,
|
getSchemaForDatasource,
|
||||||
} from "builderStore/dataBinding"
|
} from "builderStore/dataBinding"
|
||||||
import { currentAsset } from "builderStore"
|
import { currentAsset } from "builderStore"
|
||||||
|
import SettingsList from "../SettingsList.svelte"
|
||||||
|
import { createEventDispatcher } from "svelte"
|
||||||
|
import { store, selectedScreen } from "builderStore"
|
||||||
|
import {
|
||||||
|
getBindableProperties,
|
||||||
|
getComponentBindableProperties,
|
||||||
|
} from "builderStore/dataBinding"
|
||||||
|
|
||||||
|
import EditFieldPopover from "./EditFieldPopover.svelte"
|
||||||
|
|
||||||
export let componentInstance
|
export let componentInstance
|
||||||
export let value = []
|
export let value
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
let drawer
|
// Dean - From the inner form block - make a util
|
||||||
let boundValue
|
const FieldTypeToComponentMap = {
|
||||||
|
string: "stringfield",
|
||||||
|
number: "numberfield",
|
||||||
|
bigint: "bigintfield",
|
||||||
|
options: "optionsfield",
|
||||||
|
array: "multifieldselect",
|
||||||
|
boolean: "booleanfield",
|
||||||
|
longform: "longformfield",
|
||||||
|
datetime: "datetimefield",
|
||||||
|
attachment: "attachmentfield",
|
||||||
|
link: "relationshipfield",
|
||||||
|
json: "jsonfield",
|
||||||
|
barcodeqr: "codescanner",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dean - From the inner form block - make a util
|
||||||
|
const getComponentForField = field => {
|
||||||
|
if (!field || !schema?.[field]) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const type = schema[field].type
|
||||||
|
return FieldTypeToComponentMap[type]
|
||||||
|
}
|
||||||
|
|
||||||
|
let fieldConfigList
|
||||||
|
|
||||||
$: text = getText(value)
|
|
||||||
$: convertOldColumnFormat(value)
|
|
||||||
$: datasource = getDatasourceForProvider($currentAsset, componentInstance)
|
$: datasource = getDatasourceForProvider($currentAsset, componentInstance)
|
||||||
$: schema = getSchema($currentAsset, datasource)
|
$: schema = getSchema($currentAsset, datasource)
|
||||||
$: options = Object.keys(schema || {})
|
$: options = Object.keys(schema || {})
|
||||||
$: sanitisedValue = getValidColumns(value, options)
|
$: sanitisedValue = getValidColumns(convertOldFieldFormat(value), options)
|
||||||
$: updateBoundValue(sanitisedValue)
|
$: updateBoundValue(sanitisedValue)
|
||||||
|
|
||||||
const getText = value => {
|
$: fieldConfigList.forEach(column => {
|
||||||
if (!value?.length) {
|
if (!column.id) {
|
||||||
return "All fields"
|
column.id = generate()
|
||||||
}
|
}
|
||||||
let text = `${value.length} field`
|
})
|
||||||
if (value.length !== 1) {
|
|
||||||
text += "s"
|
$: bindings = getBindableProperties(
|
||||||
}
|
$selectedScreen,
|
||||||
return text
|
$store.selectedComponentId
|
||||||
|
)
|
||||||
|
$: console.log("bindings ", bindings)
|
||||||
|
|
||||||
|
$: componentBindings = getComponentBindableProperties(
|
||||||
|
$selectedScreen,
|
||||||
|
$store.selectedComponentId
|
||||||
|
)
|
||||||
|
$: console.log("componentBindings ", componentBindings)
|
||||||
|
|
||||||
|
// Builds unused ones only
|
||||||
|
const buildListOptions = (schema, selected) => {
|
||||||
|
let schemaClone = cloneDeep(schema)
|
||||||
|
selected.forEach(val => {
|
||||||
|
delete schemaClone[val.name]
|
||||||
|
})
|
||||||
|
|
||||||
|
return Object.keys(schemaClone)
|
||||||
|
.filter(key => !schemaClone[key].autocolumn)
|
||||||
|
.map(key => {
|
||||||
|
const col = schemaClone[key]
|
||||||
|
return {
|
||||||
|
name: key,
|
||||||
|
displayName: key,
|
||||||
|
id: generate(),
|
||||||
|
active: typeof col.active != "boolean" ? !value : col.active,
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const convertOldColumnFormat = oldColumns => {
|
/*
|
||||||
if (typeof oldColumns?.[0] === "string") {
|
SUPPORT
|
||||||
value = oldColumns.map(field => ({ name: field, displayName: field }))
|
- ["FIELD1", "FIELD2"...]
|
||||||
|
"fields": [ "First Name", "Last Name" ]
|
||||||
|
|
||||||
|
- [{name: "FIELD1", displayName: "FIELD1"}, ... only the currentlyadded fields]
|
||||||
|
* [{name: "FIELD1", displayName: "FIELD1", active: true|false}, all currently available fields]
|
||||||
|
*/
|
||||||
|
|
||||||
|
$: unconfigured = buildListOptions(schema, fieldConfigList)
|
||||||
|
|
||||||
|
const convertOldFieldFormat = fields => {
|
||||||
|
let formFields
|
||||||
|
if (typeof fields?.[0] === "string") {
|
||||||
|
formFields = fields.map(field => ({
|
||||||
|
name: field,
|
||||||
|
displayName: field,
|
||||||
|
active: true,
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
formFields = fields
|
||||||
}
|
}
|
||||||
|
return (formFields || []).map(field => {
|
||||||
|
return {
|
||||||
|
...field,
|
||||||
|
active: typeof field?.active != "boolean" ? true : field?.active,
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const getSchema = (asset, datasource) => {
|
const getSchema = (asset, datasource) => {
|
||||||
|
@ -55,7 +135,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateBoundValue = value => {
|
const updateBoundValue = value => {
|
||||||
boundValue = cloneDeep(value)
|
fieldConfigList = cloneDeep(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
const getValidColumns = (columns, options) => {
|
const getValidColumns = (columns, options) => {
|
||||||
|
@ -75,29 +155,32 @@
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const open = () => {
|
let listOptions
|
||||||
updateBoundValue(sanitisedValue)
|
$: if (fieldConfigList) {
|
||||||
drawer.show()
|
listOptions = [...fieldConfigList, ...unconfigured].map(column => {
|
||||||
|
const type = getComponentForField(column.name)
|
||||||
|
const _component = `@budibase/standard-components/${type}`
|
||||||
|
|
||||||
|
return { ...column, _component } //only necessary if it doesnt exist
|
||||||
|
})
|
||||||
|
console.log(listOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
const save = () => {
|
const listUpdated = e => {
|
||||||
dispatch("change", getValidColumns(boundValue, options))
|
const parsedColumns = getValidColumns(e.detail, options)
|
||||||
drawer.hide()
|
dispatch("change", parsedColumns)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="field-configuration">
|
<div class="field-configuration">
|
||||||
<ActionButton on:click={open}>{text}</ActionButton>
|
<SettingsList
|
||||||
|
value={listOptions}
|
||||||
|
on:change={listUpdated}
|
||||||
|
rightButton={EditFieldPopover}
|
||||||
|
rightProps={{ componentBindings, bindings, parent: componentInstance }}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Drawer bind:this={drawer} title="Form Fields">
|
|
||||||
<svelte:fragment slot="description">
|
|
||||||
Configure the fields in your form.
|
|
||||||
</svelte:fragment>
|
|
||||||
<Button cta slot="buttons" on:click={save}>Save</Button>
|
|
||||||
<ColumnDrawer slot="body" bind:columns={boundValue} {options} {schema} />
|
|
||||||
</Drawer>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.field-configuration :global(.spectrum-ActionButton) {
|
.field-configuration :global(.spectrum-ActionButton) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -0,0 +1,133 @@
|
||||||
|
<script>
|
||||||
|
import { Toggle, Icon } from "@budibase/bbui"
|
||||||
|
import { dndzone } from "svelte-dnd-action"
|
||||||
|
import { flip } from "svelte/animate"
|
||||||
|
import { createEventDispatcher } from "svelte"
|
||||||
|
|
||||||
|
export let value = []
|
||||||
|
export let showHandle = true
|
||||||
|
export let rightButton
|
||||||
|
export let rightProps = {}
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
const flipDurationMs = 150
|
||||||
|
let dragDisabled = false
|
||||||
|
let listOptions = [...value]
|
||||||
|
let anchors = {}
|
||||||
|
|
||||||
|
const updateColumnOrder = e => {
|
||||||
|
listOptions = e.detail.items
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleFinalize = e => {
|
||||||
|
updateColumnOrder(e)
|
||||||
|
dispatch("change", listOptions)
|
||||||
|
dragDisabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is optional and should be moved.
|
||||||
|
const onToggle = item => {
|
||||||
|
return e => {
|
||||||
|
console.log(`${item.name} toggled: ${e.detail}`)
|
||||||
|
item.active = e.detail
|
||||||
|
dispatch("change", listOptions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ul
|
||||||
|
class="list-wrap"
|
||||||
|
use:dndzone={{
|
||||||
|
items: listOptions,
|
||||||
|
flipDurationMs,
|
||||||
|
dropTargetStyle: { outline: "none" },
|
||||||
|
dragDisabled,
|
||||||
|
}}
|
||||||
|
on:finalize={handleFinalize}
|
||||||
|
on:consider={updateColumnOrder}
|
||||||
|
>
|
||||||
|
{#each listOptions as item (item.id)}
|
||||||
|
<li
|
||||||
|
animate:flip={{ duration: flipDurationMs }}
|
||||||
|
bind:this={anchors[item.id]}
|
||||||
|
>
|
||||||
|
<div class="left-content">
|
||||||
|
{#if showHandle}
|
||||||
|
<div
|
||||||
|
class="handle"
|
||||||
|
aria-label="drag-handle"
|
||||||
|
style={dragDisabled ? "cursor: grab" : "cursor: grabbing"}
|
||||||
|
>
|
||||||
|
<Icon name="DragHandle" size="XL" />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<!-- slot - left action -->
|
||||||
|
<Toggle on:change={onToggle(item)} text="" value={item.active} thin />
|
||||||
|
{item.name}
|
||||||
|
</div>
|
||||||
|
<!-- slot - right action -->
|
||||||
|
<div class="right-content">
|
||||||
|
{#if rightButton}
|
||||||
|
<svelte:component
|
||||||
|
this={rightButton}
|
||||||
|
anchor={anchors[item.id]}
|
||||||
|
field={item}
|
||||||
|
componentBindings={rightProps.componentBindings}
|
||||||
|
bindings={rightProps.bindings}
|
||||||
|
parent={rightProps.parent}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.list-wrap {
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: var(
|
||||||
|
--spectrum-table-background-color,
|
||||||
|
var(--spectrum-global-color-gray-50)
|
||||||
|
);
|
||||||
|
border: 1px solid
|
||||||
|
var(--spectrum-table-border-color, var(--spectrum-alias-border-color-mid));
|
||||||
|
}
|
||||||
|
.list-wrap > li {
|
||||||
|
background-color: var(
|
||||||
|
--spectrum-table-background-color,
|
||||||
|
var(--spectrum-global-color-gray-50)
|
||||||
|
);
|
||||||
|
transition: background-color ease-in-out 130ms;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
border-bottom: 1px solid
|
||||||
|
var(--spectrum-table-border-color, var(--spectrum-alias-border-color-mid));
|
||||||
|
}
|
||||||
|
.list-wrap > li:hover {
|
||||||
|
background-color: var(
|
||||||
|
--spectrum-table-row-background-color-hover,
|
||||||
|
var(--spectrum-alias-highlight-hover)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
.list-wrap > li:first-child {
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
border-top-right-radius: 4px;
|
||||||
|
}
|
||||||
|
.list-wrap > li:last-child {
|
||||||
|
border-top-left-radius: var(--spectrum-table-regular-border-radius);
|
||||||
|
border-top-right-radius: var(--spectrum-table-regular-border-radius);
|
||||||
|
}
|
||||||
|
.left-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.list-wrap li {
|
||||||
|
padding-left: var(--spacing-s);
|
||||||
|
padding-right: var(--spacing-s);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -13,11 +13,16 @@
|
||||||
export let bindings
|
export let bindings
|
||||||
export let componentBindings
|
export let componentBindings
|
||||||
export let isScreen = false
|
export let isScreen = false
|
||||||
|
export let onUpdateSetting
|
||||||
|
|
||||||
$: sections = getSections(componentInstance, componentDefinition, isScreen)
|
$: sections = getSections(componentInstance, componentDefinition, isScreen)
|
||||||
|
|
||||||
const getSections = (instance, definition, isScreen) => {
|
const getSections = (instance, definition, isScreen) => {
|
||||||
const settings = definition?.settings ?? []
|
const settings = definition?.settings ?? []
|
||||||
|
console.log(
|
||||||
|
"ComponentSettingsSection::definition?.settings",
|
||||||
|
definition?.settings
|
||||||
|
)
|
||||||
const generalSettings = settings.filter(setting => !setting.section)
|
const generalSettings = settings.filter(setting => !setting.section)
|
||||||
const customSections = settings.filter(setting => setting.section)
|
const customSections = settings.filter(setting => setting.section)
|
||||||
let sections = [
|
let sections = [
|
||||||
|
@ -46,19 +51,23 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateSetting = async (setting, value) => {
|
const updateSetting = async (setting, value) => {
|
||||||
try {
|
if (typeof onUpdateSetting === "function") {
|
||||||
await store.actions.components.updateSetting(setting.key, value)
|
onUpdateSetting(setting, value)
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
await store.actions.components.updateSetting(setting.key, value)
|
||||||
|
|
||||||
// Send event if required
|
// Send event if required
|
||||||
if (setting.sendEvents) {
|
if (setting.sendEvents) {
|
||||||
analytics.captureEvent(Events.COMPONENT_UPDATED, {
|
analytics.captureEvent(Events.COMPONENT_UPDATED, {
|
||||||
name: componentInstance._component,
|
name: componentInstance._component,
|
||||||
setting: setting.key,
|
setting: setting.key,
|
||||||
value,
|
value,
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
notifications.error("Error updating component prop")
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
notifications.error("Error updating component prop")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,10 +138,13 @@
|
||||||
{/if}
|
{/if}
|
||||||
{#each section.settings as setting (setting.key)}
|
{#each section.settings as setting (setting.key)}
|
||||||
{#if setting.visible}
|
{#if setting.visible}
|
||||||
|
<!-- DEAN - Remove fieldConfiguration label config -->
|
||||||
<PropertyControl
|
<PropertyControl
|
||||||
type={setting.type}
|
type={setting.type}
|
||||||
control={getComponentForSetting(setting)}
|
control={getComponentForSetting(setting)}
|
||||||
label={setting.label}
|
label={setting.type != "fieldConfiguration"
|
||||||
|
? setting.label
|
||||||
|
: undefined}
|
||||||
labelHidden={setting.labelHidden}
|
labelHidden={setting.labelHidden}
|
||||||
key={setting.key}
|
key={setting.key}
|
||||||
value={componentInstance[setting.key]}
|
value={componentInstance[setting.key]}
|
||||||
|
|
|
@ -5106,22 +5106,6 @@
|
||||||
"key": "title",
|
"key": "title",
|
||||||
"nested": true
|
"nested": true
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"type": "select",
|
|
||||||
"label": "Size",
|
|
||||||
"key": "size",
|
|
||||||
"options": [
|
|
||||||
{
|
|
||||||
"label": "Medium",
|
|
||||||
"value": "spectrum--medium"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Large",
|
|
||||||
"value": "spectrum--large"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"defaultValue": "spectrum--medium"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"label": "Empty text",
|
"label": "Empty text",
|
||||||
|
@ -5133,45 +5117,6 @@
|
||||||
"invert": true
|
"invert": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"section": true,
|
|
||||||
"name": "Fields",
|
|
||||||
"settings": [
|
|
||||||
{
|
|
||||||
"type": "fieldConfiguration",
|
|
||||||
"label": "Fields",
|
|
||||||
"key": "fields",
|
|
||||||
"selectAllFields": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "select",
|
|
||||||
"label": "Field labels",
|
|
||||||
"key": "labelPosition",
|
|
||||||
"defaultValue": "left",
|
|
||||||
"options": [
|
|
||||||
{
|
|
||||||
"label": "Left",
|
|
||||||
"value": "left"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Above",
|
|
||||||
"value": "above"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "boolean",
|
|
||||||
"label": "Disabled",
|
|
||||||
"key": "disabled",
|
|
||||||
"defaultValue": false,
|
|
||||||
"dependsOn": {
|
|
||||||
"setting": "actionType",
|
|
||||||
"value": "View",
|
|
||||||
"invert": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"section": true,
|
"section": true,
|
||||||
"name": "Buttons",
|
"name": "Buttons",
|
||||||
|
@ -5241,6 +5186,61 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"section": true,
|
||||||
|
"name": "Fields",
|
||||||
|
"settings": [
|
||||||
|
{
|
||||||
|
"type": "select",
|
||||||
|
"label": "Align labels",
|
||||||
|
"key": "labelPosition",
|
||||||
|
"defaultValue": "left",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"label": "Left",
|
||||||
|
"value": "left"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Above",
|
||||||
|
"value": "above"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "select",
|
||||||
|
"label": "Size",
|
||||||
|
"key": "size",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"label": "Medium",
|
||||||
|
"value": "spectrum--medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Large",
|
||||||
|
"value": "spectrum--large"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"defaultValue": "spectrum--medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "fieldConfiguration",
|
||||||
|
"label": "Fields",
|
||||||
|
"key": "fields",
|
||||||
|
"selectAllFields": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"label": "Disabled",
|
||||||
|
"key": "disabled",
|
||||||
|
"defaultValue": false,
|
||||||
|
"dependsOn": {
|
||||||
|
"setting": "actionType",
|
||||||
|
"value": "View",
|
||||||
|
"invert": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"context": [
|
"context": [
|
||||||
|
|
|
@ -24,14 +24,20 @@
|
||||||
const { fetchDatasourceSchema } = getContext("sdk")
|
const { fetchDatasourceSchema } = getContext("sdk")
|
||||||
|
|
||||||
const convertOldFieldFormat = fields => {
|
const convertOldFieldFormat = fields => {
|
||||||
if (typeof fields?.[0] === "string") {
|
return typeof fields?.[0] === "string"
|
||||||
return fields.map(field => ({ name: field, displayName: field }))
|
? fields.map(field => ({
|
||||||
}
|
name: field,
|
||||||
|
displayName: field,
|
||||||
return fields
|
active: true,
|
||||||
|
}))
|
||||||
|
: fields
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//All settings need to derive from the block config now
|
||||||
|
|
||||||
|
// Parse the fields here too. Not present means false.
|
||||||
const getDefaultFields = (fields, schema) => {
|
const getDefaultFields = (fields, schema) => {
|
||||||
|
let formFields
|
||||||
if (schema && (!fields || fields.length === 0)) {
|
if (schema && (!fields || fields.length === 0)) {
|
||||||
const defaultFields = []
|
const defaultFields = []
|
||||||
|
|
||||||
|
@ -41,13 +47,20 @@
|
||||||
defaultFields.push({
|
defaultFields.push({
|
||||||
name: field.name,
|
name: field.name,
|
||||||
displayName: field.name,
|
displayName: field.name,
|
||||||
|
active: true,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
formFields = [...defaultFields]
|
||||||
return defaultFields
|
} else {
|
||||||
|
formFields = (fields || []).map(field => {
|
||||||
|
return {
|
||||||
|
...field,
|
||||||
|
active: typeof field?.active != "boolean" ? true : field?.active,
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return fields
|
return formFields.filter(field => field.active)
|
||||||
}
|
}
|
||||||
|
|
||||||
let schema
|
let schema
|
||||||
|
@ -56,7 +69,6 @@
|
||||||
|
|
||||||
$: formattedFields = convertOldFieldFormat(fields)
|
$: formattedFields = convertOldFieldFormat(fields)
|
||||||
$: fieldsOrDefault = getDefaultFields(formattedFields, schema)
|
$: fieldsOrDefault = getDefaultFields(formattedFields, schema)
|
||||||
|
|
||||||
$: fetchSchema(dataSource)
|
$: fetchSchema(dataSource)
|
||||||
$: dataProvider = `{{ literal ${safe(providerId)} }}`
|
$: dataProvider = `{{ literal ${safe(providerId)} }}`
|
||||||
$: filter = [
|
$: filter = [
|
||||||
|
|
|
@ -197,22 +197,24 @@
|
||||||
{/if}
|
{/if}
|
||||||
</BlockComponent>
|
</BlockComponent>
|
||||||
{/if}
|
{/if}
|
||||||
<BlockComponent type="fieldgroup" props={{ labelPosition }} order={1}>
|
{#key fields}
|
||||||
{#each fields as field, idx}
|
<BlockComponent type="fieldgroup" props={{ labelPosition }} order={1}>
|
||||||
{#if getComponentForField(field.name)}
|
{#each fields as field, idx}
|
||||||
<BlockComponent
|
{#if getComponentForField(field.name)}
|
||||||
type={getComponentForField(field.name)}
|
<BlockComponent
|
||||||
props={{
|
type={getComponentForField(field.name)}
|
||||||
validation: field.validation,
|
props={{
|
||||||
field: field.name,
|
validation: field.validation,
|
||||||
label: field.displayName,
|
field: field.name,
|
||||||
placeholder: field.displayName,
|
label: field.displayName,
|
||||||
}}
|
placeholder: field.displayName,
|
||||||
order={idx}
|
}}
|
||||||
/>
|
order={idx}
|
||||||
{/if}
|
/>
|
||||||
{/each}
|
{/if}
|
||||||
</BlockComponent>
|
{/each}
|
||||||
|
</BlockComponent>
|
||||||
|
{/key}
|
||||||
</BlockComponent>
|
</BlockComponent>
|
||||||
</BlockComponent>
|
</BlockComponent>
|
||||||
{:else}
|
{:else}
|
||||||
|
|
Loading…
Reference in New Issue