From 73d3ce2038c4086b99e4494b6d6caf6de7faa6ad Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 5 Feb 2025 11:01:09 +0000 Subject: [PATCH 01/72] Initial work on support dragging new components on to grid screens and typing improvements for client --- .../new/_components/NewComponentPanel.svelte | 12 +- packages/builder/src/stores/builder/app.ts | 6 +- .../builder/src/stores/builder/components.ts | 82 +------- .../builder/src/stores/builder/navigation.ts | 4 +- .../builder/src/stores/builder/preview.ts | 17 +- packages/client/package.json | 2 +- .../client/src/components/ClientApp.svelte | 2 + .../preview/GridNewComponentDNDHandler.svelte | 166 +++++++++++++++ packages/client/src/index.d.ts | 5 - packages/client/src/index.js | 138 ------------- packages/client/src/index.ts | 195 +++++++++++++++++- packages/client/src/stores/dnd.js | 11 +- packages/client/src/stores/index.js | 2 +- packages/client/src/stores/screens.js | 4 + packages/client/vite.config.mjs | 10 +- packages/types/src/ui/components/index.ts | 10 + 16 files changed, 430 insertions(+), 236 deletions(-) create mode 100644 packages/client/src/components/preview/GridNewComponentDNDHandler.svelte delete mode 100644 packages/client/src/index.d.ts delete mode 100644 packages/client/src/index.js diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/NewComponentPanel.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/NewComponentPanel.svelte index aa0b26f6b7..784642787b 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/NewComponentPanel.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/NewComponentPanel.svelte @@ -13,6 +13,11 @@ import { fly } from "svelte/transition" import { findComponentPath } from "@/helpers/components" + // Smallest possible 1x1 transparent GIF + const ghost = new Image(1, 1) + ghost.src = + "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" + let searchString let searchRef let selectedIndex @@ -217,7 +222,8 @@ } }) - const onDragStart = component => { + const onDragStart = (e, component) => { + e.dataTransfer.setDragImage(ghost, 0, 0) previewStore.startDrag(component) } @@ -250,7 +256,7 @@ {#each category.children as component}
onDragStart(component.component)} + on:dragstart={e => onDragStart(e, component.component)} on:dragend={onDragEnd} class="component" class:selected={selectedIndex === orderMap[component.component]} @@ -308,7 +314,7 @@ } .component:hover { background: var(--spectrum-global-color-gray-300); - cursor: pointer; + cursor: grab; } .component :global(.spectrum-Body) { line-height: 1.2 !important; diff --git a/packages/builder/src/stores/builder/app.ts b/packages/builder/src/stores/builder/app.ts index 3c8ed4afc1..abebb5d9f1 100644 --- a/packages/builder/src/stores/builder/app.ts +++ b/packages/builder/src/stores/builder/app.ts @@ -39,7 +39,7 @@ interface AppMetaState { appInstance: { _id: string } | null initialised: boolean hasAppPackage: boolean - usedPlugins: Plugin[] | null + usedPlugins: Plugin[] automations: AutomationSettings routes: { [key: string]: any } version?: string @@ -76,7 +76,7 @@ export const INITIAL_APP_META_STATE: AppMetaState = { appInstance: null, initialised: false, hasAppPackage: false, - usedPlugins: null, + usedPlugins: [], automations: {}, routes: {}, } @@ -109,7 +109,7 @@ export class AppMetaStore extends BudiStore { appInstance: app.instance, revertableVersion: app.revertableVersion, upgradableVersion: app.upgradableVersion, - usedPlugins: app.usedPlugins || null, + usedPlugins: app.usedPlugins || [], icon: app.icon, features: { ...INITIAL_APP_META_STATE.features, diff --git a/packages/builder/src/stores/builder/components.ts b/packages/builder/src/stores/builder/components.ts index a8f52e2660..ad221cc67d 100644 --- a/packages/builder/src/stores/builder/components.ts +++ b/packages/builder/src/stores/builder/components.ts @@ -139,10 +139,6 @@ export class ComponentStore extends BudiStore { /** * Retrieve the component definition object - * @param {string} componentType - * @example - * '@budibase/standard-components/container' - * @returns {object} */ getDefinition(componentType: string) { if (!componentType) { @@ -151,10 +147,6 @@ export class ComponentStore extends BudiStore { return get(this.store).components[componentType] } - /** - * - * @returns {object} - */ getDefaultDatasource() { // Ignore users table const validTables = get(tables).list.filter(x => x._id !== "ta_users") @@ -188,8 +180,6 @@ export class ComponentStore extends BudiStore { /** * Takes an enriched component instance and applies any required migration * logic - * @param {object} enrichedComponent - * @returns {object} migrated Component */ migrateSettings(enrichedComponent: Component) { const componentPrefix = "@budibase/standard-components" @@ -230,22 +220,15 @@ export class ComponentStore extends BudiStore { for (let setting of filterableTypes || []) { const isLegacy = Array.isArray(enrichedComponent[setting.key]) if (isLegacy) { - const processedSetting = utils.processSearchFilters( + enrichedComponent[setting.key] = utils.processSearchFilters( enrichedComponent[setting.key] ) - enrichedComponent[setting.key] = processedSetting migrated = true } } return migrated } - /** - * - * @param {object} component - * @param {object} opts - * @returns - */ enrichEmptySettings( component: Component, opts: { screen?: Screen; parent?: Component; useDefaultValues?: boolean } @@ -427,17 +410,10 @@ export class ComponentStore extends BudiStore { } } - /** - * - * @param {string} componentName - * @param {object} presetProps - * @param {object} parent - * @returns - */ createInstance( componentType: string, - presetProps: any, - parent: any + presetProps?: Record, + parent?: Component ): Component | null { const screen = get(selectedScreen) if (!screen) { @@ -463,7 +439,7 @@ export class ComponentStore extends BudiStore { _id: Helpers.uuid(), _component: definition.component, _styles: { - normal: {}, + normal: { ...presetProps?._styles?.normal }, hover: {}, active: {}, }, @@ -512,19 +488,11 @@ export class ComponentStore extends BudiStore { } } - /** - * - * @param {string} componentName - * @param {object} presetProps - * @param {object} parent - * @param {number} index - * @returns - */ async create( componentType: string, - presetProps: any, - parent: Component, - index: number + presetProps?: Record, + parent?: Component, + index?: number ) { const state = get(this.store) const componentInstance = this.createInstance( @@ -611,13 +579,6 @@ export class ComponentStore extends BudiStore { return componentInstance } - /** - * - * @param {function} patchFn - * @param {string} componentId - * @param {string} screenId - * @returns - */ async patch( patchFn: (component: Component, screen: Screen) => any, componentId?: string, @@ -652,11 +613,6 @@ export class ComponentStore extends BudiStore { await screenStore.patch(patchScreen, screenId) } - /** - * - * @param {object} component - * @returns - */ async delete(component: Component) { if (!component) { return @@ -737,13 +693,6 @@ export class ComponentStore extends BudiStore { }) } - /** - * - * @param {object} targetComponent - * @param {string} mode - * @param {object} targetScreen - * @returns - */ async paste( targetComponent: Component, mode: string, @@ -1101,6 +1050,7 @@ export class ComponentStore extends BudiStore { async updateStyles(styles: Record, id: string) { const patchFn = (component: Component) => { + delete component._placeholder component._styles.normal = { ...component._styles.normal, ...styles, @@ -1231,7 +1181,7 @@ export class ComponentStore extends BudiStore { } // Create new parent instance - const newParentDefinition = this.createInstance(parentType, null, parent) + const newParentDefinition = this.createInstance(parentType) if (!newParentDefinition) { return } @@ -1267,10 +1217,6 @@ export class ComponentStore extends BudiStore { /** * Check if the components settings have been cached - * @param {string} componentType - * @example - * '@budibase/standard-components/container' - * @returns {boolean} */ isCached(componentType: string) { const settings = get(this.store).settingsCache @@ -1279,11 +1225,6 @@ export class ComponentStore extends BudiStore { /** * Cache component settings - * @param {string} componentType - * @param {object} definition - * @example - * '@budibase/standard-components/container' - * @returns {array} the settings */ cacheSettings(componentType: string, definition: ComponentDefinition | null) { let settings: ComponentSetting[] = [] @@ -1313,12 +1254,7 @@ export class ComponentStore extends BudiStore { /** * Retrieve an array of the component settings. * These settings are cached because they cannot change at run time. - * * Searches a component's definition for a setting matching a certain predicate. - * @param {string} componentType - * @example - * '@budibase/standard-components/container' - * @returns {Array} */ getComponentSettings(componentType: string) { if (!componentType) { diff --git a/packages/builder/src/stores/builder/navigation.ts b/packages/builder/src/stores/builder/navigation.ts index 1574efee2a..1ef019a11c 100644 --- a/packages/builder/src/stores/builder/navigation.ts +++ b/packages/builder/src/stores/builder/navigation.ts @@ -4,15 +4,13 @@ import { appStore } from "@/stores/builder" import { BudiStore } from "../BudiStore" import { AppNavigation, AppNavigationLink, UIObject } from "@budibase/types" -interface BuilderNavigationStore extends AppNavigation {} - export const INITIAL_NAVIGATION_STATE = { navigation: "Top", links: [], textAlign: "Left", } -export class NavigationStore extends BudiStore { +export class NavigationStore extends BudiStore { constructor() { super(INITIAL_NAVIGATION_STATE) } diff --git a/packages/builder/src/stores/builder/preview.ts b/packages/builder/src/stores/builder/preview.ts index 87b2b9355e..9aeaf99346 100644 --- a/packages/builder/src/stores/builder/preview.ts +++ b/packages/builder/src/stores/builder/preview.ts @@ -1,5 +1,7 @@ import { get } from "svelte/store" import { BudiStore } from "../BudiStore" +import { componentStore } from "./components" +import { selectedScreen } from "./screens" type PreviewDevice = "desktop" | "tablet" | "mobile" type PreviewEventHandler = (name: string, payload?: any) => void @@ -54,10 +56,21 @@ export class PreviewStore extends BudiStore { })) } - startDrag(component: any) { + async startDrag(componentType: string) { + let componentId + const gridScreen = get(selectedScreen)?.props?.layout === "grid" + if (gridScreen) { + const component = await componentStore.create(componentType, { + _placeholder: true, + _styles: { normal: { opacity: 0 } }, + }) + componentId = component?._id + } this.sendEvent("dragging-new-component", { dragging: true, - component, + componentType, + componentId, + gridScreen, }) } diff --git a/packages/client/package.json b/packages/client/package.json index 2ae049f6d0..72be403698 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -5,7 +5,7 @@ "module": "dist/budibase-client.js", "main": "dist/budibase-client.js", "type": "module", - "svelte": "src/index.js", + "svelte": "src/index.ts", "exports": { ".": { "import": "./dist/budibase-client.js", diff --git a/packages/client/src/components/ClientApp.svelte b/packages/client/src/components/ClientApp.svelte index 2840d82f47..7144105fd8 100644 --- a/packages/client/src/components/ClientApp.svelte +++ b/packages/client/src/components/ClientApp.svelte @@ -44,6 +44,7 @@ import MaintenanceScreen from "components/MaintenanceScreen.svelte" import SnippetsProvider from "./context/SnippetsProvider.svelte" import EmbedProvider from "./context/EmbedProvider.svelte" + import GridNewComponentDNDHandler from "components/preview/GridNewComponentDNDHandler.svelte" // Provide contexts setContext("sdk", SDK) @@ -266,6 +267,7 @@ {#if $builderStore.inBuilder} + {/if} diff --git a/packages/client/src/components/preview/GridNewComponentDNDHandler.svelte b/packages/client/src/components/preview/GridNewComponentDNDHandler.svelte new file mode 100644 index 0000000000..8a3da6f419 --- /dev/null +++ b/packages/client/src/components/preview/GridNewComponentDNDHandler.svelte @@ -0,0 +1,166 @@ + diff --git a/packages/client/src/index.d.ts b/packages/client/src/index.d.ts deleted file mode 100644 index 7e13670b33..0000000000 --- a/packages/client/src/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -interface Window { - "##BUDIBASE_APP_ID##": string - "##BUDIBASE_IN_BUILDER##": string - MIGRATING_APP: boolean -} diff --git a/packages/client/src/index.js b/packages/client/src/index.js deleted file mode 100644 index 8a48aa08e5..0000000000 --- a/packages/client/src/index.js +++ /dev/null @@ -1,138 +0,0 @@ -import ClientApp from "./components/ClientApp.svelte" -import UpdatingApp from "./components/UpdatingApp.svelte" -import { - builderStore, - appStore, - blockStore, - componentStore, - environmentStore, - dndStore, - eventStore, - hoverStore, - stateStore, -} from "./stores" -import loadSpectrumIcons from "@budibase/bbui/spectrum-icons-vite.js" -import { get } from "svelte/store" -import { initWebsocket } from "./websocket.js" - -// Provide svelte and svelte/internal as globals for custom components -import * as svelte from "svelte" -import * as internal from "svelte/internal" - -window.svelte_internal = internal -window.svelte = svelte - -// Initialise spectrum icons -loadSpectrumIcons() - -let app - -const loadBudibase = async () => { - // Update builder store with any builder flags - builderStore.set({ - ...get(builderStore), - inBuilder: !!window["##BUDIBASE_IN_BUILDER##"], - layout: window["##BUDIBASE_PREVIEW_LAYOUT##"], - screen: window["##BUDIBASE_PREVIEW_SCREEN##"], - selectedComponentId: window["##BUDIBASE_SELECTED_COMPONENT_ID##"], - previewId: window["##BUDIBASE_PREVIEW_ID##"], - theme: window["##BUDIBASE_PREVIEW_THEME##"], - customTheme: window["##BUDIBASE_PREVIEW_CUSTOM_THEME##"], - previewDevice: window["##BUDIBASE_PREVIEW_DEVICE##"], - navigation: window["##BUDIBASE_PREVIEW_NAVIGATION##"], - hiddenComponentIds: window["##BUDIBASE_HIDDEN_COMPONENT_IDS##"], - usedPlugins: window["##BUDIBASE_USED_PLUGINS##"], - location: window["##BUDIBASE_LOCATION##"], - snippets: window["##BUDIBASE_SNIPPETS##"], - componentErrors: window["##BUDIBASE_COMPONENT_ERRORS##"], - }) - - // Set app ID - this window flag is set by both the preview and the real - // server rendered app HTML - appStore.actions.setAppId(window["##BUDIBASE_APP_ID##"]) - - // Set the flag used to determine if the app is being loaded via an iframe - appStore.actions.setAppEmbedded( - window["##BUDIBASE_APP_EMBEDDED##"] === "true" - ) - - if (window.MIGRATING_APP) { - new UpdatingApp({ - target: window.document.body, - }) - return - } - - // Fetch environment info - if (!get(environmentStore)?.loaded) { - await environmentStore.actions.fetchEnvironment() - } - - // Register handler for runtime events from the builder - window.handleBuilderRuntimeEvent = (type, data) => { - if (!window["##BUDIBASE_IN_BUILDER##"]) { - return - } - if (type === "event-completed") { - eventStore.actions.resolveEvent(data) - } else if (type === "eject-block") { - const block = blockStore.actions.getBlock(data) - block?.eject() - } else if (type === "dragging-new-component") { - const { dragging, component } = data - if (dragging) { - const definition = - componentStore.actions.getComponentDefinition(component) - dndStore.actions.startDraggingNewComponent({ component, definition }) - } else { - dndStore.actions.reset() - } - } else if (type === "request-context") { - const { selectedComponentInstance, screenslotInstance } = - get(componentStore) - const instance = selectedComponentInstance || screenslotInstance - const context = instance?.getDataContext() - let stringifiedContext = null - try { - stringifiedContext = JSON.stringify(context) - } catch (error) { - // Ignore - invalid context - } - eventStore.actions.dispatchEvent("provide-context", { - context: stringifiedContext, - }) - } else if (type === "hover-component") { - hoverStore.actions.hoverComponent(data, false) - } else if (type === "builder-meta") { - builderStore.actions.setMetadata(data) - } else if (type === "builder-state") { - const [[key, value]] = Object.entries(data) - stateStore.actions.setValue(key, value) - } - } - - // Register any custom components - if (window["##BUDIBASE_CUSTOM_COMPONENTS##"]) { - window["##BUDIBASE_CUSTOM_COMPONENTS##"].forEach(component => { - componentStore.actions.registerCustomComponent(component) - }) - } - - // Make a callback available for custom component bundles to register - // themselves at runtime - window.registerCustomComponent = - componentStore.actions.registerCustomComponent - - // Initialise websocket - initWebsocket() - - // Create app if one hasn't been created yet - if (!app) { - app = new ClientApp({ - target: window.document.body, - }) - } -} - -// Attach to window so the HTML template can call this when it loads -window.loadBudibase = loadBudibase diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index 08330a60fa..e7d454e2f1 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -1,6 +1,83 @@ +import ClientApp from "./components/ClientApp.svelte" +import UpdatingApp from "./components/UpdatingApp.svelte" +import { + builderStore, + appStore, + blockStore, + componentStore, + environmentStore, + dndStore, + eventStore, + hoverStore, + stateStore, +} from "@/stores" +import { get } from "svelte/store" +import { initWebsocket } from "@/websocket" import { APIClient } from "@budibase/frontend-core" -import type { ActionTypes } from "./constants" +import type { ActionTypes } from "@/constants" import { Readable } from "svelte/store" +import { + Screen, + Layout, + Theme, + AppCustomTheme, + PreviewDevice, + AppNavigation, + Plugin, + Snippet, + UIComponentError, + CustomComponent, +} from "@budibase/types" + +// Provide svelte and svelte/internal as globals for custom components +import * as svelte from "svelte" +// @ts-ignore +import * as internal from "svelte/internal" +window.svelte_internal = internal +window.svelte = svelte + +// Initialise spectrum icons +// eslint-disable-next-line local-rules/no-budibase-imports +import loadSpectrumIcons from "@budibase/bbui/spectrum-icons-vite.js" +loadSpectrumIcons() + +// Extend global window scope +declare global { + interface Window { + // Data from builder + "##BUDIBASE_APP_ID##"?: string + "##BUDIBASE_IN_BUILDER##"?: true + "##BUDIBASE_PREVIEW_LAYOUT##"?: Layout + "##BUDIBASE_PREVIEW_SCREEN##"?: Screen + "##BUDIBASE_SELECTED_COMPONENT_ID##"?: string + "##BUDIBASE_PREVIEW_ID##"?: number + "##BUDIBASE_PREVIEW_THEME##"?: Theme + "##BUDIBASE_PREVIEW_CUSTOM_THEME##"?: AppCustomTheme + "##BUDIBASE_PREVIEW_DEVICE##"?: PreviewDevice + "##BUDIBASE_APP_EMBEDDED##"?: string // This is a bool wrapped in a string + "##BUDIBASE_PREVIEW_NAVIGATION##"?: AppNavigation + "##BUDIBASE_HIDDEN_COMPONENT_IDS##"?: string[] + "##BUDIBASE_USED_PLUGINS##"?: Plugin[] + "##BUDIBASE_LOCATION##"?: { + protocol: string + hostname: string + port: string + } + "##BUDIBASE_SNIPPETS##"?: Snippet[] + "##BUDIBASE_COMPONENT_ERRORS##"?: Record[] + "##BUDIBASE_CUSTOM_COMPONENTS##"?: CustomComponent[] + + // Other flags + MIGRATING_APP: boolean + + // Client additions + handleBuilderRuntimeEvent: (type: string, data: any) => void + registerCustomComponent: typeof componentStore.actions.registerCustomComponent + loadBudibase: typeof loadBudibase + svelte: typeof svelte + svelte_internal: typeof internal + } +} export interface SDK { API: APIClient @@ -29,3 +106,119 @@ export type Component = Readable<{ }> export type Context = Readable<{}> + +let app: ClientApp + +const loadBudibase = async () => { + // Update builder store with any builder flags + builderStore.set({ + ...get(builderStore), + inBuilder: !!window["##BUDIBASE_IN_BUILDER##"], + layout: window["##BUDIBASE_PREVIEW_LAYOUT##"], + screen: window["##BUDIBASE_PREVIEW_SCREEN##"], + selectedComponentId: window["##BUDIBASE_SELECTED_COMPONENT_ID##"], + previewId: window["##BUDIBASE_PREVIEW_ID##"], + theme: window["##BUDIBASE_PREVIEW_THEME##"], + customTheme: window["##BUDIBASE_PREVIEW_CUSTOM_THEME##"], + previewDevice: window["##BUDIBASE_PREVIEW_DEVICE##"], + navigation: window["##BUDIBASE_PREVIEW_NAVIGATION##"], + hiddenComponentIds: window["##BUDIBASE_HIDDEN_COMPONENT_IDS##"], + usedPlugins: window["##BUDIBASE_USED_PLUGINS##"], + location: window["##BUDIBASE_LOCATION##"], + snippets: window["##BUDIBASE_SNIPPETS##"], + componentErrors: window["##BUDIBASE_COMPONENT_ERRORS##"], + }) + + // Set app ID - this window flag is set by both the preview and the real + // server rendered app HTML + appStore.actions.setAppId(window["##BUDIBASE_APP_ID##"]) + + // Set the flag used to determine if the app is being loaded via an iframe + appStore.actions.setAppEmbedded( + window["##BUDIBASE_APP_EMBEDDED##"] === "true" + ) + + if (window.MIGRATING_APP) { + new UpdatingApp({ + target: window.document.body, + }) + return + } + + // Fetch environment info + if (!get(environmentStore)?.loaded) { + await environmentStore.actions.fetchEnvironment() + } + + // Register handler for runtime events from the builder + window.handleBuilderRuntimeEvent = (type, data) => { + if (!window["##BUDIBASE_IN_BUILDER##"]) { + return + } + if (type === "event-completed") { + eventStore.actions.resolveEvent(data) + } else if (type === "eject-block") { + const block = blockStore.actions.getBlock(data) + block?.eject() + } else if (type === "dragging-new-component") { + const { dragging, component, componentId } = data + if (dragging) { + const definition = + componentStore.actions.getComponentDefinition(component) + dndStore.actions.startDraggingNewComponent({ + component, + definition, + componentId, + }) + } else { + dndStore.actions.reset() + } + } else if (type === "request-context") { + const { selectedComponentInstance, screenslotInstance } = + get(componentStore) + const instance = selectedComponentInstance || screenslotInstance + const context = instance?.getDataContext() + let stringifiedContext = null + try { + stringifiedContext = JSON.stringify(context) + } catch (error) { + // Ignore - invalid context + } + eventStore.actions.dispatchEvent("provide-context", { + context: stringifiedContext, + }) + } else if (type === "hover-component") { + hoverStore.actions.hoverComponent(data, false) + } else if (type === "builder-meta") { + builderStore.actions.setMetadata(data) + } else if (type === "builder-state") { + const [[key, value]] = Object.entries(data) + stateStore.actions.setValue(key, value) + } + } + + // Register any custom components + if (window["##BUDIBASE_CUSTOM_COMPONENTS##"]) { + window["##BUDIBASE_CUSTOM_COMPONENTS##"].forEach(component => { + componentStore.actions.registerCustomComponent(component) + }) + } + + // Make a callback available for custom component bundles to register + // themselves at runtime + window.registerCustomComponent = + componentStore.actions.registerCustomComponent + + // Initialise websocket + initWebsocket() + + // Create app if one hasn't been created yet + if (!app) { + app = new ClientApp({ + target: window.document.body, + }) + } +} + +// Attach to window so the HTML template can call this when it loads +window.loadBudibase = loadBudibase diff --git a/packages/client/src/stores/dnd.js b/packages/client/src/stores/dnd.js index 43d4eeeed8..5cfb12c7d2 100644 --- a/packages/client/src/stores/dnd.js +++ b/packages/client/src/stores/dnd.js @@ -21,7 +21,11 @@ const createDndStore = () => { }) } - const startDraggingNewComponent = ({ component, definition }) => { + const startDraggingNewComponent = ({ + component, + definition, + componentId, + }) => { if (!component) { return } @@ -38,6 +42,7 @@ const createDndStore = () => { bounds: { height, width }, index: null, newComponentType: component, + newComponentId: componentId, }, }) } @@ -82,6 +87,10 @@ export const dndParent = derivedMemo(dndStore, x => x.drop?.parent) export const dndIndex = derivedMemo(dndStore, x => x.drop?.index) export const dndBounds = derivedMemo(dndStore, x => x.source?.bounds) export const dndIsDragging = derivedMemo(dndStore, x => !!x.source) +export const dndNewComponentId = derivedMemo( + dndStore, + x => x.source?.newComponentId +) export const dndIsNewComponent = derivedMemo( dndStore, x => x.source?.newComponentType != null diff --git a/packages/client/src/stores/index.js b/packages/client/src/stores/index.js index f2b80ed732..268b0cc4bf 100644 --- a/packages/client/src/stores/index.js +++ b/packages/client/src/stores/index.js @@ -2,7 +2,7 @@ export { authStore } from "./auth" export { appStore } from "./app" export { notificationStore } from "./notification" export { routeStore } from "./routes" -export { screenStore } from "./screens" +export { screenStore, isGridScreen } from "./screens" export { builderStore } from "./builder" export { dataSourceStore } from "./dataSource" export { confirmationStore } from "./confirmation" diff --git a/packages/client/src/stores/screens.js b/packages/client/src/stores/screens.js index a7277ca2b5..f85675e389 100644 --- a/packages/client/src/stores/screens.js +++ b/packages/client/src/stores/screens.js @@ -198,3 +198,7 @@ const createScreenStore = () => { } export const screenStore = createScreenStore() + +export const isGridScreen = derived(screenStore, $screenStore => { + return $screenStore.activeScreen?.props?.layout === "grid" +}) diff --git a/packages/client/vite.config.mjs b/packages/client/vite.config.mjs index 7c163181ae..9ef87d8ec8 100644 --- a/packages/client/vite.config.mjs +++ b/packages/client/vite.config.mjs @@ -20,7 +20,7 @@ export default defineConfig(({ mode }) => { }, build: { lib: { - entry: "src/index.js", + entry: "src/index.ts", formats: ["iife"], outDir: "dist", name: "budibase_client", @@ -67,10 +67,6 @@ export default defineConfig(({ mode }) => { find: "constants", replacement: path.resolve("./src/constants"), }, - { - find: "@/constants", - replacement: path.resolve("./src/constants"), - }, { find: "sdk", replacement: path.resolve("./src/sdk"), @@ -87,6 +83,10 @@ export default defineConfig(({ mode }) => { find: "@budibase/bbui", replacement: path.resolve("../bbui/src"), }, + { + find: "@", + replacement: path.resolve(__dirname, "src"), + }, ], }, } diff --git a/packages/types/src/ui/components/index.ts b/packages/types/src/ui/components/index.ts index 283ceb47da..85c7b1af84 100644 --- a/packages/types/src/ui/components/index.ts +++ b/packages/types/src/ui/components/index.ts @@ -2,6 +2,16 @@ export * from "./sidepanel" export * from "./codeEditor" export * from "./errors" +export interface CustomComponent { + Component: any + schema: { + type: "component" + metadata: Record + schema: ComponentDefinition + } + version: string +} + export interface ComponentDefinition { component: string name: string From 5b5a0d2ba80218758985d695655a6513408fd6dd Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 5 Feb 2025 14:55:05 +0000 Subject: [PATCH 02/72] Add working DND placeholder support for grid screens --- .../new/_components/NewComponentPanel.svelte | 3 +- .../[screenId]/_components/AppPreview.svelte | 4 +- .../builder/src/stores/builder/preview.ts | 17 +--- .../src/components/preview/DNDHandler.svelte | 23 ++++-- .../preview/DNDPlaceholderOverlay.svelte | 33 ++++++-- .../components/preview/GridDNDHandler.svelte | 78 +++++++++++++++++-- packages/client/src/index.ts | 5 +- packages/client/src/stores/builder.js | 3 +- packages/client/src/stores/dnd.js | 68 +++++++++++++--- packages/client/src/stores/index.js | 1 + packages/client/src/stores/screens.js | 3 +- 11 files changed, 185 insertions(+), 53 deletions(-) diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/NewComponentPanel.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/NewComponentPanel.svelte index 784642787b..c76b03be36 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/NewComponentPanel.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/NewComponentPanel.svelte @@ -223,11 +223,13 @@ }) const onDragStart = (e, component) => { + console.log("DRAG START") e.dataTransfer.setDragImage(ghost, 0, 0) previewStore.startDrag(component) } const onDragEnd = () => { + console.log("DRAG END") previewStore.stopDrag() } @@ -314,7 +316,6 @@ } .component:hover { background: var(--spectrum-global-color-gray-300); - cursor: grab; } .component :global(.spectrum-Body) { line-height: 1.2 !important; diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPreview.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPreview.svelte index 3951c0e902..39e0835fa8 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPreview.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPreview.svelte @@ -199,8 +199,8 @@ } else if (type === "reload-plugin") { await componentStore.refreshDefinitions() } else if (type === "drop-new-component") { - const { component, parent, index } = data - await componentStore.create(component, null, parent, index) + const { component, parent, index, props } = data + await componentStore.create(component, props, parent, index) } else if (type === "add-parent-component") { const { componentId, parentType } = data await componentStore.addParent(componentId, parentType) diff --git a/packages/builder/src/stores/builder/preview.ts b/packages/builder/src/stores/builder/preview.ts index 9aeaf99346..a382eeefb0 100644 --- a/packages/builder/src/stores/builder/preview.ts +++ b/packages/builder/src/stores/builder/preview.ts @@ -1,7 +1,5 @@ import { get } from "svelte/store" import { BudiStore } from "../BudiStore" -import { componentStore } from "./components" -import { selectedScreen } from "./screens" type PreviewDevice = "desktop" | "tablet" | "mobile" type PreviewEventHandler = (name: string, payload?: any) => void @@ -56,21 +54,10 @@ export class PreviewStore extends BudiStore { })) } - async startDrag(componentType: string) { - let componentId - const gridScreen = get(selectedScreen)?.props?.layout === "grid" - if (gridScreen) { - const component = await componentStore.create(componentType, { - _placeholder: true, - _styles: { normal: { opacity: 0 } }, - }) - componentId = component?._id - } + async startDrag(component: string) { this.sendEvent("dragging-new-component", { dragging: true, - componentType, - componentId, - gridScreen, + component, }) } diff --git a/packages/client/src/components/preview/DNDHandler.svelte b/packages/client/src/components/preview/DNDHandler.svelte index bdd538748b..11bed5a1b7 100644 --- a/packages/client/src/components/preview/DNDHandler.svelte +++ b/packages/client/src/components/preview/DNDHandler.svelte @@ -8,12 +8,14 @@ dndStore, dndParent, dndIsDragging, + isGridScreen, + dndInitialised, } from "stores" import DNDPlaceholderOverlay from "./DNDPlaceholderOverlay.svelte" import { Utils } from "@budibase/frontend-core" - import { findComponentById } from "utils/components.js" - import { DNDPlaceholderID } from "constants" - import { isGridEvent } from "utils/grid" + import { findComponentById } from "@/utils/components.js" + import { isGridEvent } from "@/utils/grid" + import { DNDPlaceholderID } from "@/constants" const ThrottleRate = 130 @@ -219,9 +221,9 @@ processEvent(e.clientX, e.clientY) } - // Callback when on top of a component. + // Callback when on top of a component const onDragOver = e => { - if (!source || !target) { + if (!source || !target || $isGridScreen) { return } handleEvent(e) @@ -233,6 +235,14 @@ return } + // Mark as initialised if this is our first valid drag enter event + if (!$dndInitialised) { + dndStore.actions.markInitialised() + } + if ($isGridScreen) { + return + } + // Find the next valid component to consider dropping over, ignoring nested // block components const component = e.target?.closest?.( @@ -262,7 +272,8 @@ builderStore.actions.dropNewComponent( source.newComponentType, drop.parent, - drop.index + drop.index, + $dndStore.meta.newComponentProps ) dropping = false stopDragging() diff --git a/packages/client/src/components/preview/DNDPlaceholderOverlay.svelte b/packages/client/src/components/preview/DNDPlaceholderOverlay.svelte index 61cecc885b..50b76c6208 100644 --- a/packages/client/src/components/preview/DNDPlaceholderOverlay.svelte +++ b/packages/client/src/components/preview/DNDPlaceholderOverlay.svelte @@ -1,38 +1,56 @@ -{#if left != null && top != null && width && height} +{#if left != null && top != null && width && height && $dndInitialised}
diff --git a/packages/client/src/components/preview/GridDNDHandler.svelte b/packages/client/src/components/preview/GridDNDHandler.svelte index f173dfd960..3d50bfded9 100644 --- a/packages/client/src/components/preview/GridDNDHandler.svelte +++ b/packages/client/src/components/preview/GridDNDHandler.svelte @@ -1,15 +1,22 @@ diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index e7d454e2f1..7d6b100dc6 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -36,6 +36,8 @@ import * as internal from "svelte/internal" window.svelte_internal = internal window.svelte = svelte +console.log("NEW CLIENT") + // Initialise spectrum icons // eslint-disable-next-line local-rules/no-budibase-imports import loadSpectrumIcons from "@budibase/bbui/spectrum-icons-vite.js" @@ -161,14 +163,13 @@ const loadBudibase = async () => { const block = blockStore.actions.getBlock(data) block?.eject() } else if (type === "dragging-new-component") { - const { dragging, component, componentId } = data + const { dragging, component } = data if (dragging) { const definition = componentStore.actions.getComponentDefinition(component) dndStore.actions.startDraggingNewComponent({ component, definition, - componentId, }) } else { dndStore.actions.reset() diff --git a/packages/client/src/stores/builder.js b/packages/client/src/stores/builder.js index 1ae7d3a670..7effd718a0 100644 --- a/packages/client/src/stores/builder.js +++ b/packages/client/src/stores/builder.js @@ -77,11 +77,12 @@ const createBuilderStore = () => { mode, }) }, - dropNewComponent: (component, parent, index) => { + dropNewComponent: (component, parent, index, props) => { eventStore.actions.dispatchEvent("drop-new-component", { component, parent, index, + props, }) }, setEditMode: enabled => { diff --git a/packages/client/src/stores/dnd.js b/packages/client/src/stores/dnd.js index 5cfb12c7d2..74987ea7d9 100644 --- a/packages/client/src/stores/dnd.js +++ b/packages/client/src/stores/dnd.js @@ -1,5 +1,7 @@ -import { writable } from "svelte/store" +import { writable, get } from "svelte/store" import { derivedMemo } from "@budibase/frontend-core" +import { screenStore } from "@/stores" +import { ScreenslotID } from "@/constants" const createDndStore = () => { const initialState = { @@ -11,6 +13,12 @@ const createDndStore = () => { // Info about where the component would be dropped drop: null, + + // Metadata about the event + meta: { + initialised: false, + newComponentProps: null, + }, } const store = writable(initialState) @@ -21,35 +29,53 @@ const createDndStore = () => { }) } - const startDraggingNewComponent = ({ - component, - definition, - componentId, - }) => { + const startDraggingNewComponent = ({ component, definition }) => { + console.log("start", component, definition) if (!component) { return } + let target, drop + const screen = get(screenStore)?.activeScreen + const isGridScreen = screen?.props?.layout === "grid" + if (isGridScreen) { + const id = screen?.props?._id + drop = { + parent: id, + index: screen?.props?._children?.length, + } + target = { + id, + parent: ScreenslotID, + node: null, + empty: false, + acceptsChildren: true, + } + } + // Get size of new component so we can show a properly sized placeholder const width = definition?.size?.width || 128 const height = definition?.size?.height || 64 store.set({ ...initialState, + isGridScreen, source: { id: null, parent: null, bounds: { height, width }, index: null, newComponentType: component, - newComponentId: componentId, }, + target, + drop, }) } const updateTarget = ({ id, parent, node, empty, acceptsChildren }) => { store.update(state => { state.target = { id, parent, node, empty, acceptsChildren } + console.log("TARGET", state.target) return state }) } @@ -57,6 +83,7 @@ const createDndStore = () => { const updateDrop = ({ parent, index }) => { store.update(state => { state.drop = { parent, index } + console.log("DROP", state.drop) return state }) } @@ -65,6 +92,26 @@ const createDndStore = () => { store.set(initialState) } + const markInitialised = () => { + store.update(state => ({ + ...state, + meta: { + ...state.meta, + initialised: true, + }, + })) + } + + const updateNewComponentProps = newComponentProps => { + store.update(state => ({ + ...state, + meta: { + ...state.meta, + newComponentProps, + }, + })) + } + return { subscribe: store.subscribe, actions: { @@ -73,6 +120,8 @@ const createDndStore = () => { updateTarget, updateDrop, reset, + markInitialised, + updateNewComponentProps, }, } } @@ -87,10 +136,7 @@ export const dndParent = derivedMemo(dndStore, x => x.drop?.parent) export const dndIndex = derivedMemo(dndStore, x => x.drop?.index) export const dndBounds = derivedMemo(dndStore, x => x.source?.bounds) export const dndIsDragging = derivedMemo(dndStore, x => !!x.source) -export const dndNewComponentId = derivedMemo( - dndStore, - x => x.source?.newComponentId -) +export const dndInitialised = derivedMemo(dndStore, x => x.meta.initialised) export const dndIsNewComponent = derivedMemo( dndStore, x => x.source?.newComponentType != null diff --git a/packages/client/src/stores/index.js b/packages/client/src/stores/index.js index 268b0cc4bf..b1f0f8955a 100644 --- a/packages/client/src/stores/index.js +++ b/packages/client/src/stores/index.js @@ -25,6 +25,7 @@ export { dndBounds, dndIsNewComponent, dndIsDragging, + dndInitialised, } from "./dnd" export { sidePanelStore } from "./sidePanel" export { modalStore } from "./modal" diff --git a/packages/client/src/stores/screens.js b/packages/client/src/stores/screens.js index f85675e389..6193dcca94 100644 --- a/packages/client/src/stores/screens.js +++ b/packages/client/src/stores/screens.js @@ -7,7 +7,7 @@ import { dndIndex, dndParent, dndIsNewComponent, dndBounds } from "./dnd.js" import { RoleUtils } from "@budibase/frontend-core" import { findComponentById, findComponentParent } from "../utils/components.js" import { Helpers } from "@budibase/bbui" -import { DNDPlaceholderID, ScreenslotID, ScreenslotType } from "constants" +import { DNDPlaceholderID, ScreenslotID, ScreenslotType } from "@/constants" const createScreenStore = () => { const store = derived( @@ -99,7 +99,6 @@ const createScreenStore = () => { normal: { width: `${$dndBounds?.width || 400}px`, height: `${$dndBounds?.height || 200}px`, - opacity: 0, "--default-width": $dndBounds?.width || 400, "--default-height": $dndBounds?.height || 200, }, From b9c2b07f3e7d696a7956e0b1c9e492bd00be8cfe Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 5 Feb 2025 16:16:22 +0000 Subject: [PATCH 03/72] Finally actually fix devilish issue of flashing placeholder overlay --- .../src/components/preview/DNDHandler.svelte | 27 ++++++-------- .../preview/DNDPlaceholderOverlay.svelte | 36 ++++++++++++++++--- .../components/preview/GridDNDHandler.svelte | 21 ++++++----- .../preview/SelectionIndicator.svelte | 4 +-- packages/client/src/stores/dnd.js | 18 +++------- packages/client/src/stores/index.js | 2 +- packages/client/src/stores/screens.js | 1 + 7 files changed, 62 insertions(+), 47 deletions(-) diff --git a/packages/client/src/components/preview/DNDHandler.svelte b/packages/client/src/components/preview/DNDHandler.svelte index 11bed5a1b7..f62f21e94c 100644 --- a/packages/client/src/components/preview/DNDHandler.svelte +++ b/packages/client/src/components/preview/DNDHandler.svelte @@ -9,7 +9,6 @@ dndParent, dndIsDragging, isGridScreen, - dndInitialised, } from "stores" import DNDPlaceholderOverlay from "./DNDPlaceholderOverlay.svelte" import { Utils } from "@budibase/frontend-core" @@ -230,16 +229,8 @@ } // Callback when entering a potential drop target - const onDragEnter = e => { - if (!source) { - return - } - - // Mark as initialised if this is our first valid drag enter event - if (!$dndInitialised) { - dndStore.actions.markInitialised() - } - if ($isGridScreen) { + const onDragEnter = async e => { + if (!source || $isGridScreen) { return } @@ -345,12 +336,14 @@ }) - +{#if !$isGridScreen} + +{/if} {#if $dndIsDragging} diff --git a/packages/client/src/components/preview/DNDPlaceholderOverlay.svelte b/packages/client/src/components/preview/DNDPlaceholderOverlay.svelte index 50b76c6208..5593fa010d 100644 --- a/packages/client/src/components/preview/DNDPlaceholderOverlay.svelte +++ b/packages/client/src/components/preview/DNDPlaceholderOverlay.svelte @@ -1,14 +1,38 @@ -{#if left != null && top != null && width && height && $dndInitialised} +{#if left != null && top != null && width && height && !waitingForGrid}
{/if} @@ -65,4 +90,7 @@ border-radius: 4px; border: 2px solid var(--spectrum-global-color-static-green-500); } + .overlay.animate { + transition: all 130ms ease-out; + } diff --git a/packages/client/src/components/preview/GridDNDHandler.svelte b/packages/client/src/components/preview/GridDNDHandler.svelte index 3d50bfded9..4f810d226f 100644 --- a/packages/client/src/components/preview/GridDNDHandler.svelte +++ b/packages/client/src/components/preview/GridDNDHandler.svelte @@ -42,23 +42,26 @@ // Set ephemeral styles $: instance = componentStore.actions.getComponentInstance(id) - $: $instance?.setEphemeralStyles($styles) + $: applyStyles($instance, $styles) - // Keep DND store up to date with grid styles if dragging a new component - // on to a grid screen - $: { - if ($dndIsNewComponent) { + // Reset when not dragging new components + $: !$dndIsDragging && stopDragging() + + const applyStyles = async (instance, styles) => { + instance?.setEphemeralStyles(styles) + + // If dragging a new component on to a grid screen, tick to allow the + // real component to render in the new position before updating the DND + // store, preventing the green DND overlay from being out of position + if ($dndIsNewComponent && styles) { dndStore.actions.updateNewComponentProps({ _styles: { - normal: $styles, + normal: styles, }, }) } } - // Reset when not dragging new components - $: !$dndIsDragging && stopDragging() - // Sugar for a combination of both min and max const minMax = (value, min, max) => Math.min(max, Math.max(min, value)) diff --git a/packages/client/src/components/preview/SelectionIndicator.svelte b/packages/client/src/components/preview/SelectionIndicator.svelte index a271389cbd..5c34fe6f42 100644 --- a/packages/client/src/components/preview/SelectionIndicator.svelte +++ b/packages/client/src/components/preview/SelectionIndicator.svelte @@ -1,5 +1,5 @@ { // Metadata about the event meta: { - initialised: false, newComponentProps: null, }, } @@ -30,7 +29,6 @@ const createDndStore = () => { } const startDraggingNewComponent = ({ component, definition }) => { - console.log("start", component, definition) if (!component) { return } @@ -92,16 +90,6 @@ const createDndStore = () => { store.set(initialState) } - const markInitialised = () => { - store.update(state => ({ - ...state, - meta: { - ...state.meta, - initialised: true, - }, - })) - } - const updateNewComponentProps = newComponentProps => { store.update(state => ({ ...state, @@ -120,7 +108,6 @@ const createDndStore = () => { updateTarget, updateDrop, reset, - markInitialised, updateNewComponentProps, }, } @@ -136,8 +123,11 @@ export const dndParent = derivedMemo(dndStore, x => x.drop?.parent) export const dndIndex = derivedMemo(dndStore, x => x.drop?.index) export const dndBounds = derivedMemo(dndStore, x => x.source?.bounds) export const dndIsDragging = derivedMemo(dndStore, x => !!x.source) -export const dndInitialised = derivedMemo(dndStore, x => x.meta.initialised) export const dndIsNewComponent = derivedMemo( dndStore, x => x.source?.newComponentType != null ) +export const dndNewComponentProps = derivedMemo( + dndStore, + x => x.meta?.newComponentProps +) diff --git a/packages/client/src/stores/index.js b/packages/client/src/stores/index.js index b1f0f8955a..7d8257f6d0 100644 --- a/packages/client/src/stores/index.js +++ b/packages/client/src/stores/index.js @@ -25,7 +25,7 @@ export { dndBounds, dndIsNewComponent, dndIsDragging, - dndInitialised, + dndNewComponentProps, } from "./dnd" export { sidePanelStore } from "./sidePanel" export { modalStore } from "./modal" diff --git a/packages/client/src/stores/screens.js b/packages/client/src/stores/screens.js index 6193dcca94..99b943fbd2 100644 --- a/packages/client/src/stores/screens.js +++ b/packages/client/src/stores/screens.js @@ -99,6 +99,7 @@ const createScreenStore = () => { normal: { width: `${$dndBounds?.width || 400}px`, height: `${$dndBounds?.height || 200}px`, + opacity: 0, "--default-width": $dndBounds?.width || 400, "--default-height": $dndBounds?.height || 200, }, From e71e8ff6b68fcc61762ebdcaa053bd447ba53de0 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 5 Feb 2025 16:20:31 +0000 Subject: [PATCH 04/72] Tidy up --- .../components/preview/GridDNDHandler.svelte | 2 -- packages/client/src/index.ts | 2 -- packages/client/src/stores/dnd.js | 21 +++++++------------ packages/client/src/stores/index.js | 1 - 4 files changed, 7 insertions(+), 19 deletions(-) diff --git a/packages/client/src/components/preview/GridDNDHandler.svelte b/packages/client/src/components/preview/GridDNDHandler.svelte index 4f810d226f..6ada5b4b4b 100644 --- a/packages/client/src/components/preview/GridDNDHandler.svelte +++ b/packages/client/src/components/preview/GridDNDHandler.svelte @@ -124,7 +124,6 @@ } const startDraggingPlaceholder = () => { - console.log("START PLACEHOLDER") const mode = GridDragModes.Move const id = DNDPlaceholderID @@ -160,7 +159,6 @@ } // Add event handler to clear all drag state when dragging ends - console.log("add up listener") document.addEventListener("mouseup", stopDragging) } diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index 7d6b100dc6..20f582106b 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -36,8 +36,6 @@ import * as internal from "svelte/internal" window.svelte_internal = internal window.svelte = svelte -console.log("NEW CLIENT") - // Initialise spectrum icons // eslint-disable-next-line local-rules/no-budibase-imports import loadSpectrumIcons from "@budibase/bbui/spectrum-icons-vite.js" diff --git a/packages/client/src/stores/dnd.js b/packages/client/src/stores/dnd.js index 16f1441950..38c1e8aab8 100644 --- a/packages/client/src/stores/dnd.js +++ b/packages/client/src/stores/dnd.js @@ -1,6 +1,6 @@ import { writable, get } from "svelte/store" import { derivedMemo } from "@budibase/frontend-core" -import { screenStore } from "@/stores" +import { screenStore, isGridScreen } from "@/stores" import { ScreenslotID } from "@/constants" const createDndStore = () => { @@ -33,15 +33,11 @@ const createDndStore = () => { return } + // On grid screens, we already know exactly where to insert the component let target, drop const screen = get(screenStore)?.activeScreen - const isGridScreen = screen?.props?.layout === "grid" - if (isGridScreen) { + if (get(isGridScreen)) { const id = screen?.props?._id - drop = { - parent: id, - index: screen?.props?._children?.length, - } target = { id, parent: ScreenslotID, @@ -49,6 +45,10 @@ const createDndStore = () => { empty: false, acceptsChildren: true, } + drop = { + parent: id, + index: screen?.props?._children?.length, + } } // Get size of new component so we can show a properly sized placeholder @@ -57,7 +57,6 @@ const createDndStore = () => { store.set({ ...initialState, - isGridScreen, source: { id: null, parent: null, @@ -73,7 +72,6 @@ const createDndStore = () => { const updateTarget = ({ id, parent, node, empty, acceptsChildren }) => { store.update(state => { state.target = { id, parent, node, empty, acceptsChildren } - console.log("TARGET", state.target) return state }) } @@ -81,7 +79,6 @@ const createDndStore = () => { const updateDrop = ({ parent, index }) => { store.update(state => { state.drop = { parent, index } - console.log("DROP", state.drop) return state }) } @@ -127,7 +124,3 @@ export const dndIsNewComponent = derivedMemo( dndStore, x => x.source?.newComponentType != null ) -export const dndNewComponentProps = derivedMemo( - dndStore, - x => x.meta?.newComponentProps -) diff --git a/packages/client/src/stores/index.js b/packages/client/src/stores/index.js index 7d8257f6d0..268b0cc4bf 100644 --- a/packages/client/src/stores/index.js +++ b/packages/client/src/stores/index.js @@ -25,7 +25,6 @@ export { dndBounds, dndIsNewComponent, dndIsDragging, - dndNewComponentProps, } from "./dnd" export { sidePanelStore } from "./sidePanel" export { modalStore } from "./modal" From 0471e113e37103831910f3faeaf66363de132148 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 5 Feb 2025 16:31:38 +0000 Subject: [PATCH 05/72] More tidy up --- .../new/_components/NewComponentPanel.svelte | 2 -- .../components/preview/GridDNDHandler.svelte | 17 +++++------------ 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/NewComponentPanel.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/NewComponentPanel.svelte index c76b03be36..f80b681782 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/NewComponentPanel.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/NewComponentPanel.svelte @@ -223,13 +223,11 @@ }) const onDragStart = (e, component) => { - console.log("DRAG START") e.dataTransfer.setDragImage(ghost, 0, 0) previewStore.startDrag(component) } const onDragEnd = () => { - console.log("DRAG END") previewStore.stopDrag() } diff --git a/packages/client/src/components/preview/GridDNDHandler.svelte b/packages/client/src/components/preview/GridDNDHandler.svelte index 6ada5b4b4b..569a9879a6 100644 --- a/packages/client/src/components/preview/GridDNDHandler.svelte +++ b/packages/client/src/components/preview/GridDNDHandler.svelte @@ -123,12 +123,10 @@ processEvent(e.clientX, e.clientY) } + // Callback when dragging a new component over the preview iframe in a valid + // position for the first time const startDraggingPlaceholder = () => { - const mode = GridDragModes.Move - const id = DNDPlaceholderID - - // Find grid parent and read from DOM - const domComponent = document.getElementsByClassName(id)[0] + const domComponent = document.getElementsByClassName(DNDPlaceholderID)[0] const domGrid = domComponent?.closest(".grid") if (!domGrid) { return @@ -139,15 +137,14 @@ // Show as active domComponent.classList.add("dragging") domGrid.classList.add("highlight") - builderStore.actions.selectComponent(id) // Update state dragInfo = { domComponent, domGrid, - id, + id: DNDPlaceholderID, gridId: domGrid.parentNode.dataset.id, - mode, + mode: GridDragModes.Move, grid: { startX: bounds.left + bounds.width / 2, startY: bounds.top + bounds.height / 2, @@ -157,9 +154,6 @@ colEnd: parseInt(styles["grid-column-end"]), }, } - - // Add event handler to clear all drag state when dragging ends - document.addEventListener("mouseup", stopDragging) } // Callback when initially starting a drag on a draggable component @@ -266,6 +260,5 @@ onDestroy(() => { document.removeEventListener("dragstart", onDragStart, false) document.removeEventListener("dragover", onDragOver, false) - document.removeEventListener("mouseup", stopDragging, false) }) From 0ad0ded2e2febf94370fdda88643833cda4d60ba Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 6 Feb 2025 09:13:20 +0000 Subject: [PATCH 06/72] Use new technique for DND selection indicators --- .../client/src/components/ClientApp.svelte | 4 +- .../client/src/components/Component.svelte | 2 +- .../src/components/preview/DNDHandler.svelte | 21 +- .../preview/DNDPlaceholderOverlay.svelte | 186 ++++++++++-------- .../preview/DNDSelectionIndicators.svelte | 27 +++ .../components/preview/GridDNDHandler.svelte | 6 +- .../src/components/preview/Indicator.svelte | 10 +- .../components/preview/IndicatorSet.svelte | 31 +-- packages/client/src/stores/dnd.js | 22 ++- packages/client/src/stores/index.js | 9 +- packages/client/src/stores/screens.js | 18 +- 11 files changed, 188 insertions(+), 148 deletions(-) create mode 100644 packages/client/src/components/preview/DNDSelectionIndicators.svelte diff --git a/packages/client/src/components/ClientApp.svelte b/packages/client/src/components/ClientApp.svelte index 7144105fd8..144b5a8dec 100644 --- a/packages/client/src/components/ClientApp.svelte +++ b/packages/client/src/components/ClientApp.svelte @@ -44,7 +44,7 @@ import MaintenanceScreen from "components/MaintenanceScreen.svelte" import SnippetsProvider from "./context/SnippetsProvider.svelte" import EmbedProvider from "./context/EmbedProvider.svelte" - import GridNewComponentDNDHandler from "components/preview/GridNewComponentDNDHandler.svelte" + import DNDSelectionIndicators from "./preview/DNDSelectionIndicators.svelte" // Provide contexts setContext("sdk", SDK) @@ -267,7 +267,7 @@ {#if $builderStore.inBuilder} - + {/if}
diff --git a/packages/client/src/components/Component.svelte b/packages/client/src/components/Component.svelte index 3e22ffada1..67f8fdc63c 100644 --- a/packages/client/src/components/Component.svelte +++ b/packages/client/src/components/Component.svelte @@ -120,7 +120,7 @@ $: children = instance._children || [] $: id = instance._id $: name = isRoot ? "Screen" : instance._instanceName - $: icon = definition?.icon + $: icon = instance._icon || definition?.icon // Determine if the component is selected or is part of the critical path // leading to the selected component diff --git a/packages/client/src/components/preview/DNDHandler.svelte b/packages/client/src/components/preview/DNDHandler.svelte index f62f21e94c..82db06a623 100644 --- a/packages/client/src/components/preview/DNDHandler.svelte +++ b/packages/client/src/components/preview/DNDHandler.svelte @@ -7,10 +7,10 @@ screenStore, dndStore, dndParent, + dndSource, dndIsDragging, isGridScreen, } from "stores" - import DNDPlaceholderOverlay from "./DNDPlaceholderOverlay.svelte" import { Utils } from "@budibase/frontend-core" import { findComponentById } from "@/utils/components.js" import { isGridEvent } from "@/utils/grid" @@ -92,6 +92,8 @@ bounds: component.children[0].getBoundingClientRect(), parent: parentId, index, + name: component.dataset.name, + icon: component.dataset.icon, }) builderStore.actions.selectComponent(id) @@ -258,10 +260,10 @@ } // Check if we're adding a new component rather than moving one - if (source.newComponentType) { + if (source.isNew) { dropping = true builderStore.actions.dropNewComponent( - source.newComponentType, + source.type, drop.parent, drop.index, $dndStore.meta.newComponentProps @@ -335,16 +337,3 @@ document.removeEventListener("drop", onDrop, false) }) - -{#if !$isGridScreen} - -{/if} - -{#if $dndIsDragging} - -{/if} diff --git a/packages/client/src/components/preview/DNDPlaceholderOverlay.svelte b/packages/client/src/components/preview/DNDPlaceholderOverlay.svelte index 5593fa010d..f06454acce 100644 --- a/packages/client/src/components/preview/DNDPlaceholderOverlay.svelte +++ b/packages/client/src/components/preview/DNDPlaceholderOverlay.svelte @@ -1,96 +1,108 @@ - + + + + + + + -{#if left != null && top != null && width && height && !waitingForGrid} -
-{/if} + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/client/src/components/preview/DNDSelectionIndicators.svelte b/packages/client/src/components/preview/DNDSelectionIndicators.svelte new file mode 100644 index 0000000000..9949f07ace --- /dev/null +++ b/packages/client/src/components/preview/DNDSelectionIndicators.svelte @@ -0,0 +1,27 @@ + + +{#if !$isGridScreen} + +{/if} + +{#if $dndIsDragging} + +{/if} diff --git a/packages/client/src/components/preview/GridDNDHandler.svelte b/packages/client/src/components/preview/GridDNDHandler.svelte index 569a9879a6..58ba92ab07 100644 --- a/packages/client/src/components/preview/GridDNDHandler.svelte +++ b/packages/client/src/components/preview/GridDNDHandler.svelte @@ -4,8 +4,8 @@ builderStore, componentStore, dndIsDragging, - dndIsNewComponent, dndStore, + dndSource, isGridScreen, } from "stores" import { Utils, memo } from "@budibase/frontend-core" @@ -53,7 +53,7 @@ // If dragging a new component on to a grid screen, tick to allow the // real component to render in the new position before updating the DND // store, preventing the green DND overlay from being out of position - if ($dndIsNewComponent && styles) { + if ($dndSource?.isNew && styles) { dndStore.actions.updateNewComponentProps({ _styles: { normal: styles, @@ -222,7 +222,7 @@ const onDragOver = e => { if (!dragInfo) { // Check if we're dragging a new component - if ($dndIsDragging && $dndIsNewComponent && $isGridScreen) { + if ($dndIsDragging && $dndSource?.isNew && $isGridScreen) { startDraggingPlaceholder() } return diff --git a/packages/client/src/components/preview/Indicator.svelte b/packages/client/src/components/preview/Indicator.svelte index dce7945b29..94940285ff 100644 --- a/packages/client/src/components/preview/Indicator.svelte +++ b/packages/client/src/components/preview/Indicator.svelte @@ -14,6 +14,8 @@ export let line = false export let alignRight = false export let showResizeAnchors = false + export let background = null + export let animate = false const AnchorSides = [ "right", @@ -33,10 +35,12 @@ class="indicator" class:flipped class:line - style="top: {top}px; left: {left}px; width: {width}px; height: {height}px; --color: {color}; --zIndex: {zIndex};" + style="top: {top}px; left: {left}px; width: {width}px; height: {height}px; --color: {color}; --zIndex: {zIndex}; --bg: {background || + 'none'};" class:withText={!!text} class:vCompact={height < 40} class:hCompact={width < 40} + class:animate > {#if text || icon}
{ mutationObserver.observe(element, { attributes: true, - attributeFilter: ["style"], }) observingMutations = true } @@ -108,17 +111,19 @@ } // Check if we're inside a grid - if (allowResizeAnchors) { - nextState.insideGrid = elements[0]?.dataset.insideGrid === "true" - } + nextState.insideGrid = elements[0]?.dataset.insideGrid === "true" - // Get text to display - nextState.text = elements[0].dataset.name - if (nextState.prefix) { - nextState.text = `${nextState.prefix} ${nextState.text}` + // Get text and icon to display + if (!text) { + nextState.text = elements[0].dataset.name + if (nextState.prefix) { + nextState.text = `${nextState.prefix} ${nextState.text}` + } } - if (elements[0].dataset.icon) { - nextState.icon = elements[0].dataset.icon + if (!icon) { + if (elements[0].dataset.icon) { + nextState.icon = elements[0].dataset.icon + } } nextState.error = elements[0].classList.contains("error") @@ -205,5 +210,7 @@ color={state.error ? errorColor : state.color} componentId={state.componentId} zIndex={state.zIndex} + {background} + {animate} /> {/each} diff --git a/packages/client/src/stores/dnd.js b/packages/client/src/stores/dnd.js index 38c1e8aab8..f6b8fec48c 100644 --- a/packages/client/src/stores/dnd.js +++ b/packages/client/src/stores/dnd.js @@ -21,10 +21,17 @@ const createDndStore = () => { } const store = writable(initialState) - const startDraggingExistingComponent = ({ id, parent, bounds, index }) => { + const startDraggingExistingComponent = ({ + id, + parent, + bounds, + index, + name, + icon, + }) => { store.set({ ...initialState, - source: { id, parent, bounds, index }, + source: { id, parent, bounds, index, name, icon, isNew: false }, }) } @@ -62,7 +69,10 @@ const createDndStore = () => { parent: null, bounds: { height, width }, index: null, - newComponentType: component, + type: component, + isNew: true, + name: `New ${definition.name}`, + icon: definition.icon, }, target, drop, @@ -118,9 +128,5 @@ export const dndStore = createDndStore() // or components which depend on DND state unless values actually change. export const dndParent = derivedMemo(dndStore, x => x.drop?.parent) export const dndIndex = derivedMemo(dndStore, x => x.drop?.index) -export const dndBounds = derivedMemo(dndStore, x => x.source?.bounds) +export const dndSource = derivedMemo(dndStore, x => x.source) export const dndIsDragging = derivedMemo(dndStore, x => !!x.source) -export const dndIsNewComponent = derivedMemo( - dndStore, - x => x.source?.newComponentType != null -) diff --git a/packages/client/src/stores/index.js b/packages/client/src/stores/index.js index 268b0cc4bf..522fadc643 100644 --- a/packages/client/src/stores/index.js +++ b/packages/client/src/stores/index.js @@ -18,14 +18,7 @@ export { environmentStore } from "./environment" export { eventStore } from "./events" export { orgStore } from "./org" export { roleStore } from "./roles" -export { - dndStore, - dndIndex, - dndParent, - dndBounds, - dndIsNewComponent, - dndIsDragging, -} from "./dnd" +export { dndStore, dndIndex, dndParent, dndIsDragging, dndSource } from "./dnd" export { sidePanelStore } from "./sidePanel" export { modalStore } from "./modal" export { hoverStore } from "./hover" diff --git a/packages/client/src/stores/screens.js b/packages/client/src/stores/screens.js index 99b943fbd2..df0dc0e365 100644 --- a/packages/client/src/stores/screens.js +++ b/packages/client/src/stores/screens.js @@ -3,7 +3,7 @@ import { routeStore } from "./routes" import { builderStore } from "./builder" import { appStore } from "./app" import { orgStore } from "./org" -import { dndIndex, dndParent, dndIsNewComponent, dndBounds } from "./dnd.js" +import { dndIndex, dndParent, dndSource } from "./dnd.js" import { RoleUtils } from "@budibase/frontend-core" import { findComponentById, findComponentParent } from "../utils/components.js" import { Helpers } from "@budibase/bbui" @@ -18,8 +18,7 @@ const createScreenStore = () => { orgStore, dndParent, dndIndex, - dndIsNewComponent, - dndBounds, + dndSource, ], ([ $appStore, @@ -28,8 +27,7 @@ const createScreenStore = () => { $orgStore, $dndParent, $dndIndex, - $dndIsNewComponent, - $dndBounds, + $dndSource, ]) => { let activeLayout, activeScreen let screens @@ -85,7 +83,7 @@ const createScreenStore = () => { // Remove selected component from tree if we are moving an existing // component - if (!$dndIsNewComponent && selectedParent) { + if (!$dndSource.isNew && selectedParent) { selectedParent._children = selectedParent._children?.filter( x => x._id !== selectedComponentId ) @@ -97,11 +95,11 @@ const createScreenStore = () => { _id: DNDPlaceholderID, _styles: { normal: { - width: `${$dndBounds?.width || 400}px`, - height: `${$dndBounds?.height || 200}px`, + width: `${$dndSource?.bounds?.width || 400}px`, + height: `${$dndSource?.bounds?.height || 200}px`, opacity: 0, - "--default-width": $dndBounds?.width || 400, - "--default-height": $dndBounds?.height || 200, + "--default-width": $dndSource?.bounds?.width || 400, + "--default-height": $dndSource?.bounds?.height || 200, }, }, static: true, From 89d6231dad6a0f13999471237704e7cd9a09d2c2 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 6 Feb 2025 09:47:09 +0000 Subject: [PATCH 07/72] Type IndicatorSet --- .../preview/DNDSelectionIndicators.svelte | 20 ++-- .../components/preview/IndicatorSet.svelte | 107 +++++++++++------- 2 files changed, 79 insertions(+), 48 deletions(-) diff --git a/packages/client/src/components/preview/DNDSelectionIndicators.svelte b/packages/client/src/components/preview/DNDSelectionIndicators.svelte index 9949f07ace..cb81e83a6b 100644 --- a/packages/client/src/components/preview/DNDSelectionIndicators.svelte +++ b/packages/client/src/components/preview/DNDSelectionIndicators.svelte @@ -1,19 +1,19 @@ -{#if !$isGridScreen} - -{/if} - {#if $dndIsDragging} + {#if !$isGridScreen} + + {/if} + + - +{#if !$dndIsDragging && componentId} + +{/if}} diff --git a/packages/client/src/components/preview/Indicator.svelte b/packages/client/src/components/preview/Indicator.svelte index 94940285ff..b14807e535 100644 --- a/packages/client/src/components/preview/Indicator.svelte +++ b/packages/client/src/components/preview/Indicator.svelte @@ -1,20 +1,20 @@ - - +{#if !$dndIsDragging && $builderStore.selectedComponentId} + +{/if} From a541603267ceb664f1fb3a36343af98f01a326ef Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 6 Feb 2025 09:57:04 +0000 Subject: [PATCH 09/72] Type hover and selection indicators --- .../src/components/preview/HoverIndicator.svelte | 16 ++++++++++------ .../components/preview/SelectionIndicator.svelte | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/client/src/components/preview/HoverIndicator.svelte b/packages/client/src/components/preview/HoverIndicator.svelte index 4930fc35eb..d8846bdb42 100644 --- a/packages/client/src/components/preview/HoverIndicator.svelte +++ b/packages/client/src/components/preview/HoverIndicator.svelte @@ -1,4 +1,4 @@ - diff --git a/packages/client/src/components/context/RowSelectionProvider.svelte b/packages/client/src/components/context/RowSelectionProvider.svelte index 2c87a5fa00..da731e6f05 100644 --- a/packages/client/src/components/context/RowSelectionProvider.svelte +++ b/packages/client/src/components/context/RowSelectionProvider.svelte @@ -1,6 +1,6 @@ diff --git a/packages/client/src/components/context/SnippetsProvider.svelte b/packages/client/src/components/context/SnippetsProvider.svelte index 53fa1e8b7f..104147ecf7 100644 --- a/packages/client/src/components/context/SnippetsProvider.svelte +++ b/packages/client/src/components/context/SnippetsProvider.svelte @@ -1,6 +1,6 @@ diff --git a/packages/client/src/components/context/StateBindingsProvider.svelte b/packages/client/src/components/context/StateBindingsProvider.svelte index a1166594a8..4ef99228c1 100644 --- a/packages/client/src/components/context/StateBindingsProvider.svelte +++ b/packages/client/src/components/context/StateBindingsProvider.svelte @@ -1,6 +1,6 @@ diff --git a/packages/client/src/components/context/UserBindingsProvider.svelte b/packages/client/src/components/context/UserBindingsProvider.svelte index 98769cf76a..4cfb804998 100644 --- a/packages/client/src/components/context/UserBindingsProvider.svelte +++ b/packages/client/src/components/context/UserBindingsProvider.svelte @@ -1,6 +1,6 @@ diff --git a/packages/client/src/components/devtools/DevToolsComponentContextTab.svelte b/packages/client/src/components/devtools/DevToolsComponentContextTab.svelte index 3b4c426851..6e020c06f4 100644 --- a/packages/client/src/components/devtools/DevToolsComponentContextTab.svelte +++ b/packages/client/src/components/devtools/DevToolsComponentContextTab.svelte @@ -1,6 +1,6 @@ diff --git a/packages/client/src/components/overlay/ConfirmationDisplay.svelte b/packages/client/src/components/overlay/ConfirmationDisplay.svelte index b96af502df..823c1c1ab2 100644 --- a/packages/client/src/components/overlay/ConfirmationDisplay.svelte +++ b/packages/client/src/components/overlay/ConfirmationDisplay.svelte @@ -1,5 +1,5 @@ diff --git a/packages/client/src/components/overlay/NotificationDisplay.svelte b/packages/client/src/components/overlay/NotificationDisplay.svelte index 46b3a2a6a1..28f4b33433 100644 --- a/packages/client/src/components/overlay/NotificationDisplay.svelte +++ b/packages/client/src/components/overlay/NotificationDisplay.svelte @@ -1,5 +1,5 @@ diff --git a/packages/client/src/components/overlay/PeekScreenDisplay.svelte b/packages/client/src/components/overlay/PeekScreenDisplay.svelte index 6e0fa81b43..17a92797d5 100644 --- a/packages/client/src/components/overlay/PeekScreenDisplay.svelte +++ b/packages/client/src/components/overlay/PeekScreenDisplay.svelte @@ -5,7 +5,7 @@ notificationStore, routeStore, stateStore, - } from "stores" + } from "@/stores" import { Modal, ModalContent, ActionButton } from "@budibase/bbui" import { onDestroy } from "svelte" diff --git a/packages/client/src/components/preview/DNDHandler.svelte b/packages/client/src/components/preview/DNDHandler.svelte index 82db06a623..263be016fc 100644 --- a/packages/client/src/components/preview/DNDHandler.svelte +++ b/packages/client/src/components/preview/DNDHandler.svelte @@ -10,7 +10,7 @@ dndSource, dndIsDragging, isGridScreen, - } from "stores" + } from "@/stores" import { Utils } from "@budibase/frontend-core" import { findComponentById } from "@/utils/components.js" import { isGridEvent } from "@/utils/grid" @@ -266,7 +266,7 @@ source.type, drop.parent, drop.index, - $dndStore.meta.newComponentProps + $dndStore.source?.props ) dropping = false stopDragging() diff --git a/packages/client/src/components/preview/DNDSelectionIndicators.svelte b/packages/client/src/components/preview/DNDSelectionIndicators.svelte index cb81e83a6b..42f2e464cc 100644 --- a/packages/client/src/components/preview/DNDSelectionIndicators.svelte +++ b/packages/client/src/components/preview/DNDSelectionIndicators.svelte @@ -1,5 +1,5 @@ diff --git a/packages/client/src/components/preview/GridDNDHandler.svelte b/packages/client/src/components/preview/GridDNDHandler.svelte index 58ba92ab07..e2414e9d78 100644 --- a/packages/client/src/components/preview/GridDNDHandler.svelte +++ b/packages/client/src/components/preview/GridDNDHandler.svelte @@ -7,7 +7,7 @@ dndStore, dndSource, isGridScreen, - } from "stores" + } from "@/stores" import { Utils, memo } from "@budibase/frontend-core" import { DNDPlaceholderID, GridRowHeight } from "@/constants" import { @@ -54,7 +54,7 @@ // real component to render in the new position before updating the DND // store, preventing the green DND overlay from being out of position if ($dndSource?.isNew && styles) { - dndStore.actions.updateNewComponentProps({ + dndStore.actions.updateSourceProps({ _styles: { normal: styles, }, diff --git a/packages/client/src/components/preview/GridNewComponentDNDHandler.svelte b/packages/client/src/components/preview/GridNewComponentDNDHandler.svelte index 8a3da6f419..4a444b59e9 100644 --- a/packages/client/src/components/preview/GridNewComponentDNDHandler.svelte +++ b/packages/client/src/components/preview/GridNewComponentDNDHandler.svelte @@ -1,6 +1,6 @@ diff --git a/packages/client/src/components/preview/GridNewComponentDNDHandler.svelte b/packages/client/src/components/preview/GridNewComponentDNDHandler.svelte index 4a444b59e9..a705561294 100644 --- a/packages/client/src/components/preview/GridNewComponentDNDHandler.svelte +++ b/packages/client/src/components/preview/GridNewComponentDNDHandler.svelte @@ -2,8 +2,8 @@ import { onDestroy, getContext } from "svelte" import { builderStore, componentStore, screenStore } from "@/stores" import { Utils, memo } from "@budibase/frontend-core" - import { GridRowHeight } from "constants" - import { GridParams, getGridVar, Devices } from "utils/grid" + import { GridRowHeight } from "@/constants" + import { GridParams, getGridVar, Devices } from "@/utils/grid" const context = getContext("context") diff --git a/packages/client/src/components/preview/HoverIndicator.svelte b/packages/client/src/components/preview/HoverIndicator.svelte index d8846bdb42..981e82bc2e 100644 --- a/packages/client/src/components/preview/HoverIndicator.svelte +++ b/packages/client/src/components/preview/HoverIndicator.svelte @@ -1,7 +1,7 @@ {#if $dndIsDragging} - {#if !$isGridScreen} + {#if !$isGridScreen && $dndParent} @@ -24,7 +24,7 @@ interface DNDTarget { parent: string empty: boolean acceptsChildren: boolean - node?: Node + element?: HTMLElement } interface DNDDrop { diff --git a/packages/client/src/utils/grid.ts b/packages/client/src/utils/grid.ts index 6294c70f55..c0e4bd5bfa 100644 --- a/packages/client/src/utils/grid.ts +++ b/packages/client/src/utils/grid.ts @@ -68,15 +68,18 @@ export const getGridVar = (device: string, param: string) => `--grid-${device}-${param}` // Determines whether a JS event originated from immediately within a grid -export const isGridEvent = (e: Event & { target: HTMLElement }): boolean => { +export const isGridEvent = (e: Event): boolean => { + if (!(e.target instanceof HTMLElement)) { + return false + } + const componentParent = e.target.closest?.(".component")?.parentNode as + | HTMLElement + | undefined + const gridDOMCandidate = componentParent?.closest(".component") + ?.childNodes[0] as HTMLElement | undefined return ( e.target.dataset?.indicator === "true" || - // @ts-expect-error: api is not properly typed - e.target - .closest?.(".component") - // @ts-expect-error - ?.parentNode.closest(".component") - ?.childNodes[0]?.classList?.contains("grid") + !!gridDOMCandidate?.classList?.contains("grid") ) } From 732b2575d1b499f61d638e8ea39427aaebeec9d3 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 6 Feb 2025 14:21:39 +0000 Subject: [PATCH 13/72] Type grid DND handler --- .../components/preview/GridDNDHandler.svelte | 124 ++++++++++++------ .../src/components/preview/Indicator.svelte | 6 +- packages/client/src/index.ts | 2 +- packages/client/src/utils/grid.ts | 39 +++--- packages/frontend-core/src/utils/memo.d.ts | 2 +- 5 files changed, 110 insertions(+), 63 deletions(-) diff --git a/packages/client/src/components/preview/GridDNDHandler.svelte b/packages/client/src/components/preview/GridDNDHandler.svelte index e2414e9d78..24d61bad2b 100644 --- a/packages/client/src/components/preview/GridDNDHandler.svelte +++ b/packages/client/src/components/preview/GridDNDHandler.svelte @@ -1,4 +1,4 @@ - {#if $dndIsDragging} @@ -14,14 +19,16 @@ /> {/if} - + {#if !waitingForGrid} + + {/if} {/if} From 61940705b94394005c9a634c93e5f891edce3be8 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 6 Feb 2025 16:01:45 +0000 Subject: [PATCH 15/72] Increase DND performance via minimzing component updates --- .../new/_components/NewComponentPanel.svelte | 3 +- .../src/components/preview/DNDHandler.svelte | 7 +- .../preview/DNDPlaceholderOverlay.svelte | 108 ------------------ .../preview/DNDSelectionIndicators.svelte | 10 +- .../components/preview/GridDNDHandler.svelte | 2 +- .../components/preview/IndicatorSet.svelte | 1 + packages/client/src/stores/dnd.ts | 17 +-- 7 files changed, 24 insertions(+), 124 deletions(-) delete mode 100644 packages/client/src/components/preview/DNDPlaceholderOverlay.svelte diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/NewComponentPanel.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/NewComponentPanel.svelte index f80b681782..d13d72b89f 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/NewComponentPanel.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/NewComponentPanel.svelte @@ -261,8 +261,7 @@ class="component" class:selected={selectedIndex === orderMap[component.component]} on:click={() => addComponent(component.component)} - on:mouseover={() => (selectedIndex = null)} - on:focus + on:mouseenter={() => (selectedIndex = null)} > {component.name} diff --git a/packages/client/src/components/preview/DNDHandler.svelte b/packages/client/src/components/preview/DNDHandler.svelte index a0f9bfe2aa..15177a90c4 100644 --- a/packages/client/src/components/preview/DNDHandler.svelte +++ b/packages/client/src/components/preview/DNDHandler.svelte @@ -25,6 +25,7 @@ $: source = $dndStore.source $: target = $dndStore.target $: drop = $dndStore.drop + $: gridScreen = $isGridScreen // Local flag for whether we are awaiting an async drop event let dropping = false @@ -235,7 +236,7 @@ // Callback when on top of a component const onDragOver = (e: DragEvent) => { - if (!source || !target || $isGridScreen) { + if (!source || !target || gridScreen) { return } handleEvent(e) @@ -243,7 +244,7 @@ // Callback when entering a potential drop target const onDragEnter = async (e: DragEvent) => { - if (!source || $isGridScreen || !(e.target instanceof HTMLElement)) { + if (!source || gridScreen || !(e.target instanceof HTMLElement)) { return } @@ -278,7 +279,7 @@ source.type, drop.parent, drop.index, - $dndStore.source?.props + $dndStore.meta?.props ) dropping = false stopDragging() diff --git a/packages/client/src/components/preview/DNDPlaceholderOverlay.svelte b/packages/client/src/components/preview/DNDPlaceholderOverlay.svelte deleted file mode 100644 index adff10baf8..0000000000 --- a/packages/client/src/components/preview/DNDPlaceholderOverlay.svelte +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/client/src/components/preview/DNDSelectionIndicators.svelte b/packages/client/src/components/preview/DNDSelectionIndicators.svelte index d07082d439..cd7eb8302d 100644 --- a/packages/client/src/components/preview/DNDSelectionIndicators.svelte +++ b/packages/client/src/components/preview/DNDSelectionIndicators.svelte @@ -1,12 +1,18 @@ {#if $dndIsDragging} diff --git a/packages/client/src/components/preview/GridDNDHandler.svelte b/packages/client/src/components/preview/GridDNDHandler.svelte index 24d61bad2b..aa5187dec1 100644 --- a/packages/client/src/components/preview/GridDNDHandler.svelte +++ b/packages/client/src/components/preview/GridDNDHandler.svelte @@ -85,7 +85,7 @@ // real component to render in the new position before updating the DND // store, preventing the green DND overlay from being out of position if ($dndSource?.isNew && styles) { - dndStore.actions.updateSourceProps({ + dndStore.actions.updateNewComponentProps({ _styles: { normal: styles, }, diff --git a/packages/client/src/components/preview/IndicatorSet.svelte b/packages/client/src/components/preview/IndicatorSet.svelte index 4fc45e7b8a..83535f92cb 100644 --- a/packages/client/src/components/preview/IndicatorSet.svelte +++ b/packages/client/src/components/preview/IndicatorSet.svelte @@ -93,6 +93,7 @@ const observeMutations = (node: Node) => { mutationObserver.observe(node, { attributes: true, + attributeFilter: ["style"], }) observingMutations = true } diff --git a/packages/client/src/stores/dnd.ts b/packages/client/src/stores/dnd.ts index 6160d654da..c04ca9547e 100644 --- a/packages/client/src/stores/dnd.ts +++ b/packages/client/src/stores/dnd.ts @@ -16,7 +16,6 @@ interface DNDSource { icon?: string type: string isNew: boolean - props?: Record } interface DNDTarget { @@ -32,10 +31,15 @@ interface DNDDrop { index: number } +interface DNDMeta { + props?: Record +} + interface DNDState { source?: DNDSource target?: DNDTarget drop?: DNDDrop + meta?: DNDMeta } const createDndStore = () => { @@ -106,15 +110,12 @@ const createDndStore = () => { store.set({}) } - const updateSourceProps = (props: Record) => { + const updateNewComponentProps = (props: Record) => { store.update(state => { - if (!state.source) { - return state - } return { ...state, - source: { - ...state.source, + meta: { + ...state.meta, props, }, } @@ -129,7 +130,7 @@ const createDndStore = () => { updateTarget, updateDrop, reset, - updateSourceProps, + updateNewComponentProps, }, } } From b4fed6a02ffecb6b9a23cae7b30493902257b713 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 6 Feb 2025 16:06:45 +0000 Subject: [PATCH 16/72] Cleanup --- .../preview/GridNewComponentDNDHandler.svelte | 166 ------------------ 1 file changed, 166 deletions(-) delete mode 100644 packages/client/src/components/preview/GridNewComponentDNDHandler.svelte diff --git a/packages/client/src/components/preview/GridNewComponentDNDHandler.svelte b/packages/client/src/components/preview/GridNewComponentDNDHandler.svelte deleted file mode 100644 index a705561294..0000000000 --- a/packages/client/src/components/preview/GridNewComponentDNDHandler.svelte +++ /dev/null @@ -1,166 +0,0 @@ - From 585c9a8f6227dd1e9f5fbe47822fd5fba2571534 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 6 Feb 2025 16:36:39 +0000 Subject: [PATCH 17/72] Support scrolling while dragging components --- .../src/components/preview/GridDNDHandler.svelte | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/client/src/components/preview/GridDNDHandler.svelte b/packages/client/src/components/preview/GridDNDHandler.svelte index aa5187dec1..e5297e9208 100644 --- a/packages/client/src/components/preview/GridDNDHandler.svelte +++ b/packages/client/src/components/preview/GridDNDHandler.svelte @@ -53,6 +53,7 @@ ghost.src = "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" + let scrollElement: HTMLElement let dragInfo: GridDragInfo | undefined let styles = memo | undefined>() @@ -75,6 +76,8 @@ // Reset when not dragging new components $: !$dndIsDragging && stopDragging() + const scrollOffset = () => scrollElement?.scrollTop || 0 + const applyStyles = async ( instance: any, styles: Record | undefined @@ -113,7 +116,7 @@ } const diffX = mouseX - startX let deltaX = Math.round(diffX / colSize) - const diffY = mouseY - startY + const diffY = mouseY - startY + scrollOffset() let deltaY = Math.round(diffY / GridRowHeight) if (mode === GridDragMode.Move) { deltaX = minMax(deltaX, 1 - colStart, cols + 1 - colEnd) @@ -186,7 +189,7 @@ mode: GridDragMode.Move, grid: { startX: bounds.left + bounds.width / 2, - startY: bounds.top + bounds.height / 2, + startY: bounds.top + bounds.height / 2 + scrollOffset(), rowStart: parseInt(styles.gridRowStart), rowEnd: parseInt(styles.gridRowEnd), colStart: parseInt(styles.gridColumnStart), @@ -249,7 +252,7 @@ side, grid: { startX: e.clientX, - startY: e.clientY, + startY: e.clientY + scrollOffset(), rowStart: parseInt(styles.gridRowStart), rowEnd: parseInt(styles.gridRowEnd), colStart: parseInt(styles.gridColumnStart), @@ -295,12 +298,17 @@ } onMount(() => { + scrollElement = document.getElementsByClassName( + "screen-wrapper" + )[0] as HTMLElement document.addEventListener("dragstart", onDragStart, false) document.addEventListener("dragover", onDragOver, false) + document.addEventListener("scroll", processEvent) }) onDestroy(() => { document.removeEventListener("dragstart", onDragStart, false) document.removeEventListener("dragover", onDragOver, false) + document.removeEventListener("scroll", processEvent) }) From c26d9f4e13cd39d8a18db90bd224666eeffb476f Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Tue, 11 Feb 2025 11:28:14 +0000 Subject: [PATCH 18/72] url bindings test values --- .../controls/URLVariableTestInput.svelte | 94 +++++++++ .../_components/Screen/GeneralPanel.svelte | 10 + .../builder/src/stores/builder/preview.ts | 4 + .../client/src/components/ClientApp.svelte | 187 +++++++++--------- .../context/TestUrlBindingsProvider.svelte | 8 + packages/client/src/index.js | 4 + packages/client/src/stores/routes.ts | 13 ++ 7 files changed, 228 insertions(+), 92 deletions(-) create mode 100644 packages/builder/src/components/design/settings/controls/URLVariableTestInput.svelte create mode 100644 packages/client/src/components/context/TestUrlBindingsProvider.svelte diff --git a/packages/builder/src/components/design/settings/controls/URLVariableTestInput.svelte b/packages/builder/src/components/design/settings/controls/URLVariableTestInput.svelte new file mode 100644 index 0000000000..67a372141b --- /dev/null +++ b/packages/builder/src/components/design/settings/controls/URLVariableTestInput.svelte @@ -0,0 +1,94 @@ + + +
+
URL Variable Testing
+
Pattern: {baseRoute}
+
+
+ +
+
+ p.slice(1)).join("/")}` + : "Add test values"} + /> +
+
+
+ + diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Screen/GeneralPanel.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Screen/GeneralPanel.svelte index 3a6e7a702c..1591331f45 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Screen/GeneralPanel.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Screen/GeneralPanel.svelte @@ -15,9 +15,11 @@ import ButtonActionEditor from "@/components/design/settings/controls/ButtonActionEditor/ButtonActionEditor.svelte" import { getBindableProperties } from "@/dataBinding" import BarButtonList from "@/components/design/settings/controls/BarButtonList.svelte" + import URLVariableTestInput from "@/components/design/settings/controls/URLVariableTestInput.svelte" $: bindings = getBindableProperties($selectedScreen, null) $: screenSettings = getScreenSettings($selectedScreen) + let urlTestValue = "" let errors = {} @@ -93,6 +95,14 @@ ], }, }, + { + key: "urlTest", + control: URLVariableTestInput, + props: { + baseRoute: screen.routing?.route, + testValue: urlTestValue, + }, + }, ] return settings diff --git a/packages/builder/src/stores/builder/preview.ts b/packages/builder/src/stores/builder/preview.ts index 87b2b9355e..84d6447bb7 100644 --- a/packages/builder/src/stores/builder/preview.ts +++ b/packages/builder/src/stores/builder/preview.ts @@ -86,6 +86,10 @@ export class PreviewStore extends BudiStore { this.sendEvent("builder-state", data) } + updateUrl(data: Record) { + this.sendEvent("builder-test-url", data) + } + requestComponentContext() { this.sendEvent("request-context") } diff --git a/packages/client/src/components/ClientApp.svelte b/packages/client/src/components/ClientApp.svelte index 2840d82f47..8169328b5f 100644 --- a/packages/client/src/components/ClientApp.svelte +++ b/packages/client/src/components/ClientApp.svelte @@ -29,6 +29,7 @@ import UserBindingsProvider from "components/context/UserBindingsProvider.svelte" import DeviceBindingsProvider from "components/context/DeviceBindingsProvider.svelte" import StateBindingsProvider from "components/context/StateBindingsProvider.svelte" + import TestUrlBindingsProvider from "components/context/TestUrlBindingsProvider.svelte" import RowSelectionProvider from "components/context/RowSelectionProvider.svelte" import QueryParamsProvider from "components/context/QueryParamsProvider.svelte" import SettingsBar from "components/preview/SettingsBar.svelte" @@ -168,107 +169,109 @@ - - - - {#key $builderStore.selectedComponentId} - {#if $builderStore.inBuilder} - - {/if} - {/key} - - -
- -
- {#if showDevTools} - + + + + + {#key $builderStore.selectedComponentId} + {#if $builderStore.inBuilder} + {/if} + {/key} -
- {#if permissionError} -
- - - {@html ErrorSVG} - - You don't have permission to use this app - - - Ask your administrator to grant you access - - -
- {:else if !$screenStore.activeLayout} -
- - - {@html ErrorSVG} - - Something went wrong rendering your app - - - Get in touch with support if this issue - persists - - -
- {:else if embedNoScreens} -
- - - {@html ErrorSVG} - - This Budibase app is not publicly accessible - - -
- {:else} - - {#key $screenStore.activeLayout._id} - - {/key} - - - - - - + +
+ +
+ {#if showDevTools} + {/if} - {#if showDevTools} - +
+ {#if permissionError} +
+ + + {@html ErrorSVG} + + You don't have permission to use this app + + + Ask your administrator to grant you access + + +
+ {:else if !$screenStore.activeLayout} +
+ + + {@html ErrorSVG} + + Something went wrong rendering your app + + + Get in touch with support if this issue + persists + + +
+ {:else if embedNoScreens} +
+ + + {@html ErrorSVG} + + This Budibase app is not publicly accessible + + +
+ {:else} + + {#key $screenStore.activeLayout._id} + + {/key} + + + + + + + {/if} + + {#if showDevTools} + + {/if} +
+ + {#if !$builderStore.inBuilder && $featuresStore.logoEnabled} + {/if}
- {#if !$builderStore.inBuilder && $featuresStore.logoEnabled} - + + {#if $appStore.isDevApp} + + {/if} + {#if $builderStore.inBuilder || $devToolsStore.allowSelection} + + {/if} + {#if $builderStore.inBuilder} + + {/if}
- - - {#if $appStore.isDevApp} - - {/if} - {#if $builderStore.inBuilder || $devToolsStore.allowSelection} - - {/if} - {#if $builderStore.inBuilder} - - - {/if} -
-
+ +
diff --git a/packages/client/src/components/context/TestUrlBindingsProvider.svelte b/packages/client/src/components/context/TestUrlBindingsProvider.svelte new file mode 100644 index 0000000000..7f8c0f04da --- /dev/null +++ b/packages/client/src/components/context/TestUrlBindingsProvider.svelte @@ -0,0 +1,8 @@ + + + + + diff --git a/packages/client/src/index.js b/packages/client/src/index.js index 8a48aa08e5..b76186af8e 100644 --- a/packages/client/src/index.js +++ b/packages/client/src/index.js @@ -10,6 +10,7 @@ import { eventStore, hoverStore, stateStore, + routeStore, } from "./stores" import loadSpectrumIcons from "@budibase/bbui/spectrum-icons-vite.js" import { get } from "svelte/store" @@ -108,6 +109,9 @@ const loadBudibase = async () => { } else if (type === "builder-state") { const [[key, value]] = Object.entries(data) stateStore.actions.setValue(key, value) + } else if (type === "builder-test-url") { + const { route, testValue } = data + routeStore.actions.setTestUrlParams(route, testValue) } } diff --git a/packages/client/src/stores/routes.ts b/packages/client/src/stores/routes.ts index 3f200a9c88..f9e2cca27e 100644 --- a/packages/client/src/stores/routes.ts +++ b/packages/client/src/stores/routes.ts @@ -119,7 +119,19 @@ const createRouteStore = () => { const base = window.location.href.split("#")[0] return `${base}#${relativeURL}` } + const setTestUrlParams = (route: string, testValue: string) => { + const routeSegments = route.split("/").slice(2) + const testSegments = testValue.split("/") + const params: Record = {} + routeSegments.forEach((segment, index) => { + if (segment.startsWith(":") && index < testSegments.length) { + params[segment.slice(1)] = testSegments[index] + } + }) + + store.update(state => ({ ...state, testUrlParams: params })) + } return { subscribe: store.subscribe, actions: { @@ -130,6 +142,7 @@ const createRouteStore = () => { setQueryParams, setActiveRoute, setRouterLoaded, + setTestUrlParams, }, } } From dc202f63a90f72b5f9fd8cc7d0d31db6a977cc9e Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Tue, 11 Feb 2025 13:27:12 +0000 Subject: [PATCH 19/72] tweaks --- .../controls/URLVariableTestInput.svelte | 55 ++++++++++++------- packages/client/src/stores/routes.ts | 3 +- 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/packages/builder/src/components/design/settings/controls/URLVariableTestInput.svelte b/packages/builder/src/components/design/settings/controls/URLVariableTestInput.svelte index 67a372141b..d1a20dbb44 100644 --- a/packages/builder/src/components/design/settings/controls/URLVariableTestInput.svelte +++ b/packages/builder/src/components/design/settings/controls/URLVariableTestInput.svelte @@ -1,13 +1,28 @@
-
URL Variable Testing
-
Pattern: {baseRoute}
+
+ URL Variable Testing + +
+ +
+
+
@@ -35,9 +60,7 @@ p.slice(1)).join("/")}` - : "Add test values"} + placeholder={`${placeholder}`} />
@@ -49,18 +72,13 @@ margin-top: var(--spacing-xl); } - .label { - font-size: var(--spectrum-global-dimension-font-size-75); - font-weight: 500; + .info { + display: flex; + align-items: center; + gap: var(--spacing-s); margin-bottom: var(--spacing-s); } - .url-pattern { - font-size: var(--spectrum-global-dimension-font-size-75); - color: var(--spectrum-global-color-gray-700); - margin-bottom: var(--spacing-xs); - } - .url-test-container { display: flex; width: 100%; @@ -86,9 +104,4 @@ border-top-left-radius: 0; border-bottom-left-radius: 0; } - - /* Override input styles to make them look connected */ - .url-test-container :global(.spectrum-Textfield:focus-within) { - z-index: 1; - } diff --git a/packages/client/src/stores/routes.ts b/packages/client/src/stores/routes.ts index f9e2cca27e..50df18fa11 100644 --- a/packages/client/src/stores/routes.ts +++ b/packages/client/src/stores/routes.ts @@ -122,14 +122,13 @@ const createRouteStore = () => { const setTestUrlParams = (route: string, testValue: string) => { const routeSegments = route.split("/").slice(2) const testSegments = testValue.split("/") - const params: Record = {} + const params: Record = {} routeSegments.forEach((segment, index) => { if (segment.startsWith(":") && index < testSegments.length) { params[segment.slice(1)] = testSegments[index] } }) - store.update(state => ({ ...state, testUrlParams: params })) } return { From af0c722af1a5c75b394f67f369145a7434b719fb Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Wed, 12 Feb 2025 12:10:53 +0000 Subject: [PATCH 20/72] further changes to support persisting values when component changes --- .../controls/URLVariableTestInput.svelte | 86 +++++++++++++------ .../_components/Screen/GeneralPanel.svelte | 2 - .../builder/src/stores/builder/preview.ts | 3 +- .../builder/src/stores/builder/screens.ts | 4 + packages/client/src/stores/routes.ts | 27 +++++- packages/types/src/ui/stores/preview.ts | 1 + 6 files changed, 90 insertions(+), 33 deletions(-) diff --git a/packages/builder/src/components/design/settings/controls/URLVariableTestInput.svelte b/packages/builder/src/components/design/settings/controls/URLVariableTestInput.svelte index d1a20dbb44..99f0fb7182 100644 --- a/packages/builder/src/components/design/settings/controls/URLVariableTestInput.svelte +++ b/packages/builder/src/components/design/settings/controls/URLVariableTestInput.svelte @@ -1,37 +1,71 @@ -

import { onMount } from "svelte" - import { - Input, - Icon, - Body, - AbsTooltip, - TooltipPosition, - } from "@budibase/bbui" + import { Input, Label } from "@budibase/bbui" import { previewStore, selectedScreen } from "@/stores/builder" import type { ComponentContext } from "@budibase/types" @@ -14,6 +8,8 @@ let testValue: string | undefined + $: routeParams = baseRoute.match(/:[a-zA-Z]+/g) || [] + $: hasUrlParams = routeParams.length > 0 $: placeholder = getPlaceholder(baseRoute) $: baseInput = createBaseInput(baseRoute) $: updateTestValueFromContext($previewStore.selectedComponentContext) @@ -73,28 +69,21 @@ }) -

-
- URL Variable Testing - -
- +{#if hasUrlParams} +
+
+ +
+
+
+ +
+
+
- -
-
-
- -
-
-
-
+{/if}