Update builder preview to be interactive and improve builder preview experience
This commit is contained in:
parent
6034102fd0
commit
bfebf0226a
|
@ -11,9 +11,6 @@
|
||||||
*, *:before, *:after {
|
*, *:before, *:after {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
* {
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
<script src='/assets/budibase-client.js'></script>
|
<script src='/assets/budibase-client.js'></script>
|
||||||
<script>
|
<script>
|
||||||
|
|
|
@ -120,7 +120,7 @@
|
||||||
value={componentInstance[setting.key] ?? componentInstance[setting.key]?.defaultValue}
|
value={componentInstance[setting.key] ?? componentInstance[setting.key]?.defaultValue}
|
||||||
{componentInstance}
|
{componentInstance}
|
||||||
onChange={val => onChange(setting.key, val)}
|
onChange={val => onChange(setting.key, val)}
|
||||||
props={{ options: setting.options }} />
|
props={{ options: setting.options, placeholder: setting.placeholder }} />
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
{:else}
|
{:else}
|
||||||
|
|
|
@ -9,7 +9,6 @@ export const layout = [
|
||||||
key: "display",
|
key: "display",
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "Block", value: "block" },
|
{ label: "Block", value: "block" },
|
||||||
{ label: "Inline Block", value: "inline-block" },
|
{ label: "Inline Block", value: "inline-block" },
|
||||||
{ label: "Flex", value: "flex" },
|
{ label: "Flex", value: "flex" },
|
||||||
|
@ -37,7 +36,6 @@ export const layout = [
|
||||||
key: "justify-content",
|
key: "justify-content",
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "Flex Start", value: "flex-start" },
|
{ label: "Flex Start", value: "flex-start" },
|
||||||
{ label: "Flex End", value: "flex-end" },
|
{ label: "Flex End", value: "flex-end" },
|
||||||
{ label: "Center", value: "center" },
|
{ label: "Center", value: "center" },
|
||||||
|
@ -51,7 +49,6 @@ export const layout = [
|
||||||
key: "align-items",
|
key: "align-items",
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "Flex Start", value: "flex-start" },
|
{ label: "Flex Start", value: "flex-start" },
|
||||||
{ label: "Flex End", value: "flex-end" },
|
{ label: "Flex End", value: "flex-end" },
|
||||||
{ label: "Center", value: "center" },
|
{ label: "Center", value: "center" },
|
||||||
|
@ -64,7 +61,6 @@ export const layout = [
|
||||||
key: "flex-wrap",
|
key: "flex-wrap",
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "Wrap", value: "wrap" },
|
{ label: "Wrap", value: "wrap" },
|
||||||
{ label: "No wrap", value: "nowrap" },
|
{ label: "No wrap", value: "nowrap" },
|
||||||
],
|
],
|
||||||
|
@ -74,7 +70,6 @@ export const layout = [
|
||||||
key: "gap",
|
key: "gap",
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "None", value: "0px" },
|
{ label: "None", value: "0px" },
|
||||||
{ label: "4px", value: "4px" },
|
{ label: "4px", value: "4px" },
|
||||||
{ label: "8px", value: "8px" },
|
{ label: "8px", value: "8px" },
|
||||||
|
@ -93,7 +88,6 @@ export const margin = [
|
||||||
key: "margin",
|
key: "margin",
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "None", value: "0px" },
|
{ label: "None", value: "0px" },
|
||||||
{ label: "4px", value: "4px" },
|
{ label: "4px", value: "4px" },
|
||||||
{ label: "8px", value: "8px" },
|
{ label: "8px", value: "8px" },
|
||||||
|
@ -113,7 +107,6 @@ export const margin = [
|
||||||
key: "margin-top",
|
key: "margin-top",
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "None", value: "0px" },
|
{ label: "None", value: "0px" },
|
||||||
{ label: "4px", value: "4px" },
|
{ label: "4px", value: "4px" },
|
||||||
{ label: "8px", value: "8px" },
|
{ label: "8px", value: "8px" },
|
||||||
|
@ -133,7 +126,6 @@ export const margin = [
|
||||||
key: "margin-right",
|
key: "margin-right",
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "None", value: "0px" },
|
{ label: "None", value: "0px" },
|
||||||
{ label: "4px", value: "4px" },
|
{ label: "4px", value: "4px" },
|
||||||
{ label: "8px", value: "8px" },
|
{ label: "8px", value: "8px" },
|
||||||
|
@ -153,7 +145,6 @@ export const margin = [
|
||||||
key: "margin-bottom",
|
key: "margin-bottom",
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "None", value: "0px" },
|
{ label: "None", value: "0px" },
|
||||||
{ label: "4px", value: "4px" },
|
{ label: "4px", value: "4px" },
|
||||||
{ label: "8px", value: "8px" },
|
{ label: "8px", value: "8px" },
|
||||||
|
@ -173,7 +164,6 @@ export const margin = [
|
||||||
key: "margin-left",
|
key: "margin-left",
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "None", value: "0px" },
|
{ label: "None", value: "0px" },
|
||||||
{ label: "4px", value: "4px" },
|
{ label: "4px", value: "4px" },
|
||||||
{ label: "8px", value: "8px" },
|
{ label: "8px", value: "8px" },
|
||||||
|
@ -196,7 +186,6 @@ export const padding = [
|
||||||
key: "padding",
|
key: "padding",
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "None", value: "0px" },
|
{ label: "None", value: "0px" },
|
||||||
{ label: "4px", value: "4px" },
|
{ label: "4px", value: "4px" },
|
||||||
{ label: "8px", value: "8px" },
|
{ label: "8px", value: "8px" },
|
||||||
|
@ -214,7 +203,6 @@ export const padding = [
|
||||||
key: "padding-top",
|
key: "padding-top",
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "None", value: "0px" },
|
{ label: "None", value: "0px" },
|
||||||
{ label: "4px", value: "4px" },
|
{ label: "4px", value: "4px" },
|
||||||
{ label: "8px", value: "8px" },
|
{ label: "8px", value: "8px" },
|
||||||
|
@ -232,7 +220,6 @@ export const padding = [
|
||||||
key: "padding-right",
|
key: "padding-right",
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "None", value: "0px" },
|
{ label: "None", value: "0px" },
|
||||||
{ label: "4px", value: "4px" },
|
{ label: "4px", value: "4px" },
|
||||||
{ label: "8px", value: "8px" },
|
{ label: "8px", value: "8px" },
|
||||||
|
@ -250,7 +237,6 @@ export const padding = [
|
||||||
key: "padding-bottom",
|
key: "padding-bottom",
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "None", value: "0px" },
|
{ label: "None", value: "0px" },
|
||||||
{ label: "4px", value: "4px" },
|
{ label: "4px", value: "4px" },
|
||||||
{ label: "8px", value: "8px" },
|
{ label: "8px", value: "8px" },
|
||||||
|
@ -268,7 +254,6 @@ export const padding = [
|
||||||
key: "padding-left",
|
key: "padding-left",
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "None", value: "0px" },
|
{ label: "None", value: "0px" },
|
||||||
{ label: "4px", value: "4px" },
|
{ label: "4px", value: "4px" },
|
||||||
{ label: "8px", value: "8px" },
|
{ label: "8px", value: "8px" },
|
||||||
|
@ -289,7 +274,6 @@ export const size = [
|
||||||
key: "flex",
|
key: "flex",
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "Shrink", value: "0 1 auto" },
|
{ label: "Shrink", value: "0 1 auto" },
|
||||||
{ label: "Grow", value: "1 1 auto" },
|
{ label: "Grow", value: "1 1 auto" },
|
||||||
],
|
],
|
||||||
|
@ -338,7 +322,6 @@ export const position = [
|
||||||
key: "position",
|
key: "position",
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "Static", value: "static" },
|
{ label: "Static", value: "static" },
|
||||||
{ label: "Relative", value: "relative" },
|
{ label: "Relative", value: "relative" },
|
||||||
{ label: "Fixed", value: "fixed" },
|
{ label: "Fixed", value: "fixed" },
|
||||||
|
@ -375,7 +358,6 @@ export const position = [
|
||||||
key: "z-index",
|
key: "z-index",
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "-9999", value: "-9999" },
|
{ label: "-9999", value: "-9999" },
|
||||||
{ label: "-3", value: "-3" },
|
{ label: "-3", value: "-3" },
|
||||||
{ label: "-2", value: "-2" },
|
{ label: "-2", value: "-2" },
|
||||||
|
@ -395,7 +377,6 @@ export const typography = [
|
||||||
key: "font-family",
|
key: "font-family",
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "Arial", value: "Arial" },
|
{ label: "Arial", value: "Arial" },
|
||||||
{ label: "Arial Black", value: "Arial Black" },
|
{ label: "Arial Black", value: "Arial Black" },
|
||||||
{ label: "Cursive", value: "Cursive" },
|
{ label: "Cursive", value: "Cursive" },
|
||||||
|
@ -418,7 +399,6 @@ export const typography = [
|
||||||
key: "font-weight",
|
key: "font-weight",
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "200", value: "200" },
|
{ label: "200", value: "200" },
|
||||||
{ label: "300", value: "300" },
|
{ label: "300", value: "300" },
|
||||||
{ label: "400", value: "400" },
|
{ label: "400", value: "400" },
|
||||||
|
@ -434,7 +414,6 @@ export const typography = [
|
||||||
key: "font-size",
|
key: "font-size",
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "8px", value: "8px" },
|
{ label: "8px", value: "8px" },
|
||||||
{ label: "10px", value: "10px" },
|
{ label: "10px", value: "10px" },
|
||||||
{ label: "12px", value: "12px" },
|
{ label: "12px", value: "12px" },
|
||||||
|
@ -454,7 +433,6 @@ export const typography = [
|
||||||
key: "line-height",
|
key: "line-height",
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "1", value: "1" },
|
{ label: "1", value: "1" },
|
||||||
{ label: "1.25", value: "1.25" },
|
{ label: "1.25", value: "1.25" },
|
||||||
{ label: "1.5", value: "1.5" },
|
{ label: "1.5", value: "1.5" },
|
||||||
|
@ -496,7 +474,6 @@ export const typography = [
|
||||||
key: "text-decoration-line",
|
key: "text-decoration-line",
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "Underline", value: "underline" },
|
{ label: "Underline", value: "underline" },
|
||||||
{ label: "Overline", value: "overline" },
|
{ label: "Overline", value: "overline" },
|
||||||
{ label: "Line-through", value: "line-through" },
|
{ label: "Line-through", value: "line-through" },
|
||||||
|
@ -516,7 +493,6 @@ export const background = [
|
||||||
key: "background-image",
|
key: "background-image",
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "None", value: "none" },
|
{ label: "None", value: "none" },
|
||||||
{
|
{
|
||||||
label: "Warm Flame",
|
label: "Warm Flame",
|
||||||
|
@ -603,7 +579,6 @@ export const border = [
|
||||||
key: "border-radius",
|
key: "border-radius",
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "None", value: "0" },
|
{ label: "None", value: "0" },
|
||||||
{ label: "X Small", value: "0.125rem" },
|
{ label: "X Small", value: "0.125rem" },
|
||||||
{ label: "Small", value: "0.25rem" },
|
{ label: "Small", value: "0.25rem" },
|
||||||
|
@ -619,7 +594,6 @@ export const border = [
|
||||||
key: "border-width",
|
key: "border-width",
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "None", value: "0" },
|
{ label: "None", value: "0" },
|
||||||
{ label: "X Small", value: "0.5px" },
|
{ label: "X Small", value: "0.5px" },
|
||||||
{ label: "Small", value: "1px" },
|
{ label: "Small", value: "1px" },
|
||||||
|
@ -638,7 +612,6 @@ export const border = [
|
||||||
key: "border-style",
|
key: "border-style",
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "None", value: "none" },
|
{ label: "None", value: "none" },
|
||||||
{ label: "Hidden", value: "hidden" },
|
{ label: "Hidden", value: "hidden" },
|
||||||
{ label: "Dotted", value: "dotted" },
|
{ label: "Dotted", value: "dotted" },
|
||||||
|
@ -659,7 +632,6 @@ export const effects = [
|
||||||
key: "opacity",
|
key: "opacity",
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "0", value: "0" },
|
{ label: "0", value: "0" },
|
||||||
{ label: "0.2", value: "0.2" },
|
{ label: "0.2", value: "0.2" },
|
||||||
{ label: "0.4", value: "0.4" },
|
{ label: "0.4", value: "0.4" },
|
||||||
|
@ -673,7 +645,6 @@ export const effects = [
|
||||||
key: "transform",
|
key: "transform",
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "None", value: "0" },
|
{ label: "None", value: "0" },
|
||||||
{ label: "45 deg", value: "rotate(45deg)" },
|
{ label: "45 deg", value: "rotate(45deg)" },
|
||||||
{ label: "90 deg", value: "rotate(90deg)" },
|
{ label: "90 deg", value: "rotate(90deg)" },
|
||||||
|
@ -690,7 +661,6 @@ export const effects = [
|
||||||
key: "box-shadow",
|
key: "box-shadow",
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "None", value: "none" },
|
{ label: "None", value: "none" },
|
||||||
{ label: "X Small", value: "0 1px 2px 0 rgba(0, 0, 0, 0.05)" },
|
{ label: "X Small", value: "0 1px 2px 0 rgba(0, 0, 0, 0.05)" },
|
||||||
{
|
{
|
||||||
|
@ -723,7 +693,6 @@ export const transitions = [
|
||||||
key: "transition-property",
|
key: "transition-property",
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "None", value: "none" },
|
{ label: "None", value: "none" },
|
||||||
{ label: "All", value: "all" },
|
{ label: "All", value: "all" },
|
||||||
{ label: "Background Color", value: "background color" },
|
{ label: "Background Color", value: "background color" },
|
||||||
|
@ -745,7 +714,6 @@ export const transitions = [
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
placeholder: "sec",
|
placeholder: "sec",
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "0.4s", value: "0.4s" },
|
{ label: "0.4s", value: "0.4s" },
|
||||||
{ label: "0.6s", value: "0.6s" },
|
{ label: "0.6s", value: "0.6s" },
|
||||||
{ label: "0.8s", value: "0.8s" },
|
{ label: "0.8s", value: "0.8s" },
|
||||||
|
@ -759,7 +727,6 @@ export const transitions = [
|
||||||
key: "transition-timing-function",
|
key: "transition-timing-function",
|
||||||
control: OptionSelect,
|
control: OptionSelect,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Choose option", value: "" },
|
|
||||||
{ label: "Linear", value: "linear" },
|
{ label: "Linear", value: "linear" },
|
||||||
{ label: "Ease", value: "ease" },
|
{ label: "Ease", value: "ease" },
|
||||||
{ label: "Ease in", value: "ease-in" },
|
{ label: "Ease in", value: "ease-in" },
|
||||||
|
|
|
@ -2,9 +2,14 @@
|
||||||
import { writable } from "svelte/store"
|
import { writable } from "svelte/store"
|
||||||
import { setContext, onMount } from "svelte"
|
import { setContext, onMount } from "svelte"
|
||||||
import Component from "./Component.svelte"
|
import Component from "./Component.svelte"
|
||||||
import NotificationDisplay from './NotificationDisplay.svelte'
|
import NotificationDisplay from "./NotificationDisplay.svelte"
|
||||||
import SDK from "../sdk"
|
import SDK from "../sdk"
|
||||||
import { createDataStore, initialise, screenStore, notificationStore } from "../store"
|
import {
|
||||||
|
createDataStore,
|
||||||
|
initialise,
|
||||||
|
screenStore,
|
||||||
|
builderStore,
|
||||||
|
} from "../store"
|
||||||
|
|
||||||
// Provide contexts
|
// Provide contexts
|
||||||
setContext("sdk", SDK)
|
setContext("sdk", SDK)
|
||||||
|
@ -23,5 +28,5 @@
|
||||||
|
|
||||||
{#if loaded && $screenStore.activeLayout}
|
{#if loaded && $screenStore.activeLayout}
|
||||||
<Component definition={$screenStore.activeLayout.props} />
|
<Component definition={$screenStore.activeLayout.props} />
|
||||||
|
<NotificationDisplay />
|
||||||
{/if}
|
{/if}
|
||||||
<NotificationDisplay />
|
|
|
@ -1,19 +1,23 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext, setContext } from "svelte"
|
import { getContext, setContext } from "svelte"
|
||||||
import { writable } from "svelte/store"
|
import { writable, get } from "svelte/store"
|
||||||
import * as ComponentLibrary from "@budibase/standard-components"
|
import * as ComponentLibrary from "@budibase/standard-components"
|
||||||
import Router from "./Router.svelte"
|
import Router from "./Router.svelte"
|
||||||
import { enrichProps, propsAreSame } from "../utils/componentProps"
|
import { enrichProps, propsAreSame } from "../utils/componentProps"
|
||||||
import { bindingStore, builderStore } from "../store"
|
import { bindingStore, builderStore } from "../store"
|
||||||
|
import { hashString } from "../utils/hash"
|
||||||
|
|
||||||
export let definition = {}
|
export let definition = {}
|
||||||
|
|
||||||
let enrichedProps
|
let enrichedProps
|
||||||
let componentProps
|
let componentProps
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
// Get contexts
|
// Get contexts
|
||||||
const dataContext = getContext("data")
|
const dataContext = getContext("data")
|
||||||
const screenslotContext = getContext("screenslot")
|
|
||||||
|
|
||||||
// Create component context
|
// Create component context
|
||||||
const componentStore = writable({})
|
const componentStore = writable({})
|
||||||
|
@ -27,16 +31,11 @@
|
||||||
$: updateProps(enrichedProps)
|
$: updateProps(enrichedProps)
|
||||||
$: styles = definition._styles
|
$: styles = definition._styles
|
||||||
|
|
||||||
// Allow component selection in the builder preview if we're previewing a
|
|
||||||
// layout, or we're preview a screen and we're inside the screenslot
|
|
||||||
$: allowSelection =
|
|
||||||
$builderStore.previewType === "layout" || screenslotContext
|
|
||||||
|
|
||||||
// Update component context
|
// Update component context
|
||||||
$: componentStore.set({
|
$: componentStore.set({
|
||||||
id,
|
id,
|
||||||
children: children.length,
|
children: children.length,
|
||||||
styles: { ...styles, id, allowSelection },
|
styles: { ...styles, id },
|
||||||
})
|
})
|
||||||
|
|
||||||
// Updates the component props.
|
// Updates the component props.
|
||||||
|
@ -46,14 +45,20 @@
|
||||||
if (!props) {
|
if (!props) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
let propsChanged = false
|
||||||
if (!componentProps) {
|
if (!componentProps) {
|
||||||
componentProps = {}
|
componentProps = {}
|
||||||
|
propsChanged = true
|
||||||
}
|
}
|
||||||
Object.keys(props).forEach(key => {
|
Object.keys(props).forEach(key => {
|
||||||
if (!propsAreSame(props[key], componentProps[key])) {
|
if (!propsAreSame(props[key], componentProps[key])) {
|
||||||
|
propsChanged = true
|
||||||
componentProps[key] = props[key]
|
componentProps[key] = props[key]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
if (get(builderStore).inBuilder && propsChanged) {
|
||||||
|
propsHash = hashString(JSON.stringify(componentProps))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the component constructor for the specified component
|
// Gets the component constructor for the specified component
|
||||||
|
@ -70,22 +75,16 @@
|
||||||
const enrichComponentProps = async (definition, context, bindingStore) => {
|
const enrichComponentProps = async (definition, context, bindingStore) => {
|
||||||
enrichedProps = await enrichProps(definition, context, bindingStore)
|
enrichedProps = await enrichProps(definition, context, bindingStore)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a unique key to let svelte know when to remount components.
|
|
||||||
// If a component is selected we want to remount it every time any props
|
|
||||||
// change.
|
|
||||||
const getChildKey = childId => {
|
|
||||||
const selected = childId === $builderStore.selectedComponentId
|
|
||||||
return selected ? `${childId}-${$builderStore.previewId}` : childId
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if constructor && componentProps}
|
{#if constructor && componentProps}
|
||||||
<svelte:component this={constructor} {...componentProps}>
|
{#key propsHash}
|
||||||
{#if children.length}
|
<svelte:component this={constructor} {...componentProps}>
|
||||||
{#each children as child (getChildKey(child._id))}
|
{#if children.length}
|
||||||
<svelte:self definition={child} />
|
{#each children as child (child._id)}
|
||||||
{/each}
|
<svelte:self definition={child} />
|
||||||
{/if}
|
{/each}
|
||||||
</svelte:component>
|
{/if}
|
||||||
|
</svelte:component>
|
||||||
|
{/key}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -7,9 +7,6 @@
|
||||||
const { styleable } = getContext("sdk")
|
const { styleable } = getContext("sdk")
|
||||||
const component = getContext("component")
|
const component = getContext("component")
|
||||||
|
|
||||||
// Set context flag so components know that we're now inside the screenslot
|
|
||||||
setContext("screenslot", true)
|
|
||||||
|
|
||||||
// Only wrap this as an array to take advantage of svelte keying,
|
// Only wrap this as an array to take advantage of svelte keying,
|
||||||
// to ensure the svelte-spa-router is fully remounted when route config
|
// to ensure the svelte-spa-router is fully remounted when route config
|
||||||
// changes
|
// changes
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
export const hashString = str => {
|
||||||
|
if (!str) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
let hash = 0
|
||||||
|
for (let i = 0; i < str.length; i++) {
|
||||||
|
let char = str.charCodeAt(i)
|
||||||
|
hash = (hash << 5) - hash + char
|
||||||
|
hash = hash & hash // Convert to 32bit integer
|
||||||
|
}
|
||||||
|
return hash
|
||||||
|
}
|
|
@ -1,9 +1,6 @@
|
||||||
import { get } from "svelte/store"
|
import { get } from "svelte/store"
|
||||||
import { builderStore } from "../store"
|
import { builderStore } from "../store"
|
||||||
|
|
||||||
const selectedComponentWidth = 2
|
|
||||||
const selectedComponentColor = "#4285f4"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper to build a CSS string from a style object.
|
* Helper to build a CSS string from a style object.
|
||||||
*/
|
*/
|
||||||
|
@ -23,24 +20,14 @@ const buildStyleString = (styleObject, customStyles) => {
|
||||||
* events for any selectable components (overriding the blanket ban on pointer
|
* events for any selectable components (overriding the blanket ban on pointer
|
||||||
* events in the iframe HTML).
|
* events in the iframe HTML).
|
||||||
*/
|
*/
|
||||||
const addBuilderPreviewStyles = (styleString, componentId, selectable) => {
|
const addBuilderPreviewStyles = (node, styleString, componentId) => {
|
||||||
let str = styleString
|
if (componentId === get(builderStore).selectedComponentId) {
|
||||||
|
const style = window.getComputedStyle(node)
|
||||||
// Apply extra styles if we're in the builder preview
|
const property = style?.display === "table-row" ? "outline" : "border"
|
||||||
const state = get(builderStore)
|
return styleString + `;${property}: 2px solid #4285f4 !important;`
|
||||||
if (state.inBuilder) {
|
} else {
|
||||||
// Allow pointer events and always enable cursor
|
return styleString
|
||||||
if (selectable) {
|
|
||||||
str += ";pointer-events: all !important; cursor: pointer !important;"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Highlighted selected element
|
|
||||||
if (componentId === state.selectedComponentId) {
|
|
||||||
str += `;border: ${selectedComponentWidth}px solid ${selectedComponentColor} !important;`
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return str
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -52,17 +39,9 @@ export const styleable = (node, styles = {}) => {
|
||||||
let applyHoverStyles
|
let applyHoverStyles
|
||||||
let selectComponent
|
let selectComponent
|
||||||
|
|
||||||
// Kill JS even bubbling
|
|
||||||
const blockEvent = event => {
|
|
||||||
event.preventDefault()
|
|
||||||
event.stopPropagation()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates event listeners and applies initial styles
|
// Creates event listeners and applies initial styles
|
||||||
const setupStyles = (newStyles = {}) => {
|
const setupStyles = (newStyles = {}) => {
|
||||||
const componentId = newStyles.id
|
const componentId = newStyles.id
|
||||||
const selectable = !!newStyles.allowSelection
|
|
||||||
const customStyles = newStyles.custom || ""
|
const customStyles = newStyles.custom || ""
|
||||||
const normalStyles = newStyles.normal || {}
|
const normalStyles = newStyles.normal || {}
|
||||||
const hoverStyles = {
|
const hoverStyles = {
|
||||||
|
@ -70,10 +49,9 @@ export const styleable = (node, styles = {}) => {
|
||||||
...(newStyles.hover || {}),
|
...(newStyles.hover || {}),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Applies a style string to a DOM node, enriching it for the builder
|
// Applies a style string to a DOM node
|
||||||
// preview
|
|
||||||
const applyStyles = styleString => {
|
const applyStyles = styleString => {
|
||||||
node.style = addBuilderPreviewStyles(styleString, componentId, selectable)
|
node.style = addBuilderPreviewStyles(node, styleString, componentId)
|
||||||
node.dataset.componentId = componentId
|
node.dataset.componentId = componentId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +69,9 @@ export const styleable = (node, styles = {}) => {
|
||||||
// builder preview
|
// builder preview
|
||||||
selectComponent = event => {
|
selectComponent = event => {
|
||||||
builderStore.actions.selectComponent(componentId)
|
builderStore.actions.selectComponent(componentId)
|
||||||
return blockEvent(event)
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add listeners to toggle hover styles
|
// Add listeners to toggle hover styles
|
||||||
|
@ -101,10 +81,6 @@ export const styleable = (node, styles = {}) => {
|
||||||
// Add builder preview click listener
|
// Add builder preview click listener
|
||||||
if (get(builderStore).inBuilder) {
|
if (get(builderStore).inBuilder) {
|
||||||
node.addEventListener("click", selectComponent, false)
|
node.addEventListener("click", selectComponent, false)
|
||||||
|
|
||||||
// Kill other interaction events
|
|
||||||
node.addEventListener("mousedown", blockEvent)
|
|
||||||
node.addEventListener("mouseup", blockEvent)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply initial normal styles
|
// Apply initial normal styles
|
||||||
|
@ -119,8 +95,6 @@ export const styleable = (node, styles = {}) => {
|
||||||
// Remove builder preview click listener
|
// Remove builder preview click listener
|
||||||
if (get(builderStore).inBuilder) {
|
if (get(builderStore).inBuilder) {
|
||||||
node.removeEventListener("click", selectComponent)
|
node.removeEventListener("click", selectComponent)
|
||||||
node.removeEventListener("mousedown", blockEvent)
|
|
||||||
node.removeEventListener("mouseup", blockEvent)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
|
|
||||||
const { authStore, styleable } = getContext("sdk")
|
const { authStore, styleable, builderStore } = getContext("sdk")
|
||||||
const component = getContext("component")
|
const component = getContext("component")
|
||||||
|
|
||||||
export let buttonText = "Log In"
|
export let buttonText = "Log In"
|
||||||
|
@ -23,6 +23,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const login = async () => {
|
const login = async () => {
|
||||||
|
if ($builderStore.inBuilder) {
|
||||||
|
return
|
||||||
|
}
|
||||||
loading = true
|
loading = true
|
||||||
await authStore.actions.logIn({ email, password })
|
await authStore.actions.logIn({ email, password })
|
||||||
loading = false
|
loading = false
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
|
|
||||||
const { authStore, linkable, styleable } = getContext("sdk")
|
const { authStore, linkable, styleable, builderStore } = getContext("sdk")
|
||||||
const component = getContext("component")
|
const component = getContext("component")
|
||||||
|
|
||||||
export let logoUrl
|
export let logoUrl
|
||||||
|
|
||||||
const logOut = async () => {
|
const logOut = async () => {
|
||||||
|
if ($builderStore.inBuilder) {
|
||||||
|
return
|
||||||
|
}
|
||||||
await authStore.actions.logOut()
|
await authStore.actions.logOut()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -92,16 +92,4 @@
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
{#if $fieldState.error}
|
|
||||||
<div class="error">{$fieldState.error}</div>
|
|
||||||
{/if}
|
|
||||||
</SpectrumField>
|
</SpectrumField>
|
||||||
|
|
||||||
<style>
|
|
||||||
.error {
|
|
||||||
color: var(
|
|
||||||
--spectrum-semantic-negative-color-default,
|
|
||||||
var(--spectrum-global-color-red-500)
|
|
||||||
) !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
const component = getContext("component")
|
const component = getContext("component")
|
||||||
const { labelPosition, formApi } = formContext || {}
|
const { labelPosition, formApi } = formContext || {}
|
||||||
const formField = formApi?.registerField(field) ?? {}
|
const formField = formApi?.registerField(field) ?? {}
|
||||||
const { fieldId } = formField
|
const { fieldId, fieldState } = formField
|
||||||
|
|
||||||
$: labelPositionClass =
|
$: labelPositionClass =
|
||||||
labelPosition === "top" ? "" : `spectrum-FieldLabel--${labelPosition}`
|
labelPosition === "top" ? "" : `spectrum-FieldLabel--${labelPosition}`
|
||||||
|
@ -31,6 +31,20 @@
|
||||||
{/if}
|
{/if}
|
||||||
<div class="spectrum-Form-itemField">
|
<div class="spectrum-Form-itemField">
|
||||||
<slot />
|
<slot />
|
||||||
|
{#if $fieldState.error}
|
||||||
|
<div class="error">{$fieldState.error}</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.error {
|
||||||
|
color: var(
|
||||||
|
--spectrum-semantic-negative-color-default,
|
||||||
|
var(--spectrum-global-color-red-500)
|
||||||
|
);
|
||||||
|
font-size: var(--spectrum-global-dimension-font-size-75);
|
||||||
|
margin-top: var(--spectrum-global-dimension-size-75);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import "@spectrum-css/textfield/dist/index-vars.css"
|
import "@spectrum-css/textfield/dist/index-vars.css"
|
||||||
import "@spectrum-css/actionbutton/dist/index-vars.css"
|
|
||||||
import "@spectrum-css/stepper/dist/index-vars.css"
|
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
import SpectrumField from "./SpectrumField.svelte"
|
import SpectrumField from "./SpectrumField.svelte"
|
||||||
|
|
||||||
|
@ -15,8 +13,6 @@
|
||||||
const formField = formApi?.registerField(field) ?? {}
|
const formField = formApi?.registerField(field) ?? {}
|
||||||
const { fieldApi, fieldState } = formField
|
const { fieldApi, fieldState } = formField
|
||||||
|
|
||||||
$: numeric = type === "number"
|
|
||||||
|
|
||||||
// Update value on blur only
|
// Update value on blur only
|
||||||
const onBlur = event => {
|
const onBlur = event => {
|
||||||
fieldApi.setValue(event.target.value)
|
fieldApi.setValue(event.target.value)
|
||||||
|
@ -24,11 +20,8 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SpectrumField {label} {field}>
|
<SpectrumField {label} {field}>
|
||||||
<div class:spectrum-Stepper={type === 'number'}>
|
<div>
|
||||||
<div
|
<div class="spectrum-Textfield" class:is-invalid={!$fieldState.valid}>
|
||||||
class="spectrum-Textfield"
|
|
||||||
class:spectrum-Stepper-textfield={numeric}
|
|
||||||
class:is-invalid={!$fieldState.valid}>
|
|
||||||
{#if !$fieldState.valid}
|
{#if !$fieldState.valid}
|
||||||
<svg
|
<svg
|
||||||
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Textfield-validationIcon"
|
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Textfield-validationIcon"
|
||||||
|
@ -43,44 +36,7 @@
|
||||||
placeholder={placeholder || ''}
|
placeholder={placeholder || ''}
|
||||||
on:blur={onBlur}
|
on:blur={onBlur}
|
||||||
{type}
|
{type}
|
||||||
class="spectrum-Textfield-input"
|
class="spectrum-Textfield-input" />
|
||||||
class:spectrum-Stepper-input={numeric} />
|
|
||||||
</div>
|
</div>
|
||||||
{#if numeric}
|
|
||||||
<span class="spectrum-Stepper-buttons">
|
|
||||||
<button
|
|
||||||
class="spectrum-ActionButton spectrum-ActionButton--sizeM spectrum-Stepper-stepUp"
|
|
||||||
tabindex="-1">
|
|
||||||
<svg
|
|
||||||
class="spectrum-Icon spectrum-UIIcon-ChevronUp75 spectrum-Stepper-stepUpIcon"
|
|
||||||
focusable="false"
|
|
||||||
aria-hidden="true">
|
|
||||||
<use xlink:href="#spectrum-css-icon-Chevron75" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="spectrum-ActionButton spectrum-ActionButton--sizeM spectrum-Stepper-stepDown"
|
|
||||||
tabindex="-1">
|
|
||||||
<svg
|
|
||||||
class="spectrum-Icon spectrum-UIIcon-ChevronDown75 spectrum-Stepper-stepDownIcon"
|
|
||||||
focusable="false"
|
|
||||||
aria-hidden="true">
|
|
||||||
<use xlink:href="#spectrum-css-icon-Chevron75" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
{/if}
|
|
||||||
{#if $fieldState.error}
|
|
||||||
<div class="error">{$fieldState.error}</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
</SpectrumField>
|
</SpectrumField>
|
||||||
|
|
||||||
<style>
|
|
||||||
.error {
|
|
||||||
color: var(
|
|
||||||
--spectrum-semantic-negative-color-default,
|
|
||||||
var(--spectrum-global-color-red-500)
|
|
||||||
) !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
Loading…
Reference in New Issue