@webstudio-is/react-sdk 0.82.0 → 0.84.0

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.
Files changed (71) hide show
  1. package/LICENSE +661 -21
  2. package/lib/cjs/component-renderer.js +125 -0
  3. package/lib/cjs/components/component-meta.js +10 -0
  4. package/lib/cjs/components/components-utils.js +1 -0
  5. package/lib/cjs/context.js +2 -1
  6. package/lib/cjs/css/index.js +0 -1
  7. package/lib/cjs/css/style-rules.js +1 -1
  8. package/lib/cjs/embed-template.js +130 -55
  9. package/lib/cjs/expression.js +47 -4
  10. package/lib/cjs/hook.js +34 -0
  11. package/lib/cjs/index.js +7 -0
  12. package/lib/cjs/instance-utils.js +65 -0
  13. package/lib/cjs/props.js +18 -3
  14. package/lib/cjs/tree/create-elements-tree.js +5 -4
  15. package/lib/cjs/tree/root.js +7 -2
  16. package/lib/cjs/tree/webstudio-component.js +26 -10
  17. package/lib/component-renderer.js +111 -0
  18. package/lib/components/component-meta.js +10 -0
  19. package/lib/components/components-utils.js +1 -0
  20. package/lib/context.js +2 -1
  21. package/lib/css/index.js +0 -1
  22. package/lib/css/style-rules.js +1 -1
  23. package/lib/embed-template.js +138 -55
  24. package/lib/expression.js +47 -4
  25. package/lib/hook.js +14 -0
  26. package/lib/index.js +10 -1
  27. package/lib/instance-utils.js +45 -0
  28. package/lib/props.js +19 -4
  29. package/lib/tree/create-elements-tree.js +8 -5
  30. package/lib/tree/root.js +14 -4
  31. package/lib/tree/webstudio-component.js +27 -11
  32. package/lib/types/app/root.d.ts +1 -2
  33. package/lib/types/component-renderer.d.ts +8 -0
  34. package/lib/types/components/component-meta.d.ts +14 -8
  35. package/lib/types/context.d.ts +3 -1
  36. package/lib/types/css/css.d.ts +19 -19
  37. package/lib/types/css/global-rules.d.ts +19 -19
  38. package/lib/types/css/index.d.ts +0 -1
  39. package/lib/types/css/normalize.d.ts +47 -47
  40. package/lib/types/embed-template.d.ts +297 -174
  41. package/lib/types/expression.d.ts +3 -2
  42. package/lib/types/hook.d.ts +31 -0
  43. package/lib/types/index.d.ts +5 -2
  44. package/lib/types/instance-utils.d.ts +16 -0
  45. package/lib/types/instance-utils.test.d.ts +1 -0
  46. package/lib/types/props.d.ts +48 -46
  47. package/lib/types/tree/create-elements-tree.d.ts +9 -6
  48. package/lib/types/tree/root.d.ts +8 -5
  49. package/lib/types/tree/webstudio-component.d.ts +16 -7
  50. package/package.json +18 -19
  51. package/src/component-renderer.tsx +117 -0
  52. package/src/components/component-meta.ts +10 -0
  53. package/src/context.tsx +4 -0
  54. package/src/css/index.ts +0 -1
  55. package/src/css/style-rules.ts +1 -1
  56. package/src/embed-template.test.ts +113 -26
  57. package/src/embed-template.ts +149 -56
  58. package/src/expression.test.ts +74 -6
  59. package/src/expression.ts +55 -2
  60. package/src/hook.ts +42 -0
  61. package/src/index.ts +5 -0
  62. package/src/instance-utils.test.ts +89 -0
  63. package/src/instance-utils.ts +65 -0
  64. package/src/props.ts +19 -2
  65. package/src/tree/create-elements-tree.tsx +25 -8
  66. package/src/tree/root.ts +22 -3
  67. package/src/tree/webstudio-component.tsx +42 -14
  68. package/lib/cjs/css/get-browser-style.js +0 -83
  69. package/lib/css/get-browser-style.js +0 -65
  70. package/lib/types/css/get-browser-style.d.ts +0 -2
  71. package/src/css/get-browser-style.ts +0 -81
package/lib/expression.js CHANGED
@@ -208,12 +208,15 @@ const executeComputingExpressions = (expressions, variables) => {
208
208
  const values = executeFn(variables);
209
209
  return values;
210
210
  };
