@salesforce/storefront-next-runtime 0.4.0-alpha.2 → 0.4.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.
- package/dist/ComponentContext.js.map +1 -1
- package/dist/DesignComponent.js +46 -31
- package/dist/DesignComponent.js.map +1 -1
- package/dist/DesignContext.js +91 -65
- package/dist/DesignContext.js.map +1 -1
- package/dist/DesignFrame.js +8 -4
- package/dist/DesignFrame.js.map +1 -1
- package/dist/DesignRegion.js +6 -8
- package/dist/DesignRegion.js.map +1 -1
- package/dist/component.types.d.ts +2 -2
- package/dist/config.d.ts +2 -2
- package/dist/custom-global-preferences.d.ts +3 -3
- package/dist/custom-site-preferences.d.ts +3 -3
- package/dist/data-store.d.ts +3 -3
- package/dist/data-store.d.ts.map +1 -1
- package/dist/design-react-core.d.ts +2 -1
- package/dist/design-react-core.d.ts.map +1 -1
- package/dist/gcp-preferences.d.ts +3 -3
- package/dist/index.d.ts +25 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/scapi.d.ts.map +1 -1
- package/dist/site-context.d.ts +5 -5
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DesignContext.js","names":["results: ComponentDiscoveryResult[]","rect","component: ComponentDiscoveryResult | null","region: ComponentDiscoveryResult | null","insertType: InsertionType","initialUpdates: Record<string, ComponentUpdate>"],"sources":["../src/design/react/hooks/useInteraction.ts","../src/design/react/hooks/useSelectInteraction.ts","../src/design/react/hooks/useHoverInteraction.ts","../src/design/react/hooks/useDeleteInteraction.ts","../src/design/react/hooks/useFocusInteraction.ts","../src/design/react/hooks/useScrollInteraction.ts","../src/design/react/hooks/useComponentDiscovery.ts","../src/design/react/utils/regionUtils.ts","../src/design/react/hooks/useDragInteraction.ts","../src/design/react/hooks/useComponentUpdateInteraction.ts","../src/design/react/context/DesignStateContext.tsx","../src/design/react/hooks/useDesignState.ts","../src/design/react/hooks/useThrottledCallback.ts","../src/design/react/hooks/useDebouncedCallback.ts","../src/design/react/hooks/useGlobalListeners.ts","../src/design/react/hooks/useGlobalAnchorBlock.ts","../src/design/react/components/DesignApp.tsx","../src/design/react/context/DesignContext.tsx"],"sourcesContent":["/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useEffect, useState, type Dispatch, type SetStateAction } from 'react';\nimport type { ClientApi, ClientEventNameMapping } from '../../messaging-api';\nimport { useDesignContext } from '../context/DesignContext';\n\nexport interface EventHandler<TState, TName extends keyof ClientEventNameMapping> {\n handler: (event: ClientEventNameMapping[TName], setState: Dispatch<SetStateAction<TState>>) => void;\n}\n\nexport interface InteractionConfig<TState, TActions> {\n /** Initial state value */\n initialState: TState | (() => TState);\n /** Event handlers to register with the client API */\n eventHandlers?: {\n [TKey in keyof ClientEventNameMapping]?: EventHandler<TState, TKey>;\n };\n /** Action creators that return functions to interact with the client API */\n actions?: (state: TState, setState: Dispatch<SetStateAction<TState>>, clientApi: ClientApi | null) => TActions;\n}\n\n/**\n * Base hook that provides common interaction patterns for design-time functionality.\n * Reduces boilerplate by handling state management, event listeners, and cleanup.\n *\n * @param config - Configuration object defining the interaction behavior\n * @returns Object containing state and action methods\n */\nexport function useInteraction<\n TState,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n TActions extends Record<string, (...args: any[]) => any>,\n>(config: InteractionConfig<TState, TActions>): { state: TState } & TActions {\n const [state, setState] = useState<TState>(config.initialState);\n const { isDesignMode, clientApi } = useDesignContext();\n\n useEffect(() => {\n if (!isDesignMode || !clientApi) {\n return () => {\n // Return empty cleanup function for consistency\n };\n }\n\n const unsubscribeFunctions = Object.entries(config.eventHandlers ?? {}).map(([eventName, entry]) =>\n clientApi.on(eventName as keyof ClientEventNameMapping, (event) =>\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n entry.handler(event as any, setState)\n )\n );\n\n return () => {\n unsubscribeFunctions.forEach((unsubscribe) => unsubscribe());\n };\n }, [isDesignMode, clientApi, config.eventHandlers]);\n\n const actions = config.actions?.(state, setState, clientApi ?? null) ?? ({} as TActions);\n\n return { state, ...actions };\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useInteraction } from './useInteraction';\n\nexport interface SelectInteraction {\n selectedComponentId: string;\n setSelectedComponent: (componentId: string) => void;\n}\n\n/**\n * Custom hook that manages component selection state and handles\n * client-host communication for selection events.\n *\n * @param isDesignMode - Whether design mode is active\n * @param clientApi - Client API for host communication\n * @returns Selection state and interaction methods\n */\nexport function useSelectInteraction(): SelectInteraction {\n const { state: selectedComponentId, setSelectedComponent } = useInteraction({\n initialState: '',\n eventHandlers: {\n ComponentSelected: {\n handler: (event, setState) => {\n setState(event.componentId);\n },\n },\n ComponentDeselected: {\n handler: (_, setState) => {\n setState('');\n },\n },\n },\n actions: (_state, setState, clientApi) => ({\n setSelectedComponent: (componentId: string) => {\n setState(componentId);\n clientApi?.selectComponent({ componentId });\n },\n }),\n });\n\n return {\n selectedComponentId,\n setSelectedComponent,\n };\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useInteraction } from './useInteraction';\n\nexport interface HoverInteraction {\n hoveredComponentId: string | null;\n setHoveredComponent: (componentId: string | null) => void;\n}\n\n/**\n * Custom hook that manages component hover state and handles\n * client-host communication for hover events.\n *\n * @returns Hover state and interaction methods\n */\nexport function useHoverInteraction(): HoverInteraction {\n const { state: hoveredComponentId, setHoveredComponent } = useInteraction({\n initialState: null as string | null,\n eventHandlers: {\n ComponentHoveredIn: {\n handler: (event, setState) => setState(event.componentId),\n },\n ComponentHoveredOut: {\n handler: (_, setState) => setState(null),\n },\n },\n actions: (state, setState, clientApi) => ({\n setHoveredComponent: (componentId: string | null) => {\n if (state && componentId !== state) {\n // Use the current hovered component for hover out\n clientApi?.hoverOutOfComponent({\n componentId: state,\n });\n }\n\n if (componentId && componentId !== state) {\n clientApi?.hoverInToComponent({ componentId });\n }\n\n setState(componentId);\n },\n }),\n });\n\n return {\n hoveredComponentId,\n setHoveredComponent,\n };\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useInteraction } from './useInteraction';\nimport type { ComponentDeletedEvent, EventPayload } from '../../messaging-api';\n\nexport interface DeleteInteraction {\n deleteComponent: (componentId: EventPayload<ComponentDeletedEvent>) => void;\n}\n\nexport function useDeleteInteraction({\n selectedComponentId,\n setSelectedComponent,\n}: {\n selectedComponentId: string | null;\n setSelectedComponent: (componentId: string) => void;\n}): DeleteInteraction {\n const { deleteComponent } = useInteraction({\n initialState: null,\n eventHandlers: {},\n actions: (_state, _setState, clientApi) => ({\n deleteComponent: (event: EventPayload<ComponentDeletedEvent>) => {\n clientApi?.deleteComponent(event);\n\n // When a component is deleted, we want to make sure it's no longer selected.\n if (selectedComponentId === event.componentId) {\n setSelectedComponent('');\n }\n },\n }),\n });\n\n return {\n deleteComponent,\n };\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useInteraction } from './useInteraction';\n\nexport interface FocusInteraction {\n focusedComponentId: string | null;\n focusComponent: (node: Element) => void;\n}\n\nexport function useFocusInteraction({\n setSelectedComponent,\n}: {\n setSelectedComponent: (componentId: string) => void;\n}): FocusInteraction {\n const { state: focusedComponentId, focusComponent } = useInteraction({\n initialState: null as string | null,\n eventHandlers: {\n ComponentFocused: {\n handler: (event, setState) => {\n setSelectedComponent('');\n setState(event.componentId);\n },\n },\n },\n actions: (_state, setState) => ({\n focusComponent: (node: Element) => {\n node.scrollIntoView();\n setState(null);\n },\n }),\n });\n\n return {\n focusedComponentId,\n focusComponent,\n };\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useInteraction } from './useInteraction';\n\nexport interface ScrollInteraction {\n notifyWindowScrollChange: (x: number, y: number) => void;\n}\n\n/**\n * Custom hook that manages component hover state and handles\n * client-host communication for hover events.\n *\n * @returns Hover state and interaction methods\n */\nexport function useScrollInteraction(): ScrollInteraction {\n const { notifyWindowScrollChange } = useInteraction({\n initialState: null,\n eventHandlers: {\n WindowScrollChanged: {\n handler: (event) => {\n if (event.scrollY != null) {\n window.scrollTo({\n behavior: 'instant',\n top: event.scrollY,\n });\n }\n },\n },\n },\n actions: (_state, _setState, clientApi) => ({\n notifyWindowScrollChange: (x: number, y: number) => {\n clientApi?.notifyWindowScrollChanged({\n scrollX: x,\n scrollY: y,\n });\n },\n }),\n });\n\n return { notifyWindowScrollChange };\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useCallback } from 'react';\nimport type { NodeToTargetMapEntry } from '../context/DesignStateContext';\n\nexport type ComponentDiscoveryResult = NodeToTargetMapEntry & { node: Element };\n\n/**\n * Returns a utility for discovering components and regions at a given\n * x, y coordinates.\n * @param nodeToTargetMap - The map of nodes to target entries.\n */\nexport function useComponentDiscovery({\n nodeToTargetMap,\n}: {\n nodeToTargetMap: WeakMap<Element, NodeToTargetMapEntry>;\n}): (query: { x: number; y: number; filter?: (entry: NodeToTargetMapEntry) => boolean }) => ComponentDiscoveryResult[] {\n return useCallback(\n ({ x, y, filter = () => true }) => {\n const nodeStack = document.elementsFromPoint(x, y);\n const results: ComponentDiscoveryResult[] = [];\n\n for (let i = 0; i < nodeStack.length; i += 1) {\n const node = nodeStack[i];\n const entry = nodeToTargetMap.get(node);\n\n if (entry && filter(entry)) {\n results.push({ ...entry, node });\n }\n }\n\n return results;\n },\n [nodeToTargetMap]\n );\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Checks if a component type is allowed in a region based on inclusion and exclusion rules.\n *\n * @param componentType - The type of component being checked\n * @param componentTypeInclusions - Array of allowed component types (if empty, all types are allowed by default)\n * @param componentTypeExclusions - Array of forbidden component types\n * @returns true if the component type is allowed, false otherwise\n */\nexport function isComponentTypeAllowedInRegion(\n componentType: string | undefined,\n componentTypeInclusions: string[],\n componentTypeExclusions: string[]\n): boolean {\n if (!componentType) {\n return false;\n }\n\n if (componentTypeExclusions?.includes(componentType)) {\n return false;\n }\n\n // If there are inclusions specified, the component type must be in the list\n if (componentTypeInclusions?.length > 0) {\n return componentTypeInclusions.includes(componentType);\n }\n\n return true;\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useCallback, useEffect, useRef } from 'react';\nimport { useInteraction } from './useInteraction';\nimport type { NodeToTargetMapEntry } from '../context/DesignStateContext';\nimport { useComponentDiscovery, type ComponentDiscoveryResult } from './useComponentDiscovery';\nimport { isComponentTypeAllowedInRegion } from '../utils/regionUtils';\n\n// The height of the scroll buffer on the top and bottom of the window\n// as a percentage of the window height.\nconst SCROLL_BUFFER_HEIGHT_PERCENTAGE = 15;\nconst SCROLL_BUFFER_MIN_HEIGHT_IN_PIXELS = 50;\n// The interval at which the window will scroll within the buffer.\n// More often means a smoother experience.\nconst SCROLL_INTERVAL_IN_MS = 1000 / 60; // 60fps\n// The multiplier applied to the scroll factor.\n// The scroll factor is a value between 0 and 1 that determines how much to scroll.\n// This value will be the maximum amount of pixels that will be scrolled in a single frame.\nconst SCROLL_BASE_AMOUNT_IN_PIXELS = 50;\n\ninterface InsertionType {\n axis: 'x' | 'y';\n type: 'before' | 'after';\n}\n\nexport interface DropTarget extends NodeToTargetMapEntry {\n beforeComponentId?: string;\n afterComponentId?: string;\n insertType: InsertionType;\n insertComponentId?: string;\n regionId: string;\n}\n\nexport interface DragInteraction {\n dragState: {\n isDragging: boolean;\n x: number;\n y: number;\n currentDropTarget: DropTarget | null;\n pendingTargetCommit: boolean;\n componentType?: string;\n sourceComponentId?: string;\n sourceRegionId?: string;\n rectCache: WeakMap<Element, DOMRect>;\n scrollDirection: 0 | 1 | -1;\n pendingComponentDragId: string | null;\n };\n commitCurrentDropTarget: () => void;\n startComponentMove: (componentId: string, regionId: string, componentType: string) => void;\n updateComponentMove: (params: { x: number; y: number }) => void;\n setPendingComponentDragId: (componentId: string) => void;\n dropComponent: () => void;\n cancelDrag: () => void;\n}\n\nfunction getInsertionType({\n cache,\n node,\n x,\n y,\n}: {\n cache: WeakMap<Element, DOMRect>;\n node: Element;\n x: number;\n y: number;\n}): InsertionType {\n if (!cache.has(node)) {\n const rect = node.getBoundingClientRect();\n const screenLeft = rect.left - window.scrollX;\n const screenTop = rect.top + window.scrollY;\n\n // A bounding box is relative to the viewport.\n // We need to know the absolute position, taking into account the scroll position.\n cache.set(node, new DOMRect(screenLeft, screenTop, rect.width, rect.height));\n }\n\n const rect = cache.get(node) as DOMRect;\n const screenX = x + window.scrollX;\n const screenY = y + window.scrollY;\n const midX = rect.left + rect.width / 2;\n const midY = rect.top + rect.height / 2;\n const deltaX = screenX - midX;\n const deltaY = screenY - midY;\n // Use the relative delta for boxes that are not square.\n const relativeDeltaX = deltaX / (rect.width / 2);\n const relativeDeltaY = deltaY / (rect.height / 2);\n\n if (Math.abs(relativeDeltaX) > Math.abs(relativeDeltaY)) {\n return { axis: 'x', type: relativeDeltaX < 0 ? 'before' : 'after' };\n }\n\n return { axis: 'y', type: relativeDeltaY < 0 ? 'before' : 'after' };\n}\n\n// Determines whether a source component is being dropped on itself.\nfunction isOnSelfDropTarget({\n sourceComponentId,\n beforeComponentId,\n afterComponentId,\n insertType,\n componentId,\n}: {\n sourceComponentId: string | undefined;\n beforeComponentId: string | undefined;\n afterComponentId: string | undefined;\n insertType: InsertionType;\n componentId: string;\n}) {\n const isOnSource = sourceComponentId && componentId === sourceComponentId;\n const isOnSameRegionBefore =\n sourceComponentId && insertType.type === 'before' && beforeComponentId === sourceComponentId;\n const isOnSameRegionAfter =\n sourceComponentId && insertType.type === 'after' && afterComponentId === sourceComponentId;\n\n return isOnSource || isOnSameRegionBefore || isOnSameRegionAfter;\n}\n\nexport function useDragInteraction({\n nodeToTargetMap,\n}: {\n nodeToTargetMap: WeakMap<Element, NodeToTargetMapEntry>;\n}): DragInteraction {\n const discoverComponents = useComponentDiscovery({\n nodeToTargetMap,\n });\n const getNearestComponentAndRegion = useCallback(\n (\n x: number,\n y: number\n ): {\n component: ComponentDiscoveryResult | null;\n region: ComponentDiscoveryResult | null;\n } => {\n const stack = discoverComponents({ x, y });\n let component: ComponentDiscoveryResult | null = null;\n let region: ComponentDiscoveryResult | null = null;\n\n for (let i = 0; i < stack.length; i += 1) {\n const entry = stack[i];\n\n // We need a region id and direction for this to be a target.\n if (entry.regionId) {\n if (entry.type === 'component') {\n component = entry;\n } else if (entry.type === 'region') {\n region = entry;\n // Once we find a region we need to exit.\n break;\n }\n }\n }\n\n return { component, region };\n },\n [discoverComponents]\n );\n\n const getInsertionComponentIds = (\n componentId: string,\n region: NodeToTargetMapEntry & { node: Element }\n ): [string | undefined, string | undefined] => {\n const componentIndex = region.componentIds.indexOf(componentId);\n\n return [region.componentIds[componentIndex - 1], region.componentIds[componentIndex + 1]];\n };\n\n const getCurrentDropTarget = useCallback(\n ({\n x,\n y,\n rectCache,\n componentType,\n }: {\n x: number;\n y: number;\n rectCache: WeakMap<Element, DOMRect>;\n componentType?: string;\n }): DropTarget | null => {\n const { component, region } = getNearestComponentAndRegion(x, y);\n\n if (region) {\n // If component type is not allowed, don't return a drop target\n if (\n !isComponentTypeAllowedInRegion(\n componentType,\n region.componentTypeInclusions || [],\n region.componentTypeExclusions || []\n )\n ) {\n return null;\n }\n\n const insertType: InsertionType = component\n ? getInsertionType({\n cache: rectCache,\n node: component.node,\n x,\n y,\n })\n : { axis: 'y', type: 'after' };\n\n const [beforeComponentId, afterComponentId] = component\n ? getInsertionComponentIds(component.componentId, region)\n : [];\n\n // If we find a component before a region, it means we are dropping over a component.\n // If no component is found before a region, it means we are dropping over an empty region.\n return {\n type: component ? 'component' : 'region',\n regionId: region.regionId,\n componentIds: region.componentIds,\n componentId: component?.componentId ?? '',\n parentId: region.parentId,\n beforeComponentId,\n afterComponentId,\n insertComponentId: component?.componentId,\n insertType,\n componentTypeInclusions: region.componentTypeInclusions,\n componentTypeExclusions: region.componentTypeExclusions,\n };\n }\n\n return null;\n },\n [getNearestComponentAndRegion]\n );\n\n const computeScrollFactor = ({ y, windowHeight }: { y: number; windowHeight: number }) => {\n const bufferHeight = Math.max(\n windowHeight * (SCROLL_BUFFER_HEIGHT_PERCENTAGE / 100),\n SCROLL_BUFFER_MIN_HEIGHT_IN_PIXELS\n );\n const bottomBufferStart = windowHeight - bufferHeight;\n\n if (y > bottomBufferStart) {\n return (y - bottomBufferStart) / bufferHeight;\n }\n\n if (y < bufferHeight) {\n return (y - bufferHeight) / bufferHeight;\n }\n\n return 0;\n };\n\n const computeScrollDirection = (factor: number): 0 | 1 | -1 => {\n if (factor > 0) {\n return 1;\n }\n\n if (factor < 0) {\n return -1;\n }\n\n return 0;\n };\n\n const scrollFactorRef = useRef(0);\n\n const {\n state: dragState,\n commitCurrentDropTarget,\n updateComponentMove,\n startComponentMove,\n dropComponent,\n cancelDrag,\n setPendingComponentDragId,\n } = useInteraction({\n initialState: {\n isDragging: false,\n componentType: '',\n sourceComponentId: undefined as string | undefined,\n sourceRegionId: undefined as string | undefined,\n x: 0,\n y: 0,\n currentDropTarget: null as DropTarget | null,\n pendingTargetCommit: false,\n rectCache: new WeakMap<Element, DOMRect>(),\n pendingComponentDragId: null,\n } as DragInteraction['dragState'],\n eventHandlers: {\n ComponentDragStarted: {\n handler: (event, setState) => {\n scrollFactorRef.current = 0;\n\n setState((prevState) => ({\n ...prevState,\n componentType: event.componentType,\n sourceComponentId: undefined,\n sourceRegionId: undefined,\n x: 0,\n y: 0,\n isDragging: true,\n currentDropTarget: null,\n pendingTargetCommit: false,\n scrollDirection: 0,\n rectCache: new WeakMap<Element, DOMRect>(),\n }));\n },\n },\n ClientWindowDragExited: {\n handler: (_, setState) => {\n scrollFactorRef.current = 0;\n\n setState((prevState) => ({\n ...prevState,\n componentType: '',\n x: 0,\n y: 0,\n isDragging: false,\n currentDropTarget: null,\n scrollDirection: 0,\n pendingTargetCommit: false,\n }));\n },\n },\n ClientWindowDragMoved: {\n handler: (event, setState) => {\n scrollFactorRef.current = computeScrollFactor({\n y: event.y,\n windowHeight: window.innerHeight,\n });\n\n setState((prevState) => ({\n ...prevState,\n x: event.x,\n y: event.y,\n isDragging: true,\n scrollDirection: computeScrollDirection(scrollFactorRef.current),\n currentDropTarget: getCurrentDropTarget({\n x: event.x,\n y: event.y,\n rectCache: dragState.rectCache,\n componentType: prevState.componentType,\n }),\n }));\n },\n },\n ClientWindowDragDropped: {\n handler: (_, setState) => {\n setState((prevState) => ({\n ...prevState,\n isDragging: false,\n pendingTargetCommit: true,\n }));\n },\n },\n },\n actions: (state, setState, clientApi) => ({\n cancelDrag: () => {\n scrollFactorRef.current = 0;\n\n setState((prevState) => ({\n ...prevState,\n x: 0,\n y: 0,\n scrollDirection: 0,\n isDragging: false,\n pendingComponentDragId: null,\n }));\n },\n updateComponentMove: ({ x, y }: { x: number; y: number }) => {\n scrollFactorRef.current = computeScrollFactor({\n y,\n windowHeight: window.innerHeight,\n });\n\n setState((prevState) => ({\n ...prevState,\n x,\n y,\n scrollDirection: computeScrollDirection(scrollFactorRef.current),\n currentDropTarget: getCurrentDropTarget({\n x,\n y,\n rectCache: state.rectCache,\n componentType: state.componentType,\n }),\n }));\n },\n setPendingComponentDragId: (componentId: string) => {\n setState((prevState) => ({\n ...prevState,\n pendingComponentDragId: componentId,\n }));\n },\n dropComponent: () => {\n setState((prevState) => ({\n ...prevState,\n isDragging: false,\n pendingTargetCommit: true,\n }));\n },\n startComponentMove: (componentId: string, regionId: string, componentType: string) => {\n scrollFactorRef.current = 0;\n\n setState((prevState) => ({\n ...prevState,\n x: 0,\n y: 0,\n componentType,\n sourceComponentId: componentId,\n sourceRegionId: regionId,\n isDragging: true,\n scrollDirection: 0,\n rectCache: new WeakMap<Element, DOMRect>(),\n }));\n },\n commitCurrentDropTarget: () => {\n // Don't do anything if we don't have a drop target.\n if (state.currentDropTarget) {\n // If we have a source component id, then we are moving a component to a different region.\n if (state.sourceComponentId) {\n if (\n !isOnSelfDropTarget({\n sourceComponentId: state.sourceComponentId,\n beforeComponentId: state.currentDropTarget.beforeComponentId,\n afterComponentId: state.currentDropTarget.afterComponentId,\n insertType: state.currentDropTarget.insertType,\n componentId: state.currentDropTarget.componentId,\n })\n ) {\n clientApi?.moveComponentToRegion({\n componentId: state.sourceComponentId,\n sourceRegionId: state.sourceRegionId ?? '',\n insertType: state.currentDropTarget.insertType?.type,\n insertComponentId: state.currentDropTarget.insertComponentId,\n beforeComponentId: state.currentDropTarget.beforeComponentId,\n afterComponentId: state.currentDropTarget.afterComponentId,\n targetRegionId: state.currentDropTarget.regionId,\n targetComponentId: state.currentDropTarget.parentId ?? '',\n });\n }\n // If we don't have a source component id, then we are adding a new component to a region.\n } else if (state.componentType) {\n clientApi?.addComponentToRegion({\n insertType: state.currentDropTarget.insertType?.type,\n insertComponentId: state.currentDropTarget.insertComponentId,\n componentProperties: {},\n componentType: state.componentType,\n targetComponentId: state.currentDropTarget.parentId ?? '',\n beforeComponentId: state.currentDropTarget.beforeComponentId,\n afterComponentId: state.currentDropTarget.afterComponentId,\n targetRegionId: state.currentDropTarget.regionId,\n });\n }\n }\n\n scrollFactorRef.current = 0;\n\n setState((prevState) => ({\n ...prevState,\n x: 0,\n y: 0,\n componentType: '',\n scrollDirection: 0,\n sourceComponentId: undefined,\n sourceRegionId: undefined,\n pendingComponentDragId: null,\n currentDropTarget: null,\n pendingTargetCommit: false,\n }));\n },\n }),\n });\n\n // Commits the current drop target if we are pending a target commit.\n useEffect(() => {\n if (dragState.pendingTargetCommit) {\n commitCurrentDropTarget();\n }\n }, [dragState.pendingTargetCommit, commitCurrentDropTarget]);\n\n // Starts scrolling the window when the drag state scroll factor is not 0.\n useEffect(() => {\n if (dragState.scrollDirection !== 0) {\n const interval = setInterval(() => {\n window.scrollBy(0, scrollFactorRef.current * SCROLL_BASE_AMOUNT_IN_PIXELS);\n }, SCROLL_INTERVAL_IN_MS);\n\n return () => clearInterval(interval);\n }\n\n return () => {\n // noop\n };\n }, [dragState.scrollDirection, scrollFactorRef]);\n\n return {\n dragState,\n setPendingComponentDragId,\n commitCurrentDropTarget,\n startComponentMove,\n updateComponentMove,\n dropComponent,\n cancelDrag,\n };\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useInteraction } from './useInteraction';\n\nexport interface ComponentUpdate {\n name?: string;\n [key: string]: unknown;\n}\n\nexport interface ComponentUpdateInteraction {\n componentUpdates: Record<string, ComponentUpdate>;\n}\n\n/**\n * Custom hook that manages component update state and handles\n * client-host communication for component update events.\n *\n * Listens for ComponentUpdated events from the host and maintains\n * a map of component IDs to their updated data.\n *\n * @returns Component update state\n */\nexport function useComponentUpdateInteraction(): ComponentUpdateInteraction {\n const { state: componentUpdates } = useInteraction<Record<string, ComponentUpdate>, Record<string, never>>({\n initialState: {},\n eventHandlers: {\n // Handle initial component names from page-init\n ClientAcknowledged: {\n handler: (event, setState) => {\n const initialUpdates: Record<string, ComponentUpdate> = {};\n Object.entries(event.components).forEach(([id, componentInfo]) => {\n if (componentInfo.name) {\n initialUpdates[id] = { name: componentInfo.name };\n }\n });\n // Merge to preserve ComponentUpdated changes\n if (Object.keys(initialUpdates).length > 0) {\n setState((prev) => ({ ...prev, ...initialUpdates }));\n }\n },\n },\n // Handle runtime component updates\n ComponentUpdated: {\n handler: (event, setState) => {\n setState((prev) => {\n const componentId = event.componentId;\n const existing = prev[componentId] || {};\n\n // Update the specific field based on changeType\n const updated = { ...existing };\n if (event.changeType === 'name') {\n updated.name = event.newValue as string;\n } else if (event.changeType === 'visibility') {\n updated.visibility = event.newValue;\n }\n\n return {\n ...prev,\n [componentId]: updated,\n };\n });\n },\n },\n },\n });\n\n return {\n componentUpdates,\n };\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React from 'react';\nimport { useSelectInteraction } from '../hooks/useSelectInteraction';\nimport { useHoverInteraction } from '../hooks/useHoverInteraction';\nimport { useDeleteInteraction } from '../hooks/useDeleteInteraction';\nimport { useFocusInteraction } from '../hooks/useFocusInteraction';\nimport { useScrollInteraction, type ScrollInteraction } from '../hooks/useScrollInteraction';\nimport { useDragInteraction, type DragInteraction } from '../hooks/useDragInteraction';\nimport { useComponentUpdateInteraction, type ComponentUpdateInteraction } from '../hooks/useComponentUpdateInteraction';\nimport type { ComponentDeletedEvent, EventPayload } from '../../messaging-api';\n\nexport interface NodeToTargetMapEntry {\n type: 'region' | 'component';\n parentId?: string;\n componentId: string;\n regionId: string;\n componentIds: string[];\n componentTypeInclusions?: string[];\n componentTypeExclusions?: string[];\n}\n\nexport interface DesignState extends DragInteraction, ScrollInteraction, ComponentUpdateInteraction {\n selectedComponentId: string | null;\n hoveredComponentId: string | null;\n setSelectedComponent: (componentId: string) => void;\n setHoveredComponent: (componentId: string | null) => void;\n deleteComponent: (event: EventPayload<ComponentDeletedEvent>) => void;\n focusComponent: (node: Element) => void;\n focusedComponentId: string | null;\n nodeToTargetMap: WeakMap<Element, NodeToTargetMapEntry>;\n}\n\n// eslint-disable-next-line react-refresh/only-export-components\nexport const DesignStateContext = React.createContext<DesignState>(null as unknown as DesignState);\n\nexport const DesignStateProvider = ({ children }: { children: React.ReactNode }): React.JSX.Element => {\n const selectInteraction = useSelectInteraction();\n const hoverInteraction = useHoverInteraction();\n const deleteInteraction = useDeleteInteraction({\n selectedComponentId: selectInteraction.selectedComponentId,\n setSelectedComponent: selectInteraction.setSelectedComponent,\n });\n const focusInteraction = useFocusInteraction({\n setSelectedComponent: selectInteraction.setSelectedComponent,\n });\n const scrollInteraction = useScrollInteraction();\n const componentUpdateInteraction = useComponentUpdateInteraction();\n const nodeToTargetMap = React.useMemo(() => new WeakMap(), []);\n const dragInteraction = useDragInteraction({ nodeToTargetMap });\n\n const state = React.useMemo(\n () => ({\n ...deleteInteraction,\n ...selectInteraction,\n ...hoverInteraction,\n ...focusInteraction,\n ...dragInteraction,\n ...scrollInteraction,\n ...componentUpdateInteraction,\n nodeToTargetMap,\n }),\n [\n deleteInteraction,\n selectInteraction,\n hoverInteraction,\n focusInteraction,\n dragInteraction,\n nodeToTargetMap,\n scrollInteraction,\n componentUpdateInteraction,\n ]\n );\n\n return <DesignStateContext.Provider value={state}>{children}</DesignStateContext.Provider>;\n};\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React from 'react';\nimport { DesignStateContext, type DesignState } from '../context/DesignStateContext';\n\n/**\n * Custom hook that manages design-time component state by composing\n * individual interaction hooks for better maintainability and testability.\n *\n * @returns Combined design state from all interactions\n */\nexport const useDesignState = (): DesignState => {\n const context = React.useContext(DesignStateContext);\n\n if (!context) {\n throw new Error('useDesignState must be used within a DesignStateProvider');\n }\n\n return context;\n};\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useRef, useCallback } from 'react';\n\nexport function useThrottledCallback<TArgs extends unknown[], TReturn>(\n callback: (...args: TArgs) => TReturn,\n interval: number,\n deps: unknown[] = []\n): (...args: TArgs) => TReturn | void {\n const lastCallTime = useRef<number>(0);\n\n return useCallback(\n (...args: TArgs): TReturn | void => {\n const now = Date.now();\n\n if (now >= lastCallTime.current + interval) {\n lastCallTime.current = now;\n\n callback(...args);\n }\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [callback, interval, ...deps]\n );\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useRef, useCallback } from 'react';\n\nexport function useDebouncedCallback<TArgs extends unknown[], TReturn>(\n callback: (...args: TArgs) => TReturn,\n interval: number,\n deps: unknown[] = []\n): (...args: TArgs) => TReturn | void {\n const timeoutRef = useRef<number | null>(null);\n\n return useCallback(\n (...args: TArgs): TReturn | void => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n\n timeoutRef.current = setTimeout(() => {\n callback(...args);\n timeoutRef.current = null;\n }, interval) as unknown as number;\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [callback, interval, ...deps]\n );\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useEffect } from 'react';\nimport { useDesignState } from './useDesignState';\nimport { useThrottledCallback } from './useThrottledCallback';\nimport { useDebouncedCallback } from './useDebouncedCallback';\n\nconst FPS_60 = 1000 / 60;\n\nexport function useGlobalListeners(): void {\n const { dropComponent, updateComponentMove, cancelDrag, notifyWindowScrollChange } = useDesignState();\n const dragListener = useThrottledCallback(\n (event: DragEvent) => updateComponentMove({ x: event.clientX, y: event.clientY }),\n FPS_60,\n [updateComponentMove]\n );\n const scrollListener = useDebouncedCallback(() => notifyWindowScrollChange(window.scrollX, window.scrollY), 100, [\n notifyWindowScrollChange,\n ]);\n\n useEffect(() => {\n const dragEndListener = () => dropComponent();\n const mouseUpListener = () => cancelDrag();\n\n window.addEventListener('dragover', dragListener);\n window.addEventListener('dragend', dragEndListener);\n window.addEventListener('scroll', scrollListener);\n // We need to make sure we cancel dragging on mouseup since we\n // we are using mousedown to start dragging or else it would stay in a dragging\n // state from regular click events.\n window.addEventListener('mouseup', mouseUpListener);\n\n return () => {\n window.removeEventListener('dragover', dragListener);\n window.removeEventListener('dragend', dragEndListener);\n window.removeEventListener('mouseup', mouseUpListener);\n window.removeEventListener('scroll', scrollListener);\n };\n }, [dropComponent, cancelDrag, dragListener, scrollListener]);\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useEffect } from 'react';\n\n/**\n * React hook that prevents all <a> (anchor) navigation by default in the document,\n * unless the anchor has the attribute `data-pd-allow-link`.\n */\nexport function useGlobalAnchorBlock(): void {\n useEffect(() => {\n function preventAnchorClicks(event: MouseEvent) {\n const target = event.target as HTMLElement;\n const anchor = target.closest('a');\n\n // This data attribute acts as a workaround in the event we do\n // want to have an anchor tag navigate in design mode.\n if (anchor && !anchor.hasAttribute('data-pd-allow-link')) {\n event.preventDefault();\n }\n }\n\n document.addEventListener('click', preventAnchorClicks);\n\n return () => document.removeEventListener('click', preventAnchorClicks);\n }, []);\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useGlobalListeners } from '../hooks/useGlobalListeners';\nimport { useGlobalAnchorBlock } from '../hooks/useGlobalAnchorBlock';\n\n/**\n * Containes any global setup logic for the design layer.\n */\nexport const DesignApp = ({ children }: React.PropsWithChildren<unknown>): React.JSX.Element => {\n useGlobalListeners();\n useGlobalAnchorBlock();\n\n return <>{children}</>;\n};\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React from 'react';\nimport {\n createClientApi,\n type ClientApi,\n type IsomorphicConfiguration,\n type ClientAcknowledgedEvent,\n type EventPayload,\n type HostToClientConfiguration,\n} from '../../messaging-api';\nimport type { ShopperExperience } from '@/scapi-client/types';\nimport { DesignStateProvider } from './DesignStateContext';\nimport { DesignApp } from '../components/DesignApp';\nimport { usePageDesignerMode } from '../core/PageDesignerProvider';\n\nconst noop = () => {\n /* noop */\n};\n\n/**\n * Type definition for the Design Context\n * Extends DesignState with additional design-time properties\n */\nexport interface DesignContextType {\n /** Whether design mode is currently active */\n isDesignMode: boolean;\n /** Client API for host communication */\n clientApi?: ClientApi;\n /** Whether the client is connected to the host */\n isConnected: boolean;\n /** The page designer config */\n pageDesignerConfig: EventPayload<ClientAcknowledgedEvent> | null;\n /** Page data that the client has retrieved */\n clientPage: ShopperExperience.schemas['Page'] | null;\n /** Sets the client page data */\n setClientPage: (page: ShopperExperience.schemas['Page']) => void;\n}\n\n// eslint-disable-next-line react-refresh/only-export-components\nexport const DesignContext = React.createContext<DesignContextType>({\n isDesignMode: false,\n isConnected: false,\n pageDesignerConfig: null,\n clientPage: null,\n setClientPage: noop,\n});\n\n/**\n * Provider component that enables design-time functionality for child components.\n * Sets up client-host communication and manages component selection state.\n *\n * @param children - Child components to wrap with design functionality\n * @param targetOrigin - Target origin for postMessage communication\n * @param clientId - Id for the client API\n * @returns JSX element wrapping children with design context\n */\nexport const DesignProvider = ({\n children,\n targetOrigin,\n clientId,\n usid,\n clientConnectionTimeout,\n clientConnectionInterval,\n clientLogger = noop,\n}: React.PropsWithChildren<{\n targetOrigin: string;\n clientId: string;\n usid?: string;\n clientConnectionTimeout?: number;\n clientConnectionInterval?: number;\n clientLogger?: IsomorphicConfiguration['logger'];\n}>): React.JSX.Element => {\n const { isDesignMode } = usePageDesignerMode();\n const [isConnected, setIsConnected] = React.useState(false);\n const [pageDesignerConfig, setPageDesignerConfig] = React.useState<HostToClientConfiguration | null>(null);\n const [clientPage, setClientPage] = React.useState<ShopperExperience.schemas['Page'] | null>(null);\n const clientPageRef = React.useRef<ShopperExperience.schemas['Page'] | null>(null);\n\n const clientApi = React.useMemo(\n () =>\n createClientApi({\n logger: clientLogger,\n emitter: {\n postMessage: (message) => window.parent.postMessage(message, targetOrigin),\n addEventListener: (handler) => {\n const listener = (event: MessageEvent) => handler(event.data);\n\n window.addEventListener('message', listener);\n\n return () => window.removeEventListener('message', listener);\n },\n },\n id: clientId,\n }),\n [targetOrigin, clientId, clientLogger]\n );\n\n React.useEffect(() => {\n // This will poll the host for a connection until the client is acknowledged.\n clientApi.connect({\n timeout: clientConnectionTimeout,\n interval: clientConnectionInterval,\n onHostConnected: (event) => {\n setPageDesignerConfig(event);\n setIsConnected(true);\n },\n onHostDisconnected: (reconnect) => {\n setPageDesignerConfig(null);\n setIsConnected(false);\n reconnect();\n },\n onError: () => {\n // TODO: Figure out how to handle this.\n },\n usid,\n });\n\n return () => {\n clientApi.disconnect();\n setPageDesignerConfig(null);\n setIsConnected(false);\n };\n }, [clientApi, clientConnectionTimeout, clientConnectionInterval, usid]);\n\n // Use the extracted state management hook\n const contextValue = React.useMemo<DesignContextType>(\n () => ({\n isDesignMode,\n clientApi,\n isConnected,\n pageDesignerConfig,\n clientPage,\n setClientPage: (page: ShopperExperience.schemas['Page']) => {\n if (page !== clientPageRef.current) {\n clientPageRef.current = page;\n setClientPage(page);\n clientApi?.notifyClientPageChanged({ page });\n }\n },\n }),\n [isDesignMode, clientApi, isConnected, pageDesignerConfig, clientPage, setClientPage]\n );\n\n return (\n <DesignContext.Provider value={contextValue}>\n <DesignStateProvider>\n <DesignApp>{children}</DesignApp>\n </DesignStateProvider>\n </DesignContext.Provider>\n );\n};\n\nDesignProvider.defaultProps = {\n clientLogger: noop,\n clientConnectionTimeout: 60_000,\n clientConnectionInterval: 1_000,\n};\n\n/**\n * Custom hook to access the design context\n * Provides access to design mode state and component selection functionality\n *\n * @returns The current design context\n */\n// eslint-disable-next-line react-refresh/only-export-components\nexport const useDesignContext = (): DesignContextType => React.useContext(DesignContext);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DesignContext.js","names":["results: ComponentDiscoveryResult[]","rect","component: ComponentDiscoveryResult | null","region: ComponentDiscoveryResult | null","insertType: InsertionType","initialUpdates: Record<string, ComponentUpdate>"],"sources":["../src/design/react/hooks/useInteraction.ts","../src/design/react/hooks/useSelectInteraction.ts","../src/design/react/hooks/useHoverInteraction.ts","../src/design/react/hooks/useDeleteInteraction.ts","../src/design/react/hooks/useFocusInteraction.ts","../src/design/react/hooks/useScrollInteraction.ts","../src/design/react/hooks/useComponentDiscovery.ts","../src/design/react/utils/regionUtils.ts","../src/design/react/hooks/useDragInteraction.ts","../src/design/react/hooks/useComponentUpdateInteraction.ts","../src/design/react/context/DesignStateContext.tsx","../src/design/react/hooks/useDesignState.ts","../src/design/react/hooks/useThrottledCallback.ts","../src/design/react/hooks/useDebouncedCallback.ts","../src/design/react/hooks/useGlobalListeners.ts","../src/design/react/hooks/useGlobalAnchorBlock.ts","../src/design/react/components/DesignApp.tsx","../src/design/react/context/DesignContext.tsx"],"sourcesContent":["/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useEffect, useState, type Dispatch, type SetStateAction } from 'react';\nimport type { ClientApi, ClientEventNameMapping } from '../../messaging-api';\nimport { useDesignContext } from '../context/DesignContext';\n\nexport interface EventHandler<TState, TName extends keyof ClientEventNameMapping> {\n handler: (event: ClientEventNameMapping[TName], setState: Dispatch<SetStateAction<TState>>) => void;\n}\n\nexport interface InteractionConfig<TState, TActions> {\n /** Initial state value */\n initialState: TState | (() => TState);\n /** Event handlers to register with the client API */\n eventHandlers?: {\n [TKey in keyof ClientEventNameMapping]?: EventHandler<TState, TKey>;\n };\n /** Action creators that return functions to interact with the client API */\n actions?: (state: TState, setState: Dispatch<SetStateAction<TState>>, clientApi: ClientApi | null) => TActions;\n}\n\n/**\n * Base hook that provides common interaction patterns for design-time functionality.\n * Reduces boilerplate by handling state management, event listeners, and cleanup.\n *\n * @param config - Configuration object defining the interaction behavior\n * @returns Object containing state and action methods\n */\nexport function useInteraction<\n TState,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n TActions extends Record<string, (...args: any[]) => any>,\n>(config: InteractionConfig<TState, TActions>): { state: TState } & TActions {\n const [state, setState] = useState<TState>(config.initialState);\n const { isDesignMode, clientApi } = useDesignContext();\n\n useEffect(() => {\n if (!isDesignMode || !clientApi) {\n return () => {\n // Return empty cleanup function for consistency\n };\n }\n\n const unsubscribeFunctions = Object.entries(config.eventHandlers ?? {}).map(([eventName, entry]) =>\n clientApi.on(eventName as keyof ClientEventNameMapping, (event) =>\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n entry.handler(event as any, setState)\n )\n );\n\n return () => {\n unsubscribeFunctions.forEach((unsubscribe) => unsubscribe());\n };\n }, [isDesignMode, clientApi, config.eventHandlers]);\n\n const actions = config.actions?.(state, setState, clientApi ?? null) ?? ({} as TActions);\n\n return { state, ...actions };\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useInteraction } from './useInteraction';\n\nexport interface SelectInteraction {\n selectedContentLinkUuid: string;\n setSelectedComponent: (contentLinkUuid: string) => void;\n}\n\n/**\n * Custom hook that manages component selection state and handles\n * client-host communication for selection events.\n *\n * @returns Selection state and interaction methods\n */\nexport function useSelectInteraction({\n contentLinkMap,\n}: {\n contentLinkMap: Record<string, string>;\n}): SelectInteraction {\n const { state: selectedContentLinkUuid, setSelectedComponent } = useInteraction({\n initialState: '',\n eventHandlers: {\n ComponentSelected: {\n handler: (event, setState) => {\n setState(event.contentLinkUuid);\n },\n },\n ComponentDeselected: {\n handler: (_, setState) => {\n setState('');\n },\n },\n },\n actions: (_state, setState, clientApi) => ({\n setSelectedComponent: (contentLinkUuid: string) => {\n setState(contentLinkUuid);\n clientApi?.selectComponent({\n componentId: contentLinkMap[contentLinkUuid] ?? '',\n contentLinkUuid,\n });\n },\n }),\n });\n\n return {\n selectedContentLinkUuid,\n setSelectedComponent,\n };\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useInteraction } from './useInteraction';\n\nexport interface HoverInteraction {\n hoveredContentLinkUuid: string | null;\n setHoveredComponent: (componentUuid: string | null) => void;\n}\n\n/**\n * Custom hook that manages component hover state and handles\n * client-host communication for hover events.\n *\n * @returns Hover state and interaction methods\n */\nexport function useHoverInteraction({ contentLinkMap }: { contentLinkMap: Record<string, string> }): HoverInteraction {\n const { state: hoveredContentLinkUuid, setHoveredComponent } = useInteraction({\n initialState: null as string | null,\n eventHandlers: {\n ComponentHoveredIn: {\n handler: (event, setState) => setState(event.contentLinkUuid),\n },\n ComponentHoveredOut: {\n handler: (_, setState) => setState(null),\n },\n },\n actions: (state, setState, clientApi) => ({\n setHoveredComponent: (componentUuid: string | null) => {\n if (state && componentUuid !== state) {\n clientApi?.hoverOutOfComponent({\n componentId: contentLinkMap[state] ?? state,\n contentLinkUuid: state,\n });\n }\n\n if (componentUuid && componentUuid !== state) {\n clientApi?.hoverInToComponent({\n componentId: contentLinkMap[componentUuid] ?? null,\n contentLinkUuid: componentUuid,\n });\n }\n\n setState(componentUuid);\n },\n }),\n });\n\n return {\n hoveredContentLinkUuid,\n setHoveredComponent,\n };\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useInteraction } from './useInteraction';\nimport type { ComponentDeletedEvent, EventPayload } from '../../messaging-api';\n\nexport interface DeleteInteraction {\n deleteComponent: (componentId: EventPayload<ComponentDeletedEvent>) => void;\n}\n\nexport function useDeleteInteraction({\n selectedContentLinkUuid,\n setSelectedComponent,\n}: {\n selectedContentLinkUuid: string | null;\n setSelectedComponent: (contentLinkUuid: string) => void;\n}): DeleteInteraction {\n const { deleteComponent } = useInteraction({\n initialState: null,\n eventHandlers: {},\n actions: (_state, _setState, clientApi) => ({\n deleteComponent: (event: EventPayload<ComponentDeletedEvent>) => {\n clientApi?.deleteComponent(event);\n\n // When a component is deleted, we want to make sure it's no longer selected.\n if (selectedContentLinkUuid === event.contentLinkUuid) {\n setSelectedComponent('');\n }\n },\n }),\n });\n\n return {\n deleteComponent,\n };\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useInteraction } from './useInteraction';\n\nexport interface FocusInteraction {\n focusedContentLinkUuid: string | null;\n focusComponent: (node: Element) => void;\n}\n\nexport function useFocusInteraction({\n setSelectedComponent,\n}: {\n setSelectedComponent: (contentLinkUuid: string) => void;\n}): FocusInteraction {\n const { state: focusedContentLinkUuid, focusComponent } = useInteraction({\n initialState: null as string | null,\n eventHandlers: {\n ComponentFocused: {\n handler: (event, setState) => {\n setSelectedComponent('');\n setState(event.contentLinkUuid);\n },\n },\n },\n actions: (_state, setState) => ({\n focusComponent: (node: Element) => {\n node.scrollIntoView();\n setState(null);\n },\n }),\n });\n\n return {\n focusedContentLinkUuid,\n focusComponent,\n };\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useInteraction } from './useInteraction';\n\nexport interface ScrollInteraction {\n notifyWindowScrollChange: (x: number, y: number) => void;\n}\n\n/**\n * Custom hook that manages component hover state and handles\n * client-host communication for hover events.\n *\n * @returns Hover state and interaction methods\n */\nexport function useScrollInteraction(): ScrollInteraction {\n const { notifyWindowScrollChange } = useInteraction({\n initialState: null,\n eventHandlers: {\n WindowScrollChanged: {\n handler: (event) => {\n if (event.scrollY != null) {\n window.scrollTo({\n behavior: 'instant',\n top: event.scrollY,\n });\n }\n },\n },\n },\n actions: (_state, _setState, clientApi) => ({\n notifyWindowScrollChange: (x: number, y: number) => {\n clientApi?.notifyWindowScrollChanged({\n scrollX: x,\n scrollY: y,\n });\n },\n }),\n });\n\n return { notifyWindowScrollChange };\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useCallback } from 'react';\nimport type { NodeToTargetMapEntry } from '../context/DesignStateContext';\n\nexport type ComponentDiscoveryResult = NodeToTargetMapEntry & { node: Element };\n\n/**\n * Returns a utility for discovering components and regions at a given\n * x, y coordinates.\n * @param nodeToTargetMap - The map of nodes to target entries.\n */\nexport function useComponentDiscovery({\n nodeToTargetMap,\n}: {\n nodeToTargetMap: WeakMap<Element, NodeToTargetMapEntry>;\n}): (query: { x: number; y: number; filter?: (entry: NodeToTargetMapEntry) => boolean }) => ComponentDiscoveryResult[] {\n return useCallback(\n ({ x, y, filter = () => true }) => {\n const nodeStack = document.elementsFromPoint(x, y);\n const results: ComponentDiscoveryResult[] = [];\n\n for (let i = 0; i < nodeStack.length; i += 1) {\n const node = nodeStack[i];\n const entry = nodeToTargetMap.get(node);\n\n if (entry && filter(entry)) {\n results.push({ ...entry, node });\n }\n }\n\n return results;\n },\n [nodeToTargetMap]\n );\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Checks if a component type is allowed in a region based on inclusion and exclusion rules.\n *\n * @param componentType - The type of component being checked\n * @param componentTypeInclusions - Array of allowed component types (if empty, all types are allowed by default)\n * @param componentTypeExclusions - Array of forbidden component types\n * @returns true if the component type is allowed, false otherwise\n */\nexport function isComponentTypeAllowedInRegion(\n componentType: string | undefined,\n componentTypeInclusions: string[],\n componentTypeExclusions: string[]\n): boolean {\n if (!componentType) {\n return false;\n }\n\n if (componentTypeExclusions?.includes(componentType)) {\n return false;\n }\n\n // If there are inclusions specified, the component type must be in the list\n if (componentTypeInclusions?.length > 0) {\n return componentTypeInclusions.includes(componentType);\n }\n\n return true;\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useCallback, useEffect, useRef } from 'react';\nimport { useInteraction } from './useInteraction';\nimport type { NodeToTargetMapEntry } from '../context/DesignStateContext';\nimport { useComponentDiscovery, type ComponentDiscoveryResult } from './useComponentDiscovery';\nimport { isComponentTypeAllowedInRegion } from '../utils/regionUtils';\n\n// The height of the scroll buffer on the top and bottom of the window\n// as a percentage of the window height.\nconst SCROLL_BUFFER_HEIGHT_PERCENTAGE = 15;\nconst SCROLL_BUFFER_MIN_HEIGHT_IN_PIXELS = 50;\n// The interval at which the window will scroll within the buffer.\n// More often means a smoother experience.\nconst SCROLL_INTERVAL_IN_MS = 1000 / 60; // 60fps\n// The multiplier applied to the scroll factor.\n// The scroll factor is a value between 0 and 1 that determines how much to scroll.\n// This value will be the maximum amount of pixels that will be scrolled in a single frame.\nconst SCROLL_BASE_AMOUNT_IN_PIXELS = 50;\n\ninterface InsertionType {\n axis: 'x' | 'y';\n type: 'before' | 'after';\n}\n\nexport interface DropTarget extends Omit<NodeToTargetMapEntry, 'contentLinkUuids'> {\n beforeContentLinkUuid?: string;\n afterContentLinkUuid?: string;\n insertType: InsertionType;\n insertContentLinkUuid?: string;\n regionId: string;\n}\n\nexport interface DragInteraction {\n dragState: {\n isDragging: boolean;\n x: number;\n y: number;\n currentDropTarget: DropTarget | null;\n pendingTargetCommit: boolean;\n componentType?: string;\n fragmentId?: string;\n sourceContentLinkUuid?: string;\n sourceRegionId?: string;\n rectCache: WeakMap<Element, DOMRect>;\n scrollDirection: 0 | 1 | -1;\n pendingDragContentLinkUuid: string | null;\n };\n commitCurrentDropTarget: () => void;\n startComponentMove: (componentId: string, regionId: string, componentType: string, contentLinkUuid: string) => void;\n updateComponentMove: (params: { x: number; y: number }) => void;\n setPendingDragContentLinkUuid: (contentLinkUuid: string) => void;\n dropComponent: () => void;\n cancelDrag: () => void;\n}\n\nfunction getInsertionType({\n cache,\n node,\n x,\n y,\n}: {\n cache: WeakMap<Element, DOMRect>;\n node: Element;\n x: number;\n y: number;\n}): InsertionType {\n if (!cache.has(node)) {\n const rect = node.getBoundingClientRect();\n const screenLeft = rect.left - window.scrollX;\n const screenTop = rect.top + window.scrollY;\n\n // A bounding box is relative to the viewport.\n // We need to know the absolute position, taking into account the scroll position.\n cache.set(node, new DOMRect(screenLeft, screenTop, rect.width, rect.height));\n }\n\n const rect = cache.get(node) as DOMRect;\n const screenX = x + window.scrollX;\n const screenY = y + window.scrollY;\n const midX = rect.left + rect.width / 2;\n const midY = rect.top + rect.height / 2;\n const deltaX = screenX - midX;\n const deltaY = screenY - midY;\n // Use the relative delta for boxes that are not square.\n const relativeDeltaX = deltaX / (rect.width / 2);\n const relativeDeltaY = deltaY / (rect.height / 2);\n\n if (Math.abs(relativeDeltaX) > Math.abs(relativeDeltaY)) {\n return { axis: 'x', type: relativeDeltaX < 0 ? 'before' : 'after' };\n }\n\n return { axis: 'y', type: relativeDeltaY < 0 ? 'before' : 'after' };\n}\n\n// Determines whether a source component is being dropped on itself.\n// Note: componentId parameters here are actually contentLinkUuid values (with fallback to id)\n// from the region's contentLinkUuids array, allowing proper duplicate component handling.\nfunction isOnSelfDropTarget({\n sourceContentLinkUuid,\n beforeContentLinkUuid,\n afterContentLinkUuid,\n insertType,\n contentLinkUuid,\n}: {\n sourceContentLinkUuid: string | undefined;\n beforeContentLinkUuid: string | undefined;\n afterContentLinkUuid: string | undefined;\n insertType: InsertionType;\n contentLinkUuid: string;\n}) {\n const isOnSource = sourceContentLinkUuid && contentLinkUuid === sourceContentLinkUuid;\n const isOnSameRegionBefore =\n sourceContentLinkUuid && insertType.type === 'before' && beforeContentLinkUuid === sourceContentLinkUuid;\n const isOnSameRegionAfter =\n sourceContentLinkUuid && insertType.type === 'after' && afterContentLinkUuid === sourceContentLinkUuid;\n\n return isOnSource || isOnSameRegionBefore || isOnSameRegionAfter;\n}\n\nexport function useDragInteraction({\n nodeToTargetMap,\n}: {\n nodeToTargetMap: WeakMap<Element, NodeToTargetMapEntry>;\n}): DragInteraction {\n const discoverComponents = useComponentDiscovery({\n nodeToTargetMap,\n });\n const getNearestComponentAndRegion = useCallback(\n (\n x: number,\n y: number\n ): {\n component: ComponentDiscoveryResult | null;\n region: ComponentDiscoveryResult | null;\n } => {\n const stack = discoverComponents({ x, y });\n let component: ComponentDiscoveryResult | null = null;\n let region: ComponentDiscoveryResult | null = null;\n\n for (let i = 0; i < stack.length; i += 1) {\n const entry = stack[i];\n\n // We need a region id and direction for this to be a target.\n if (entry.regionId) {\n if (entry.type === 'component') {\n component = entry;\n } else if (entry.type === 'region') {\n region = entry;\n // Once we find a region we need to exit.\n break;\n }\n }\n }\n\n return { component, region };\n },\n [discoverComponents]\n );\n\n const getInsertionComponentUuids = (\n contentLinkUuid: string,\n region: NodeToTargetMapEntry & { node: Element }\n ): [string | undefined, string | undefined] => {\n const componentIndex = region.contentLinkUuids.indexOf(contentLinkUuid);\n\n return [region.contentLinkUuids[componentIndex - 1], region.contentLinkUuids[componentIndex + 1]];\n };\n\n const getCurrentDropTarget = useCallback(\n ({\n x,\n y,\n rectCache,\n componentType,\n }: {\n x: number;\n y: number;\n rectCache: WeakMap<Element, DOMRect>;\n componentType?: string;\n }): DropTarget | null => {\n const { component, region } = getNearestComponentAndRegion(x, y);\n\n if (region) {\n // If component type is not allowed, don't return a drop target\n if (\n !isComponentTypeAllowedInRegion(\n componentType,\n region.componentTypeInclusions || [],\n region.componentTypeExclusions || []\n )\n ) {\n return null;\n }\n\n const insertType: InsertionType = component\n ? getInsertionType({\n cache: rectCache,\n node: component.node,\n x,\n y,\n })\n : { axis: 'y', type: 'after' };\n\n const componentContentLinkUuid = component?.contentLinkUuid ?? '';\n const [beforeContentLinkUuid, afterContentLinkUuid] = component\n ? getInsertionComponentUuids(componentContentLinkUuid, region)\n : [];\n\n // If we find a component before a region, it means we are dropping over a component.\n // If no component is found before a region, it means we are dropping over an empty region.\n return {\n type: component ? 'component' : 'region',\n regionId: region.regionId,\n componentId: component?.componentId ?? '',\n contentLinkUuid: componentContentLinkUuid,\n parentId: region.parentId,\n beforeContentLinkUuid,\n afterContentLinkUuid,\n insertContentLinkUuid: componentContentLinkUuid,\n insertType,\n componentTypeInclusions: region.componentTypeInclusions,\n componentTypeExclusions: region.componentTypeExclusions,\n };\n }\n\n return null;\n },\n [getNearestComponentAndRegion]\n );\n\n const computeScrollFactor = ({ y, windowHeight }: { y: number; windowHeight: number }) => {\n const bufferHeight = Math.max(\n windowHeight * (SCROLL_BUFFER_HEIGHT_PERCENTAGE / 100),\n SCROLL_BUFFER_MIN_HEIGHT_IN_PIXELS\n );\n const bottomBufferStart = windowHeight - bufferHeight;\n\n if (y > bottomBufferStart) {\n return (y - bottomBufferStart) / bufferHeight;\n }\n\n if (y < bufferHeight) {\n return (y - bufferHeight) / bufferHeight;\n }\n\n return 0;\n };\n\n const computeScrollDirection = (factor: number): 0 | 1 | -1 => {\n if (factor > 0) {\n return 1;\n }\n\n if (factor < 0) {\n return -1;\n }\n\n return 0;\n };\n\n const scrollFactorRef = useRef(0);\n\n const {\n state: dragState,\n commitCurrentDropTarget,\n updateComponentMove,\n startComponentMove,\n dropComponent,\n cancelDrag,\n setPendingDragContentLinkUuid,\n } = useInteraction({\n initialState: {\n isDragging: false,\n componentType: '',\n fragmentId: undefined as string | undefined,\n sourceContentLinkUuid: undefined as string | undefined,\n sourceRegionId: undefined as string | undefined,\n x: 0,\n y: 0,\n currentDropTarget: null as DropTarget | null,\n pendingTargetCommit: false,\n rectCache: new WeakMap<Element, DOMRect>(),\n pendingDragContentLinkUuid: null,\n } as DragInteraction['dragState'],\n eventHandlers: {\n ComponentDragStarted: {\n handler: (event, setState) => {\n scrollFactorRef.current = 0;\n\n setState((prevState) => ({\n ...prevState,\n componentType: event.componentType,\n fragmentId: event.fragmentId,\n sourceContentLinkUuid: undefined,\n sourceRegionId: undefined,\n x: 0,\n y: 0,\n isDragging: true,\n currentDropTarget: null,\n pendingTargetCommit: false,\n scrollDirection: 0,\n rectCache: new WeakMap<Element, DOMRect>(),\n }));\n },\n },\n ClientWindowDragExited: {\n handler: (_, setState) => {\n scrollFactorRef.current = 0;\n\n setState((prevState) => ({\n ...prevState,\n componentType: '',\n x: 0,\n y: 0,\n isDragging: false,\n currentDropTarget: null,\n scrollDirection: 0,\n pendingTargetCommit: false,\n }));\n },\n },\n ClientWindowDragMoved: {\n handler: (event, setState) => {\n scrollFactorRef.current = computeScrollFactor({\n y: event.y,\n windowHeight: window.innerHeight,\n });\n\n setState((prevState) => ({\n ...prevState,\n x: event.x,\n y: event.y,\n isDragging: true,\n scrollDirection: computeScrollDirection(scrollFactorRef.current),\n currentDropTarget: getCurrentDropTarget({\n x: event.x,\n y: event.y,\n rectCache: dragState.rectCache,\n componentType: prevState.componentType,\n }),\n }));\n },\n },\n ClientWindowDragDropped: {\n handler: (_, setState) => {\n setState((prevState) => ({\n ...prevState,\n isDragging: false,\n pendingTargetCommit: true,\n }));\n },\n },\n },\n actions: (state, setState, clientApi) => ({\n cancelDrag: () => {\n scrollFactorRef.current = 0;\n\n setState((prevState) => ({\n ...prevState,\n x: 0,\n y: 0,\n scrollDirection: 0,\n isDragging: false,\n pendingDragContentLinkUuid: null,\n }));\n },\n updateComponentMove: ({ x, y }: { x: number; y: number }) => {\n scrollFactorRef.current = computeScrollFactor({\n y,\n windowHeight: window.innerHeight,\n });\n\n setState((prevState) => ({\n ...prevState,\n x,\n y,\n scrollDirection: computeScrollDirection(scrollFactorRef.current),\n currentDropTarget: getCurrentDropTarget({\n x,\n y,\n rectCache: state.rectCache,\n componentType: state.componentType,\n }),\n }));\n },\n setPendingDragContentLinkUuid: (contentLinkUuid: string) => {\n setState((prevState) => ({\n ...prevState,\n pendingDragContentLinkUuid: contentLinkUuid,\n }));\n },\n dropComponent: () => {\n setState((prevState) => ({\n ...prevState,\n isDragging: false,\n pendingTargetCommit: true,\n }));\n },\n startComponentMove: (\n componentId: string,\n regionId: string,\n componentType: string,\n contentLinkUuid: string\n ) => {\n scrollFactorRef.current = 0;\n\n setState((prevState) => ({\n ...prevState,\n x: 0,\n y: 0,\n componentType,\n sourceContentLinkUuid: contentLinkUuid,\n sourceRegionId: regionId,\n isDragging: true,\n scrollDirection: 0,\n rectCache: new WeakMap<Element, DOMRect>(),\n }));\n },\n commitCurrentDropTarget: () => {\n // Don't do anything if we don't have a drop target.\n if (state.currentDropTarget) {\n // If we have a source content link uuid, then we are moving a component to a different region.\n if (state.sourceContentLinkUuid) {\n if (\n !isOnSelfDropTarget({\n sourceContentLinkUuid: state.sourceContentLinkUuid,\n beforeContentLinkUuid: state.currentDropTarget.beforeContentLinkUuid,\n afterContentLinkUuid: state.currentDropTarget.afterContentLinkUuid,\n insertType: state.currentDropTarget.insertType,\n contentLinkUuid: state.currentDropTarget.contentLinkUuid ?? '',\n })\n ) {\n clientApi?.moveComponentToRegion({\n componentId: state.currentDropTarget.componentId ?? '',\n contentLinkUuid: state.sourceContentLinkUuid,\n sourceRegionId: state.sourceRegionId ?? '',\n insertType: state.currentDropTarget.insertType?.type,\n insertComponentId: state.currentDropTarget.insertContentLinkUuid,\n beforeComponentId: state.currentDropTarget.beforeContentLinkUuid,\n afterComponentId: state.currentDropTarget.afterContentLinkUuid,\n targetRegionId: state.currentDropTarget.regionId,\n targetComponentId: state.currentDropTarget.parentId ?? '',\n });\n }\n // If we don't have a source content link uuid, then we are adding a new component to a region.\n } else if (state.componentType || state.fragmentId) {\n clientApi?.addComponentToRegion({\n insertType: state.currentDropTarget.insertType?.type,\n insertComponentId: state.currentDropTarget.insertContentLinkUuid,\n beforeComponentId: state.currentDropTarget.beforeContentLinkUuid,\n componentProperties: {},\n componentType: state.fragmentId ? '' : (state.componentType ?? ''),\n fragmentId: state.fragmentId,\n targetComponentId: state.currentDropTarget.parentId ?? '',\n afterComponentId: state.currentDropTarget.afterContentLinkUuid,\n targetRegionId: state.currentDropTarget.regionId,\n });\n }\n }\n\n scrollFactorRef.current = 0;\n\n setState((prevState) => ({\n ...prevState,\n x: 0,\n y: 0,\n componentType: '',\n scrollDirection: 0,\n sourceContentLinkUuid: undefined,\n sourceRegionId: undefined,\n pendingDragContentLinkUuid: null,\n currentDropTarget: null,\n pendingTargetCommit: false,\n }));\n },\n }),\n });\n\n // Commits the current drop target if we are pending a target commit.\n useEffect(() => {\n if (dragState.pendingTargetCommit) {\n commitCurrentDropTarget();\n }\n }, [dragState.pendingTargetCommit, commitCurrentDropTarget]);\n\n // Starts scrolling the window when the drag state scroll factor is not 0.\n useEffect(() => {\n if (dragState.scrollDirection !== 0) {\n const interval = setInterval(() => {\n window.scrollBy(0, scrollFactorRef.current * SCROLL_BASE_AMOUNT_IN_PIXELS);\n }, SCROLL_INTERVAL_IN_MS);\n\n return () => clearInterval(interval);\n }\n\n return () => {\n // noop\n };\n }, [dragState.scrollDirection, scrollFactorRef]);\n\n return {\n dragState,\n setPendingDragContentLinkUuid,\n commitCurrentDropTarget,\n startComponentMove,\n updateComponentMove,\n dropComponent,\n cancelDrag,\n };\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useInteraction } from './useInteraction';\n\nexport interface ComponentUpdate {\n name?: string;\n [key: string]: unknown;\n}\n\nexport interface ComponentUpdateInteraction {\n componentUpdates: Record<string, ComponentUpdate>;\n}\n\n/**\n * Custom hook that manages component update state and handles\n * client-host communication for component update events.\n *\n * Listens for ComponentUpdated events from the host and maintains\n * a map of component IDs to their updated data.\n *\n * @returns Component update state\n */\nexport function useComponentUpdateInteraction(): ComponentUpdateInteraction {\n const { state: componentUpdates } = useInteraction<Record<string, ComponentUpdate>, Record<string, never>>({\n initialState: {},\n eventHandlers: {\n // Handle initial component names from page-init\n ClientAcknowledged: {\n handler: (event, setState) => {\n const initialUpdates: Record<string, ComponentUpdate> = {};\n Object.entries(event.components).forEach(([id, componentInfo]) => {\n if (componentInfo.name) {\n initialUpdates[id] = { name: componentInfo.name };\n }\n });\n // Merge to preserve ComponentUpdated changes\n if (Object.keys(initialUpdates).length > 0) {\n setState((prev) => ({ ...prev, ...initialUpdates }));\n }\n },\n },\n // Handle runtime component updates\n ComponentUpdated: {\n handler: (event, setState) => {\n setState((prev) => {\n const componentId = event.componentId;\n const existing = prev[componentId] || {};\n\n // Update the specific field based on changeType\n const updated = { ...existing };\n if (event.changeType === 'name') {\n updated.name = event.newValue as string;\n } else if (event.changeType === 'visibility') {\n updated.visibility = event.newValue;\n }\n\n return {\n ...prev,\n [componentId]: updated,\n };\n });\n },\n },\n },\n });\n\n return {\n componentUpdates,\n };\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React from 'react';\nimport { useSelectInteraction } from '../hooks/useSelectInteraction';\nimport { useHoverInteraction } from '../hooks/useHoverInteraction';\nimport { useDeleteInteraction } from '../hooks/useDeleteInteraction';\nimport { useFocusInteraction } from '../hooks/useFocusInteraction';\nimport { useScrollInteraction, type ScrollInteraction } from '../hooks/useScrollInteraction';\nimport { useDragInteraction, type DragInteraction } from '../hooks/useDragInteraction';\nimport { useComponentUpdateInteraction, type ComponentUpdateInteraction } from '../hooks/useComponentUpdateInteraction';\nimport type { ComponentDeletedEvent, EventPayload } from '../../messaging-api';\n\nexport interface NodeToTargetMapEntry {\n type: 'region' | 'component';\n parentId?: string;\n componentId: string;\n contentLinkUuid?: string;\n regionId: string;\n contentLinkUuids: string[];\n componentTypeInclusions?: string[];\n componentTypeExclusions?: string[];\n}\n\nexport interface DesignState extends DragInteraction, ScrollInteraction, ComponentUpdateInteraction {\n selectedContentLinkUuid: string | null;\n hoveredContentLinkUuid: string | null;\n setSelectedComponent: (contentLinkUuid: string) => void;\n setHoveredComponent: (componentUuid: string | null) => void;\n deleteComponent: (event: EventPayload<ComponentDeletedEvent>) => void;\n focusComponent: (node: Element) => void;\n focusedContentLinkUuid: string | null;\n nodeToTargetMap: WeakMap<Element, NodeToTargetMapEntry>;\n /**\n * Maps contentLinkUuid to componentId for all mounted DesignComponent instances.\n * Should only be used in interactions and not functional components as it is populated after components are rendered.\n */\n contentLinkMap: Record<string, string>;\n registerContentLink: (contentLinkUuid: string, componentId: string) => void;\n}\n\n// eslint-disable-next-line react-refresh/only-export-components\nexport const DesignStateContext = React.createContext<DesignState>(null as unknown as DesignState);\n\nexport const DesignStateProvider = ({ children }: { children: React.ReactNode }): React.JSX.Element => {\n const [contentLinkMap, setContentLinkMap] = React.useState<Record<string, string>>({});\n\n const registerContentLink = React.useCallback((contentLinkUuid: string, componentId: string) => {\n setContentLinkMap((prev) => {\n if (prev[contentLinkUuid] === componentId) return prev;\n return { ...prev, [contentLinkUuid]: componentId };\n });\n }, []);\n\n const selectInteraction = useSelectInteraction({ contentLinkMap });\n const hoverInteraction = useHoverInteraction({ contentLinkMap });\n const deleteInteraction = useDeleteInteraction({\n selectedContentLinkUuid: selectInteraction.selectedContentLinkUuid,\n setSelectedComponent: selectInteraction.setSelectedComponent,\n });\n const focusInteraction = useFocusInteraction({\n setSelectedComponent: selectInteraction.setSelectedComponent,\n });\n const scrollInteraction = useScrollInteraction();\n const componentUpdateInteraction = useComponentUpdateInteraction();\n const nodeToTargetMap = React.useMemo(() => new WeakMap(), []);\n const dragInteraction = useDragInteraction({ nodeToTargetMap });\n\n const state = React.useMemo(\n () => ({\n ...deleteInteraction,\n ...selectInteraction,\n ...hoverInteraction,\n ...focusInteraction,\n ...dragInteraction,\n ...scrollInteraction,\n ...componentUpdateInteraction,\n nodeToTargetMap,\n contentLinkMap,\n registerContentLink,\n }),\n [\n deleteInteraction,\n selectInteraction,\n hoverInteraction,\n focusInteraction,\n dragInteraction,\n nodeToTargetMap,\n scrollInteraction,\n componentUpdateInteraction,\n contentLinkMap,\n registerContentLink,\n ]\n );\n\n return <DesignStateContext.Provider value={state}>{children}</DesignStateContext.Provider>;\n};\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React from 'react';\nimport { DesignStateContext, type DesignState } from '../context/DesignStateContext';\n\n/**\n * Custom hook that manages design-time component state by composing\n * individual interaction hooks for better maintainability and testability.\n *\n * @returns Combined design state from all interactions\n */\nexport const useDesignState = (): DesignState => {\n const context = React.useContext(DesignStateContext);\n\n if (!context) {\n throw new Error('useDesignState must be used within a DesignStateProvider');\n }\n\n return context;\n};\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useRef, useCallback } from 'react';\n\nexport function useThrottledCallback<TArgs extends unknown[], TReturn>(\n callback: (...args: TArgs) => TReturn,\n interval: number,\n deps: unknown[] = []\n): (...args: TArgs) => TReturn | void {\n const lastCallTime = useRef<number>(0);\n\n return useCallback(\n (...args: TArgs): TReturn | void => {\n const now = Date.now();\n\n if (now >= lastCallTime.current + interval) {\n lastCallTime.current = now;\n\n callback(...args);\n }\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [callback, interval, ...deps]\n );\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useRef, useCallback } from 'react';\n\nexport function useDebouncedCallback<TArgs extends unknown[], TReturn>(\n callback: (...args: TArgs) => TReturn,\n interval: number,\n deps: unknown[] = []\n): (...args: TArgs) => TReturn | void {\n const timeoutRef = useRef<number | null>(null);\n\n return useCallback(\n (...args: TArgs): TReturn | void => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n\n timeoutRef.current = setTimeout(() => {\n callback(...args);\n timeoutRef.current = null;\n }, interval) as unknown as number;\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [callback, interval, ...deps]\n );\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useEffect } from 'react';\nimport { useDesignState } from './useDesignState';\nimport { useThrottledCallback } from './useThrottledCallback';\nimport { useDebouncedCallback } from './useDebouncedCallback';\n\nconst FPS_60 = 1000 / 60;\n\nexport function useGlobalListeners(): void {\n const { dropComponent, updateComponentMove, cancelDrag, notifyWindowScrollChange } = useDesignState();\n const dragListener = useThrottledCallback(\n (event: DragEvent) => updateComponentMove({ x: event.clientX, y: event.clientY }),\n FPS_60,\n [updateComponentMove]\n );\n const scrollListener = useDebouncedCallback(() => notifyWindowScrollChange(window.scrollX, window.scrollY), 100, [\n notifyWindowScrollChange,\n ]);\n\n useEffect(() => {\n const dragEndListener = () => dropComponent();\n const mouseUpListener = () => cancelDrag();\n\n window.addEventListener('dragover', dragListener);\n window.addEventListener('dragend', dragEndListener);\n window.addEventListener('scroll', scrollListener);\n // We need to make sure we cancel dragging on mouseup since we\n // we are using mousedown to start dragging or else it would stay in a dragging\n // state from regular click events.\n window.addEventListener('mouseup', mouseUpListener);\n\n return () => {\n window.removeEventListener('dragover', dragListener);\n window.removeEventListener('dragend', dragEndListener);\n window.removeEventListener('mouseup', mouseUpListener);\n window.removeEventListener('scroll', scrollListener);\n };\n }, [dropComponent, cancelDrag, dragListener, scrollListener]);\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useEffect } from 'react';\n\n/**\n * React hook that prevents all <a> (anchor) navigation by default in the document,\n * unless the anchor has the attribute `data-pd-allow-link`.\n */\nexport function useGlobalAnchorBlock(): void {\n useEffect(() => {\n function preventAnchorClicks(event: MouseEvent) {\n const target = event.target as HTMLElement;\n const anchor = target.closest('a');\n\n // This data attribute acts as a workaround in the event we do\n // want to have an anchor tag navigate in design mode.\n if (anchor && !anchor.hasAttribute('data-pd-allow-link')) {\n event.preventDefault();\n }\n }\n\n document.addEventListener('click', preventAnchorClicks);\n\n return () => document.removeEventListener('click', preventAnchorClicks);\n }, []);\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useGlobalListeners } from '../hooks/useGlobalListeners';\nimport { useGlobalAnchorBlock } from '../hooks/useGlobalAnchorBlock';\n\n/**\n * Containes any global setup logic for the design layer.\n */\nexport const DesignApp = ({ children }: React.PropsWithChildren<unknown>): React.JSX.Element => {\n useGlobalListeners();\n useGlobalAnchorBlock();\n\n return <>{children}</>;\n};\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React from 'react';\nimport {\n createClientApi,\n type ClientApi,\n type IsomorphicConfiguration,\n type ClientAcknowledgedEvent,\n type EventPayload,\n type HostToClientConfiguration,\n} from '../../messaging-api';\nimport type { ShopperExperience } from '@/scapi-client/types';\nimport { DesignStateProvider } from './DesignStateContext';\nimport { DesignApp } from '../components/DesignApp';\nimport { usePageDesignerMode } from '../core/PageDesignerProvider';\n\nconst noop = () => {\n /* noop */\n};\n\n/**\n * Type definition for the Design Context\n * Extends DesignState with additional design-time properties\n */\nexport interface DesignContextType {\n /** Whether design mode is currently active */\n isDesignMode: boolean;\n /** Client API for host communication */\n clientApi?: ClientApi;\n /** Whether the client is connected to the host */\n isConnected: boolean;\n /** The page designer config */\n pageDesignerConfig: EventPayload<ClientAcknowledgedEvent> | null;\n /** Page data that the client has retrieved */\n clientPage: ShopperExperience.schemas['Page'] | null;\n /** Sets the client page data */\n setClientPage: (page: ShopperExperience.schemas['Page']) => void;\n}\n\n// eslint-disable-next-line react-refresh/only-export-components\nexport const DesignContext = React.createContext<DesignContextType>({\n isDesignMode: false,\n isConnected: false,\n pageDesignerConfig: null,\n clientPage: null,\n setClientPage: noop,\n});\n\n/**\n * Provider component that enables design-time functionality for child components.\n * Sets up client-host communication and manages component selection state.\n *\n * @param children - Child components to wrap with design functionality\n * @param targetOrigin - Target origin for postMessage communication\n * @param clientId - Id for the client API\n * @returns JSX element wrapping children with design context\n */\nexport const DesignProvider = ({\n children,\n targetOrigin,\n clientId,\n usid,\n clientConnectionTimeout,\n clientConnectionInterval,\n clientLogger = noop,\n}: React.PropsWithChildren<{\n targetOrigin: string;\n clientId: string;\n usid?: string;\n clientConnectionTimeout?: number;\n clientConnectionInterval?: number;\n clientLogger?: IsomorphicConfiguration['logger'];\n}>): React.JSX.Element => {\n const { isDesignMode } = usePageDesignerMode();\n const [isConnected, setIsConnected] = React.useState(false);\n const [pageDesignerConfig, setPageDesignerConfig] = React.useState<HostToClientConfiguration | null>(null);\n const [clientPage, setClientPage] = React.useState<ShopperExperience.schemas['Page'] | null>(null);\n const clientPageRef = React.useRef<ShopperExperience.schemas['Page'] | null>(null);\n\n const clientApi = React.useMemo(\n () =>\n createClientApi({\n logger: clientLogger,\n emitter: {\n postMessage: (message) => window.parent.postMessage(message, targetOrigin),\n addEventListener: (handler) => {\n const listener = (event: MessageEvent) => handler(event.data);\n\n window.addEventListener('message', listener);\n\n return () => window.removeEventListener('message', listener);\n },\n },\n id: clientId,\n }),\n [targetOrigin, clientId, clientLogger]\n );\n\n React.useEffect(() => {\n // This will poll the host for a connection until the client is acknowledged.\n clientApi.connect({\n timeout: clientConnectionTimeout,\n interval: clientConnectionInterval,\n onHostConnected: (event) => {\n setPageDesignerConfig(event);\n setIsConnected(true);\n },\n onHostDisconnected: (reconnect) => {\n setPageDesignerConfig(null);\n setIsConnected(false);\n reconnect();\n },\n onError: () => {\n // TODO: Figure out how to handle this.\n },\n usid,\n });\n\n return () => {\n clientApi.disconnect();\n setPageDesignerConfig(null);\n setIsConnected(false);\n };\n }, [clientApi, clientConnectionTimeout, clientConnectionInterval, usid]);\n\n // Use the extracted state management hook\n const contextValue = React.useMemo<DesignContextType>(\n () => ({\n isDesignMode,\n clientApi,\n isConnected,\n pageDesignerConfig,\n clientPage,\n setClientPage: (page: ShopperExperience.schemas['Page']) => {\n if (page !== clientPageRef.current) {\n clientPageRef.current = page;\n setClientPage(page);\n clientApi?.notifyClientPageChanged({ page });\n }\n },\n }),\n [isDesignMode, clientApi, isConnected, pageDesignerConfig, clientPage, setClientPage]\n );\n\n return (\n <DesignContext.Provider value={contextValue}>\n <DesignStateProvider>\n <DesignApp>{children}</DesignApp>\n </DesignStateProvider>\n </DesignContext.Provider>\n );\n};\n\nDesignProvider.defaultProps = {\n clientLogger: noop,\n clientConnectionTimeout: 60_000,\n clientConnectionInterval: 1_000,\n};\n\n/**\n * Custom hook to access the design context\n * Provides access to design mode state and component selection functionality\n *\n * @returns The current design context\n */\n// eslint-disable-next-line react-refresh/only-export-components\nexport const useDesignContext = (): DesignContextType => React.useContext(DesignContext);\n"],"mappings":"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"}
|
package/dist/DesignFrame.js
CHANGED
|
@@ -3,15 +3,16 @@ import React from "react";
|
|
|
3
3
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
4
|
|
|
5
5
|
//#region src/design/react/hooks/useNodeToTargetStore.ts
|
|
6
|
-
function useNodeToTargetStore({ parentId, componentId, regionId, nodeRef, type,
|
|
6
|
+
function useNodeToTargetStore({ parentId, componentId, contentLinkUuid, regionId, nodeRef, type, contentLinkUuids, componentTypeInclusions, componentTypeExclusions }) {
|
|
7
7
|
const { nodeToTargetMap } = useDesignState();
|
|
8
8
|
React.useEffect(() => {
|
|
9
9
|
if (nodeRef.current) nodeToTargetMap.set(nodeRef.current, {
|
|
10
10
|
parentId,
|
|
11
11
|
componentId,
|
|
12
|
+
contentLinkUuid,
|
|
12
13
|
regionId,
|
|
13
14
|
type,
|
|
14
|
-
|
|
15
|
+
contentLinkUuids,
|
|
15
16
|
componentTypeInclusions,
|
|
16
17
|
componentTypeExclusions
|
|
17
18
|
});
|
|
@@ -19,9 +20,10 @@ function useNodeToTargetStore({ parentId, componentId, regionId, nodeRef, type,
|
|
|
19
20
|
nodeRef,
|
|
20
21
|
parentId,
|
|
21
22
|
componentId,
|
|
23
|
+
contentLinkUuid,
|
|
22
24
|
regionId,
|
|
23
25
|
type,
|
|
24
|
-
|
|
26
|
+
contentLinkUuids,
|
|
25
27
|
nodeToTargetMap,
|
|
26
28
|
componentTypeInclusions,
|
|
27
29
|
componentTypeExclusions
|
|
@@ -125,7 +127,7 @@ const DesignOverlay = () => {
|
|
|
125
127
|
|
|
126
128
|
//#endregion
|
|
127
129
|
//#region src/design/react/components/DesignFrame.tsx
|
|
128
|
-
const DesignFrame = ({ componentId, children, name, parentId, regionId, localized = false, showFrame = false, showToolbox = true, isMoveable = true, className }) => {
|
|
130
|
+
const DesignFrame = ({ componentId, children, name, parentId, regionId, contentLinkUuid, localized = false, showFrame = false, showToolbox = true, isMoveable = true, className }) => {
|
|
129
131
|
const componentType = useComponentType(componentId ?? "");
|
|
130
132
|
const { deleteComponent } = useDesignState();
|
|
131
133
|
const labels = useLabels();
|
|
@@ -134,12 +136,14 @@ const DesignFrame = ({ componentId, children, name, parentId, regionId, localize
|
|
|
134
136
|
event.stopPropagation();
|
|
135
137
|
if (componentId) deleteComponent({
|
|
136
138
|
componentId,
|
|
139
|
+
contentLinkUuid: contentLinkUuid ?? "",
|
|
137
140
|
sourceComponentId: parentId ?? "",
|
|
138
141
|
sourceRegionId: regionId ?? ""
|
|
139
142
|
});
|
|
140
143
|
}, [
|
|
141
144
|
deleteComponent,
|
|
142
145
|
componentId,
|
|
146
|
+
contentLinkUuid,
|
|
143
147
|
parentId,
|
|
144
148
|
regionId
|
|
145
149
|
]);
|
package/dist/DesignFrame.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DesignFrame.js","names":[],"sources":["../src/design/react/hooks/useNodeToTargetStore.ts","../src/design/react/hooks/useComponentType.ts","../src/design/react/components/DeleteToolboxButton.tsx","../src/design/react/components/MoveToolboxButton.tsx","../src/design/react/hooks/useLabels.ts","../src/design/react/components/DesignOverlay.tsx","../src/design/react/components/DesignFrame.tsx"],"sourcesContent":["/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React from 'react';\nimport { useDesignState } from './useDesignState';\nimport type { NodeToTargetMapEntry } from '../context/DesignStateContext';\n\nexport function useNodeToTargetStore({\n parentId,\n componentId,\n regionId,\n nodeRef,\n type,\n componentIds,\n componentTypeInclusions,\n componentTypeExclusions,\n}: Partial<NodeToTargetMapEntry> & {\n nodeRef: React.RefObject<Element | null>;\n}): void {\n const { nodeToTargetMap } = useDesignState();\n\n React.useEffect(() => {\n if (nodeRef.current) {\n nodeToTargetMap.set(nodeRef.current, {\n parentId,\n componentId,\n regionId,\n type,\n componentIds,\n componentTypeInclusions,\n componentTypeExclusions,\n } as NodeToTargetMapEntry);\n }\n }, [\n nodeRef,\n parentId,\n componentId,\n regionId,\n type,\n componentIds,\n nodeToTargetMap,\n componentTypeInclusions,\n componentTypeExclusions,\n ]);\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useDesignContext } from '../context/DesignContext';\nimport type { ComponentType } from '../../messaging-api/domain-types';\n\nexport function useComponentType(componentId: string): ComponentType | null {\n const { pageDesignerConfig } = useDesignContext();\n const { type = '' } = pageDesignerConfig?.components[componentId] ?? {};\n\n return pageDesignerConfig?.componentTypes[type] ?? null;\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type React from 'react';\n\nexport const DeleteToolboxButton = ({\n title,\n onClick,\n onMouseDown = () => {\n /* noop */\n },\n}: {\n title: string;\n onClick: (event: React.MouseEvent<HTMLButtonElement>) => void;\n onMouseDown?: (event: React.MouseEvent<HTMLButtonElement>) => void;\n}): React.JSX.Element => (\n <button\n className=\"pd-design__frame__toolbox-button\"\n title={title}\n type=\"button\"\n onMouseDown={onMouseDown}\n onClick={onClick}>\n <svg\n className=\"pd-design__frame__delete-icon\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M18 6L6 18M6 6l12 12\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </button>\n);\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type React from 'react';\n\nexport const MoveToolboxButton = ({ title }: { title: string }): React.JSX.Element => (\n <button className=\"pd-design__frame__toolbox-button\" title={title} type=\"button\">\n <svg className=\"pd-design__frame__move-icon\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M22.9 11.7l-3.8-4.2c-.3-.3-.6 0-.6.4v2.7h-4.7c-.2 0-.4-.2-.4-.4V5.5h2.7c.5 0 .7-.4.4-.6l-4.1-3.8c-.2-.2-.5-.2-.7 0L7.6 4.9c-.3.3-.1.6.4.6h2.6v4.7c0 .2-.2.4-.4.4H5.5V7.9c0-.5-.4-.7-.6-.4l-3.8 4.1c-.2.2-.2.5 0 .7l3.8 4.1c.3.3.6.1.6-.4v-2.6h4.7c.2 0 .4.2.4.4v4.7H7.9c-.5 0-.7.4-.4.6l4.1 3.8c.2.2.5.2.7 0l4.1-3.8c.3-.3.1-.6-.4-.6h-2.6v-4.7c0-.2.2-.4.4-.4h4.7v2.7c0 .5.4.7.6.4l3.8-4.1c.2-.3.2-.5 0-.7z\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </button>\n);\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useDesignContext } from '../context/DesignContext';\n\nexport function useLabels(): Record<string, string> {\n const { pageDesignerConfig } = useDesignContext();\n\n return pageDesignerConfig?.labels ?? {};\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nexport const DesignOverlay = () => {\n return (\n <div className=\"pd-design__frame__overlay\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n x=\"0px\"\n y=\"0px\"\n width=\"52px\"\n height=\"52px\"\n viewBox=\"0 0 52 52\"\n enableBackground=\"new 0 0 52 52\"\n xmlSpace=\"preserve\">\n <path\n fill=\"#FFFFFF\"\n d=\"M26,2C12.7,2,2,12.7,2,26s10.7,24,24,24s24-10.7,24-24S39.3,2,26,2z M26,7C26,7,26,7,26,7C26,7,26,7,26,7\n\tC26,7,26,7,26,7z M28,7.1c-0.1,0-0.1,0-0.2,0C27.9,7.1,28,7.1,28,7.1z M26,45C15.5,45,7,36.5,7,26c0-1,0.1-2.1,0.3-3\n\tc1.3,0.2,2.9,0.7,3.7,1.5c1.7,1.8,3.6,3.9,5.4,4.3c0,0-0.2,0.1-0.4,0.4c-0.2,0.3-0.4,0.9-0.4,1.9c0,4.7,4.4,1.9,4.4,6.6\n\tc0,4.7,5.3,6.6,5.3,2.8s3.5-5.6,3.5-8.5s-2.7-2.8-4.4-3.8c-1.8-0.9-2.7-2.4-6.1-1.9c-1.8-1.7-2.8-3.1-2-4.7c0.9-1.7,4.6-2,4.6-4.6\n\ts-2.5-3.1-4.3-3.1c-0.8,0-2.5-0.6-3.9-1.3c1.7-1.7,3.8-3.1,6-4.1c1.6,0.7,4.3,1.8,6.6,1.8c2.7,0,4.1-1.9,3.7-3.1\n\tc4.5,0.7,8.5,3,11.4,6.2c-1.5,0.9-3.5,1.9-7,1.9c-4.6,0-4.6,4.7-1.9,5.6c2.8,0.9,5.6-1.8,6.5,0c0.9,1.8-6.5,1.8-4.6,6.4\n\tc1.9,4.6,3.7-0.1,5.6,4.5c1.9,4.6,5.6-0.7,2.8-4.3c-1.2-1.6-0.9-6.5,1.9-6.5h0.9c0.4,1.6,0.7,3.3,0.7,5C45,36.5,36.5,45,26,45z\"\n />\n </svg>\n </div>\n );\n};\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React from 'react';\nimport { useComponentType } from '../hooks/useComponentType';\nimport { DeleteToolboxButton } from './DeleteToolboxButton';\nimport { MoveToolboxButton } from './MoveToolboxButton';\nimport { useDesignState } from '../hooks/useDesignState';\nimport { useLabels } from '../hooks/useLabels';\nimport { DesignOverlay } from './DesignOverlay';\n\nexport const DesignFrame = ({\n componentId,\n children,\n name,\n parentId,\n regionId,\n localized = false,\n showFrame = false,\n showToolbox = true,\n isMoveable = true,\n className,\n}: React.PropsWithChildren<{\n componentId?: string;\n name: string;\n localized?: boolean;\n parentId?: string;\n regionId?: string;\n showToolbox?: boolean;\n showFrame?: boolean;\n isMoveable?: boolean;\n className?: string;\n}>): React.JSX.Element => {\n const componentType = useComponentType(componentId ?? '');\n const { deleteComponent } = useDesignState();\n const labels = useLabels();\n const nodeRef = React.useRef<HTMLDivElement>(null);\n\n const handleDelete = React.useCallback(\n (event: React.MouseEvent) => {\n // Stop propagation so we don't select the component as well when\n // this bubbles up.\n event.stopPropagation();\n\n if (componentId) {\n deleteComponent({\n componentId,\n sourceComponentId: parentId ?? '',\n sourceRegionId: regionId ?? '',\n });\n }\n },\n [deleteComponent, componentId, parentId, regionId]\n );\n\n const stopPropagation = (event: React.MouseEvent) => event.stopPropagation();\n\n const classes = ['pd-design__frame', showFrame && 'pd-design__frame--visible', className].filter(Boolean).join(' ');\n\n // TODO: For the frame label, when there is not enough space above the component to display it, we\n // need to display it inside the container instead.\n return (\n <div className={classes} ref={nodeRef}>\n {showFrame && (\n <>\n <div className=\"pd-design__frame--x\" />\n <div className=\"pd-design__frame--y\" />\n </>\n )}\n <div className=\"pd-design__frame__label\" onMouseDown={stopPropagation}>\n {componentType?.image && (\n <span className=\"pd-design__icon\">\n <img src={componentType.image} alt=\"\" />\n </span>\n )}\n <span className=\"pd-design__frame__name\">{name}</span>\n {!localized && (\n <span className=\"pd-design__frame__fallback-badge\">{labels.fallback ?? 'Fallback'}</span>\n )}\n </div>\n {showToolbox && (\n <div className=\"pd-design__frame__toolbox\">\n {isMoveable && <MoveToolboxButton title={labels.moveComponent ?? 'Move component'} />}\n <DeleteToolboxButton\n title={labels.deleteComponent ?? 'Delete component'}\n onMouseDown={stopPropagation}\n onClick={handleDelete}\n />\n </div>\n )}\n <DesignOverlay />\n {children}\n </div>\n );\n};\n\nDesignFrame.defaultProps = {\n parentId: undefined,\n componentId: undefined,\n showToolbox: true,\n regionId: undefined,\n showFrame: false,\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DesignFrame.js","names":[],"sources":["../src/design/react/hooks/useNodeToTargetStore.ts","../src/design/react/hooks/useComponentType.ts","../src/design/react/components/DeleteToolboxButton.tsx","../src/design/react/components/MoveToolboxButton.tsx","../src/design/react/hooks/useLabels.ts","../src/design/react/components/DesignOverlay.tsx","../src/design/react/components/DesignFrame.tsx"],"sourcesContent":["/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React from 'react';\nimport { useDesignState } from './useDesignState';\nimport type { NodeToTargetMapEntry } from '../context/DesignStateContext';\n\nexport function useNodeToTargetStore({\n parentId,\n componentId,\n contentLinkUuid,\n regionId,\n nodeRef,\n type,\n contentLinkUuids,\n componentTypeInclusions,\n componentTypeExclusions,\n}: Partial<NodeToTargetMapEntry> & {\n nodeRef: React.RefObject<Element | null>;\n}): void {\n const { nodeToTargetMap } = useDesignState();\n\n React.useEffect(() => {\n if (nodeRef.current) {\n nodeToTargetMap.set(nodeRef.current, {\n parentId,\n componentId,\n contentLinkUuid,\n regionId,\n type,\n contentLinkUuids,\n componentTypeInclusions,\n componentTypeExclusions,\n } as NodeToTargetMapEntry);\n }\n }, [\n nodeRef,\n parentId,\n componentId,\n contentLinkUuid,\n regionId,\n type,\n contentLinkUuids,\n nodeToTargetMap,\n componentTypeInclusions,\n componentTypeExclusions,\n ]);\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useDesignContext } from '../context/DesignContext';\nimport type { ComponentType } from '../../messaging-api/domain-types';\n\nexport function useComponentType(componentId: string): ComponentType | null {\n const { pageDesignerConfig } = useDesignContext();\n const { type = '' } = pageDesignerConfig?.components[componentId] ?? {};\n\n return pageDesignerConfig?.componentTypes[type] ?? null;\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type React from 'react';\n\nexport const DeleteToolboxButton = ({\n title,\n onClick,\n onMouseDown = () => {\n /* noop */\n },\n}: {\n title: string;\n onClick: (event: React.MouseEvent<HTMLButtonElement>) => void;\n onMouseDown?: (event: React.MouseEvent<HTMLButtonElement>) => void;\n}): React.JSX.Element => (\n <button\n className=\"pd-design__frame__toolbox-button\"\n title={title}\n type=\"button\"\n onMouseDown={onMouseDown}\n onClick={onClick}>\n <svg\n className=\"pd-design__frame__delete-icon\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M18 6L6 18M6 6l12 12\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </button>\n);\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type React from 'react';\n\nexport const MoveToolboxButton = ({ title }: { title: string }): React.JSX.Element => (\n <button className=\"pd-design__frame__toolbox-button\" title={title} type=\"button\">\n <svg className=\"pd-design__frame__move-icon\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M22.9 11.7l-3.8-4.2c-.3-.3-.6 0-.6.4v2.7h-4.7c-.2 0-.4-.2-.4-.4V5.5h2.7c.5 0 .7-.4.4-.6l-4.1-3.8c-.2-.2-.5-.2-.7 0L7.6 4.9c-.3.3-.1.6.4.6h2.6v4.7c0 .2-.2.4-.4.4H5.5V7.9c0-.5-.4-.7-.6-.4l-3.8 4.1c-.2.2-.2.5 0 .7l3.8 4.1c.3.3.6.1.6-.4v-2.6h4.7c.2 0 .4.2.4.4v4.7H7.9c-.5 0-.7.4-.4.6l4.1 3.8c.2.2.5.2.7 0l4.1-3.8c.3-.3.1-.6-.4-.6h-2.6v-4.7c0-.2.2-.4.4-.4h4.7v2.7c0 .5.4.7.6.4l3.8-4.1c.2-.3.2-.5 0-.7z\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </button>\n);\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useDesignContext } from '../context/DesignContext';\n\nexport function useLabels(): Record<string, string> {\n const { pageDesignerConfig } = useDesignContext();\n\n return pageDesignerConfig?.labels ?? {};\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nexport const DesignOverlay = () => {\n return (\n <div className=\"pd-design__frame__overlay\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n x=\"0px\"\n y=\"0px\"\n width=\"52px\"\n height=\"52px\"\n viewBox=\"0 0 52 52\"\n enableBackground=\"new 0 0 52 52\"\n xmlSpace=\"preserve\">\n <path\n fill=\"#FFFFFF\"\n d=\"M26,2C12.7,2,2,12.7,2,26s10.7,24,24,24s24-10.7,24-24S39.3,2,26,2z M26,7C26,7,26,7,26,7C26,7,26,7,26,7\n\tC26,7,26,7,26,7z M28,7.1c-0.1,0-0.1,0-0.2,0C27.9,7.1,28,7.1,28,7.1z M26,45C15.5,45,7,36.5,7,26c0-1,0.1-2.1,0.3-3\n\tc1.3,0.2,2.9,0.7,3.7,1.5c1.7,1.8,3.6,3.9,5.4,4.3c0,0-0.2,0.1-0.4,0.4c-0.2,0.3-0.4,0.9-0.4,1.9c0,4.7,4.4,1.9,4.4,6.6\n\tc0,4.7,5.3,6.6,5.3,2.8s3.5-5.6,3.5-8.5s-2.7-2.8-4.4-3.8c-1.8-0.9-2.7-2.4-6.1-1.9c-1.8-1.7-2.8-3.1-2-4.7c0.9-1.7,4.6-2,4.6-4.6\n\ts-2.5-3.1-4.3-3.1c-0.8,0-2.5-0.6-3.9-1.3c1.7-1.7,3.8-3.1,6-4.1c1.6,0.7,4.3,1.8,6.6,1.8c2.7,0,4.1-1.9,3.7-3.1\n\tc4.5,0.7,8.5,3,11.4,6.2c-1.5,0.9-3.5,1.9-7,1.9c-4.6,0-4.6,4.7-1.9,5.6c2.8,0.9,5.6-1.8,6.5,0c0.9,1.8-6.5,1.8-4.6,6.4\n\tc1.9,4.6,3.7-0.1,5.6,4.5c1.9,4.6,5.6-0.7,2.8-4.3c-1.2-1.6-0.9-6.5,1.9-6.5h0.9c0.4,1.6,0.7,3.3,0.7,5C45,36.5,36.5,45,26,45z\"\n />\n </svg>\n </div>\n );\n};\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React from 'react';\nimport { useComponentType } from '../hooks/useComponentType';\nimport { DeleteToolboxButton } from './DeleteToolboxButton';\nimport { MoveToolboxButton } from './MoveToolboxButton';\nimport { useDesignState } from '../hooks/useDesignState';\nimport { useLabels } from '../hooks/useLabels';\nimport { DesignOverlay } from './DesignOverlay';\n\nexport const DesignFrame = ({\n componentId,\n children,\n name,\n parentId,\n regionId,\n contentLinkUuid,\n localized = false,\n showFrame = false,\n showToolbox = true,\n isMoveable = true,\n className,\n}: React.PropsWithChildren<{\n componentId?: string;\n name: string;\n localized?: boolean;\n parentId?: string;\n regionId?: string;\n contentLinkUuid?: string;\n showToolbox?: boolean;\n showFrame?: boolean;\n isMoveable?: boolean;\n className?: string;\n}>): React.JSX.Element => {\n const componentType = useComponentType(componentId ?? '');\n const { deleteComponent } = useDesignState();\n const labels = useLabels();\n const nodeRef = React.useRef<HTMLDivElement>(null);\n\n const handleDelete = React.useCallback(\n (event: React.MouseEvent) => {\n // Stop propagation so we don't select the component as well when\n // this bubbles up.\n event.stopPropagation();\n\n if (componentId) {\n deleteComponent({\n componentId,\n contentLinkUuid: contentLinkUuid ?? '',\n sourceComponentId: parentId ?? '',\n sourceRegionId: regionId ?? '',\n });\n }\n },\n [deleteComponent, componentId, contentLinkUuid, parentId, regionId]\n );\n\n const stopPropagation = (event: React.MouseEvent) => event.stopPropagation();\n\n const classes = ['pd-design__frame', showFrame && 'pd-design__frame--visible', className].filter(Boolean).join(' ');\n\n // TODO: For the frame label, when there is not enough space above the component to display it, we\n // need to display it inside the container instead.\n return (\n <div className={classes} ref={nodeRef}>\n {showFrame && (\n <>\n <div className=\"pd-design__frame--x\" />\n <div className=\"pd-design__frame--y\" />\n </>\n )}\n <div className=\"pd-design__frame__label\" onMouseDown={stopPropagation}>\n {componentType?.image && (\n <span className=\"pd-design__icon\">\n <img src={componentType.image} alt=\"\" />\n </span>\n )}\n <span className=\"pd-design__frame__name\">{name}</span>\n {!localized && (\n <span className=\"pd-design__frame__fallback-badge\">{labels.fallback ?? 'Fallback'}</span>\n )}\n </div>\n {showToolbox && (\n <div className=\"pd-design__frame__toolbox\">\n {isMoveable && <MoveToolboxButton title={labels.moveComponent ?? 'Move component'} />}\n <DeleteToolboxButton\n title={labels.deleteComponent ?? 'Delete component'}\n onMouseDown={stopPropagation}\n onClick={handleDelete}\n />\n </div>\n )}\n <DesignOverlay />\n {children}\n </div>\n );\n};\n\nDesignFrame.defaultProps = {\n parentId: undefined,\n componentId: undefined,\n showToolbox: true,\n regionId: undefined,\n showFrame: false,\n};\n"],"mappings":"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"}
|
package/dist/DesignRegion.js
CHANGED
|
@@ -27,7 +27,7 @@ function useRegionDecoratorClasses({ regionId, componentTypeInclusions, componen
|
|
|
27
27
|
//#region src/design/react/components/DesignRegion.tsx
|
|
28
28
|
function DesignRegion(props) {
|
|
29
29
|
const { designMetadata, children, className } = props;
|
|
30
|
-
const { name, id = "",
|
|
30
|
+
const { name, id = "", contentLinkUuids = [], componentTypeInclusions = [], componentTypeExclusions = [] } = designMetadata ?? {};
|
|
31
31
|
const nodeRef = React.useRef(null);
|
|
32
32
|
const classes = useRegionDecoratorClasses({
|
|
33
33
|
regionId: id,
|
|
@@ -37,21 +37,20 @@ function DesignRegion(props) {
|
|
|
37
37
|
const { dragState } = useDesignState();
|
|
38
38
|
const labels = useLabels();
|
|
39
39
|
const showFrame = Boolean(id && dragState.currentDropTarget?.regionId === id);
|
|
40
|
-
const {
|
|
40
|
+
const { contentLinkUuid: parentContentLinkUuid } = useComponentContext() ?? {};
|
|
41
41
|
useNodeToTargetStore({
|
|
42
42
|
type: "region",
|
|
43
43
|
nodeRef,
|
|
44
|
-
parentId:
|
|
45
|
-
|
|
46
|
-
componentId: parentComponentId ?? "",
|
|
44
|
+
parentId: parentContentLinkUuid,
|
|
45
|
+
contentLinkUuids,
|
|
47
46
|
regionId: id,
|
|
48
47
|
componentTypeInclusions,
|
|
49
48
|
componentTypeExclusions
|
|
50
49
|
});
|
|
51
50
|
const context = React.useMemo(() => ({
|
|
52
51
|
regionId: id,
|
|
53
|
-
|
|
54
|
-
}), [id,
|
|
52
|
+
contentLinkUuids
|
|
53
|
+
}), [id, contentLinkUuids]);
|
|
55
54
|
return /* @__PURE__ */ jsx("div", {
|
|
56
55
|
className: classes,
|
|
57
56
|
ref: nodeRef,
|
|
@@ -65,7 +64,6 @@ function DesignRegion(props) {
|
|
|
65
64
|
"data-region-id": id,
|
|
66
65
|
children: /* @__PURE__ */ jsx(DesignFrame, {
|
|
67
66
|
name: name ?? labels.defaultRegionName ?? "Region",
|
|
68
|
-
parentId: parentComponentId,
|
|
69
67
|
regionId: id,
|
|
70
68
|
localized: true,
|
|
71
69
|
showFrame,
|
package/dist/DesignRegion.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DesignRegion.js","names":[],"sources":["../src/design/react/hooks/useRegionDecoratorClasses.ts","../src/design/react/components/DesignRegion.tsx"],"sourcesContent":["/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useMemo } from 'react';\nimport { useDesignState } from './useDesignState';\nimport { isComponentTypeAllowedInRegion } from '../utils/regionUtils';\n\nexport function useRegionDecoratorClasses({\n regionId,\n componentTypeInclusions,\n componentTypeExclusions,\n}: {\n regionId: string;\n componentTypeInclusions: string[];\n componentTypeExclusions: string[];\n}): string {\n const {\n dragState: { currentDropTarget, componentType },\n } = useDesignState();\n\n const isHovered = regionId && currentDropTarget?.regionId === regionId;\n\n const isComponentAllowed = useMemo(\n () => isComponentTypeAllowedInRegion(componentType, componentTypeInclusions, componentTypeExclusions),\n [componentType, componentTypeInclusions, componentTypeExclusions]\n );\n\n // Only show hover state if the region is hovered and the component is allowed\n const shouldShowHover = isHovered && isComponentAllowed;\n\n return [\n 'pd-design__decorator',\n 'pd-design__region',\n shouldShowHover && 'pd-design__region--hovered pd-design__frame--visible',\n ]\n .filter(Boolean)\n .join(' ');\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React, { useCallback } from 'react';\nimport type { RegionDecoratorProps } from '../core/component.types';\nimport { useRegionDecoratorClasses } from '../hooks/useRegionDecoratorClasses';\nimport { useNodeToTargetStore } from '../hooks/useNodeToTargetStore';\nimport { DesignFrame } from './DesignFrame';\nimport { useLabels } from '../hooks/useLabels';\nimport { RegionContext, type RegionContextType } from '../core/RegionContext';\nimport { useComponentContext } from '../core/ComponentContext';\nimport { useDesignState } from '../hooks/useDesignState';\nimport { isComponentTypeAllowedInRegion } from '../utils/regionUtils';\n\nexport function DesignRegion(props: RegionDecoratorProps<unknown>): React.JSX.Element {\n const { designMetadata, children, className } = props;\n const {\n name,\n id = '',\n
|
|
1
|
+
{"version":3,"file":"DesignRegion.js","names":[],"sources":["../src/design/react/hooks/useRegionDecoratorClasses.ts","../src/design/react/components/DesignRegion.tsx"],"sourcesContent":["/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useMemo } from 'react';\nimport { useDesignState } from './useDesignState';\nimport { isComponentTypeAllowedInRegion } from '../utils/regionUtils';\n\nexport function useRegionDecoratorClasses({\n regionId,\n componentTypeInclusions,\n componentTypeExclusions,\n}: {\n regionId: string;\n componentTypeInclusions: string[];\n componentTypeExclusions: string[];\n}): string {\n const {\n dragState: { currentDropTarget, componentType },\n } = useDesignState();\n\n const isHovered = regionId && currentDropTarget?.regionId === regionId;\n\n const isComponentAllowed = useMemo(\n () => isComponentTypeAllowedInRegion(componentType, componentTypeInclusions, componentTypeExclusions),\n [componentType, componentTypeInclusions, componentTypeExclusions]\n );\n\n // Only show hover state if the region is hovered and the component is allowed\n const shouldShowHover = isHovered && isComponentAllowed;\n\n return [\n 'pd-design__decorator',\n 'pd-design__region',\n shouldShowHover && 'pd-design__region--hovered pd-design__frame--visible',\n ]\n .filter(Boolean)\n .join(' ');\n}\n","/**\n * Copyright 2026 Salesforce, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React, { useCallback } from 'react';\nimport type { RegionDecoratorProps } from '../core/component.types';\nimport { useRegionDecoratorClasses } from '../hooks/useRegionDecoratorClasses';\nimport { useNodeToTargetStore } from '../hooks/useNodeToTargetStore';\nimport { DesignFrame } from './DesignFrame';\nimport { useLabels } from '../hooks/useLabels';\nimport { RegionContext, type RegionContextType } from '../core/RegionContext';\nimport { useComponentContext } from '../core/ComponentContext';\nimport { useDesignState } from '../hooks/useDesignState';\nimport { isComponentTypeAllowedInRegion } from '../utils/regionUtils';\n\nexport function DesignRegion(props: RegionDecoratorProps<unknown>): React.JSX.Element {\n const { designMetadata, children, className } = props;\n const {\n name,\n id = '',\n contentLinkUuids = [],\n componentTypeInclusions = [],\n componentTypeExclusions = [],\n } = designMetadata ?? {};\n const nodeRef = React.useRef<HTMLDivElement>(null);\n const classes = useRegionDecoratorClasses({\n regionId: id,\n componentTypeInclusions,\n componentTypeExclusions,\n });\n const { dragState } = useDesignState();\n const labels = useLabels();\n const showFrame = Boolean(id && dragState.currentDropTarget?.regionId === id);\n const { contentLinkUuid: parentContentLinkUuid } = useComponentContext() ?? {};\n\n useNodeToTargetStore({\n type: 'region',\n nodeRef,\n parentId: parentContentLinkUuid,\n contentLinkUuids,\n regionId: id,\n componentTypeInclusions,\n componentTypeExclusions,\n });\n\n const context = React.useMemo<RegionContextType>(\n () => ({ regionId: id, contentLinkUuids }),\n [id, contentLinkUuids]\n );\n\n const handleDragOver = useCallback(\n (event: React.DragEvent<HTMLDivElement>) => {\n const isComponentAllowed = isComponentTypeAllowedInRegion(\n dragState.componentType,\n componentTypeInclusions,\n componentTypeExclusions\n );\n\n if (isComponentAllowed) {\n event.preventDefault();\n }\n },\n [dragState.componentType, componentTypeInclusions, componentTypeExclusions]\n );\n\n return (\n <div className={classes} ref={nodeRef} onDragOver={handleDragOver} data-region-id={id}>\n <DesignFrame\n name={name ?? labels.defaultRegionName ?? 'Region'}\n regionId={id}\n localized\n showFrame={showFrame}\n showToolbox={false}\n className={className}>\n <RegionContext.Provider value={context}>{children}</RegionContext.Provider>\n </DesignFrame>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;AAmBA,SAAgB,0BAA0B,EACtC,UACA,yBACA,2BAKO;CACP,MAAM,EACF,WAAW,EAAE,mBAAmB,oBAChC,gBAAgB;CAEpB,MAAM,YAAY,YAAY,mBAAmB,aAAa;CAE9D,MAAM,qBAAqB,cACjB,+BAA+B,eAAe,yBAAyB,wBAAwB,EACrG;EAAC;EAAe;EAAyB;EAAwB,CACpE;AAKD,QAAO;EACH;EACA;EAJoB,aAAa,sBAKd;EACtB,CACI,OAAO,QAAQ,CACf,KAAK,IAAI;;;;;ACtBlB,SAAgB,aAAa,OAAyD;CAClF,MAAM,EAAE,gBAAgB,UAAU,cAAc;CAChD,MAAM,EACF,MACA,KAAK,IACL,mBAAmB,EAAE,EACrB,0BAA0B,EAAE,EAC5B,0BAA0B,EAAE,KAC5B,kBAAkB,EAAE;CACxB,MAAM,UAAU,MAAM,OAAuB,KAAK;CAClD,MAAM,UAAU,0BAA0B;EACtC,UAAU;EACV;EACA;EACH,CAAC;CACF,MAAM,EAAE,cAAc,gBAAgB;CACtC,MAAM,SAAS,WAAW;CAC1B,MAAM,YAAY,QAAQ,MAAM,UAAU,mBAAmB,aAAa,GAAG;CAC7E,MAAM,EAAE,iBAAiB,0BAA0B,qBAAqB,IAAI,EAAE;AAE9E,sBAAqB;EACjB,MAAM;EACN;EACA,UAAU;EACV;EACA,UAAU;EACV;EACA;EACH,CAAC;CAEF,MAAM,UAAU,MAAM,eACX;EAAE,UAAU;EAAI;EAAkB,GACzC,CAAC,IAAI,iBAAiB,CACzB;AAiBD,QACI,oBAAC;EAAI,WAAW;EAAS,KAAK;EAAS,YAhBpB,aAClB,UAA2C;AAOxC,OAN2B,+BACvB,UAAU,eACV,yBACA,wBACH,CAGG,OAAM,gBAAgB;KAG9B;GAAC,UAAU;GAAe;GAAyB;GAAwB,CAC9E;EAGsE,kBAAgB;YAC/E,oBAAC;GACG,MAAM,QAAQ,OAAO,qBAAqB;GAC1C,UAAU;GACV;GACW;GACX,aAAa;GACF;aACX,oBAAC,cAAc;IAAS,OAAO;IAAU;KAAkC;IACjE;GACZ"}
|
|
@@ -32,9 +32,9 @@ interface RegionDesignMetadata {
|
|
|
32
32
|
*/
|
|
33
33
|
maxComponents?: number;
|
|
34
34
|
/**
|
|
35
|
-
* A list of
|
|
35
|
+
* A list of content link UUIDs for component instances in this region.
|
|
36
36
|
*/
|
|
37
|
-
|
|
37
|
+
contentLinkUuids?: string[];
|
|
38
38
|
/**
|
|
39
39
|
* A list of allowed component types in this region.
|
|
40
40
|
*/
|