Merge pull request #6309 from Budibase/builder-focus-updates
Builder focus updates
This commit is contained in:
commit
eb3a7b44b6
|
@ -1,8 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import "@spectrum-css/actionbutton/dist/index-vars.css"
|
import "@spectrum-css/actionbutton/dist/index-vars.css"
|
||||||
import { createEventDispatcher, getContext } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const context = getContext("builderFocus")
|
|
||||||
|
|
||||||
export let quiet = false
|
export let quiet = false
|
||||||
export let emphasized = false
|
export let emphasized = false
|
||||||
|
@ -14,14 +13,6 @@
|
||||||
export let size = "M"
|
export let size = "M"
|
||||||
export let active = false
|
export let active = false
|
||||||
export let fullWidth = false
|
export let fullWidth = false
|
||||||
export let autofocus = false
|
|
||||||
|
|
||||||
let focus = false
|
|
||||||
let actionButton
|
|
||||||
$: focus = autofocus && actionButton !== undefined
|
|
||||||
$: if (focus) {
|
|
||||||
actionButton.focus()
|
|
||||||
}
|
|
||||||
|
|
||||||
function longPress(element) {
|
function longPress(element) {
|
||||||
if (!longPressable) return
|
if (!longPressable) return
|
||||||
|
@ -46,7 +37,6 @@
|
||||||
|
|
||||||
<button
|
<button
|
||||||
data-cy={dataCy}
|
data-cy={dataCy}
|
||||||
bind:this={actionButton}
|
|
||||||
use:longPress
|
use:longPress
|
||||||
class:spectrum-ActionButton--quiet={quiet}
|
class:spectrum-ActionButton--quiet={quiet}
|
||||||
class:spectrum-ActionButton--emphasized={emphasized}
|
class:spectrum-ActionButton--emphasized={emphasized}
|
||||||
|
@ -54,17 +44,9 @@
|
||||||
class:fullWidth
|
class:fullWidth
|
||||||
class="spectrum-ActionButton spectrum-ActionButton--size{size}"
|
class="spectrum-ActionButton spectrum-ActionButton--size{size}"
|
||||||
class:active
|
class:active
|
||||||
class:is-focused={focus}
|
|
||||||
{disabled}
|
{disabled}
|
||||||
on:longPress
|
on:longPress
|
||||||
on:click|preventDefault
|
on:click|preventDefault
|
||||||
on:focus={() => {
|
|
||||||
focus = true
|
|
||||||
}}
|
|
||||||
on:blur={() => {
|
|
||||||
focus = false
|
|
||||||
if (context) context.clear()
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{#if longPressable}
|
{#if longPressable}
|
||||||
<svg
|
<svg
|
||||||
|
@ -98,10 +80,4 @@
|
||||||
.active svg {
|
.active svg {
|
||||||
color: var(--spectrum-global-color-blue-600);
|
color: var(--spectrum-global-color-blue-600);
|
||||||
}
|
}
|
||||||
button.is-focused {
|
|
||||||
border-color: var(
|
|
||||||
--spectrum-textfield-m-border-color-down,
|
|
||||||
var(--spectrum-alias-border-color-mouse-focus)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
export let options = []
|
export let options = []
|
||||||
export let getOptionLabel = option => extractProperty(option, "label")
|
export let getOptionLabel = option => extractProperty(option, "label")
|
||||||
export let getOptionValue = option => extractProperty(option, "value")
|
export let getOptionValue = option => extractProperty(option, "value")
|
||||||
export let autofocus = false
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const onChange = e => {
|
const onChange = e => {
|
||||||
|
@ -36,7 +35,6 @@
|
||||||
{options}
|
{options}
|
||||||
{placeholder}
|
{placeholder}
|
||||||
{readonly}
|
{readonly}
|
||||||
{autofocus}
|
|
||||||
{getOptionLabel}
|
{getOptionLabel}
|
||||||
{getOptionValue}
|
{getOptionValue}
|
||||||
on:change={onChange}
|
on:change={onChange}
|
||||||
|
|
|
@ -3,29 +3,21 @@
|
||||||
import "@spectrum-css/popover/dist/index-vars.css"
|
import "@spectrum-css/popover/dist/index-vars.css"
|
||||||
import "@spectrum-css/menu/dist/index-vars.css"
|
import "@spectrum-css/menu/dist/index-vars.css"
|
||||||
import { fly } from "svelte/transition"
|
import { fly } from "svelte/transition"
|
||||||
import { createEventDispatcher, getContext } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
|
|
||||||
export let value = null
|
export let value = null
|
||||||
export let id = null
|
export let id = null
|
||||||
export let placeholder = "Choose an option or type"
|
export let placeholder = "Choose an option or type"
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
export let readonly = false
|
export let readonly = false
|
||||||
export let autofocus = false
|
|
||||||
export let error = null
|
export let error = null
|
||||||
export let options = []
|
export let options = []
|
||||||
export let getOptionLabel = option => option
|
export let getOptionLabel = option => option
|
||||||
export let getOptionValue = option => option
|
export let getOptionValue = option => option
|
||||||
|
|
||||||
const context = getContext("builderFocus")
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
let open = false
|
let open = false
|
||||||
let focus = false
|
let focus = false
|
||||||
let comboInput
|
|
||||||
|
|
||||||
$: focus = autofocus && comboInput !== undefined
|
|
||||||
$: if (focus) {
|
|
||||||
comboInput.focus()
|
|
||||||
}
|
|
||||||
|
|
||||||
const selectOption = value => {
|
const selectOption = value => {
|
||||||
dispatch("change", value)
|
dispatch("change", value)
|
||||||
|
@ -57,16 +49,10 @@
|
||||||
class:is-focused={open || focus}
|
class:is-focused={open || focus}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
bind:this={comboInput}
|
|
||||||
{id}
|
{id}
|
||||||
type="text"
|
type="text"
|
||||||
on:focus={() => {
|
on:focus={() => (focus = true)}
|
||||||
focus = true
|
on:blur={() => (focus = false)}
|
||||||
}}
|
|
||||||
on:blur={() => {
|
|
||||||
focus = false
|
|
||||||
context.clear()
|
|
||||||
}}
|
|
||||||
on:change={onType}
|
on:change={onType}
|
||||||
value={value || ""}
|
value={value || ""}
|
||||||
placeholder={placeholder || ""}
|
placeholder={placeholder || ""}
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
export let readonly = false
|
export let readonly = false
|
||||||
export let autocomplete = false
|
export let autocomplete = false
|
||||||
export let sort = false
|
export let sort = false
|
||||||
export let autofocus = false
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
$: selectedLookupMap = getSelectedLookupMap(value)
|
$: selectedLookupMap = getSelectedLookupMap(value)
|
||||||
|
@ -86,5 +85,4 @@
|
||||||
{getOptionValue}
|
{getOptionValue}
|
||||||
onSelectOption={toggleOption}
|
onSelectOption={toggleOption}
|
||||||
{sort}
|
{sort}
|
||||||
{autofocus}
|
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import "@spectrum-css/popover/dist/index-vars.css"
|
import "@spectrum-css/popover/dist/index-vars.css"
|
||||||
import "@spectrum-css/menu/dist/index-vars.css"
|
import "@spectrum-css/menu/dist/index-vars.css"
|
||||||
import { fly } from "svelte/transition"
|
import { fly } from "svelte/transition"
|
||||||
import { createEventDispatcher, getContext } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
import clickOutside from "../../Actions/click_outside"
|
import clickOutside from "../../Actions/click_outside"
|
||||||
import Search from "./Search.svelte"
|
import Search from "./Search.svelte"
|
||||||
|
|
||||||
|
@ -26,18 +26,9 @@
|
||||||
export let autoWidth = false
|
export let autoWidth = false
|
||||||
export let autocomplete = false
|
export let autocomplete = false
|
||||||
export let sort = false
|
export let sort = false
|
||||||
export let autofocus = false
|
|
||||||
|
|
||||||
const context = getContext("builderFocus")
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
let searchTerm = null
|
let searchTerm = null
|
||||||
let focus = false
|
|
||||||
let pickerButton
|
|
||||||
|
|
||||||
$: focus = autofocus && pickerButton !== undefined
|
|
||||||
$: if (focus) {
|
|
||||||
pickerButton.focus()
|
|
||||||
}
|
|
||||||
|
|
||||||
$: sortedOptions = getSortedOptions(options, getOptionLabel, sort)
|
$: sortedOptions = getSortedOptions(options, getOptionLabel, sort)
|
||||||
$: filteredOptions = getFilteredOptions(
|
$: filteredOptions = getFilteredOptions(
|
||||||
|
@ -89,24 +80,7 @@
|
||||||
class:is-invalid={!!error}
|
class:is-invalid={!!error}
|
||||||
class:is-open={open}
|
class:is-open={open}
|
||||||
aria-haspopup="listbox"
|
aria-haspopup="listbox"
|
||||||
class:is-focused={focus}
|
|
||||||
bind:this={pickerButton}
|
|
||||||
on:mousedown={onClick}
|
on:mousedown={onClick}
|
||||||
on:keydown={e => {
|
|
||||||
var keycode = e.key
|
|
||||||
if (focus) {
|
|
||||||
if (keycode === "Enter") {
|
|
||||||
onClick(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
on:focus={() => {
|
|
||||||
focus = true
|
|
||||||
}}
|
|
||||||
on:blur={() => {
|
|
||||||
focus = false
|
|
||||||
if (context) context.clear()
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{#if fieldIcon}
|
{#if fieldIcon}
|
||||||
<span class="icon-Placeholder-Padding">
|
<span class="icon-Placeholder-Padding">
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
export let autoWidth = false
|
export let autoWidth = false
|
||||||
export let autocomplete = false
|
export let autocomplete = false
|
||||||
export let sort = false
|
export let sort = false
|
||||||
export let autofocus = false
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
let open = false
|
let open = false
|
||||||
|
@ -75,7 +74,6 @@
|
||||||
{fieldIcon}
|
{fieldIcon}
|
||||||
{autocomplete}
|
{autocomplete}
|
||||||
{sort}
|
{sort}
|
||||||
{autofocus}
|
|
||||||
isPlaceholder={value == null || value === ""}
|
isPlaceholder={value == null || value === ""}
|
||||||
placeholderOption={placeholder}
|
placeholderOption={placeholder}
|
||||||
isOptionSelected={option => option === value}
|
isOptionSelected={option => option === value}
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
export let getOptionLabel = option => option
|
export let getOptionLabel = option => option
|
||||||
export let getOptionValue = option => option
|
export let getOptionValue = option => option
|
||||||
export let sort = false
|
export let sort = false
|
||||||
export let autofocus = false
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const onChange = e => {
|
const onChange = e => {
|
||||||
|
@ -36,6 +35,5 @@
|
||||||
{getOptionValue}
|
{getOptionValue}
|
||||||
on:change={onChange}
|
on:change={onChange}
|
||||||
on:click
|
on:click
|
||||||
{autofocus}
|
|
||||||
/>
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
export let autoWidth = false
|
export let autoWidth = false
|
||||||
export let sort = false
|
export let sort = false
|
||||||
export let tooltip = ""
|
export let tooltip = ""
|
||||||
export let autofocus = false
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const onChange = e => {
|
const onChange = e => {
|
||||||
|
@ -45,7 +44,6 @@
|
||||||
{placeholder}
|
{placeholder}
|
||||||
{autoWidth}
|
{autoWidth}
|
||||||
{sort}
|
{sort}
|
||||||
{autofocus}
|
|
||||||
{getOptionLabel}
|
{getOptionLabel}
|
||||||
{getOptionValue}
|
{getOptionValue}
|
||||||
{getOptionIcon}
|
{getOptionIcon}
|
||||||
|
|
|
@ -20,7 +20,6 @@ import analytics, { Events } from "analytics"
|
||||||
import {
|
import {
|
||||||
findComponentType,
|
findComponentType,
|
||||||
findComponentParent,
|
findComponentParent,
|
||||||
findComponentPath,
|
|
||||||
findClosestMatchingComponent,
|
findClosestMatchingComponent,
|
||||||
findAllMatchingComponents,
|
findAllMatchingComponents,
|
||||||
findComponent,
|
findComponent,
|
||||||
|
@ -61,6 +60,7 @@ const INITIAL_FRONTEND_STATE = {
|
||||||
theme: "",
|
theme: "",
|
||||||
customTheme: {},
|
customTheme: {},
|
||||||
previewDevice: "desktop",
|
previewDevice: "desktop",
|
||||||
|
highlightedSettingKey: null,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getFrontendStore = () => {
|
export const getFrontendStore = () => {
|
||||||
|
@ -423,21 +423,6 @@ export const getFrontendStore = () => {
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
state.currentView = "component"
|
state.currentView = "component"
|
||||||
state.selectedComponentId = componentInstance._id
|
state.selectedComponentId = componentInstance._id
|
||||||
|
|
||||||
const focusSetting = resolveComponentFocus(
|
|
||||||
asset?.props,
|
|
||||||
componentInstance
|
|
||||||
)
|
|
||||||
if (focusSetting) {
|
|
||||||
state.builderFocus = [
|
|
||||||
{
|
|
||||||
key: focusSetting.key,
|
|
||||||
target: state.selectedComponentId,
|
|
||||||
location: "component_settings",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -678,33 +663,14 @@ export const getFrontendStore = () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
settings: {
|
||||||
|
highlight: key => {
|
||||||
// Determine the initial focus for newly created components
|
store.update(state => ({
|
||||||
// Take into account the fact that data providers should be
|
...state,
|
||||||
// skipped if they will be inherited from the path
|
highlightedSettingKey: key,
|
||||||
const resolveComponentFocus = (asset_props, componentInstance) => {
|
}))
|
||||||
const definition = store.actions.components.getDefinition(
|
},
|
||||||
componentInstance._component
|
},
|
||||||
)
|
|
||||||
let providerIdx = -1
|
|
||||||
let required = definition.settings.filter((s, idx) => {
|
|
||||||
if (s.type === "dataProvider") {
|
|
||||||
providerIdx = idx
|
|
||||||
}
|
|
||||||
return s.required
|
|
||||||
})
|
|
||||||
|
|
||||||
if (providerIdx > -1) {
|
|
||||||
const path = findComponentPath(asset_props, componentInstance._id)
|
|
||||||
const providers = path.filter(c =>
|
|
||||||
c._component?.endsWith("/dataprovider")
|
|
||||||
)
|
|
||||||
if (providers.length) {
|
|
||||||
required = required.splice(providerIdx, 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return required[0]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return store
|
return store
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
export let options
|
export let options
|
||||||
export let allowJS = true
|
export let allowJS = true
|
||||||
export let appendBindingsAsOptions = true
|
export let appendBindingsAsOptions = true
|
||||||
export let autofocus = false
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
let bindingDrawer
|
let bindingDrawer
|
||||||
|
@ -62,7 +61,6 @@
|
||||||
on:pick={e => onChange(e.detail, true)}
|
on:pick={e => onChange(e.detail, true)}
|
||||||
{placeholder}
|
{placeholder}
|
||||||
options={allOptions}
|
options={allOptions}
|
||||||
{autofocus}
|
|
||||||
/>
|
/>
|
||||||
{#if !disabled}
|
{#if !disabled}
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -68,7 +68,7 @@
|
||||||
customTheme: $store.customTheme,
|
customTheme: $store.customTheme,
|
||||||
previewDevice: $store.previewDevice,
|
previewDevice: $store.previewDevice,
|
||||||
messagePassing: $store.clientFeatures.messagePassing,
|
messagePassing: $store.clientFeatures.messagePassing,
|
||||||
isBudibaseEvent: true
|
isBudibaseEvent: true,
|
||||||
}
|
}
|
||||||
$: json = JSON.stringify(previewData)
|
$: json = JSON.stringify(previewData)
|
||||||
|
|
||||||
|
@ -148,21 +148,6 @@
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const resolveFocus = (data) => {
|
|
||||||
if($store.builderFocus){
|
|
||||||
const comp = $store.builderFocus.reduce((acc, item)=>{
|
|
||||||
acc = item.target
|
|
||||||
return acc
|
|
||||||
}, "")
|
|
||||||
if(data.id !== comp){
|
|
||||||
store.update(state => {
|
|
||||||
delete state.builderFocus
|
|
||||||
return state
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleBudibaseEvent = async event => {
|
const handleBudibaseEvent = async event => {
|
||||||
const { type, data } = event.data || event.detail
|
const { type, data } = event.data || event.detail
|
||||||
if (!type) {
|
if (!type) {
|
||||||
|
@ -172,7 +157,6 @@
|
||||||
try {
|
try {
|
||||||
if (type === "select-component" && data.id) {
|
if (type === "select-component" && data.id) {
|
||||||
store.actions.components.select({ _id: data.id })
|
store.actions.components.select({ _id: data.id })
|
||||||
resolveFocus(data)
|
|
||||||
} else if (type === "update-prop") {
|
} else if (type === "update-prop") {
|
||||||
await store.actions.components.updateProp(data.prop, data.value)
|
await store.actions.components.updateProp(data.prop, data.value)
|
||||||
} else if (type === "delete-component" && data.id) {
|
} else if (type === "delete-component" && data.id) {
|
||||||
|
@ -206,12 +190,18 @@
|
||||||
store.actions.components.copy(source, true)
|
store.actions.components.copy(source, true)
|
||||||
await store.actions.components.paste(destination, data.mode)
|
await store.actions.components.paste(destination, data.mode)
|
||||||
}
|
}
|
||||||
} else if(type == "builder-focus") {
|
} else if (type === "highlight-setting") {
|
||||||
store.update(state => ({
|
store.actions.settings.highlight(data.setting)
|
||||||
...state,
|
|
||||||
builderFocus :
|
// Also scroll setting into view
|
||||||
[...data]
|
const selector = `[data-cy="${data.setting}-prop-control"`
|
||||||
}))
|
const element = document.querySelector(selector)?.parentElement
|
||||||
|
if (element) {
|
||||||
|
element.scrollIntoView({
|
||||||
|
behavior: "smooth",
|
||||||
|
block: "center",
|
||||||
|
})
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
console.warn(`Client sent unknown event type: ${type}`)
|
console.warn(`Client sent unknown event type: ${type}`)
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,15 +73,6 @@
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
const isFocused = setting => {
|
|
||||||
if (!$store.builderFocus) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
setting.required === true && $store.builderFocus[0].key === setting.key
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#each sections as section, idx (section.name)}
|
{#each sections as section, idx (section.name)}
|
||||||
|
@ -102,10 +93,11 @@
|
||||||
control={getComponentForSettingType(setting.type)}
|
control={getComponentForSettingType(setting.type)}
|
||||||
label={setting.label}
|
label={setting.label}
|
||||||
key={setting.key}
|
key={setting.key}
|
||||||
value={componentInstance[setting.key] ??
|
value={componentInstance[setting.key]}
|
||||||
componentInstance[setting.key]?.defaultValue}
|
defaultValue={setting.defaultValue}
|
||||||
nested={setting.nested}
|
nested={setting.nested}
|
||||||
onChange={val => updateProp(setting.key, val)}
|
onChange={val => updateProp(setting.key, val)}
|
||||||
|
highlighted={$store.highlightedSettingKey === setting.key}
|
||||||
props={{
|
props={{
|
||||||
options: setting.options || [],
|
options: setting.options || [],
|
||||||
placeholder: setting.placeholder || null,
|
placeholder: setting.placeholder || null,
|
||||||
|
@ -116,7 +108,6 @@
|
||||||
{componentBindings}
|
{componentBindings}
|
||||||
{componentInstance}
|
{componentInstance}
|
||||||
{componentDefinition}
|
{componentDefinition}
|
||||||
autofocus={isFocused(setting)}
|
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
import { createEventDispatcher, onMount } from "svelte"
|
import { createEventDispatcher, onMount } from "svelte"
|
||||||
|
|
||||||
export let value
|
export let value
|
||||||
export let autofocus = false
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const getValue = component => `{{ literal ${makePropSafe(component._id)} }}`
|
const getValue = component => `{{ literal ${makePropSafe(component._id)} }}`
|
||||||
|
@ -25,7 +24,6 @@
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
{value}
|
{value}
|
||||||
{autofocus}
|
|
||||||
placeholder={null}
|
placeholder={null}
|
||||||
on:change
|
on:change
|
||||||
options={providers}
|
options={providers}
|
||||||
|
|
|
@ -25,7 +25,6 @@
|
||||||
export let otherSources
|
export let otherSources
|
||||||
export let showAllQueries
|
export let showAllQueries
|
||||||
export let bindings = []
|
export let bindings = []
|
||||||
export let autofocus = false
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const arrayTypes = ["attachment", "array"]
|
const arrayTypes = ["attachment", "array"]
|
||||||
|
@ -159,7 +158,6 @@
|
||||||
value={text}
|
value={text}
|
||||||
options={[text]}
|
options={[text]}
|
||||||
on:click={dropdownRight.show}
|
on:click={dropdownRight.show}
|
||||||
{autofocus}
|
|
||||||
/>
|
/>
|
||||||
{#if value?.type === "query"}
|
{#if value?.type === "query"}
|
||||||
<i class="ri-settings-5-line" on:click={openQueryParamsDrawer} />
|
<i class="ri-settings-5-line" on:click={openQueryParamsDrawer} />
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
export let componentInstance = {}
|
export let componentInstance = {}
|
||||||
export let value = ""
|
export let value = ""
|
||||||
export let placeholder
|
export let placeholder
|
||||||
export let autofocus = false
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
$: datasource = getDatasourceForProvider($currentAsset, componentInstance)
|
$: datasource = getDatasourceForProvider($currentAsset, componentInstance)
|
||||||
|
@ -38,10 +37,4 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Select
|
<Select {placeholder} value={boundValue} on:change={onChange} {options} />
|
||||||
{placeholder}
|
|
||||||
value={boundValue}
|
|
||||||
on:change={onChange}
|
|
||||||
{options}
|
|
||||||
{autofocus}
|
|
||||||
/>
|
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
export let componentInstance
|
export let componentInstance
|
||||||
export let value
|
export let value
|
||||||
export let type
|
export let type
|
||||||
export let autofocus = false
|
|
||||||
|
|
||||||
$: form = findClosestMatchingComponent(
|
$: form = findClosestMatchingComponent(
|
||||||
$currentAsset?.props,
|
$currentAsset?.props,
|
||||||
|
@ -41,4 +40,4 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Combobox on:change {value} {options} {autofocus} />
|
<Combobox on:change {value} {options} />
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
|
|
||||||
export let value = ""
|
export let value = ""
|
||||||
export let maxIconsPerPage = 30
|
export let maxIconsPerPage = 30
|
||||||
export let autofocus = false
|
|
||||||
|
|
||||||
let searchTerm = ""
|
let searchTerm = ""
|
||||||
let selectedLetter = "A"
|
let selectedLetter = "A"
|
||||||
|
@ -118,7 +117,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div bind:this={buttonAnchor}>
|
<div bind:this={buttonAnchor}>
|
||||||
<ActionButton on:click={dropdown.show} {autofocus}>
|
<ActionButton on:click={dropdown.show}>
|
||||||
{displayValue}
|
{displayValue}
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
export let componentInstance = {}
|
export let componentInstance = {}
|
||||||
export let value = ""
|
export let value = ""
|
||||||
export let placeholder
|
export let placeholder
|
||||||
export let autofocus
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
$: datasource = getDatasourceForProvider($currentAsset, componentInstance)
|
$: datasource = getDatasourceForProvider($currentAsset, componentInstance)
|
||||||
|
@ -32,10 +31,4 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Multiselect
|
<Multiselect {placeholder} value={boundValue} on:change={setValue} {options} />
|
||||||
{placeholder}
|
|
||||||
value={boundValue}
|
|
||||||
on:change={setValue}
|
|
||||||
{options}
|
|
||||||
{autofocus}
|
|
||||||
/>
|
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
readableToRuntimeBinding,
|
readableToRuntimeBinding,
|
||||||
runtimeToReadableBinding,
|
runtimeToReadableBinding,
|
||||||
} from "builderStore/dataBinding"
|
} from "builderStore/dataBinding"
|
||||||
import { setContext } from "svelte"
|
|
||||||
import { store } from "builderStore"
|
import { store } from "builderStore"
|
||||||
|
import { onDestroy } from "svelte"
|
||||||
|
|
||||||
export let label = ""
|
export let label = ""
|
||||||
export let componentInstance = {}
|
export let componentInstance = {}
|
||||||
|
@ -13,15 +13,17 @@
|
||||||
export let key = ""
|
export let key = ""
|
||||||
export let type = ""
|
export let type = ""
|
||||||
export let value = null
|
export let value = null
|
||||||
|
export let defaultValue = null
|
||||||
export let props = {}
|
export let props = {}
|
||||||
export let onChange = () => {}
|
export let onChange = () => {}
|
||||||
export let bindings = []
|
export let bindings = []
|
||||||
export let componentBindings = []
|
export let componentBindings = []
|
||||||
export let nested = false
|
export let nested = false
|
||||||
export let autofocus = false
|
export let highlighted = false
|
||||||
|
|
||||||
|
$: nullishValue = value == null || value === ""
|
||||||
$: allBindings = getAllBindings(bindings, componentBindings, nested)
|
$: allBindings = getAllBindings(bindings, componentBindings, nested)
|
||||||
$: safeValue = getSafeValue(value, props.defaultValue, allBindings)
|
$: safeValue = getSafeValue(value, defaultValue, allBindings)
|
||||||
$: tempValue = safeValue
|
$: tempValue = safeValue
|
||||||
$: replaceBindings = val => readableToRuntimeBinding(allBindings, val)
|
$: replaceBindings = val => readableToRuntimeBinding(allBindings, val)
|
||||||
|
|
||||||
|
@ -64,30 +66,18 @@
|
||||||
: enriched
|
: enriched
|
||||||
}
|
}
|
||||||
|
|
||||||
setContext("builderFocus", {
|
onDestroy(() => {
|
||||||
clear: () => {
|
if (highlighted) {
|
||||||
if (!$store?.builderFocus) {
|
store.actions.settings.highlight(null)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
store.update(state => {
|
|
||||||
const updatedFocus = $store?.builderFocus?.filter(focus => {
|
|
||||||
return (
|
|
||||||
focus.location === "component_settings" &&
|
|
||||||
focus.target !== componentInstance._id
|
|
||||||
)
|
|
||||||
})
|
|
||||||
if (updatedFocus?.length > 0) {
|
|
||||||
state.builderFocus = updatedFocus
|
|
||||||
} else {
|
|
||||||
delete state.builderFocus
|
|
||||||
}
|
|
||||||
return state
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="property-control" data-cy={`setting-${key}`}>
|
<div
|
||||||
|
class="property-control"
|
||||||
|
class:highlighted={highlighted && nullishValue}
|
||||||
|
data-cy={`setting-${key}`}
|
||||||
|
>
|
||||||
{#if type !== "boolean" && label}
|
{#if type !== "boolean" && label}
|
||||||
<div class="label">
|
<div class="label">
|
||||||
<Label>{label}</Label>
|
<Label>{label}</Label>
|
||||||
|
@ -107,7 +97,6 @@
|
||||||
{key}
|
{key}
|
||||||
{type}
|
{type}
|
||||||
{...props}
|
{...props}
|
||||||
{autofocus}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -119,6 +108,14 @@
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
|
transition: background 130ms ease-out, border-color 130ms ease-out;
|
||||||
|
border-left: 4px solid transparent;
|
||||||
|
margin: -6px calc(-1 * var(--spacing-xl));
|
||||||
|
padding: 6px var(--spacing-xl) 6px calc(var(--spacing-xl) - 4px);
|
||||||
|
}
|
||||||
|
.property-control.highlighted {
|
||||||
|
background: var(--spectrum-global-color-gray-300);
|
||||||
|
border-color: var(--spectrum-global-color-blue-400);
|
||||||
}
|
}
|
||||||
.label {
|
.label {
|
||||||
padding-bottom: var(--spectrum-global-dimension-size-65);
|
padding-bottom: var(--spectrum-global-dimension-size-65);
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
export let value
|
export let value
|
||||||
export let bindings
|
export let bindings
|
||||||
export let autofocus
|
|
||||||
|
|
||||||
$: urlOptions = $store.screens
|
$: urlOptions = $store.screens
|
||||||
.map(screen => screen.routing?.route)
|
.map(screen => screen.routing?.route)
|
||||||
|
@ -17,5 +16,4 @@
|
||||||
on:change
|
on:change
|
||||||
options={urlOptions}
|
options={urlOptions}
|
||||||
appendBindingsAsOptions={false}
|
appendBindingsAsOptions={false}
|
||||||
{autofocus}
|
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -115,7 +115,6 @@
|
||||||
control={def.control}
|
control={def.control}
|
||||||
label={def.label}
|
label={def.label}
|
||||||
key={def.key}
|
key={def.key}
|
||||||
error="asdasds"
|
|
||||||
value={deepGet($currentAsset, def.key)}
|
value={deepGet($currentAsset, def.key)}
|
||||||
onChange={val => setAssetProps(def.key, val, def.parser, def.validate)}
|
onChange={val => setAssetProps(def.key, val, def.parser, def.validate)}
|
||||||
{bindings}
|
{bindings}
|
||||||
|
|
|
@ -274,7 +274,8 @@
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"label": "Text",
|
"label": "Text",
|
||||||
"key": "text"
|
"key": "text",
|
||||||
|
"defaultValue": "New Button"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "select",
|
"type": "select",
|
||||||
|
@ -1154,15 +1155,13 @@
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"label": "Text",
|
"label": "Text",
|
||||||
"key": "text",
|
"key": "text"
|
||||||
"required": true
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "url",
|
"type": "url",
|
||||||
"label": "URL",
|
"label": "URL",
|
||||||
"key": "url",
|
"key": "url",
|
||||||
"placeholder": "/screen",
|
"placeholder": "/screen"
|
||||||
"required": true
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
import Manifest from "manifest.json"
|
import Manifest from "manifest.json"
|
||||||
import { getActiveConditions, reduceConditionActions } from "utils/conditions"
|
import { getActiveConditions, reduceConditionActions } from "utils/conditions"
|
||||||
import Placeholder from "components/app/Placeholder.svelte"
|
import Placeholder from "components/app/Placeholder.svelte"
|
||||||
|
import ComponentPlaceholder from "components/app/ComponentPlaceholder.svelte"
|
||||||
|
|
||||||
export let instance = {}
|
export let instance = {}
|
||||||
export let isLayout = false
|
export let isLayout = false
|
||||||
|
@ -81,6 +82,7 @@
|
||||||
let definition
|
let definition
|
||||||
let settingsDefinition
|
let settingsDefinition
|
||||||
let settingsDefinitionMap
|
let settingsDefinitionMap
|
||||||
|
let missingRequiredSettings = false
|
||||||
|
|
||||||
// Set up initial state for each new component instance
|
// Set up initial state for each new component instance
|
||||||
$: initialise(instance)
|
$: initialise(instance)
|
||||||
|
@ -99,9 +101,10 @@
|
||||||
|
|
||||||
// Derive definition properties which can all be optional, so need to be
|
// Derive definition properties which can all be optional, so need to be
|
||||||
// coerced to booleans
|
// coerced to booleans
|
||||||
$: editable = !!definition?.editable
|
|
||||||
$: hasChildren = !!definition?.hasChildren
|
$: hasChildren = !!definition?.hasChildren
|
||||||
$: showEmptyState = definition?.showEmptyState !== false
|
$: showEmptyState = definition?.showEmptyState !== false
|
||||||
|
$: hasMissingRequiredSettings = missingRequiredSettings?.length > 0
|
||||||
|
$: editable = !!definition?.editable && !hasMissingRequiredSettings
|
||||||
|
|
||||||
// Interactive components can be selected, dragged and highlighted inside
|
// Interactive components can be selected, dragged and highlighted inside
|
||||||
// the builder preview
|
// the builder preview
|
||||||
|
@ -155,6 +158,7 @@
|
||||||
name,
|
name,
|
||||||
editing,
|
editing,
|
||||||
type: instance._component,
|
type: instance._component,
|
||||||
|
missingRequiredSettings,
|
||||||
})
|
})
|
||||||
|
|
||||||
const initialise = instance => {
|
const initialise = instance => {
|
||||||
|
@ -201,6 +205,27 @@
|
||||||
staticSettings = instanceSettings.staticSettings
|
staticSettings = instanceSettings.staticSettings
|
||||||
dynamicSettings = instanceSettings.dynamicSettings
|
dynamicSettings = instanceSettings.dynamicSettings
|
||||||
|
|
||||||
|
// Check if we have any missing required settings
|
||||||
|
missingRequiredSettings = settingsDefinition.filter(setting => {
|
||||||
|
let empty = instance[setting.key] == null || instance[setting.key] === ""
|
||||||
|
let missing = setting.required && empty
|
||||||
|
|
||||||
|
// Check if this setting depends on another, as it may not be required
|
||||||
|
if (setting.dependsOn) {
|
||||||
|
const dependsOnKey = setting.dependsOn.setting || setting.dependsOn
|
||||||
|
const dependsOnValue = setting.dependsOn.value
|
||||||
|
const realDependentValue = instance[dependsOnKey]
|
||||||
|
if (dependsOnValue == null && realDependentValue == null) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (dependsOnValue !== realDependentValue) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return missing
|
||||||
|
})
|
||||||
|
|
||||||
// Force an initial enrichment of the new settings
|
// Force an initial enrichment of the new settings
|
||||||
enrichComponentSettings(get(context), settingsDefinitionMap, {
|
enrichComponentSettings(get(context), settingsDefinitionMap, {
|
||||||
force: true,
|
force: true,
|
||||||
|
@ -414,6 +439,9 @@
|
||||||
data-id={id}
|
data-id={id}
|
||||||
data-name={name}
|
data-name={name}
|
||||||
>
|
>
|
||||||
|
{#if hasMissingRequiredSettings}
|
||||||
|
<ComponentPlaceholder />
|
||||||
|
{:else}
|
||||||
<svelte:component this={constructor} bind:this={ref} {...initialSettings}>
|
<svelte:component this={constructor} bind:this={ref} {...initialSettings}>
|
||||||
{#if children.length}
|
{#if children.length}
|
||||||
{#each children as child (child._id)}
|
{#each children as child (child._id)}
|
||||||
|
@ -425,6 +453,7 @@
|
||||||
<slot />
|
<slot />
|
||||||
{/if}
|
{/if}
|
||||||
</svelte:component>
|
</svelte:component>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
<script>
|
||||||
|
import { getContext } from "svelte"
|
||||||
|
import { builderStore } from "stores"
|
||||||
|
|
||||||
|
const { styleable } = getContext("sdk")
|
||||||
|
const component = getContext("component")
|
||||||
|
|
||||||
|
$: requiredSetting = $component.missingRequiredSettings?.[0]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if $builderStore.inBuilder && requiredSetting}
|
||||||
|
<div use:styleable={$component.styles}>
|
||||||
|
<div class="component-placeholder">
|
||||||
|
<span>
|
||||||
|
Add the <mark>{requiredSetting.label}</mark> setting to start using your
|
||||||
|
component -
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="spectrum-Link"
|
||||||
|
on:click={() => {
|
||||||
|
builderStore.actions.highlightSetting(requiredSetting.key)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Show me
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.component-placeholder {
|
||||||
|
color: var(--spectrum-global-color-gray-600);
|
||||||
|
font-size: var(--font-size-s);
|
||||||
|
padding: var(--spacing-xs);
|
||||||
|
}
|
||||||
|
.component-placeholder mark {
|
||||||
|
background-color: var(--spectrum-global-color-gray-400);
|
||||||
|
padding: 0 2px;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
.component-placeholder .spectrum-Link {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,6 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
import Placeholder from "./Placeholder.svelte"
|
|
||||||
|
|
||||||
const { linkable, styleable, builderStore } = getContext("sdk")
|
const { linkable, styleable, builderStore } = getContext("sdk")
|
||||||
const component = getContext("component")
|
const component = getContext("component")
|
||||||
|
@ -80,11 +79,7 @@
|
||||||
{componentText}
|
{componentText}
|
||||||
</div>
|
</div>
|
||||||
{:else if $builderStore.inBuilder || componentText}
|
{:else if $builderStore.inBuilder || componentText}
|
||||||
{#if !url && !text}
|
{#if externalLink || openInNewTab}
|
||||||
<div use:styleable={{ ...$component.styles, empty: true }}>
|
|
||||||
<Placeholder />
|
|
||||||
</div>
|
|
||||||
{:else if externalLink || openInNewTab}
|
|
||||||
<a
|
<a
|
||||||
{target}
|
{target}
|
||||||
href={sanitizedUrl}
|
href={sanitizedUrl}
|
||||||
|
|
|
@ -1,67 +1,15 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
import Manifest from "manifest.json"
|
|
||||||
import { builderStore } from "stores"
|
|
||||||
|
|
||||||
const { componentStore } = getContext("sdk")
|
const { builderStore } = getContext("sdk")
|
||||||
const component = getContext("component")
|
const component = getContext("component")
|
||||||
|
|
||||||
export let text
|
export let text
|
||||||
|
|
||||||
$: componentInstance = componentStore.actions.getComponentById($component.id)
|
|
||||||
|
|
||||||
const getComponentKey = compId => {
|
|
||||||
if (!compId) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const prefix = "@budibase/standard-components/"
|
|
||||||
let componentKey = compId.replace(prefix, "")
|
|
||||||
return componentKey
|
|
||||||
}
|
|
||||||
|
|
||||||
const emptyFields = (definition, options) => {
|
|
||||||
if (!options) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
return definition?.settings
|
|
||||||
? definition.settings.filter(setting => {
|
|
||||||
return (
|
|
||||||
setting.required &&
|
|
||||||
(!options[setting.key] || options[setting.key] == "")
|
|
||||||
)
|
|
||||||
})
|
|
||||||
: []
|
|
||||||
}
|
|
||||||
const definition = Manifest[getComponentKey($component.type)]
|
|
||||||
$: focus_setting = emptyFields(definition, componentInstance)[0]
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $builderStore.inBuilder}
|
{#if $builderStore.inBuilder}
|
||||||
<div class="placeholder_wrap">
|
|
||||||
{#if componentInstance && focus_setting}
|
|
||||||
<div>
|
<div>
|
||||||
<span>
|
|
||||||
Add the <mark>{focus_setting?.label}</mark> setting to start using your
|
|
||||||
component
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
class="showMe spectrum-Link"
|
|
||||||
on:click={() => {
|
|
||||||
builderStore.actions.setFocus([
|
|
||||||
{
|
|
||||||
location: "component_settings",
|
|
||||||
key: focus_setting.key,
|
|
||||||
target: $component.id,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Show me
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
{text || $component.name || "Placeholder"}
|
{text || $component.name || "Placeholder"}
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
@ -69,18 +17,5 @@
|
||||||
div {
|
div {
|
||||||
color: var(--spectrum-global-color-gray-600);
|
color: var(--spectrum-global-color-gray-600);
|
||||||
font-size: var(--font-size-s);
|
font-size: var(--font-size-s);
|
||||||
padding: var(--spacing-xs);
|
|
||||||
}
|
|
||||||
:global(div.placeholder_wrap mark) {
|
|
||||||
background-color: var(--spectrum-global-color-gray-400);
|
|
||||||
padding: 0px 2px;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
:global(div.placeholder_wrap .showMe) {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
:global(div.placeholder_wrap .showMe:hover) {
|
|
||||||
text-decoration: underline;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -76,9 +76,7 @@
|
||||||
{#if !formContext}
|
{#if !formContext}
|
||||||
<Placeholder text="Form components need to be wrapped in a form" />
|
<Placeholder text="Form components need to be wrapped in a form" />
|
||||||
{:else if !fieldState}
|
{:else if !fieldState}
|
||||||
{#if $builderStore.inBuilder}
|
|
||||||
<Placeholder />
|
<Placeholder />
|
||||||
{/if}
|
|
||||||
{:else if schemaType && schemaType !== type && type !== "options"}
|
{:else if schemaType && schemaType !== type && type !== "options"}
|
||||||
<Placeholder
|
<Placeholder
|
||||||
text="This Field setting is the wrong data type for this component"
|
text="This Field setting is the wrong data type for this component"
|
||||||
|
|
|
@ -68,8 +68,8 @@ const createBuilderStore = () => {
|
||||||
}
|
}
|
||||||
store.update(state => ({ ...state, editMode: enabled }))
|
store.update(state => ({ ...state, editMode: enabled }))
|
||||||
},
|
},
|
||||||
setFocus: data => {
|
highlightSetting: setting => {
|
||||||
window.parent.postMessage({ type: "builder-focus", data })
|
dispatchEvent("highlight-setting", { setting })
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
|
Loading…
Reference in New Issue