@salesforce/storefront-next-runtime 0.1.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 (49) hide show
  1. package/LICENSE.txt +181 -0
  2. package/README.md +158 -0
  3. package/dist/ComponentContext.js +14 -0
  4. package/dist/ComponentContext.js.map +1 -0
  5. package/dist/DesignContext.js +769 -0
  6. package/dist/DesignContext.js.map +1 -0
  7. package/dist/DesignContext2.js +6 -0
  8. package/dist/PageDesignerProvider.js +53 -0
  9. package/dist/PageDesignerProvider.js.map +1 -0
  10. package/dist/PageRegistration.js +29 -0
  11. package/dist/PageRegistration.js.map +1 -0
  12. package/dist/PreviewContext.js +18 -0
  13. package/dist/PreviewContext.js.map +1 -0
  14. package/dist/design-messaging.d.ts +3 -0
  15. package/dist/design-messaging.js +3 -0
  16. package/dist/design-mode.d.ts +40 -0
  17. package/dist/design-mode.d.ts.map +1 -0
  18. package/dist/design-mode.js +3 -0
  19. package/dist/design-react-core.d.ts +69 -0
  20. package/dist/design-react-core.d.ts.map +1 -0
  21. package/dist/design-react-core.js +23 -0
  22. package/dist/design-react-core.js.map +1 -0
  23. package/dist/design-react.d.ts +130 -0
  24. package/dist/design-react.d.ts.map +1 -0
  25. package/dist/design-react.js +488 -0
  26. package/dist/design-react.js.map +1 -0
  27. package/dist/design-styles.css +232 -0
  28. package/dist/design.d.ts +2 -0
  29. package/dist/design.js +219 -0
  30. package/dist/design.js.map +1 -0
  31. package/dist/events.d.ts +208 -0
  32. package/dist/events.d.ts.map +1 -0
  33. package/dist/events.js +104 -0
  34. package/dist/events.js.map +1 -0
  35. package/dist/index.d.ts +215 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index2.d.ts +1171 -0
  38. package/dist/index2.d.ts.map +1 -0
  39. package/dist/messaging-api.js +340 -0
  40. package/dist/messaging-api.js.map +1 -0
  41. package/dist/modeDetection.js +50 -0
  42. package/dist/modeDetection.js.map +1 -0
  43. package/dist/scapi.d.ts +29278 -0
  44. package/dist/scapi.d.ts.map +1 -0
  45. package/dist/scapi.js +2 -0
  46. package/dist/scapi.js.map +1 -0
  47. package/dist/types.d.ts +13293 -0
  48. package/dist/types.d.ts.map +1 -0
  49. package/package.json +108 -0
