@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
@@ -9,8 +9,9 @@ export declare const validateExpression: (code: string, options?: {
9
9
  */
10
10
  export declare const generateComputingExpressions: (expressions: Map<string, string>, allowedVariables: Set<string>) => string;
11
11
  export declare const executeComputingExpressions: (expressions: Map<string, string>, variables: Map<string, unknown>) => Map<string, unknown>;
12
- export declare const generateEffectfulExpression: (code: string, allowedVariables: Set<string>) => string;
13
- export declare const executeEffectfulExpression: (code: string, variables: Map<string, unknown>) => Map<string, unknown>;
12
+ export declare const generateEffectfulExpression: (code: string, args: Set<string>, allowedVariables: Set<string>) => string;
13
+ export declare const executeEffectfulExpression: (code: string, args: Map<string, unknown>, variables: Map<string, unknown>) => Map<string, unknown>;
14
+ export declare const computeExpressionsDependencies: (expressions: Map<string, string>) => Map<string, Set<string>>;
14
15
  type Values = Map<string, unknown>;
15
16
  export declare const encodeDataSourceVariable: (id: string) => string;
16
17
  export declare const encodeVariablesMap: (values: Values) => Values;
@@ -0,0 +1,31 @@
1
+ import type { Instance, Prop } from "@webstudio-is/project-build";
2
+ /**
3
+ * Hooks are subscriptions to builder events
4
+ * with limited way to interact with it.
5
+ * Called independently from components.
6
+ */
7
+ export type HookContext = {
8
+ setPropVariable: (instanceId: Instance["id"], propName: Prop["name"], value: unknown) => void;
9
+ };
10
+ export type InstanceSelection = Instance[];
11
+ type NavigatorEvent = {
12
+ instanceSelection: InstanceSelection;
13
+ };
14
+ export type Hook = {
15
+ onNavigatorSelect?: (context: HookContext, event: NavigatorEvent) => void;
16
+ onNavigatorUnselect?: (context: HookContext, event: NavigatorEvent) => void;
17
+ };
18
+ export declare const getClosestInstance: (instanceSelection: InstanceSelection, currentInstance: Instance, closestComponent: Instance["component"]) => {
19
+ type: "instance";
20
+ id: string;
21
+ component: string;
22
+ children: ({
23
+ type: "text";
24
+ value: string;
25
+ } | {
26
+ type: "id";
27
+ value: string;
28
+ })[];
29
+ label?: string | undefined;
30
+ } | undefined;
31
+ export {};
@@ -5,6 +5,9 @@ export * from "./app";
5
5
  export * from "./components/components-utils";
6
6
  export { type WsComponentPropsMeta, type WsComponentMeta, type ComponentState, type PresetStyle, componentCategories, stateCategories, defaultStates, } from "./components/component-meta";
7
7
  export * from "./embed-template";
8
- export { useInstanceProps, usePropUrl, usePropAsset, getInstanceIdFromComponentProps, } from "./props";
8
+ export { useInstanceProps, usePropUrl, usePropAsset, getInstanceIdFromComponentProps, getIndexWithinAncestorFromComponentProps, } from "./props";
9
9
  export { type Params, ReactSdkContext } from "./context";
10
- export { validateExpression, generateComputingExpressions, executeComputingExpressions, generateEffectfulExpression, executeEffectfulExpression, encodeDataSourceVariable, encodeVariablesMap, decodeDataSourceVariable, decodeVariablesMap, } from "./expression";
10
+ export { validateExpression, generateComputingExpressions, executeComputingExpressions, generateEffectfulExpression, executeEffectfulExpression, computeExpressionsDependencies, encodeDataSourceVariable, encodeVariablesMap, decodeDataSourceVariable, decodeVariablesMap, } from "./expression";
11
+ export { renderComponentTemplate } from "./component-renderer";
12
+ export { getIndexesWithinAncestors } from "./instance-utils";
13
+ export * from "./hook";
@@ -0,0 +1,16 @@
1
+ import type { Instance } from "@webstudio-is/project-build";
2
+ import type { WsComponentMeta } from "./components/component-meta";
3
+ export type IndexesWithinAncestors = Map<Instance["id"], number>;
4
+ export declare const getIndexesWithinAncestors: (metas: Map<Instance["component"], WsComponentMeta>, instances: Map<string, {
5
+ type: "instance";
6
+ id: string;
7
+ component: string;
8
+ children: ({
9
+ type: "text";
10
+ value: string;
11
+ } | {
12
+ type: "id";
13
+ value: string;
14
+ })[];
15
+ label?: string | undefined;
16
+ }>, rootIds: Instance["id"][]) => IndexesWithinAncestors;
@@ -0,0 +1 @@
1
+ export {};
@@ -3,35 +3,34 @@ import type { Asset, Assets } from "@webstudio-is/asset-uploader";
3
3
  export type PropsByInstanceId = Map<Instance["id"], Prop[]>;
4
4
  export type Pages = Map<Page["id"], Page>;
5
5
  export declare const getPropsByInstanceId: (props: Map<string, {
6
- name: string;
7
6
  type: "number";
8
7
  value: number;
9
8
  id: string;
9
+ name: string;
10
10
  instanceId: string;
11
11
  required?: boolean | undefined;
12
12
  } | {
13
- name: string;
14
13
  type: "string";
15
14
  value: string;
16
15
  id: string;
16
+ name: string;
17
17
  instanceId: string;
18
18
  required?: boolean | undefined;
19
19
  } | {
20
- name: string;
21
20
  type: "boolean";
22
21
  value: boolean;
23
22
  id: string;
23
+ name: string;
24
24
  instanceId: string;
25
25
  required?: boolean | undefined;
26
26
  } | {
27
- name: string;
28
27
  type: "asset";
29
28
  value: string;
30
29
  id: string;
30
+ name: string;
31
31
  instanceId: string;
32
32
  required?: boolean | undefined;
33
33
  } | {
34
- name: string;
35
34
  type: "page";
36
35
  value: (string | {
37
36
  instanceId: string;
@@ -41,132 +40,134 @@ export declare const getPropsByInstanceId: (props: Map<string, {
41
40
  pageId: string;
42
41
  } | undefined);
43
42
  id: string;
43
+ name: string;
44
44
  instanceId: string;
45
45
  required?: boolean | undefined;
46
46
  } | {
47
- name: string;
48
47
  type: "string[]";
49
48
  value: string[];
50
49
  id: string;
50
+ name: string;
51
51
  instanceId: string;
52
52
  required?: boolean | undefined;
53
53
  } | {
54
- name: string;
55
54
  type: "dataSource";
56
55
  value: string;
57
56
  id: string;
57
+ name: string;
58
58
  instanceId: string;
59
59
  required?: boolean | undefined;
60
60
  } | {
61
- name: string;
62
61
  type: "action";
63
62
  value: {
64
63
  code: string;
65
64
  type: "execute";
65
+ args: string[];
66
66
  }[];
67
67
  id: string;
68
+ name: string;
68
69
  instanceId: string;
69
70
  required?: boolean | undefined;
70
71
  }>) => PropsByInstanceId;
71
72
  export declare const useInstanceProps: (instanceId: Instance["id"]) => Record<string, unknown>;
72
73
  export declare const usePropAsset: (instanceId: Instance["id"], name: string) => {
73
- name: string;
74
74
  type: "font";
75
- format: "ttf" | "woff" | "woff2" | "otf";
76
75
  id: string;
77
- projectId: string;
78
- size: number;
79
- description: string | null;
80
- createdAt: string;
76
+ name: string;
81
77
  meta: ({
82
- style: "normal" | "italic" | "oblique";
83
78
  weight: number;
79
+ style: "normal" | "italic" | "oblique";
84
80
  family: string;
85
81
  } | {
86
82
  variationAxes: Partial<Record<"wght" | "wdth" | "slnt" | "opsz" | "ital" | "GRAD" | "XTRA" | "XOPQ" | "YOPQ" | "YTLC" | "YTUC" | "YTAS" | "YTDE" | "YTFI", {
87
- name: string;
88
83
  min: number;
89
- default: number;
90
84
  max: number;
85
+ name: string;
86
+ default: number;
91
87
  }>>;
92
88
  family: string;
93
89
  }) & ({
94
- style: "normal" | "italic" | "oblique";
95
90
  weight: number;
91
+ style: "normal" | "italic" | "oblique";
96
92
  family: string;
97
93
  } | {
98
94
  variationAxes: Partial<Record<"wght" | "wdth" | "slnt" | "opsz" | "ital" | "GRAD" | "XTRA" | "XOPQ" | "YOPQ" | "YTLC" | "YTUC" | "YTAS" | "YTDE" | "YTFI", {
99
- name: string;
100
95
  min: number;
101
- default: number;
102
96
  max: number;
97
+ name: string;
98
+ default: number;
103
99
  }>>;
104
100
  family: string;
105
101
  } | undefined);
106
- } | {
107
- name: string;
108
- type: "image";
109
- format: string;
110
- id: string;
111
- projectId: string;
112
102
  size: number;
113
103
  description: string | null;
104
+ format: "ttf" | "woff" | "woff2" | "otf";
105
+ projectId: string;
114
106
  createdAt: string;
107
+ } | {
108
+ type: "image";
109
+ id: string;
110
+ name: string;
115
111
  meta: {
116
- width: number;
117
112
  height: number;
113
+ width: number;
118
114
  };
115
+ size: number;
116
+ description: string | null;
117
+ format: string;
118
+ projectId: string;
119
+ createdAt: string;
119
120
  } | undefined;
120
121
  export declare const resolveUrlProp: (instanceId: Instance["id"], name: string, { props, pages, assets, }: {
121
122
  props: PropsByInstanceId;
122
123
  pages: Pages;
123
124
  assets: Map<string, {
124
- name: string;
125
125
  type: "font";
126
- format: "ttf" | "woff" | "woff2" | "otf";
127
126
  id: string;
128
- projectId: string;
129
- size: number;
130
- description: string | null;
131
- createdAt: string;
127
+ name: string;
132
128
  meta: ({
133
- style: "normal" | "italic" | "oblique";
134
129
  weight: number;
130
+ style: "normal" | "italic" | "oblique";
135
131
  family: string;
136
132
  } | {
137
133
  variationAxes: Partial<Record<"wght" | "wdth" | "slnt" | "opsz" | "ital" | "GRAD" | "XTRA" | "XOPQ" | "YOPQ" | "YTLC" | "YTUC" | "YTAS" | "YTDE" | "YTFI", {
138
- name: string;
139
134
  min: number;
140
- default: number;
141
135
  max: number;
136
+ name: string;
137
+ default: number;
142
138
  }>>;
143
139
  family: string;
144
140
  }) & ({
145
- style: "normal" | "italic" | "oblique";
146
141
  weight: number;
142
+ style: "normal" | "italic" | "oblique";
147
143
  family: string;
148
144
  } | {
149
145
  variationAxes: Partial<Record<"wght" | "wdth" | "slnt" | "opsz" | "ital" | "GRAD" | "XTRA" | "XOPQ" | "YOPQ" | "YTLC" | "YTUC" | "YTAS" | "YTDE" | "YTFI", {
150
- name: string;
151
146
  min: number;
152
- default: number;
153
147
  max: number;
148
+ name: string;
149
+ default: number;
154
150
  }>>;
155
151
  family: string;
156
152
  } | undefined);
157
- } | {
158
- name: string;
159
- type: "image";
160
- format: string;
161
- id: string;
162
- projectId: string;
163
153
  size: number;
164
154
  description: string | null;
155
+ format: "ttf" | "woff" | "woff2" | "otf";
156
+ projectId: string;
165
157
  createdAt: string;
158
+ } | {
159
+ type: "image";
160
+ id: string;
161
+ name: string;
166
162
  meta: {
167
- width: number;
168
163
  height: number;
164
+ width: number;
169
165
  };
166
+ size: number;
167
+ description: string | null;
168
+ format: string;
169
+ projectId: string;
170
+ createdAt: string;
170
171
  } | undefined>;
171
172
  }) => {
172
173
  type: "page";
@@ -193,3 +194,4 @@ export declare const usePropUrl: (instanceId: Instance["id"], name: string) => {
193
194
  url: string;
194
195
  } | undefined;
195
196
  export declare const getInstanceIdFromComponentProps: (props: Record<string, unknown>) => string;
197
+ export declare const getIndexWithinAncestorFromComponentProps: (props: Record<string, unknown>) => string | undefined;
@@ -1,12 +1,13 @@
1
- import { type ComponentProps } from "react";
1
+ import { type ForwardRefExoticComponent, type RefAttributes, type ReactNode } from "react";
2
2
  import type { ReadableAtom } from "nanostores";
3
3
  import type { Assets } from "@webstudio-is/asset-uploader";
4
4
  import type { Instance, Instances } from "@webstudio-is/project-build";
5
5
  import type { Components } from "../components/components-utils";
6
6
  import { type Params, type DataSourceValues } from "../context";
7
7
  import type { Pages, PropsByInstanceId } from "../props";
8
- import type { WebstudioComponent } from "./webstudio-component";
9
- export declare const createElementsTree: ({ renderer, imageBaseUrl, assetBaseUrl, instances, rootInstanceId, propsByInstanceIdStore, assetsStore, pagesStore, dataSourceValuesStore, executeEffectfulExpression, onDataSourceUpdate, Component, components, }: Params & {
8
+ import type { WebstudioComponentProps } from "./webstudio-component";
9
+ import type { IndexesWithinAncestors } from "../instance-utils";
10
+ export declare const createElementsTree: ({ renderer, imageBaseUrl, assetBaseUrl, instances, rootInstanceId, propsByInstanceIdStore, assetsStore, pagesStore, dataSourceValuesStore, executeEffectfulExpression, onDataSourceUpdate, indexesWithinAncestors, Component, components, scripts, }: Params & {
10
11
  instances: Map<string, {
11
12
  type: "instance";
12
13
  id: string;
@@ -24,9 +25,11 @@ export declare const createElementsTree: ({ renderer, imageBaseUrl, assetBaseUrl
24
25
  propsByInstanceIdStore: ReadableAtom<PropsByInstanceId>;
25
26
  assetsStore: ReadableAtom<Assets>;
26
27
  pagesStore: ReadableAtom<Pages>;
27
- executeEffectfulExpression: (expression: string, values: DataSourceValues) => DataSourceValues;
28
+ executeEffectfulExpression: (expression: string, args: DataSourceValues, values: DataSourceValues) => DataSourceValues;
28
29
  dataSourceValuesStore: ReadableAtom<DataSourceValues>;
29
30
  onDataSourceUpdate: (newValues: DataSourceValues) => void;
30
- Component: (props: ComponentProps<typeof WebstudioComponent>) => JSX.Element;
31
+ indexesWithinAncestors: IndexesWithinAncestors;
32
+ Component: ForwardRefExoticComponent<WebstudioComponentProps & RefAttributes<HTMLElement>>;
31
33
  components: Components;
32
- }) => JSX.Element | null;
34
+ scripts?: ReactNode;
35
+ }) => import("react/jsx-runtime").JSX.Element | null;
@@ -1,9 +1,10 @@
1
- import { type ComponentProps } from "react";
1
+ import { type ForwardRefExoticComponent, type RefAttributes, type ReactNode } from "react";
2
2
  import { type Build, type Page } from "@webstudio-is/project-build";
3
3
  import type { Asset } from "@webstudio-is/asset-uploader";
4
- import { WebstudioComponent } from "./webstudio-component";
4
+ import { type WebstudioComponentProps } from "./webstudio-component";
5
5
  import type { Components } from "../components/components-utils";
6
6
  import type { Params, DataSourceValues } from "../context";
7
+ import type { IndexesWithinAncestors } from "../instance-utils";
7
8
  export type Data = {
8
9
  page: Page;
9
10
  pages: Array<Page>;
@@ -16,10 +17,12 @@ export type RootPropsData = Omit<Data, "build"> & {
16
17
  };
17
18
  type RootProps = {
18
19
  data: RootPropsData;
20
+ indexesWithinAncestors: IndexesWithinAncestors;
19
21
  executeComputingExpressions: (values: DataSourceValues) => DataSourceValues;
20
- executeEffectfulExpression: (expression: string, values: DataSourceValues) => DataSourceValues;
21
- Component?: (props: ComponentProps<typeof WebstudioComponent>) => JSX.Element;
22
+ executeEffectfulExpression: (expression: string, args: DataSourceValues, values: DataSourceValues) => DataSourceValues;
23
+ Component?: ForwardRefExoticComponent<WebstudioComponentProps & RefAttributes<HTMLElement>>;
22
24
  components: Components;
25
+ scripts?: ReactNode;
23
26
  };
24
- export declare const InstanceRoot: ({ data, executeComputingExpressions, executeEffectfulExpression, Component, components, }: RootProps) => JSX.Element | null;
27
+ export declare const InstanceRoot: ({ data, indexesWithinAncestors, executeComputingExpressions, executeEffectfulExpression, Component, components, scripts, }: RootProps) => JSX.Element | null;
25
28
  export {};
@@ -2,15 +2,24 @@
2
2
  import type { Instance } from "@webstudio-is/project-build";
3
3
  import type { Components } from "../components/components-utils";
4
4
  export declare const renderWebstudioComponentChildren: (children: Array<JSX.Element | string> | undefined) => Array<JSX.Element | string | Array<JSX.Element | string>> | undefined;
5
- type WebstudioComponentProps = {
5
+ export type WebstudioComponentProps = {
6
6
  instance: Instance;
7
7
  instanceSelector: Instance["id"][];
8
8
  children: Array<JSX.Element | string>;
9
9
  components: Components;
10
10
  };
11
- export declare const WebstudioComponent: ({ instance, instanceSelector, children, components, ...rest }: WebstudioComponentProps) => JSX.Element;
12
- export declare const idAttribute = "data-ws-id";
13
- export declare const componentAttribute = "data-ws-component";
14
- export declare const showAttribute = "data-ws-show";
15
- export declare const collapsedAttribute = "data-ws-collapsed";
16
- export {};
11
+ export declare const WebstudioComponent: import("react").ForwardRefExoticComponent<WebstudioComponentProps & import("react").RefAttributes<HTMLElement>>;
12
+ export declare const idAttribute: "data-ws-id";
13
+ export declare const selectorIdAttribute: "data-ws-selector";
14
+ export declare const componentAttribute: "data-ws-component";
15
+ export declare const showAttribute: "data-ws-show";
16
+ export declare const indexAttribute: "data-ws-index";
17
+ export declare const collapsedAttribute: "data-ws-collapsed";
18
+ export type WebstudioAttributes = {
19
+ [idAttribute]?: string | undefined;
20
+ [selectorIdAttribute]?: string | undefined;
21
+ [componentAttribute]?: string | undefined;
22
+ [showAttribute]?: string | undefined;
23
+ [collapsedAttribute]?: string | undefined;
24
+ };
25
+ export declare const splitPropsWithWebstudioAttributes: <P extends WebstudioAttributes>({ [idAttribute]: idAttributeValue, [componentAttribute]: componentAttributeValue, [showAttribute]: showAttributeValue, [collapsedAttribute]: collapsedAttributeValue, [selectorIdAttribute]: parentIdAttributeValue, ...props }: P) => [WebstudioAttributes, Omit<P, keyof WebstudioAttributes>];
package/package.json CHANGED
@@ -1,27 +1,27 @@
1
1
  {
2
2
  "name": "@webstudio-is/react-sdk",
3
- "version": "0.82.0",
3
+ "version": "0.84.0",
4
4
  "description": "Webstudio JavaScript / TypeScript API",
5
5
  "author": "Webstudio <github@webstudio.is>",
6
6
  "homepage": "https://webstudio.is",
7
7
  "type": "module",
8
8
  "devDependencies": {
9
- "@jest/globals": "^29.6.1",
10
- "@remix-run/react": "^1.18.1",
11
- "@types/react": "^18.0.35",
12
- "@types/react-dom": "^18.0.11",
13
- "jest": "^29.6.1",
9
+ "@jest/globals": "^29.6.2",
10
+ "@remix-run/react": "^1.19.2",
11
+ "@types/react": "^18.2.16",
12
+ "@types/react-dom": "^18.2.7",
13
+ "jest": "^29.6.2",
14
14
  "react": "^18.2.0",
15
15
  "react-dom": "^18.2.0",
16
16
  "type-fest": "^3.7.1",
17
- "typescript": "5.1.3",
17
+ "typescript": "5.1.6",
18
18
  "zod": "^3.21.4",
19
- "@webstudio-is/jest-config": "^1.0.6",
19
+ "@webstudio-is/jest-config": "^1.0.7",
20
20
  "@webstudio-is/scripts": "^0.0.0",
21
- "@webstudio-is/tsconfig": "^1.0.6"
21
+ "@webstudio-is/tsconfig": "^1.0.7"
22
22
  },
23
23
  "peerDependencies": {
24
- "@remix-run/react": "^1.18.0",
24
+ "@remix-run/react": "^1.19.1",
25
25
  "react": "^18.2.0",
26
26
  "react-dom": "^18.2.0",
27
27
  "zod": "^3.19.1"
@@ -29,18 +29,17 @@
29
29
  "dependencies": {
30
30
  "@jsep-plugin/assignment": "^1.2.1",
31
31
  "@nanostores/react": "^0.7.1",
32
- "detect-font": "^0.1.5",
33
32
  "html-tags": "^3.3.1",
34
33
  "jsep": "^1.3.8",
35
34
  "nanoevents": "^8.0.0",
36
35
  "nanoid": "^4.0.2",
37
36
  "nanostores": "^0.9.3",
38
- "@webstudio-is/asset-uploader": "^0.82.0",
39
- "@webstudio-is/css-data": "^0.82.0",
40
- "@webstudio-is/css-engine": "^0.82.0",
41
- "@webstudio-is/fonts": "^0.82.0",
42
- "@webstudio-is/generate-arg-types": "^0.82.0",
43
- "@webstudio-is/project-build": "^0.82.0"
37
+ "@webstudio-is/css-data": "^0.84.0",
38
+ "@webstudio-is/css-engine": "^0.84.0",
39
+ "@webstudio-is/fonts": "^0.84.0",
40
+ "@webstudio-is/generate-arg-types": "^0.84.0",
41
+ "@webstudio-is/project-build": "^0.84.0",
42
+ "@webstudio-is/asset-uploader": "^0.84.0"
44
43
  },
45
44
  "exports": {
46
45
  ".": {
@@ -61,7 +60,7 @@
61
60
  "src/*",
62
61
  "!*.test.*"
63
62
  ],
64
- "license": "MIT",
63
+ "license": "AGPL-3.0-or-later",
65
64
  "private": false,
66
65
  "sideEffects": false,
67
66
  "scripts": {
@@ -69,7 +68,7 @@
69
68
  "build": "build-package",
70
69
  "build:args": "generate-arg-types './src/components/*.tsx ./src/app/custom-components/*.tsx !./src/**/*.stories.tsx !./src/**/*.ws.tsx' && prettier --write \"**/*.props.ts\"",
71
70
  "dts": "tsc --project tsconfig.dts.json",
72
- "typecheck": "tsc --noEmit --emitDeclarationOnly false",
71
+ "typecheck": "tsc",
73
72
  "test": "NODE_OPTIONS=--experimental-vm-modules jest --passWithNoTests",
74
73
  "checks": "pnpm typecheck && pnpm test"
75
74
  }
@@ -0,0 +1,117 @@
1
+ import type { ExoticComponent } from "react";
2
+ import type { Instance } from "@webstudio-is/project-build";
3
+ import { getStyleDeclKey } from "@webstudio-is/project-build";
4
+ import type { WsComponentMeta } from "./components/component-meta";
5
+ import { generateDataFromEmbedTemplate } from "./embed-template";
6
+ import { generateCssText } from "./css";
7
+ import { InstanceRoot, WebstudioComponent } from "./tree";
8
+ import {
9
+ decodeVariablesMap,
10
+ encodeDataSourceVariable,
11
+ encodeVariablesMap,
12
+ executeComputingExpressions,
13
+ executeEffectfulExpression,
14
+ } from "./expression";
15
+ import { getIndexesWithinAncestors } from "./instance-utils";
16
+
17
+ export const renderComponentTemplate = ({
18
+ name,
19
+ metas: metasRecord,
20
+ components,
21
+ }: {
22
+ name: Instance["component"];
23
+ metas: Record<string, WsComponentMeta>;
24
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
25
+ components: Record<string, ExoticComponent<any>>;
26
+ }) => {
27
+ const metas = new Map(Object.entries(metasRecord));
28
+ const data = generateDataFromEmbedTemplate(
29
+ metas.get(name)?.template ?? [
30
+ {
31
+ type: "instance",
32
+ component: name,
33
+ children: [],
34
+ },
35
+ ],
36
+ "base"
37
+ );
38
+ const instances: [Instance["id"], Instance][] = [
39
+ [
40
+ "root",
41
+ {
42
+ type: "instance",
43
+ id: "root",
44
+ component: "Box",
45
+ children: data.children,
46
+ },
47
+ ],
48
+ ...data.instances.map(
49
+ (instance) => [instance.id, instance] satisfies [Instance["id"], Instance]
50
+ ),
51
+ ];
52
+ return (
53
+ <>
54
+ <style>
55
+ {generateCssText(
56
+ {
57
+ assets: [],
58
+ breakpoints: [["base", { id: "base", label: "base" }]],
59
+ styles: data.styles.map((item) => [getStyleDeclKey(item), item]),
60
+ styleSourceSelections: data.styleSourceSelections.map((item) => [
61
+ item.instanceId,
62
+ item,
63
+ ]),
64
+ componentMetas: metas,
65
+ },
66
+ { assetBaseUrl: "/" }
67
+ )}
68
+ </style>
69
+ <InstanceRoot
70
+ data={{
71
+ page: {
72
+ path: "",
73
+ id: "",
74
+ name: "",
75
+ title: "",
76
+ meta: {},
77
+ rootInstanceId: "root",
78
+ },
79
+ pages: [],
80
+ assets: [],
81
+ build: {
82
+ instances,
83
+ props: data.props.map((prop) => [prop.id, prop]),
84
+ dataSources: data.dataSources.map((dataSource) => [
85
+ dataSource.id,
86
+ dataSource,
87
+ ]),
88
+ },
89
+ }}
90
+ executeComputingExpressions={(values) => {
91
+ const expressions = new Map<string, string>();
92
+ for (const dataSource of data.dataSources) {
93
+ const name = encodeDataSourceVariable(dataSource.id);
94
+ if (dataSource.type === "expression") {
95
+ expressions.set(name, dataSource.code);
96
+ }
97
+ }
98
+ return decodeVariablesMap(
99
+ executeComputingExpressions(expressions, encodeVariablesMap(values))
100
+ );
101
+ }}
102
+ executeEffectfulExpression={(code, args, values) => {
103
+ return decodeVariablesMap(
104
+ executeEffectfulExpression(code, args, encodeVariablesMap(values))
105
+ );
106
+ }}
107
+ Component={WebstudioComponent}
108
+ components={new Map(Object.entries(components))}
109
+ indexesWithinAncestors={getIndexesWithinAncestors(
110
+ metas,
111
+ new Map(instances),
112
+ ["root"]
113
+ )}
114
+ />
115
+ </>
116
+ );
117
+ };
@@ -22,6 +22,7 @@ export const componentCategories = [
22
22
  "text",
23
23
  "media",
24
24
  "forms",
25
+ "radix",
25
26
  "hidden",
26
27
  ] as const;
27
28
 
@@ -52,7 +53,16 @@ const WsComponentMeta = z.object({
52
53
  type: z.enum(["container", "control", "embed", "rich-text-child"]),
53
54
  requiredAncestors: z.optional(z.array(z.string())),
54
55
  invalidAncestors: z.optional(z.array(z.string())),
56
+ // when this field is specified component receives
57
+ // prop with index of same components withiin specified ancestor
58
+ // important to automatically enumerate collections without
59
+ // naming every item manually
60
+ indexWithinAncestor: z.optional(z.string()),
55
61
  stylable: z.optional(z.boolean()),
62
+ // specifies whether the instance can be deleted,
63
+ // copied or dragged out of its parent instance
64
+ // true by default
65
+ detachable: z.optional(z.boolean()),
56
66
  label: z.string(),
57
67
  description: z.string().optional(),
58
68
  icon: z.string(),
package/src/context.tsx CHANGED
@@ -3,6 +3,7 @@ import { createContext } from "react";
3
3
  import type { Assets } from "@webstudio-is/asset-uploader";
4
4
  import type { DataSource, Instance, Prop } from "@webstudio-is/project-build";
5
5
  import type { Pages, PropsByInstanceId } from "./props";
6
+ import type { IndexesWithinAncestors } from "./instance-utils";
6
7
 
7
8
  export type Params = {
8
9
  renderer?: "canvas" | "preview";
@@ -41,6 +42,7 @@ export const ReactSdkContext = createContext<
41
42
  dataSourceValuesStore: ReadableAtom<DataSourceValues>;
42
43
  executeEffectfulExpression: (
43
44
  expression: string,
45
+ args: DataSourceValues,
44
46
  values: DataSourceValues
45
47
  ) => DataSourceValues;
46
48
  setDataSourceValues: (newValues: DataSourceValues) => void;
@@ -49,6 +51,7 @@ export const ReactSdkContext = createContext<
49
51
  prop: Prop["name"],
50
52
  value: unknown
51
53
  ) => void;
54
+ indexesWithinAncestors: IndexesWithinAncestors;
52
55
  }
53
56
  >({
54
57
  imageBaseUrl: "/",
@@ -66,4 +69,5 @@ export const ReactSdkContext = createContext<
66
69
  setBoundDataSourceValue: () => {
67
70
  throw Error("React SDK setBoundDataSourceValue is not implemented");
68
71
  },
72
+ indexesWithinAncestors: new Map(),
69
73
  });
package/src/css/index.ts CHANGED
@@ -1,4 +1,3 @@
1
- export * from "./get-browser-style";
2
1
  export * from "./global-rules";
3
2
  export * from "./style-rules";
4
3
  export * from "./css";
@@ -86,7 +86,7 @@ export const getPresetStyleRules = (
86
86
  const presetStyleRules = new Map<string, Style>();
87
87
  for (const [tag, styles] of Object.entries(presetStyle)) {
88
88
  for (const styleDecl of styles) {
89
- const selector = `${tag}:where([${componentAttribute}=${component}])${
89
+ const selector = `${tag}:where([${componentAttribute}="${component}"])${
90
90
  styleDecl.state ?? ""
91
91
  }`;
92
92
  let rule = presetStyleRules.get(selector);