@salesforce/storefront-next-runtime 0.1.1 → 0.2.0-alpha.1
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/DesignComponent.js +150 -0
- package/dist/DesignComponent.js.map +1 -0
- package/dist/DesignFrame.js +196 -0
- package/dist/DesignFrame.js.map +1 -0
- package/dist/DesignRegion.js +83 -0
- package/dist/DesignRegion.js.map +1 -0
- package/dist/apply-url-config.js +130 -0
- package/dist/apply-url-config.js.map +1 -0
- package/dist/component.types.d.ts +87 -0
- package/dist/component.types.d.ts.map +1 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.js +0 -0
- package/dist/design-data.d.ts +983 -0
- package/dist/design-data.d.ts.map +1 -0
- package/dist/design-data.js +908 -0
- package/dist/design-data.js.map +1 -0
- package/dist/design-messaging.d.ts +2 -2
- package/dist/design-react-core.d.ts +48 -3
- package/dist/design-react-core.d.ts.map +1 -1
- package/dist/design-react-core.js +81 -2
- package/dist/design-react-core.js.map +1 -1
- package/dist/design-react.d.ts +20 -95
- package/dist/design-react.d.ts.map +1 -1
- package/dist/design-react.js +3 -485
- package/dist/design-styles.css +2 -1
- package/dist/design.d.ts +110 -2
- package/dist/design.d.ts.map +1 -0
- package/dist/events.d.ts +1 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/index.d.ts +1110 -154
- package/dist/index.d.ts.map +1 -1
- package/dist/multi-site.d.ts +154 -0
- package/dist/multi-site.d.ts.map +1 -0
- package/dist/multi-site.js +393 -0
- package/dist/multi-site.js.map +1 -0
- package/dist/routing-app-wrapper.d.ts +18 -0
- package/dist/routing-app-wrapper.d.ts.map +1 -0
- package/dist/routing-app-wrapper.js +21 -0
- package/dist/routing-app-wrapper.js.map +1 -0
- package/dist/routing.d.ts +42 -0
- package/dist/routing.d.ts.map +1 -0
- package/dist/routing.js +175 -0
- package/dist/routing.js.map +1 -0
- package/dist/scapi.d.ts +69 -5
- package/dist/scapi.d.ts.map +1 -1
- package/dist/scapi.js +1 -1
- package/dist/scapi.js.map +1 -1
- package/dist/types.d.ts +40 -13289
- package/dist/types.d.ts.map +1 -1
- package/dist/types2.d.ts +13293 -0
- package/dist/types2.d.ts.map +1 -0
- package/dist/types3.d.ts +110 -0
- package/dist/types3.d.ts.map +1 -0
- package/dist/workspace.d.ts +46 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +52 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +45 -2
- package/dist/design-react.js.map +0 -1
- package/dist/index2.d.ts +0 -1171
- package/dist/index2.d.ts.map +0 -1
- /package/{LICENSE.txt → LICENSE} +0 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import "./messaging-api.js";
|
|
2
|
+
import { i as useDesignState, o as useComponentDiscovery } from "./DesignContext.js";
|
|
3
|
+
import "./modeDetection.js";
|
|
4
|
+
import "./PageDesignerProvider.js";
|
|
5
|
+
import { i as useRegionContext, n as useComponentContext, t as ComponentContext } from "./ComponentContext.js";
|
|
6
|
+
import { i as useNodeToTargetStore, r as useComponentType, t as DesignFrame } from "./DesignFrame.js";
|
|
7
|
+
import React, { useCallback, useRef } from "react";
|
|
8
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
|
|
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;
|
|
15
|
+
const showFrame = (isSelected || isHovered) && !dragState.isDragging;
|
|
16
|
+
const isMoving = dragState.isDragging && dragState.sourceComponentId === componentId;
|
|
17
|
+
const isDropTarget = dragState.currentDropTarget?.componentId === componentId;
|
|
18
|
+
const dropTargetInsertType = dragState.currentDropTarget?.insertType;
|
|
19
|
+
const dropTargetAxis = dropTargetInsertType?.axis;
|
|
20
|
+
return [
|
|
21
|
+
"pd-design__decorator",
|
|
22
|
+
isFragment ? "pd-design__fragment" : "pd-design__component",
|
|
23
|
+
showFrame && "pd-design__frame--visible",
|
|
24
|
+
isSelected && "pd-design__decorator--selected",
|
|
25
|
+
isHovered && "pd-design__decorator--hovered",
|
|
26
|
+
isMoving && "pd-design__decorator--moving",
|
|
27
|
+
!isLocalized && "pd-design__component--unlocalized",
|
|
28
|
+
isDropTarget && dropTargetAxis && dropTargetInsertType && `pd-design__drop-target__${dropTargetAxis}-${dropTargetInsertType.type}`
|
|
29
|
+
].filter(Boolean).join(" ");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region src/design/react/hooks/useFocusedComponentHandler.ts
|
|
34
|
+
/**
|
|
35
|
+
* Focuses a component when the focused component id matches the component id.
|
|
36
|
+
* @param componentId - The id of the component to focus.
|
|
37
|
+
* @param nodeRef - The ref object to the node to focus.
|
|
38
|
+
*/
|
|
39
|
+
function useFocusedComponentHandler(componentId, nodeRef) {
|
|
40
|
+
const { focusedComponentId, focusComponent } = useDesignState();
|
|
41
|
+
React.useEffect(() => {
|
|
42
|
+
if (focusedComponentId === componentId && nodeRef.current) focusComponent(nodeRef.current);
|
|
43
|
+
}, [
|
|
44
|
+
focusedComponentId,
|
|
45
|
+
componentId,
|
|
46
|
+
focusComponent,
|
|
47
|
+
nodeRef
|
|
48
|
+
]);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
//#endregion
|
|
52
|
+
//#region src/design/react/components/DesignComponent.tsx
|
|
53
|
+
function DesignComponent(props) {
|
|
54
|
+
const { designMetadata, children } = props;
|
|
55
|
+
const { id = "", name, isFragment = false, isVisible = true, isLocalized = false } = designMetadata ?? {};
|
|
56
|
+
const componentId = id;
|
|
57
|
+
const componentType = useComponentType(componentId);
|
|
58
|
+
const componentName = componentType?.label || name || "Component";
|
|
59
|
+
const dragRef = useRef(null);
|
|
60
|
+
const { regionId } = useRegionContext() ?? {};
|
|
61
|
+
const { componentId: parentComponentId } = useComponentContext() ?? {};
|
|
62
|
+
const { nodeToTargetMap } = useDesignState();
|
|
63
|
+
const { selectedComponentId, hoveredComponentId, setSelectedComponent, setHoveredComponent, startComponentMove, setPendingComponentDragId, dragState: { pendingComponentDragId, isDragging, sourceComponentId: draggingSourceComponentId } } = useDesignState();
|
|
64
|
+
useFocusedComponentHandler(componentId, dragRef);
|
|
65
|
+
useNodeToTargetStore({
|
|
66
|
+
type: "component",
|
|
67
|
+
nodeRef: dragRef,
|
|
68
|
+
parentId: parentComponentId,
|
|
69
|
+
regionId,
|
|
70
|
+
componentId
|
|
71
|
+
});
|
|
72
|
+
const discoverComponents = useComponentDiscovery({ nodeToTargetMap });
|
|
73
|
+
const isPendingDrag = pendingComponentDragId === componentId;
|
|
74
|
+
const handleMouseEnter = useCallback((event) => {
|
|
75
|
+
event.stopPropagation();
|
|
76
|
+
setHoveredComponent(componentId);
|
|
77
|
+
}, [setHoveredComponent, componentId]);
|
|
78
|
+
const handleMouseLeave = useCallback((event) => {
|
|
79
|
+
event.stopPropagation();
|
|
80
|
+
setHoveredComponent(discoverComponents({
|
|
81
|
+
x: event.clientX,
|
|
82
|
+
y: event.clientY,
|
|
83
|
+
filter: (entry) => entry.type === "component"
|
|
84
|
+
})[0]?.componentId ?? null);
|
|
85
|
+
}, [setHoveredComponent, discoverComponents]);
|
|
86
|
+
const handleClick = useCallback((e) => {
|
|
87
|
+
e.stopPropagation();
|
|
88
|
+
setSelectedComponent(componentId);
|
|
89
|
+
}, [setSelectedComponent, componentId]);
|
|
90
|
+
const showFrame = [selectedComponentId, hoveredComponentId].includes(componentId) && !isDragging;
|
|
91
|
+
const isDraggable = Boolean(componentId && regionId && componentType?.id);
|
|
92
|
+
const classes = useComponentDecoratorClasses({
|
|
93
|
+
componentId,
|
|
94
|
+
isLocalized,
|
|
95
|
+
isFragment: Boolean(isFragment)
|
|
96
|
+
});
|
|
97
|
+
const context = React.useMemo(() => ({
|
|
98
|
+
componentId: id,
|
|
99
|
+
name
|
|
100
|
+
}), [id, name]);
|
|
101
|
+
const handleDragOver = React.useCallback((event) => {
|
|
102
|
+
if (draggingSourceComponentId !== componentId) event.preventDefault();
|
|
103
|
+
}, [draggingSourceComponentId, componentId]);
|
|
104
|
+
const handleMouseDown = React.useCallback((event) => {
|
|
105
|
+
if (componentId) {
|
|
106
|
+
event.stopPropagation();
|
|
107
|
+
setPendingComponentDragId(componentId);
|
|
108
|
+
}
|
|
109
|
+
}, [componentId, setPendingComponentDragId]);
|
|
110
|
+
const handleDragStart = React.useCallback((event) => {
|
|
111
|
+
event.stopPropagation();
|
|
112
|
+
if (componentId && regionId && componentType?.id) startComponentMove(componentId, regionId, componentType.id);
|
|
113
|
+
}, [
|
|
114
|
+
componentId,
|
|
115
|
+
regionId,
|
|
116
|
+
componentType?.id,
|
|
117
|
+
startComponentMove
|
|
118
|
+
]);
|
|
119
|
+
if (!isVisible) return /* @__PURE__ */ jsx(Fragment, {});
|
|
120
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
121
|
+
ref: dragRef,
|
|
122
|
+
className: classes,
|
|
123
|
+
draggable: isPendingDrag && isDraggable,
|
|
124
|
+
onClick: handleClick,
|
|
125
|
+
onDragOver: handleDragOver,
|
|
126
|
+
onDragStart: handleDragStart,
|
|
127
|
+
onMouseEnter: handleMouseEnter,
|
|
128
|
+
onMouseLeave: handleMouseLeave,
|
|
129
|
+
onMouseDown: handleMouseDown,
|
|
130
|
+
"data-component-type": componentType?.id,
|
|
131
|
+
"data-testid": `design-component-${componentId}`,
|
|
132
|
+
children: [/* @__PURE__ */ jsx("div", { className: "pd-design__component__drop-target" }), /* @__PURE__ */ jsx(DesignFrame, {
|
|
133
|
+
showFrame,
|
|
134
|
+
componentId,
|
|
135
|
+
localized: isLocalized,
|
|
136
|
+
name: componentName,
|
|
137
|
+
parentId: parentComponentId,
|
|
138
|
+
isMoveable: isDraggable,
|
|
139
|
+
regionId,
|
|
140
|
+
children: /* @__PURE__ */ jsx(ComponentContext.Provider, {
|
|
141
|
+
value: context,
|
|
142
|
+
children
|
|
143
|
+
})
|
|
144
|
+
})]
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
//#endregion
|
|
149
|
+
export { DesignComponent };
|
|
150
|
+
//# sourceMappingURL=DesignComponent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DesignComponent.js","names":[],"sources":["../src/design/react/hooks/useComponentDecoratorClasses.ts","../src/design/react/hooks/useFocusedComponentHandler.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 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';\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 componentName = componentType?.label || name || 'Component';\n const dragRef = useRef<HTMLDivElement>(null);\n const { regionId } = useRegionContext() ?? {};\n const { componentId: parentComponentId } = useComponentContext() ?? {};\n const { nodeToTargetMap } = useDesignState();\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\n const handleMouseEnter = useCallback(\n (event: React.MouseEvent) => {\n event.stopPropagation();\n setHoveredComponent(componentId);\n },\n [setHoveredComponent, componentId]\n );\n\n const handleMouseLeave = useCallback(\n (event: React.MouseEvent) => {\n event.stopPropagation();\n\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: event.clientX,\n y: event.clientY,\n filter: (entry) => entry.type === 'component',\n });\n\n setHoveredComponent(components[0]?.componentId ?? null);\n },\n [setHoveredComponent, discoverComponents]\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 onMouseEnter={handleMouseEnter}\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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"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { i as useDesignState, r as useDesignContext } from "./DesignContext.js";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
|
|
5
|
+
//#region src/design/react/hooks/useNodeToTargetStore.ts
|
|
6
|
+
function useNodeToTargetStore({ parentId, componentId, regionId, nodeRef, type, componentIds, componentTypeInclusions, componentTypeExclusions }) {
|
|
7
|
+
const { nodeToTargetMap } = useDesignState();
|
|
8
|
+
React.useEffect(() => {
|
|
9
|
+
if (nodeRef.current) nodeToTargetMap.set(nodeRef.current, {
|
|
10
|
+
parentId,
|
|
11
|
+
componentId,
|
|
12
|
+
regionId,
|
|
13
|
+
type,
|
|
14
|
+
componentIds,
|
|
15
|
+
componentTypeInclusions,
|
|
16
|
+
componentTypeExclusions
|
|
17
|
+
});
|
|
18
|
+
}, [
|
|
19
|
+
nodeRef,
|
|
20
|
+
parentId,
|
|
21
|
+
componentId,
|
|
22
|
+
regionId,
|
|
23
|
+
type,
|
|
24
|
+
componentIds,
|
|
25
|
+
nodeToTargetMap,
|
|
26
|
+
componentTypeInclusions,
|
|
27
|
+
componentTypeExclusions
|
|
28
|
+
]);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region src/design/react/hooks/useComponentType.ts
|
|
33
|
+
function useComponentType(componentId) {
|
|
34
|
+
const { pageDesignerConfig } = useDesignContext();
|
|
35
|
+
const { type = "" } = pageDesignerConfig?.components[componentId] ?? {};
|
|
36
|
+
return pageDesignerConfig?.componentTypes[type] ?? null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/design/react/components/DeleteToolboxButton.tsx
|
|
41
|
+
const DeleteToolboxButton = ({ title, onClick, onMouseDown = () => {} }) => /* @__PURE__ */ jsx("button", {
|
|
42
|
+
className: "pd-design__frame__toolbox-button",
|
|
43
|
+
title,
|
|
44
|
+
type: "button",
|
|
45
|
+
onMouseDown,
|
|
46
|
+
onClick,
|
|
47
|
+
children: /* @__PURE__ */ jsx("svg", {
|
|
48
|
+
className: "pd-design__frame__delete-icon",
|
|
49
|
+
viewBox: "0 0 24 24",
|
|
50
|
+
fill: "none",
|
|
51
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
52
|
+
children: /* @__PURE__ */ jsx("path", {
|
|
53
|
+
d: "M18 6L6 18M6 6l12 12",
|
|
54
|
+
stroke: "currentColor",
|
|
55
|
+
strokeWidth: "2",
|
|
56
|
+
strokeLinecap: "round",
|
|
57
|
+
strokeLinejoin: "round"
|
|
58
|
+
})
|
|
59
|
+
})
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region src/design/react/components/MoveToolboxButton.tsx
|
|
64
|
+
const MoveToolboxButton = ({ title }) => /* @__PURE__ */ jsx("button", {
|
|
65
|
+
className: "pd-design__frame__toolbox-button",
|
|
66
|
+
title,
|
|
67
|
+
type: "button",
|
|
68
|
+
children: /* @__PURE__ */ jsx("svg", {
|
|
69
|
+
className: "pd-design__frame__move-icon",
|
|
70
|
+
viewBox: "0 0 24 24",
|
|
71
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
72
|
+
children: /* @__PURE__ */ jsx("path", {
|
|
73
|
+
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",
|
|
74
|
+
stroke: "currentColor",
|
|
75
|
+
strokeWidth: "2",
|
|
76
|
+
strokeLinecap: "round",
|
|
77
|
+
strokeLinejoin: "round"
|
|
78
|
+
})
|
|
79
|
+
})
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
//#endregion
|
|
83
|
+
//#region src/design/react/hooks/useLabels.ts
|
|
84
|
+
function useLabels() {
|
|
85
|
+
const { pageDesignerConfig } = useDesignContext();
|
|
86
|
+
return pageDesignerConfig?.labels ?? {};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
//#endregion
|
|
90
|
+
//#region src/design/react/components/DesignOverlay.tsx
|
|
91
|
+
/**
|
|
92
|
+
* Copyright 2026 Salesforce, Inc.
|
|
93
|
+
*
|
|
94
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
95
|
+
* you may not use this file except in compliance with the License.
|
|
96
|
+
* You may obtain a copy of the License at
|
|
97
|
+
*
|
|
98
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
99
|
+
*
|
|
100
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
101
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
102
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
103
|
+
* See the License for the specific language governing permissions and
|
|
104
|
+
* limitations under the License.
|
|
105
|
+
*/
|
|
106
|
+
const DesignOverlay = () => {
|
|
107
|
+
return /* @__PURE__ */ jsx("div", {
|
|
108
|
+
className: "pd-design__frame__overlay",
|
|
109
|
+
children: /* @__PURE__ */ jsx("svg", {
|
|
110
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
111
|
+
x: "0px",
|
|
112
|
+
y: "0px",
|
|
113
|
+
width: "52px",
|
|
114
|
+
height: "52px",
|
|
115
|
+
viewBox: "0 0 52 52",
|
|
116
|
+
enableBackground: "new 0 0 52 52",
|
|
117
|
+
xmlSpace: "preserve",
|
|
118
|
+
children: /* @__PURE__ */ jsx("path", {
|
|
119
|
+
fill: "#FFFFFF",
|
|
120
|
+
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 C26,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 c1.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 c0,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 s-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 c4.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 c1.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"
|
|
121
|
+
})
|
|
122
|
+
})
|
|
123
|
+
});
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
//#endregion
|
|
127
|
+
//#region src/design/react/components/DesignFrame.tsx
|
|
128
|
+
const DesignFrame = ({ componentId, children, name, parentId, regionId, localized = false, showFrame = false, showToolbox = true, isMoveable = true }) => {
|
|
129
|
+
const componentType = useComponentType(componentId ?? "");
|
|
130
|
+
const { deleteComponent } = useDesignState();
|
|
131
|
+
const labels = useLabels();
|
|
132
|
+
const nodeRef = React.useRef(null);
|
|
133
|
+
const handleDelete = React.useCallback((event) => {
|
|
134
|
+
event.stopPropagation();
|
|
135
|
+
if (componentId) deleteComponent({
|
|
136
|
+
componentId,
|
|
137
|
+
sourceComponentId: parentId ?? "",
|
|
138
|
+
sourceRegionId: regionId ?? ""
|
|
139
|
+
});
|
|
140
|
+
}, [
|
|
141
|
+
deleteComponent,
|
|
142
|
+
componentId,
|
|
143
|
+
parentId,
|
|
144
|
+
regionId
|
|
145
|
+
]);
|
|
146
|
+
const stopPropagation = (event) => event.stopPropagation();
|
|
147
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
148
|
+
className: ["pd-design__frame", showFrame && "pd-design__frame--visible"].filter(Boolean).join(" "),
|
|
149
|
+
ref: nodeRef,
|
|
150
|
+
children: [
|
|
151
|
+
showFrame && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", { className: "pd-design__frame--x" }), /* @__PURE__ */ jsx("div", { className: "pd-design__frame--y" })] }),
|
|
152
|
+
/* @__PURE__ */ jsxs("div", {
|
|
153
|
+
className: "pd-design__frame__label",
|
|
154
|
+
onMouseDown: stopPropagation,
|
|
155
|
+
children: [
|
|
156
|
+
componentType?.image && /* @__PURE__ */ jsx("span", {
|
|
157
|
+
className: "pd-design__icon",
|
|
158
|
+
children: /* @__PURE__ */ jsx("img", {
|
|
159
|
+
src: componentType.image,
|
|
160
|
+
alt: ""
|
|
161
|
+
})
|
|
162
|
+
}),
|
|
163
|
+
/* @__PURE__ */ jsx("span", {
|
|
164
|
+
className: "pd-design__frame__name",
|
|
165
|
+
children: name
|
|
166
|
+
}),
|
|
167
|
+
!localized && /* @__PURE__ */ jsx("span", {
|
|
168
|
+
className: "pd-design__frame__fallback-badge",
|
|
169
|
+
children: labels.fallback ?? "Fallback"
|
|
170
|
+
})
|
|
171
|
+
]
|
|
172
|
+
}),
|
|
173
|
+
showToolbox && /* @__PURE__ */ jsxs("div", {
|
|
174
|
+
className: "pd-design__frame__toolbox",
|
|
175
|
+
children: [isMoveable && /* @__PURE__ */ jsx(MoveToolboxButton, { title: labels.moveComponent ?? "Move component" }), /* @__PURE__ */ jsx(DeleteToolboxButton, {
|
|
176
|
+
title: labels.deleteComponent ?? "Delete component",
|
|
177
|
+
onMouseDown: stopPropagation,
|
|
178
|
+
onClick: handleDelete
|
|
179
|
+
})]
|
|
180
|
+
}),
|
|
181
|
+
/* @__PURE__ */ jsx(DesignOverlay, {}),
|
|
182
|
+
children
|
|
183
|
+
]
|
|
184
|
+
});
|
|
185
|
+
};
|
|
186
|
+
DesignFrame.defaultProps = {
|
|
187
|
+
parentId: void 0,
|
|
188
|
+
componentId: void 0,
|
|
189
|
+
showToolbox: true,
|
|
190
|
+
regionId: void 0,
|
|
191
|
+
showFrame: false
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
//#endregion
|
|
195
|
+
export { useNodeToTargetStore as i, useLabels as n, useComponentType as r, DesignFrame as t };
|
|
196
|
+
//# sourceMappingURL=DesignFrame.js.map
|
|
@@ -0,0 +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}: 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}>): 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'].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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"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import "./messaging-api.js";
|
|
2
|
+
import { a as isComponentTypeAllowedInRegion, i as useDesignState } from "./DesignContext.js";
|
|
3
|
+
import "./modeDetection.js";
|
|
4
|
+
import "./PageDesignerProvider.js";
|
|
5
|
+
import { n as useComponentContext, r as RegionContext } from "./ComponentContext.js";
|
|
6
|
+
import { i as useNodeToTargetStore, n as useLabels, t as DesignFrame } from "./DesignFrame.js";
|
|
7
|
+
import React, { useCallback, useMemo } from "react";
|
|
8
|
+
import { jsx } from "react/jsx-runtime";
|
|
9
|
+
|
|
10
|
+
//#region src/design/react/hooks/useRegionDecoratorClasses.ts
|
|
11
|
+
function useRegionDecoratorClasses({ regionId, componentTypeInclusions, componentTypeExclusions }) {
|
|
12
|
+
const { dragState: { currentDropTarget, componentType } } = useDesignState();
|
|
13
|
+
const isHovered = regionId && currentDropTarget?.regionId === regionId;
|
|
14
|
+
const isComponentAllowed = useMemo(() => isComponentTypeAllowedInRegion(componentType, componentTypeInclusions, componentTypeExclusions), [
|
|
15
|
+
componentType,
|
|
16
|
+
componentTypeInclusions,
|
|
17
|
+
componentTypeExclusions
|
|
18
|
+
]);
|
|
19
|
+
return [
|
|
20
|
+
"pd-design__decorator",
|
|
21
|
+
"pd-design__region",
|
|
22
|
+
isHovered && isComponentAllowed && "pd-design__region--hovered pd-design__frame--visible"
|
|
23
|
+
].filter(Boolean).join(" ");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region src/design/react/components/DesignRegion.tsx
|
|
28
|
+
function DesignRegion(props) {
|
|
29
|
+
const { designMetadata, children } = props;
|
|
30
|
+
const { name, id = "", componentIds = [], componentTypeInclusions = [], componentTypeExclusions = [] } = designMetadata ?? {};
|
|
31
|
+
const nodeRef = React.useRef(null);
|
|
32
|
+
const classes = useRegionDecoratorClasses({
|
|
33
|
+
regionId: id,
|
|
34
|
+
componentTypeInclusions,
|
|
35
|
+
componentTypeExclusions
|
|
36
|
+
});
|
|
37
|
+
const { dragState } = useDesignState();
|
|
38
|
+
const labels = useLabels();
|
|
39
|
+
const showFrame = Boolean(id && dragState.currentDropTarget?.regionId === id);
|
|
40
|
+
const { componentId: parentComponentId } = useComponentContext() ?? {};
|
|
41
|
+
useNodeToTargetStore({
|
|
42
|
+
type: "region",
|
|
43
|
+
nodeRef,
|
|
44
|
+
parentId: parentComponentId,
|
|
45
|
+
componentIds,
|
|
46
|
+
componentId: parentComponentId ?? "",
|
|
47
|
+
regionId: id,
|
|
48
|
+
componentTypeInclusions,
|
|
49
|
+
componentTypeExclusions
|
|
50
|
+
});
|
|
51
|
+
const context = React.useMemo(() => ({
|
|
52
|
+
regionId: id,
|
|
53
|
+
componentIds
|
|
54
|
+
}), [id, componentIds]);
|
|
55
|
+
return /* @__PURE__ */ jsx("div", {
|
|
56
|
+
className: classes,
|
|
57
|
+
ref: nodeRef,
|
|
58
|
+
onDragOver: useCallback((event) => {
|
|
59
|
+
if (isComponentTypeAllowedInRegion(dragState.componentType, componentTypeInclusions, componentTypeExclusions)) event.preventDefault();
|
|
60
|
+
}, [
|
|
61
|
+
dragState.componentType,
|
|
62
|
+
componentTypeInclusions,
|
|
63
|
+
componentTypeExclusions
|
|
64
|
+
]),
|
|
65
|
+
"data-region-id": id,
|
|
66
|
+
children: /* @__PURE__ */ jsx(DesignFrame, {
|
|
67
|
+
name: name ?? labels.defaultRegionName ?? "Region",
|
|
68
|
+
parentId: parentComponentId,
|
|
69
|
+
regionId: id,
|
|
70
|
+
localized: true,
|
|
71
|
+
showFrame,
|
|
72
|
+
showToolbox: false,
|
|
73
|
+
children: /* @__PURE__ */ jsx(RegionContext.Provider, {
|
|
74
|
+
value: context,
|
|
75
|
+
children
|
|
76
|
+
})
|
|
77
|
+
})
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
//#endregion
|
|
82
|
+
export { DesignRegion };
|
|
83
|
+
//# sourceMappingURL=DesignRegion.js.map
|
|
@@ -0,0 +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 } = props;\n const {\n name,\n id = '',\n componentIds = [],\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 { componentId: parentComponentId } = useComponentContext() ?? {};\n\n useNodeToTargetStore({\n type: 'region',\n nodeRef,\n parentId: parentComponentId,\n componentIds,\n componentId: parentComponentId ?? '',\n regionId: id,\n componentTypeInclusions,\n componentTypeExclusions,\n });\n\n const context = React.useMemo<RegionContextType>(() => ({ regionId: id, componentIds }), [id, componentIds]);\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 parentId={parentComponentId}\n regionId={id}\n localized\n showFrame={showFrame}\n showToolbox={false}>\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,aAAa;CACrC,MAAM,EACF,MACA,KAAK,IACL,eAAe,EAAE,EACjB,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,aAAa,sBAAsB,qBAAqB,IAAI,EAAE;AAEtE,sBAAqB;EACjB,MAAM;EACN;EACA,UAAU;EACV;EACA,aAAa,qBAAqB;EAClC,UAAU;EACV;EACA;EACH,CAAC;CAEF,MAAM,UAAU,MAAM,eAAkC;EAAE,UAAU;EAAI;EAAc,GAAG,CAAC,IAAI,aAAa,CAAC;AAiB5G,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,UAAU;GACV;GACW;GACX,aAAa;aACb,oBAAC,cAAc;IAAS,OAAO;IAAU;KAAkC;IACjE;GACZ"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
//#region src/utils/index.ts
|
|
2
|
+
/**
|
|
3
|
+
* Copyright 2026 Salesforce, Inc.
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Creates a matcher function from an array of path patterns.
|
|
19
|
+
* Supports `/**` suffix wildcards (e.g. '/resource/**', '/action/**').
|
|
20
|
+
* Exact paths without wildcards are matched literally.
|
|
21
|
+
*/
|
|
22
|
+
function createPatternMatcher(patterns) {
|
|
23
|
+
const exactMatches = /* @__PURE__ */ new Set();
|
|
24
|
+
const prefixPatterns = [];
|
|
25
|
+
for (const pattern of patterns) if (pattern.endsWith("/**")) prefixPatterns.push(pattern.slice(0, -3));
|
|
26
|
+
else exactMatches.add(pattern);
|
|
27
|
+
return (path) => {
|
|
28
|
+
if (exactMatches.has(path)) return true;
|
|
29
|
+
return prefixPatterns.some((prefix) => path === prefix || path.startsWith(`${prefix}/`));
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
//#endregion
|
|
34
|
+
//#region src/multi-site/apply-url-config.ts
|
|
35
|
+
const DEFAULT_EXCLUDED_ROUTES = ["/resource/**", "/action/**"];
|
|
36
|
+
/**
|
|
37
|
+
* Separates routes into excluded (stay at root) and included (go under prefix).
|
|
38
|
+
*/
|
|
39
|
+
function partitionRoutes(routes, excludePatterns) {
|
|
40
|
+
const isExcluded = createPatternMatcher(excludePatterns);
|
|
41
|
+
const excludedRoutes = [];
|
|
42
|
+
const includedRoutes = [];
|
|
43
|
+
for (const route of routes) {
|
|
44
|
+
const matchPath = route.path?.startsWith("/") ? route.path : `/${route.path}`;
|
|
45
|
+
if (route.path && isExcluded(matchPath)) excludedRoutes.push(route);
|
|
46
|
+
else includedRoutes.push(route);
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
excludedRoutes,
|
|
50
|
+
includedRoutes
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Normalizes route paths by stripping leading `/` so they're relative under a
|
|
55
|
+
* parent route (React Router requirement).
|
|
56
|
+
*/
|
|
57
|
+
function normalizeRoutePaths(routes) {
|
|
58
|
+
return routes.map((route) => ({
|
|
59
|
+
...route,
|
|
60
|
+
path: route.path?.startsWith("/") ? route.path.slice(1) : route.path
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Creates the `multi-site-wrapper` parent route entry with the given prefix.
|
|
65
|
+
*/
|
|
66
|
+
function createPrefixWrapper(prefix, children, wrapperFile) {
|
|
67
|
+
return {
|
|
68
|
+
id: "multi-site-wrapper",
|
|
69
|
+
file: wrapperFile,
|
|
70
|
+
path: prefix.slice(1),
|
|
71
|
+
children
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Finds the root index route (`/`) and duplicates it with its parent layout.
|
|
76
|
+
* Looks at the top level for pathless layouts whose direct children include an index route.
|
|
77
|
+
* e.g. _app (pathless) → _app._index (index: true)
|
|
78
|
+
* Returns: _app--root-duplicate → _app._index--root-duplicate
|
|
79
|
+
*/
|
|
80
|
+
function cloneRootIndexRoutes(routes) {
|
|
81
|
+
const duplicates = [];
|
|
82
|
+
for (const route of routes) if (route.index === true) duplicates.push({
|
|
83
|
+
...route,
|
|
84
|
+
id: `${route.id}--root-duplicate`
|
|
85
|
+
});
|
|
86
|
+
else if (!route.path && route.children) {
|
|
87
|
+
const indexChild = route.children.find((child) => child.index === true);
|
|
88
|
+
if (indexChild) duplicates.push({
|
|
89
|
+
...route,
|
|
90
|
+
id: `${route.id}--root-duplicate`,
|
|
91
|
+
children: [{
|
|
92
|
+
...indexChild,
|
|
93
|
+
id: `${indexChild.id}--root-duplicate`
|
|
94
|
+
}]
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
return duplicates;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Applies multi-site URL configuration to a set of route entries.
|
|
101
|
+
*
|
|
102
|
+
* Wraps non-excluded routes under a parent route with the configured URL prefix
|
|
103
|
+
* (e.g. `/:siteId/:localeId`), while keeping excluded routes (action/resource by default)
|
|
104
|
+
* at the root level. The homepage index route (and its parent layout) is always
|
|
105
|
+
* duplicated at `/` so the root URL still serves content.
|
|
106
|
+
*
|
|
107
|
+
* @param options - Configuration for URL customisation.
|
|
108
|
+
* @param options.routes - The flat route entries discovered from the filesystem.
|
|
109
|
+
* @param options.urlConfig - URL customisation configuration (prefix, excludeRoutes).
|
|
110
|
+
* @param options.wrapperFile - Path to the wrapper component file, relative to appDirectory.
|
|
111
|
+
* @returns The transformed route entries with prefix wrapping applied.
|
|
112
|
+
*/
|
|
113
|
+
function applyUrlConfig(options) {
|
|
114
|
+
const { routes, urlConfig, wrapperFile } = options;
|
|
115
|
+
if (!urlConfig) return routes;
|
|
116
|
+
if (!urlConfig.prefix?.startsWith("/")) throw new Error(`urlConfig.prefix must start with a leading slash ("/"). Received: "${urlConfig.prefix}"`);
|
|
117
|
+
if (urlConfig.prefix === "/") return routes;
|
|
118
|
+
const { excludedRoutes, includedRoutes } = partitionRoutes(routes, urlConfig.excludeRoutes ?? DEFAULT_EXCLUDED_ROUTES);
|
|
119
|
+
const wrappableRoutes = normalizeRoutePaths(includedRoutes);
|
|
120
|
+
const wrapperRoute = createPrefixWrapper(urlConfig.prefix, wrappableRoutes, wrapperFile);
|
|
121
|
+
return [
|
|
122
|
+
...cloneRootIndexRoutes(includedRoutes),
|
|
123
|
+
wrapperRoute,
|
|
124
|
+
...excludedRoutes
|
|
125
|
+
];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
//#endregion
|
|
129
|
+
export { applyUrlConfig as t };
|
|
130
|
+
//# sourceMappingURL=apply-url-config.js.map
|