Merge pull request #13327 from Budibase/cheeks-fixes
Various fixes and improvements
This commit is contained in:
commit
cc02d759d5
|
@ -79,7 +79,8 @@ const removeHandler = id => {
|
||||||
export default (element, opts) => {
|
export default (element, opts) => {
|
||||||
const id = Math.random()
|
const id = Math.random()
|
||||||
const update = newOpts => {
|
const update = newOpts => {
|
||||||
const callback = newOpts?.callback || newOpts
|
const callback =
|
||||||
|
newOpts?.callback || (typeof newOpts === "function" ? newOpts : null)
|
||||||
const anchor = newOpts?.anchor || element
|
const anchor = newOpts?.anchor || element
|
||||||
const allowedType = newOpts?.allowedType || "click"
|
const allowedType = newOpts?.allowedType || "click"
|
||||||
updateHandler(id, element, anchor, callback, allowedType)
|
updateHandler(id, element, anchor, callback, allowedType)
|
||||||
|
|
|
@ -42,7 +42,6 @@
|
||||||
.main {
|
.main {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
}
|
||||||
.padding .main {
|
.padding .main {
|
||||||
padding: var(--spacing-xl);
|
padding: var(--spacing-xl);
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
export let schema
|
export let schema
|
||||||
export let value
|
export let value
|
||||||
export let customRenderers = []
|
export let customRenderers = []
|
||||||
|
export let snippets
|
||||||
|
|
||||||
let renderer
|
let renderer
|
||||||
const typeMap = {
|
const typeMap = {
|
||||||
|
@ -44,7 +45,7 @@
|
||||||
if (!template) {
|
if (!template) {
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
return processStringSync(template, { value })
|
return processStringSync(template, { value, snippets })
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
export let customPlaceholder = false
|
export let customPlaceholder = false
|
||||||
export let showHeaderBorder = true
|
export let showHeaderBorder = true
|
||||||
export let placeholderText = "No rows found"
|
export let placeholderText = "No rows found"
|
||||||
|
export let snippets = []
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
@ -425,6 +426,7 @@
|
||||||
<CellRenderer
|
<CellRenderer
|
||||||
{customRenderers}
|
{customRenderers}
|
||||||
{row}
|
{row}
|
||||||
|
{snippets}
|
||||||
schema={schema[field]}
|
schema={schema[field]}
|
||||||
value={deepGet(row, field)}
|
value={deepGet(row, field)}
|
||||||
on:clickrelationship
|
on:clickrelationship
|
||||||
|
|
|
@ -313,7 +313,7 @@ export const bindingsToCompletions = (bindings, mode) => {
|
||||||
...bindingByCategory[catKey].reduce((acc, binding) => {
|
...bindingByCategory[catKey].reduce((acc, binding) => {
|
||||||
let displayType = binding.fieldSchema?.type || binding.display?.type
|
let displayType = binding.fieldSchema?.type || binding.display?.type
|
||||||
acc.push({
|
acc.push({
|
||||||
label: binding.display?.name || "NO NAME",
|
label: binding.display?.name || binding.readableBinding || "NO NAME",
|
||||||
info: completion => {
|
info: completion => {
|
||||||
return buildBindingInfoNode(completion, binding)
|
return buildBindingInfoNode(completion, binding)
|
||||||
},
|
},
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
export let allowJS = false
|
export let allowJS = false
|
||||||
export let allowHelpers = true
|
export let allowHelpers = true
|
||||||
export let autofocusEditor = false
|
export let autofocusEditor = false
|
||||||
|
export let context = null
|
||||||
|
|
||||||
$: enrichedBindings = enrichBindings(bindings)
|
$: enrichedBindings = enrichBindings(bindings)
|
||||||
|
|
||||||
|
@ -27,7 +28,7 @@
|
||||||
|
|
||||||
<BindingPanel
|
<BindingPanel
|
||||||
bindings={enrichedBindings}
|
bindings={enrichedBindings}
|
||||||
context={$previewStore.selectedComponentContext}
|
context={{ ...$previewStore.selectedComponentContext, ...context }}
|
||||||
snippets={$snippets}
|
snippets={$snippets}
|
||||||
{value}
|
{value}
|
||||||
{allowJS}
|
{allowJS}
|
||||||
|
|
|
@ -7,10 +7,13 @@
|
||||||
Layout,
|
Layout,
|
||||||
Label,
|
Label,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import { themeStore } from "stores/builder"
|
import { themeStore, previewStore } from "stores/builder"
|
||||||
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte"
|
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte"
|
||||||
|
|
||||||
export let column
|
export let column
|
||||||
|
|
||||||
|
$: columnValue =
|
||||||
|
$previewStore.selectedComponentContext?.eventContext?.row?.[column.name]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<DrawerContent>
|
<DrawerContent>
|
||||||
|
@ -41,6 +44,9 @@
|
||||||
icon: "TableColumnMerge",
|
icon: "TableColumnMerge",
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
context={{
|
||||||
|
value: columnValue,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<Layout noPadding gap="XS">
|
<Layout noPadding gap="XS">
|
||||||
<Label>Background color</Label>
|
<Label>Background color</Label>
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
{
|
{
|
||||||
"name": "Layout",
|
"name": "Layout",
|
||||||
"icon": "ClassicGridView",
|
"icon": "ClassicGridView",
|
||||||
"children": ["container", "section", "grid", "sidepanel"]
|
"children": ["container", "section", "sidepanel"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Data",
|
"name": "Data",
|
||||||
|
|
|
@ -279,12 +279,10 @@ export class ComponentStore extends BudiStore {
|
||||||
else {
|
else {
|
||||||
if (setting.type === "dataProvider") {
|
if (setting.type === "dataProvider") {
|
||||||
// Validate data provider exists, or else clear it
|
// Validate data provider exists, or else clear it
|
||||||
const treeId = parent?._id || component._id
|
const providers = findAllMatchingComponents(
|
||||||
const path = findComponentPath(screen?.props, treeId)
|
screen?.props,
|
||||||
const providers = path.filter(component =>
|
x => x._component === "@budibase/standard-components/dataprovider"
|
||||||
component._component?.endsWith("/dataprovider")
|
|
||||||
)
|
)
|
||||||
// Validate non-empty values
|
|
||||||
const valid = providers?.some(dp => value.includes?.(dp._id))
|
const valid = providers?.some(dp => value.includes?.(dp._id))
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
if (providers.length) {
|
if (providers.length) {
|
||||||
|
|
|
@ -7,12 +7,25 @@ export const INITIAL_HOVER_STATE = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class HoverStore extends BudiStore {
|
export class HoverStore extends BudiStore {
|
||||||
|
hoverTimeout
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super({ ...INITIAL_HOVER_STATE })
|
super({ ...INITIAL_HOVER_STATE })
|
||||||
this.hover = this.hover.bind(this)
|
this.hover = this.hover.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
hover(componentId, notifyClient = true) {
|
hover(componentId, notifyClient = true) {
|
||||||
|
clearTimeout(this.hoverTimeout)
|
||||||
|
if (componentId) {
|
||||||
|
this.processHover(componentId, notifyClient)
|
||||||
|
} else {
|
||||||
|
this.hoverTimeout = setTimeout(() => {
|
||||||
|
this.processHover(componentId, notifyClient)
|
||||||
|
}, 10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
processHover(componentId, notifyClient) {
|
||||||
if (componentId === get(this.store).componentId) {
|
if (componentId === get(this.store).componentId) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,9 +26,12 @@
|
||||||
|
|
||||||
let schema
|
let schema
|
||||||
|
|
||||||
|
$: id = $component.id
|
||||||
|
$: selected = $component.selected
|
||||||
|
$: builderStep = $builderStore.metadata?.step
|
||||||
$: fetchSchema(dataSource)
|
$: fetchSchema(dataSource)
|
||||||
$: enrichedSteps = enrichSteps(steps, schema, $component.id, $currentStep)
|
$: enrichedSteps = enrichSteps(steps, schema, id)
|
||||||
$: updateCurrentStep(enrichedSteps, $builderStore, $component)
|
$: updateCurrentStep(enrichedSteps, selected, builderStep)
|
||||||
|
|
||||||
// Provide additional data context for live binding eval
|
// Provide additional data context for live binding eval
|
||||||
export const getAdditionalDataContext = () => {
|
export const getAdditionalDataContext = () => {
|
||||||
|
@ -40,30 +43,22 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateCurrentStep = (steps, builderStore, component) => {
|
const updateCurrentStep = (steps, selected, builderStep) => {
|
||||||
const { componentId, step } = builderStore.metadata || {}
|
// If we aren't selected in the builder then just allowing the normal form
|
||||||
|
// to take control.
|
||||||
// If we aren't in the builder or aren't selected then don't update the step
|
if (!selected) {
|
||||||
// context at all, allowing the normal form to take control.
|
|
||||||
if (
|
|
||||||
!component.selected ||
|
|
||||||
!builderStore.inBuilder ||
|
|
||||||
componentId !== component.id
|
|
||||||
) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure we have a valid step selected
|
// Ensure we have a valid step selected
|
||||||
let newStep = Math.min(step || 0, steps.length - 1)
|
let newStep = Math.min(builderStep || 0, steps.length - 1)
|
||||||
|
|
||||||
// Sanity check
|
|
||||||
newStep = Math.max(newStep, 0)
|
newStep = Math.max(newStep, 0)
|
||||||
|
|
||||||
// Add 1 because the form component expects 1 indexed rather than 0 indexed
|
// Add 1 because the form component expects 1 indexed rather than 0 indexed
|
||||||
currentStep.set(newStep + 1)
|
currentStep.set(newStep + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchSchema = async () => {
|
const fetchSchema = async dataSource => {
|
||||||
schema = (await fetchDatasourceSchema(dataSource)) || {}
|
schema = (await fetchDatasourceSchema(dataSource)) || {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
$: formattedFields = convertOldFieldFormat(fields)
|
$: formattedFields = convertOldFieldFormat(fields)
|
||||||
$: fieldsOrDefault = getDefaultFields(formattedFields, schema)
|
$: fieldsOrDefault = getDefaultFields(formattedFields, schema)
|
||||||
$: fetchSchema(dataSource)
|
$: fetchSchema(dataSource)
|
||||||
|
$: id = $component.id
|
||||||
// We could simply spread $$props into the inner form and append our
|
// We could simply spread $$props into the inner form and append our
|
||||||
// additions, but that would create svelte warnings about unused props and
|
// additions, but that would create svelte warnings about unused props and
|
||||||
// make maintenance in future more confusing as we typically always have a
|
// make maintenance in future more confusing as we typically always have a
|
||||||
|
@ -53,7 +54,7 @@
|
||||||
buttons:
|
buttons:
|
||||||
buttons ||
|
buttons ||
|
||||||
Utils.buildFormBlockButtonConfig({
|
Utils.buildFormBlockButtonConfig({
|
||||||
_id: $component.id,
|
_id: id,
|
||||||
showDeleteButton,
|
showDeleteButton,
|
||||||
showSaveButton,
|
showSaveButton,
|
||||||
saveButtonLabel,
|
saveButtonLabel,
|
||||||
|
|
|
@ -50,6 +50,7 @@
|
||||||
let schemaLoaded = false
|
let schemaLoaded = false
|
||||||
|
|
||||||
$: deleteLabel = setDeleteLabel(sidePanelDeleteLabel, sidePanelShowDelete)
|
$: deleteLabel = setDeleteLabel(sidePanelDeleteLabel, sidePanelShowDelete)
|
||||||
|
$: id = $component.id
|
||||||
$: isDSPlus = dataSource?.type === "table" || dataSource?.type === "viewV2"
|
$: isDSPlus = dataSource?.type === "table" || dataSource?.type === "viewV2"
|
||||||
$: fetchSchema(dataSource)
|
$: fetchSchema(dataSource)
|
||||||
$: enrichSearchColumns(searchColumns, schema).then(
|
$: enrichSearchColumns(searchColumns, schema).then(
|
||||||
|
@ -279,7 +280,7 @@
|
||||||
dataSource,
|
dataSource,
|
||||||
buttonPosition: "top",
|
buttonPosition: "top",
|
||||||
buttons: Utils.buildFormBlockButtonConfig({
|
buttons: Utils.buildFormBlockButtonConfig({
|
||||||
_id: $component.id + "-form-edit",
|
_id: id + "-form-edit",
|
||||||
showDeleteButton: deleteLabel !== "",
|
showDeleteButton: deleteLabel !== "",
|
||||||
showSaveButton: true,
|
showSaveButton: true,
|
||||||
saveButtonLabel: sidePanelSaveLabel || "Save",
|
saveButtonLabel: sidePanelSaveLabel || "Save",
|
||||||
|
@ -313,7 +314,7 @@
|
||||||
dataSource,
|
dataSource,
|
||||||
buttonPosition: "top",
|
buttonPosition: "top",
|
||||||
buttons: Utils.buildFormBlockButtonConfig({
|
buttons: Utils.buildFormBlockButtonConfig({
|
||||||
_id: $component.id + "-form-new",
|
_id: id + "-form-new",
|
||||||
showDeleteButton: false,
|
showDeleteButton: false,
|
||||||
showSaveButton: true,
|
showSaveButton: true,
|
||||||
saveButtonLabel: "Save",
|
saveButtonLabel: "Save",
|
||||||
|
|
|
@ -16,8 +16,15 @@
|
||||||
export let noRowsMessage
|
export let noRowsMessage
|
||||||
|
|
||||||
const component = getContext("component")
|
const component = getContext("component")
|
||||||
const { styleable, getAction, ActionTypes, rowSelectionStore } =
|
const context = getContext("context")
|
||||||
getContext("sdk")
|
const {
|
||||||
|
styleable,
|
||||||
|
getAction,
|
||||||
|
ActionTypes,
|
||||||
|
rowSelectionStore,
|
||||||
|
generateGoldenSample,
|
||||||
|
} = getContext("sdk")
|
||||||
|
|
||||||
const customColumnKey = `custom-${Math.random()}`
|
const customColumnKey = `custom-${Math.random()}`
|
||||||
const customRenderers = [
|
const customRenderers = [
|
||||||
{
|
{
|
||||||
|
@ -28,6 +35,7 @@
|
||||||
|
|
||||||
let selectedRows = []
|
let selectedRows = []
|
||||||
|
|
||||||
|
$: snippets = $context.snippets
|
||||||
$: hasChildren = $component.children
|
$: hasChildren = $component.children
|
||||||
$: loading = dataProvider?.loading ?? false
|
$: loading = dataProvider?.loading ?? false
|
||||||
$: data = dataProvider?.rows || []
|
$: data = dataProvider?.rows || []
|
||||||
|
@ -61,6 +69,16 @@
|
||||||
selectedRows,
|
selectedRows,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Provide additional data context for live binding eval
|
||||||
|
export const getAdditionalDataContext = () => {
|
||||||
|
const goldenRow = generateGoldenSample(data)
|
||||||
|
return {
|
||||||
|
eventContext: {
|
||||||
|
row: goldenRow,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const getFields = (
|
const getFields = (
|
||||||
schema,
|
schema,
|
||||||
customColumns,
|
customColumns,
|
||||||
|
@ -178,6 +196,7 @@
|
||||||
{quiet}
|
{quiet}
|
||||||
{compact}
|
{compact}
|
||||||
{customRenderers}
|
{customRenderers}
|
||||||
|
{snippets}
|
||||||
allowSelectRows={allowSelectRows && table}
|
allowSelectRows={allowSelectRows && table}
|
||||||
bind:selectedRows
|
bind:selectedRows
|
||||||
allowEditRows={false}
|
allowEditRows={false}
|
||||||
|
|
|
@ -6,36 +6,24 @@ export const getOptions = (
|
||||||
valueColumn,
|
valueColumn,
|
||||||
customOptions
|
customOptions
|
||||||
) => {
|
) => {
|
||||||
const isArray = fieldSchema?.type === "array"
|
|
||||||
// Take options from schema
|
// Take options from schema
|
||||||
if (optionsSource == null || optionsSource === "schema") {
|
if (optionsSource == null || optionsSource === "schema") {
|
||||||
return fieldSchema?.constraints?.inclusion ?? []
|
return fieldSchema?.constraints?.inclusion ?? []
|
||||||
}
|
}
|
||||||
|
|
||||||
if (optionsSource === "provider" && isArray) {
|
|
||||||
let optionsSet = {}
|
|
||||||
|
|
||||||
dataProvider?.rows?.forEach(row => {
|
|
||||||
const value = row?.[valueColumn]
|
|
||||||
if (value != null) {
|
|
||||||
const label = row[labelColumn] || value
|
|
||||||
optionsSet[value] = { value, label }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return Object.values(optionsSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract options from data provider
|
// Extract options from data provider
|
||||||
if (optionsSource === "provider" && valueColumn) {
|
if (optionsSource === "provider" && valueColumn) {
|
||||||
let optionsSet = {}
|
let valueCache = {}
|
||||||
|
let options = []
|
||||||
dataProvider?.rows?.forEach(row => {
|
dataProvider?.rows?.forEach(row => {
|
||||||
const value = row?.[valueColumn]
|
const value = row?.[valueColumn]
|
||||||
if (value != null) {
|
if (value != null && !valueCache[value]) {
|
||||||
|
valueCache[value] = true
|
||||||
const label = row[labelColumn] || value
|
const label = row[labelColumn] || value
|
||||||
optionsSet[value] = { value, label }
|
options.push({ value, label })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return Object.values(optionsSet)
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract custom options
|
// Extract custom options
|
||||||
|
|
|
@ -345,8 +345,7 @@
|
||||||
<IndicatorSet
|
<IndicatorSet
|
||||||
componentId={$dndParent}
|
componentId={$dndParent}
|
||||||
color="var(--spectrum-global-color-static-green-500)"
|
color="var(--spectrum-global-color-static-green-500)"
|
||||||
zIndex="930"
|
zIndex={920}
|
||||||
transition
|
|
||||||
prefix="Inside"
|
prefix="Inside"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
<script>
|
<script>
|
||||||
import { onMount, onDestroy } from "svelte"
|
import { onMount, onDestroy } from "svelte"
|
||||||
import IndicatorSet from "./IndicatorSet.svelte"
|
import IndicatorSet from "./IndicatorSet.svelte"
|
||||||
import { builderStore, dndIsDragging, hoverStore } from "stores"
|
import { dndIsDragging, hoverStore, builderStore } from "stores"
|
||||||
|
|
||||||
$: componentId = $hoverStore.hoveredComponentId
|
$: componentId = $hoverStore.hoveredComponentId
|
||||||
$: zIndex = componentId === $builderStore.selectedComponentId ? 900 : 920
|
$: selectedComponentId = $builderStore.selectedComponentId
|
||||||
|
$: selected = componentId === selectedComponentId
|
||||||
|
|
||||||
const onMouseOver = e => {
|
const onMouseOver = e => {
|
||||||
// Ignore if dragging
|
// Ignore if dragging
|
||||||
|
@ -45,7 +46,6 @@
|
||||||
<IndicatorSet
|
<IndicatorSet
|
||||||
componentId={$dndIsDragging ? null : componentId}
|
componentId={$dndIsDragging ? null : componentId}
|
||||||
color="var(--spectrum-global-color-static-blue-200)"
|
color="var(--spectrum-global-color-static-blue-200)"
|
||||||
transition
|
zIndex={selected ? 890 : 910}
|
||||||
{zIndex}
|
|
||||||
allowResizeAnchors
|
allowResizeAnchors
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<script>
|
<script>
|
||||||
import { fade } from "svelte/transition"
|
|
||||||
import { Icon } from "@budibase/bbui"
|
import { Icon } from "@budibase/bbui"
|
||||||
|
|
||||||
export let top
|
export let top
|
||||||
|
@ -11,7 +10,6 @@
|
||||||
export let color
|
export let color
|
||||||
export let zIndex
|
export let zIndex
|
||||||
export let componentId
|
export let componentId
|
||||||
export let transition = false
|
|
||||||
export let line = false
|
export let line = false
|
||||||
export let alignRight = false
|
export let alignRight = false
|
||||||
export let showResizeAnchors = false
|
export let showResizeAnchors = false
|
||||||
|
@ -31,10 +29,6 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
in:fade={{
|
|
||||||
delay: transition ? 100 : 0,
|
|
||||||
duration: transition ? 100 : 0,
|
|
||||||
}}
|
|
||||||
class="indicator"
|
class="indicator"
|
||||||
class:flipped
|
class:flipped
|
||||||
class:line
|
class:line
|
||||||
|
@ -127,10 +121,6 @@
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Icon styles */
|
|
||||||
.label :global(.spectrum-Icon + .text) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Anchor */
|
/* Anchor */
|
||||||
.anchor {
|
.anchor {
|
||||||
--size: 24px;
|
--size: 24px;
|
||||||
|
|
|
@ -4,27 +4,39 @@
|
||||||
import { domDebounce } from "utils/domDebounce"
|
import { domDebounce } from "utils/domDebounce"
|
||||||
import { builderStore } from "stores"
|
import { builderStore } from "stores"
|
||||||
|
|
||||||
export let componentId
|
export let componentId = null
|
||||||
export let color
|
export let color = null
|
||||||
export let transition
|
export let zIndex = 900
|
||||||
export let zIndex
|
|
||||||
export let prefix = null
|
export let prefix = null
|
||||||
export let allowResizeAnchors = false
|
export let allowResizeAnchors = false
|
||||||
|
|
||||||
let indicators = []
|
const errorColor = "var(--spectrum-global-color-static-red-600)"
|
||||||
|
const defaultState = () => ({
|
||||||
|
// Cached props
|
||||||
|
componentId,
|
||||||
|
color,
|
||||||
|
zIndex,
|
||||||
|
prefix,
|
||||||
|
allowResizeAnchors,
|
||||||
|
|
||||||
|
// Computed state
|
||||||
|
indicators: [],
|
||||||
|
text: null,
|
||||||
|
icon: null,
|
||||||
|
insideGrid: false,
|
||||||
|
error: false,
|
||||||
|
})
|
||||||
|
|
||||||
let interval
|
let interval
|
||||||
let text
|
let state = defaultState()
|
||||||
let icon
|
let nextState = null
|
||||||
let insideGrid = false
|
|
||||||
let errorState = false
|
|
||||||
|
|
||||||
$: visibleIndicators = indicators.filter(x => x.visible)
|
|
||||||
$: offset = $builderStore.inBuilder ? 0 : 2
|
|
||||||
|
|
||||||
let updating = false
|
let updating = false
|
||||||
let observers = []
|
let observers = []
|
||||||
let callbackCount = 0
|
let callbackCount = 0
|
||||||
let nextIndicators = []
|
|
||||||
|
$: visibleIndicators = state.indicators.filter(x => x.visible)
|
||||||
|
$: offset = $builderStore.inBuilder ? 0 : 2
|
||||||
|
$: $$props, debouncedUpdate()
|
||||||
|
|
||||||
const checkInsideGrid = id => {
|
const checkInsideGrid = id => {
|
||||||
const component = document.getElementsByClassName(id)[0]
|
const component = document.getElementsByClassName(id)[0]
|
||||||
|
@ -44,10 +56,10 @@
|
||||||
if (callbackCount >= observers.length) {
|
if (callbackCount >= observers.length) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
nextIndicators[idx].visible =
|
nextState.indicators[idx].visible =
|
||||||
nextIndicators[idx].insideSidePanel || entries[0].isIntersecting
|
nextState.indicators[idx].insideSidePanel || entries[0].isIntersecting
|
||||||
if (++callbackCount === observers.length) {
|
if (++callbackCount === observers.length) {
|
||||||
indicators = nextIndicators
|
state = nextState
|
||||||
updating = false
|
updating = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,7 +71,7 @@
|
||||||
|
|
||||||
// Sanity check
|
// Sanity check
|
||||||
if (!componentId) {
|
if (!componentId) {
|
||||||
indicators = []
|
state = defaultState()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,25 +80,25 @@
|
||||||
callbackCount = 0
|
callbackCount = 0
|
||||||
observers.forEach(o => o.disconnect())
|
observers.forEach(o => o.disconnect())
|
||||||
observers = []
|
observers = []
|
||||||
nextIndicators = []
|
nextState = defaultState()
|
||||||
|
|
||||||
// Check if we're inside a grid
|
// Check if we're inside a grid
|
||||||
if (allowResizeAnchors) {
|
if (allowResizeAnchors) {
|
||||||
insideGrid = checkInsideGrid(componentId)
|
nextState.insideGrid = checkInsideGrid(componentId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine next set of indicators
|
// Determine next set of indicators
|
||||||
const parents = document.getElementsByClassName(componentId)
|
const parents = document.getElementsByClassName(componentId)
|
||||||
if (parents.length) {
|
if (parents.length) {
|
||||||
text = parents[0].dataset.name
|
nextState.text = parents[0].dataset.name
|
||||||
if (prefix) {
|
if (nextState.prefix) {
|
||||||
text = `${prefix} ${text}`
|
nextState.text = `${nextState.prefix} ${nextState.text}`
|
||||||
}
|
}
|
||||||
if (parents[0].dataset.icon) {
|
if (parents[0].dataset.icon) {
|
||||||
icon = parents[0].dataset.icon
|
nextState.icon = parents[0].dataset.icon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
errorState = parents?.[0]?.classList.contains("error")
|
nextState.error = parents?.[0]?.classList.contains("error")
|
||||||
|
|
||||||
// Batch reads to minimize reflow
|
// Batch reads to minimize reflow
|
||||||
const scrollX = window.scrollX
|
const scrollX = window.scrollX
|
||||||
|
@ -102,8 +114,9 @@
|
||||||
|
|
||||||
// If there aren't any nodes then reset
|
// If there aren't any nodes then reset
|
||||||
if (!children.length) {
|
if (!children.length) {
|
||||||
indicators = []
|
state = defaultState()
|
||||||
updating = false
|
updating = false
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const device = document.getElementById("app-root")
|
const device = document.getElementById("app-root")
|
||||||
|
@ -119,7 +132,7 @@
|
||||||
observers.push(observer)
|
observers.push(observer)
|
||||||
|
|
||||||
const elBounds = child.getBoundingClientRect()
|
const elBounds = child.getBoundingClientRect()
|
||||||
nextIndicators.push({
|
nextState.indicators.push({
|
||||||
top: elBounds.top + scrollY - deviceBounds.top - offset,
|
top: elBounds.top + scrollY - deviceBounds.top - offset,
|
||||||
left: elBounds.left + scrollX - deviceBounds.left - offset,
|
left: elBounds.left + scrollX - deviceBounds.left - offset,
|
||||||
width: elBounds.width + 4,
|
width: elBounds.width + 4,
|
||||||
|
@ -144,20 +157,17 @@
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#key componentId}
|
{#each visibleIndicators as indicator, idx}
|
||||||
{#each visibleIndicators as indicator, idx}
|
<Indicator
|
||||||
<Indicator
|
top={indicator.top}
|
||||||
top={indicator.top}
|
left={indicator.left}
|
||||||
left={indicator.left}
|
width={indicator.width}
|
||||||
width={indicator.width}
|
height={indicator.height}
|
||||||
height={indicator.height}
|
text={idx === 0 ? state.text : null}
|
||||||
text={idx === 0 ? text : null}
|
icon={idx === 0 ? state.icon : null}
|
||||||
icon={idx === 0 ? icon : null}
|
showResizeAnchors={state.allowResizeAnchors && state.insideGrid}
|
||||||
showResizeAnchors={allowResizeAnchors && insideGrid}
|
color={state.error ? errorColor : state.color}
|
||||||
color={errorState ? "var(--spectrum-global-color-static-red-600)" : color}
|
componentId={state.componentId}
|
||||||
{componentId}
|
zIndex={state.zIndex}
|
||||||
{transition}
|
/>
|
||||||
{zIndex}
|
{/each}
|
||||||
/>
|
|
||||||
{/each}
|
|
||||||
{/key}
|
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
<IndicatorSet
|
<IndicatorSet
|
||||||
componentId={$builderStore.selectedComponentId}
|
componentId={$builderStore.selectedComponentId}
|
||||||
{color}
|
{color}
|
||||||
zIndex="910"
|
zIndex={900}
|
||||||
transition
|
|
||||||
allowResizeAnchors
|
allowResizeAnchors
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -98,7 +98,7 @@ const loadBudibase = async () => {
|
||||||
context: stringifiedContext,
|
context: stringifiedContext,
|
||||||
})
|
})
|
||||||
} else if (type === "hover-component") {
|
} else if (type === "hover-component") {
|
||||||
hoverStore.actions.hoverComponent(data)
|
hoverStore.actions.hoverComponent(data, false)
|
||||||
} else if (type === "builder-meta") {
|
} else if (type === "builder-meta") {
|
||||||
builderStore.actions.setMetadata(data)
|
builderStore.actions.setMetadata(data)
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,8 @@ import {
|
||||||
LuceneUtils,
|
LuceneUtils,
|
||||||
Constants,
|
Constants,
|
||||||
RowUtils,
|
RowUtils,
|
||||||
|
memo,
|
||||||
|
derivedMemo,
|
||||||
} from "@budibase/frontend-core"
|
} from "@budibase/frontend-core"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -71,6 +73,8 @@ export default {
|
||||||
makePropSafe,
|
makePropSafe,
|
||||||
createContextStore,
|
createContextStore,
|
||||||
generateGoldenSample: RowUtils.generateGoldenSample,
|
generateGoldenSample: RowUtils.generateGoldenSample,
|
||||||
|
memo,
|
||||||
|
derivedMemo,
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
Provider,
|
Provider,
|
||||||
|
|
|
@ -5,13 +5,27 @@ const createHoverStore = () => {
|
||||||
const store = writable({
|
const store = writable({
|
||||||
hoveredComponentId: null,
|
hoveredComponentId: null,
|
||||||
})
|
})
|
||||||
|
let hoverTimeout
|
||||||
|
|
||||||
const hoverComponent = id => {
|
const hoverComponent = (id, notifyBuilder = true) => {
|
||||||
|
clearTimeout(hoverTimeout)
|
||||||
|
if (id) {
|
||||||
|
processHover(id, notifyBuilder)
|
||||||
|
} else {
|
||||||
|
hoverTimeout = setTimeout(() => {
|
||||||
|
processHover(id, notifyBuilder)
|
||||||
|
}, 10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const processHover = (id, notifyBuilder = true) => {
|
||||||
if (id === get(store).hoveredComponentId) {
|
if (id === get(store).hoveredComponentId) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
store.set({ hoveredComponentId: id })
|
store.set({ hoveredComponentId: id })
|
||||||
eventStore.actions.dispatchEvent("hover-component", { id })
|
if (notifyBuilder) {
|
||||||
|
eventStore.actions.dispatchEvent("hover-component", { id })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
Loading…
Reference in New Issue