2020-11-13 16:42:32 +01:00
|
|
|
<script>
|
2020-11-20 10:50:10 +01:00
|
|
|
import { getContext, setContext } from "svelte"
|
2021-01-27 16:52:12 +01:00
|
|
|
import { writable, get } from "svelte/store"
|
2020-11-18 20:18:18 +01:00
|
|
|
import * as ComponentLibrary from "@budibase/standard-components"
|
|
|
|
import Router from "./Router.svelte"
|
2021-01-21 11:41:30 +01:00
|
|
|
import { enrichProps, propsAreSame } from "../utils/componentProps"
|
2021-02-05 12:44:33 +01:00
|
|
|
import { builderStore } from "../store"
|
2021-01-27 16:52:12 +01:00
|
|
|
import { hashString } from "../utils/hash"
|
2020-11-13 16:42:32 +01:00
|
|
|
|
2020-11-17 13:08:24 +01:00
|
|
|
export let definition = {}
|
2020-11-13 16:42:32 +01:00
|
|
|
|
2021-01-29 14:22:38 +01:00
|
|
|
// Props that will be passed to the component instance
|
2021-01-21 12:31:45 +01:00
|
|
|
let componentProps
|
2020-11-13 16:42:32 +01:00
|
|
|
|
2021-01-27 16:52:12 +01:00
|
|
|
// Props are hashed when inside the builder preview and used as a key, so that
|
|
|
|
// components fully remount whenever any props change
|
|
|
|
let propsHash = 0
|
|
|
|
|
2021-01-29 14:22:38 +01:00
|
|
|
// Latest timestamp that we started a props update.
|
|
|
|
// Due to enrichment now being async, we need to avoid overwriting newer
|
|
|
|
// props with old ones, depending on how long enrichment takes.
|
|
|
|
let latestUpdateTime
|
|
|
|
|
2021-01-06 11:13:30 +01:00
|
|
|
// Get contexts
|
2021-02-01 19:51:22 +01:00
|
|
|
const context = getContext("context")
|
2020-11-18 20:18:18 +01:00
|
|
|
|
2020-11-25 10:50:51 +01:00
|
|
|
// Create component context
|
|
|
|
const componentStore = writable({})
|
|
|
|
setContext("component", componentStore)
|
2020-11-17 13:08:24 +01:00
|
|
|
|
2020-11-20 10:50:10 +01:00
|
|
|
// Extract component definition info
|
2020-11-25 10:50:51 +01:00
|
|
|
$: constructor = getComponentConstructor(definition._component)
|
2021-01-22 12:08:42 +01:00
|
|
|
$: children = definition._children || []
|
2020-11-24 12:02:10 +01:00
|
|
|
$: id = definition._id
|
2021-06-08 15:19:03 +02:00
|
|
|
$: name = definition._instanceName
|
2021-02-05 12:44:33 +01:00
|
|
|
$: updateComponentProps(definition, $context)
|
2021-01-06 11:13:30 +01:00
|
|
|
$: styles = definition._styles
|
2021-03-01 16:46:05 +01:00
|
|
|
$: transition = definition._transition
|
2021-06-08 09:00:54 +02:00
|
|
|
$: selected =
|
|
|
|
$builderStore.inBuilder &&
|
|
|
|
$builderStore.selectedComponentId === definition._id
|
2021-01-06 11:13:30 +01:00
|
|
|
|
2020-11-24 12:02:10 +01:00
|
|
|
// Update component context
|
2021-01-22 12:08:42 +01:00
|
|
|
$: componentStore.set({
|
|
|
|
id,
|
|
|
|
children: children.length,
|
2021-01-27 16:52:12 +01:00
|
|
|
styles: { ...styles, id },
|
2021-03-10 18:56:16 +01:00
|
|
|
transition,
|
2021-06-08 09:00:54 +02:00
|
|
|
selected,
|
|
|
|
props: componentProps,
|
2021-01-22 12:08:42 +01:00
|
|
|
})
|
2020-11-25 10:50:51 +01:00
|
|
|
|
2021-01-29 14:22:38 +01:00
|
|
|
// Gets the component constructor for the specified component
|
|
|
|
const getComponentConstructor = component => {
|
|
|
|
const split = component?.split("/")
|
|
|
|
const name = split?.[split.length - 1]
|
|
|
|
if (name === "screenslot" && $builderStore.previewType !== "layout") {
|
|
|
|
return Router
|
|
|
|
}
|
|
|
|
return ComponentLibrary[name]
|
|
|
|
}
|
|
|
|
|
|
|
|
// Enriches any string component props using handlebars
|
2021-06-01 15:59:42 +02:00
|
|
|
const updateComponentProps = (definition, context) => {
|
2021-01-29 14:22:38 +01:00
|
|
|
// Record the timestamp so we can reference it after enrichment
|
|
|
|
latestUpdateTime = Date.now()
|
|
|
|
const enrichmentTime = latestUpdateTime
|
|
|
|
|
|
|
|
// Enrich props with context
|
2021-06-01 15:59:42 +02:00
|
|
|
const enrichedProps = enrichProps(definition, context)
|
2021-01-29 14:22:38 +01:00
|
|
|
|
|
|
|
// Abandon this update if a newer update has started
|
|
|
|
if (enrichmentTime !== latestUpdateTime) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the component props.
|
|
|
|
// Most props are deeply compared so that svelte will only trigger reactive
|
|
|
|
// statements on props that have actually changed.
|
|
|
|
if (!enrichedProps) {
|
2021-01-21 12:31:45 +01:00
|
|
|
return
|
|
|
|
}
|
2021-01-27 16:52:12 +01:00
|
|
|
let propsChanged = false
|
2021-01-21 12:31:45 +01:00
|
|
|
if (!componentProps) {
|
|
|
|
componentProps = {}
|
2021-01-27 16:52:12 +01:00
|
|
|
propsChanged = true
|
2021-01-21 12:31:45 +01:00
|
|
|
}
|
2021-01-29 14:22:38 +01:00
|
|
|
Object.keys(enrichedProps).forEach(key => {
|
|
|
|
if (!propsAreSame(enrichedProps[key], componentProps[key])) {
|
2021-01-27 16:52:12 +01:00
|
|
|
propsChanged = true
|
2021-01-29 14:22:38 +01:00
|
|
|
componentProps[key] = enrichedProps[key]
|
2021-01-21 11:41:30 +01:00
|
|
|
}
|
|
|
|
})
|
2021-01-29 14:22:38 +01:00
|
|
|
|
|
|
|
// Update the hash if we're in the builder so we can fully remount this
|
|
|
|
// component
|
2021-01-27 16:52:12 +01:00
|
|
|
if (get(builderStore).inBuilder && propsChanged) {
|
|
|
|
propsHash = hashString(JSON.stringify(componentProps))
|
|
|
|
}
|
2021-01-21 11:41:30 +01:00
|
|
|
}
|
2020-11-13 16:42:32 +01:00
|
|
|
</script>
|
|
|
|
|
2021-06-08 15:19:03 +02:00
|
|
|
<div class={id} data-type="component" data-id={id} data-name={name}>
|
2021-01-27 16:52:12 +01:00
|
|
|
{#key propsHash}
|
2021-06-08 15:19:03 +02:00
|
|
|
{#if constructor && componentProps}
|
2021-06-08 09:00:54 +02:00
|
|
|
<svelte:component this={constructor} {...componentProps}>
|
|
|
|
{#if children.length}
|
|
|
|
{#each children as child (child._id)}
|
|
|
|
<svelte:self definition={child} />
|
|
|
|
{/each}
|
|
|
|
{/if}
|
|
|
|
</svelte:component>
|
2021-06-08 15:19:03 +02:00
|
|
|
{/if}
|
2021-01-27 16:52:12 +01:00
|
|
|
{/key}
|
2021-06-08 15:19:03 +02:00
|
|
|
</div>
|
2021-06-08 09:00:54 +02:00
|
|
|
|
|
|
|
<style>
|
|
|
|
div {
|
|
|
|
display: contents;
|
|
|
|
}
|
|
|
|
</style>
|