@@ -0,0 +1,769 @@
1
+ import { n as createClientApi } from "./messaging-api.js";
2
+ import { n as usePageDesignerMode } from "./PageDesignerProvider.js";
3
+ import React, { useCallback, useEffect, useRef, useState } from "react";
4
+ import { Fragment, jsx } from "react/jsx-runtime";
5
+
6
+ //#region src/design/react/hooks/useInteraction.ts
7
+ /**
8
+ * Base hook that provides common interaction patterns for design-time functionality.
9
+ * Reduces boilerplate by handling state management, event listeners, and cleanup.
10
+ *
11
+ * @param config - Configuration object defining the interaction behavior
12
+ * @returns Object containing state and action methods
13
+ */
14
+ function useInteraction(config) {
15
+ const [state, setState] = useState(config.initialState);
16
+ const { isDesignMode, clientApi } = useDesignContext();
17
+ useEffect(() => {
18
+ if (!isDesignMode || !clientApi) return () => {};
19
+ const unsubscribeFunctions = Object.entries(config.eventHandlers ?? {}).map(([eventName, entry]) => clientApi.on(eventName, (event) => entry.handler(event, setState)));
20
+ return () => {
21
+ unsubscribeFunctions.forEach((unsubscribe) => unsubscribe());
22
+ };
23
+ }, [
24
+ isDesignMode,
25
+ clientApi,
26
+ config.eventHandlers
27
+ ]);
28
+ return {
29
+ state,
30
+ ...config.actions?.(state, setState, clientApi ?? null) ?? {}
31
+ };
32
+ }
33
+
34
+ //#endregion
35
+ //#region src/design/react/hooks/useSelectInteraction.ts
36
+ /**
37
+ * Custom hook that manages component selection state and handles
38
+ * client-host communication for selection events.
39
+ *
40
+ * @param isDesignMode - Whether design mode is active
41
+ * @param clientApi - Client API for host communication
42
+ * @returns Selection state and interaction methods
43
+ */
44
+ function useSelectInteraction() {
45
+ const { state: selectedComponentId, setSelectedComponent } = useInteraction({
46
+ initialState: "",
47
+ eventHandlers: {
48
+ ComponentSelected: { handler: (event, setState) => {
49
+ setState(event.componentId);
50
+ } },
51
+ ComponentDeselected: { handler: (_, setState) => {
52
+ setState("");
53
+ } }
54
+ },
55
+ actions: (_state, setState, clientApi) => ({ setSelectedComponent: (componentId) => {
56
+ setState(componentId);
57
+ clientApi?.selectComponent({ componentId });
58
+ } })
59
+ });
60
+ return {
61
+ selectedComponentId,
62
+ setSelectedComponent
63
+ };
64
+ }
65
+
66
+ //#endregion
67
+ //#region src/design/react/hooks/useHoverInteraction.ts
68
+ /**
69
+ * Custom hook that manages component hover state and handles
70
+ * client-host communication for hover events.
71
+ *
72
+ * @returns Hover state and interaction methods
73
+ */
74
+ function useHoverInteraction() {
75
+ const { state: hoveredComponentId, setHoveredComponent } = useInteraction({
76
+ initialState: null,
77
+ eventHandlers: {
78
+ ComponentHoveredIn: { handler: (event, setState) => setState(event.componentId) },
79
+ ComponentHoveredOut: { handler: (_, setState) => setState(null) }
80
+ },
81
+ actions: (state, setState, clientApi) => ({ setHoveredComponent: (componentId) => {
82
+ if (state && componentId !== state) clientApi?.hoverOutOfComponent({ componentId: state });
83
+ if (componentId && componentId !== state) clientApi?.hoverInToComponent({ componentId });
84
+ setState(componentId);
85
+ } })
86
+ });
87
+ return {
88
+ hoveredComponentId,
89
+ setHoveredComponent
90
+ };
91
+ }
92
+
93
+ //#endregion
94
+ //#region src/design/react/hooks/useDeleteInteraction.ts
95
+ function useDeleteInteraction({ selectedComponentId, setSelectedComponent }) {
96
+ const { deleteComponent } = useInteraction({
97
+ initialState: null,
98
+ eventHandlers: {},
99
+ actions: (_state, _setState, clientApi) => ({ deleteComponent: (event) => {
100
+ clientApi?.deleteComponent(event);
101
+ if (selectedComponentId === event.componentId) setSelectedComponent("");
102
+ } })
103
+ });
104
+ return { deleteComponent };
105
+ }
106
+
107
+ //#endregion
108
+ //#region src/design/react/hooks/useFocusInteraction.ts
109
+ function useFocusInteraction({ setSelectedComponent }) {
110
+ const { state: focusedComponentId, focusComponent } = useInteraction({
111
+ initialState: null,
112
+ eventHandlers: { ComponentFocused: { handler: (event, setState) => {
113
+ setSelectedComponent("");
114
+ setState(event.componentId);
115
+ } } },
116
+ actions: (_state, setState) => ({ focusComponent: (node) => {
117
+ node.scrollIntoView();
118
+ setState(null);
119
+ } })
120
+ });
121
+ return {
122
+ focusedComponentId,
123
+ focusComponent
124
+ };
125
+ }
126
+
127
+ //#endregion
128
+ //#region src/design/react/hooks/useScrollInteraction.ts
129
+ /**
130
+ * Custom hook that manages component hover state and handles
131
+ * client-host communication for hover events.
132
+ *
133
+ * @returns Hover state and interaction methods
134
+ */
135
+ function useScrollInteraction() {
136
+ const { notifyWindowScrollChange } = useInteraction({
137
+ initialState: null,
138
+ eventHandlers: { WindowScrollChanged: { handler: (event) => {
139
+ if (event.scrollY != null) window.scrollTo({
140
+ behavior: "instant",
141
+ top: event.scrollY
142
+ });
143
+ } } },
144
+ actions: (_state, _setState, clientApi) => ({ notifyWindowScrollChange: (x, y) => {
145
+ clientApi?.notifyWindowScrollChanged({
146
+ scrollX: x,
147
+ scrollY: y
148
+ });
149
+ } })
150
+ });
151
+ return { notifyWindowScrollChange };
152
+ }
153
+
154
+ //#endregion
155
+ //#region src/design/react/hooks/useComponentDiscovery.ts
156
+ /**
157
+ * Returns a utility for discovering components and regions at a given
158
+ * x, y coordinates.
159
+ * @param nodeToTargetMap - The map of nodes to target entries.
160
+ */
161
+ function useComponentDiscovery({ nodeToTargetMap }) {
162
+ return useCallback(({ x, y, filter = () => true }) => {
163
+ const nodeStack = document.elementsFromPoint(x, y);
164
+ const results = [];
165
+ for (let i = 0; i < nodeStack.length; i += 1) {
166
+ const node = nodeStack[i];
167
+ const entry = nodeToTargetMap.get(node);
168
+ if (entry && filter(entry)) results.push({
169
+ ...entry,
170
+ node
171
+ });
172
+ }
173
+ return results;
174
+ }, [nodeToTargetMap]);
175
+ }
176
+
177
+ //#endregion
178
+ //#region src/design/react/utils/regionUtils.ts
179
+ /**
180
+ * Copyright 2026 Salesforce, Inc.
181
+ *
182
+ * Licensed under the Apache License, Version 2.0 (the "License");
183
+ * you may not use this file except in compliance with the License.
184
+ * You may obtain a copy of the License at
185
+ *
186
+ * http://www.apache.org/licenses/LICENSE-2.0
187
+ *
188
+ * Unless required by applicable law or agreed to in writing, software
189
+ * distributed under the License is distributed on an "AS IS" BASIS,
190
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
191
+ * See the License for the specific language governing permissions and
192
+ * limitations under the License.
193
+ */
194
+ /**
195
+ * Checks if a component type is allowed in a region based on inclusion and exclusion rules.
196
+ *
197
+ * @param componentType - The type of component being checked
198
+ * @param componentTypeInclusions - Array of allowed component types (if empty, all types are allowed by default)
199
+ * @param componentTypeExclusions - Array of forbidden component types
200
+ * @returns true if the component type is allowed, false otherwise
201
+ */
202
+ function isComponentTypeAllowedInRegion(componentType, componentTypeInclusions, componentTypeExclusions) {
203
+ if (!componentType) return false;
204
+ if (componentTypeExclusions?.includes(componentType)) return false;
205
+ if (componentTypeInclusions?.length > 0) return componentTypeInclusions.includes(componentType);
206
+ return true;
207
+ }
208
+
209
+ //#endregion
210
+ //#region src/design/react/hooks/useDragInteraction.ts
211
+ const SCROLL_BUFFER_HEIGHT_PERCENTAGE = 15;
212
+ const SCROLL_BUFFER_MIN_HEIGHT_IN_PIXELS = 50;
213
+ const SCROLL_INTERVAL_IN_MS = 1e3 / 60;
214
+ const SCROLL_BASE_AMOUNT_IN_PIXELS = 50;
215
+ function getInsertionType({ cache, node, x, y }) {
216
+ if (!cache.has(node)) {
217
+ const rect$1 = node.getBoundingClientRect();
218
+ const screenLeft = rect$1.left - window.scrollX;
219
+ const screenTop = rect$1.top + window.scrollY;
220
+ cache.set(node, new DOMRect(screenLeft, screenTop, rect$1.width, rect$1.height));
221
+ }
222
+ const rect = cache.get(node);
223
+ const screenX = x + window.scrollX;
224
+ const screenY = y + window.scrollY;
225
+ const midX = rect.left + rect.width / 2;
226
+ const midY = rect.top + rect.height / 2;
227
+ const deltaX = screenX - midX;
228
+ const deltaY = screenY - midY;
229
+ const relativeDeltaX = deltaX / (rect.width / 2);
230
+ const relativeDeltaY = deltaY / (rect.height / 2);
231
+ if (Math.abs(relativeDeltaX) > Math.abs(relativeDeltaY)) return {
232
+ axis: "x",
233
+ type: relativeDeltaX < 0 ? "before" : "after"
234
+ };
235
+ return {
236
+ axis: "y",
237
+ type: relativeDeltaY < 0 ? "before" : "after"
238
+ };
239
+ }
240
+ function isOnSelfDropTarget({ sourceComponentId, beforeComponentId, afterComponentId, insertType, componentId }) {
241
+ const isOnSource = sourceComponentId && componentId === sourceComponentId;
242
+ const isOnSameRegionBefore = sourceComponentId && insertType.type === "before" && beforeComponentId === sourceComponentId;
243
+ const isOnSameRegionAfter = sourceComponentId && insertType.type === "after" && afterComponentId === sourceComponentId;
244
+ return isOnSource || isOnSameRegionBefore || isOnSameRegionAfter;
245
+ }
246
+ function useDragInteraction({ nodeToTargetMap }) {
247
+ const discoverComponents = useComponentDiscovery({ nodeToTargetMap });
248
+ const getNearestComponentAndRegion = useCallback((x, y) => {
249
+ const stack = discoverComponents({
250
+ x,
251
+ y
252
+ });
253
+ let component = null;
254
+ let region = null;
255
+ for (let i = 0; i < stack.length; i += 1) {
256
+ const entry = stack[i];
257
+ if (entry.regionId) {
258
+ if (entry.type === "component") component = entry;
259
+ else if (entry.type === "region") {
260
+ region = entry;
261
+ break;
262
+ }
263
+ }
264
+ }
265
+ return {
266
+ component,
267
+ region
268
+ };
269
+ }, [discoverComponents]);
270
+ const getInsertionComponentIds = (componentId, region) => {
271
+ const componentIndex = region.componentIds.indexOf(componentId);
272
+ return [region.componentIds[componentIndex - 1], region.componentIds[componentIndex + 1]];
273
+ };
274
+ const getCurrentDropTarget = useCallback(({ x, y, rectCache, componentType }) => {
275
+ const { component, region } = getNearestComponentAndRegion(x, y);
276
+ if (region) {
277
+ if (!isComponentTypeAllowedInRegion(componentType, region.componentTypeInclusions || [], region.componentTypeExclusions || [])) return null;
278
+ const insertType = component ? getInsertionType({
279
+ cache: rectCache,
280
+ node: component.node,
281
+ x,
282
+ y
283
+ }) : {
284
+ axis: "y",
285
+ type: "after"
286
+ };
287
+ const [beforeComponentId, afterComponentId] = component ? getInsertionComponentIds(component.componentId, region) : [];
288
+ return {
289
+ type: component ? "component" : "region",
290
+ regionId: region.regionId,
291
+ componentIds: region.componentIds,
292
+ componentId: component?.componentId ?? "",
293
+ parentId: region.parentId,
294
+ beforeComponentId,
295
+ afterComponentId,
296
+ insertComponentId: component?.componentId,
297
+ insertType,
298
+ componentTypeInclusions: region.componentTypeInclusions,
299
+ componentTypeExclusions: region.componentTypeExclusions
300
+ };
301
+ }
302
+ return null;
303
+ }, [getNearestComponentAndRegion]);
304
+ const computeScrollFactor = ({ y, windowHeight }) => {
305
+ const bufferHeight = Math.max(windowHeight * (SCROLL_BUFFER_HEIGHT_PERCENTAGE / 100), SCROLL_BUFFER_MIN_HEIGHT_IN_PIXELS);
306
+ const bottomBufferStart = windowHeight - bufferHeight;
307
+ if (y > bottomBufferStart) return (y - bottomBufferStart) / bufferHeight;
308
+ if (y < bufferHeight) return (y - bufferHeight) / bufferHeight;
309
+ return 0;
310
+ };
311
+ const computeScrollDirection = (factor) => {
312
+ if (factor > 0) return 1;
313
+ if (factor < 0) return -1;
314
+ return 0;
315
+ };
316
+ const scrollFactorRef = useRef(0);
317
+ const { state: dragState, commitCurrentDropTarget, updateComponentMove, startComponentMove, dropComponent, cancelDrag, setPendingComponentDragId } = useInteraction({
318
+ initialState: {
319
+ isDragging: false,
320
+ componentType: "",
321
+ sourceComponentId: void 0,
322
+ sourceRegionId: void 0,
323
+ x: 0,
324
+ y: 0,
325
+ currentDropTarget: null,
326
+ pendingTargetCommit: false,
327
+ rectCache: /* @__PURE__ */ new WeakMap(),
328
+ pendingComponentDragId: null
329
+ },
330
+ eventHandlers: {
331
+ ComponentDragStarted: { handler: (event, setState) => {
332
+ scrollFactorRef.current = 0;
333
+ setState((prevState) => ({
334
+ ...prevState,
335
+ componentType: event.componentType,
336
+ sourceComponentId: void 0,
337
+ sourceRegionId: void 0,
338
+ x: 0,
339
+ y: 0,
340
+ isDragging: true,
341
+ currentDropTarget: null,
342
+ pendingTargetCommit: false,
343
+ scrollDirection: 0,
344
+ rectCache: /* @__PURE__ */ new WeakMap()
345
+ }));
346
+ } },
347
+ ClientWindowDragExited: { handler: (_, setState) => {
348
+ scrollFactorRef.current = 0;
349
+ setState((prevState) => ({
350
+ ...prevState,
351
+ componentType: "",
352
+ x: 0,
353
+ y: 0,
354
+ isDragging: false,
355
+ currentDropTarget: null,
356
+ scrollDirection: 0,
357
+ pendingTargetCommit: false
358
+ }));
359
+ } },
360
+ ClientWindowDragMoved: { handler: (event, setState) => {
361
+ scrollFactorRef.current = computeScrollFactor({
362
+ y: event.y,
363
+ windowHeight: window.innerHeight
364
+ });
365
+ setState((prevState) => ({
366
+ ...prevState,
367
+ x: event.x,
368
+ y: event.y,
369
+ isDragging: true,
370
+ scrollDirection: computeScrollDirection(scrollFactorRef.current),
371
+ currentDropTarget: getCurrentDropTarget({
372
+ x: event.x,
373
+ y: event.y,
374
+ rectCache: dragState.rectCache,
375
+ componentType: prevState.componentType
376
+ })
377
+ }));
378
+ } },
379
+ ClientWindowDragDropped: { handler: (_, setState) => {
380
+ setState((prevState) => ({
381
+ ...prevState,
382
+ isDragging: false,
383
+ pendingTargetCommit: true
384
+ }));
385
+ } }
386
+ },
387
+ actions: (state, setState, clientApi) => ({
388
+ cancelDrag: () => {
389
+ scrollFactorRef.current = 0;
390
+ setState((prevState) => ({
391
+ ...prevState,
392
+ x: 0,
393
+ y: 0,
394
+ scrollDirection: 0,
395
+ isDragging: false,
396
+ pendingComponentDragId: null
397
+ }));
398
+ },
399
+ updateComponentMove: ({ x, y }) => {
400
+ scrollFactorRef.current = computeScrollFactor({
401
+ y,
402
+ windowHeight: window.innerHeight
403
+ });
404
+ setState((prevState) => ({
405
+ ...prevState,
406
+ x,
407
+ y,
408
+ scrollDirection: computeScrollDirection(scrollFactorRef.current),
409
+ currentDropTarget: getCurrentDropTarget({
410
+ x,
411
+ y,
412
+ rectCache: state.rectCache,
413
+ componentType: state.componentType
414
+ })
415
+ }));
416
+ },
417
+ setPendingComponentDragId: (componentId) => {
418
+ setState((prevState) => ({
419
+ ...prevState,
420
+ pendingComponentDragId: componentId
421
+ }));
422
+ },
423
+ dropComponent: () => {
424
+ setState((prevState) => ({
425
+ ...prevState,
426
+ isDragging: false,
427
+ pendingTargetCommit: true
428
+ }));
429
+ },
430
+ startComponentMove: (componentId, regionId, componentType) => {
431
+ scrollFactorRef.current = 0;
432
+ setState((prevState) => ({
433
+ ...prevState,
434
+ x: 0,
435
+ y: 0,
436
+ componentType,
437
+ sourceComponentId: componentId,
438
+ sourceRegionId: regionId,
439
+ isDragging: true,
440
+ scrollDirection: 0,
441
+ rectCache: /* @__PURE__ */ new WeakMap()
442
+ }));
443
+ },
444
+ commitCurrentDropTarget: () => {
445
+ if (state.currentDropTarget) {
446
+ if (state.sourceComponentId) {
447
+ if (!isOnSelfDropTarget({
448
+ sourceComponentId: state.sourceComponentId,
449
+ beforeComponentId: state.currentDropTarget.beforeComponentId,
450
+ afterComponentId: state.currentDropTarget.afterComponentId,
451
+ insertType: state.currentDropTarget.insertType,
452
+ componentId: state.currentDropTarget.componentId
453
+ })) clientApi?.moveComponentToRegion({
454
+ componentId: state.sourceComponentId,
455
+ sourceRegionId: state.sourceRegionId ?? "",
456
+ insertType: state.currentDropTarget.insertType?.type,
457
+ insertComponentId: state.currentDropTarget.insertComponentId,
458
+ beforeComponentId: state.currentDropTarget.beforeComponentId,
459
+ afterComponentId: state.currentDropTarget.afterComponentId,
460
+ targetRegionId: state.currentDropTarget.regionId,
461
+ targetComponentId: state.currentDropTarget.parentId ?? ""
462
+ });
463
+ } else if (state.componentType) clientApi?.addComponentToRegion({
464
+ insertType: state.currentDropTarget.insertType?.type,
465
+ insertComponentId: state.currentDropTarget.insertComponentId,
466
+ componentProperties: {},
467
+ componentType: state.componentType,
468
+ targetComponentId: state.currentDropTarget.parentId ?? "",
469
+ beforeComponentId: state.currentDropTarget.beforeComponentId,
470
+ afterComponentId: state.currentDropTarget.afterComponentId,
471
+ targetRegionId: state.currentDropTarget.regionId
472
+ });
473
+ }
474
+ scrollFactorRef.current = 0;
475
+ setState((prevState) => ({
476
+ ...prevState,
477
+ x: 0,
478
+ y: 0,
479
+ componentType: "",
480
+ scrollDirection: 0,
481
+ sourceComponentId: void 0,
482
+ sourceRegionId: void 0,
483
+ pendingComponentDragId: null,
484
+ currentDropTarget: null,
485
+ pendingTargetCommit: false
486
+ }));
487
+ }
488
+ })
489
+ });
490
+ useEffect(() => {
491
+ if (dragState.pendingTargetCommit) commitCurrentDropTarget();
492
+ }, [dragState.pendingTargetCommit, commitCurrentDropTarget]);
493
+ useEffect(() => {
494
+ if (dragState.scrollDirection !== 0) {
495
+ const interval = setInterval(() => {
496
+ window.scrollBy(0, scrollFactorRef.current * SCROLL_BASE_AMOUNT_IN_PIXELS);
497
+ }, SCROLL_INTERVAL_IN_MS);
498
+ return () => clearInterval(interval);
499
+ }
500
+ return () => {};
501
+ }, [dragState.scrollDirection, scrollFactorRef]);
502
+ return {
503
+ dragState,
504
+ setPendingComponentDragId,
505
+ commitCurrentDropTarget,
506
+ startComponentMove,
507
+ updateComponentMove,
508
+ dropComponent,
509
+ cancelDrag
510
+ };
511
+ }
512
+
513
+ //#endregion
514
+ //#region src/design/react/context/DesignStateContext.tsx
515
+ const DesignStateContext = React.createContext(null);
516
+ const DesignStateProvider = ({ children }) => {
517
+ const selectInteraction = useSelectInteraction();
518
+ const hoverInteraction = useHoverInteraction();
519
+ const deleteInteraction = useDeleteInteraction({
520
+ selectedComponentId: selectInteraction.selectedComponentId,
521
+ setSelectedComponent: selectInteraction.setSelectedComponent
522
+ });
523
+ const focusInteraction = useFocusInteraction({ setSelectedComponent: selectInteraction.setSelectedComponent });
524
+ const scrollInteraction = useScrollInteraction();
525
+ const nodeToTargetMap = React.useMemo(() => /* @__PURE__ */ new WeakMap(), []);
526
+ const dragInteraction = useDragInteraction({ nodeToTargetMap });
527
+ const state = React.useMemo(() => ({
528
+ ...deleteInteraction,
529
+ ...selectInteraction,
530
+ ...hoverInteraction,
531
+ ...focusInteraction,
532
+ ...dragInteraction,
533
+ ...scrollInteraction,
534
+ nodeToTargetMap
535
+ }), [
536
+ deleteInteraction,
537
+ selectInteraction,
538
+ hoverInteraction,
539
+ focusInteraction,
540
+ dragInteraction,
541
+ nodeToTargetMap,
542
+ scrollInteraction
543
+ ]);
544
+ return /* @__PURE__ */ jsx(DesignStateContext.Provider, {
545
+ value: state,
546
+ children
547
+ });
548
+ };
549
+
550
+ //#endregion
551
+ //#region src/design/react/hooks/useDesignState.ts
552
+ /**
553
+ * Custom hook that manages design-time component state by composing
554
+ * individual interaction hooks for better maintainability and testability.
555
+ *
556
+ * @returns Combined design state from all interactions
557
+ */
558
+ const useDesignState = () => {
559
+ const context = React.useContext(DesignStateContext);
560
+ if (!context) throw new Error("useDesignState must be used within a DesignStateProvider");
561
+ return context;
562
+ };
563
+
564
+ //#endregion
565
+ //#region src/design/react/hooks/useThrottledCallback.ts
566
+ function useThrottledCallback(callback, interval, deps = []) {
567
+ const lastCallTime = useRef(0);
568
+ return useCallback((...args) => {
569
+ const now = Date.now();
570
+ if (now >= lastCallTime.current + interval) {
571
+ lastCallTime.current = now;
572
+ callback(...args);
573
+ }
574
+ }, [
575
+ callback,
576
+ interval,
577
+ ...deps
578
+ ]);
579
+ }
580
+
581
+ //#endregion
582
+ //#region src/design/react/hooks/useDebouncedCallback.ts
583
+ function useDebouncedCallback(callback, interval, deps = []) {
584
+ const timeoutRef = useRef(null);
585
+ return useCallback((...args) => {
586
+ if (timeoutRef.current) {
587
+ clearTimeout(timeoutRef.current);
588
+ timeoutRef.current = null;
589
+ }
590
+ timeoutRef.current = setTimeout(() => {
591
+ callback(...args);
592
+ timeoutRef.current = null;
593
+ }, interval);
594
+ }, [
595
+ callback,
596
+ interval,
597
+ ...deps
598
+ ]);
599
+ }
600
+
601
+ //#endregion
602
+ //#region src/design/react/hooks/useGlobalListeners.ts
603
+ const FPS_60 = 1e3 / 60;
604
+ function useGlobalListeners() {
605
+ const { dropComponent, updateComponentMove, cancelDrag, notifyWindowScrollChange } = useDesignState();
606
+ const dragListener = useThrottledCallback((event) => updateComponentMove({
607
+ x: event.clientX,
608
+ y: event.clientY
609
+ }), FPS_60, [updateComponentMove]);
610
+ const scrollListener = useDebouncedCallback(() => notifyWindowScrollChange(window.scrollX, window.scrollY), 100, [notifyWindowScrollChange]);
611
+ useEffect(() => {
612
+ const dragEndListener = () => dropComponent();
613
+ const mouseUpListener = () => cancelDrag();
614
+ window.addEventListener("dragover", dragListener);
615
+ window.addEventListener("dragend", dragEndListener);
616
+ window.addEventListener("scroll", scrollListener);
617
+ window.addEventListener("mouseup", mouseUpListener);
618
+ return () => {
619
+ window.removeEventListener("dragover", dragListener);
620
+ window.removeEventListener("dragend", dragEndListener);
621
+ window.removeEventListener("mouseup", mouseUpListener);
622
+ window.removeEventListener("scroll", scrollListener);
623
+ };
624
+ }, [
625
+ dropComponent,
626
+ cancelDrag,
627
+ dragListener,
628
+ scrollListener
629
+ ]);
630
+ }
631
+
632
+ //#endregion
633
+ //#region src/design/react/hooks/useGlobalAnchorBlock.ts
634
+ /**
635
+ * React hook that prevents all <a> (anchor) navigation by default in the document,
636
+ * unless the anchor has the attribute `data-pd-allow-link`.
637
+ */
638
+ function useGlobalAnchorBlock() {
639
+ useEffect(() => {
640
+ function preventAnchorClicks(event) {
641
+ const anchor = event.target.closest("a");
642
+ if (anchor && !anchor.hasAttribute("data-pd-allow-link")) event.preventDefault();
643
+ }
644
+ document.addEventListener("click", preventAnchorClicks);
645
+ return () => document.removeEventListener("click", preventAnchorClicks);
646
+ }, []);
647
+ }
648
+
649
+ //#endregion
650
+ //#region src/design/react/components/DesignApp.tsx
651
+ /**
652
+ * Containes any global setup logic for the design layer.
653
+ */
654
+ const DesignApp = ({ children }) => {
655
+ useGlobalListeners();
656
+ useGlobalAnchorBlock();
657
+ return /* @__PURE__ */ jsx(Fragment, { children });
658
+ };
659
+
660
+ //#endregion
661
+ //#region src/design/react/context/DesignContext.tsx
662
+ const noop = () => {};
663
+ const DesignContext = React.createContext({
664
+ isDesignMode: false,
665
+ isConnected: false,
666
+ pageDesignerConfig: null,
667
+ clientPage: null,
668
+ setClientPage: noop
669
+ });
670
+ /**
671
+ * Provider component that enables design-time functionality for child components.
672
+ * Sets up client-host communication and manages component selection state.
673
+ *
674
+ * @param children - Child components to wrap with design functionality
675
+ * @param targetOrigin - Target origin for postMessage communication
676
+ * @param clientId - Id for the client API
677
+ * @returns JSX element wrapping children with design context
678
+ */
679
+ const DesignProvider = ({ children, targetOrigin, clientId, usid, clientConnectionTimeout, clientConnectionInterval, clientLogger = noop }) => {
680
+ const { isDesignMode } = usePageDesignerMode();
681
+ const [isConnected, setIsConnected] = React.useState(false);
682
+ const [pageDesignerConfig, setPageDesignerConfig] = React.useState(null);
683
+ const [clientPage, setClientPage] = React.useState(null);
684
+ const clientPageRef = React.useRef(null);
685
+ const clientApi = React.useMemo(() => createClientApi({
686
+ logger: clientLogger,
687
+ emitter: {
688
+ postMessage: (message) => window.parent.postMessage(message, targetOrigin),
689
+ addEventListener: (handler) => {
690
+ const listener = (event) => handler(event.data);
691
+ window.addEventListener("message", listener);
692
+ return () => window.removeEventListener("message", listener);
693
+ }
694
+ },
695
+ id: clientId
696
+ }), [
697
+ targetOrigin,
698
+ clientId,
699
+ clientLogger
700
+ ]);
701
+ React.useEffect(() => {
702
+ clientApi.connect({
703
+ timeout: clientConnectionTimeout,
704
+ interval: clientConnectionInterval,
705
+ onHostConnected: (event) => {
706
+ setPageDesignerConfig(event);
707
+ setIsConnected(true);
708
+ },
709
+ onHostDisconnected: (reconnect) => {
710
+ setPageDesignerConfig(null);
711
+ setIsConnected(false);
712
+ reconnect();
713
+ },
714
+ onError: () => {},
715
+ usid
716
+ });
717
+ return () => {
718
+ clientApi.disconnect();
719
+ setPageDesignerConfig(null);
720
+ setIsConnected(false);
721
+ };
722
+ }, [
723
+ clientApi,
724
+ clientConnectionTimeout,
725
+ clientConnectionInterval,
726
+ usid
727
+ ]);
728
+ const contextValue = React.useMemo(() => ({
729
+ isDesignMode,
730
+ clientApi,
731
+ isConnected,
732
+ pageDesignerConfig,
733
+ clientPage,
734
+ setClientPage: (page) => {
735
+ if (page !== clientPageRef.current) {
736
+ clientPageRef.current = page;
737
+ setClientPage(page);
738
+ clientApi?.notifyClientPageChanged({ page });
739
+ }
740
+ }
741
+ }), [
742
+ isDesignMode,
743
+ clientApi,
744
+ isConnected,
745
+ pageDesignerConfig,
746
+ clientPage,
747
+ setClientPage
748
+ ]);
749
+ return /* @__PURE__ */ jsx(DesignContext.Provider, {
750
+ value: contextValue,
751
+ children: /* @__PURE__ */ jsx(DesignStateProvider, { children: /* @__PURE__ */ jsx(DesignApp, { children }) })
752
+ });
753
+ };
754
+ DesignProvider.defaultProps = {
755
+ clientLogger: noop,
756
+ clientConnectionTimeout: 6e4,
757
+ clientConnectionInterval: 1e3
758
+ };
759
+ /**
760
+ * Custom hook to access the design context
761
+ * Provides access to design mode state and component selection functionality
762
+ *
763
+ * @returns The current design context
764
+ */
765
+ const useDesignContext = () => React.useContext(DesignContext);
766
+
767
+ //#endregion
768
+ export { isComponentTypeAllowedInRegion as a, useDesignState as i, DesignProvider as n, useComponentDiscovery as o, useDesignContext as r, DesignContext as t };
769
+ //# sourceMappingURL=DesignContext.js.map