@slicemachine/adapter-next 0.3.37-beta.7 → 0.3.37-beta.8

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 (61) hide show
  1. package/dist/hooks/project-init.cjs +64 -14
  2. package/dist/hooks/project-init.cjs.map +1 -1
  3. package/dist/hooks/project-init.js +64 -14
  4. package/dist/hooks/project-init.js.map +1 -1
  5. package/dist/hooks/sliceSimulator-setup-read.cjs +50 -13
  6. package/dist/hooks/sliceSimulator-setup-read.cjs.map +1 -1
  7. package/dist/hooks/sliceSimulator-setup-read.js +50 -13
  8. package/dist/hooks/sliceSimulator-setup-read.js.map +1 -1
  9. package/dist/simulator/SliceSimulator.cjs +7 -12
  10. package/dist/simulator/SliceSimulator.cjs.map +1 -1
  11. package/dist/simulator/SliceSimulator.d.ts +11 -3
  12. package/dist/simulator/SliceSimulator.js +8 -13
  13. package/dist/simulator/SliceSimulator.js.map +1 -1
  14. package/dist/simulator/SliceSimulatorWrapper.cjs +19 -0
  15. package/dist/simulator/SliceSimulatorWrapper.cjs.map +1 -0
  16. package/dist/simulator/SliceSimulatorWrapper.d.ts +14 -0
  17. package/dist/simulator/SliceSimulatorWrapper.js +19 -0
  18. package/dist/simulator/SliceSimulatorWrapper.js.map +1 -0
  19. package/dist/simulator/getSlices.cjs +7 -0
  20. package/dist/simulator/getSlices.cjs.map +1 -0
  21. package/dist/simulator/getSlices.d.ts +9 -0
  22. package/dist/simulator/getSlices.js +7 -0
  23. package/dist/simulator/getSlices.js.map +1 -0
  24. package/dist/simulator/index.d.ts +2 -0
  25. package/dist/simulator/react-server/SliceSimulator.cjs +76 -0
  26. package/dist/simulator/react-server/SliceSimulator.cjs.map +1 -0
  27. package/dist/simulator/react-server/SliceSimulator.d.ts +7 -0
  28. package/dist/simulator/react-server/SliceSimulator.js +59 -0
  29. package/dist/simulator/react-server/SliceSimulator.js.map +1 -0
  30. package/dist/simulator/react-server/actions.cjs +10 -0
  31. package/dist/simulator/react-server/actions.cjs.map +1 -0
  32. package/dist/simulator/react-server/actions.d.ts +1 -0
  33. package/dist/simulator/react-server/actions.js +10 -0
  34. package/dist/simulator/react-server/actions.js.map +1 -0
  35. package/dist/simulator/react-server/getSlices.cjs +9 -0
  36. package/dist/simulator/react-server/getSlices.cjs.map +1 -0
  37. package/dist/simulator/react-server/getSlices.d.ts +2 -0
  38. package/dist/simulator/react-server/getSlices.js +9 -0
  39. package/dist/simulator/react-server/getSlices.js.map +1 -0
  40. package/dist/simulator/react-server/index.d.ts +2 -0
  41. package/dist/simulator/react-server.cjs +7 -0
  42. package/dist/simulator/react-server.cjs.map +1 -0
  43. package/dist/simulator/react-server.js +7 -0
  44. package/dist/simulator/react-server.js.map +1 -0
  45. package/dist/simulator/types.d.ts +11 -0
  46. package/dist/simulator.cjs +2 -0
  47. package/dist/simulator.cjs.map +1 -1
  48. package/dist/simulator.js +3 -1
  49. package/dist/simulator.js.map +1 -1
  50. package/package.json +14 -5
  51. package/src/hooks/project-init.ts +64 -13
  52. package/src/hooks/sliceSimulator-setup-read.ts +64 -14
  53. package/src/simulator/SliceSimulator.tsx +49 -60
  54. package/src/simulator/SliceSimulatorWrapper.tsx +68 -0
  55. package/src/simulator/getSlices.ts +19 -0
  56. package/src/simulator/index.ts +3 -0
  57. package/src/simulator/react-server/SliceSimulator.tsx +113 -0
  58. package/src/simulator/react-server/actions.ts +7 -0
  59. package/src/simulator/react-server/getSlices.ts +16 -0
  60. package/src/simulator/react-server/index.ts +4 -0
  61. package/src/simulator/types.ts +11 -0
