@salesforce/storefront-next-runtime 0.4.0-alpha.2 → 0.4.0-alpha.3

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.
@@ -1 +1 @@
1
- {"version":3,"file":"ComponentContext.js","names":[],"sources":["../src/design/react/core/RegionContext.tsx","../src/design/react/core/ComponentContext.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';\n\nexport interface RegionContextType {\n regionId: string;\n componentIds: string[];\n}\n\nexport const RegionContext = React.createContext<RegionContextType | null>(null);\n\nexport const useRegionContext = (): RegionContextType | null => React.useContext(RegionContext);\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';\n\nexport interface ComponentContextType {\n componentId: string;\n name?: string;\n}\n\nexport const ComponentContext = React.createContext<ComponentContextType | null>(null);\n\nexport const useComponentContext = (): ComponentContextType | null => React.useContext(ComponentContext);\n"],"mappings":";;;AAsBA,MAAa,gBAAgB,MAAM,cAAwC,KAAK;AAEhF,MAAa,yBAAmD,MAAM,WAAW,cAAc;;;;ACF/F,MAAa,mBAAmB,MAAM,cAA2C,KAAK;AAEtF,MAAa,4BAAyD,MAAM,WAAW,iBAAiB"}
1
+ {"version":3,"file":"ComponentContext.js","names":[],"sources":["../src/design/react/core/RegionContext.tsx","../src/design/react/core/ComponentContext.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';\n\nexport interface RegionContextType {\n regionId: string;\n contentLinkUuids: string[];\n}\n\nexport const RegionContext = React.createContext<RegionContextType | null>(null);\n\nexport const useRegionContext = (): RegionContextType | null => React.useContext(RegionContext);\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';\n\nexport interface ComponentContextType {\n componentId: string;\n name?: string;\n contentLinkUuid?: string;\n}\n\nexport const ComponentContext = React.createContext<ComponentContextType | null>(null);\n\nexport const useComponentContext = (): ComponentContextType | null => React.useContext(ComponentContext);\n"],"mappings":";;;AAsBA,MAAa,gBAAgB,MAAM,cAAwC,KAAK;AAEhF,MAAa,yBAAmD,MAAM,WAAW,cAAc;;;;ACD/F,MAAa,mBAAmB,MAAM,cAA2C,KAAK;AAEtF,MAAa,4BAAyD,MAAM,WAAW,iBAAiB"}
@@ -8,13 +8,13 @@ import React, { useCallback, useRef } from "react";
8
8
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
9
 
10
10
  //#region src/design/react/hooks/useComponentDecoratorClasses.ts
