@player-ui/storybook 0.0.1-next.10

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.
@@ -0,0 +1,61 @@
1
+ import { DecoratorFn } from '@storybook/react';
2
+ import React from 'react';
3
+ import { WebPlayerPlugin, Flow } from '@player-ui/react';
4
+
5
+ /** register all the storybook addons */
6
+ declare function register(): void;
7
+
8
+ /**
9
+ * A story decorator for rendering player content
10
+ */
11
+ declare const PlayerDecorator: DecoratorFn;
12
+
13
+ interface AppetizeVersions {
14
+ /** The iOS version to load */
15
+ ios: string;
16
+ /** The Android version to load */
17
+ android: string;
18
+ }
19
+
20
+ declare type AsyncImportFactory<T> = () => Promise<{
21
+ /** default export of the module */
22
+ default: T;
23
+ }>;
24
+ declare type RenderTarget = {
25
+ /** platform to render on */
26
+ platform: 'ios' | 'android' | 'web';
27
+ /** the token to use if applicable */
28
+ token?: string;
29
+ /** A different base URL for appetize if necessary */
30
+ baseUrl?: string;
31
+ /** The OS Versions to use for appetize */
32
+ appetizeVersions?: AppetizeVersions;
33
+ };
34
+
35
+ declare const WebPlayerPluginContext: React.Context<{
36
+ /** Plugins to use for the player */
37
+ plugins?: WebPlayerPlugin[] | undefined;
38
+ }>;
39
+ declare const PlayerRenderContext: React.Context<RenderTarget>;
40
+ declare const StorybookControlsContext: React.Context<{
41
+ /** any storybook controls to include */
42
+ controls?: Flow['data'];
43
+ }>;
44
+ declare type Mock = Record<string, unknown>;
45
+ declare type MockFactory = () => Mock;
46
+ declare type MockFactoryOrPromise = MockFactory | Mock;
47
+ interface PlayerStoryProps {
48
+ /** The mock to load */
49
+ flow: AsyncImportFactory<MockFactoryOrPromise> | MockFactoryOrPromise;
50
+ /** Custom data to use in the flow */
51
+ data?: Flow['data'];
52
+ /** props from storybook controls */
53
+ storybookControls?: Flow['data'];
54
+ }
55
+ /**
56
+ * Takes an initial flow and renders it as a story in storybook.
57
+ * This handles all of the wiring of the mock into the flow editor, events, etc
58
+ */
59
+ declare const PlayerStory: (props: PlayerStoryProps) => JSX.Element;
60
+
61
+ export { PlayerDecorator, PlayerRenderContext, PlayerStory, PlayerStoryProps, StorybookControlsContext, WebPlayerPluginContext, register };
@@ -0,0 +1,643 @@
1
+ import React from 'react';
2
+ import addons, { types } from '@storybook/addons';
3
+ import { useDarkMode } from 'storybook-dark-mode';
4
+ import { dequal } from 'dequal';
5
+ import Editor from '@monaco-editor/react';
6
+ import { v4 } from 'uuid';
7
+ import { Table, Head, Row, HeadCell, Body, Cell } from '@devtools-ds/table';
8
+ import makeClass from 'clsx';
9
+ import { Separator, IconButton, Icons, WithTooltip, TooltipLinkList } from '@storybook/components';
10
+ import { useParameter } from '@storybook/api';
11
+ import { STORY_CHANGED } from '@storybook/core-events';
12
+ import { WebPlayer } from '@player-ui/react';
13
+ import { VStack, Heading, Code, Text, Button, ChakraProvider, Spinner } from '@chakra-ui/react';
14
+ import { makeFlow } from '@player-ui/make-flow';
15
+ import { DocsContext } from '@storybook/addon-docs';
16
+ import { compressToEncodedURIComponent } from 'lz-string';
17
+ import { MetricsPlugin } from '@player-ui/metrics-plugin-react';
18
+
19
+ const ADDON_ID = "web-player/addon";
20
+ const FLOW_PANEL_ID = `${ADDON_ID}/flow-editor-panel`;
21
+ const EVENT_PANEL_ID = `${ADDON_ID}/events-panel`;
22
+ const FLOW_REFRESH_TOOL_ID = `${ADDON_ID}/flow-refresh-tool`;
23
+ const RENDER_SELECT_TOOL_ID = `${ADDON_ID}/render-select-tool`;
24
+
25
+ var __defProp$2 = Object.defineProperty;
26
+ var __getOwnPropSymbols$2 = Object.getOwnPropertySymbols;
27
+ var __hasOwnProp$2 = Object.prototype.hasOwnProperty;
28
+ var __propIsEnum$2 = Object.prototype.propertyIsEnumerable;
29
+ var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
30
+ var __spreadValues$2 = (a, b) => {
31
+ for (var prop in b || (b = {}))
32
+ if (__hasOwnProp$2.call(b, prop))
33
+ __defNormalProp$2(a, prop, b[prop]);
34
+ if (__getOwnPropSymbols$2)
35
+ for (var prop of __getOwnPropSymbols$2(b)) {
36
+ if (__propIsEnum$2.call(b, prop))
37
+ __defNormalProp$2(a, prop, b[prop]);
38
+ }
39
+ return a;
40
+ };
41
+ function createEvent(base) {
42
+ return __spreadValues$2({
43
+ id: v4(),
44
+ time: Date.now()
45
+ }, base);
46
+ }
47
+
48
+ function subscribe(chan, eventName, callback) {
49
+ const handler = (payload) => {
50
+ callback(JSON.parse(payload));
51
+ };
52
+ chan.addListener(eventName, handler);
53
+ return () => {
54
+ chan.removeListener(eventName, handler);
55
+ };
56
+ }
57
+ function publish(chan, event) {
58
+ chan.emit(event.type, JSON.stringify(event));
59
+ }
60
+ function useStateActions(chan) {
61
+ return React.useMemo(() => ({
62
+ addEvents: (events) => {
63
+ publish(chan, {
64
+ type: "@@player/event/add",
65
+ events
66
+ });
67
+ },
68
+ setMetrics: (metrics) => {
69
+ publish(chan, {
70
+ type: "@@player/metrics/set",
71
+ metrics
72
+ });
73
+ },
74
+ clearEvents: () => {
75
+ publish(chan, {
76
+ type: "@@player/event/clear"
77
+ });
78
+ },
79
+ setFlow: (flow) => {
80
+ publish(chan, {
81
+ type: "@@player/flow/set",
82
+ flow
83
+ });
84
+ },
85
+ resetFlow: () => {
86
+ publish(chan, { type: "@@player/flow/reset" });
87
+ },
88
+ setPlatform: (platform) => {
89
+ publish(chan, { type: "@@player/platform/set", platform });
90
+ }
91
+ }), [chan]);
92
+ }
93
+ function useEventState(chan) {
94
+ const [events, setEvents] = React.useState([]);
95
+ React.useEffect(() => {
96
+ const unsubAdd = subscribe(chan, "@@player/event/add", (evt) => setEvents((old) => [...old, ...evt.events]));
97
+ const unsubClear = subscribe(chan, "@@player/event/clear", () => {
98
+ setEvents([]);
99
+ });
100
+ return () => {
101
+ unsubAdd();
102
+ unsubClear();
103
+ };
104
+ }, [chan]);
105
+ return events;
106
+ }
107
+ function useFlowState(chan) {
108
+ const [flow, setFlow] = React.useState();
109
+ React.useEffect(() => {
110
+ return subscribe(chan, "@@player/flow/set", (evt) => {
111
+ setFlow(evt.flow);
112
+ });
113
+ }, [chan]);
114
+ return flow;
115
+ }
116
+
117
+ const EditorPanel = (props) => {
118
+ const { active } = props;
119
+ const darkMode = useDarkMode();
120
+ const flow = useFlowState(props.api.getChannel());
121
+ const actions = useStateActions(props.api.getChannel());
122
+ const [editorValue, setEditorValue] = React.useState(flow ? JSON.stringify(flow, null, 2) : "{}");
123
+ const updateTimerRef = React.useRef(void 0);
124
+ function clearPending() {
125
+ if (updateTimerRef.current) {
126
+ clearTimeout(updateTimerRef.current);
127
+ updateTimerRef.current = void 0;
128
+ }
129
+ }
130
+ React.useEffect(() => {
131
+ if (!active) {
132
+ return;
133
+ }
134
+ try {
135
+ if (editorValue) {
136
+ const parsed = JSON.parse(editorValue);
137
+ if (dequal(flow, parsed)) {
138
+ return;
139
+ }
140
+ }
141
+ } catch (e) {
142
+ }
143
+ setEditorValue(JSON.stringify(flow, null, 2));
144
+ }, [flow, active]);
145
+ if (!active) {
146
+ return null;
147
+ }
148
+ const onChange = (val) => {
149
+ clearPending();
150
+ setEditorValue(val != null ? val : "");
151
+ try {
152
+ if (val) {
153
+ const parsed = JSON.parse(val);
154
+ if (!dequal(parsed, flow)) {
155
+ updateTimerRef.current = setTimeout(() => {
156
+ if (active) {
157
+ actions.setFlow(parsed);
158
+ }
159
+ }, 1e3);
160
+ }
161
+ }
162
+ } catch (e) {
163
+ }
164
+ };
165
+ return /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(Editor, {
166
+ theme: darkMode ? "vs-dark" : "light",
167
+ value: editorValue,
168
+ language: "json",
169
+ onChange
170
+ }));
171
+ };
172
+
173
+ var e=[],t=[];function n(n,r){if(n&&"undefined"!=typeof document){var a,s=!0===r.prepend?"prepend":"append",d=!0===r.singleTag,i="string"==typeof r.container?document.querySelector(r.container):document.getElementsByTagName("head")[0];if(d){var u=e.indexOf(i);-1===u&&(u=e.push(i)-1,t[u]={}),a=t[u]&&t[u][s]?t[u][s]:t[u][s]=c();}else a=c();65279===n.charCodeAt(0)&&(n=n.substring(1)),a.styleSheet?a.styleSheet.cssText+=n:a.appendChild(document.createTextNode(n));}function c(){var e=document.createElement("style");if(e.setAttribute("type","text/css"),r.attributes)for(var t=Object.keys(r.attributes),n=0;n<t.length;n++)e.setAttribute(t[n],r.attributes[t[n]]);var a="prepend"===s?"afterbegin":"beforeend";return i.insertAdjacentElement(a,e),e}}
174
+
175
+ var css = "\n.events_header__1bcc1085 td {\n width: 200px;\n}\n\n.events_body__1bcc1085 td {\n}\n";
176
+ var modules_2563542d = {"header":"events_header__1bcc1085","body":"events_body__1bcc1085"};
177
+ n(css,{});
178
+
179
+ var __defProp$1 = Object.defineProperty;
180
+ var __getOwnPropSymbols$1 = Object.getOwnPropertySymbols;
181
+ var __hasOwnProp$1 = Object.prototype.hasOwnProperty;
182
+ var __propIsEnum$1 = Object.prototype.propertyIsEnumerable;
183
+ var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
184
+ var __spreadValues$1 = (a, b) => {
185
+ for (var prop in b || (b = {}))
186
+ if (__hasOwnProp$1.call(b, prop))
187
+ __defNormalProp$1(a, prop, b[prop]);
188
+ if (__getOwnPropSymbols$1)
189
+ for (var prop of __getOwnPropSymbols$1(b)) {
190
+ if (__propIsEnum$1.call(b, prop))
191
+ __defNormalProp$1(a, prop, b[prop]);
192
+ }
193
+ return a;
194
+ };
195
+ const ExtraCells = (event) => {
196
+ var _a, _b;
197
+ if (event.type === "log") {
198
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("td", null, event.severity), /* @__PURE__ */ React.createElement("td", null, event.message.map((a) => JSON.stringify(a)).join(" ")));
199
+ }
200
+ if (event.type === "dataChange") {
201
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("td", null, event.binding), /* @__PURE__ */ React.createElement("td", null, `${JSON.stringify(event.from)} \u279C ${JSON.stringify(event.to)}`));
202
+ }
203
+ if (event.type === "stateChange") {
204
+ let name = event.state;
205
+ if (event.state === "completed") {
206
+ name = `${name} (${event.error ? "error" : "success"})`;
207
+ }
208
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("td", null, name), /* @__PURE__ */ React.createElement("td", null, (_b = (_a = event.outcome) != null ? _a : event.error) != null ? _b : ""));
209
+ }
210
+ if (event.type === "metric") {
211
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("td", null, event.metricType), /* @__PURE__ */ React.createElement("td", null, event.message));
212
+ }
213
+ return null;
214
+ };
215
+ const EventsPanel = (props) => {
216
+ const events = useEventState(props.api.getChannel());
217
+ const darkMode = useDarkMode();
218
+ if (!props.active) {
219
+ return null;
220
+ }
221
+ return /* @__PURE__ */ React.createElement("div", {
222
+ className: makeClass(modules_2563542d.wrapper, {
223
+ [modules_2563542d.dark]: darkMode
224
+ })
225
+ }, /* @__PURE__ */ React.createElement(Table, {
226
+ colorScheme: darkMode ? "dark" : "light"
227
+ }, /* @__PURE__ */ React.createElement(Head, {
228
+ className: modules_2563542d.header
229
+ }, /* @__PURE__ */ React.createElement(Row, null, /* @__PURE__ */ React.createElement(HeadCell, null, "Time"), /* @__PURE__ */ React.createElement(HeadCell, null, "Type"), /* @__PURE__ */ React.createElement(HeadCell, null), /* @__PURE__ */ React.createElement(HeadCell, null))), /* @__PURE__ */ React.createElement(Body, {
230
+ className: modules_2563542d.body
231
+ }, events.map((evt) => /* @__PURE__ */ React.createElement(Row, {
232
+ key: evt.id
233
+ }, /* @__PURE__ */ React.createElement(Cell, null, evt.time), /* @__PURE__ */ React.createElement(Cell, null, evt.type), /* @__PURE__ */ React.createElement(ExtraCells, __spreadValues$1({}, evt)))))));
234
+ };
235
+
236
+ const FlowRefresh = ({ api }) => {
237
+ const actions = useStateActions(api.getChannel());
238
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Separator, null), /* @__PURE__ */ React.createElement(IconButton, {
239
+ title: "Reset the current flow",
240
+ onClick: () => {
241
+ actions.resetFlow();
242
+ }
243
+ }, /* @__PURE__ */ React.createElement(Icons, {
244
+ icon: "sync"
245
+ })));
246
+ };
247
+
248
+ const RenderSelection = ({ api }) => {
249
+ const params = useParameter("appetizeTokens", {});
250
+ const actions = useStateActions(api.getChannel());
251
+ const [selectedPlatform, setPlatform] = React.useState("web");
252
+ React.useEffect(() => {
253
+ const listener = () => {
254
+ setPlatform("web");
255
+ };
256
+ api.getChannel().addListener(STORY_CHANGED, listener);
257
+ return () => {
258
+ api.getChannel().removeListener(STORY_CHANGED, listener);
259
+ };
260
+ }, [api]);
261
+ const mobilePlatforms = Object.keys(params);
262
+ if (mobilePlatforms.length === 0) {
263
+ return null;
264
+ }
265
+ return /* @__PURE__ */ React.createElement(WithTooltip, {
266
+ closeOnClick: true,
267
+ placement: "top",
268
+ trigger: "click",
269
+ tooltip: ({ onHide }) => /* @__PURE__ */ React.createElement(TooltipLinkList, {
270
+ links: ["web", ...mobilePlatforms].map((platform) => ({
271
+ id: platform,
272
+ title: platform,
273
+ onClick: () => {
274
+ setPlatform(platform);
275
+ actions.setPlatform(platform);
276
+ onHide();
277
+ },
278
+ value: platform,
279
+ active: platform === selectedPlatform
280
+ }))
281
+ })
282
+ }, /* @__PURE__ */ React.createElement(IconButton, {
283
+ title: "Change the render target"
284
+ }, /* @__PURE__ */ React.createElement(Icons, {
285
+ icon: selectedPlatform === "web" ? "browser" : "mobile"
286
+ })));
287
+ };
288
+
289
+ function register() {
290
+ addons.register(ADDON_ID, (api) => {
291
+ addons.addPanel(EVENT_PANEL_ID, {
292
+ title: "Events",
293
+ render: ({ active, key }) => /* @__PURE__ */ React.createElement(EventsPanel, {
294
+ key,
295
+ api,
296
+ active: Boolean(active)
297
+ })
298
+ });
299
+ addons.addPanel(FLOW_PANEL_ID, {
300
+ title: "Flow",
301
+ render: ({ active, key }) => /* @__PURE__ */ React.createElement(EditorPanel, {
302
+ key,
303
+ api,
304
+ active: Boolean(active)
305
+ })
306
+ });
307
+ addons.add(FLOW_REFRESH_TOOL_ID, {
308
+ title: "Refresh Flow",
309
+ type: types.TOOL,
310
+ render: () => /* @__PURE__ */ React.createElement(FlowRefresh, {
311
+ api
312
+ })
313
+ });
314
+ addons.add(RENDER_SELECT_TOOL_ID, {
315
+ title: "Render Selection",
316
+ type: types.TOOL,
317
+ render: () => /* @__PURE__ */ React.createElement(RenderSelection, {
318
+ api
319
+ })
320
+ });
321
+ });
322
+ }
323
+
324
+ function useEditorFlow(initialFlow) {
325
+ const stateActions = useStateActions(addons.getChannel());
326
+ const flow = useFlowState(addons.getChannel());
327
+ const docsContext = React.useContext(DocsContext);
328
+ React.useEffect(() => {
329
+ stateActions.setFlow(initialFlow);
330
+ }, [initialFlow]);
331
+ React.useEffect(() => {
332
+ if (!flow) {
333
+ stateActions.setFlow(initialFlow);
334
+ }
335
+ }, [flow]);
336
+ if (docsContext.id) {
337
+ return initialFlow;
338
+ }
339
+ return flow != null ? flow : initialFlow;
340
+ }
341
+
342
+ const DEVICE_HEIGHT = {
343
+ iphonexs: 845
344
+ };
345
+ const toAppetizeUrl = (baseUrl, key, params) => `https://${baseUrl}/embed/${key}?${Object.entries(params).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&")}`;
346
+ const Appetize = (props) => {
347
+ const device = "iphonexs";
348
+ const height = DEVICE_HEIGHT[device];
349
+ const defaultVersions = {
350
+ ios: "13.7",
351
+ android: "8.1"
352
+ };
353
+ const {
354
+ baseUrl = "appetize.io",
355
+ token,
356
+ flow,
357
+ osVersions = defaultVersions,
358
+ platform
359
+ } = props;
360
+ const osVersion = osVersions[platform];
361
+ return /* @__PURE__ */ React.createElement("iframe", {
362
+ title: "native app",
363
+ style: { height: `${height}px`, border: "none", width: "100%" },
364
+ src: toAppetizeUrl(baseUrl, token, {
365
+ autoplay: true,
366
+ device: "iphonexs",
367
+ deviceColor: "black",
368
+ scale: 100,
369
+ osVersion,
370
+ params: encodeURIComponent(JSON.stringify({
371
+ json: compressToEncodedURIComponent(JSON.stringify(flow))
372
+ }))
373
+ })
374
+ });
375
+ };
376
+
377
+ class StorybookPlayerPlugin {
378
+ constructor(actions) {
379
+ this.name = "Storybook";
380
+ this.actions = actions;
381
+ this.metricsPlugin = new MetricsPlugin({
382
+ onUpdate: (metrics) => {
383
+ actions.setMetrics(metrics);
384
+ },
385
+ onRenderEnd: (timing) => {
386
+ this.onMetricChange(timing, "render");
387
+ },
388
+ onUpdateEnd: (timing) => {
389
+ this.onMetricChange(timing, "update");
390
+ }
391
+ });
392
+ }
393
+ apply(player) {
394
+ player.registerPlugin(this.metricsPlugin);
395
+ }
396
+ applyWeb(wp) {
397
+ wp.registerPlugin(this.metricsPlugin);
398
+ wp.player.hooks.dataController.tap(this.name, (dc) => {
399
+ this.actions.clearEvents();
400
+ dc.hooks.onUpdate.tap(this.name, (dataUpdates) => {
401
+ const events = dataUpdates.map((dataUpdate) => createEvent({
402
+ type: "dataChange",
403
+ binding: dataUpdate.binding.asString(),
404
+ from: dataUpdate.oldValue,
405
+ to: dataUpdate.newValue
406
+ }));
407
+ this.actions.addEvents(events);
408
+ });
409
+ });
410
+ wp.player.logger.hooks.log.tap(this.name, (severity, data) => {
411
+ this.actions.addEvents([
412
+ createEvent({
413
+ type: "log",
414
+ message: data,
415
+ severity
416
+ })
417
+ ]);
418
+ });
419
+ wp.player.hooks.state.tap(this.name, (newState) => {
420
+ if ("error" in newState) {
421
+ this.actions.addEvents([
422
+ createEvent({
423
+ type: "stateChange",
424
+ state: newState.status,
425
+ error: newState.error.message
426
+ })
427
+ ]);
428
+ } else if (newState.status === "completed") {
429
+ this.actions.addEvents([
430
+ createEvent({
431
+ type: "stateChange",
432
+ state: newState.status,
433
+ outcome: newState.endState.outcome
434
+ })
435
+ ]);
436
+ } else {
437
+ this.actions.addEvents([
438
+ createEvent({
439
+ type: "stateChange",
440
+ state: newState.status
441
+ })
442
+ ]);
443
+ }
444
+ });
445
+ }
446
+ onMetricChange(timing, metricType) {
447
+ if (!timing.completed) {
448
+ return;
449
+ }
450
+ this.actions.addEvents([
451
+ createEvent({
452
+ type: "metric",
453
+ metricType,
454
+ message: `Duration: ${timing.duration.toFixed(0)} ms`
455
+ })
456
+ ]);
457
+ }
458
+ }
459
+
460
+ const PlayerFlowSummary = (props) => {
461
+ var _a;
462
+ return /* @__PURE__ */ React.createElement(VStack, {
463
+ gap: "10"
464
+ }, /* @__PURE__ */ React.createElement(Heading, null, "Flow Completed ", props.error ? "with Error" : ""), props.outcome && /* @__PURE__ */ React.createElement(Code, null, "Outcome: ", /* @__PURE__ */ React.createElement(Text, {
465
+ as: "strong"
466
+ }, props.outcome)), props.error && /* @__PURE__ */ React.createElement(Code, {
467
+ colorScheme: "red"
468
+ }, /* @__PURE__ */ React.createElement("pre", null, (_a = props.error) == null ? void 0 : _a.message)), /* @__PURE__ */ React.createElement(Button, {
469
+ variant: "solid",
470
+ onClick: props.reset
471
+ }, "Reset"));
472
+ };
473
+
474
+ var __defProp = Object.defineProperty;
475
+ var __defProps = Object.defineProperties;
476
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
477
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
478
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
479
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
480
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
481
+ var __spreadValues = (a, b) => {
482
+ for (var prop in b || (b = {}))
483
+ if (__hasOwnProp.call(b, prop))
484
+ __defNormalProp(a, prop, b[prop]);
485
+ if (__getOwnPropSymbols)
486
+ for (var prop of __getOwnPropSymbols(b)) {
487
+ if (__propIsEnum.call(b, prop))
488
+ __defNormalProp(a, prop, b[prop]);
489
+ }
490
+ return a;
491
+ };
492
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
493
+ var __objRest = (source, exclude) => {
494
+ var target = {};
495
+ for (var prop in source)
496
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
497
+ target[prop] = source[prop];
498
+ if (source != null && __getOwnPropSymbols)
499
+ for (var prop of __getOwnPropSymbols(source)) {
500
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
501
+ target[prop] = source[prop];
502
+ }
503
+ return target;
504
+ };
505
+ var __async = (__this, __arguments, generator) => {
506
+ return new Promise((resolve, reject) => {
507
+ var fulfilled = (value) => {
508
+ try {
509
+ step(generator.next(value));
510
+ } catch (e) {
511
+ reject(e);
512
+ }
513
+ };
514
+ var rejected = (value) => {
515
+ try {
516
+ step(generator.throw(value));
517
+ } catch (e) {
518
+ reject(e);
519
+ }
520
+ };
521
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
522
+ step((generator = generator.apply(__this, __arguments)).next());
523
+ });
524
+ };
525
+ const WebPlayerPluginContext = React.createContext({ plugins: [] });
526
+ const PlayerRenderContext = React.createContext({
527
+ platform: "web"
528
+ });
529
+ const StorybookControlsContext = React.createContext({
530
+ controls: {}
531
+ });
532
+ const LocalPlayerStory = (props) => {
533
+ var _a, _b;
534
+ let flow = useEditorFlow(props.flow);
535
+ const renderContext = React.useContext(PlayerRenderContext);
536
+ const pluginContext = React.useContext(WebPlayerPluginContext);
537
+ const controlsContext = React.useContext(StorybookControlsContext);
538
+ const stateActions = useStateActions(addons.getChannel());
539
+ const plugins = (_b = (_a = props.webPlugins) != null ? _a : pluginContext == null ? void 0 : pluginContext.plugins) != null ? _b : [];
540
+ const [playerState, setPlayerState] = React.useState("not-started");
541
+ const wp = React.useMemo(() => {
542
+ return new WebPlayer({
543
+ plugins: [new StorybookPlayerPlugin(stateActions), ...plugins]
544
+ });
545
+ }, [plugins]);
546
+ const startFlow = () => {
547
+ setPlayerState("in-progress");
548
+ wp.start(flow).then(() => {
549
+ setPlayerState("completed");
550
+ }).catch((e) => {
551
+ console.error("Error starting flow", e);
552
+ setPlayerState("error");
553
+ });
554
+ };
555
+ React.useEffect(() => {
556
+ startFlow();
557
+ }, [wp, flow]);
558
+ React.useEffect(() => {
559
+ var _a2;
560
+ if (controlsContext) {
561
+ flow = __spreadProps(__spreadValues({}, flow), {
562
+ data: __spreadValues(__spreadValues({}, (_a2 = flow.data) != null ? _a2 : {}), controlsContext)
563
+ });
564
+ stateActions.setFlow(flow);
565
+ }
566
+ }, [controlsContext]);
567
+ React.useEffect(() => {
568
+ return subscribe(addons.getChannel(), "@@player/flow/reset", () => {
569
+ startFlow();
570
+ });
571
+ }, [wp, flow]);
572
+ if (renderContext.platform !== "web" && renderContext.token) {
573
+ return /* @__PURE__ */ React.createElement(Appetize, {
574
+ flow,
575
+ platform: renderContext.platform,
576
+ token: renderContext.token,
577
+ baseUrl: renderContext.baseUrl,
578
+ osVersions: renderContext.appetizeVersions
579
+ });
580
+ }
581
+ const currentState = wp.player.getState();
582
+ if (playerState === "completed" && currentState.status === "completed") {
583
+ return /* @__PURE__ */ React.createElement(PlayerFlowSummary, {
584
+ reset: startFlow,
585
+ outcome: currentState.endState.outcome
586
+ });
587
+ }
588
+ if (playerState === "error" && currentState.status === "error") {
589
+ return /* @__PURE__ */ React.createElement(PlayerFlowSummary, {
590
+ reset: startFlow,
591
+ error: currentState.error
592
+ });
593
+ }
594
+ return /* @__PURE__ */ React.createElement(wp.Component, null);
595
+ };
596
+ function wrapInLazy(Component, flowFactory, other) {
597
+ const asPlayer = () => __async(this, null, function* () {
598
+ const mock = typeof flowFactory === "function" ? yield flowFactory() : flowFactory;
599
+ const Comp = () => {
600
+ const flow = __spreadValues(__spreadValues({}, makeFlow("default" in mock ? mock.default : mock)), other != null ? other : {});
601
+ return /* @__PURE__ */ React.createElement(Component, {
602
+ flow
603
+ });
604
+ };
605
+ return {
606
+ default: Comp
607
+ };
608
+ });
609
+ return React.lazy(asPlayer);
610
+ }
611
+ const PlayerStory = (props) => {
612
+ const _a = props, { flow, storybookControls } = _a, other = __objRest(_a, ["flow", "storybookControls"]);
613
+ const MockComp = React.useMemo(() => wrapInLazy(LocalPlayerStory, flow, other), []);
614
+ return /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(ChakraProvider, null, /* @__PURE__ */ React.createElement(React.Suspense, {
615
+ fallback: /* @__PURE__ */ React.createElement(Spinner, null)
616
+ }, /* @__PURE__ */ React.createElement(StorybookControlsContext.Provider, {
617
+ value: __spreadValues({}, storybookControls)
618
+ }, /* @__PURE__ */ React.createElement(MockComp, null)))));
619
+ };
620
+
621
+ const PlayerDecorator = (story, ctx) => {
622
+ var _a;
623
+ const playerParams = ctx.parameters;
624
+ const [selectedPlatform, setPlatform] = React.useState("web");
625
+ React.useEffect(() => {
626
+ return subscribe(addons.getChannel(), "@@player/platform/set", (evt) => {
627
+ setPlatform(evt.platform);
628
+ });
629
+ }, []);
630
+ return /* @__PURE__ */ React.createElement(PlayerRenderContext.Provider, {
631
+ value: {
632
+ platform: selectedPlatform,
633
+ token: selectedPlatform === "web" ? void 0 : (_a = playerParams == null ? void 0 : playerParams.appetizeTokens) == null ? void 0 : _a[selectedPlatform],
634
+ baseUrl: playerParams.appetizeBaseUrl,
635
+ appetizeVersions: playerParams.appetizeVersions
636
+ }
637
+ }, /* @__PURE__ */ React.createElement(WebPlayerPluginContext.Provider, {
638
+ value: { plugins: playerParams.webplayerPlugins }
639
+ }, story()));
640
+ };
641
+
642
+ export { PlayerDecorator, PlayerRenderContext, PlayerStory, StorybookControlsContext, WebPlayerPluginContext, register };
643
+ //# sourceMappingURL=index.esm.js.map