@@ -1 +1 @@
1
- {"version":3,"file":"simulator.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
1
+ {"version":3,"file":"simulator.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;"}
package/dist/simulator.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { SliceSimulator } from "./simulator/SliceSimulator.js";
2
+ import { getSlices } from "./simulator/getSlices.js";
2
3
  export {
3
- SliceSimulator
4
+ SliceSimulator,
5
+ getSlices
4
6
  };
5
7
  //# sourceMappingURL=simulator.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"simulator.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
1
+ {"version":3,"file":"simulator.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slicemachine/adapter-next",
3
- "version": "0.3.37-beta.7",
3
+ "version": "0.3.37-beta.8",
4
4
  "description": "Slice Machine adapter for Next.js.",
5
5
  "keywords": [
6
6
  "typescript",
@@ -21,9 +21,16 @@
21
21
  "import": "./dist/index.js"
22
22
  },
23
23
  "./simulator": {
24
- "types": "./dist/simulator/index.d.ts",
25
- "require": "./dist/simulator.cjs",
26
- "import": "./dist/simulator.js"
24
+ "react-server": {
25
+ "types": "./dist/simulator/react-server/index.d.ts",
26
+ "require": "./dist/simulator/react-server.cjs",
27
+ "import": "./dist/simulator/react-server.js"
28
+ },
29
+ "default": {
30
+ "types": "./dist/simulator/index.d.ts",
31
+ "require": "./dist/simulator.cjs",
32
+ "import": "./dist/simulator.js"
33
+ }
27
34
  },
28
35
  "./package.json": "./package.json"
29
36
  },
@@ -61,11 +68,12 @@
61
68
  "dependencies": {
62
69
  "@prismicio/simulator": "^0.1.4",
63
70
  "@prismicio/types-internal": "^2.2.0",
64
- "@slicemachine/plugin-kit": "0.4.37-beta.7",
71
+ "@slicemachine/plugin-kit": "0.4.37-beta.8",
65
72
  "common-tags": "^1.8.2",
66
73
  "fp-ts": "^2.13.1",
67
74
  "io-ts": "^2.2.20",
68
75
  "io-ts-types": "^0.5.19",
76
+ "lz-string": "1.5.0",
69
77
  "monocle-ts": "^2.3.13",
70
78
  "newtype-ts": "^0.3.5",
71
79
  "node-fetch": "^3.3.1",
@@ -86,6 +94,7 @@
86
94
  "eslint-config-prettier": "9.0.0",
87
95
  "eslint-plugin-prettier": "5.0.1",
88
96
  "eslint-plugin-tsdoc": "0.2.17",
97
+ "next": "14.1.3",
89
98
  "prettier": "3.0.3",
90
99
  "prettier-plugin-jsdoc": "1.1.1",
91
100
  "react": "18.2.0",
@@ -251,6 +251,10 @@ const createSliceSimulatorPage = async ({
251
251
  helpers,
252
252
  options,
253
253
  }: CreateSliceSimulatorPageArgs) => {
254
+ const isTypeScriptProject = await checkIsTypeScriptProject({
255
+ helpers,
256
+ options,
257
+ });
254
258
  const hasSrcDirectory = await checkHasSrcDirectory({ helpers });
255
259
  const hasAppRouter = await checkHasAppRouter({ helpers });
256
260
 
@@ -270,22 +274,69 @@ const createSliceSimulatorPage = async ({
270
274
  return;
271
275
  }
272
276
 
273
- const contents = source`
274
- "use client"
277
+ let contents;
275
278
 
276
- import { SliceSimulator } from "@slicemachine/adapter-next/simulator";
277
- import { SliceZone } from "@prismicio/react";
279
+ if (hasAppRouter) {
280
+ if (isTypeScriptProject) {
281
+ contents = source`
282
+ import {
283
+ SliceSimulator,
284
+ SliceSimulatorParams,
285
+ getSlices,
286
+ } from "@slicemachine/adapter-next/simulator";
287
+ import { SliceZone } from "@prismicio/react";
288
+
289
+ import { components } from "../../slices";
290
+
291
+ export default function SliceSimulatorPage({
292
+ searchParams,
293
+ }: SliceSimulatorParams) {
294
+ const slices = getSlices(searchParams.state);
295
+
296
+ return (
297
+ <SliceSimulator>
298
+ <SliceZone slices={slices} components={components} />
299
+ </SliceSimulator>
300
+ );
301
+ }
302
+ `;
303
+ } else {
304
+ contents = source`
305
+ import {
306
+ SliceSimulator,
307
+ getSlices,
308
+ } from "@slicemachine/adapter-next/simulator";
309
+ import { SliceZone } from "@prismicio/react";
310
+
311
+ import { components } from "../../slices";
312
+
313
+ export default function SliceSimulatorPage({ searchParams }) {
314
+ const slices = getSlices(searchParams.state);
315
+
316
+ return (
317
+ <SliceSimulator>
318
+ <SliceZone slices={slices} components={components} />
319
+ </SliceSimulator>
320
+ );
321
+ }
322
+ `;
323
+ }
324
+ } else {
325
+ contents = source`
326
+ import { SliceSimulator } from "@slicemachine/adapter-next/simulator";
327
+ import { SliceZone } from "@prismicio/react";
278
328
 
279
- import { components } from "${hasAppRouter ? "../.." : ".."}/slices";
329
+ import { components } from "../slices";
280
330
 
281
- export default function SliceSimulatorPage() {
282
- return (
283
- <SliceSimulator
284
- sliceZone={(props) => <SliceZone {...props} components={components} />}
285
- />
286
- );
287
- }
288
- `;
331
+ export default function SliceSimulatorPage() {
332
+ return (
333
+ <SliceSimulator
334
+ sliceZone={(props) => <SliceZone {...props} components={components} />}
335
+ />
336
+ );
337
+ }
338
+ `;
339
+ }
289
340
 
290
341
  await writeProjectFile({
291
342
  filename,
@@ -10,6 +10,7 @@ import fetch, { Response } from "node-fetch";
10
10
  import { getJSFileExtension } from "../lib/getJSFileExtension";
11
11
 
12
12
  import type { PluginOptions } from "../types";
13
+ import { checkHasAppRouter } from "../lib/checkHasAppRouter";
13
14
 
14
15
  const REQUIRED_DEPENDENCIES = ["@prismicio/react"];
15
16
 
@@ -79,29 +80,78 @@ const createStep2 = async ({
79
80
  helpers,
80
81
  options,
81
82
  }: Args): Promise<SliceSimulatorSetupStep> => {
83
+ const hasAppRouter = await checkHasAppRouter({ helpers });
84
+
82
85
  const extension = await getJSFileExtension({ helpers, options, jsx: true });
86
+
87
+ if (hasAppRouter) {
88
+ const fileName = `page.${extension}`;
89
+ const filePath = helpers.joinPathFromRoot(
90
+ "app",
91
+ "slice-simulator",
92
+ fileName,
93
+ );
94
+
95
+ const fileContents = await helpers.format(
96
+ source`
97
+ import {
98
+ SliceSimulator,
99
+ SliceSimulatorParams,
100
+ getSlices,
101
+ } from "@slicemachine/adapter-next/simulator";
102
+ import { SliceZone } from "@prismicio/react";
103
+
104
+ import { components } from "../../slices";
105
+
106
+ export default function SliceSimulatorPage({
107
+ searchParams,
108
+ }: SliceSimulatorParams) {
109
+ const slices = getSlices(searchParams.state);
110
+
111
+ return (
112
+ <SliceSimulator>
113
+ <SliceZone slices={slices} components={components} />
114
+ </SliceSimulator>
115
+ );
116
+ }
117
+ `,
118
+ filePath,
119
+ { includeNewlineAtEnd: false },
120
+ );
121
+
122
+ return {
123
+ title: "Create a page for the simulator",
124
+ description: `In your \`app\` directory, create a file called \`slice-simulator/${fileName}\` containing this code.`,
125
+ body: source`
126
+ In your \`app\` directory, create a file called \`slice-simulator/${fileName}\` and add the following code. This route will be used to simulate and develop your components.
127
+
128
+ ~~~tsx
129
+ ${fileContents}
130
+ ~~~
131
+ `,
132
+ };
133
+ }
134
+
83
135
  const fileName = `slice-simulator.${extension}`;
84
136
  const filePath = helpers.joinPathFromRoot("pages", fileName);
85
137
 
86
138
  const fileContents = await helpers.format(
87
139
  source`
88
- import { SliceSimulator } from "@slicemachine/adapter-next/simulator";
89
- import { SliceZone } from "@prismicio/react";
140
+ import { SliceSimulator } from "@slicemachine/adapter-next/simulator";
141
+ import { SliceZone } from "@prismicio/react";
90
142
 
91
- import { components } from "../slices";
143
+ import { components } from "../slices";
92
144
 
93
- export default function SliceSimulatorPage() {
94
- return (
95
- <SliceSimulator
96
- sliceZone={(props) => <SliceZone {...props} components={components} />}
97
- />
98
- );
99
- }
100
- `,
145
+ export default function SliceSimulatorPage() {
146
+ return (
147
+ <SliceSimulator
148
+ sliceZone={(props) => <SliceZone {...props} components={components} />}
149
+ />
150
+ );
151
+ }
152
+ `,
101
153
  filePath,
102
- {
103
- includeNewlineAtEnd: false,
104
- },
154
+ { includeNewlineAtEnd: false },
105
155
  );
106
156
 
107
157
  return {
@@ -7,15 +7,12 @@ import {
7
7
  SimulatorManager,
8
8
  SliceSimulatorState,
9
9
  StateEventType,
10
- disableEventHandler,
11
10
  getDefaultMessage,
12
- getDefaultProps,
13
11
  getDefaultSlices,
14
- onClickHandler,
15
- simulatorClass,
16
- simulatorRootClass,
17
12
  } from "@prismicio/simulator/kit";
18
13
 
14
+ import { SliceSimulatorWrapper } from "./SliceSimulatorWrapper";
15
+
19
16
  const simulatorManager = new SimulatorManager();
20
17
 
21
18
  export type SliceSimulatorSliceZoneProps = {
@@ -23,35 +20,50 @@ export type SliceSimulatorSliceZoneProps = {
23
20
  };
24
21
 
25
22
  export type SliceSimulatorProps = {
26
- /**
27
- * React component to render simulated Slices.
28
- *
29
- * @example
30
- *
31
- * ```tsx
32
- * import { SliceSimulator } from "@slicemachine/adapter-next/simulator";
33
- * import { SliceZone } from "@prismicio/react";
34
- *
35
- * import { components } from "../slices";
36
- *
37
- * <SliceSimulator
38
- * sliceZone={({ slices }) => (
39
- * <SliceZone slices={slices} components={components} />
40
- * )}
41
- * />;
42
- * ```
43
- */
44
- sliceZone: (props: SliceSimulatorSliceZoneProps) => JSX.Element;
45
23
  className?: string;
46
- } & Omit<BaseSliceSimulatorProps, "state">;
24
+ } & Omit<BaseSliceSimulatorProps, "state"> &
25
+ (
26
+ | {
27
+ /**
28
+ * React component to render simulated Slices.
29
+ *
30
+ * @example
31
+ *
32
+ * ```tsx
33
+ * import { SliceSimulator } from "@slicemachine/adapter-next/simulator";
34
+ * import { SliceZone } from "@prismicio/react";
35
+ *
36
+ * import { components } from "../slices";
37
+ *
38
+ * <SliceSimulator
39
+ * sliceZone={({ slices }) => (
40
+ * <SliceZone slices={slices} components={components} />
41
+ * )}
42
+ * />;
43
+ * ```
44
+ */
45
+ sliceZone: (props: SliceSimulatorSliceZoneProps) => JSX.Element;
46
+ }
47
+ | {
48
+ children: React.ReactNode;
49
+ }
50
+ );
47
51
 
52
+ /**
53
+ * Simulate slices in isolation. The slice simulator enables live slice
54
+ * development in Slice Machine and live previews in the Page Builder.
55
+ */
48
56
  export const SliceSimulator = ({
49
- sliceZone: SliceZoneComp,
50
57
  background,
51
58
  zIndex,
52
59
  className,
60
+ ...restProps
53
61
  }: SliceSimulatorProps): JSX.Element => {
54
- const defaultProps = getDefaultProps();
62
+ if (!("sliceZone" in restProps)) {
63
+ throw new Error(
64
+ "A sliceZone prop must be provided when <SliceZone> is rendered in a Client Component. Add a sliceZone prop or convert your simulator to a Server Component with the getSlices helper.",
65
+ );
66
+ }
55
67
 
56
68
  const [slices, setSlices] = React.useState(() => getDefaultSlices());
57
69
  const [message, setMessage] = React.useState(() => getDefaultMessage());
@@ -81,40 +93,17 @@ export const SliceSimulator = ({
81
93
  };
82
94
  }, []);
83
95
 
96
+ const SliceZoneComp = restProps.sliceZone;
97
+
84
98
  return (
85
- <div
86
- className={[simulatorClass, className].filter(Boolean).join(" ")}
87
- style={{
88
- zIndex:
89
- typeof zIndex === "undefined"
90
- ? defaultProps.zIndex
91
- : zIndex ?? undefined,
92
- position: "fixed",
93
- top: 0,
94
- left: 0,
95
- width: "100%",
96
- height: "100vh",
97
- overflow: "auto",
98
- background:
99
- typeof background === "undefined"
100
- ? defaultProps.background
101
- : background ?? undefined,
102
- }}
99
+ <SliceSimulatorWrapper
100
+ message={message}
101
+ hasSlices={slices.length > 0}
102
+ background={background}
103
+ zIndex={zIndex}
104
+ className={className}
103
105
  >
104
- {message ? (
105
- <article dangerouslySetInnerHTML={{ __html: message }} />
106
- ) : slices.length ? (
107
- <div
108
- id="root"
109
- className={simulatorRootClass}
110
- onClickCapture={onClickHandler as unknown as React.MouseEventHandler}
111
- onSubmitCapture={
112
- disableEventHandler as unknown as React.FormEventHandler
113
- }
114
- >
115
- <SliceZoneComp slices={slices} />
116
- </div>
117
- ) : null}
118
- </div>
106
+ <SliceZoneComp slices={slices} />
107
+ </SliceSimulatorWrapper>
119
108
  );
120
109
  };
@@ -0,0 +1,68 @@
1
+ import { ReactNode } from "react";
2
+ import {
3
+ SliceSimulatorProps,
4
+ disableEventHandler,
5
+ getDefaultProps,
6
+ onClickHandler,
7
+ simulatorClass,
8
+ simulatorRootClass,
9
+ } from "@prismicio/simulator/kit";
10
+
11
+ type SliceSimulatorWrapperProps = {
12
+ children: ReactNode;
13
+ className?: string;
14
+ message?: string;
15
+ hasSlices: boolean;
16
+ } & Omit<SliceSimulatorProps, "state">;
17
+
18
+ /**
19
+ * A wrapper for the slice simulator that isolates the given children from the
20
+ * page's layout.
21
+ */
22
+ export const SliceSimulatorWrapper = ({
23
+ className,
24
+ children,
25
+ zIndex,
26
+ background,
27
+ message,
28
+ hasSlices,
29
+ }: SliceSimulatorWrapperProps): JSX.Element => {
30
+ const defaultProps = getDefaultProps();
31
+
32
+ return (
33
+ <div
34
+ className={[simulatorClass, className].filter(Boolean).join(" ")}
35
+ style={{
36
+ zIndex:
37
+ typeof zIndex === "undefined"
38
+ ? defaultProps.zIndex
39
+ : zIndex ?? undefined,
40
+ position: "fixed",
41
+ top: 0,
42
+ left: 0,
43
+ width: "100%",
44
+ height: "100vh",
45
+ overflow: "auto",
46
+ background:
47
+ typeof background === "undefined"
48
+ ? defaultProps.background
49
+ : background ?? undefined,
50
+ }}
51
+ >
52
+ {message ? (
53
+ <article dangerouslySetInnerHTML={{ __html: message }} />
54
+ ) : hasSlices ? (
55
+ <div
56
+ id="root"
57
+ className={simulatorRootClass}
58
+ onClickCapture={onClickHandler as unknown as React.MouseEventHandler}
59
+ onSubmitCapture={
60
+ disableEventHandler as unknown as React.FormEventHandler
61
+ }
62
+ >
63
+ {children}
64
+ </div>
65
+ ) : null}
66
+ </div>
67
+ );
68
+ };
@@ -0,0 +1,19 @@
1
+ import { StateEvents, StateEventType } from "@prismicio/simulator/kit";
2
+
3
+ /**
4
+ * Returns the simulator's slices from the page's `searchParams.state` value.
5
+ * The `state` value is set by `<SliceSimulator>`.
6
+ *
7
+ * **Note**: `getSlices` should only be used in the App Router with a Server
8
+ * Component.
9
+ */
10
+ export const getSlices = (
11
+ state: string | null | undefined,
12
+ ): StateEvents[StateEventType.Slices] => {
13
+ // Prevent tsserver + eslint warnings about unused vars.
14
+ state;
15
+
16
+ throw new Error(
17
+ "getSlices is designed only for Server Components. Convert your simulator page to a Server Component or remove the function from your Client Component.",
18
+ );
19
+ };
@@ -3,3 +3,6 @@ export type {
3
3
  SliceSimulatorProps,
4
4
  SliceSimulatorSliceZoneProps,
5
5
  } from "./SliceSimulator";
6
+
7
+ export { getSlices } from "./getSlices";
8
+ export type { SliceSimulatorParams } from "./types";
@@ -0,0 +1,113 @@
1
+ // This `<SliceSimulator>` is only accessible from Server Components.
2
+
3
+ "use client";
4
+
5
+ import * as React from "react";
6
+
7
+ import {
8
+ SliceSimulatorProps as BaseSliceSimulatorProps,
9
+ SimulatorManager,
10
+ StateEventType,
11
+ getDefaultMessage,
12
+ } from "@prismicio/simulator/kit";
13
+ import { compressToEncodedURIComponent } from "lz-string";
14
+
15
+ import { SliceSimulatorWrapper } from "../SliceSimulatorWrapper";
16
+ import { revalidatePath } from "./actions";
17
+ import { getSlices } from "./getSlices";
18
+
19
+ const STATE_PARAMS_KEY = "state";
20
+
21
+ const throttle =
22
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
23
+ <TArgs extends [...any]>(fn: (...args: TArgs) => unknown, wait: number) => {
24
+ let timeoutId: ReturnType<typeof setTimeout>;
25
+ let lastCallTime = 0;
26
+
27
+ return (...args: TArgs) => {
28
+ clearTimeout(timeoutId);
29
+
30
+ const now = Date.now();
31
+ const timeSinceLastCall = now - lastCallTime;
32
+ const delayForNextCall = wait - timeSinceLastCall;
33
+
34
+ if (delayForNextCall <= 0) {
35
+ lastCallTime = now;
36
+ fn(...args);
37
+ } else {
38
+ timeoutId = setTimeout(() => {
39
+ lastCallTime = Date.now();
40
+ fn(...args);
41
+ }, delayForNextCall);
42
+ }
43
+ };
44
+ };
45
+ const throttledRevalidatePath = throttle(revalidatePath, 300);
46
+
47
+ const simulatorManager = new SimulatorManager();
48
+
49
+ export type SliceSimulatorProps = Omit<BaseSliceSimulatorProps, "state"> & {
50
+ children: React.ReactNode;
51
+ className?: string;
52
+ };
53
+
54
+ export const SliceSimulator = ({
55
+ children,
56
+ background,
57
+ zIndex,
58
+ className,
59
+ }: SliceSimulatorProps): JSX.Element => {
60
+ const [message, setMessage] = React.useState(() => getDefaultMessage());
61
+
62
+ const state =
63
+ typeof window !== "undefined"
64
+ ? new URL(window.location.href).searchParams.get(STATE_PARAMS_KEY)
65
+ : undefined;
66
+ const hasSlices = getSlices(state).length > 0;
67
+
68
+ React.useEffect(() => {
69
+ simulatorManager.state.on(
70
+ StateEventType.Slices,
71
+ (newSlices) => {
72
+ const url = new URL(window.location.href);
73
+ url.searchParams.set(
74
+ STATE_PARAMS_KEY,
75
+ compressToEncodedURIComponent(JSON.stringify(newSlices)),
76
+ );
77
+ window.history.pushState(null, "", url);
78
+
79
+ // A 0 ms timeout is needed to prevent a bug
80
+ // where the path is revalidated before the URL
81
+ // is updated with the new state.
82
+ const path = window.location.pathname;
83
+ setTimeout(() => throttledRevalidatePath(path), 0);
84
+ },
85
+ "simulator-slices",
86
+ );
87
+ simulatorManager.state.on(
88
+ StateEventType.Message,
89
+ (newMessage) => setMessage(newMessage),
90
+ "simulator-message",
91
+ );
92
+
93
+ simulatorManager.init();
94
+
95
+ return () => {
96
+ simulatorManager.state.off(StateEventType.Slices, "simulator-slices");
97
+
98
+ simulatorManager.state.off(StateEventType.Message, "simulator-message");
99
+ };
100
+ }, []);
101
+
102
+ return (
103
+ <SliceSimulatorWrapper
104
+ message={message}
105
+ hasSlices={hasSlices}
106
+ background={background}
107
+ zIndex={zIndex}
108
+ className={className}
109
+ >
110
+ {children}
111
+ </SliceSimulatorWrapper>
112
+ );
113
+ };
@@ -0,0 +1,7 @@
1
+ "use server";
2
+
3
+ import { revalidatePath as baseRevalidatePath } from "next/cache";
4
+
5
+ export async function revalidatePath(path: string): Promise<void> {
6
+ baseRevalidatePath(path);
7
+ }
@@ -0,0 +1,16 @@
1
+ // This `getSlices` is only accessible from Server Components.
2
+
3
+ import {
4
+ getDefaultSlices,
5
+ StateEvents,
6
+ StateEventType,
7
+ } from "@prismicio/simulator/kit";
8
+ import { decompressFromEncodedURIComponent } from "lz-string";
9
+
10
+ export const getSlices = (
11
+ state: string | null | undefined,
12
+ ): StateEvents[StateEventType.Slices] => {
13
+ return state
14
+ ? JSON.parse(decompressFromEncodedURIComponent(state))
15
+ : getDefaultSlices();
16
+ };
@@ -0,0 +1,4 @@
1
+ // These exports are only accessible from Server Components.
2
+
3
+ export { SliceSimulator } from "./SliceSimulator";
4
+ export { getSlices } from "./getSlices";
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Parameters provided to the Slice Simulator page.
3
+ *
4
+ * **Note**: `SliceSimulatorParams` should only be used in the App Router with a
5
+ * Server Component.
6
+ */
7
+ export type SliceSimulatorParams = {
8
+ searchParams: {
9
+ state?: string;
10
+ };
11
+ };