11
- function useComponentDecoratorClasses({ componentId, isFragment, isLocalized }) {
12
- const { selectedComponentId, hoveredComponentId, dragState } = useDesignState();
13
- const isSelected = selectedComponentId === componentId;
14
- const isHovered = !dragState.isDragging && hoveredComponentId === componentId;
11
+ function useComponentDecoratorClasses({ contentLinkUuid, isFragment, isLocalized }) {
12
+ const { selectedContentLinkUuid, hoveredContentLinkUuid, dragState } = useDesignState();
13
+ const isSelected = selectedContentLinkUuid === contentLinkUuid;
14
+ const isHovered = !dragState.isDragging && hoveredContentLinkUuid === contentLinkUuid;
15
15
  const showFrame = (isSelected || isHovered) && !dragState.isDragging;
16
- const isMoving = dragState.isDragging && dragState.sourceComponentId === componentId;
17
- const isDropTarget = dragState.currentDropTarget?.componentId === componentId;
16
+ const isMoving = dragState.isDragging && dragState.sourceContentLinkUuid === contentLinkUuid;
17
+ const isDropTarget = dragState.currentDropTarget?.contentLinkUuid === contentLinkUuid;
18
18
  const dropTargetInsertType = dragState.currentDropTarget?.insertType;
19
19
  const dropTargetAxis = dropTargetInsertType?.axis;
20
20
  return [
@@ -32,17 +32,17 @@ function useComponentDecoratorClasses({ componentId, isFragment, isLocalized })
32
32
  //#endregion
33
33
  //#region src/design/react/hooks/useFocusedComponentHandler.ts
34
34
  /**
35
- * Focuses a component when the focused component id matches the component id.
36
- * @param componentId - The id of the component to focus.
35
+ * Focuses a component when the focused component id matches the content link UUID.
36
+ * @param contentLinkUuid - The content link UUID of the component.
37
37
  * @param nodeRef - The ref object to the node to focus.
38
38
  */
39
- function useFocusedComponentHandler(componentId, nodeRef) {
40
- const { focusedComponentId, focusComponent } = useDesignState();
39
+ function useFocusedComponentHandler(contentLinkUuid, nodeRef) {
40
+ const { focusedContentLinkUuid, focusComponent } = useDesignState();
41
41
  React.useEffect(() => {
42
- if (focusedComponentId === componentId && nodeRef.current) focusComponent(nodeRef.current);
42
+ if (focusedContentLinkUuid === contentLinkUuid && nodeRef.current) focusComponent(nodeRef.current);
43
43
  }, [
44
- focusedComponentId,
45
- componentId,
44
+ focusedContentLinkUuid,
45
+ contentLinkUuid,
46
46
  focusComponent,
47
47
  nodeRef
48
48
  ]);
@@ -73,7 +73,7 @@ function useComponentInfo(componentId) {
73
73
  //#region src/design/react/components/DesignComponent.tsx
74
74
  function DesignComponent(props) {
75
75
  const { designMetadata, children } = props;
76
- const { id = "", name, isFragment = false, isVisible = true, isLocalized = false } = designMetadata ?? {};
76
+ const { id = "", contentLinkUuid = "", name, isFragment = false, isVisible = true, isLocalized = false } = designMetadata ?? {};
77
77
  const componentId = id;
78
78
  const componentType = useComponentType(componentId);
79
79
  const componentInfo = useComponentInfo(componentId);
@@ -82,23 +82,31 @@ function DesignComponent(props) {
82
82
  const dragRef = useRef(null);
83
83
  const { regionId } = useRegionContext() ?? {};
84
84
  const { componentId: parentComponentId } = useComponentContext() ?? {};
85
- const { selectedComponentId, hoveredComponentId, setSelectedComponent, setHoveredComponent, startComponentMove, setPendingComponentDragId, dragState: { pendingComponentDragId, isDragging, sourceComponentId: draggingSourceComponentId } } = useDesignState();
86
- useFocusedComponentHandler(componentId, dragRef);
85
+ const { selectedContentLinkUuid, hoveredContentLinkUuid, setSelectedComponent, setHoveredComponent, startComponentMove, setPendingDragContentLinkUuid, dragState: { pendingDragContentLinkUuid, isDragging, sourceContentLinkUuid: draggingSourceContentLinkUuid }, registerContentLink } = useDesignState();
86
+ React.useEffect(() => {
87
+ if (contentLinkUuid && componentId) registerContentLink(contentLinkUuid, componentId);
88
+ }, [
89
+ componentId,
90
+ contentLinkUuid,
91
+ registerContentLink
92
+ ]);
93
+ useFocusedComponentHandler(contentLinkUuid, dragRef);
87
94
  useNodeToTargetStore({
88
95
  type: "component",
89
96
  nodeRef: dragRef,
90
97
  parentId: parentComponentId,
91
98
  regionId,
92
- componentId
99
+ componentId,
100
+ contentLinkUuid
93
101
  });
94
102
  const discoverComponents = useComponentDiscovery({ nodeToTargetMap });
95
- const isPendingDrag = pendingComponentDragId === componentId;
103
+ const isPendingDrag = pendingDragContentLinkUuid === contentLinkUuid;
96
104
  const findAndSetHoveredComponent = useCallback((x, y) => {
97
105
  setHoveredComponent(discoverComponents({
98
106
  x,
99
107
  y,
100
108
  filter: (entry) => entry.type === "component"
101
- })[0]?.componentId ?? null);
109
+ })[0]?.contentLinkUuid ?? null);
102
110
  }, [setHoveredComponent, discoverComponents]);
103
111
  const handleMouseMove = useThrottledCallback((event) => {
104
112
  event.stopPropagation();
@@ -110,35 +118,41 @@ function DesignComponent(props) {
110
118
  }, [findAndSetHoveredComponent]);
111
119
  const handleClick = useCallback((e) => {
112
120
  e.stopPropagation();
113
- setSelectedComponent(componentId);
114
- }, [setSelectedComponent, componentId]);
115
- const showFrame = [selectedComponentId, hoveredComponentId].includes(componentId) && !isDragging;
121
+ setSelectedComponent(contentLinkUuid ?? "");
122
+ }, [setSelectedComponent, contentLinkUuid]);
123
+ const showFrame = [selectedContentLinkUuid, hoveredContentLinkUuid].includes(contentLinkUuid ?? "") && !isDragging;
116
124
  const isDraggable = Boolean(componentId && regionId && componentType?.id);
117
125
  const classes = useComponentDecoratorClasses({
118
- componentId,
126
+ contentLinkUuid,
119
127
  isLocalized,
120
128
  isFragment: Boolean(isFragment)
121
129
  });
122
130
  const context = React.useMemo(() => ({
123
131
  componentId: id,
124
- name
125
- }), [id, name]);
132
+ name,
133
+ contentLinkUuid
134
+ }), [
135
+ id,
136
+ name,
137
+ contentLinkUuid
138
+ ]);
126
139
  const handleDragOver = React.useCallback((event) => {
127
- if (draggingSourceComponentId !== componentId) event.preventDefault();
128
- }, [draggingSourceComponentId, componentId]);
140
+ if (draggingSourceContentLinkUuid !== contentLinkUuid) event.preventDefault();
141
+ }, [draggingSourceContentLinkUuid, contentLinkUuid]);
129
142
  const handleMouseDown = React.useCallback((event) => {
130
- if (componentId) {
143
+ if (contentLinkUuid) {
131
144
  event.stopPropagation();
132
- setPendingComponentDragId(componentId);
145
+ setPendingDragContentLinkUuid(contentLinkUuid);
133
146
  }
134
- }, [componentId, setPendingComponentDragId]);
147
+ }, [contentLinkUuid, setPendingDragContentLinkUuid]);
135
148
  const handleDragStart = React.useCallback((event) => {
136
149
  event.stopPropagation();
137
- if (componentId && regionId && componentType?.id) startComponentMove(componentId, regionId, componentType.id);
150
+ if (componentId && regionId && componentType?.id) startComponentMove(componentId, regionId, componentType.id, contentLinkUuid);
138
151
  }, [
139
152
  componentId,
140
153
  regionId,
141
154
  componentType?.id,
155
+ contentLinkUuid,
142
156
  startComponentMove
143
157
  ]);
144
158
  if (!isVisible) return /* @__PURE__ */ jsx(Fragment, {});
