diff --git a/packages/backend-core/src/constants.js b/packages/backend-core/src/constants.js index 559dc0e6b2..42450190e5 100644 --- a/packages/backend-core/src/constants.js +++ b/packages/backend-core/src/constants.js @@ -16,6 +16,7 @@ exports.Headers = { API_VER: "x-budibase-api-version", APP_ID: "x-budibase-app-id", TYPE: "x-budibase-type", + PREVIEW_ROLE: "x-budibase-role", TENANT_ID: "x-budibase-tenant-id", TOKEN: "x-budibase-token", CSRF_TOKEN: "x-csrf-token", diff --git a/packages/bbui/src/Layout/Layout.svelte b/packages/bbui/src/Layout/Layout.svelte index 0dcb1f46ee..c66a409242 100644 --- a/packages/bbui/src/Layout/Layout.svelte +++ b/packages/bbui/src/Layout/Layout.svelte @@ -36,6 +36,10 @@ padding-left: var(--spacing-l); padding-right: var(--spacing-l); } + .paddingX-XL { + padding-left: var(--spacing-xl); + padding-right: var(--spacing-xl); + } .paddingY-S { padding-top: var(--spacing-s); padding-bottom: var(--spacing-s); @@ -48,6 +52,10 @@ padding-top: var(--spacing-l); padding-bottom: var(--spacing-l); } + .paddingY-XL { + padding-top: var(--spacing-xl); + padding-bottom: var(--spacing-xl); + } .gap-XXS { grid-gap: var(--spacing-xs); } diff --git a/packages/bbui/src/Table/InternalRenderer.svelte b/packages/bbui/src/Table/InternalRenderer.svelte index 7e2dd0b2aa..d38fb9f691 100644 --- a/packages/bbui/src/Table/InternalRenderer.svelte +++ b/packages/bbui/src/Table/InternalRenderer.svelte @@ -1,42 +1,21 @@ diff --git a/packages/bbui/src/Tabs/Tabs.svelte b/packages/bbui/src/Tabs/Tabs.svelte index 6930a6cdb5..579c61e28d 100644 --- a/packages/bbui/src/Tabs/Tabs.svelte +++ b/packages/bbui/src/Tabs/Tabs.svelte @@ -108,7 +108,7 @@ padding-left: var(--spacing-xl); padding-right: var(--spacing-xl); position: relative; - border-bottom: var(--border-light); + border-bottom: 1px solid var(--spectrum-global-color-gray-300); } .spectrum-Tabs-content { margin-top: var(--spectrum-global-dimension-static-size-150); diff --git a/packages/bbui/src/helpers.js b/packages/bbui/src/helpers.js index cf40e12d74..b02783e0bd 100644 --- a/packages/bbui/src/helpers.js +++ b/packages/bbui/src/helpers.js @@ -106,3 +106,29 @@ export const deepSet = (obj, key, value) => { export const cloneDeep = obj => { return JSON.parse(JSON.stringify(obj)) } + +/** + * Copies a value to the clipboard + * @param value the value to copy + */ +export const copyToClipboard = value => { + return new Promise(res => { + if (navigator.clipboard && window.isSecureContext) { + // Try using the clipboard API first + navigator.clipboard.writeText(value).then(res) + } else { + // Fall back to the textarea hack + let textArea = document.createElement("textarea") + textArea.value = value + textArea.style.position = "fixed" + textArea.style.left = "-9999px" + textArea.style.top = "-9999px" + document.body.appendChild(textArea) + textArea.focus() + textArea.select() + document.execCommand("copy") + textArea.remove() + res() + } + }) +} diff --git a/packages/bbui/yarn.lock b/packages/bbui/yarn.lock index 2b6b15dd15..0b20a580ba 100644 --- a/packages/bbui/yarn.lock +++ b/packages/bbui/yarn.lock @@ -53,10 +53,10 @@ to-gfm-code-block "^0.1.1" year "^0.2.1" -"@budibase/string-templates@^1.0.104": - version "1.0.104" - resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-1.0.104.tgz#f812700f2b21f638fd1e48dde065ae693fae2897" - integrity sha512-3caq3qwpIieyb9m8eSl8OhcE0ppzuyJ/0ubDlWmtpbmwmG2v3ynI+DwxpbG4CcVQFuebD2yxU0CZfioU76vKCQ== +"@budibase/string-templates@^1.0.105-alpha.4": + version "1.0.108" + resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-1.0.108.tgz#14949560148ef11b6b385952ed5787c12b890f2c" + integrity sha512-7Tts91Dzy+A7OdObTIaBNAdaixC7wmabnNTWYqk1d6TM6H5yv++bd/a9gVdUM7ptsuMy7uoP/ZoTZZRhp3ozfA== dependencies: "@budibase/handlebars-helpers" "^0.11.8" dayjs "^1.10.4" diff --git a/packages/client/src/api/api.js b/packages/client/src/api/api.js index 591d4a6782..1d575d9d27 100644 --- a/packages/client/src/api/api.js +++ b/packages/client/src/api/api.js @@ -1,5 +1,5 @@ import { createAPIClient } from "@budibase/frontend-core" -import { notificationStore, authStore } from "../stores" +import { notificationStore, authStore, devToolsStore } from "../stores" import { get } from "svelte/store" export const API = createAPIClient({ @@ -21,6 +21,12 @@ export const API = createAPIClient({ if (auth?.csrfToken) { headers["x-csrf-token"] = auth.csrfToken } + + // Add role header + const role = get(devToolsStore).role + if (role) { + headers["x-budibase-role"] = role + } }, // Show an error notification for all API failures. diff --git a/packages/client/src/components/ClientApp.svelte b/packages/client/src/components/ClientApp.svelte index 27767862ab..6bd0313c75 100644 --- a/packages/client/src/components/ClientApp.svelte +++ b/packages/client/src/components/ClientApp.svelte @@ -14,6 +14,8 @@ routeStore, builderStore, themeStore, + appStore, + devToolsStore, } from "stores" import NotificationDisplay from "components/overlay/NotificationDisplay.svelte" import ConfirmationDisplay from "components/overlay/ConfirmationDisplay.svelte" @@ -28,6 +30,8 @@ import CustomThemeWrapper from "./CustomThemeWrapper.svelte" import DNDHandler from "components/preview/DNDHandler.svelte" import KeyboardManager from "components/preview/KeyboardManager.svelte" + import DevToolsHeader from "components/devtools/DevToolsHeader.svelte" + import DevTools from "components/devtools/DevTools.svelte" // Provide contexts setContext("sdk", SDK) @@ -55,8 +59,22 @@ if ($authStore) { // There is a logged in user, so handle them if ($screenStore.screens.length) { + let firstRoute + + // If using devtools, find the first screen matching our role + if ($devToolsStore.role) { + const roleRoutes = $screenStore.screens.filter( + screen => screen.routing?.roleId === $devToolsStore.role + ) + firstRoute = roleRoutes[0]?.routing?.route || "/" + } + + // Otherwise just use the first route + else { + firstRoute = $screenStore.screens[0]?.routing?.route ?? "/" + } + // Screens exist so navigate back to the home screen - const firstRoute = $screenStore.screens[0].routing?.route ?? "/" routeStore.actions.navigate(firstRoute) } else { // No screens likely means the user has no permissions to view this app @@ -70,6 +88,8 @@ } } } + + $: isDevPreview = $appStore.isDevApp && !$builderStore.inBuilder {#if dataLoaded} @@ -109,39 +129,49 @@ >
- - {#key `${$screenStore.activeLayout._id}-${$builderStore.previewType}`} - - {/key} + {#if isDevPreview} + + {/if} - -
+
+ + {#key `${$screenStore.activeLayout._id}-${$builderStore.previewType}`} + + {/key} - - @@ -167,6 +197,7 @@ justify-content: center; align-items: center; } + #clip-root { max-width: 100%; max-height: 100%; @@ -176,10 +207,24 @@ overflow: hidden; background-color: transparent; } + #app-root { overflow: hidden; height: 100%; width: 100%; + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: stretch; + } + + #app-body { + flex: 1 1 auto; + display: flex; + flex-direction: row; + justify-content: flex-start; + align-items: stretch; + overflow: hidden; } .error { @@ -192,19 +237,23 @@ text-align: center; padding: 20px; } + .error :global(svg) { fill: var(--spectrum-global-color-gray-500); width: 80px; height: 80px; } + .error :global(h1), .error :global(p) { color: var(--spectrum-global-color-gray-800); } + .error :global(p) { font-style: italic; margin-top: -0.5em; } + .error :global(h1) { font-weight: 400; } @@ -214,14 +263,17 @@ #clip-root.preview { padding: 2px; } + #clip-root.tablet-preview { width: calc(1024px + 6px); height: calc(768px + 6px); } + #clip-root.mobile-preview { width: calc(390px + 6px); height: calc(844px + 6px); } + .preview #app-root { border: 1px solid var(--spectrum-global-color-gray-300); border-radius: 4px; diff --git a/packages/client/src/components/Component.svelte b/packages/client/src/components/Component.svelte index d9af295108..3c29cb875b 100644 --- a/packages/client/src/components/Component.svelte +++ b/packages/client/src/components/Component.svelte @@ -9,12 +9,16 @@ {#if constructor && initialSettings && (visible || inSelectedPath)} @@ -419,12 +431,15 @@ .component { display: contents; } + .interactive :global(*:hover) { cursor: pointer; } + .draggable :global(*:hover) { cursor: grab; } + .editing :global(*:hover) { cursor: auto; } diff --git a/packages/client/src/components/app/Layout.svelte b/packages/client/src/components/app/Layout.svelte index a801ea4b46..4df9087904 100644 --- a/packages/client/src/components/app/Layout.svelte +++ b/packages/client/src/components/app/Layout.svelte @@ -179,6 +179,7 @@ justify-content: flex-start; align-items: stretch; height: 100%; + flex: 1 1 auto; overflow: auto; overflow-x: hidden; position: relative; diff --git a/packages/client/src/components/app/forms/FormStep.svelte b/packages/client/src/components/app/forms/FormStep.svelte index 58300287a8..4441f515ee 100644 --- a/packages/client/src/components/app/forms/FormStep.svelte +++ b/packages/client/src/components/app/forms/FormStep.svelte @@ -5,7 +5,7 @@ export let step = 1 - const { styleable, builderStore } = getContext("sdk") + const { styleable, builderStore, componentStore } = getContext("sdk") const component = getContext("component") const formContext = getContext("form") @@ -22,7 +22,7 @@ if ( formContext && $builderStore.inBuilder && - $builderStore.selectedComponentPath?.includes($component.id) + $componentStore.selectedComponentPath?.includes($component.id) ) { formContext.formApi.setStep(step) } diff --git a/packages/client/src/components/context/UserBindingsProvider.svelte b/packages/client/src/components/context/UserBindingsProvider.svelte index e788d80dc4..f7605122ae 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/DevTools.svelte b/packages/client/src/components/devtools/DevTools.svelte new file mode 100644 index 0000000000..4bb332da2f --- /dev/null +++ b/packages/client/src/components/devtools/DevTools.svelte @@ -0,0 +1,69 @@ + + +
+ {#if $devToolsStore.visible} + +
+ Budibase DevTools + devToolsStore.actions.setVisible(false)} + /> +
+ + +
+ +
+
+ +
+ +
+
+
+
+ {/if} +
+ + diff --git a/packages/client/src/components/devtools/DevToolsComponentContextTab.svelte b/packages/client/src/components/devtools/DevToolsComponentContextTab.svelte new file mode 100644 index 0000000000..3b4c426851 --- /dev/null +++ b/packages/client/src/components/devtools/DevToolsComponentContextTab.svelte @@ -0,0 +1,113 @@ + + + + + Choose a category to see the value of all its available bindings. + + devToolsStore.actions.changeRole(e.detail)} + /> + {#if !$context.device.mobile} + + {/if} +
+ + diff --git a/packages/client/src/components/devtools/DevToolsStat.svelte b/packages/client/src/components/devtools/DevToolsStat.svelte new file mode 100644 index 0000000000..30600737d2 --- /dev/null +++ b/packages/client/src/components/devtools/DevToolsStat.svelte @@ -0,0 +1,75 @@ + + +
+
{prettyLabel}
+
+ {prettyValue} +
+
+ + diff --git a/packages/client/src/components/devtools/DevToolsStatsTab.svelte b/packages/client/src/components/devtools/DevToolsStatsTab.svelte new file mode 100644 index 0000000000..ab029db815 --- /dev/null +++ b/packages/client/src/components/devtools/DevToolsStatsTab.svelte @@ -0,0 +1,27 @@ + + + + + + + {#if $appStore.clientLoadTime} + + {/if} + + + + + + + + diff --git a/packages/client/src/components/preview/IndicatorSet.svelte b/packages/client/src/components/preview/IndicatorSet.svelte index 012aa7e470..6fcf552d21 100644 --- a/packages/client/src/components/preview/IndicatorSet.svelte +++ b/packages/client/src/components/preview/IndicatorSet.svelte @@ -2,6 +2,7 @@ import { onMount, onDestroy } from "svelte" import Indicator from "./Indicator.svelte" import { domDebounce } from "utils/domDebounce" + import { builderStore } from "stores" export let componentId export let color @@ -13,6 +14,7 @@ let interval let text $: visibleIndicators = indicators.filter(x => x.visible) + $: offset = $builderStore.inBuilder ? 0 : 2 let updating = false let observers = [] @@ -88,8 +90,8 @@ const elBounds = child.getBoundingClientRect() nextIndicators.push({ - top: elBounds.top + scrollY - deviceBounds.top, - left: elBounds.left + scrollX - deviceBounds.left, + top: elBounds.top + scrollY - deviceBounds.top - offset, + left: elBounds.left + scrollX - deviceBounds.left - offset, width: elBounds.width + 4, height: elBounds.height + 4, visible: false, diff --git a/packages/client/src/components/preview/SettingsBar.svelte b/packages/client/src/components/preview/SettingsBar.svelte index 06ff159fcf..bf0b48250a 100644 --- a/packages/client/src/components/preview/SettingsBar.svelte +++ b/packages/client/src/components/preview/SettingsBar.svelte @@ -3,7 +3,7 @@ import SettingsButton from "./SettingsButton.svelte" import SettingsColorPicker from "./SettingsColorPicker.svelte" import SettingsPicker from "./SettingsPicker.svelte" - import { builderStore } from "stores" + import { builderStore, componentStore } from "stores" import { domDebounce } from "utils/domDebounce" const verticalOffset = 28 @@ -15,7 +15,7 @@ let self let measured = false - $: definition = $builderStore.selectedComponentDefinition + $: definition = $componentStore.selectedComponentDefinition $: showBar = definition?.showSettingsBar && !$builderStore.isDragging $: settings = getBarSettings(definition) @@ -163,9 +163,7 @@ { - builderStore.actions.deleteComponent( - $builderStore.selectedComponent._id - ) + builderStore.actions.deleteComponent($builderStore.selectedComponentId) }} title="Delete component" /> diff --git a/packages/client/src/components/preview/SettingsButton.svelte b/packages/client/src/components/preview/SettingsButton.svelte index 1490b2c9b7..6f7d95f5ae 100644 --- a/packages/client/src/components/preview/SettingsButton.svelte +++ b/packages/client/src/components/preview/SettingsButton.svelte @@ -1,6 +1,6 @@ diff --git a/packages/client/src/components/preview/SettingsColorPicker.svelte b/packages/client/src/components/preview/SettingsColorPicker.svelte index 68061e0163..b078d048d2 100644 --- a/packages/client/src/components/preview/SettingsColorPicker.svelte +++ b/packages/client/src/components/preview/SettingsColorPicker.svelte @@ -1,10 +1,10 @@
diff --git a/packages/client/src/components/preview/SettingsPicker.svelte b/packages/client/src/components/preview/SettingsPicker.svelte index 8d7129b812..8b83729fde 100644 --- a/packages/client/src/components/preview/SettingsPicker.svelte +++ b/packages/client/src/components/preview/SettingsPicker.svelte @@ -1,12 +1,12 @@
diff --git a/packages/client/src/stores/app.js b/packages/client/src/stores/app.js index a28a4cd9eb..2c2ead66c4 100644 --- a/packages/client/src/stores/app.js +++ b/packages/client/src/stores/app.js @@ -1,8 +1,14 @@ import { API } from "api" import { get, writable } from "svelte/store" +const initialState = { + appId: null, + isDevApp: false, + clientLoadTime: window.INIT_TIME ? Date.now() - window.INIT_TIME : null, +} + const createAppStore = () => { - const store = writable(null) + const store = writable(initialState) // Fetches the app definition including screens, layouts and theme const fetchAppDefinition = async () => { @@ -13,11 +19,13 @@ const createAppStore = () => { try { const appDefinition = await API.fetchAppPackage(appId) store.set({ + ...initialState, ...appDefinition, appId: appDefinition?.application?.appId, + isDevApp: appId.startsWith("app_dev"), }) } catch (error) { - store.set(null) + store.set(initialState) } } diff --git a/packages/client/src/stores/builder.js b/packages/client/src/stores/builder.js index 27c8bbe2a2..6d57ce7762 100644 --- a/packages/client/src/stores/builder.js +++ b/packages/client/src/stores/builder.js @@ -1,7 +1,6 @@ -import { writable, derived, get } from "svelte/store" -import Manifest from "manifest.json" -import { findComponentById, findComponentPathById } from "../utils/components" +import { writable, get } from "svelte/store" import { API } from "api" +import { devToolsStore } from "./devTools.js" const dispatchEvent = (type, data = {}) => { window.parent.postMessage({ type, data }) @@ -22,38 +21,18 @@ const createBuilderStore = () => { previewDevice: "desktop", isDragging: false, } - const writableStore = writable(initialState) - const derivedStore = derived(writableStore, $state => { - // Avoid any of this logic if we aren't in the builder preview - if (!$state.inBuilder) { - return $state - } - - // Derive the selected component instance and definition - const { layout, screen, previewType, selectedComponentId } = $state - const asset = previewType === "layout" ? layout : screen - const component = findComponentById(asset?.props, selectedComponentId) - const prefix = "@budibase/standard-components/" - const type = component?._component?.replace(prefix, "") - const definition = type ? Manifest[type] : null - - // Derive the selected component path - const path = findComponentPathById(asset.props, selectedComponentId) || [] - - return { - ...$state, - selectedComponent: component, - selectedComponentDefinition: definition, - selectedComponentPath: path?.map(component => component._id), - } - }) - + const store = writable(initialState) const actions = { selectComponent: id => { - if (id === get(writableStore).selectedComponentId) { + if (id === get(store).selectedComponentId) { return } - writableStore.update(state => ({ ...state, editMode: false })) + store.update(state => ({ + ...state, + editMode: false, + selectedComponentId: id, + })) + devToolsStore.actions.setAllowSelection(false) dispatchEvent("select-component", { id }) }, updateProp: (prop, value) => { @@ -76,7 +55,7 @@ const createBuilderStore = () => { } }, setSelectedPath: path => { - writableStore.update(state => ({ ...state, selectedPath: path })) + store.update(state => ({ ...state, selectedPath: path })) }, moveComponent: (componentId, destinationComponentId, mode) => { dispatchEvent("move-component", { @@ -86,22 +65,21 @@ const createBuilderStore = () => { }) }, setDragging: dragging => { - if (dragging === get(writableStore).isDragging) { + if (dragging === get(store).isDragging) { return } - writableStore.update(state => ({ ...state, isDragging: dragging })) + store.update(state => ({ ...state, isDragging: dragging })) }, setEditMode: enabled => { - if (enabled === get(writableStore).editMode) { + if (enabled === get(store).editMode) { return } - writableStore.update(state => ({ ...state, editMode: enabled })) + store.update(state => ({ ...state, editMode: enabled })) }, } return { - ...writableStore, - set: state => writableStore.set({ ...initialState, ...state }), - subscribe: derivedStore.subscribe, + ...store, + set: state => store.set({ ...initialState, ...state }), actions, } } diff --git a/packages/client/src/stores/components.js b/packages/client/src/stores/components.js new file mode 100644 index 0000000000..4f972b23c7 --- /dev/null +++ b/packages/client/src/stores/components.js @@ -0,0 +1,81 @@ +import { get, writable, derived } from "svelte/store" +import Manifest from "manifest.json" +import { findComponentById, findComponentPathById } from "../utils/components" +import { devToolsStore } from "./devTools" +import { screenStore } from "./screens" +import { builderStore } from "./builder" + +const createComponentStore = () => { + const store = writable({}) + + const derivedStore = derived( + [store, builderStore, devToolsStore, screenStore], + ([$store, $builderState, $devToolsState, $screenState]) => { + // Avoid any of this logic if we aren't in the builder preview + if (!$builderState.inBuilder && !$devToolsState.visible) { + return {} + } + + // Derive the selected component instance and definition + let asset + const { layout, screen, previewType, selectedComponentId } = $builderState + if ($builderState.inBuilder) { + asset = previewType === "layout" ? layout : screen + } else { + asset = $screenState.activeScreen + } + const component = findComponentById(asset?.props, selectedComponentId) + const prefix = "@budibase/standard-components/" + const type = component?._component?.replace(prefix, "") + const definition = type ? Manifest[type] : null + + // Derive the selected component path + const path = + findComponentPathById(asset?.props, selectedComponentId) || [] + + return { + selectedComponentInstance: $store[selectedComponentId], + selectedComponent: component, + selectedComponentDefinition: definition, + selectedComponentPath: path?.map(component => component._id), + mountedComponents: Object.keys($store).length, + currentAsset: asset, + } + } + ) + + const registerInstance = (id, instance) => { + store.update(state => ({ + ...state, + [id]: instance, + })) + } + + const unregisterInstance = id => { + store.update(state => { + delete state[id] + return state + }) + } + + const isComponentRegistered = id => { + return get(store)[id] != null + } + + const getComponentById = id => { + const asset = get(derivedStore).currentAsset + return findComponentById(asset?.props, id) + } + + return { + ...derivedStore, + actions: { + registerInstance, + unregisterInstance, + isComponentRegistered, + getComponentById, + }, + } +} + +export const componentStore = createComponentStore() diff --git a/packages/client/src/stores/devTools.js b/packages/client/src/stores/devTools.js new file mode 100644 index 0000000000..6d631080ab --- /dev/null +++ b/packages/client/src/stores/devTools.js @@ -0,0 +1,47 @@ +import { get } from "svelte/store" +import { createLocalStorageStore } from "@budibase/frontend-core" +import { appStore } from "./app" +import { initialise } from "./initialise" +import { authStore } from "./auth" + +const initialState = { + visible: false, + allowSelection: false, + role: null, +} + +const createDevToolStore = () => { + const localStorageKey = `${get(appStore).appId}.devTools` + const store = createLocalStorageStore(localStorageKey, initialState) + + const setVisible = visible => { + store.update(state => ({ + ...state, + visible: visible, + })) + } + + const setAllowSelection = allowSelection => { + store.update(state => ({ + ...state, + allowSelection, + })) + } + + const changeRole = async role => { + store.update(state => ({ + ...state, + role: role === "self" ? null : role, + })) + // location.reload() + await authStore.actions.fetchUser() + await initialise() + } + + return { + subscribe: store.subscribe, + actions: { setVisible, setAllowSelection, changeRole }, + } +} + +export const devToolsStore = createDevToolStore() diff --git a/packages/client/src/stores/index.js b/packages/client/src/stores/index.js index ddd052fb4e..280a32e069 100644 --- a/packages/client/src/stores/index.js +++ b/packages/client/src/stores/index.js @@ -9,6 +9,8 @@ export { confirmationStore } from "./confirmation" export { peekStore } from "./peek" export { stateStore } from "./state" export { themeStore } from "./theme" +export { devToolsStore } from "./devTools" +export { componentStore } from "./components" export { uploadStore } from "./uploads.js" export { rowSelectionStore } from "./rowSelection.js" // Context stores are layered and duplicated, so it is not a singleton diff --git a/packages/client/src/stores/screens.js b/packages/client/src/stores/screens.js index 702f662f8a..9635f2b5a0 100644 --- a/packages/client/src/stores/screens.js +++ b/packages/client/src/stores/screens.js @@ -66,7 +66,6 @@ const createScreenStore = () => { } let children = [] findChildrenByType(component, type, children) - console.log(children) return children }, } diff --git a/packages/client/src/utils/componentProps.js b/packages/client/src/utils/componentProps.js index 14760252a9..38eaf77885 100644 --- a/packages/client/src/utils/componentProps.js +++ b/packages/client/src/utils/componentProps.js @@ -107,3 +107,21 @@ export const propsUseBinding = (props, bindingKey) => { } return false } + +/** + * Gets the definition of this component's settings from the manifest + */ +export const getSettingsDefinition = definition => { + if (!definition) { + return [] + } + let settings = [] + definition.settings?.forEach(setting => { + if (setting.section) { + settings = settings.concat(setting.settings || []) + } else { + settings.push(setting) + } + }) + return settings +} diff --git a/packages/server/src/api/controllers/static/templates/BudibaseApp.svelte b/packages/server/src/api/controllers/static/templates/BudibaseApp.svelte index 761e953ff7..35379ba6d8 100644 --- a/packages/server/src/api/controllers/static/templates/BudibaseApp.svelte +++ b/packages/server/src/api/controllers/static/templates/BudibaseApp.svelte @@ -78,6 +78,9 @@ app.
+