2021-11-26 14:25:02 +01:00
|
|
|
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"
|
2022-08-10 16:34:00 +02:00
|
|
|
import Router from "../components/Router.svelte"
|
|
|
|
import * as AppComponents from "../components/app/index.js"
|
2022-10-19 15:39:28 +02:00
|
|
|
import { ScreenslotType } from "../constants.js"
|
2022-08-10 16:34:00 +02:00
|
|
|
|
|
|
|
const budibasePrefix = "@budibase/standard-components/"
|
2021-11-26 14:25:02 +01:00
|
|
|
|
|
|
|
const createComponentStore = () => {
|
2022-08-11 18:05:42 +02:00
|
|
|
const store = writable({
|
|
|
|
customComponentManifest: {},
|
2022-08-12 15:02:11 +02:00
|
|
|
customComponentMap: {},
|
2022-08-11 18:05:42 +02:00
|
|
|
mountedComponents: {},
|
|
|
|
})
|
2021-11-26 14:25:02 +01:00
|
|
|
|
|
|
|
const derivedStore = derived(
|
2022-10-14 19:16:19 +02:00
|
|
|
[store, builderStore, devToolsStore, screenStore],
|
|
|
|
([$store, $builderStore, $devToolsStore, $screenStore]) => {
|
|
|
|
const { inBuilder, selectedComponentId } = $builderStore
|
|
|
|
|
2021-11-26 14:25:02 +01:00
|
|
|
// Avoid any of this logic if we aren't in the builder preview
|
2022-10-14 19:16:19 +02:00
|
|
|
if (!inBuilder && !$devToolsStore.visible) {
|
2021-11-26 14:25:02 +01:00
|
|
|
return {}
|
|
|
|
}
|
|
|
|
|
2022-10-14 19:16:19 +02:00
|
|
|
const root = $screenStore.activeScreen?.props
|
|
|
|
const component = findComponentById(root, selectedComponentId)
|
2022-08-11 18:05:42 +02:00
|
|
|
const definition = getComponentDefinition(component?._component)
|
2021-11-26 14:25:02 +01:00
|
|
|
|
|
|
|
// Derive the selected component path
|
2022-10-06 10:17:26 +02:00
|
|
|
const selectedPath =
|
2022-10-14 19:16:19 +02:00
|
|
|
findComponentPathById(root, selectedComponentId) || []
|
2021-11-26 14:25:02 +01:00
|
|
|
|
|
|
|
return {
|
2022-08-10 16:34:00 +02:00
|
|
|
customComponentManifest: $store.customComponentManifest,
|
2022-08-11 18:05:42 +02:00
|
|
|
selectedComponentInstance:
|
|
|
|
$store.mountedComponents[selectedComponentId],
|
2021-11-26 14:25:02 +01:00
|
|
|
selectedComponent: component,
|
|
|
|
selectedComponentDefinition: definition,
|
2022-10-06 10:17:26 +02:00
|
|
|
selectedComponentPath: selectedPath?.map(component => component._id),
|
2022-08-11 18:05:42 +02:00
|
|
|
mountedComponentCount: Object.keys($store.mountedComponents).length,
|
2021-11-26 14:25:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
const registerInstance = (id, instance) => {
|
2022-10-20 09:43:33 +02:00
|
|
|
if (!id) {
|
|
|
|
return
|
|
|
|
}
|
2022-08-11 18:05:42 +02:00
|
|
|
store.update(state => {
|
2022-08-12 15:02:11 +02:00
|
|
|
// If this is a custom component, flag it so we can reload this component
|
|
|
|
// later if required
|
2022-08-11 18:05:42 +02:00
|
|
|
const component = instance.component
|
2022-08-12 15:02:11 +02:00
|
|
|
if (component?.startsWith("plugin")) {
|
|
|
|
if (!state.customComponentMap[component]) {
|
|
|
|
state.customComponentMap[component] = [id]
|
|
|
|
} else {
|
|
|
|
state.customComponentMap[component].push(id)
|
2022-08-11 18:05:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-12 15:02:11 +02:00
|
|
|
// Register to mounted components
|
|
|
|
state.mountedComponents[id] = instance
|
|
|
|
return state
|
2022-08-11 18:05:42 +02:00
|
|
|
})
|
2021-11-26 14:25:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const unregisterInstance = id => {
|
2022-10-20 09:43:33 +02:00
|
|
|
if (!id) {
|
|
|
|
return
|
|
|
|
}
|
2021-11-26 14:25:02 +01:00
|
|
|
store.update(state => {
|
2022-08-12 15:02:11 +02:00
|
|
|
// Remove from custom component map if required
|
|
|
|
const component = state.mountedComponents[id]?.instance?.component
|
|
|
|
let customComponentMap = state.customComponentMap
|
|
|
|
if (component?.startsWith("plugin")) {
|
|
|
|
customComponentMap[component] = customComponentMap[component].filter(
|
|
|
|
x => {
|
|
|
|
return x !== id
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove from mounted components
|
2022-08-11 18:05:42 +02:00
|
|
|
delete state.mountedComponents[id]
|
2021-11-26 14:25:02 +01:00
|
|
|
return state
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-02-24 22:48:54 +01:00
|
|
|
const isComponentRegistered = id => {
|
2022-08-11 18:05:42 +02:00
|
|
|
return get(store).mountedComponents[id] != null
|
2022-02-24 22:48:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const getComponentById = id => {
|
2022-10-14 19:16:19 +02:00
|
|
|
const root = get(screenStore).activeScreen?.props
|
|
|
|
return findComponentById(root, id)
|
2022-02-24 22:48:54 +01:00
|
|
|
}
|
|
|
|
|
2022-05-17 15:33:12 +02:00
|
|
|
const getComponentDefinition = type => {
|
2022-08-10 16:34:00 +02:00
|
|
|
if (!type) {
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
|
|
|
|
// Screenslot is an edge case
|
2022-10-11 17:02:09 +02:00
|
|
|
if (type === ScreenslotType) {
|
2022-08-10 16:34:00 +02:00
|
|
|
type = `${budibasePrefix}${type}`
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle built-in components
|
|
|
|
if (type.startsWith(budibasePrefix)) {
|
|
|
|
type = type.replace(budibasePrefix, "")
|
|
|
|
return type ? Manifest[type] : null
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle custom components
|
|
|
|
const { customComponentManifest } = get(store)
|
|
|
|
return customComponentManifest?.[type]?.schema?.schema
|
|
|
|
}
|
|
|
|
|
|
|
|
const getComponentConstructor = type => {
|
|
|
|
if (!type) {
|
|
|
|
return null
|
|
|
|
}
|
2022-10-11 17:02:09 +02:00
|
|
|
if (type === ScreenslotType) {
|
2022-08-10 16:34:00 +02:00
|
|
|
return Router
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle budibase components
|
|
|
|
if (type.startsWith(budibasePrefix)) {
|
|
|
|
const split = type.split("/")
|
|
|
|
const name = split[split.length - 1]
|
|
|
|
return AppComponents[name]
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle custom components
|
|
|
|
const { customComponentManifest } = get(store)
|
|
|
|
return customComponentManifest?.[type]?.Component
|
|
|
|
}
|
|
|
|
|
2022-10-19 15:39:28 +02:00
|
|
|
const getComponentInstance = id => {
|
2022-10-20 09:43:33 +02:00
|
|
|
if (!id) {
|
|
|
|
return null
|
|
|
|
}
|
2022-10-19 15:39:28 +02:00
|
|
|
return get(store).mountedComponents[id]
|
|
|
|
}
|
|
|
|
|
2022-08-22 19:30:13 +02:00
|
|
|
const registerCustomComponent = ({ Component, schema, version }) => {
|
|
|
|
if (!Component || !schema?.schema?.name || !version) {
|
2022-08-10 16:34:00 +02:00
|
|
|
return
|
|
|
|
}
|
2022-09-02 20:52:52 +02:00
|
|
|
const component = `plugin/${schema.schema.name}`
|
2022-08-10 16:34:00 +02:00
|
|
|
store.update(state => {
|
2022-08-12 15:02:11 +02:00
|
|
|
state.customComponentManifest[component] = {
|
2022-08-10 16:34:00 +02:00
|
|
|
Component,
|
2022-08-12 15:02:11 +02:00
|
|
|
schema,
|
2022-08-10 16:34:00 +02:00
|
|
|
}
|
|
|
|
return state
|
|
|
|
})
|
2022-08-11 18:05:42 +02:00
|
|
|
|
2022-08-12 15:02:11 +02:00
|
|
|
// Reload any mounted instances of this custom component
|
2022-08-11 18:05:42 +02:00
|
|
|
const state = get(store)
|
2022-08-12 15:02:11 +02:00
|
|
|
if (state.customComponentMap[component]?.length) {
|
|
|
|
state.customComponentMap[component].forEach(id => {
|
|
|
|
state.mountedComponents[id]?.reload()
|
2022-08-11 18:05:42 +02:00
|
|
|
})
|
|
|
|
}
|
2022-05-17 15:33:12 +02:00
|
|
|
}
|
|
|
|
|
2021-11-26 14:25:02 +01:00
|
|
|
return {
|
|
|
|
...derivedStore,
|
2022-02-24 22:48:54 +01:00
|
|
|
actions: {
|
|
|
|
registerInstance,
|
|
|
|
unregisterInstance,
|
|
|
|
isComponentRegistered,
|
|
|
|
getComponentById,
|
2022-05-17 15:33:12 +02:00
|
|
|
getComponentDefinition,
|
2022-08-10 16:34:00 +02:00
|
|
|
getComponentConstructor,
|
2022-10-19 15:39:28 +02:00
|
|
|
getComponentInstance,
|
2022-08-10 16:34:00 +02:00
|
|
|
registerCustomComponent,
|
2022-02-24 22:48:54 +01:00
|
|
|
},
|
2021-11-26 14:25:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export const componentStore = createComponentStore()
|