@@ -157,6 +171,7 @@ function DesignComponent(props) {
157
171
  children: [/* @__PURE__ */ jsx("div", { className: "pd-design__component__drop-target" }), /* @__PURE__ */ jsx(DesignFrame, {
158
172
  showFrame,
159
173
  componentId,
174
+ contentLinkUuid,
160
175
  localized: isLocalized,
161
176
  name: componentName,
162
177
  parentId: parentComponentId,
@@ -1 +1 @@
1
- {"version":3,"file":"DesignComponent.js","names":[],"sources":["../src/design/react/hooks/useComponentDecoratorClasses.ts","../src/design/react/hooks/useFocusedComponentHandler.ts","../src/design/react/hooks/useComponentInfo.ts","../src/design/react/components/DesignComponent.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 { useDesignState } from './useDesignState';\n\nexport function useComponentDecoratorClasses({\n componentId,\n isFragment,\n isLocalized,\n}: {\n componentId: string;\n isFragment: boolean;\n isLocalized: boolean;\n}): string {\n const { selectedComponentId, hoveredComponentId, dragState } = useDesignState();\n\n const isSelected = selectedComponentId === componentId;\n const isHovered = !dragState.isDragging && hoveredComponentId === componentId;\n const showFrame = (isSelected || isHovered) && !dragState.isDragging;\n const isMoving = dragState.isDragging && dragState.sourceComponentId === componentId;\n const isDropTarget = dragState.currentDropTarget?.componentId === componentId;\n const dropTargetInsertType = dragState.currentDropTarget?.insertType;\n const dropTargetAxis = dropTargetInsertType?.axis;\n\n return [\n 'pd-design__decorator',\n isFragment ? 'pd-design__fragment' : 'pd-design__component',\n showFrame && 'pd-design__frame--visible',\n isSelected && 'pd-design__decorator--selected',\n isHovered && 'pd-design__decorator--hovered',\n isMoving && 'pd-design__decorator--moving',\n !isLocalized && 'pd-design__component--unlocalized',\n isDropTarget &&\n dropTargetAxis &&\n dropTargetInsertType &&\n `pd-design__drop-target__${dropTargetAxis}-${dropTargetInsertType.type}`,\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 from 'react';\nimport { useDesignState } from './useDesignState';\n\n/**\n * Focuses a component when the focused component id matches the component id.\n * @param componentId - The id of the component to focus.\n * @param nodeRef - The ref object to the node to focus.\n */\nexport function useFocusedComponentHandler(componentId: string, nodeRef: React.RefObject<Element | null>): void {\n const { focusedComponentId, focusComponent } = useDesignState();\n\n React.useEffect(() => {\n if (focusedComponentId === componentId && nodeRef.current) {\n focusComponent(nodeRef.current);\n }\n }, [focusedComponentId, componentId, focusComponent, nodeRef]);\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 { ComponentInfo } from '../../messaging-api';\nimport { useDesignContext } from '../context/DesignContext';\nimport { useDesignState } from './useDesignState';\n\n/**\n * Hook that returns the current ComponentInfo for a given component ID,\n * merging the base config with any runtime updates.\n *\n * @param componentId - The ID of the component to get info for\n * @returns The merged ComponentInfo or null if the component doesn't exist\n */\nexport function useComponentInfo(componentId: string): ComponentInfo | null {\n const { pageDesignerConfig } = useDesignContext();\n const { componentUpdates } = useDesignState();\n const baseComponentInfo = pageDesignerConfig?.components?.[componentId];\n const updates = componentUpdates?.[componentId] ?? {};\n\n if (!baseComponentInfo) {\n return null;\n }\n\n return { ...baseComponentInfo, ...updates };\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, { useRef, useCallback } from 'react';\nimport type { ComponentDecoratorProps } from '../core/component.types';\nimport { useComponentDecoratorClasses } from '../hooks/useComponentDecoratorClasses';\nimport { useDesignState } from '../hooks/useDesignState';\nimport { useFocusedComponentHandler } from '../hooks/useFocusedComponentHandler';\nimport { useNodeToTargetStore } from '../hooks/useNodeToTargetStore';\nimport { DesignFrame } from './DesignFrame';\nimport { useRegionContext } from '../core/RegionContext';\nimport { ComponentContext, useComponentContext, type ComponentContextType } from '../core/ComponentContext';\nimport { useComponentDiscovery } from '../hooks/useComponentDiscovery';\nimport { useComponentType } from '../hooks/useComponentType';\nimport { useThrottledCallback } from '../hooks/useThrottledCallback';\nimport { useComponentInfo } from '../hooks/useComponentInfo';\n\nexport function DesignComponent(props: ComponentDecoratorProps<unknown>): React.JSX.Element {\n const { designMetadata, children } = props;\n const { id = '', name, isFragment = false, isVisible = true, isLocalized = false } = designMetadata ?? {};\n const componentId = id;\n const componentType = useComponentType(componentId);\n const componentInfo = useComponentInfo(componentId);\n const { nodeToTargetMap } = useDesignState();\n\n const componentName = componentInfo?.name || componentType?.label || name || 'Component';\n const dragRef = useRef<HTMLDivElement>(null);\n const { regionId } = useRegionContext() ?? {};\n const { componentId: parentComponentId } = useComponentContext() ?? {};\n\n const {\n selectedComponentId,\n hoveredComponentId,\n setSelectedComponent,\n setHoveredComponent,\n startComponentMove,\n setPendingComponentDragId,\n dragState: { pendingComponentDragId, isDragging, sourceComponentId: draggingSourceComponentId },\n } = useDesignState();\n\n useFocusedComponentHandler(componentId, dragRef);\n useNodeToTargetStore({\n type: 'component',\n nodeRef: dragRef,\n parentId: parentComponentId,\n regionId,\n componentId,\n });\n\n const discoverComponents = useComponentDiscovery({\n nodeToTargetMap,\n });\n\n const isPendingDrag = pendingComponentDragId === componentId;\n const findAndSetHoveredComponent = useCallback(\n (x: number, y: number) => {\n // If we hover off a component, we could still be hovering over a parent component\n // that contains that child. In this instance, the mouse enter doesn't fire and that parent\n // would not be highlighted. Everytime we leave a component, we check\n // if we are hovering over a component at those coordinates. If we are,\n // we set the hovered component to that component.\n const components = discoverComponents({\n x,\n y,\n filter: (entry) => entry.type === 'component',\n });\n\n setHoveredComponent(components[0]?.componentId ?? null);\n },\n [setHoveredComponent, discoverComponents]\n );\n\n const handleMouseMove = useThrottledCallback(\n (event: React.MouseEvent) => {\n event.stopPropagation();\n findAndSetHoveredComponent(event.clientX, event.clientY);\n },\n 1000 / 60, // 60 FPS\n [findAndSetHoveredComponent]\n );\n\n const handleMouseLeave = useCallback(\n (event: React.MouseEvent) => {\n event.stopPropagation();\n findAndSetHoveredComponent(event.clientX, event.clientY);\n },\n [findAndSetHoveredComponent]\n );\n\n const handleClick = useCallback(\n (e: React.MouseEvent) => {\n e.stopPropagation();\n setSelectedComponent(componentId);\n },\n [setSelectedComponent, componentId]\n );\n\n const showFrame = [selectedComponentId, hoveredComponentId].includes(componentId) && !isDragging;\n const isDraggable = Boolean(componentId && regionId && componentType?.id);\n\n const classes = useComponentDecoratorClasses({\n componentId,\n isLocalized,\n isFragment: Boolean(isFragment),\n });\n\n const context = React.useMemo<ComponentContextType>(() => ({ componentId: id, name }), [id, name]);\n\n // Makes the component a drop target.\n const handleDragOver = React.useCallback(\n (event: React.DragEvent<HTMLDivElement>) => {\n // Don't prevent propagation here.\n // We depend on the global listener to handle the drag over event.\n // If we are moving a component, don't let it be droppable on itself.\n if (draggingSourceComponentId !== componentId) {\n event.preventDefault();\n }\n },\n [draggingSourceComponentId, componentId]\n );\n\n // When dragging, we don't consider the component as dragging until the drag start event\n // is triggered. However, we need to mark the component as draggable on mouse down so that\n // it can even be dragged via the native drag and drop API.\n //\n // If we were to mark the components as dragging on mouse down instead, a selection of a component\n // would first remove the frame because this thinks we are dragging the component instead of selecting it.\n // This is why it is split up into two events.\n const handleMouseDown = React.useCallback(\n (event: React.MouseEvent) => {\n if (componentId) {\n event.stopPropagation();\n setPendingComponentDragId(componentId);\n }\n },\n [componentId, setPendingComponentDragId]\n );\n\n const handleDragStart = React.useCallback(\n (event: React.DragEvent) => {\n event.stopPropagation();\n\n if (componentId && regionId && componentType?.id) {\n startComponentMove(componentId, regionId, componentType.id);\n }\n },\n [componentId, regionId, componentType?.id, startComponentMove]\n );\n\n // Don't render anything if the components is hidden via visibility rules.\n // We still want the component to be reactive in case the use changes the\n // visibility rules or the render context.\n if (!isVisible) {\n return <></>;\n }\n\n return (\n <div\n ref={dragRef}\n className={classes}\n draggable={isPendingDrag && isDraggable}\n onClick={handleClick}\n onDragOver={handleDragOver}\n onDragStart={handleDragStart}\n onMouseMove={handleMouseMove}\n onMouseLeave={handleMouseLeave}\n onMouseDown={handleMouseDown}\n data-component-type={componentType?.id}\n data-testid={`design-component-${componentId}`}>\n <div className=\"pd-design__component__drop-target\" />\n <DesignFrame\n showFrame={showFrame}\n componentId={componentId}\n localized={isLocalized}\n name={componentName}\n parentId={parentComponentId}\n isMoveable={isDraggable}\n regionId={regionId}>\n <ComponentContext.Provider value={context}>{children}</ComponentContext.Provider>\n </DesignFrame>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"DesignComponent.js","names":[],"sources":["../src/design/react/hooks/useComponentDecoratorClasses.ts","../src/design/react/hooks/useFocusedComponentHandler.ts","../src/design/react/hooks/useComponentInfo.ts","../src/design/react/components/DesignComponent.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 { useDesignState } from './useDesignState';\n\nexport function useComponentDecoratorClasses({\n contentLinkUuid,\n isFragment,\n isLocalized,\n}: {\n contentLinkUuid: string;\n isFragment: boolean;\n isLocalized: boolean;\n}): string {\n const { selectedContentLinkUuid, hoveredContentLinkUuid, dragState } = useDesignState();\n\n const isSelected = selectedContentLinkUuid === contentLinkUuid;\n const isHovered = !dragState.isDragging && hoveredContentLinkUuid === contentLinkUuid;\n const showFrame = (isSelected || isHovered) && !dragState.isDragging;\n const isMoving = dragState.isDragging && dragState.sourceContentLinkUuid === contentLinkUuid;\n const isDropTarget = dragState.currentDropTarget?.contentLinkUuid === contentLinkUuid;\n const dropTargetInsertType = dragState.currentDropTarget?.insertType;\n const dropTargetAxis = dropTargetInsertType?.axis;\n\n return [\n 'pd-design__decorator',\n isFragment ? 'pd-design__fragment' : 'pd-design__component',\n showFrame && 'pd-design__frame--visible',\n isSelected && 'pd-design__decorator--selected',\n isHovered && 'pd-design__decorator--hovered',\n isMoving && 'pd-design__decorator--moving',\n !isLocalized && 'pd-design__component--unlocalized',\n isDropTarget &&\n dropTargetAxis &&\n dropTargetInsertType &&\n `pd-design__drop-target__${dropTargetAxis}-${dropTargetInsertType.type}`,\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 from 'react';\nimport { useDesignState } from './useDesignState';\n\n/**\n * Focuses a component when the focused component id matches the content link UUID.\n * @param contentLinkUuid - The content link UUID of the component.\n * @param nodeRef - The ref object to the node to focus.\n */\nexport function useFocusedComponentHandler(contentLinkUuid: string, nodeRef: React.RefObject<Element | null>): void {\n const { focusedContentLinkUuid, focusComponent } = useDesignState();\n\n React.useEffect(() => {\n if (focusedContentLinkUuid === contentLinkUuid && nodeRef.current) {\n focusComponent(nodeRef.current);\n }\n }, [focusedContentLinkUuid, contentLinkUuid, focusComponent, nodeRef]);\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 { ComponentInfo } from '../../messaging-api';\nimport { useDesignContext } from '../context/DesignContext';\nimport { useDesignState } from './useDesignState';\n\n/**\n * Hook that returns the current ComponentInfo for a given component ID,\n * merging the base config with any runtime updates.\n *\n * @param componentId - The ID of the component to get info for\n * @returns The merged ComponentInfo or null if the component doesn't exist\n */\nexport function useComponentInfo(componentId: string): ComponentInfo | null {\n const { pageDesignerConfig } = useDesignContext();\n const { componentUpdates } = useDesignState();\n const baseComponentInfo = pageDesignerConfig?.components?.[componentId];\n const updates = componentUpdates?.[componentId] ?? {};\n\n if (!baseComponentInfo) {\n return null;\n }\n\n return { ...baseComponentInfo, ...updates };\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, { useRef, useCallback } from 'react';\nimport type { ComponentDecoratorProps } from '../core/component.types';\nimport { useComponentDecoratorClasses } from '../hooks/useComponentDecoratorClasses';\nimport { useDesignState } from '../hooks/useDesignState';\nimport { useFocusedComponentHandler } from '../hooks/useFocusedComponentHandler';\nimport { useNodeToTargetStore } from '../hooks/useNodeToTargetStore';\nimport { DesignFrame } from './DesignFrame';\nimport { useRegionContext } from '../core/RegionContext';\nimport { ComponentContext, useComponentContext, type ComponentContextType } from '../core/ComponentContext';\nimport { useComponentDiscovery } from '../hooks/useComponentDiscovery';\nimport { useComponentType } from '../hooks/useComponentType';\nimport { useThrottledCallback } from '../hooks/useThrottledCallback';\nimport { useComponentInfo } from '../hooks/useComponentInfo';\n\nexport function DesignComponent(props: ComponentDecoratorProps<unknown>): React.JSX.Element {\n const { designMetadata, children } = props;\n const {\n id = '',\n contentLinkUuid = '',\n name,\n isFragment = false,\n isVisible = true,\n isLocalized = false,\n } = designMetadata ?? {};\n const componentId = id;\n const componentType = useComponentType(componentId);\n const componentInfo = useComponentInfo(componentId);\n const { nodeToTargetMap } = useDesignState();\n\n const componentName = componentInfo?.name || componentType?.label || name || 'Component';\n const dragRef = useRef<HTMLDivElement>(null);\n const { regionId } = useRegionContext() ?? {};\n const { componentId: parentComponentId } = useComponentContext() ?? {};\n\n const {\n selectedContentLinkUuid,\n hoveredContentLinkUuid,\n setSelectedComponent,\n setHoveredComponent,\n startComponentMove,\n setPendingDragContentLinkUuid,\n dragState: { pendingDragContentLinkUuid, isDragging, sourceContentLinkUuid: draggingSourceContentLinkUuid },\n registerContentLink,\n } = useDesignState();\n\n React.useEffect(() => {\n if (contentLinkUuid && componentId) {\n registerContentLink(contentLinkUuid, componentId);\n }\n }, [componentId, contentLinkUuid, registerContentLink]);\n\n useFocusedComponentHandler(contentLinkUuid, dragRef);\n useNodeToTargetStore({\n type: 'component',\n nodeRef: dragRef,\n parentId: parentComponentId,\n regionId,\n componentId,\n contentLinkUuid,\n });\n\n const discoverComponents = useComponentDiscovery({\n nodeToTargetMap,\n });\n\n const isPendingDrag = pendingDragContentLinkUuid === contentLinkUuid;\n const findAndSetHoveredComponent = useCallback(\n (x: number, y: number) => {\n // If we hover off a component, we could still be hovering over a parent component\n // that contains that child. In this instance, the mouse enter doesn't fire and that parent\n // would not be highlighted. Everytime we leave a component, we check\n // if we are hovering over a component at those coordinates. If we are,\n // we set the hovered component to that component.\n const components = discoverComponents({\n x,\n y,\n filter: (entry) => entry.type === 'component',\n });\n\n setHoveredComponent(components[0]?.contentLinkUuid ?? null);\n },\n [setHoveredComponent, discoverComponents]\n );\n\n const handleMouseMove = useThrottledCallback(\n (event: React.MouseEvent) => {\n event.stopPropagation();\n findAndSetHoveredComponent(event.clientX, event.clientY);\n },\n 1000 / 60, // 60 FPS\n [findAndSetHoveredComponent]\n );\n\n const handleMouseLeave = useCallback(\n (event: React.MouseEvent) => {\n event.stopPropagation();\n findAndSetHoveredComponent(event.clientX, event.clientY);\n },\n [findAndSetHoveredComponent]\n );\n\n const handleClick = useCallback(\n (e: React.MouseEvent) => {\n e.stopPropagation();\n setSelectedComponent(contentLinkUuid ?? '');\n },\n [setSelectedComponent, contentLinkUuid]\n );\n\n const showFrame = [selectedContentLinkUuid, hoveredContentLinkUuid].includes(contentLinkUuid ?? '') && !isDragging;\n const isDraggable = Boolean(componentId && regionId && componentType?.id);\n\n const classes = useComponentDecoratorClasses({\n contentLinkUuid,\n isLocalized,\n isFragment: Boolean(isFragment),\n });\n\n const context = React.useMemo<ComponentContextType>(\n () => ({ componentId: id, name, contentLinkUuid }),\n [id, name, contentLinkUuid]\n );\n\n // Makes the component a drop target.\n const handleDragOver = React.useCallback(\n (event: React.DragEvent<HTMLDivElement>) => {\n // Don't prevent propagation here.\n // We depend on the global listener to handle the drag over event.\n // If we are moving a component, don't let it be droppable on itself.\n // Compare by contentLinkUuid to handle duplicate components correctly.\n if (draggingSourceContentLinkUuid !== contentLinkUuid) {\n event.preventDefault();\n }\n },\n [draggingSourceContentLinkUuid, contentLinkUuid]\n );\n\n // When dragging, we don't consider the component as dragging until the drag start event\n // is triggered. However, we need to mark the component as draggable on mouse down so that\n // it can even be dragged via the native drag and drop API.\n //\n // If we were to mark the components as dragging on mouse down instead, a selection of a component\n // would first remove the frame because this thinks we are dragging the component instead of selecting it.\n // This is why it is split up into two events.\n const handleMouseDown = React.useCallback(\n (event: React.MouseEvent) => {\n if (contentLinkUuid) {\n event.stopPropagation();\n setPendingDragContentLinkUuid(contentLinkUuid);\n }\n },\n [contentLinkUuid, setPendingDragContentLinkUuid]\n );\n\n const handleDragStart = React.useCallback(\n (event: React.DragEvent) => {\n event.stopPropagation();\n\n if (componentId && regionId && componentType?.id) {\n startComponentMove(componentId, regionId, componentType.id, contentLinkUuid);\n }\n },\n [componentId, regionId, componentType?.id, contentLinkUuid, startComponentMove]\n );\n\n // Don't render anything if the components is hidden via visibility rules.\n // We still want the component to be reactive in case the use changes the\n // visibility rules or the render context.\n if (!isVisible) {\n return <></>;\n }\n\n return (\n <div\n ref={dragRef}\n className={classes}\n draggable={isPendingDrag && isDraggable}\n onClick={handleClick}\n onDragOver={handleDragOver}\n onDragStart={handleDragStart}\n onMouseMove={handleMouseMove}\n onMouseLeave={handleMouseLeave}\n onMouseDown={handleMouseDown}\n data-component-type={componentType?.id}\n data-testid={`design-component-${componentId}`}>\n <div className=\"pd-design__component__drop-target\" />\n <DesignFrame\n showFrame={showFrame}\n componentId={componentId}\n contentLinkUuid={contentLinkUuid}\n localized={isLocalized}\n name={componentName}\n parentId={parentComponentId}\n isMoveable={isDraggable}\n regionId={regionId}>\n <ComponentContext.Provider value={context}>{children}</ComponentContext.Provider>\n </DesignFrame>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -37,28 +37,29 @@ function useInteraction(config) {
37
37
  * Custom hook that manages component selection state and handles
38
38
  * client-host communication for selection events.
39
39
  *
40
- * @param isDesignMode - Whether design mode is active
41
- * @param clientApi - Client API for host communication
42
40
  * @returns Selection state and interaction methods
43
41
  */
44
- function useSelectInteraction() {
45
- const { state: selectedComponentId, setSelectedComponent } = useInteraction({
42
+ function useSelectInteraction({ contentLinkMap }) {
43
+ const { state: selectedContentLinkUuid, setSelectedComponent } = useInteraction({
46
44
  initialState: "",
47
45
  eventHandlers: {
48
46
  ComponentSelected: { handler: (event, setState) => {
49
- setState(event.componentId);
47
+ setState(event.contentLinkUuid);
50
48
  } },
51
49
  ComponentDeselected: { handler: (_, setState) => {
52
50
  setState("");
53
51
  } }
54
52
  },
55
- actions: (_state, setState, clientApi) => ({ setSelectedComponent: (componentId) => {
56
- setState(componentId);
57
- clientApi?.selectComponent({ componentId });
53
+ actions: (_state, setState, clientApi) => ({ setSelectedComponent: (contentLinkUuid) => {
54
+ setState(contentLinkUuid);
55
+ clientApi?.selectComponent({
56
+ componentId: contentLinkMap[contentLinkUuid] ?? "",
57
+ contentLinkUuid
58
+ });
58
59
  } })
59
60
  });
60
61
  return {
61
- selectedComponentId,
62
+ selectedContentLinkUuid,
62
63
  setSelectedComponent
63
64
  };
64
65
  }
@@ -71,34 +72,40 @@ function useSelectInteraction() {
71
72
  *
72
73
  * @returns Hover state and interaction methods
73
74
  */
74
- function useHoverInteraction() {
75
- const { state: hoveredComponentId, setHoveredComponent } = useInteraction({
75
+ function useHoverInteraction({ contentLinkMap }) {
76
+ const { state: hoveredContentLinkUuid, setHoveredComponent } = useInteraction({
76
77
  initialState: null,
77
78
  eventHandlers: {
78
- ComponentHoveredIn: { handler: (event, setState) => setState(event.componentId) },
79
+ ComponentHoveredIn: { handler: (event, setState) => setState(event.contentLinkUuid) },
79
80
  ComponentHoveredOut: { handler: (_, setState) => setState(null) }
80
81
  },
81
- actions: (state, setState, clientApi) => ({ setHoveredComponent: (componentId) => {
82
- if (state && componentId !== state) clientApi?.hoverOutOfComponent({ componentId: state });
83
- if (componentId && componentId !== state) clientApi?.hoverInToComponent({ componentId });
84
- setState(componentId);
82
+ actions: (state, setState, clientApi) => ({ setHoveredComponent: (componentUuid) => {
83
+ if (state && componentUuid !== state) clientApi?.hoverOutOfComponent({
84
+ componentId: contentLinkMap[state] ?? state,
85
+ contentLinkUuid: state
86
+ });
87
+ if (componentUuid && componentUuid !== state) clientApi?.hoverInToComponent({
88
+ componentId: contentLinkMap[componentUuid] ?? null,
89
+ contentLinkUuid: componentUuid
90
+ });
91
+ setState(componentUuid);
85
92
  } })
86
93
  });
87
94
  return {
88
- hoveredComponentId,
95
+ hoveredContentLinkUuid,
89
96
  setHoveredComponent
90
97
  };
91
98
  }
92
99
 
93
100
  //#endregion
94
101
  //#region src/design/react/hooks/useDeleteInteraction.ts
95
- function useDeleteInteraction({ selectedComponentId, setSelectedComponent }) {
102
+ function useDeleteInteraction({ selectedContentLinkUuid, setSelectedComponent }) {
96
103
  const { deleteComponent } = useInteraction({
97
104
  initialState: null,
98
105
  eventHandlers: {},
99
106
  actions: (_state, _setState, clientApi) => ({ deleteComponent: (event) => {
100
107
  clientApi?.deleteComponent(event);
101
- if (selectedComponentId === event.componentId) setSelectedComponent("");
108
+ if (selectedContentLinkUuid === event.contentLinkUuid) setSelectedComponent("");
102
109
  } })
103
110
  });
104
111
  return { deleteComponent };
@@ -107,11 +114,11 @@ function useDeleteInteraction({ selectedComponentId, setSelectedComponent }) {
107
114
  //#endregion
108
115
  //#region src/design/react/hooks/useFocusInteraction.ts
109
116
  function useFocusInteraction({ setSelectedComponent }) {
110
- const { state: focusedComponentId, focusComponent } = useInteraction({
117
+ const { state: focusedContentLinkUuid, focusComponent } = useInteraction({
111
118
  initialState: null,
112
119
  eventHandlers: { ComponentFocused: { handler: (event, setState) => {
113
120
  setSelectedComponent("");
114
- setState(event.componentId);
121
+ setState(event.contentLinkUuid);
115
122
  } } },
116
123
  actions: (_state, setState) => ({ focusComponent: (node) => {
117
124
  node.scrollIntoView();
@@ -119,7 +126,7 @@ function useFocusInteraction({ setSelectedComponent }) {
119
126
  } })
120
127
  });
121
128
  return {
122
- focusedComponentId,
129
+ focusedContentLinkUuid,
123
130
  focusComponent
124
131
  };
125
132
  }
@@ -237,10 +244,10 @@ function getInsertionType({ cache, node, x, y }) {
237
244
  type: relativeDeltaY < 0 ? "before" : "after"
238
245
  };
239
246
  }
240
- function isOnSelfDropTarget({ sourceComponentId, beforeComponentId, afterComponentId, insertType, componentId }) {
241
- const isOnSource = sourceComponentId && componentId === sourceComponentId;
242
- const isOnSameRegionBefore = sourceComponentId && insertType.type === "before" && beforeComponentId === sourceComponentId;
243
- const isOnSameRegionAfter = sourceComponentId && insertType.type === "after" && afterComponentId === sourceComponentId;
247
+ function isOnSelfDropTarget({ sourceContentLinkUuid, beforeContentLinkUuid, afterContentLinkUuid, insertType, contentLinkUuid }) {
248
+ const isOnSource = sourceContentLinkUuid && contentLinkUuid === sourceContentLinkUuid;
249
+ const isOnSameRegionBefore = sourceContentLinkUuid && insertType.type === "before" && beforeContentLinkUuid === sourceContentLinkUuid;
250
+ const isOnSameRegionAfter = sourceContentLinkUuid && insertType.type === "after" && afterContentLinkUuid === sourceContentLinkUuid;
244
251
  return isOnSource || isOnSameRegionBefore || isOnSameRegionAfter;
245
252
  }
246
253
  function useDragInteraction({ nodeToTargetMap }) {
@@ -267,9 +274,9 @@ function useDragInteraction({ nodeToTargetMap }) {
267
274
  region
268
275
  };
269
276
  }, [discoverComponents]);
270
- const getInsertionComponentIds = (componentId, region) => {
271
- const componentIndex = region.componentIds.indexOf(componentId);
272
- return [region.componentIds[componentIndex - 1], region.componentIds[componentIndex + 1]];
277
+ const getInsertionComponentUuids = (contentLinkUuid, region) => {
278
+ const componentIndex = region.contentLinkUuids.indexOf(contentLinkUuid);
279
+ return [region.contentLinkUuids[componentIndex - 1], region.contentLinkUuids[componentIndex + 1]];
273
280
  };
274
281
  const getCurrentDropTarget = useCallback(({ x, y, rectCache, componentType }) => {
275
282
  const { component, region } = getNearestComponentAndRegion(x, y);
@@ -284,16 +291,17 @@ function useDragInteraction({ nodeToTargetMap }) {
284
291
  axis: "y",
285
292
  type: "after"
286
293
  };
287
- const [beforeComponentId, afterComponentId] = component ? getInsertionComponentIds(component.componentId, region) : [];
294
+ const componentContentLinkUuid = component?.contentLinkUuid ?? "";
295
+ const [beforeContentLinkUuid, afterContentLinkUuid] = component ? getInsertionComponentUuids(componentContentLinkUuid, region) : [];
288
296
  return {
289
297
  type: component ? "component" : "region",
290
298
  regionId: region.regionId,
291
- componentIds: region.componentIds,
292
299
  componentId: component?.componentId ?? "",
300
+ contentLinkUuid: componentContentLinkUuid,
293
301
  parentId: region.parentId,
294
- beforeComponentId,
295
- afterComponentId,
296
- insertComponentId: component?.componentId,
302
+ beforeContentLinkUuid,
303
+ afterContentLinkUuid,
304
+ insertContentLinkUuid: componentContentLinkUuid,
297
305
  insertType,
298
306
  componentTypeInclusions: region.componentTypeInclusions,
299
307
  componentTypeExclusions: region.componentTypeExclusions
@@ -314,18 +322,19 @@ function useDragInteraction({ nodeToTargetMap }) {
314
322
  return 0;
315
323
  };
316
324
  const scrollFactorRef = useRef(0);
317
- const { state: dragState, commitCurrentDropTarget, updateComponentMove, startComponentMove, dropComponent, cancelDrag, setPendingComponentDragId } = useInteraction({
325
+ const { state: dragState, commitCurrentDropTarget, updateComponentMove, startComponentMove, dropComponent, cancelDrag, setPendingDragContentLinkUuid } = useInteraction({
318
326
  initialState: {
319
327
  isDragging: false,
320
328
  componentType: "",
321
- sourceComponentId: void 0,
329
+ fragmentId: void 0,
330
+ sourceContentLinkUuid: void 0,
322
331
  sourceRegionId: void 0,
323
332
  x: 0,
324
333
  y: 0,
325
334
  currentDropTarget: null,
326
335
  pendingTargetCommit: false,
327
336
  rectCache: /* @__PURE__ */ new WeakMap(),
328
- pendingComponentDragId: null
337
+ pendingDragContentLinkUuid: null
329
338
  },
330
339
  eventHandlers: {
331
340
  ComponentDragStarted: { handler: (event, setState) => {
@@ -333,7 +342,8 @@ function useDragInteraction({ nodeToTargetMap }) {
333
342
  setState((prevState) => ({
334
343
  ...prevState,
335
344
  componentType: event.componentType,
336
- sourceComponentId: void 0,
345
+ fragmentId: event.fragmentId,
346
+ sourceContentLinkUuid: void 0,
337
347
  sourceRegionId: void 0,
338
348
  x: 0,
339
349
  y: 0,
@@ -393,7 +403,7 @@ function useDragInteraction({ nodeToTargetMap }) {
393
403
  y: 0,
394
404
  scrollDirection: 0,
395
405
  isDragging: false,
396
- pendingComponentDragId: null
406
+ pendingDragContentLinkUuid: null
397
407
  }));
398
408
  },
399
409
  updateComponentMove: ({ x, y }) => {
@@ -414,10 +424,10 @@ function useDragInteraction({ nodeToTargetMap }) {
414
424
  })
415
425
  }));
416
426
  },
417
- setPendingComponentDragId: (componentId) => {
427
+ setPendingDragContentLinkUuid: (contentLinkUuid) => {
418
428
  setState((prevState) => ({
419
429
  ...prevState,
420
- pendingComponentDragId: componentId
430
+ pendingDragContentLinkUuid: contentLinkUuid
421
431
  }));
422
432
  },
423
433
  dropComponent: () => {
@@ -427,14 +437,14 @@ function useDragInteraction({ nodeToTargetMap }) {
427
437
  pendingTargetCommit: true
428
438
  }));
429
439
  },
430
- startComponentMove: (componentId, regionId, componentType) => {
440
+ startComponentMove: (componentId, regionId, componentType, contentLinkUuid) => {
431
441
  scrollFactorRef.current = 0;
432
442
  setState((prevState) => ({
433
443
  ...prevState,
434
444
  x: 0,
435
445
  y: 0,
436
446
  componentType,
437
- sourceComponentId: componentId,
447
+ sourceContentLinkUuid: contentLinkUuid,
438
448
  sourceRegionId: regionId,
439
449
  isDragging: true,
440
450
  scrollDirection: 0,
@@ -443,31 +453,33 @@ function useDragInteraction({ nodeToTargetMap }) {
443
453
  },
444
454
  commitCurrentDropTarget: () => {
445
455
  if (state.currentDropTarget) {
446
- if (state.sourceComponentId) {
456
+ if (state.sourceContentLinkUuid) {
447
457
  if (!isOnSelfDropTarget({
448
- sourceComponentId: state.sourceComponentId,
449
- beforeComponentId: state.currentDropTarget.beforeComponentId,
450
- afterComponentId: state.currentDropTarget.afterComponentId,
458
+ sourceContentLinkUuid: state.sourceContentLinkUuid,
459
+ beforeContentLinkUuid: state.currentDropTarget.beforeContentLinkUuid,
460
+ afterContentLinkUuid: state.currentDropTarget.afterContentLinkUuid,
451
461
  insertType: state.currentDropTarget.insertType,
452
- componentId: state.currentDropTarget.componentId
462
+ contentLinkUuid: state.currentDropTarget.contentLinkUuid ?? ""
453
463
  })) clientApi?.moveComponentToRegion({
454
- componentId: state.sourceComponentId,
464
+ componentId: state.currentDropTarget.componentId ?? "",
465
+ contentLinkUuid: state.sourceContentLinkUuid,
455
466
  sourceRegionId: state.sourceRegionId ?? "",
456
467
  insertType: state.currentDropTarget.insertType?.type,
457
- insertComponentId: state.currentDropTarget.insertComponentId,
458
- beforeComponentId: state.currentDropTarget.beforeComponentId,
459
- afterComponentId: state.currentDropTarget.afterComponentId,
468
+ insertComponentId: state.currentDropTarget.insertContentLinkUuid,
469
+ beforeComponentId: state.currentDropTarget.beforeContentLinkUuid,
470
+ afterComponentId: state.currentDropTarget.afterContentLinkUuid,
460
471
  targetRegionId: state.currentDropTarget.regionId,
461
472
  targetComponentId: state.currentDropTarget.parentId ?? ""
462
473
  });
463
- } else if (state.componentType) clientApi?.addComponentToRegion({
474
+ } else if (state.componentType || state.fragmentId) clientApi?.addComponentToRegion({
464
475
  insertType: state.currentDropTarget.insertType?.type,
465
- insertComponentId: state.currentDropTarget.insertComponentId,
476
+ insertComponentId: state.currentDropTarget.insertContentLinkUuid,
477
+ beforeComponentId: state.currentDropTarget.beforeContentLinkUuid,
466
478
  componentProperties: {},
467
- componentType: state.componentType,
479
+ componentType: state.fragmentId ? "" : state.componentType ?? "",
480
+ fragmentId: state.fragmentId,
468
481
  targetComponentId: state.currentDropTarget.parentId ?? "",
469
- beforeComponentId: state.currentDropTarget.beforeComponentId,
470
- afterComponentId: state.currentDropTarget.afterComponentId,
482
+ afterComponentId: state.currentDropTarget.afterContentLinkUuid,
471
483
  targetRegionId: state.currentDropTarget.regionId
472
484
  });
473
485
  }
@@ -478,9 +490,9 @@ function useDragInteraction({ nodeToTargetMap }) {
478
490
  y: 0,
479
491
  componentType: "",
480
492
  scrollDirection: 0,
481
- sourceComponentId: void 0,
493
+ sourceContentLinkUuid: void 0,
482
494
  sourceRegionId: void 0,
483
- pendingComponentDragId: null,
495
+ pendingDragContentLinkUuid: null,
484
496
  currentDropTarget: null,
485
497
  pendingTargetCommit: false
486
498
  }));
@@ -501,7 +513,7 @@ function useDragInteraction({ nodeToTargetMap }) {
501
513
  }, [dragState.scrollDirection, scrollFactorRef]);
502
514
  return {
503
515
  dragState,
504
- setPendingComponentDragId,
516
+ setPendingDragContentLinkUuid,
505
517
  commitCurrentDropTarget,
506
518
  startComponentMove,
507
519
  updateComponentMove,
@@ -556,10 +568,20 @@ function useComponentUpdateInteraction() {
556
568
  //#region src/design/react/context/DesignStateContext.tsx
557
569
  const DesignStateContext = React.createContext(null);
558
570
  const DesignStateProvider = ({ children }) => {
559
- const selectInteraction = useSelectInteraction();
560
- const hoverInteraction = useHoverInteraction();
571
+ const [contentLinkMap, setContentLinkMap] = React.useState({});
572
+ const registerContentLink = React.useCallback((contentLinkUuid, componentId) => {
573
+ setContentLinkMap((prev) => {
574
+ if (prev[contentLinkUuid] === componentId) return prev;
575
+ return {
576
+ ...prev,
577
+ [contentLinkUuid]: componentId
578
+ };
579
+ });
580
+ }, []);
581
+ const selectInteraction = useSelectInteraction({ contentLinkMap });
582
+ const hoverInteraction = useHoverInteraction({ contentLinkMap });
561
583
  const deleteInteraction = useDeleteInteraction({
562
- selectedComponentId: selectInteraction.selectedComponentId,
584
+ selectedContentLinkUuid: selectInteraction.selectedContentLinkUuid,
563
585
  setSelectedComponent: selectInteraction.setSelectedComponent
564
586
  });
565
587
  const focusInteraction = useFocusInteraction({ setSelectedComponent: selectInteraction.setSelectedComponent });
@@ -575,7 +597,9 @@ const DesignStateProvider = ({ children }) => {
575
597
  ...dragInteraction,
576
598
  ...scrollInteraction,
577
599
  ...componentUpdateInteraction,
578
- nodeToTargetMap
600
+ nodeToTargetMap,
601
+ contentLinkMap,
602
+ registerContentLink
579
603
  }), [
580
604
  deleteInteraction,
581
605
  selectInteraction,
@@ -584,7 +608,9 @@ const DesignStateProvider = ({ children }) => {
584
608
  dragInteraction,
585
609
  nodeToTargetMap,
586
610
  scrollInteraction,
587
- componentUpdateInteraction
611
+ componentUpdateInteraction,
612
+ contentLinkMap,
613
+ registerContentLink
588
614
  ]);
589
615
  return /* @__PURE__ */ jsx(DesignStateContext.Provider, {
590
616
  value: state,