@builder.io/sdk-svelte 0.2.3 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package/--package.json +3 -1
- package/package/CHANGELOG.md +4 -0
- package/package/blocks/columns/columns.svelte +1 -1
- package/package/blocks/symbol/symbol.svelte +1 -1
- package/package/components/render-block/block-styles.svelte +3 -1
- package/package/components/render-block/render-block.helpers.d.ts +0 -1
- package/package/components/render-block/render-block.helpers.js +8 -20
- package/package/components/render-block/render-block.svelte +21 -15
- package/package/components/render-block/render-repeated-block.svelte +3 -2
- package/package/components/render-content/render-content.svelte +15 -8
- package/package/components/render-inlined-styles.svelte +2 -2
- package/package/constants/sdk-version.d.ts +1 -0
- package/package/constants/sdk-version.js +1 -0
- package/package/context/builder.context.d.ts +3 -2
- package/package/context/builder.context.js +3 -2
- package/package/context/types.d.ts +17 -2
- package/package/functions/evaluate.d.ts +4 -3
- package/package/functions/evaluate.js +23 -2
- package/package/functions/evaluate.test.d.ts +1 -0
- package/package/functions/evaluate.test.js +17 -0
- package/package/functions/get-block-actions-handler.d.ts +1 -1
- package/package/functions/get-block-actions-handler.js +3 -1
- package/package/functions/get-block-actions.d.ts +1 -1
- package/package/functions/get-processed-block.d.ts +2 -2
- package/package/functions/get-processed-block.js +16 -4
- package/package/functions/get-processed-block.test.js +3 -1
- package/package/scripts/init-editing.js +2 -0
- package/package.json +1 -4
- package/src/blocks/columns/columns.svelte +1 -5
- package/src/blocks/symbol/symbol.svelte +1 -1
- package/src/components/render-block/block-styles.svelte +3 -1
- package/src/components/render-block/render-block.helpers.ts +9 -31
- package/src/components/render-block/render-block.svelte +20 -15
- package/src/components/render-block/render-repeated-block.svelte +3 -2
- package/src/components/render-content/render-content.svelte +15 -8
- package/src/components/render-inlined-styles.svelte +2 -2
- package/src/constants/sdk-version.ts +1 -0
- package/src/context/builder.context.ts +3 -2
- package/src/context/types.ts +17 -2
- package/src/functions/evaluate.test.ts +21 -0
- package/src/functions/evaluate.ts +46 -4
- package/src/functions/get-block-actions-handler.ts +7 -2
- package/src/functions/get-block-actions.ts +4 -1
- package/src/functions/get-processed-block.test.ts +3 -1
- package/src/functions/get-processed-block.ts +28 -6
- package/src/scripts/init-editing.ts +2 -0
package/package/--package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@builder.io/sdk-svelte",
|
|
3
3
|
"description": "Builder.io SDK for Svelte",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"package",
|
|
@@ -81,10 +81,12 @@
|
|
|
81
81
|
"./components/render-inlined-styles.svelte": "./components/render-inlined-styles.svelte",
|
|
82
82
|
"./constants/builder-registered-components": "./constants/builder-registered-components.js",
|
|
83
83
|
"./constants/device-sizes": "./constants/device-sizes.js",
|
|
84
|
+
"./constants/sdk-version": "./constants/sdk-version.js",
|
|
84
85
|
"./constants/target": "./constants/target.js",
|
|
85
86
|
"./context/builder.context": "./context/builder.context.js",
|
|
86
87
|
"./context/types": "./context/types.js",
|
|
87
88
|
"./functions/camel-to-kebab-case": "./functions/camel-to-kebab-case.js",
|
|
89
|
+
"./functions/evaluate.test": "./functions/evaluate.test.js",
|
|
88
90
|
"./functions/evaluate": "./functions/evaluate.js",
|
|
89
91
|
"./functions/event-handler-name": "./functions/event-handler-name.js",
|
|
90
92
|
"./functions/extract-text-styles": "./functions/extract-text-styles.js",
|
package/package/CHANGELOG.md
CHANGED
|
@@ -104,7 +104,7 @@ $: onUpdateFn_0(...[symbol]);
|
|
|
104
104
|
customComponents={Object.values(builderContext.registeredComponents)}
|
|
105
105
|
data={{
|
|
106
106
|
...symbol?.data,
|
|
107
|
-
...builderContext.
|
|
107
|
+
...builderContext.localState,
|
|
108
108
|
...contentToUse?.data?.state,
|
|
109
109
|
}}
|
|
110
110
|
model={symbol?.model}
|
|
@@ -11,7 +11,9 @@ export let context;
|
|
|
11
11
|
$: useBlock = () => {
|
|
12
12
|
return getProcessedBlock({
|
|
13
13
|
block: block,
|
|
14
|
-
|
|
14
|
+
localState: context.localState,
|
|
15
|
+
rootState: context.rootState,
|
|
16
|
+
rootSetState: context.rootSetState,
|
|
15
17
|
context: context.context,
|
|
16
18
|
shouldEvaluateBindings: true,
|
|
17
19
|
});
|
|
@@ -27,7 +27,9 @@ export const isEmptyHtmlElement = (tagName) => {
|
|
|
27
27
|
export const getComponent = ({ block, context, }) => {
|
|
28
28
|
const componentName = getProcessedBlock({
|
|
29
29
|
block,
|
|
30
|
-
|
|
30
|
+
localState: context.localState,
|
|
31
|
+
rootState: context.rootState,
|
|
32
|
+
rootSetState: context.rootSetState,
|
|
31
33
|
context: context.context,
|
|
32
34
|
shouldEvaluateBindings: false,
|
|
33
35
|
}).component?.name;
|
|
@@ -57,7 +59,9 @@ export const getRepeatItemData = ({ block, context, }) => {
|
|
|
57
59
|
}
|
|
58
60
|
const itemsArray = evaluate({
|
|
59
61
|
code: repeat.collection,
|
|
60
|
-
|
|
62
|
+
localState: context.localState,
|
|
63
|
+
rootState: context.rootState,
|
|
64
|
+
rootSetState: context.rootSetState,
|
|
61
65
|
context: context.context,
|
|
62
66
|
});
|
|
63
67
|
if (!Array.isArray(itemsArray)) {
|
|
@@ -68,8 +72,8 @@ export const getRepeatItemData = ({ block, context, }) => {
|
|
|
68
72
|
const repeatArray = itemsArray.map((item, index) => ({
|
|
69
73
|
context: {
|
|
70
74
|
...context,
|
|
71
|
-
|
|
72
|
-
...context.
|
|
75
|
+
localState: {
|
|
76
|
+
...context.localState,
|
|
73
77
|
$index: index,
|
|
74
78
|
$item: item,
|
|
75
79
|
[itemNameToUse]: item,
|
|
@@ -80,19 +84,3 @@ export const getRepeatItemData = ({ block, context, }) => {
|
|
|
80
84
|
}));
|
|
81
85
|
return repeatArray;
|
|
82
86
|
};
|
|
83
|
-
export const getProxyState = (context) => {
|
|
84
|
-
if (typeof Proxy === 'undefined') {
|
|
85
|
-
console.error('no Proxy available in this environment, cannot proxy state.');
|
|
86
|
-
return context.state;
|
|
87
|
-
}
|
|
88
|
-
const useState = new Proxy(context.state, {
|
|
89
|
-
set: (obj, prop, value) => {
|
|
90
|
-
// set the value on the state object, so that the event handler instantly gets the update.
|
|
91
|
-
obj[prop] = value;
|
|
92
|
-
// set the value in the context, so that the rest of the app gets the update.
|
|
93
|
-
context.setState?.(obj);
|
|
94
|
-
return true;
|
|
95
|
-
},
|
|
96
|
-
});
|
|
97
|
-
return useState;
|
|
98
|
-
};
|
|
@@ -5,7 +5,7 @@ import { getBlockComponentOptions } from "../../functions/get-block-component-op
|
|
|
5
5
|
import { getBlockProperties } from "../../functions/get-block-properties.js";
|
|
6
6
|
import { getProcessedBlock } from "../../functions/get-processed-block.js";
|
|
7
7
|
import BlockStyles from "./block-styles.svelte";
|
|
8
|
-
import { getComponent,
|
|
8
|
+
import { getComponent, getRepeatItemData, isEmptyHtmlElement, } from "./render-block.helpers.js";
|
|
9
9
|
import RenderRepeatedBlock from "./render-repeated-block.svelte";
|
|
10
10
|
import { TARGET } from "../../constants/target.js";
|
|
11
11
|
import { extractTextStyles } from "../../functions/extract-text-styles.js";
|
|
@@ -29,12 +29,20 @@ const setAttrs = (node, attrs = {}) => {
|
|
|
29
29
|
};
|
|
30
30
|
export let block;
|
|
31
31
|
export let context;
|
|
32
|
+
$: repeatItem = () => {
|
|
33
|
+
return getRepeatItemData({
|
|
34
|
+
block: block,
|
|
35
|
+
context: context,
|
|
36
|
+
});
|
|
37
|
+
};
|
|
32
38
|
$: useBlock = () => {
|
|
33
|
-
return
|
|
39
|
+
return repeatItem()
|
|
34
40
|
? block
|
|
35
41
|
: getProcessedBlock({
|
|
36
42
|
block: block,
|
|
37
|
-
|
|
43
|
+
localState: context.localState,
|
|
44
|
+
rootState: context.rootState,
|
|
45
|
+
rootSetState: context.rootSetState,
|
|
38
46
|
context: context.context,
|
|
39
47
|
shouldEvaluateBindings: true,
|
|
40
48
|
});
|
|
@@ -51,7 +59,9 @@ $: canShowBlock = () => {
|
|
|
51
59
|
$: actions = () => {
|
|
52
60
|
return getBlockActions({
|
|
53
61
|
block: useBlock(),
|
|
54
|
-
|
|
62
|
+
rootState: context.rootState,
|
|
63
|
+
rootSetState: context.rootSetState,
|
|
64
|
+
localState: context.localState,
|
|
55
65
|
context: context.context,
|
|
56
66
|
});
|
|
57
67
|
};
|
|
@@ -77,7 +87,7 @@ $: childrenWithoutParentComponent = () => {
|
|
|
77
87
|
* NOTE: We make sure not to render this if `repeatItemData` is non-null, because that means we are rendering an array of
|
|
78
88
|
* blocks, and the children will be repeated within those blocks.
|
|
79
89
|
*/
|
|
80
|
-
const shouldRenderChildrenOutsideRef = !component?.component && !
|
|
90
|
+
const shouldRenderChildrenOutsideRef = !component?.component && !repeatItem();
|
|
81
91
|
return shouldRenderChildrenOutsideRef ? useBlock().children ?? [] : [];
|
|
82
92
|
};
|
|
83
93
|
$: childrenContext = () => {
|
|
@@ -94,10 +104,11 @@ $: childrenContext = () => {
|
|
|
94
104
|
return {
|
|
95
105
|
apiKey: context.apiKey,
|
|
96
106
|
apiVersion: context.apiVersion,
|
|
97
|
-
|
|
107
|
+
localState: context.localState,
|
|
108
|
+
rootState: context.rootState,
|
|
109
|
+
rootSetState: context.rootSetState,
|
|
98
110
|
content: context.content,
|
|
99
111
|
context: context.context,
|
|
100
|
-
setState: context.setState,
|
|
101
112
|
registeredComponents: context.registeredComponents,
|
|
102
113
|
inheritedStyles: getInheritedTextStyles(),
|
|
103
114
|
};
|
|
@@ -128,12 +139,7 @@ let component = getComponent({
|
|
|
128
139
|
block: block,
|
|
129
140
|
context: context,
|
|
130
141
|
});
|
|
131
|
-
let repeatItemData = getRepeatItemData({
|
|
132
|
-
block: block,
|
|
133
|
-
context: context,
|
|
134
|
-
});
|
|
135
142
|
let tag = block.tagName || "div";
|
|
136
|
-
let proxyState = getProxyState(context);
|
|
137
143
|
</script>
|
|
138
144
|
|
|
139
145
|
{#if canShowBlock()}
|
|
@@ -142,13 +148,13 @@ let proxyState = getProxyState(context);
|
|
|
142
148
|
<svelte:element this={tag} {...attributes()} use:setAttrs={actions()} />
|
|
143
149
|
{/if}
|
|
144
150
|
|
|
145
|
-
{#if !isEmptyHtmlElement(tag) &&
|
|
146
|
-
{#each
|
|
151
|
+
{#if !isEmptyHtmlElement(tag) && repeatItem()}
|
|
152
|
+
{#each repeatItem() as data, index (index)}
|
|
147
153
|
<RenderRepeatedBlock repeatContext={data.context} block={data.block} />
|
|
148
154
|
{/each}
|
|
149
155
|
{/if}
|
|
150
156
|
|
|
151
|
-
{#if !isEmptyHtmlElement(tag) && !
|
|
157
|
+
{#if !isEmptyHtmlElement(tag) && !repeatItem()}
|
|
152
158
|
<svelte:element this={tag} {...attributes()} use:setAttrs={actions()}>
|
|
153
159
|
<RenderComponent {...renderComponentProps()} />
|
|
154
160
|
|
|
@@ -16,8 +16,9 @@ export let block;
|
|
|
16
16
|
export let repeatContext;
|
|
17
17
|
setContext(BuilderContext.key, {
|
|
18
18
|
content: repeatContext.content,
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
localState: repeatContext.localState,
|
|
20
|
+
rootState: repeatContext.rootState,
|
|
21
|
+
rootSetState: repeatContext.rootSetState,
|
|
21
22
|
context: repeatContext.context,
|
|
22
23
|
apiKey: repeatContext.apiKey,
|
|
23
24
|
registeredComponents: repeatContext.registeredComponents,
|
|
@@ -51,8 +51,8 @@ function setBreakpoints(breakpoints) {
|
|
|
51
51
|
},
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
|
-
function
|
|
55
|
-
contentState =
|
|
54
|
+
function contentSetState(newRootState) {
|
|
55
|
+
contentState = newRootState;
|
|
56
56
|
}
|
|
57
57
|
function processMessage(event) {
|
|
58
58
|
const { data } = event;
|
|
@@ -97,7 +97,9 @@ function evaluateJsCode() {
|
|
|
97
97
|
evaluate({
|
|
98
98
|
code: jsCode,
|
|
99
99
|
context: context || {},
|
|
100
|
-
|
|
100
|
+
localState: undefined,
|
|
101
|
+
rootState: contentState,
|
|
102
|
+
rootSetState: contentSetState,
|
|
101
103
|
});
|
|
102
104
|
}
|
|
103
105
|
}
|
|
@@ -123,7 +125,9 @@ function evalExpression(expression) {
|
|
|
123
125
|
return expression.replace(/{{([^}]+)}}/g, (_match, group) => evaluate({
|
|
124
126
|
code: group,
|
|
125
127
|
context: context || {},
|
|
126
|
-
|
|
128
|
+
localState: undefined,
|
|
129
|
+
rootState: contentState,
|
|
130
|
+
rootSetState: contentSetState,
|
|
127
131
|
}));
|
|
128
132
|
}
|
|
129
133
|
function handleRequest({ url, key }) {
|
|
@@ -134,7 +138,7 @@ function handleRequest({ url, key }) {
|
|
|
134
138
|
...contentState,
|
|
135
139
|
[key]: json,
|
|
136
140
|
};
|
|
137
|
-
|
|
141
|
+
contentSetState(newState);
|
|
138
142
|
})
|
|
139
143
|
.catch((err) => {
|
|
140
144
|
console.error("error fetching dynamic data", url, err);
|
|
@@ -287,11 +291,14 @@ setContext(builderContext.key, {
|
|
|
287
291
|
get content() {
|
|
288
292
|
return useContent;
|
|
289
293
|
},
|
|
290
|
-
get
|
|
294
|
+
get localState() {
|
|
295
|
+
return undefined;
|
|
296
|
+
},
|
|
297
|
+
get rootState() {
|
|
291
298
|
return contentState;
|
|
292
299
|
},
|
|
293
|
-
get
|
|
294
|
-
return
|
|
300
|
+
get rootSetState() {
|
|
301
|
+
return TARGET === "qwik" ? undefined : contentSetState;
|
|
295
302
|
},
|
|
296
303
|
get context() {
|
|
297
304
|
return context || {};
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
<script>import { TARGET } from "../constants/target.js";
|
|
4
4
|
export let styles;
|
|
5
5
|
$: tag = () => {
|
|
6
|
-
// NOTE: we have to
|
|
6
|
+
// NOTE: we have to obfuscate the name of the tag due to a limitation in the svelte-preprocessor plugin.
|
|
7
7
|
// https://github.com/sveltejs/vite-plugin-svelte/issues/315#issuecomment-1109000027
|
|
8
|
-
return "sty" + "le";
|
|
8
|
+
return ("sty" + "le");
|
|
9
9
|
};
|
|
10
10
|
$: injectedStyleScript = () => {
|
|
11
11
|
return `<${tag()}>${styles}</${tag()}>`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const SDK_VERSION = "0.3.1";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const SDK_VERSION = "0.3.1";
|
|
@@ -11,8 +11,23 @@ export type BuilderRenderContext = Record<string, unknown>;
|
|
|
11
11
|
export interface BuilderContextInterface {
|
|
12
12
|
content: Nullable<BuilderContent>;
|
|
13
13
|
context: BuilderRenderContext;
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
/**
|
|
15
|
+
* The state of the application.
|
|
16
|
+
*
|
|
17
|
+
* NOTE: see `localState` below to understand how it is different from `rootState`.
|
|
18
|
+
*/
|
|
19
|
+
rootState: BuilderRenderState;
|
|
20
|
+
/**
|
|
21
|
+
* Some frameworks have a `setState` function which needs to be invoked to notify
|
|
22
|
+
* the framework of state change. (other frameworks don't in which case it is `undefined')
|
|
23
|
+
*/
|
|
24
|
+
rootSetState: ((rootState: BuilderRenderState) => void) | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* The local state of the current component. This is different from `rootState` in that
|
|
27
|
+
* it can be a child state created by a repeater containing local state.
|
|
28
|
+
* The `rootState` is where all of the state mutations are actually stored.
|
|
29
|
+
*/
|
|
30
|
+
localState: BuilderRenderState | undefined;
|
|
16
31
|
apiKey: string | null;
|
|
17
32
|
apiVersion: ApiVersion | undefined;
|
|
18
33
|
registeredComponents: RegisteredComponents;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { BuilderContextInterface } from '../context/types.js';
|
|
2
|
-
export declare function evaluate({ code, context,
|
|
1
|
+
import type { BuilderContextInterface, BuilderRenderState } from '../context/types.js';
|
|
2
|
+
export declare function evaluate({ code, context, localState, rootState, rootSetState, event, isExpression, }: {
|
|
3
3
|
code: string;
|
|
4
4
|
event?: Event;
|
|
5
5
|
isExpression?: boolean;
|
|
6
|
-
} & Pick<BuilderContextInterface, '
|
|
6
|
+
} & Pick<BuilderContextInterface, 'localState' | 'context' | 'rootState' | 'rootSetState'>): any;
|
|
7
|
+
export declare function flattenState(rootState: Record<string | symbol, any>, localState: Record<string | symbol, any> | undefined, rootSetState: ((rootState: BuilderRenderState) => void) | undefined): BuilderRenderState;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isBrowser } from './is-browser.js';
|
|
2
2
|
import { isEditing } from './is-editing.js';
|
|
3
|
-
export function evaluate({ code, context,
|
|
3
|
+
export function evaluate({ code, context, localState, rootState, rootSetState, event, isExpression = true, }) {
|
|
4
4
|
if (code === '') {
|
|
5
5
|
console.warn('Skipping evaluation of empty code block.');
|
|
6
6
|
return;
|
|
@@ -20,9 +20,30 @@ export function evaluate({ code, context, state, event, isExpression = true, })
|
|
|
20
20
|
code.trim().startsWith('return '));
|
|
21
21
|
const useCode = useReturn ? `return (${code});` : code;
|
|
22
22
|
try {
|
|
23
|
-
return new Function('builder', 'Builder' /* <- legacy */, 'state', 'context', 'event', useCode)(builder, builder,
|
|
23
|
+
return new Function('builder', 'Builder' /* <- legacy */, 'state', 'context', 'event', useCode)(builder, builder, flattenState(rootState, localState, rootSetState), context, event);
|
|
24
24
|
}
|
|
25
25
|
catch (e) {
|
|
26
26
|
console.warn('Builder custom code error: \n While Evaluating: \n ', useCode, '\n', e);
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
+
export function flattenState(rootState, localState, rootSetState) {
|
|
30
|
+
if (rootState === localState) {
|
|
31
|
+
throw new Error('rootState === localState');
|
|
32
|
+
}
|
|
33
|
+
return new Proxy(rootState, {
|
|
34
|
+
get: (_, prop) => {
|
|
35
|
+
if (localState && prop in localState) {
|
|
36
|
+
return localState[prop];
|
|
37
|
+
}
|
|
38
|
+
return rootState[prop];
|
|
39
|
+
},
|
|
40
|
+
set: (_, prop, value) => {
|
|
41
|
+
if (localState && prop in localState) {
|
|
42
|
+
throw new Error('Writing to local state is not allowed as it is read-only.');
|
|
43
|
+
}
|
|
44
|
+
rootState[prop] = value;
|
|
45
|
+
rootSetState?.(rootState);
|
|
46
|
+
return true;
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { flattenState } from './evaluate';
|
|
2
|
+
describe('flatten state', () => {
|
|
3
|
+
it('should behave normally when no PROTO_STATE', () => {
|
|
4
|
+
const localState = {};
|
|
5
|
+
const rootState = { foo: 'bar' };
|
|
6
|
+
const flattened = flattenState(rootState, localState, undefined);
|
|
7
|
+
expect(flattened.foo).toEqual('bar');
|
|
8
|
+
flattened.foo = 'baz';
|
|
9
|
+
expect(rootState.foo).toEqual('baz');
|
|
10
|
+
});
|
|
11
|
+
it('should shadow write ', () => {
|
|
12
|
+
const rootState = { foo: 'foo' };
|
|
13
|
+
const localState = { foo: 'baz' };
|
|
14
|
+
const flattened = flattenState(rootState, localState, undefined);
|
|
15
|
+
expect(() => (flattened.foo = 'bar')).toThrow('Writing to local state is not allowed as it is read-only.');
|
|
16
|
+
});
|
|
17
|
+
});
|
|
@@ -2,7 +2,7 @@ import type { BuilderContextInterface } from '../context/types.js';
|
|
|
2
2
|
import type { BuilderBlock } from '../types/builder-block.js';
|
|
3
3
|
type Options = {
|
|
4
4
|
block: BuilderBlock;
|
|
5
|
-
} & Pick<BuilderContextInterface, '
|
|
5
|
+
} & Pick<BuilderContextInterface, 'localState' | 'context' | 'rootState' | 'rootSetState'>;
|
|
6
6
|
type EventHandler = (event: Event) => any;
|
|
7
7
|
export declare const createEventHandler: (value: string, options: Options) => EventHandler;
|
|
8
8
|
export {};
|
|
@@ -2,7 +2,9 @@ import { evaluate } from './evaluate.js';
|
|
|
2
2
|
export const createEventHandler = (value, options) => (event) => evaluate({
|
|
3
3
|
code: value,
|
|
4
4
|
context: options.context,
|
|
5
|
-
|
|
5
|
+
localState: options.localState,
|
|
6
|
+
rootState: options.rootState,
|
|
7
|
+
rootSetState: options.rootSetState,
|
|
6
8
|
event,
|
|
7
9
|
isExpression: false,
|
|
8
10
|
});
|
|
@@ -5,5 +5,5 @@ type Actions = {
|
|
|
5
5
|
};
|
|
6
6
|
export declare function getBlockActions(options: {
|
|
7
7
|
block: BuilderBlock;
|
|
8
|
-
} & Pick<BuilderContextInterface, '
|
|
8
|
+
} & Pick<BuilderContextInterface, 'localState' | 'context' | 'rootState' | 'rootSetState'>): Actions;
|
|
9
9
|
export {};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { BuilderContextInterface } from '../context/types.js';
|
|
2
2
|
import type { BuilderBlock } from '../types/builder-block.js';
|
|
3
|
-
export declare function getProcessedBlock({ block, context, shouldEvaluateBindings,
|
|
3
|
+
export declare function getProcessedBlock({ block, context, shouldEvaluateBindings, localState, rootState, rootSetState, }: {
|
|
4
4
|
block: BuilderBlock;
|
|
5
5
|
/**
|
|
6
6
|
* In some cases, we want to avoid evaluating bindings and only want framework-specific block transformation. It is
|
|
7
7
|
* also sometimes too early to consider bindings, e.g. when we might be looking at a repeated block.
|
|
8
8
|
*/
|
|
9
9
|
shouldEvaluateBindings: boolean;
|
|
10
|
-
} & Pick<BuilderContextInterface, '
|
|
10
|
+
} & Pick<BuilderContextInterface, 'localState' | 'context' | 'rootState' | 'rootSetState'>): BuilderBlock;
|
|
@@ -2,7 +2,7 @@ import { evaluate } from './evaluate.js';
|
|
|
2
2
|
import { fastClone } from './fast-clone.js';
|
|
3
3
|
import { set } from './set.js';
|
|
4
4
|
import { transformBlock } from './transform-block.js';
|
|
5
|
-
const evaluateBindings = ({ block, context,
|
|
5
|
+
const evaluateBindings = ({ block, context, localState, rootState, rootSetState, }) => {
|
|
6
6
|
if (!block.bindings) {
|
|
7
7
|
return block;
|
|
8
8
|
}
|
|
@@ -14,15 +14,27 @@ const evaluateBindings = ({ block, context, state, }) => {
|
|
|
14
14
|
};
|
|
15
15
|
for (const binding in block.bindings) {
|
|
16
16
|
const expression = block.bindings[binding];
|
|
17
|
-
const value = evaluate({
|
|
17
|
+
const value = evaluate({
|
|
18
|
+
code: expression,
|
|
19
|
+
localState,
|
|
20
|
+
rootState,
|
|
21
|
+
rootSetState,
|
|
22
|
+
context,
|
|
23
|
+
});
|
|
18
24
|
set(copied, binding, value);
|
|
19
25
|
}
|
|
20
26
|
return copied;
|
|
21
27
|
};
|
|
22
|
-
export function getProcessedBlock({ block, context, shouldEvaluateBindings,
|
|
28
|
+
export function getProcessedBlock({ block, context, shouldEvaluateBindings, localState, rootState, rootSetState, }) {
|
|
23
29
|
const transformedBlock = transformBlock(block);
|
|
24
30
|
if (shouldEvaluateBindings) {
|
|
25
|
-
return evaluateBindings({
|
|
31
|
+
return evaluateBindings({
|
|
32
|
+
block: transformedBlock,
|
|
33
|
+
localState,
|
|
34
|
+
rootState,
|
|
35
|
+
rootSetState,
|
|
36
|
+
context,
|
|
37
|
+
});
|
|
26
38
|
}
|
|
27
39
|
else {
|
|
28
40
|
return transformedBlock;
|
|
@@ -19,7 +19,9 @@ test('Can process bindings', () => {
|
|
|
19
19
|
const processed = getProcessedBlock({
|
|
20
20
|
block,
|
|
21
21
|
context: {},
|
|
22
|
-
|
|
22
|
+
rootState: { test: 'hello' },
|
|
23
|
+
rootSetState: undefined,
|
|
24
|
+
localState: undefined,
|
|
23
25
|
shouldEvaluateBindings: true,
|
|
24
26
|
});
|
|
25
27
|
expect(processed).not.toEqual(block);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SDK_VERSION } from '../constants/sdk-version.js';
|
|
1
2
|
import { TARGET } from '../constants/target.js';
|
|
2
3
|
import { isBrowser } from '../functions/is-browser.js';
|
|
3
4
|
import { register } from '../functions/register.js';
|
|
@@ -32,6 +33,7 @@ export const setupBrowserForEditing = (options = {}) => {
|
|
|
32
33
|
type: 'builder.sdkInfo',
|
|
33
34
|
data: {
|
|
34
35
|
target: TARGET,
|
|
36
|
+
version: SDK_VERSION,
|
|
35
37
|
// TODO: compile these in
|
|
36
38
|
// type: process.env.SDK_TYPE,
|
|
37
39
|
// version: process.env.SDK_VERSION,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@builder.io/sdk-svelte",
|
|
3
3
|
"description": "Builder.io SDK for Svelte",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"package",
|
|
@@ -28,9 +28,6 @@
|
|
|
28
28
|
},
|
|
29
29
|
"types": "./package/index.d.ts",
|
|
30
30
|
"scripts": {
|
|
31
|
-
"release:patch": "yarn run build && npm version patch && npm publish",
|
|
32
|
-
"release:minor": "yarn run build && npm version minor && npm publish",
|
|
33
|
-
"release:dev": "yarn run build && npm version prerelease && npm publish --tag dev",
|
|
34
31
|
"dev": "vite dev",
|
|
35
32
|
"build": "svelte-kit sync && svelte-package",
|
|
36
33
|
"build:watch": "svelte-kit sync && svelte-package --watch",
|
|
@@ -11,12 +11,8 @@
|
|
|
11
11
|
export interface ColumnProps {
|
|
12
12
|
columns?: Column[];
|
|
13
13
|
builderBlock: BuilderBlock;
|
|
14
|
-
|
|
15
|
-
// TODO: Implement this when support for dynamic CSS lands
|
|
16
14
|
space?: number;
|
|
17
|
-
// TODO: Implement this when support for dynamic CSS lands
|
|
18
15
|
stackColumnsAt?: StackColumnsAt;
|
|
19
|
-
// TODO: Implement this when support for dynamic CSS lands
|
|
20
16
|
reverseColumnsWhenStacked?: boolean;
|
|
21
17
|
}
|
|
22
18
|
</script>
|
|
@@ -84,7 +80,7 @@
|
|
|
84
80
|
} as any as Dictionary<string>;
|
|
85
81
|
}
|
|
86
82
|
const width = getColumnCssWidth(index);
|
|
87
|
-
const gutterPixels = `${
|
|
83
|
+
const gutterPixels = `${gutter}px`;
|
|
88
84
|
const mobileWidth = "100%";
|
|
89
85
|
const mobileMarginLeft = 0;
|
|
90
86
|
return {
|
|
@@ -24,7 +24,9 @@
|
|
|
24
24
|
$: useBlock = () => {
|
|
25
25
|
return getProcessedBlock({
|
|
26
26
|
block: block,
|
|
27
|
-
|
|
27
|
+
localState: context.localState,
|
|
28
|
+
rootState: context.rootState,
|
|
29
|
+
rootSetState: context.rootSetState,
|
|
28
30
|
context: context.context,
|
|
29
31
|
shouldEvaluateBindings: true,
|
|
30
32
|
});
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
BuilderContextInterface,
|
|
3
|
-
BuilderRenderState,
|
|
4
|
-
} from '../../context/types';
|
|
1
|
+
import type { BuilderContextInterface } from '../../context/types';
|
|
5
2
|
import { evaluate } from '../../functions/evaluate';
|
|
6
3
|
import { getProcessedBlock } from '../../functions/get-processed-block';
|
|
7
4
|
import type { BuilderBlock } from '../../types/builder-block';
|
|
@@ -44,7 +41,9 @@ export const getComponent = ({
|
|
|
44
41
|
}) => {
|
|
45
42
|
const componentName = getProcessedBlock({
|
|
46
43
|
block,
|
|
47
|
-
|
|
44
|
+
localState: context.localState,
|
|
45
|
+
rootState: context.rootState,
|
|
46
|
+
rootSetState: context.rootSetState,
|
|
48
47
|
context: context.context,
|
|
49
48
|
shouldEvaluateBindings: false,
|
|
50
49
|
}).component?.name;
|
|
@@ -85,7 +84,9 @@ export const getRepeatItemData = ({
|
|
|
85
84
|
|
|
86
85
|
const itemsArray = evaluate({
|
|
87
86
|
code: repeat.collection,
|
|
88
|
-
|
|
87
|
+
localState: context.localState,
|
|
88
|
+
rootState: context.rootState,
|
|
89
|
+
rootSetState: context.rootSetState,
|
|
89
90
|
context: context.context,
|
|
90
91
|
});
|
|
91
92
|
|
|
@@ -100,8 +101,8 @@ export const getRepeatItemData = ({
|
|
|
100
101
|
const repeatArray = itemsArray.map<RepeatData>((item, index) => ({
|
|
101
102
|
context: {
|
|
102
103
|
...context,
|
|
103
|
-
|
|
104
|
-
...context.
|
|
104
|
+
localState: {
|
|
105
|
+
...context.localState,
|
|
105
106
|
$index: index,
|
|
106
107
|
$item: item,
|
|
107
108
|
[itemNameToUse]: item,
|
|
@@ -113,26 +114,3 @@ export const getRepeatItemData = ({
|
|
|
113
114
|
|
|
114
115
|
return repeatArray;
|
|
115
116
|
};
|
|
116
|
-
|
|
117
|
-
export const getProxyState = (
|
|
118
|
-
context: BuilderContextInterface
|
|
119
|
-
): BuilderContextInterface['state'] => {
|
|
120
|
-
if (typeof Proxy === 'undefined') {
|
|
121
|
-
console.error(
|
|
122
|
-
'no Proxy available in this environment, cannot proxy state.'
|
|
123
|
-
);
|
|
124
|
-
return context.state;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const useState = new Proxy(context.state, {
|
|
128
|
-
set: (obj, prop: keyof BuilderRenderState, value) => {
|
|
129
|
-
// set the value on the state object, so that the event handler instantly gets the update.
|
|
130
|
-
obj[prop] = value;
|
|
131
|
-
|
|
132
|
-
// set the value in the context, so that the rest of the app gets the update.
|
|
133
|
-
context.setState?.(obj);
|
|
134
|
-
return true;
|
|
135
|
-
},
|
|
136
|
-
});
|
|
137
|
-
return useState;
|
|
138
|
-
};
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
import BlockStyles from "./block-styles.svelte";
|
|
16
16
|
import {
|
|
17
17
|
getComponent,
|
|
18
|
-
getProxyState,
|
|
19
18
|
getRepeatItemData,
|
|
20
19
|
isEmptyHtmlElement,
|
|
21
20
|
} from "./render-block.helpers.js";
|
|
@@ -47,12 +46,20 @@
|
|
|
47
46
|
export let block: RenderBlockProps["block"];
|
|
48
47
|
export let context: RenderBlockProps["context"];
|
|
49
48
|
|
|
49
|
+
$: repeatItem = () => {
|
|
50
|
+
return getRepeatItemData({
|
|
51
|
+
block: block,
|
|
52
|
+
context: context,
|
|
53
|
+
});
|
|
54
|
+
};
|
|
50
55
|
$: useBlock = () => {
|
|
51
|
-
return
|
|
56
|
+
return repeatItem()
|
|
52
57
|
? block
|
|
53
58
|
: getProcessedBlock({
|
|
54
59
|
block: block,
|
|
55
|
-
|
|
60
|
+
localState: context.localState,
|
|
61
|
+
rootState: context.rootState,
|
|
62
|
+
rootSetState: context.rootSetState,
|
|
56
63
|
context: context.context,
|
|
57
64
|
shouldEvaluateBindings: true,
|
|
58
65
|
});
|
|
@@ -69,7 +76,9 @@
|
|
|
69
76
|
$: actions = () => {
|
|
70
77
|
return getBlockActions({
|
|
71
78
|
block: useBlock(),
|
|
72
|
-
|
|
79
|
+
rootState: context.rootState,
|
|
80
|
+
rootSetState: context.rootSetState,
|
|
81
|
+
localState: context.localState,
|
|
73
82
|
context: context.context,
|
|
74
83
|
});
|
|
75
84
|
};
|
|
@@ -96,7 +105,7 @@
|
|
|
96
105
|
* blocks, and the children will be repeated within those blocks.
|
|
97
106
|
*/
|
|
98
107
|
const shouldRenderChildrenOutsideRef =
|
|
99
|
-
!component?.component && !
|
|
108
|
+
!component?.component && !repeatItem();
|
|
100
109
|
return shouldRenderChildrenOutsideRef ? useBlock().children ?? [] : [];
|
|
101
110
|
};
|
|
102
111
|
$: childrenContext = () => {
|
|
@@ -115,10 +124,11 @@
|
|
|
115
124
|
return {
|
|
116
125
|
apiKey: context.apiKey,
|
|
117
126
|
apiVersion: context.apiVersion,
|
|
118
|
-
|
|
127
|
+
localState: context.localState,
|
|
128
|
+
rootState: context.rootState,
|
|
129
|
+
rootSetState: context.rootSetState,
|
|
119
130
|
content: context.content,
|
|
120
131
|
context: context.context,
|
|
121
|
-
setState: context.setState,
|
|
122
132
|
registeredComponents: context.registeredComponents,
|
|
123
133
|
inheritedStyles: getInheritedTextStyles(),
|
|
124
134
|
};
|
|
@@ -150,12 +160,7 @@
|
|
|
150
160
|
block: block,
|
|
151
161
|
context: context,
|
|
152
162
|
});
|
|
153
|
-
let repeatItemData = getRepeatItemData({
|
|
154
|
-
block: block,
|
|
155
|
-
context: context,
|
|
156
|
-
});
|
|
157
163
|
let tag = block.tagName || "div";
|
|
158
|
-
let proxyState = getProxyState(context);
|
|
159
164
|
</script>
|
|
160
165
|
|
|
161
166
|
{#if canShowBlock()}
|
|
@@ -164,13 +169,13 @@
|
|
|
164
169
|
<svelte:element this={tag} {...attributes()} use:setAttrs={actions()} />
|
|
165
170
|
{/if}
|
|
166
171
|
|
|
167
|
-
{#if !isEmptyHtmlElement(tag) &&
|
|
168
|
-
{#each
|
|
172
|
+
{#if !isEmptyHtmlElement(tag) && repeatItem()}
|
|
173
|
+
{#each repeatItem() as data, index (index)}
|
|
169
174
|
<RenderRepeatedBlock repeatContext={data.context} block={data.block} />
|
|
170
175
|
{/each}
|
|
171
176
|
{/if}
|
|
172
177
|
|
|
173
|
-
{#if !isEmptyHtmlElement(tag) && !
|
|
178
|
+
{#if !isEmptyHtmlElement(tag) && !repeatItem()}
|
|
174
179
|
<svelte:element this={tag} {...attributes()} use:setAttrs={actions()}>
|
|
175
180
|
<RenderComponent {...renderComponentProps()} />
|
|
176
181
|
|
|
@@ -28,8 +28,9 @@
|
|
|
28
28
|
|
|
29
29
|
setContext(BuilderContext.key, {
|
|
30
30
|
content: repeatContext.content,
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
localState: repeatContext.localState,
|
|
32
|
+
rootState: repeatContext.rootState,
|
|
33
|
+
rootSetState: repeatContext.rootSetState,
|
|
33
34
|
context: repeatContext.context,
|
|
34
35
|
apiKey: repeatContext.apiKey,
|
|
35
36
|
registeredComponents: repeatContext.registeredComponents,
|
|
@@ -79,8 +79,8 @@
|
|
|
79
79
|
},
|
|
80
80
|
};
|
|
81
81
|
}
|
|
82
|
-
function
|
|
83
|
-
contentState =
|
|
82
|
+
function contentSetState(newRootState: BuilderRenderState) {
|
|
83
|
+
contentState = newRootState;
|
|
84
84
|
}
|
|
85
85
|
function processMessage(event: MessageEvent) {
|
|
86
86
|
const { data } = event;
|
|
@@ -127,7 +127,9 @@
|
|
|
127
127
|
evaluate({
|
|
128
128
|
code: jsCode,
|
|
129
129
|
context: context || {},
|
|
130
|
-
|
|
130
|
+
localState: undefined,
|
|
131
|
+
rootState: contentState,
|
|
132
|
+
rootSetState: contentSetState,
|
|
131
133
|
});
|
|
132
134
|
}
|
|
133
135
|
}
|
|
@@ -154,7 +156,9 @@
|
|
|
154
156
|
evaluate({
|
|
155
157
|
code: group,
|
|
156
158
|
context: context || {},
|
|
157
|
-
|
|
159
|
+
localState: undefined,
|
|
160
|
+
rootState: contentState,
|
|
161
|
+
rootSetState: contentSetState,
|
|
158
162
|
})
|
|
159
163
|
);
|
|
160
164
|
}
|
|
@@ -166,7 +170,7 @@
|
|
|
166
170
|
...contentState,
|
|
167
171
|
[key]: json,
|
|
168
172
|
};
|
|
169
|
-
|
|
173
|
+
contentSetState(newState);
|
|
170
174
|
})
|
|
171
175
|
.catch((err) => {
|
|
172
176
|
console.error("error fetching dynamic data", url, err);
|
|
@@ -348,11 +352,14 @@
|
|
|
348
352
|
get content() {
|
|
349
353
|
return useContent;
|
|
350
354
|
},
|
|
351
|
-
get
|
|
355
|
+
get localState() {
|
|
356
|
+
return undefined;
|
|
357
|
+
},
|
|
358
|
+
get rootState() {
|
|
352
359
|
return contentState;
|
|
353
360
|
},
|
|
354
|
-
get
|
|
355
|
-
return
|
|
361
|
+
get rootSetState() {
|
|
362
|
+
return TARGET === "qwik" ? undefined : contentSetState;
|
|
356
363
|
},
|
|
357
364
|
get context() {
|
|
358
365
|
return context || {};
|
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
export let styles: Props["styles"];
|
|
11
11
|
|
|
12
12
|
$: tag = () => {
|
|
13
|
-
// NOTE: we have to
|
|
13
|
+
// NOTE: we have to obfuscate the name of the tag due to a limitation in the svelte-preprocessor plugin.
|
|
14
14
|
// https://github.com/sveltejs/vite-plugin-svelte/issues/315#issuecomment-1109000027
|
|
15
|
-
return "sty" + "le";
|
|
15
|
+
return ("sty" + "le") as any;
|
|
16
16
|
};
|
|
17
17
|
$: injectedStyleScript = () => {
|
|
18
18
|
return `<${tag()}>${styles}</${tag()}>`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const SDK_VERSION = "0.3.1"
|
package/src/context/types.ts
CHANGED
|
@@ -16,8 +16,23 @@ export type BuilderRenderContext = Record<string, unknown>;
|
|
|
16
16
|
export interface BuilderContextInterface {
|
|
17
17
|
content: Nullable<BuilderContent>;
|
|
18
18
|
context: BuilderRenderContext;
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
/**
|
|
20
|
+
* The state of the application.
|
|
21
|
+
*
|
|
22
|
+
* NOTE: see `localState` below to understand how it is different from `rootState`.
|
|
23
|
+
*/
|
|
24
|
+
rootState: BuilderRenderState;
|
|
25
|
+
/**
|
|
26
|
+
* Some frameworks have a `setState` function which needs to be invoked to notify
|
|
27
|
+
* the framework of state change. (other frameworks don't in which case it is `undefined')
|
|
28
|
+
*/
|
|
29
|
+
rootSetState: ((rootState: BuilderRenderState) => void) | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* The local state of the current component. This is different from `rootState` in that
|
|
32
|
+
* it can be a child state created by a repeater containing local state.
|
|
33
|
+
* The `rootState` is where all of the state mutations are actually stored.
|
|
34
|
+
*/
|
|
35
|
+
localState: BuilderRenderState | undefined;
|
|
21
36
|
apiKey: string | null;
|
|
22
37
|
apiVersion: ApiVersion | undefined;
|
|
23
38
|
registeredComponents: RegisteredComponents;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { flattenState } from './evaluate';
|
|
2
|
+
|
|
3
|
+
describe('flatten state', () => {
|
|
4
|
+
it('should behave normally when no PROTO_STATE', () => {
|
|
5
|
+
const localState = {};
|
|
6
|
+
const rootState = { foo: 'bar' };
|
|
7
|
+
const flattened = flattenState(rootState, localState, undefined);
|
|
8
|
+
expect(flattened.foo).toEqual('bar');
|
|
9
|
+
flattened.foo = 'baz';
|
|
10
|
+
expect(rootState.foo).toEqual('baz');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('should shadow write ', () => {
|
|
14
|
+
const rootState = { foo: 'foo' };
|
|
15
|
+
const localState = { foo: 'baz' };
|
|
16
|
+
const flattened = flattenState(rootState, localState, undefined);
|
|
17
|
+
expect(() => (flattened.foo = 'bar')).toThrow(
|
|
18
|
+
'Writing to local state is not allowed as it is read-only.'
|
|
19
|
+
);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
@@ -1,18 +1,26 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
BuilderContextInterface,
|
|
3
|
+
BuilderRenderState,
|
|
4
|
+
} from '../context/types.js';
|
|
2
5
|
import { isBrowser } from './is-browser.js';
|
|
3
6
|
import { isEditing } from './is-editing.js';
|
|
4
7
|
|
|
5
8
|
export function evaluate({
|
|
6
9
|
code,
|
|
7
10
|
context,
|
|
8
|
-
|
|
11
|
+
localState,
|
|
12
|
+
rootState,
|
|
13
|
+
rootSetState,
|
|
9
14
|
event,
|
|
10
15
|
isExpression = true,
|
|
11
16
|
}: {
|
|
12
17
|
code: string;
|
|
13
18
|
event?: Event;
|
|
14
19
|
isExpression?: boolean;
|
|
15
|
-
} & Pick<
|
|
20
|
+
} & Pick<
|
|
21
|
+
BuilderContextInterface,
|
|
22
|
+
'localState' | 'context' | 'rootState' | 'rootSetState'
|
|
23
|
+
>): any {
|
|
16
24
|
if (code === '') {
|
|
17
25
|
console.warn('Skipping evaluation of empty code block.');
|
|
18
26
|
return;
|
|
@@ -45,7 +53,13 @@ export function evaluate({
|
|
|
45
53
|
'context',
|
|
46
54
|
'event',
|
|
47
55
|
useCode
|
|
48
|
-
)(
|
|
56
|
+
)(
|
|
57
|
+
builder,
|
|
58
|
+
builder,
|
|
59
|
+
flattenState(rootState, localState, rootSetState),
|
|
60
|
+
context,
|
|
61
|
+
event
|
|
62
|
+
);
|
|
49
63
|
} catch (e) {
|
|
50
64
|
console.warn(
|
|
51
65
|
'Builder custom code error: \n While Evaluating: \n ',
|
|
@@ -55,3 +69,31 @@ export function evaluate({
|
|
|
55
69
|
);
|
|
56
70
|
}
|
|
57
71
|
}
|
|
72
|
+
|
|
73
|
+
export function flattenState(
|
|
74
|
+
rootState: Record<string | symbol, any>,
|
|
75
|
+
localState: Record<string | symbol, any> | undefined,
|
|
76
|
+
rootSetState: ((rootState: BuilderRenderState) => void) | undefined
|
|
77
|
+
): BuilderRenderState {
|
|
78
|
+
if (rootState === localState) {
|
|
79
|
+
throw new Error('rootState === localState');
|
|
80
|
+
}
|
|
81
|
+
return new Proxy(rootState, {
|
|
82
|
+
get: (_, prop) => {
|
|
83
|
+
if (localState && prop in localState) {
|
|
84
|
+
return localState[prop];
|
|
85
|
+
}
|
|
86
|
+
return rootState[prop as string];
|
|
87
|
+
},
|
|
88
|
+
set: (_, prop, value) => {
|
|
89
|
+
if (localState && prop in localState) {
|
|
90
|
+
throw new Error(
|
|
91
|
+
'Writing to local state is not allowed as it is read-only.'
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
rootState[prop as string] = value;
|
|
95
|
+
rootSetState?.(rootState);
|
|
96
|
+
return true;
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
}
|
|
@@ -4,7 +4,10 @@ import { evaluate } from './evaluate.js';
|
|
|
4
4
|
|
|
5
5
|
type Options = {
|
|
6
6
|
block: BuilderBlock;
|
|
7
|
-
} & Pick<
|
|
7
|
+
} & Pick<
|
|
8
|
+
BuilderContextInterface,
|
|
9
|
+
'localState' | 'context' | 'rootState' | 'rootSetState'
|
|
10
|
+
>;
|
|
8
11
|
|
|
9
12
|
type EventHandler = (event: Event) => any;
|
|
10
13
|
|
|
@@ -14,7 +17,9 @@ export const createEventHandler =
|
|
|
14
17
|
evaluate({
|
|
15
18
|
code: value,
|
|
16
19
|
context: options.context,
|
|
17
|
-
|
|
20
|
+
localState: options.localState,
|
|
21
|
+
rootState: options.rootState,
|
|
22
|
+
rootSetState: options.rootSetState,
|
|
18
23
|
event,
|
|
19
24
|
isExpression: false,
|
|
20
25
|
});
|
|
@@ -8,7 +8,10 @@ type Actions = { [index: string]: (event: Event) => any };
|
|
|
8
8
|
export function getBlockActions(
|
|
9
9
|
options: {
|
|
10
10
|
block: BuilderBlock;
|
|
11
|
-
} & Pick<
|
|
11
|
+
} & Pick<
|
|
12
|
+
BuilderContextInterface,
|
|
13
|
+
'localState' | 'context' | 'rootState' | 'rootSetState'
|
|
14
|
+
>
|
|
12
15
|
): Actions {
|
|
13
16
|
const obj: Actions = {};
|
|
14
17
|
const optionActions = options.block.actions ?? {};
|
|
@@ -21,7 +21,9 @@ test('Can process bindings', () => {
|
|
|
21
21
|
const processed = getProcessedBlock({
|
|
22
22
|
block,
|
|
23
23
|
context: {},
|
|
24
|
-
|
|
24
|
+
rootState: { test: 'hello' },
|
|
25
|
+
rootSetState: undefined,
|
|
26
|
+
localState: undefined,
|
|
25
27
|
shouldEvaluateBindings: true,
|
|
26
28
|
});
|
|
27
29
|
expect(processed).not.toEqual(block);
|
|
@@ -8,10 +8,15 @@ import { transformBlock } from './transform-block.js';
|
|
|
8
8
|
const evaluateBindings = ({
|
|
9
9
|
block,
|
|
10
10
|
context,
|
|
11
|
-
|
|
11
|
+
localState,
|
|
12
|
+
rootState,
|
|
13
|
+
rootSetState,
|
|
12
14
|
}: {
|
|
13
15
|
block: BuilderBlock;
|
|
14
|
-
} & Pick<
|
|
16
|
+
} & Pick<
|
|
17
|
+
BuilderContextInterface,
|
|
18
|
+
'localState' | 'context' | 'rootState' | 'rootSetState'
|
|
19
|
+
>): BuilderBlock => {
|
|
15
20
|
if (!block.bindings) {
|
|
16
21
|
return block;
|
|
17
22
|
}
|
|
@@ -24,7 +29,13 @@ const evaluateBindings = ({
|
|
|
24
29
|
|
|
25
30
|
for (const binding in block.bindings) {
|
|
26
31
|
const expression = block.bindings[binding];
|
|
27
|
-
const value = evaluate({
|
|
32
|
+
const value = evaluate({
|
|
33
|
+
code: expression,
|
|
34
|
+
localState,
|
|
35
|
+
rootState,
|
|
36
|
+
rootSetState,
|
|
37
|
+
context,
|
|
38
|
+
});
|
|
28
39
|
set(copied, binding, value);
|
|
29
40
|
}
|
|
30
41
|
|
|
@@ -35,7 +46,9 @@ export function getProcessedBlock({
|
|
|
35
46
|
block,
|
|
36
47
|
context,
|
|
37
48
|
shouldEvaluateBindings,
|
|
38
|
-
|
|
49
|
+
localState,
|
|
50
|
+
rootState,
|
|
51
|
+
rootSetState,
|
|
39
52
|
}: {
|
|
40
53
|
block: BuilderBlock;
|
|
41
54
|
/**
|
|
@@ -43,11 +56,20 @@ export function getProcessedBlock({
|
|
|
43
56
|
* also sometimes too early to consider bindings, e.g. when we might be looking at a repeated block.
|
|
44
57
|
*/
|
|
45
58
|
shouldEvaluateBindings: boolean;
|
|
46
|
-
} & Pick<
|
|
59
|
+
} & Pick<
|
|
60
|
+
BuilderContextInterface,
|
|
61
|
+
'localState' | 'context' | 'rootState' | 'rootSetState'
|
|
62
|
+
>): BuilderBlock {
|
|
47
63
|
const transformedBlock = transformBlock(block);
|
|
48
64
|
|
|
49
65
|
if (shouldEvaluateBindings) {
|
|
50
|
-
return evaluateBindings({
|
|
66
|
+
return evaluateBindings({
|
|
67
|
+
block: transformedBlock,
|
|
68
|
+
localState,
|
|
69
|
+
rootState,
|
|
70
|
+
rootSetState,
|
|
71
|
+
context,
|
|
72
|
+
});
|
|
51
73
|
} else {
|
|
52
74
|
return transformedBlock;
|
|
53
75
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SDK_VERSION } from '../constants/sdk-version.js';
|
|
1
2
|
import { TARGET } from '../constants/target.js';
|
|
2
3
|
import { isBrowser } from '../functions/is-browser.js';
|
|
3
4
|
import { register } from '../functions/register.js';
|
|
@@ -40,6 +41,7 @@ export const setupBrowserForEditing = (
|
|
|
40
41
|
type: 'builder.sdkInfo',
|
|
41
42
|
data: {
|
|
42
43
|
target: TARGET,
|
|
44
|
+
version: SDK_VERSION,
|
|
43
45
|
// TODO: compile these in
|
|
44
46
|
// type: process.env.SDK_TYPE,
|
|
45
47
|
// version: process.env.SDK_VERSION,
|