@webstudio-is/react-sdk 0.0.0-017f1bd
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/LICENSE +661 -0
- package/README.md +4 -0
- package/lib/index.js +845 -0
- package/lib/runtime.js +221 -0
- package/lib/types/__generated__/standard-attributes.d.ts +2 -0
- package/lib/types/component-generator.d.ts +47 -0
- package/lib/types/component-generator.test.d.ts +1 -0
- package/lib/types/components/components-utils.d.ts +8 -0
- package/lib/types/context.d.ts +33 -0
- package/lib/types/hook.d.ts +34 -0
- package/lib/types/hook.test.d.ts +1 -0
- package/lib/types/index.d.ts +7 -0
- package/lib/types/page-settings-canonical-link.d.ts +17 -0
- package/lib/types/page-settings-meta.d.ts +11 -0
- package/lib/types/page-settings-title.d.ts +17 -0
- package/lib/types/props.d.ts +1435 -0
- package/lib/types/props.test.d.ts +1 -0
- package/lib/types/remix.d.ts +20 -0
- package/lib/types/remix.test.d.ts +1 -0
- package/lib/types/runtime.d.ts +11 -0
- package/lib/types/variable-state.d.ts +2 -0
- package/package.json +64 -0
- package/placeholder.d.ts +64 -0
package/lib/runtime.js
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
// src/context.tsx
|
|
2
|
+
import { createContext, useContext, useMemo } from "react";
|
|
3
|
+
import {
|
|
4
|
+
createJsonStringifyProxy,
|
|
5
|
+
isPlainObject
|
|
6
|
+
} from "@webstudio-is/sdk/runtime";
|
|
7
|
+
var ReactSdkContext = createContext({
|
|
8
|
+
assetBaseUrl: "/",
|
|
9
|
+
imageLoader: ({ src }) => src,
|
|
10
|
+
videoLoader: ({ src }) => src,
|
|
11
|
+
resources: {},
|
|
12
|
+
breakpoints: [],
|
|
13
|
+
onError: (error) => {
|
|
14
|
+
console.error(error);
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
var useResource = (name) => {
|
|
18
|
+
const { resources } = useContext(ReactSdkContext);
|
|
19
|
+
const resource = resources[name];
|
|
20
|
+
const resourceMemozied = useMemo(
|
|
21
|
+
() => isPlainObject(resource) ? createJsonStringifyProxy(resource) : resource,
|
|
22
|
+
[resource]
|
|
23
|
+
);
|
|
24
|
+
return resourceMemozied;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// src/hook.ts
|
|
28
|
+
var getClosestInstance = (instancePath, currentInstance, closestComponent) => {
|
|
29
|
+
let matched = false;
|
|
30
|
+
for (const instance of instancePath) {
|
|
31
|
+
if (currentInstance === instance) {
|
|
32
|
+
matched = true;
|
|
33
|
+
}
|
|
34
|
+
if (matched && instance.component === closestComponent) {
|
|
35
|
+
return instance;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// src/variable-state.tsx
|
|
41
|
+
import { useState, useMemo as useMemo2 } from "react";
|
|
42
|
+
import {
|
|
43
|
+
createJsonStringifyProxy as createJsonStringifyProxy2,
|
|
44
|
+
isPlainObject as isPlainObject2
|
|
45
|
+
} from "@webstudio-is/sdk/runtime";
|
|
46
|
+
var useVariableState = (initialState) => {
|
|
47
|
+
const [state, setState] = useState(initialState);
|
|
48
|
+
const value = useMemo2(
|
|
49
|
+
() => isPlainObject2(state) ? createJsonStringifyProxy2(state) : state,
|
|
50
|
+
[state]
|
|
51
|
+
);
|
|
52
|
+
return [value, setState];
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// src/page-settings-meta.tsx
|
|
56
|
+
import { useEffect, useState as useState2 } from "react";
|
|
57
|
+
import { jsx } from "react/jsx-runtime";
|
|
58
|
+
var isElementRenderedWithReact = (element) => {
|
|
59
|
+
return Object.keys(element).some((key) => key.startsWith("__react"));
|
|
60
|
+
};
|
|
61
|
+
var isServer = typeof window === "undefined";
|
|
62
|
+
var Meta = (props) => {
|
|
63
|
+
const [localProps, setLocalProps] = useState2();
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
const selector = `meta[${props.name ? `name="${props.name}"` : `property="${props.property}"`}]`;
|
|
66
|
+
let allMetas = document.querySelectorAll(selector);
|
|
67
|
+
for (const meta of allMetas) {
|
|
68
|
+
if (!isElementRenderedWithReact(meta)) {
|
|
69
|
+
meta.remove();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
allMetas = document.querySelectorAll(selector);
|
|
73
|
+
if (allMetas.length === 0) {
|
|
74
|
+
setLocalProps(props);
|
|
75
|
+
}
|
|
76
|
+
}, [props]);
|
|
77
|
+
if (isServer) {
|
|
78
|
+
return /* @__PURE__ */ jsx("meta", { ...props });
|
|
79
|
+
}
|
|
80
|
+
if (localProps === void 0) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
return /* @__PURE__ */ jsx("meta", { ...localProps });
|
|
84
|
+
};
|
|
85
|
+
var PageSettingsMeta = ({
|
|
86
|
+
url,
|
|
87
|
+
host,
|
|
88
|
+
siteName,
|
|
89
|
+
pageMeta,
|
|
90
|
+
imageLoader,
|
|
91
|
+
assetBaseUrl
|
|
92
|
+
}) => {
|
|
93
|
+
const metas = [];
|
|
94
|
+
if (url !== void 0) {
|
|
95
|
+
metas.push({
|
|
96
|
+
property: "og:url",
|
|
97
|
+
content: url
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
if (pageMeta.title) {
|
|
101
|
+
metas.push({
|
|
102
|
+
property: "og:title",
|
|
103
|
+
content: pageMeta.title
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
metas.push({ property: "og:type", content: "website" });
|
|
107
|
+
if (siteName) {
|
|
108
|
+
metas.push({
|
|
109
|
+
property: "og:site_name",
|
|
110
|
+
content: siteName
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
if (pageMeta.excludePageFromSearch) {
|
|
114
|
+
metas.push({
|
|
115
|
+
name: "robots",
|
|
116
|
+
content: "noindex, nofollow"
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
if (pageMeta.description) {
|
|
120
|
+
metas.push({
|
|
121
|
+
name: "description",
|
|
122
|
+
content: pageMeta.description
|
|
123
|
+
});
|
|
124
|
+
metas.push({
|
|
125
|
+
property: "og:description",
|
|
126
|
+
content: pageMeta.description
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
if (pageMeta.socialImageAssetName) {
|
|
130
|
+
metas.push({
|
|
131
|
+
property: "og:image",
|
|
132
|
+
content: `https://${host}${imageLoader({
|
|
133
|
+
src: `${assetBaseUrl}${pageMeta.socialImageAssetName}`,
|
|
134
|
+
// Do not transform social image (not enough information do we need to do this)
|
|
135
|
+
format: "raw"
|
|
136
|
+
})}`
|
|
137
|
+
});
|
|
138
|
+
} else if (pageMeta.socialImageUrl) {
|
|
139
|
+
metas.push({
|
|
140
|
+
property: "og:image",
|
|
141
|
+
content: pageMeta.socialImageUrl
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
metas.push(...pageMeta.custom);
|
|
145
|
+
const isTwitterCardSizeDefined = pageMeta.custom.some(
|
|
146
|
+
(meta) => meta.property === "twitter:card"
|
|
147
|
+
);
|
|
148
|
+
if ((pageMeta.socialImageAssetName !== void 0 || pageMeta.socialImageUrl !== void 0) && isTwitterCardSizeDefined === false) {
|
|
149
|
+
metas.push({ property: "twitter:card", content: "summary_large_image" });
|
|
150
|
+
}
|
|
151
|
+
return metas.map((meta, index) => /* @__PURE__ */ jsx(Meta, { ...meta }, index));
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
// src/page-settings-title.tsx
|
|
155
|
+
import { useEffect as useEffect2, useState as useState3 } from "react";
|
|
156
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
157
|
+
var isServer2 = typeof window === "undefined";
|
|
158
|
+
var PageSettingsTitle = (props) => {
|
|
159
|
+
const [localProps, setLocalProps] = useState3();
|
|
160
|
+
useEffect2(() => {
|
|
161
|
+
const selector = `head > title`;
|
|
162
|
+
let allTitles = document.querySelectorAll(selector);
|
|
163
|
+
for (const meta of allTitles) {
|
|
164
|
+
if (!isElementRenderedWithReact(meta)) {
|
|
165
|
+
meta.remove();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
allTitles = document.querySelectorAll(selector);
|
|
169
|
+
if (allTitles.length === 0) {
|
|
170
|
+
setLocalProps(props);
|
|
171
|
+
}
|
|
172
|
+
}, [props]);
|
|
173
|
+
if (isServer2) {
|
|
174
|
+
return /* @__PURE__ */ jsx2("title", { ...props });
|
|
175
|
+
}
|
|
176
|
+
if (localProps === void 0) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
return /* @__PURE__ */ jsx2("title", { ...localProps });
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// src/page-settings-canonical-link.tsx
|
|
183
|
+
import { useEffect as useEffect3, useState as useState4 } from "react";
|
|
184
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
185
|
+
var isServer3 = typeof window === "undefined";
|
|
186
|
+
var PageSettingsCanonicalLink = (props) => {
|
|
187
|
+
const [localProps, setLocalProps] = useState4();
|
|
188
|
+
useEffect3(() => {
|
|
189
|
+
const selector = `head > link[rel="canonical"]`;
|
|
190
|
+
let allLinks = document.querySelectorAll(selector);
|
|
191
|
+
for (const meta of allLinks) {
|
|
192
|
+
if (!isElementRenderedWithReact(meta)) {
|
|
193
|
+
meta.remove();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
allLinks = document.querySelectorAll(selector);
|
|
197
|
+
if (allLinks.length === 0) {
|
|
198
|
+
setLocalProps(props);
|
|
199
|
+
}
|
|
200
|
+
}, [props]);
|
|
201
|
+
if (isServer3) {
|
|
202
|
+
return /* @__PURE__ */ jsx3("link", { rel: "canonical", ...props });
|
|
203
|
+
}
|
|
204
|
+
if (localProps === void 0) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
return /* @__PURE__ */ jsx3("link", { rel: "canonical", ...localProps });
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
// src/runtime.ts
|
|
211
|
+
var xmlNodeTagSuffix = "-ws-xml-node-fb77f896-8e96-40b9-b8f8-90a4e70d724a";
|
|
212
|
+
export {
|
|
213
|
+
PageSettingsCanonicalLink,
|
|
214
|
+
PageSettingsMeta,
|
|
215
|
+
PageSettingsTitle,
|
|
216
|
+
ReactSdkContext,
|
|
217
|
+
getClosestInstance,
|
|
218
|
+
useResource,
|
|
219
|
+
useVariableState,
|
|
220
|
+
xmlNodeTagSuffix
|
|
221
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Instances, Instance, Props, Scope, DataSources, Prop, WsComponentMeta, IndexesWithinAncestors } from "@webstudio-is/sdk";
|
|
2
|
+
export declare const generateJsxElement: ({ context, scope, metas, tagsOverrides, instance, props, dataSources, usedDataSources, indexesWithinAncestors, children, classesMap, }: {
|
|
3
|
+
context?: "expression" | "jsx";
|
|
4
|
+
scope: Scope;
|
|
5
|
+
metas: Map<Instance["component"], WsComponentMeta>;
|
|
6
|
+
/**
|
|
7
|
+
* Record<tag, componentDescriptor>
|
|
8
|
+
*/
|
|
9
|
+
tagsOverrides?: Record<string, string>;
|
|
10
|
+
instance: Instance;
|
|
11
|
+
props: Props;
|
|
12
|
+
dataSources: DataSources;
|
|
13
|
+
usedDataSources: DataSources;
|
|
14
|
+
indexesWithinAncestors: IndexesWithinAncestors;
|
|
15
|
+
children: string;
|
|
16
|
+
classesMap?: Map<string, Array<string>>;
|
|
17
|
+
}) => string;
|
|
18
|
+
export declare const generateJsxChildren: ({ scope, metas, tagsOverrides, children, instances, props, dataSources, usedDataSources, indexesWithinAncestors, classesMap, excludePlaceholders, }: {
|
|
19
|
+
scope: Scope;
|
|
20
|
+
metas: Map<Instance["component"], WsComponentMeta>;
|
|
21
|
+
tagsOverrides?: Record<string, string>;
|
|
22
|
+
children: Instance["children"];
|
|
23
|
+
instances: Instances;
|
|
24
|
+
props: Props;
|
|
25
|
+
dataSources: DataSources;
|
|
26
|
+
usedDataSources: DataSources;
|
|
27
|
+
indexesWithinAncestors: IndexesWithinAncestors;
|
|
28
|
+
classesMap?: Map<string, Array<string>>;
|
|
29
|
+
excludePlaceholders?: boolean;
|
|
30
|
+
}) => string;
|
|
31
|
+
export declare const generateWebstudioComponent: ({ scope, name, rootInstanceId, parameters, instances, props, dataSources, metas, tagsOverrides, classesMap, }: {
|
|
32
|
+
scope: Scope;
|
|
33
|
+
name: string;
|
|
34
|
+
rootInstanceId: Instance["id"];
|
|
35
|
+
parameters: Extract<Prop, {
|
|
36
|
+
type: "parameter";
|
|
37
|
+
}>[];
|
|
38
|
+
instances: Instances;
|
|
39
|
+
props: Props;
|
|
40
|
+
dataSources: DataSources;
|
|
41
|
+
classesMap: Map<string, Array<string>>;
|
|
42
|
+
metas: Map<Instance["component"], WsComponentMeta>;
|
|
43
|
+
/**
|
|
44
|
+
* Record<tag, componentDescriptor>
|
|
45
|
+
*/
|
|
46
|
+
tagsOverrides?: Record<string, string>;
|
|
47
|
+
}) => string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { componentAttribute, idAttribute, selectorIdAttribute } from "../props";
|
|
2
|
+
export type AnyComponent = React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>, "ref"> & WebstudioComponentSystemProps & React.RefAttributes<HTMLElement>>;
|
|
3
|
+
export type Components = Map<string, AnyComponent>;
|
|
4
|
+
export type WebstudioComponentSystemProps = {
|
|
5
|
+
[componentAttribute]: string;
|
|
6
|
+
[idAttribute]: string;
|
|
7
|
+
[selectorIdAttribute]: string;
|
|
8
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ImageLoader, VideoLoader } from "@webstudio-is/image";
|
|
2
|
+
export type Params = {
|
|
3
|
+
/**
|
|
4
|
+
* When rendering a published version, there is no renderer defined.
|
|
5
|
+
* - canvas is the builder canvas in dev mode
|
|
6
|
+
* - preview is the preview mode in builder
|
|
7
|
+
*/
|
|
8
|
+
renderer?: "canvas" | "preview";
|
|
9
|
+
/**
|
|
10
|
+
* Base url or base path for any asset with ending slash.
|
|
11
|
+
* Used to load assets like fonts or images in styles
|
|
12
|
+
* Concatinated with "name".
|
|
13
|
+
*
|
|
14
|
+
* For example
|
|
15
|
+
* /s/uploads/
|
|
16
|
+
* /asset/file/
|
|
17
|
+
* https://assets-dev.webstudio.is/
|
|
18
|
+
* https://assets.webstudio.is/
|
|
19
|
+
*/
|
|
20
|
+
assetBaseUrl: string;
|
|
21
|
+
};
|
|
22
|
+
export declare const ReactSdkContext: import("react").Context<Params & {
|
|
23
|
+
imageLoader: ImageLoader;
|
|
24
|
+
videoLoader?: VideoLoader;
|
|
25
|
+
resources: Record<string, any>;
|
|
26
|
+
breakpoints: {
|
|
27
|
+
id: string;
|
|
28
|
+
minWidth?: number;
|
|
29
|
+
maxWidth?: number;
|
|
30
|
+
}[];
|
|
31
|
+
onError: (error: unknown) => void;
|
|
32
|
+
}>;
|
|
33
|
+
export declare const useResource: (name: string) => any;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { IndexesWithinAncestors, Instance, Prop } from "@webstudio-is/sdk";
|
|
2
|
+
export type InstanceData = {
|
|
3
|
+
id: Instance["id"];
|
|
4
|
+
instanceKey: string;
|
|
5
|
+
component: Instance["component"];
|
|
6
|
+
tag?: Instance["tag"];
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Hooks are subscriptions to builder events
|
|
10
|
+
* with limited way to interact with it.
|
|
11
|
+
* Called independently from components.
|
|
12
|
+
*/
|
|
13
|
+
export type HookContext = {
|
|
14
|
+
indexesWithinAncestors: IndexesWithinAncestors;
|
|
15
|
+
getPropValue: (instanceData: InstanceData, propName: Prop["name"]) => unknown;
|
|
16
|
+
setMemoryProp: (instanceData: InstanceData, propName: Prop["name"], value: unknown) => void;
|
|
17
|
+
};
|
|
18
|
+
export type InstancePath = InstanceData[];
|
|
19
|
+
type NavigatorEvent = {
|
|
20
|
+
/**
|
|
21
|
+
* List of instances from selected to the root
|
|
22
|
+
*/
|
|
23
|
+
instancePath: InstancePath;
|
|
24
|
+
};
|
|
25
|
+
export type Hook = {
|
|
26
|
+
onNavigatorSelect?: (context: HookContext, event: NavigatorEvent) => void;
|
|
27
|
+
onNavigatorUnselect?: (context: HookContext, event: NavigatorEvent) => void;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Find closest matching instance by component name
|
|
31
|
+
* by lookup in instance path
|
|
32
|
+
*/
|
|
33
|
+
export declare const getClosestInstance: (instancePath: InstancePath, currentInstance: InstanceData, closestComponent: InstanceData["component"]) => InstanceData | undefined;
|
|
34
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from "./remix";
|
|
2
|
+
export * from "./components/components-utils";
|
|
3
|
+
export * from "./props";
|
|
4
|
+
export type * from "./context";
|
|
5
|
+
export type * from "./hook";
|
|
6
|
+
export { generateWebstudioComponent, generateJsxElement, generateJsxChildren, } from "./component-generator";
|
|
7
|
+
export * from "./__generated__/standard-attributes";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
type PageSettingsCanonicalLinkProps = {
|
|
2
|
+
href: string;
|
|
3
|
+
};
|
|
4
|
+
/**
|
|
5
|
+
* Link canonical tag are deduplicated on the server using the HTMLRewriter interface.
|
|
6
|
+
* This is not full deduplication. We simply skip rendering Page Setting link
|
|
7
|
+
* if it has already been rendered using HeadSlot/HeadLink.
|
|
8
|
+
* To prevent React on the client from re-adding the removed link tag, we skip rendering them client-side.
|
|
9
|
+
* This approach works because React retains server-rendered link tag as long as they are not re-rendered by the client.
|
|
10
|
+
*
|
|
11
|
+
* The following component behavior ensures this:
|
|
12
|
+
* 1. On the server: Render link tag as usual.
|
|
13
|
+
* 2. On the client: Before rendering, remove any link tag with the same `name` or `property` that were not rendered by Client React,
|
|
14
|
+
* and then proceed with rendering as usual.
|
|
15
|
+
*/
|
|
16
|
+
export declare const PageSettingsCanonicalLink: (props: PageSettingsCanonicalLinkProps) => import("react/jsx-runtime").JSX.Element | undefined;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ImageLoader } from "@webstudio-is/image";
|
|
2
|
+
import type { PageMeta } from "@webstudio-is/sdk";
|
|
3
|
+
export declare const isElementRenderedWithReact: (element: Element) => boolean;
|
|
4
|
+
export declare const PageSettingsMeta: ({ url, host, siteName, pageMeta, imageLoader, assetBaseUrl, }: {
|
|
5
|
+
url?: string;
|
|
6
|
+
host: string;
|
|
7
|
+
siteName: string;
|
|
8
|
+
pageMeta: PageMeta;
|
|
9
|
+
imageLoader: ImageLoader;
|
|
10
|
+
assetBaseUrl: string;
|
|
11
|
+
}) => import("react/jsx-runtime").JSX.Element[];
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
type PageSettingsTitleProps = {
|
|
2
|
+
children: string;
|
|
3
|
+
};
|
|
4
|
+
/**
|
|
5
|
+
* Title tags are deduplicated on the server using the HTMLRewriter interface.
|
|
6
|
+
* This is not full deduplication. We simply skip rendering Page Setting title
|
|
7
|
+
* if it has already been rendered using HeadSlot/HeadTitle.
|
|
8
|
+
* To prevent React on the client from re-adding the removed title tag, we skip rendering them client-side.
|
|
9
|
+
* This approach works because React retains server-rendered title tag as long as they are not re-rendered by the client.
|
|
10
|
+
*
|
|
11
|
+
* The following component behavior ensures this:
|
|
12
|
+
* 1. On the server: Render title tag as usual.
|
|
13
|
+
* 2. On the client: Before rendering, remove any title tag with the same `name` or `property` that were not rendered by Client React,
|
|
14
|
+
* and then proceed with rendering as usual.
|
|
15
|
+
*/
|
|
16
|
+
export declare const PageSettingsTitle: (props: PageSettingsTitleProps) => import("react/jsx-runtime").JSX.Element | undefined;
|
|
17
|
+
export {};
|