Merge pull request #15267 from Budibase/feat/ts-store-conversions-pc

Component, Integrations and componentTreeNode stores converted to typescript
This commit is contained in:
Peter Clement 2024-12-31 13:53:57 +00:00 committed by GitHub
commit 1a438a7473
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 327 additions and 207 deletions

View File

@ -900,7 +900,7 @@ export const getSchemaForDatasourcePlus = (resourceId, options) => {
* optional and only needed for "provider" datasource types. * optional and only needed for "provider" datasource types.
* @param datasource the datasource definition * @param datasource the datasource definition
* @param options options for generating the schema * @param options options for generating the schema
* @return {{schema: Object, table: Object}} * @return {{schema: Object, table: Table}}
*/ */
export const getSchemaForDatasource = (asset, datasource, options) => { export const getSchemaForDatasource = (asset, datasource, options) => {
options = options || {} options = options || {}

View File

@ -1,7 +1,21 @@
import { writable, Writable, Readable } from "svelte/store" import { writable, Writable, Readable } from "svelte/store"
import {
createLocalStorageStore,
createSessionStorageStore,
} from "@budibase/frontend-core"
export enum PersistenceType {
NONE = "none",
LOCAL = "local",
SESSION = "session",
}
interface BudiStoreOpts { interface BudiStoreOpts {
debug?: boolean debug?: boolean
persistence?: {
type: PersistenceType
key: string
}
} }
export class BudiStore<T> { export class BudiStore<T> {
@ -11,7 +25,21 @@ export class BudiStore<T> {
set: Writable<T>["set"] set: Writable<T>["set"]
constructor(init: T, opts?: BudiStoreOpts) { constructor(init: T, opts?: BudiStoreOpts) {
this.store = writable<T>(init) if (opts?.persistence) {
switch (opts.persistence.type) {
case PersistenceType.LOCAL:
this.store = createLocalStorageStore(opts.persistence.key, init)
break
case PersistenceType.SESSION:
this.store = createSessionStorageStore(opts.persistence.key, init)
break
default:
this.store = writable<T>(init)
}
} else {
this.store = writable<T>(init)
}
this.subscribe = this.store.subscribe this.subscribe = this.store.subscribe
this.update = this.store.update this.update = this.store.update
this.set = this.store.set this.set = this.store.set

View File

@ -86,7 +86,7 @@ export class AppMetaStore extends BudiStore<AppMetaState> {
super(INITIAL_APP_META_STATE) super(INITIAL_APP_META_STATE)
} }
reset(): void { reset() {
this.store.set({ ...INITIAL_APP_META_STATE }) this.store.set({ ...INITIAL_APP_META_STATE })
} }
@ -94,7 +94,7 @@ export class AppMetaStore extends BudiStore<AppMetaState> {
application: App application: App
clientLibPath: string clientLibPath: string
hasLock: boolean hasLock: boolean
}): void { }) {
const { application: app, clientLibPath, hasLock } = pkg const { application: app, clientLibPath, hasLock } = pkg
this.update(state => ({ this.update(state => ({
@ -121,7 +121,7 @@ export class AppMetaStore extends BudiStore<AppMetaState> {
})) }))
} }
syncClientFeatures(features: Partial<ClientFeatures>): void { syncClientFeatures(features: Partial<ClientFeatures>) {
this.update(state => ({ this.update(state => ({
...state, ...state,
clientFeatures: { clientFeatures: {
@ -131,14 +131,14 @@ export class AppMetaStore extends BudiStore<AppMetaState> {
})) }))
} }
syncClientTypeSupportPresets(typeSupportPresets: TypeSupportPresets): void { syncClientTypeSupportPresets(typeSupportPresets: TypeSupportPresets) {
this.update(state => ({ this.update(state => ({
...state, ...state,
typeSupportPresets, typeSupportPresets,
})) }))
} }
async syncAppRoutes(): Promise<void> { async syncAppRoutes() {
const resp = await API.fetchAppRoutes() const resp = await API.fetchAppRoutes()
this.update(state => ({ this.update(state => ({
...state, ...state,
@ -147,7 +147,7 @@ export class AppMetaStore extends BudiStore<AppMetaState> {
} }
// Returned from socket // Returned from socket
syncMetadata(metadata: { name: string; url: string; icon?: AppIcon }): void { syncMetadata(metadata: { name: string; url: string; icon?: AppIcon }) {
const { name, url, icon } = metadata const { name, url, icon } = metadata
this.update(state => ({ this.update(state => ({
...state, ...state,

View File

@ -54,7 +54,7 @@ export class BuilderStore extends BudiStore<BuilderState> {
this.startBuilderOnboarding = this.startBuilderOnboarding.bind(this) this.startBuilderOnboarding = this.startBuilderOnboarding.bind(this)
} }
init(app: App): void { init(app: App) {
if (!app?.appId) { if (!app?.appId) {
console.error("BuilderStore: No appId supplied for websocket") console.error("BuilderStore: No appId supplied for websocket")
return return
@ -64,46 +64,46 @@ export class BuilderStore extends BudiStore<BuilderState> {
} }
} }
refresh(): void { refresh() {
const currentState = get(this.store) const currentState = get(this.store)
this.store.set(currentState) this.store.set(currentState)
} }
reset(): void { reset() {
this.store.set({ ...INITIAL_BUILDER_STATE }) this.store.set({ ...INITIAL_BUILDER_STATE })
this.websocket?.disconnect() this.websocket?.disconnect()
this.websocket = undefined this.websocket = undefined
} }
highlightSetting(key?: string, type?: string): void { highlightSetting(key?: string, type?: string) {
this.update(state => ({ this.update(state => ({
...state, ...state,
highlightedSetting: key ? { key, type: type || "info" } : null, highlightedSetting: key ? { key, type: type || "info" } : null,
})) }))
} }
propertyFocus(key: string | null): void { propertyFocus(key: string | null) {
this.update(state => ({ this.update(state => ({
...state, ...state,
propertyFocus: key, propertyFocus: key,
})) }))
} }
showBuilderSidePanel(): void { showBuilderSidePanel() {
this.update(state => ({ this.update(state => ({
...state, ...state,
builderSidePanel: true, builderSidePanel: true,
})) }))
} }
hideBuilderSidePanel(): void { hideBuilderSidePanel() {
this.update(state => ({ this.update(state => ({
...state, ...state,
builderSidePanel: false, builderSidePanel: false,
})) }))
} }
setPreviousTopNavPath(route: string, url: string): void { setPreviousTopNavPath(route: string, url: string) {
this.update(state => ({ this.update(state => ({
...state, ...state,
previousTopNavPath: { previousTopNavPath: {
@ -113,13 +113,13 @@ export class BuilderStore extends BudiStore<BuilderState> {
})) }))
} }
selectResource(id: string): void { selectResource(id: string) {
this.websocket?.emit(BuilderSocketEvent.SelectResource, { this.websocket?.emit(BuilderSocketEvent.SelectResource, {
resourceId: id, resourceId: id,
}) })
} }
registerTourNode(tourStepKey: string, node: HTMLElement): void { registerTourNode(tourStepKey: string, node: HTMLElement) {
this.update(state => { this.update(state => {
const update = { const update = {
...state, ...state,
@ -132,7 +132,7 @@ export class BuilderStore extends BudiStore<BuilderState> {
}) })
} }
destroyTourNode(tourStepKey: string): void { destroyTourNode(tourStepKey: string) {
const store = get(this.store) const store = get(this.store)
if (store.tourNodes?.[tourStepKey]) { if (store.tourNodes?.[tourStepKey]) {
const nodes = { ...store.tourNodes } const nodes = { ...store.tourNodes }
@ -144,7 +144,7 @@ export class BuilderStore extends BudiStore<BuilderState> {
} }
} }
startBuilderOnboarding(): void { startBuilderOnboarding() {
this.update(state => ({ this.update(state => ({
...state, ...state,
onboarding: true, onboarding: true,
@ -152,14 +152,14 @@ export class BuilderStore extends BudiStore<BuilderState> {
})) }))
} }
endBuilderOnboarding(): void { endBuilderOnboarding() {
this.update(state => ({ this.update(state => ({
...state, ...state,
onboarding: false, onboarding: false,
})) }))
} }
setTour(tourKey?: string | null): void { setTour(tourKey?: string | null) {
this.update(state => ({ this.update(state => ({
...state, ...state,
tourStepKey: null, tourStepKey: null,

View File

@ -1,67 +0,0 @@
import { get } from "svelte/store"
import { createSessionStorageStore } from "@budibase/frontend-core"
import { selectedScreen as selectedScreenStore } from "./screens"
import { findComponentPath } from "@/helpers/components"
const baseStore = createSessionStorageStore("openNodes", {})
const toggleNode = componentId => {
baseStore.update(openNodes => {
openNodes[`nodeOpen-${componentId}`] = !openNodes[`nodeOpen-${componentId}`]
return openNodes
})
}
const expandNodes = componentIds => {
baseStore.update(openNodes => {
const newNodes = Object.fromEntries(
componentIds.map(id => [`nodeOpen-${id}`, true])
)
return { ...openNodes, ...newNodes }
})
}
const collapseNodes = componentIds => {
baseStore.update(openNodes => {
const newNodes = Object.fromEntries(
componentIds.map(id => [`nodeOpen-${id}`, false])
)
return { ...openNodes, ...newNodes }
})
}
// Will ensure all parents of a node are expanded so that it is visible in the tree
const makeNodeVisible = componentId => {
const selectedScreen = get(selectedScreenStore)
const path = findComponentPath(selectedScreen.props, componentId)
const componentIds = path.map(component => component._id)
baseStore.update(openNodes => {
const newNodes = Object.fromEntries(
componentIds.map(id => [`nodeOpen-${id}`, true])
)
return { ...openNodes, ...newNodes }
})
}
const isNodeExpanded = componentId => {
const openNodes = get(baseStore)
return !!openNodes[`nodeOpen-${componentId}`]
}
const store = {
subscribe: baseStore.subscribe,
toggleNode,
expandNodes,
makeNodeVisible,
collapseNodes,
isNodeExpanded,
}
export default store

View File

@ -0,0 +1,73 @@
import { get } from "svelte/store"
import { selectedScreen as selectedScreenStore } from "./screens"
import { findComponentPath } from "@/helpers/components"
import { Screen, Component } from "@budibase/types"
import { BudiStore, PersistenceType } from "@/stores/BudiStore"
interface OpenNodesState {
[key: string]: boolean
}
export class ComponentTreeNodesStore extends BudiStore<OpenNodesState> {
constructor() {
super({} as OpenNodesState, {
persistence: {
type: PersistenceType.SESSION,
key: "openNodes",
},
})
}
toggleNode(componentId: string) {
this.update((openNodes: OpenNodesState) => {
openNodes[`nodeOpen-${componentId}`] =
!openNodes[`nodeOpen-${componentId}`]
return openNodes
})
}
expandNodes(componentIds: string[]) {
this.update((openNodes: OpenNodesState) => {
const newNodes = Object.fromEntries(
componentIds.map(id => [`nodeOpen-${id}`, true])
)
return { ...openNodes, ...newNodes }
})
}
collapseNodes(componentIds: string[]) {
this.update((openNodes: OpenNodesState) => {
const newNodes = Object.fromEntries(
componentIds.map(id => [`nodeOpen-${id}`, false])
)
return { ...openNodes, ...newNodes }
})
}
// Will ensure all parents of a node are expanded so that it is visible in the tree
makeNodeVisible(componentId: string) {
const selectedScreen: Screen = get(selectedScreenStore)
const path = findComponentPath(selectedScreen.props, componentId)
const componentIds = path.map((component: Component) => component._id)
this.update((openNodes: OpenNodesState) => {
const newNodes = Object.fromEntries(
componentIds.map((id: string) => [`nodeOpen-${id}`, true])
)
return { ...openNodes, ...newNodes }
})
}
isNodeExpanded(componentId: string): boolean {
const openNodes = get(this)
return !!openNodes[`nodeOpen-${componentId}`]
}
}
export default new ComponentTreeNodesStore()

View File

@ -30,10 +30,40 @@ import {
} from "@/constants/backend" } from "@/constants/backend"
import { BudiStore } from "../BudiStore" import { BudiStore } from "../BudiStore"
import { Utils } from "@budibase/frontend-core" import { Utils } from "@budibase/frontend-core"
import { FieldType } from "@budibase/types" import { Component, FieldType, Screen, Table } from "@budibase/types"
import { utils } from "@budibase/shared-core" import { utils } from "@budibase/shared-core"
export const INITIAL_COMPONENTS_STATE = { interface ComponentDefinition {
component: string
name: string
friendlyName?: string
hasChildren?: boolean
settings?: ComponentSetting[]
features?: Record<string, boolean>
typeSupportPresets?: Record<string, any>
}
interface ComponentSetting {
key: string
type: string
section?: string
name?: string
defaultValue?: any
selectAllFields?: boolean
resetOn?: string | string[]
settings?: ComponentSetting[]
}
interface ComponentState {
components: Record<string, ComponentDefinition>
customComponents: string[]
selectedComponentId: string | null
componentToPaste?: Component | null
settingsCache: Record<string, ComponentSetting[]>
selectedScreenId?: string | null
}
export const INITIAL_COMPONENTS_STATE: ComponentState = {
components: {}, components: {},
customComponents: [], customComponents: [],
selectedComponentId: null, selectedComponentId: null,
@ -41,7 +71,7 @@ export const INITIAL_COMPONENTS_STATE = {
settingsCache: {}, settingsCache: {},
} }
export class ComponentStore extends BudiStore { export class ComponentStore extends BudiStore<ComponentState> {
constructor() { constructor() {
super(INITIAL_COMPONENTS_STATE) super(INITIAL_COMPONENTS_STATE)
@ -89,14 +119,14 @@ export class ComponentStore extends BudiStore {
* @param {string} appId * @param {string} appId
* @returns * @returns
*/ */
async refreshDefinitions(appId) { async refreshDefinitions(appId: string) {
if (!appId) { if (!appId) {
return return
} }
// Fetch definitions and filter out custom component definitions so we // Fetch definitions and filter out custom component definitions so we
// can flag them // can flag them
const components = await API.fetchComponentLibDefinitions(appId) const components: any = await API.fetchComponentLibDefinitions(appId)
const customComponents = Object.keys(components).filter(key => const customComponents = Object.keys(components).filter(key =>
key.startsWith("plugin/") key.startsWith("plugin/")
) )
@ -122,7 +152,7 @@ export class ComponentStore extends BudiStore {
* '@budibase/standard-components/container' * '@budibase/standard-components/container'
* @returns {object} * @returns {object}
*/ */
getDefinition(componentType) { getDefinition(componentType: string) {
if (!componentType) { if (!componentType) {
return null return null
} }
@ -169,7 +199,7 @@ export class ComponentStore extends BudiStore {
* @param {object} enrichedComponent * @param {object} enrichedComponent
* @returns {object} migrated Component * @returns {object} migrated Component
*/ */
migrateSettings(enrichedComponent) { migrateSettings(enrichedComponent: Component) {
const componentPrefix = "@budibase/standard-components" const componentPrefix = "@budibase/standard-components"
let migrated = false let migrated = false
@ -224,7 +254,7 @@ export class ComponentStore extends BudiStore {
* @param {object} opts * @param {object} opts
* @returns * @returns
*/ */
enrichEmptySettings(component, opts) { enrichEmptySettings(component: Component, opts: any) {
if (!component?._component) { if (!component?._component) {
return return
} }
@ -235,7 +265,7 @@ export class ComponentStore extends BudiStore {
if (!screen) { if (!screen) {
return return
} }
settings.forEach(setting => { settings.forEach((setting: ComponentSetting) => {
const value = component[setting.key] const value = component[setting.key]
// Fill empty settings // Fill empty settings
@ -257,7 +287,7 @@ export class ComponentStore extends BudiStore {
} else if (setting.type === "dataProvider") { } else if (setting.type === "dataProvider") {
// Pick closest data provider where required // Pick closest data provider where required
const path = findComponentPath(screen.props, treeId) const path = findComponentPath(screen.props, treeId)
const providers = path.filter(component => const providers = path.filter((component: Component) =>
component._component?.endsWith("/dataprovider") component._component?.endsWith("/dataprovider")
) )
if (providers.length) { if (providers.length) {
@ -278,7 +308,8 @@ export class ComponentStore extends BudiStore {
const form = findClosestMatchingComponent( const form = findClosestMatchingComponent(
screen.props, screen.props,
treeId, treeId,
x => x._component === "@budibase/standard-components/form" (x: Component) =>
x._component === "@budibase/standard-components/form"
) )
const usedFields = Object.keys(buildFormSchema(form) || {}) const usedFields = Object.keys(buildFormSchema(form) || {})
@ -302,7 +333,8 @@ export class ComponentStore extends BudiStore {
// Validate data provider exists, or else clear it // Validate data provider exists, or else clear it
const providers = findAllMatchingComponents( const providers = findAllMatchingComponents(
screen?.props, screen?.props,
x => x._component === "@budibase/standard-components/dataprovider" (x: Component) =>
x._component === "@budibase/standard-components/dataprovider"
) )
const valid = providers?.some(dp => value.includes?.(dp._id)) const valid = providers?.some(dp => value.includes?.(dp._id))
if (!valid) { if (!valid) {
@ -325,10 +357,14 @@ export class ComponentStore extends BudiStore {
if (cardKeys.every(key => !component[key]) && !component.cardImageURL) { if (cardKeys.every(key => !component[key]) && !component.cardImageURL) {
const { _id, dataSource } = component const { _id, dataSource } = component
if (dataSource) { if (dataSource) {
const { schema, table } = getSchemaForDatasource(screen, dataSource) const {
schema,
table,
}: { schema: Record<string, any>; table: Table } =
getSchemaForDatasource(screen, dataSource, {})
// Finds fields by types from the schema of the configured datasource // Finds fields by types from the schema of the configured datasource
const findFieldTypes = fieldTypes => { const findFieldTypes = (fieldTypes: any) => {
if (!Array.isArray(fieldTypes)) { if (!Array.isArray(fieldTypes)) {
fieldTypes = [fieldTypes] fieldTypes = [fieldTypes]
} }
@ -345,7 +381,11 @@ export class ComponentStore extends BudiStore {
} }
// Inserts a card binding for a certain setting // Inserts a card binding for a certain setting
const addBinding = (key, fallback, ...parts) => { const addBinding = (
key: string,
fallback: string | null,
...parts: any[]
) => {
if (parts.some(x => x == null)) { if (parts.some(x => x == null)) {
component[key] = fallback component[key] = fallback
} else { } else {
@ -363,7 +403,7 @@ export class ComponentStore extends BudiStore {
...findFieldTypes(FieldType.NUMBER), ...findFieldTypes(FieldType.NUMBER),
] ]
const longFields = findFieldTypes(FieldType.LONGFORM) const longFields = findFieldTypes(FieldType.LONGFORM)
if (schema?.[table?.primaryDisplay]) { if (table?.primaryDisplay && schema?.[table.primaryDisplay]) {
shortFields.unshift(table.primaryDisplay) shortFields.unshift(table.primaryDisplay)
} }
@ -399,7 +439,7 @@ export class ComponentStore extends BudiStore {
* @param {object} parent * @param {object} parent
* @returns * @returns
*/ */
createInstance(componentName, presetProps, parent) { createInstance(componentName: string, presetProps: any, parent: any) {
const definition = this.getDefinition(componentName) const definition = this.getDefinition(componentName)
if (!definition) { if (!definition) {
return null return null
@ -433,7 +473,7 @@ export class ComponentStore extends BudiStore {
} }
// Custom post processing for creation only // Custom post processing for creation only
let extras = {} let extras: any = {}
if (definition.hasChildren) { if (definition.hasChildren) {
extras._children = [] extras._children = []
} }
@ -443,10 +483,11 @@ export class ComponentStore extends BudiStore {
const parentForm = findClosestMatchingComponent( const parentForm = findClosestMatchingComponent(
get(selectedScreen).props, get(selectedScreen).props,
get(selectedComponent)._id, get(selectedComponent)._id,
component => component._component.endsWith("/form") (component: Component) => component._component.endsWith("/form")
) )
const formSteps = findAllMatchingComponents(parentForm, component => const formSteps = findAllMatchingComponents(
component._component.endsWith("/formstep") parentForm,
(component: Component) => component._component.endsWith("/formstep")
) )
extras.step = formSteps.length + 1 extras.step = formSteps.length + 1
extras._instanceName = `Step ${formSteps.length + 1}` extras._instanceName = `Step ${formSteps.length + 1}`
@ -466,7 +507,12 @@ export class ComponentStore extends BudiStore {
* @param {number} index * @param {number} index
* @returns * @returns
*/ */
async create(componentName, presetProps, parent, index) { async create(
componentName: string,
presetProps: any,
parent: any,
index: number
) {
const state = get(this.store) const state = get(this.store)
const componentInstance = this.createInstance( const componentInstance = this.createInstance(
componentName, componentName,
@ -479,23 +525,23 @@ export class ComponentStore extends BudiStore {
// Insert in position if specified // Insert in position if specified
if (parent && index != null) { if (parent && index != null) {
await screenStore.patch(screen => { await screenStore.patch((screen: Screen) => {
let parentComponent = findComponent(screen.props, parent) let parentComponent = findComponent(screen.props, parent)
if (!parentComponent._children?.length) { if (!parentComponent._children?.length) {
parentComponent._children = [componentInstance] parentComponent._children = [componentInstance]
} else { } else {
parentComponent._children.splice(index, 0, componentInstance) parentComponent._children.splice(index, 0, componentInstance)
} }
}) }, null)
} }
// Otherwise we work out where this component should be inserted // Otherwise we work out where this component should be inserted
else { else {
await screenStore.patch(screen => { await screenStore.patch((screen: Screen) => {
// Find the selected component // Find the selected component
let selectedComponentId = state.selectedComponentId let selectedComponentId = state.selectedComponentId
if (selectedComponentId.startsWith(`${screen._id}-`)) { if (selectedComponentId?.startsWith(`${screen._id}-`)) {
selectedComponentId = screen?.props._id selectedComponentId = screen.props._id || null
} }
const currentComponent = findComponent( const currentComponent = findComponent(
screen.props, screen.props,
@ -533,7 +579,7 @@ export class ComponentStore extends BudiStore {
parentComponent._children = [] parentComponent._children = []
} }
parentComponent._children.push(componentInstance) parentComponent._children.push(componentInstance)
}) }, null)
} }
// Select new component // Select new component
@ -559,18 +605,22 @@ export class ComponentStore extends BudiStore {
* @param {string} screenId * @param {string} screenId
* @returns * @returns
*/ */
async patch(patchFn, componentId, screenId) { async patch(
patchFn: (component: Component, screen: Screen) => any,
componentId?: string,
screenId?: string
) {
// Use selected component by default // Use selected component by default
if (!componentId || !screenId) { if (!componentId || !screenId) {
const state = get(this.store) const state = get(this.store)
componentId = componentId || state.selectedComponentId componentId = componentId ?? state.selectedComponentId ?? undefined
const screenState = get(screenStore) const screenState = get(screenStore)
screenId = screenId || screenState.selectedScreenId screenId = screenId || screenState.selectedScreenId
} }
if (!componentId || !screenId || !patchFn) { if (!componentId || !screenId || !patchFn) {
return return
} }
const patchScreen = screen => { const patchScreen = (screen: Screen) => {
let component = findComponent(screen.props, componentId) let component = findComponent(screen.props, componentId)
if (!component) { if (!component) {
return false return false
@ -594,7 +644,7 @@ export class ComponentStore extends BudiStore {
* @param {object} component * @param {object} component
* @returns * @returns
*/ */
async delete(component) { async delete(component: Component) {
if (!component) { if (!component) {
return return
} }
@ -602,7 +652,7 @@ export class ComponentStore extends BudiStore {
// Determine the next component to select, and select it before deletion // Determine the next component to select, and select it before deletion
// to avoid an intermediate state of no component selection // to avoid an intermediate state of no component selection
const state = get(this.store) const state = get(this.store)
let nextId let nextId: string | null = ""
if (state.selectedComponentId === component._id) { if (state.selectedComponentId === component._id) {
nextId = this.getNext() nextId = this.getNext()
if (!nextId) { if (!nextId) {
@ -621,7 +671,7 @@ export class ComponentStore extends BudiStore {
} }
// Patch screen // Patch screen
await screenStore.patch(screen => { await screenStore.patch((screen: Screen) => {
// Check component exists // Check component exists
component = findComponent(screen.props, component._id) component = findComponent(screen.props, component._id)
if (!component) { if (!component) {
@ -634,12 +684,12 @@ export class ComponentStore extends BudiStore {
return false return false
} }
parent._children = parent._children.filter( parent._children = parent._children.filter(
child => child._id !== component._id (child: Component) => child._id !== component._id
) )
}) }, null)
} }
copy(component, cut = false, selectParent = true) { copy(component: Component, cut = false, selectParent = true) {
// Update store with copied component // Update store with copied component
this.update(state => { this.update(state => {
state.componentToPaste = cloneDeep(component) state.componentToPaste = cloneDeep(component)
@ -665,7 +715,7 @@ export class ComponentStore extends BudiStore {
* *
* @param {string} componentId * @param {string} componentId
*/ */
select(componentId) { select(componentId: string) {
this.update(state => { this.update(state => {
state.selectedComponentId = componentId state.selectedComponentId = componentId
return state return state
@ -679,12 +729,17 @@ export class ComponentStore extends BudiStore {
* @param {object} targetScreen * @param {object} targetScreen
* @returns * @returns
*/ */
async paste(targetComponent, mode, targetScreen, selectComponent = true) { async paste(
targetComponent: Component,
mode: string,
targetScreen: Screen,
selectComponent = true
) {
const state = get(this.store) const state = get(this.store)
if (!state.componentToPaste) { if (!state.componentToPaste) {
return return
} }
let newComponentId let newComponentId: string | null = ""
// Remove copied component if cutting, regardless if pasting works // Remove copied component if cutting, regardless if pasting works
let componentToPaste = cloneDeep(state.componentToPaste) let componentToPaste = cloneDeep(state.componentToPaste)
@ -696,7 +751,7 @@ export class ComponentStore extends BudiStore {
} }
// Patch screen // Patch screen
const patch = screen => { const patch = (screen: Screen) => {
// Get up to date ref to target // Get up to date ref to target
targetComponent = findComponent(screen.props, targetComponent._id) targetComponent = findComponent(screen.props, targetComponent._id)
if (!targetComponent) { if (!targetComponent) {
@ -712,7 +767,7 @@ export class ComponentStore extends BudiStore {
if (!cut) { if (!cut) {
componentToPaste = makeComponentUnique(componentToPaste) componentToPaste = makeComponentUnique(componentToPaste)
} }
newComponentId = componentToPaste._id newComponentId = componentToPaste._id!
// Strip grid position metadata if pasting into a new screen, but keep // Strip grid position metadata if pasting into a new screen, but keep
// alignment metadata // alignment metadata
@ -732,7 +787,7 @@ export class ComponentStore extends BudiStore {
const parent = findComponentParent(screen.props, originalId) const parent = findComponentParent(screen.props, originalId)
if (parent?._children) { if (parent?._children) {
parent._children = parent._children.filter( parent._children = parent._children.filter(
component => component._id !== originalId (component: Component) => component._id !== originalId
) )
} }
} }
@ -740,7 +795,7 @@ export class ComponentStore extends BudiStore {
// Check inside is valid // Check inside is valid
if (mode === "inside") { if (mode === "inside") {
const definition = this.getDefinition(targetComponent._component) const definition = this.getDefinition(targetComponent._component)
if (!definition.hasChildren) { if (!definition?.hasChildren) {
mode = "below" mode = "below"
} }
} }
@ -758,14 +813,16 @@ export class ComponentStore extends BudiStore {
if (!parent?._children) { if (!parent?._children) {
return false return false
} }
const targetIndex = parent._children.findIndex(component => { const targetIndex = parent._children.findIndex(
return component._id === targetComponent._id (component: Component) => {
}) return component._id === targetComponent._id
}
)
const index = mode === "above" ? targetIndex : targetIndex + 1 const index = mode === "above" ? targetIndex : targetIndex + 1
parent._children.splice(index, 0, componentToPaste) parent._children.splice(index, 0, componentToPaste)
} }
} }
const targetScreenId = targetScreen?._id || state.selectedScreenId const targetScreenId = targetScreen?._id ?? state.selectedScreenId ?? null
await screenStore.patch(patch, targetScreenId) await screenStore.patch(patch, targetScreenId)
// Select the new component // Select the new component
@ -785,7 +842,9 @@ export class ComponentStore extends BudiStore {
const componentId = state.selectedComponentId const componentId = state.selectedComponentId
const screen = get(selectedScreen) const screen = get(selectedScreen)
const parent = findComponentParent(screen.props, componentId) const parent = findComponentParent(screen.props, componentId)
const index = parent?._children.findIndex(x => x._id === componentId) const index = parent?._children.findIndex(
(x: Component) => x._id === componentId
)
// Check for screen and navigation component edge cases // Check for screen and navigation component edge cases
const screenComponentId = `${screen._id}-screen` const screenComponentId = `${screen._id}-screen`
@ -832,7 +891,9 @@ export class ComponentStore extends BudiStore {
const componentId = component?._id const componentId = component?._id
const screen = get(selectedScreen) const screen = get(selectedScreen)
const parent = findComponentParent(screen.props, componentId) const parent = findComponentParent(screen.props, componentId)
const index = parent?._children.findIndex(x => x._id === componentId) const index = parent?._children.findIndex(
(x: Component) => x._id === componentId
)
// Check for screen and navigation component edge cases // Check for screen and navigation component edge cases
const screenComponentId = `${screen._id}-screen` const screenComponentId = `${screen._id}-screen`
@ -862,7 +923,7 @@ export class ComponentStore extends BudiStore {
let target = parent let target = parent
let targetParent = findComponentParent(screen.props, target._id) let targetParent = findComponentParent(screen.props, target._id)
let targetIndex = targetParent?._children.findIndex( let targetIndex = targetParent?._children.findIndex(
child => child._id === target._id (child: Component) => child._id === target._id
) )
while ( while (
targetParent != null && targetParent != null &&
@ -871,7 +932,7 @@ export class ComponentStore extends BudiStore {
target = targetParent target = targetParent
targetParent = findComponentParent(screen.props, target._id) targetParent = findComponentParent(screen.props, target._id)
targetIndex = targetParent?._children.findIndex( targetIndex = targetParent?._children.findIndex(
child => child._id === target._id (child: Component) => child._id === target._id
) )
} }
if (targetParent) { if (targetParent) {
@ -901,13 +962,15 @@ export class ComponentStore extends BudiStore {
} }
} }
async moveUp(component) { async moveUp(component: Component) {
await screenStore.patch(screen => { await screenStore.patch((screen: Screen) => {
const componentId = component?._id const componentId = component?._id
const parent = findComponentParent(screen.props, componentId) const parent = findComponentParent(screen.props, componentId)
// Check we aren't right at the top of the tree // Check we aren't right at the top of the tree
const index = parent?._children.findIndex(x => x._id === componentId) const index = parent?._children.findIndex(
(x: Component) => x._id === componentId
)
if (!parent || (index === 0 && parent._id === screen.props._id)) { if (!parent || (index === 0 && parent._id === screen.props._id)) {
return return
} }
@ -915,7 +978,7 @@ export class ComponentStore extends BudiStore {
// Copy original component and remove it from the parent // Copy original component and remove it from the parent
const originalComponent = cloneDeep(parent._children[index]) const originalComponent = cloneDeep(parent._children[index])
parent._children = parent._children.filter( parent._children = parent._children.filter(
component => component._id !== componentId (component: Component) => component._id !== componentId
) )
// If we have siblings above us, move up // If we have siblings above us, move up
@ -925,7 +988,7 @@ export class ComponentStore extends BudiStore {
const previousSibling = parent._children[index - 1] const previousSibling = parent._children[index - 1]
const definition = this.getDefinition(previousSibling._component) const definition = this.getDefinition(previousSibling._component)
if ( if (
definition.hasChildren && definition?.hasChildren &&
componentTreeNodesStore.isNodeExpanded(previousSibling._id) componentTreeNodesStore.isNodeExpanded(previousSibling._id)
) { ) {
previousSibling._children.push(originalComponent) previousSibling._children.push(originalComponent)
@ -942,15 +1005,15 @@ export class ComponentStore extends BudiStore {
else if (parent._id !== screen.props._id) { else if (parent._id !== screen.props._id) {
const grandParent = findComponentParent(screen.props, parent._id) const grandParent = findComponentParent(screen.props, parent._id)
const parentIndex = grandParent._children.findIndex( const parentIndex = grandParent._children.findIndex(
child => child._id === parent._id (child: Component) => child._id === parent._id
) )
grandParent._children.splice(parentIndex, 0, originalComponent) grandParent._children.splice(parentIndex, 0, originalComponent)
} }
}) }, null)
} }
async moveDown(component) { async moveDown(component: Component) {
await screenStore.patch(screen => { await screenStore.patch((screen: Screen) => {
const componentId = component?._id const componentId = component?._id
const parent = findComponentParent(screen.props, componentId) const parent = findComponentParent(screen.props, componentId)
@ -960,7 +1023,9 @@ export class ComponentStore extends BudiStore {
} }
// Check we aren't right at the bottom of the tree // Check we aren't right at the bottom of the tree
const index = parent._children.findIndex(x => x._id === componentId) const index = parent._children.findIndex(
(x: Component) => x._id === componentId
)
if ( if (
index === parent._children.length - 1 && index === parent._children.length - 1 &&
parent._id === screen.props._id parent._id === screen.props._id
@ -971,7 +1036,7 @@ export class ComponentStore extends BudiStore {
// Copy the original component and remove from parent // Copy the original component and remove from parent
const originalComponent = cloneDeep(parent._children[index]) const originalComponent = cloneDeep(parent._children[index])
parent._children = parent._children.filter( parent._children = parent._children.filter(
component => component._id !== componentId (component: Component) => component._id !== componentId
) )
// Move below the next sibling if we are not the last sibling // Move below the next sibling if we are not the last sibling
@ -980,7 +1045,7 @@ export class ComponentStore extends BudiStore {
const nextSibling = parent._children[index] const nextSibling = parent._children[index]
const definition = this.getDefinition(nextSibling._component) const definition = this.getDefinition(nextSibling._component)
if ( if (
definition.hasChildren && definition?.hasChildren &&
componentTreeNodesStore.isNodeExpanded(nextSibling._id) componentTreeNodesStore.isNodeExpanded(nextSibling._id)
) { ) {
nextSibling._children.splice(0, 0, originalComponent) nextSibling._children.splice(0, 0, originalComponent)
@ -996,15 +1061,15 @@ export class ComponentStore extends BudiStore {
else { else {
const grandParent = findComponentParent(screen.props, parent._id) const grandParent = findComponentParent(screen.props, parent._id)
const parentIndex = grandParent._children.findIndex( const parentIndex = grandParent._children.findIndex(
child => child._id === parent._id (child: Component) => child._id === parent._id
) )
grandParent._children.splice(parentIndex + 1, 0, originalComponent) grandParent._children.splice(parentIndex + 1, 0, originalComponent)
} }
}) }, null)
} }
async updateStyle(name, value) { async updateStyle(name: string, value: string) {
await this.patch(component => { await this.patch((component: Component) => {
if (value == null || value === "") { if (value == null || value === "") {
delete component._styles.normal[name] delete component._styles.normal[name]
} else { } else {
@ -1013,8 +1078,8 @@ export class ComponentStore extends BudiStore {
}) })
} }
async updateStyles(styles, id) { async updateStyles(styles: Record<string, string>, id: string) {
const patchFn = component => { const patchFn = (component: Component) => {
component._styles.normal = { component._styles.normal = {
...component._styles.normal, ...component._styles.normal,
...styles, ...styles,
@ -1023,24 +1088,24 @@ export class ComponentStore extends BudiStore {
await this.patch(patchFn, id) await this.patch(patchFn, id)
} }
async updateCustomStyle(style) { async updateCustomStyle(style: Record<string, string>) {
await this.patch(component => { await this.patch((component: Component) => {
component._styles.custom = style component._styles.custom = style
}) })
} }
async updateConditions(conditions) { async updateConditions(conditions: Record<string, any>) {
await this.patch(component => { await this.patch((component: Component) => {
component._conditions = conditions component._conditions = conditions
}) })
} }
async updateSetting(name, value) { async updateSetting(name: string, value: any) {
await this.patch(this.updateComponentSetting(name, value)) await this.patch(this.updateComponentSetting(name, value))
} }
updateComponentSetting(name, value) { updateComponentSetting(name: string, value: any) {
return component => { return (component: Component) => {
if (!name || !component) { if (!name || !component) {
return false return false
} }
@ -1050,10 +1115,12 @@ export class ComponentStore extends BudiStore {
} }
const settings = this.getComponentSettings(component._component) const settings = this.getComponentSettings(component._component)
const updatedSetting = settings.find(setting => setting.key === name) const updatedSetting = settings.find(
(setting: ComponentSetting) => setting.key === name
)
// Reset dependent fields // Reset dependent fields
settings.forEach(setting => { settings.forEach((setting: ComponentSetting) => {
const needsReset = const needsReset =
name === setting.resetOn || name === setting.resetOn ||
(Array.isArray(setting.resetOn) && setting.resetOn.includes(name)) (Array.isArray(setting.resetOn) && setting.resetOn.includes(name))
@ -1066,15 +1133,16 @@ export class ComponentStore extends BudiStore {
updatedSetting?.type === "dataSource" || updatedSetting?.type === "dataSource" ||
updatedSetting?.type === "table" updatedSetting?.type === "table"
) { ) {
const { schema } = getSchemaForDatasource(null, value) const { schema }: { schema: Record<string, any> } =
getSchemaForDatasource(null, value, null)
const columnNames = Object.keys(schema || {}) const columnNames = Object.keys(schema || {})
const multifieldKeysToSelectAll = settings const multifieldKeysToSelectAll = settings
.filter(setting => { .filter((setting: ComponentSetting) => {
return setting.type === "multifield" && setting.selectAllFields return setting.type === "multifield" && setting.selectAllFields
}) })
.map(setting => setting.key) .map((setting: ComponentSetting) => setting.key)
multifieldKeysToSelectAll.forEach(key => { multifieldKeysToSelectAll.forEach((key: string) => {
component[key] = columnNames component[key] = columnNames
}) })
} }
@ -1083,14 +1151,14 @@ export class ComponentStore extends BudiStore {
} }
} }
requestEjectBlock(componentId) { requestEjectBlock(componentId: string) {
previewStore.sendEvent("eject-block", componentId) previewStore.sendEvent("eject-block", componentId)
} }
async handleEjectBlock(componentId, ejectedDefinition) { async handleEjectBlock(componentId: string, ejectedDefinition: Component) {
let nextSelectedComponentId let nextSelectedComponentId: string | null = null
await screenStore.patch(screen => { await screenStore.patch((screen: Screen) => {
const block = findComponent(screen.props, componentId) const block = findComponent(screen.props, componentId)
const parent = findComponentParent(screen.props, componentId) const parent = findComponentParent(screen.props, componentId)
@ -1108,7 +1176,7 @@ export class ComponentStore extends BudiStore {
// _containsSlot flag to know where to insert them // _containsSlot flag to know where to insert them
const slotContainer = findAllMatchingComponents( const slotContainer = findAllMatchingComponents(
ejectedDefinition, ejectedDefinition,
x => x._containsSlot (x: Component) => x._containsSlot
)[0] )[0]
if (slotContainer) { if (slotContainer) {
delete slotContainer._containsSlot delete slotContainer._containsSlot
@ -1120,10 +1188,12 @@ export class ComponentStore extends BudiStore {
// Replace block with ejected definition // Replace block with ejected definition
ejectedDefinition = makeComponentUnique(ejectedDefinition) ejectedDefinition = makeComponentUnique(ejectedDefinition)
const index = parent._children.findIndex(x => x._id === componentId) const index = parent._children.findIndex(
(x: Component) => x._id === componentId
)
parent._children[index] = ejectedDefinition parent._children[index] = ejectedDefinition
nextSelectedComponentId = ejectedDefinition._id nextSelectedComponentId = ejectedDefinition._id ?? null
}) }, null)
// Select new root component // Select new root component
if (nextSelectedComponentId) { if (nextSelectedComponentId) {
@ -1134,7 +1204,7 @@ export class ComponentStore extends BudiStore {
} }
} }
async addParent(componentId, parentType) { async addParent(componentId: string, parentType: string) {
if (!componentId || !parentType) { if (!componentId || !parentType) {
return return
} }
@ -1146,7 +1216,7 @@ export class ComponentStore extends BudiStore {
} }
// Replace component with a version wrapped in a new parent // Replace component with a version wrapped in a new parent
await screenStore.patch(screen => { await screenStore.patch((screen: Screen) => {
// Get this component definition and parent definition // Get this component definition and parent definition
let definition = findComponent(screen.props, componentId) let definition = findComponent(screen.props, componentId)
let oldParentDefinition = findComponentParent(screen.props, componentId) let oldParentDefinition = findComponentParent(screen.props, componentId)
@ -1156,7 +1226,7 @@ export class ComponentStore extends BudiStore {
// Replace component with parent // Replace component with parent
const index = oldParentDefinition._children.findIndex( const index = oldParentDefinition._children.findIndex(
component => component._id === componentId (component: Component) => component._id === componentId
) )
if (index === -1) { if (index === -1) {
return false return false
@ -1165,7 +1235,7 @@ export class ComponentStore extends BudiStore {
...newParentDefinition, ...newParentDefinition,
_children: [definition], _children: [definition],
} }
}) }, null)
// Select the new parent // Select the new parent
this.update(state => { this.update(state => {
@ -1181,7 +1251,7 @@ export class ComponentStore extends BudiStore {
* '@budibase/standard-components/container' * '@budibase/standard-components/container'
* @returns {boolean} * @returns {boolean}
*/ */
isCached(componentType) { isCached(componentType: string) {
const settings = get(this.store).settingsCache const settings = get(this.store).settingsCache
return componentType in settings return componentType in settings
} }
@ -1194,8 +1264,8 @@ export class ComponentStore extends BudiStore {
* '@budibase/standard-components/container' * '@budibase/standard-components/container'
* @returns {array} the settings * @returns {array} the settings
*/ */
cacheSettings(componentType, definition) { cacheSettings(componentType: string, definition: ComponentDefinition | null) {
let settings = [] let settings: ComponentSetting[] = []
if (definition) { if (definition) {
settings = definition.settings?.filter(setting => !setting.section) ?? [] settings = definition.settings?.filter(setting => !setting.section) ?? []
definition.settings definition.settings
@ -1229,7 +1299,7 @@ export class ComponentStore extends BudiStore {
* '@budibase/standard-components/container' * '@budibase/standard-components/container'
* @returns {Array<object>} * @returns {Array<object>}
*/ */
getComponentSettings(componentType) { getComponentSettings(componentType: string) {
if (!componentType) { if (!componentType) {
return [] return []
} }

View File

@ -1,18 +0,0 @@
import { writable } from "svelte/store"
import { API } from "@/api"
const createIntegrationsStore = () => {
const store = writable({})
const init = async () => {
const integrations = await API.getIntegrations()
store.set(integrations)
}
return {
...store,
init,
}
}
export const integrations = createIntegrationsStore()

View File

@ -0,0 +1,34 @@
import { writable, type Writable } from "svelte/store"
import { API } from "@/api"
import { Integration } from "@budibase/types"
type IntegrationsState = Record<string, Integration>
const INITIAL_STATE: IntegrationsState = {}
const createIntegrationsStore = () => {
const store: Writable<IntegrationsState> = writable(INITIAL_STATE)
const init = async () => {
const response = await API.getIntegrations()
// Filter out undefineds
const integrations = Object.entries(response).reduce(
(acc, [key, value]) => {
if (value) {
acc[key] = value
}
return acc
},
{} as IntegrationsState
)
store.set(integrations)
}
return {
...store,
init,
}
}
export const integrations = createIntegrationsStore()

View File

@ -274,7 +274,7 @@ export class ScreenStore extends BudiStore {
/** /**
* @param {function} patchFn * @param {function} patchFn
* @param {string} screenId * @param {string | null} screenId
* @returns * @returns
*/ */
async patch(patchFn, screenId) { async patch(patchFn, screenId) {