budibase/packages/client/src/stores/components.js

148 lines
4.0 KiB
JavaScript

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"
import Router from "../components/Router.svelte"
import * as AppComponents from "../components/app/index.js"
const budibasePrefix = "@budibase/standard-components/"
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 { screen, selectedComponentId } = $builderState
if ($builderState.inBuilder) {
asset = 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 {
customComponentManifest: $store.customComponentManifest,
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)
}
const getComponentDefinition = type => {
if (!type) {
return null
}
// Screenslot is an edge case
if (type === "screenslot") {
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
}
if (type === "screenslot") {
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
}
const registerCustomComponent = ({ Component, schema }) => {
if (!Component || !schema?.schema?.name) {
return
}
store.update(state => {
if (!state.customComponentManifest) {
state.customComponentManifest = {}
}
const componentName = `plugin/${schema.schema.name}/1.0.0`
state.customComponentManifest[componentName] = {
schema,
Component,
}
return state
})
}
return {
...derivedStore,
actions: {
registerInstance,
unregisterInstance,
isComponentRegistered,
getComponentById,
getComponentDefinition,
getComponentConstructor,
registerCustomComponent,
},
}
}
export const componentStore = createComponentStore()