211
- const generateEffectfulExpression = (code, allowedVariables) => {
211
+ const generateEffectfulExpression = (code, args, allowedVariables) => {
212
212
  const inputVariables = /* @__PURE__ */ new Set();
213
213
  const outputVariables = /* @__PURE__ */ new Set();
214
214
  validateExpression(code, {
215
215
  effectful: true,
216
216
  transformIdentifier: (identifier, assignee) => {
217
+ if (args.has(identifier)) {
218
+ return identifier;
219
+ }
217
220
  if (allowedVariables.has(identifier)) {
218
221
  if (assignee) {
219
222
  outputVariables.add(identifier);
@@ -226,6 +229,10 @@ const generateEffectfulExpression = (code, allowedVariables) => {
226
229
  }
227
230
  });
228
231
  let generatedCode = "";
232
+ for (const id of args) {
233
+ generatedCode += `let ${id} = _args.get('${id}');
234
+ `;
235
+ }
229
236
  for (const id of inputVariables) {
230
237
  generatedCode += `let ${id} = _variables.get('${id}');
231
238
  `;
@@ -247,15 +254,50 @@ const generateEffectfulExpression = (code, allowedVariables) => {
247
254
  generatedCode += `]);`;
248
255
  return generatedCode;
249
256
  };
250
- const executeEffectfulExpression = (code, variables) => {
257
+ const executeEffectfulExpression = (code, args, variables) => {
251
258
  const generatedCode = generateEffectfulExpression(
252
259
  code,
260
+ new Set(args.keys()),
253
261
  new Set(variables.keys())
254
262
  );
255
- const executeFn = new Function("_variables", generatedCode);
256
- const values = executeFn(variables);
263
+ const executeFn = new Function("_variables", "_args", generatedCode);
264
+ const values = executeFn(variables, args);
257
265
  return values;
258
266
  };
267
+ const computeExpressionDependencies = (expressions, expressionId, dependencies) => {
268
+ const depsById = dependencies.get(expressionId);
269
+ if (depsById) {
270
+ return depsById;
271
+ }
272
+ const parentDeps = /* @__PURE__ */ new Set();
273
+ const code = expressions.get(expressionId);
274
+ if (code === void 0) {
275
+ return parentDeps;
276
+ }
277
+ dependencies.set(expressionId, parentDeps);
278
+ validateExpression(code, {
279
+ transformIdentifier: (id) => {
280
+ parentDeps.add(id);
281
+ const childDeps = computeExpressionDependencies(
282
+ expressions,
283
+ id,
284
+ dependencies
285
+ );
286
+ for (const depId of childDeps) {
287
+ parentDeps.add(depId);
288
+ }
289
+ return id;
290
+ }
291
+ });
292
+ return parentDeps;
293
+ };
294
+ const computeExpressionsDependencies = (expressions) => {
295
+ const dependencies = /* @__PURE__ */ new Map();
296
+ for (const id of expressions.keys()) {
297
+ computeExpressionDependencies(expressions, id, dependencies);
298
+ }
299
+ return dependencies;
300
+ };
259
301
  const dataSourceVariablePrefix = "$ws$dataSource$";
260
302
  const encodeDataSourceVariable = (id) => {
261
303
  const encoded = id.replaceAll("-", "__DASH__");
@@ -286,6 +328,7 @@ const decodeVariablesMap = (values) => {
286
328
  return decodedValues;
287
329
  };
288
330
  export {
331
+ computeExpressionsDependencies,
289
332
  decodeDataSourceVariable,
290
333
  decodeVariablesMap,
291
334
  encodeDataSourceVariable,
package/lib/hook.js ADDED
@@ -0,0 +1,14 @@
1
+ const getClosestInstance = (instanceSelection, currentInstance, closestComponent) => {
2
+ let matched = false;
3
+ for (const instance of instanceSelection) {
4
+ if (currentInstance === instance) {
5
+ matched = true;
6
+ }
7
+ if (matched && instance.component === closestComponent) {
8
+ return instance;
9
+ }
10
+ }
11
+ };
12
+ export {
13
+ getClosestInstance
14
+ };
package/lib/index.js CHANGED
@@ -13,7 +13,8 @@ import {
13
13
  useInstanceProps,
14
14
  usePropUrl,
15
15
  usePropAsset,
16
- getInstanceIdFromComponentProps
16
+ getInstanceIdFromComponentProps,
17
+ getIndexWithinAncestorFromComponentProps
17
18
  } from "./props";
18
19
  import { ReactSdkContext } from "./context";
19
20
  import {
@@ -22,14 +23,19 @@ import {
22
23
  executeComputingExpressions,
23
24
  generateEffectfulExpression,
24
25
  executeEffectfulExpression,
26
+ computeExpressionsDependencies,
25
27
  encodeDataSourceVariable,
26
28
  encodeVariablesMap,
27
29
  decodeDataSourceVariable,
28
30
  decodeVariablesMap
29
31
  } from "./expression";
32
+ import { renderComponentTemplate } from "./component-renderer";
33
+ import { getIndexesWithinAncestors } from "./instance-utils";
34
+ export * from "./hook";
30
35
  export {
31
36
  ReactSdkContext,
32
37
  componentCategories,
38
+ computeExpressionsDependencies,
33
39
  decodeDataSourceVariable,
34
40
  decodeVariablesMap,
35
41
  defaultStates,
@@ -39,7 +45,10 @@ export {
39
45
  executeEffectfulExpression,
40
46
  generateComputingExpressions,
41
47
  generateEffectfulExpression,
48
+ getIndexWithinAncestorFromComponentProps,
49
+ getIndexesWithinAncestors,
42
50
  getInstanceIdFromComponentProps,
51
+ renderComponentTemplate,
43
52
  stateCategories,
44
53
  useInstanceProps,
45
54
  usePropAsset,
@@ -0,0 +1,45 @@
1
+ const getIndexesWithinAncestors = (metas, instances, rootIds) => {
2
+ const ancestors = /* @__PURE__ */ new Set();
3
+ for (const meta of metas.values()) {
4
+ if (meta.indexWithinAncestor !== void 0) {
5
+ ancestors.add(meta.indexWithinAncestor);
6
+ }
7
+ }
8
+ const indexes = /* @__PURE__ */ new Map();
9
+ const traverseInstances = (instances2, instanceId, latestIndexes2 = /* @__PURE__ */ new Map()) => {
10
+ const instance = instances2.get(instanceId);
11
+ if (instance === void 0) {
12
+ return;
13
+ }
14
+ const meta = metas.get(instance.component);
15
+ if (meta === void 0) {
16
+ return;
17
+ }
18
+ if (ancestors.has(instance.component)) {
19
+ latestIndexes2 = new Map(latestIndexes2);
20
+ latestIndexes2.set(instance.component, /* @__PURE__ */ new Map());
21
+ }
22
+ if (meta.indexWithinAncestor !== void 0) {
23
+ const ancestorIndexes = latestIndexes2.get(meta.indexWithinAncestor);
24
+ if (ancestorIndexes !== void 0) {
25
+ let index = ancestorIndexes.get(instance.component) ?? -1;
26
+ index += 1;
27
+ ancestorIndexes.set(instance.component, index);
28
+ indexes.set(instance.id, index);
29
+ }
30
+ }
31
+ for (const child of instance.children) {
32
+ if (child.type === "id") {
33
+ traverseInstances(instances2, child.value, latestIndexes2);
34
+ }
35
+ }
36
+ };
37
+ const latestIndexes = /* @__PURE__ */ new Map();
38
+ for (const instanceId of rootIds) {
39
+ traverseInstances(instances, instanceId, latestIndexes);
40
+ }
41
+ return indexes;
42
+ };
43
+ export {
44
+ getIndexesWithinAncestors
45
+ };
package/lib/props.js CHANGED
@@ -2,7 +2,7 @@ import { useContext, useMemo } from "react";
2
2
  import { computed } from "nanostores";
3
3
  import { useStore } from "@nanostores/react";
4
4
  import { ReactSdkContext } from "./context";
5
- import { idAttribute } from "./tree/webstudio-component";
5
+ import { idAttribute, indexAttribute } from "./tree/webstudio-component";
6
6
  const getPropsByInstanceId = (props) => {
7
7
  const propsByInstanceId = /* @__PURE__ */ new Map();
8
8
  for (const prop of props.values()) {
@@ -21,13 +21,18 @@ const useInstanceProps = (instanceId) => {
21
21
  dataSourceValuesStore,
22
22
  executeEffectfulExpression,
23
23
  setDataSourceValues,
24
- renderer
24
+ renderer,
25
+ indexesWithinAncestors
25
26
  } = useContext(ReactSdkContext);
27
+ const index = indexesWithinAncestors.get(instanceId);
26
28
  const instancePropsObjectStore = useMemo(() => {
27
29
  return computed(
28
30
  [propsByInstanceIdStore, dataSourceValuesStore],
29
31
  (propsByInstanceId, dataSourceValues) => {
30
32
  const instancePropsObject2 = {};
33
+ if (index !== void 0) {
34
+ instancePropsObject2[indexAttribute] = index.toString();
35
+ }
31
36
  const instanceProps = propsByInstanceId.get(instanceId);
32
37
  if (instanceProps === void 0) {
33
38
  return instancePropsObject2;
@@ -45,14 +50,19 @@ const useInstanceProps = (instanceId) => {
45
50
  continue;
46
51
  }
47
52
  if (prop.type === "action") {
48
- instancePropsObject2[prop.name] = () => {
53
+ instancePropsObject2[prop.name] = (...args) => {
49
54
  if (renderer === "canvas") {
50
55
  return;
51
56
  }
52
57
  for (const value of prop.value) {
53
58
  if (value.type === "execute") {
59
+ const argsMap = /* @__PURE__ */ new Map();
60
+ for (const [i, name] of value.args.entries()) {
61
+ argsMap.set(name, args[i]);
62
+ }
54
63
  const newValues = executeEffectfulExpression(
55
64
  value.code,
65
+ argsMap,
56
66
  dataSourceValues
57
67
  );
58
68
  setDataSourceValues(newValues);
@@ -72,7 +82,8 @@ const useInstanceProps = (instanceId) => {
72
82
  instanceId,
73
83
  renderer,
74
84
  executeEffectfulExpression,
75
- setDataSourceValues
85
+ setDataSourceValues,
86
+ index
76
87
  ]);
77
88
  const instancePropsObject = useStore(instancePropsObjectStore);
78
89
  return instancePropsObject;
@@ -159,7 +170,11 @@ const usePropUrl = (instanceId, name) => {
159
170
  const getInstanceIdFromComponentProps = (props) => {
160
171
  return props[idAttribute];
161
172
  };
173
+ const getIndexWithinAncestorFromComponentProps = (props) => {
174
+ return props[indexAttribute];
175
+ };
162
176
  export {
177
+ getIndexWithinAncestorFromComponentProps,
163
178
  getInstanceIdFromComponentProps,
164
179
  getPropsByInstanceId,
165
180
  resolveUrlProp,
@@ -1,6 +1,7 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
- import { Fragment } from "react";
3
- import { Scripts, ScrollRestoration } from "@remix-run/react";
2
+ import {
3
+ Fragment
4
+ } from "react";
4
5
  import {
5
6
  ReactSdkContext
6
7
  } from "../context";
@@ -16,8 +17,10 @@ const createElementsTree = ({
16
17
  dataSourceValuesStore,
17
18
  executeEffectfulExpression,
18
19
  onDataSourceUpdate,
20
+ indexesWithinAncestors,
19
21
  Component,
20
- components
22
+ components,
23
+ scripts
21
24
  }) => {
22
25
  const rootInstance = instances.get(rootInstanceId);
23
26
  if (rootInstance === void 0) {
@@ -38,8 +41,7 @@ const createElementsTree = ({
38
41
  children: [
39
42
  /* @__PURE__ */ jsxs(Fragment, { children: [
40
43
  children,
41
- /* @__PURE__ */ jsx(ScrollRestoration, {}),
42
- /* @__PURE__ */ jsx(Scripts, {})
44
+ scripts
43
45
  ] }, "children")
44
46
  ],
45
47
  components
@@ -55,6 +57,7 @@ const createElementsTree = ({
55
57
  renderer,
56
58
  imageBaseUrl,
57
59
  assetBaseUrl,
60
+ indexesWithinAncestors,
58
61
  executeEffectfulExpression,
59
62
  setDataSourceValues: onDataSourceUpdate,
60
63
  setBoundDataSourceValue: (instanceId, propName, value) => {
package/lib/tree/root.js CHANGED
@@ -1,17 +1,25 @@
1
- import { useRef, useCallback } from "react";
1
+ import {
2
+ useRef,
3
+ useCallback
4
+ } from "react";
2
5
  import {
3
6
  atom,
4
7
  computed
5
8
  } from "nanostores";
9
+ import {} from "@webstudio-is/project-build";
6
10
  import { createElementsTree } from "./create-elements-tree";
7
- import { WebstudioComponent } from "./webstudio-component";
11
+ import {
12
+ WebstudioComponent
13
+ } from "./webstudio-component";
8
14
  import { getPropsByInstanceId } from "../props";
9
15
  const InstanceRoot = ({
10
16
  data,
17
+ indexesWithinAncestors,
11
18
  executeComputingExpressions,
12
19
  executeEffectfulExpression,
13
20
  Component,
14
- components
21
+ components,
22
+ scripts
15
23
  }) => {
16
24
  const dataSourceVariablesStoreRef = useRef(void 0);
17
25
  if (dataSourceVariablesStoreRef.current === void 0) {
@@ -63,11 +71,13 @@ const InstanceRoot = ({
63
71
  ),
64
72
  assetsStore: atom(new Map(data.assets.map((asset) => [asset.id, asset]))),
65
73
  pagesStore: atom(new Map(data.pages.map((page) => [page.id, page]))),
74
+ indexesWithinAncestors,
66
75
  executeEffectfulExpression,
67
76
  dataSourceValuesStore,
68
77
  onDataSourceUpdate,
69
78
  Component: Component ?? WebstudioComponent,
70
- components
79
+ components,
80
+ scripts
71
81
  });
72
82
  };
73
83
  export {
@@ -1,5 +1,5 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
- import { Fragment as Fragment2 } from "react";
2
+ import { Fragment as Fragment2, forwardRef } from "react";
3
3
  import { useInstanceProps } from "../props";
4
4
  const renderText = (text) => {
5
5
  const lines = text.split("\n");
@@ -16,13 +16,7 @@ const renderWebstudioComponentChildren = (children) => {
16
16
  return typeof child === "string" ? renderText(child) : child;
17
17
  });
18
18
  };
19
- const WebstudioComponent = ({
20
- instance,
21
- instanceSelector,
22
- children,
23
- components,
24
- ...rest
25
- }) => {
19
+ const WebstudioComponent = forwardRef(({ instance, instanceSelector, children, components, ...rest }, ref) => {
26
20
  const { [showAttribute]: show = true, ...instanceProps } = useInstanceProps(
27
21
  instance.id
28
22
  );
@@ -39,17 +33,39 @@ const WebstudioComponent = ({
39
33
  if (Component === void 0) {
40
34
  return /* @__PURE__ */ jsx(Fragment, {});
41
35
  }
42
- return /* @__PURE__ */ jsx(Component, { ...props, children: renderWebstudioComponentChildren(children) });
43
- };
36
+ return /* @__PURE__ */ jsx(Component, { ...props, ref, children: renderWebstudioComponentChildren(children) });
37
+ });
44
38
  const idAttribute = "data-ws-id";
39
+ const selectorIdAttribute = "data-ws-selector";
45
40
  const componentAttribute = "data-ws-component";
46
41
  const showAttribute = "data-ws-show";
42
+ const indexAttribute = "data-ws-index";
47
43
  const collapsedAttribute = "data-ws-collapsed";
44
+ const splitPropsWithWebstudioAttributes = ({
45
+ [idAttribute]: idAttributeValue,
46
+ [componentAttribute]: componentAttributeValue,
47
+ [showAttribute]: showAttributeValue,
48
+ [collapsedAttribute]: collapsedAttributeValue,
49
+ [selectorIdAttribute]: parentIdAttributeValue,
50
+ ...props
51
+ }) => [
52
+ {
53
+ [idAttribute]: idAttributeValue,
54
+ [componentAttribute]: componentAttributeValue,
55
+ [showAttribute]: showAttributeValue,
56
+ [collapsedAttribute]: collapsedAttributeValue,
57
+ [selectorIdAttribute]: parentIdAttributeValue
58
+ },
59
+ props
60
+ ];
48
61
  export {
49
62
  WebstudioComponent,
50
63
  collapsedAttribute,
51
64
  componentAttribute,
52
65
  idAttribute,
66
+ indexAttribute,
53
67
  renderWebstudioComponentChildren,
54
- showAttribute
68
+ selectorIdAttribute,
69
+ showAttribute,
70
+ splitPropsWithWebstudioAttributes
55
71
  };
@@ -1,8 +1,7 @@
1
- /// <reference types="react" />
2
1
  import { Outlet as DefaultOutlet } from "@remix-run/react";
3
2
  /**
4
3
  * We are using Outlet prop from index layout when user renders site from a subdomain.
5
4
  */
6
5
  export declare const Root: ({ Outlet, }: {
7
6
  Outlet: typeof DefaultOutlet;
8
- }) => JSX.Element;
7
+ }) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,8 @@
1
+ import type { ExoticComponent } from "react";
2
+ import type { Instance } from "@webstudio-is/project-build";
3
+ import type { WsComponentMeta } from "./components/component-meta";
4
+ export declare const renderComponentTemplate: ({ name, metas: metasRecord, components, }: {
5
+ name: Instance["component"];
6
+ metas: Record<string, WsComponentMeta>;
7
+ components: Record<string, ExoticComponent<any>>;
8
+ }) => import("react/jsx-runtime").JSX.Element;