@growgroup/visual-editor 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/NOTICE +52 -0
- package/README.md +143 -0
- package/dist/editor.css +2 -0
- package/package.json +94 -0
- package/src/components/DeckSlideRender.tsx +21 -0
- package/src/components/SaveNote.tsx +21 -0
- package/src/components/auth/AuthProvider.tsx +44 -0
- package/src/components/ui/accordion.tsx +57 -0
- package/src/components/ui/alert-dialog.tsx +141 -0
- package/src/components/ui/badge.tsx +36 -0
- package/src/components/ui/button.tsx +56 -0
- package/src/components/ui/card.tsx +76 -0
- package/src/components/ui/checkbox.tsx +30 -0
- package/src/components/ui/collapsible.tsx +11 -0
- package/src/components/ui/context-menu.tsx +200 -0
- package/src/components/ui/dialog.tsx +122 -0
- package/src/components/ui/dropdown-menu.tsx +201 -0
- package/src/components/ui/input.tsx +22 -0
- package/src/components/ui/label.tsx +26 -0
- package/src/components/ui/popover.tsx +33 -0
- package/src/components/ui/scroll-area.tsx +48 -0
- package/src/components/ui/scrubbable-label.tsx +95 -0
- package/src/components/ui/select.tsx +159 -0
- package/src/components/ui/separator.tsx +31 -0
- package/src/components/ui/slider.tsx +28 -0
- package/src/components/ui/switch.tsx +29 -0
- package/src/components/ui/tabs.tsx +55 -0
- package/src/components/ui/textarea.tsx +22 -0
- package/src/components/ui/tooltip.tsx +32 -0
- package/src/components/viewer/useDeck.ts +58 -0
- package/src/editor/EditorContext.tsx +632 -0
- package/src/editor/EditorToolbar.tsx +446 -0
- package/src/editor/FrontendVisualEditor.tsx +2595 -0
- package/src/editor/autosave.ts +34 -0
- package/src/editor/components/AiPromptPopover.tsx +378 -0
- package/src/editor/components/BreakpointGuides.tsx +323 -0
- package/src/editor/components/BreakpointSelector.tsx +180 -0
- package/src/editor/components/ComponentPropertyEditor.tsx +426 -0
- package/src/editor/components/CssEditorDialog.tsx +168 -0
- package/src/editor/components/EditorCanvas.tsx +987 -0
- package/src/editor/components/EditorContextMenu.tsx +515 -0
- package/src/editor/components/EditorFooter.tsx +81 -0
- package/src/editor/components/EditorHeader.tsx +524 -0
- package/src/editor/components/EditorLayerPanel.tsx +1197 -0
- package/src/editor/components/EditorPropertyPanel.tsx +2996 -0
- package/src/editor/components/EditorSidebar.tsx +226 -0
- package/src/editor/components/FigmaColorPicker.tsx +1577 -0
- package/src/editor/components/HtmlEditorDialog.tsx +71 -0
- package/src/editor/components/HtmlImportDialog.tsx +346 -0
- package/src/editor/components/JsEditorDialog.tsx +175 -0
- package/src/editor/components/LayerTreeItem.tsx +494 -0
- package/src/editor/components/MasterComponentEditor.tsx +846 -0
- package/src/editor/components/MediaLibraryDialog.tsx +447 -0
- package/src/editor/components/PageSettingsDialog.tsx +776 -0
- package/src/editor/components/VariablePicker.tsx +355 -0
- package/src/editor/components/VariablesDialog.tsx +564 -0
- package/src/editor/components/VariantEditor.tsx +687 -0
- package/src/editor/components/VariantMatrix.tsx +507 -0
- package/src/editor/components/component-panel/CategoryAccordion.tsx +139 -0
- package/src/editor/components/component-panel/ComponentGrid.tsx +51 -0
- package/src/editor/components/component-panel/ComponentItem.tsx +368 -0
- package/src/editor/components/component-panel/ComponentPanel.tsx +275 -0
- package/src/editor/components/component-panel/ComponentSearch.tsx +82 -0
- package/src/editor/components/component-panel/VariantList.tsx +260 -0
- package/src/editor/components/component-panel/index.ts +12 -0
- package/src/editor/components/index.ts +48 -0
- package/src/editor/components/multi-page/CanvasZoomControls.tsx +71 -0
- package/src/editor/components/multi-page/InfiniteCanvas.tsx +95 -0
- package/src/editor/components/multi-page/MultiPageCanvasView.tsx +1404 -0
- package/src/editor/components/multi-page/PageFrameOverlay.tsx +76 -0
- package/src/editor/components/multi-page/PageLabel.tsx +34 -0
- package/src/editor/components/multi-page/PageLivePreview.tsx +242 -0
- package/src/editor/components/multi-page/PageThumbnail.tsx +59 -0
- package/src/editor/components/multi-page/index.ts +7 -0
- package/src/editor/components/ppt/PptChrome.tsx +2508 -0
- package/src/editor/components/ppt/PptComments.tsx +589 -0
- package/src/editor/components/ppt/PptDesignProposals.tsx +275 -0
- package/src/editor/components/ppt/PptFormatPane.tsx +320 -0
- package/src/editor/components/ppt/PptNotes.tsx +246 -0
- package/src/editor/components/property-panel/AlignmentPanel.tsx +110 -0
- package/src/editor/components/property-panel/AutoLayoutPanel.tsx +615 -0
- package/src/editor/components/property-panel/BorderSection.tsx +104 -0
- package/src/editor/components/property-panel/CompactNumberInput.tsx +237 -0
- package/src/editor/components/property-panel/CompactSizeInput.tsx +148 -0
- package/src/editor/components/property-panel/EffectSection.tsx +157 -0
- package/src/editor/components/property-panel/FillSection.tsx +69 -0
- package/src/editor/components/property-panel/GoogleFontPicker.tsx +192 -0
- package/src/editor/components/property-panel/ImageSection.tsx +206 -0
- package/src/editor/components/property-panel/ImgSrcSection.tsx +125 -0
- package/src/editor/components/property-panel/InstanceOverrideSection.tsx +413 -0
- package/src/editor/components/property-panel/LayoutSection.tsx +105 -0
- package/src/editor/components/property-panel/LinkSection.tsx +103 -0
- package/src/editor/components/property-panel/PropertyVariablePicker.tsx +311 -0
- package/src/editor/components/property-panel/ScalePanel.tsx +212 -0
- package/src/editor/components/property-panel/TypographySection.tsx +230 -0
- package/src/editor/components/property-panel/UnitAwareNumberInput.tsx +238 -0
- package/src/editor/components/property-panel/VariableAwareColorInput.tsx +477 -0
- package/src/editor/components/property-panel/VariableAwareInput.tsx +606 -0
- package/src/editor/components/property-panel/VariableAwareSizeInput.tsx +588 -0
- package/src/editor/components/property-panel/VariableAwareUnitInput.tsx +546 -0
- package/src/editor/components/property-panel/index.ts +24 -0
- package/src/editor/components/property-panel/unit-utils.ts +261 -0
- package/src/editor/components/shell/EditorTopBar.tsx +392 -0
- package/src/editor/components/shell/LeftPanel.tsx +47 -0
- package/src/editor/components/shell/PagesPanel.tsx +118 -0
- package/src/editor/components/variables-panel/ColorValueCell.tsx +165 -0
- package/src/editor/components/variables-panel/TextValueCell.tsx +116 -0
- package/src/editor/components/variables-panel/VariablesPanel.tsx +518 -0
- package/src/editor/components/variables-panel/index.ts +7 -0
- package/src/editor/constants.ts +468 -0
- package/src/editor/contexts/EditorArtboardContext.tsx +229 -0
- package/src/editor/contexts/EditorComponentsContext.tsx +1320 -0
- package/src/editor/contexts/EditorDocumentContext.tsx +113 -0
- package/src/editor/contexts/EditorHistoryContext.tsx +516 -0
- package/src/editor/contexts/EditorRefsContext.tsx +204 -0
- package/src/editor/contexts/EditorSelectionContext.tsx +54 -0
- package/src/editor/contexts/EditorToolContext.tsx +58 -0
- package/src/editor/contexts/EditorUIStateContext.tsx +122 -0
- package/src/editor/contexts/EditorVariablesContext.tsx +397 -0
- package/src/editor/contexts/EditorViewContext.tsx +101 -0
- package/src/editor/contexts/MultiPageCanvasContext.tsx +498 -0
- package/src/editor/contexts/index.ts +17 -0
- package/src/editor/editor-skin.css +122 -0
- package/src/editor/hooks/index.ts +38 -0
- package/src/editor/hooks/useAiReplace.ts +397 -0
- package/src/editor/hooks/useAltMeasure.ts +67 -0
- package/src/editor/hooks/useBrowserZoomPrevention.ts +121 -0
- package/src/editor/hooks/useCanvasControls.ts +1222 -0
- package/src/editor/hooks/useComponentEditMode.ts +658 -0
- package/src/editor/hooks/useComponentInstances.ts +515 -0
- package/src/editor/hooks/useContextMenuHandler.ts +157 -0
- package/src/editor/hooks/useCoordinateTransform.ts +207 -0
- package/src/editor/hooks/useDragResize.ts +1317 -0
- package/src/editor/hooks/useDrawingMode.ts +472 -0
- package/src/editor/hooks/useEditorColors.ts +144 -0
- package/src/editor/hooks/useEditorMessages.ts +108 -0
- package/src/editor/hooks/useElementActions.ts +1754 -0
- package/src/editor/hooks/useElementSelection.ts +1573 -0
- package/src/editor/hooks/useFocusManagement.ts +249 -0
- package/src/editor/hooks/useGoogleFonts.ts +305 -0
- package/src/editor/hooks/useIframeInitializer.ts +584 -0
- package/src/editor/hooks/useIframeSetup.ts +360 -0
- package/src/editor/hooks/useImageUpload.ts +270 -0
- package/src/editor/hooks/useInfiniteCanvas.ts +301 -0
- package/src/editor/hooks/useKeyboardShortcuts.ts +205 -0
- package/src/editor/hooks/useMarqueeSelection.ts +388 -0
- package/src/editor/hooks/useMediaLibrary.ts +129 -0
- package/src/editor/hooks/usePageSettingsManager.ts +739 -0
- package/src/editor/hooks/useResizablePanel.ts +163 -0
- package/src/editor/hooks/useRichPaste.ts +255 -0
- package/src/editor/hooks/useTouchGestures.ts +197 -0
- package/src/editor/index.ts +27 -0
- package/src/editor/types/page-settings.ts +513 -0
- package/src/editor/types.ts +382 -0
- package/src/editor/utils/align-elements.ts +169 -0
- package/src/editor/utils/api-json.ts +21 -0
- package/src/editor/utils/aspect-lock.ts +50 -0
- package/src/editor/utils/component-renderer.ts +702 -0
- package/src/editor/utils/component-sync.ts +845 -0
- package/src/editor/utils/crop-mode.ts +265 -0
- package/src/editor/utils/dom-utils.ts +2238 -0
- package/src/editor/utils/element-effects.ts +218 -0
- package/src/editor/utils/eyedropper.ts +35 -0
- package/src/editor/utils/figma-export.ts +560 -0
- package/src/editor/utils/figma-kiwi-decoder.ts +722 -0
- package/src/editor/utils/figma-kiwi-encoder.ts +583 -0
- package/src/editor/utils/figma-paste.ts +2262 -0
- package/src/editor/utils/flex-reorder.ts +231 -0
- package/src/editor/utils/flex-utils.ts +1230 -0
- package/src/editor/utils/geometry.ts +232 -0
- package/src/editor/utils/grid-layout.ts +210 -0
- package/src/editor/utils/html-utils.ts +470 -0
- package/src/editor/utils/index.ts +11 -0
- package/src/editor/utils/ink-style.ts +57 -0
- package/src/editor/utils/inline-format.ts +236 -0
- package/src/editor/utils/measure-distance.ts +120 -0
- package/src/editor/utils/override-apply.ts +526 -0
- package/src/editor/utils/override-detection.ts +613 -0
- package/src/editor/utils/paste-processors.ts +609 -0
- package/src/editor/utils/paste-sanitizer.ts +244 -0
- package/src/editor/utils/restore-flow.ts +188 -0
- package/src/editor/utils/shape-library.ts +171 -0
- package/src/editor/utils/slide-root.ts +29 -0
- package/src/editor/utils/smart-guides.ts +218 -0
- package/src/editor/utils/style-utils.ts +685 -0
- package/src/editor/utils/table-edit.ts +151 -0
- package/src/editor/utils/tailwind-mappings.ts +778 -0
- package/src/editor/utils/tailwind-utils.ts +804 -0
- package/src/editor/utils/text-highlight.ts +216 -0
- package/src/editor/utils/theme-to-variables.ts +341 -0
- package/src/editor/utils/variant-resolver.ts +456 -0
- package/src/editor/utils/viewport-utils.ts +295 -0
- package/src/hooks/useEditorHistory.ts +195 -0
- package/src/hooks/useEditorShortcuts.ts +260 -0
- package/src/index.ts +40 -0
- package/src/io.ts +134 -0
- package/src/lib/agent/slide-agent/types.ts +547 -0
- package/src/lib/agent/website-agent/types.ts +805 -0
- package/src/lib/api/auth-fetch.ts +58 -0
- package/src/lib/deck.ts +69 -0
- package/src/lib/export.ts +47 -0
- package/src/lib/firebase/config.ts +29 -0
- package/src/lib/firebase/css-variables.ts +378 -0
- package/src/lib/firebase/editor-components.ts +677 -0
- package/src/lib/firebase/storage.ts +469 -0
- package/src/lib/utils.ts +6 -0
- package/src/styles/editor.css +52 -0
- package/src/styles/skin.css +122 -0
- package/src/types/css-variables.ts +645 -0
- package/src/types/editor-components.ts +944 -0
- package/src/types/editor.ts +420 -0
- package/src/types/page-master.ts +197 -0
- package/src/types/page.ts +419 -0
- package/src/types/slide.ts +402 -0
- package/src/types/website-theme.ts +788 -0
- package/src/types/website.ts +374 -0
- package/src/vendor/firebase-firestore.ts +13 -0
- package/src/vendor/firebase-functions.ts +17 -0
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Override Apply Utilities
|
|
3
|
+
*
|
|
4
|
+
* Functions to apply, reset, and manage overrides on component instances.
|
|
5
|
+
* This module handles the application of overrides to both ComponentElement trees
|
|
6
|
+
* and DOM elements.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type {
|
|
10
|
+
ComponentElement,
|
|
11
|
+
ComponentOverride,
|
|
12
|
+
OverrideType,
|
|
13
|
+
ComponentInstance,
|
|
14
|
+
OverrideImageValue,
|
|
15
|
+
OverrideInstanceSwapValue,
|
|
16
|
+
} from '../../types/editor-components';
|
|
17
|
+
import { findElementById, findElementByPath } from './override-detection';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Deep clone a ComponentElement tree
|
|
21
|
+
*/
|
|
22
|
+
export function cloneComponentElement(element: ComponentElement): ComponentElement {
|
|
23
|
+
return {
|
|
24
|
+
...element,
|
|
25
|
+
attributes: { ...element.attributes },
|
|
26
|
+
styles: { ...element.styles },
|
|
27
|
+
overridable: { ...element.overridable },
|
|
28
|
+
children: element.children.map(cloneComponentElement),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Apply a single override to a ComponentElement (mutates the element in place)
|
|
34
|
+
*/
|
|
35
|
+
export function applyOverride(
|
|
36
|
+
element: ComponentElement,
|
|
37
|
+
override: ComponentOverride
|
|
38
|
+
): void {
|
|
39
|
+
// Find the target element
|
|
40
|
+
let targetElement: ComponentElement | null = null;
|
|
41
|
+
|
|
42
|
+
if (override.targetElementId) {
|
|
43
|
+
targetElement = findElementById(element, override.targetElementId);
|
|
44
|
+
} else if (override.elementPath) {
|
|
45
|
+
targetElement = findElementByPath(element, override.elementPath);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!targetElement) {
|
|
49
|
+
console.warn(
|
|
50
|
+
`[applyOverride] Target element not found for override:`,
|
|
51
|
+
override.id
|
|
52
|
+
);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Apply the override based on type
|
|
57
|
+
switch (override.type) {
|
|
58
|
+
case 'text':
|
|
59
|
+
if (typeof override.value === 'string') {
|
|
60
|
+
targetElement.textContent = override.value;
|
|
61
|
+
}
|
|
62
|
+
break;
|
|
63
|
+
|
|
64
|
+
case 'fill':
|
|
65
|
+
if (typeof override.value === 'string') {
|
|
66
|
+
targetElement.styles.backgroundColor = override.value;
|
|
67
|
+
}
|
|
68
|
+
break;
|
|
69
|
+
|
|
70
|
+
case 'stroke':
|
|
71
|
+
if (typeof override.value === 'string') {
|
|
72
|
+
targetElement.styles.border = override.value;
|
|
73
|
+
}
|
|
74
|
+
break;
|
|
75
|
+
|
|
76
|
+
case 'visibility':
|
|
77
|
+
if (typeof override.value === 'boolean') {
|
|
78
|
+
targetElement.hidden = !override.value;
|
|
79
|
+
}
|
|
80
|
+
break;
|
|
81
|
+
|
|
82
|
+
case 'image':
|
|
83
|
+
if (isOverrideImageValue(override.value)) {
|
|
84
|
+
if (targetElement.tagName === 'img') {
|
|
85
|
+
targetElement.attributes.src = override.value.url;
|
|
86
|
+
if (override.value.alt) {
|
|
87
|
+
targetElement.attributes.alt = override.value.alt;
|
|
88
|
+
}
|
|
89
|
+
if (override.value.objectFit) {
|
|
90
|
+
targetElement.styles.objectFit = override.value.objectFit;
|
|
91
|
+
}
|
|
92
|
+
if (override.value.objectPosition) {
|
|
93
|
+
targetElement.styles.objectPosition = override.value.objectPosition;
|
|
94
|
+
}
|
|
95
|
+
} else {
|
|
96
|
+
// Apply as background image
|
|
97
|
+
targetElement.styles.backgroundImage = `url('${override.value.url}')`;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
break;
|
|
101
|
+
|
|
102
|
+
case 'style':
|
|
103
|
+
if (typeof override.value === 'object' && !Array.isArray(override.value)) {
|
|
104
|
+
const styleOverrides = override.value as Record<string, string>;
|
|
105
|
+
Object.assign(targetElement.styles, styleOverrides);
|
|
106
|
+
}
|
|
107
|
+
break;
|
|
108
|
+
|
|
109
|
+
case 'attribute':
|
|
110
|
+
if (typeof override.value === 'object' && !Array.isArray(override.value)) {
|
|
111
|
+
const attrOverrides = override.value as Record<string, string>;
|
|
112
|
+
Object.assign(targetElement.attributes, attrOverrides);
|
|
113
|
+
}
|
|
114
|
+
break;
|
|
115
|
+
|
|
116
|
+
case 'children':
|
|
117
|
+
if (Array.isArray(override.value)) {
|
|
118
|
+
targetElement.children = override.value as ComponentElement[];
|
|
119
|
+
}
|
|
120
|
+
break;
|
|
121
|
+
|
|
122
|
+
case 'instanceSwap':
|
|
123
|
+
// Instance swap is handled at a higher level (requires loading the swapped component)
|
|
124
|
+
console.log('[applyOverride] Instance swap override detected:', override.value);
|
|
125
|
+
break;
|
|
126
|
+
|
|
127
|
+
default:
|
|
128
|
+
console.warn('[applyOverride] Unknown override type:', override.type);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Apply all overrides to a ComponentElement tree (returns a new tree)
|
|
134
|
+
*/
|
|
135
|
+
export function applyOverrides(
|
|
136
|
+
element: ComponentElement,
|
|
137
|
+
overrides: ComponentOverride[]
|
|
138
|
+
): ComponentElement {
|
|
139
|
+
// Create a deep clone to avoid mutating the original
|
|
140
|
+
const clonedElement = cloneComponentElement(element);
|
|
141
|
+
|
|
142
|
+
// Sort overrides by depth (process deeper elements first to avoid conflicts)
|
|
143
|
+
const sortedOverrides = [...overrides].sort((a, b) => {
|
|
144
|
+
const depthA = (a.elementPath?.split('.').length || 0);
|
|
145
|
+
const depthB = (b.elementPath?.split('.').length || 0);
|
|
146
|
+
return depthB - depthA;
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Apply each override
|
|
150
|
+
for (const override of sortedOverrides) {
|
|
151
|
+
applyOverride(clonedElement, override);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return clonedElement;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Apply a single override to a DOM element
|
|
159
|
+
*/
|
|
160
|
+
export function applyOverrideToDOM(
|
|
161
|
+
domElement: HTMLElement,
|
|
162
|
+
override: ComponentOverride
|
|
163
|
+
): void {
|
|
164
|
+
switch (override.type) {
|
|
165
|
+
case 'text':
|
|
166
|
+
if (typeof override.value === 'string') {
|
|
167
|
+
// Find and update only direct text nodes
|
|
168
|
+
const textNodes = Array.from(domElement.childNodes).filter(
|
|
169
|
+
(node) => node.nodeType === Node.TEXT_NODE
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
if (textNodes.length > 0) {
|
|
173
|
+
textNodes[0].textContent = override.value;
|
|
174
|
+
} else {
|
|
175
|
+
// If no text nodes exist, prepend one
|
|
176
|
+
const textNode = domElement.ownerDocument.createTextNode(override.value);
|
|
177
|
+
domElement.insertBefore(textNode, domElement.firstChild);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
break;
|
|
181
|
+
|
|
182
|
+
case 'fill':
|
|
183
|
+
if (typeof override.value === 'string') {
|
|
184
|
+
domElement.style.backgroundColor = override.value;
|
|
185
|
+
}
|
|
186
|
+
break;
|
|
187
|
+
|
|
188
|
+
case 'stroke':
|
|
189
|
+
if (typeof override.value === 'string') {
|
|
190
|
+
if (override.value === 'none') {
|
|
191
|
+
domElement.style.border = 'none';
|
|
192
|
+
} else {
|
|
193
|
+
domElement.style.border = override.value;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
break;
|
|
197
|
+
|
|
198
|
+
case 'visibility':
|
|
199
|
+
if (typeof override.value === 'boolean') {
|
|
200
|
+
domElement.style.display = override.value ? '' : 'none';
|
|
201
|
+
domElement.style.visibility = override.value ? '' : 'hidden';
|
|
202
|
+
}
|
|
203
|
+
break;
|
|
204
|
+
|
|
205
|
+
case 'image':
|
|
206
|
+
if (isOverrideImageValue(override.value)) {
|
|
207
|
+
if (domElement.tagName === 'IMG') {
|
|
208
|
+
(domElement as HTMLImageElement).src = override.value.url;
|
|
209
|
+
if (override.value.alt) {
|
|
210
|
+
(domElement as HTMLImageElement).alt = override.value.alt;
|
|
211
|
+
}
|
|
212
|
+
if (override.value.objectFit) {
|
|
213
|
+
domElement.style.objectFit = override.value.objectFit;
|
|
214
|
+
}
|
|
215
|
+
if (override.value.objectPosition) {
|
|
216
|
+
domElement.style.objectPosition = override.value.objectPosition;
|
|
217
|
+
}
|
|
218
|
+
} else {
|
|
219
|
+
domElement.style.backgroundImage = `url('${override.value.url}')`;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
break;
|
|
223
|
+
|
|
224
|
+
case 'style':
|
|
225
|
+
if (typeof override.value === 'object' && !Array.isArray(override.value)) {
|
|
226
|
+
const styleOverrides = override.value as Record<string, string>;
|
|
227
|
+
for (const [key, value] of Object.entries(styleOverrides)) {
|
|
228
|
+
domElement.style.setProperty(key, value);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
break;
|
|
232
|
+
|
|
233
|
+
case 'attribute':
|
|
234
|
+
if (typeof override.value === 'object' && !Array.isArray(override.value)) {
|
|
235
|
+
const attrOverrides = override.value as Record<string, string>;
|
|
236
|
+
for (const [key, value] of Object.entries(attrOverrides)) {
|
|
237
|
+
domElement.setAttribute(key, value);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
break;
|
|
241
|
+
|
|
242
|
+
case 'children':
|
|
243
|
+
// Children replacement in DOM would require more complex handling
|
|
244
|
+
console.warn('[applyOverrideToDOM] Children override not supported in DOM mode');
|
|
245
|
+
break;
|
|
246
|
+
|
|
247
|
+
case 'instanceSwap':
|
|
248
|
+
console.log('[applyOverrideToDOM] Instance swap requires higher-level handling');
|
|
249
|
+
break;
|
|
250
|
+
|
|
251
|
+
default:
|
|
252
|
+
console.warn('[applyOverrideToDOM] Unknown override type:', override.type);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Mark element as having an override (for visual indication)
|
|
256
|
+
domElement.setAttribute('data-has-override', 'true');
|
|
257
|
+
domElement.setAttribute(`data-override-${override.type}`, 'true');
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Apply all overrides to DOM elements within a container
|
|
262
|
+
*/
|
|
263
|
+
export function applyOverridesToDOM(
|
|
264
|
+
container: HTMLElement,
|
|
265
|
+
overrides: ComponentOverride[]
|
|
266
|
+
): void {
|
|
267
|
+
for (const override of overrides) {
|
|
268
|
+
let targetElement: HTMLElement | null = null;
|
|
269
|
+
|
|
270
|
+
if (override.targetElementId) {
|
|
271
|
+
targetElement = container.querySelector(
|
|
272
|
+
`[data-element-id="${override.targetElementId}"]`
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (targetElement) {
|
|
277
|
+
applyOverrideToDOM(targetElement, override);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Reset a specific override on an instance (returns a new instance)
|
|
284
|
+
*/
|
|
285
|
+
export function resetOverride(
|
|
286
|
+
instance: ComponentInstance,
|
|
287
|
+
overrideId: string
|
|
288
|
+
): ComponentInstance {
|
|
289
|
+
return {
|
|
290
|
+
...instance,
|
|
291
|
+
overrides: instance.overrides.filter((o) => o.id !== overrideId),
|
|
292
|
+
updatedAt: new Date().toISOString(),
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Reset all overrides of a specific type on an instance
|
|
298
|
+
*/
|
|
299
|
+
export function resetOverridesByType(
|
|
300
|
+
instance: ComponentInstance,
|
|
301
|
+
type: OverrideType
|
|
302
|
+
): ComponentInstance {
|
|
303
|
+
return {
|
|
304
|
+
...instance,
|
|
305
|
+
overrides: instance.overrides.filter((o) => o.type !== type),
|
|
306
|
+
updatedAt: new Date().toISOString(),
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Reset all overrides for a specific element on an instance
|
|
312
|
+
*/
|
|
313
|
+
export function resetOverridesForElement(
|
|
314
|
+
instance: ComponentInstance,
|
|
315
|
+
elementId: string
|
|
316
|
+
): ComponentInstance {
|
|
317
|
+
return {
|
|
318
|
+
...instance,
|
|
319
|
+
overrides: instance.overrides.filter(
|
|
320
|
+
(o) =>
|
|
321
|
+
o.targetElementId !== elementId &&
|
|
322
|
+
!o.elementPath?.includes(elementId)
|
|
323
|
+
),
|
|
324
|
+
updatedAt: new Date().toISOString(),
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Reset all overrides on an instance
|
|
330
|
+
*/
|
|
331
|
+
export function resetAllOverrides(
|
|
332
|
+
instance: ComponentInstance
|
|
333
|
+
): ComponentInstance {
|
|
334
|
+
return {
|
|
335
|
+
...instance,
|
|
336
|
+
overrides: [],
|
|
337
|
+
updatedAt: new Date().toISOString(),
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Create an override from a property change
|
|
343
|
+
*/
|
|
344
|
+
export function createOverride(
|
|
345
|
+
targetElementId: string,
|
|
346
|
+
type: OverrideType,
|
|
347
|
+
value: string | boolean | OverrideImageValue | Record<string, string>
|
|
348
|
+
): Omit<ComponentOverride, 'id'> {
|
|
349
|
+
return {
|
|
350
|
+
targetElementId,
|
|
351
|
+
type,
|
|
352
|
+
value,
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Create a new override with a generated ID
|
|
358
|
+
*/
|
|
359
|
+
export function createOverrideWithId(
|
|
360
|
+
targetElementId: string,
|
|
361
|
+
type: OverrideType,
|
|
362
|
+
value: string | boolean | OverrideImageValue | Record<string, string>
|
|
363
|
+
): ComponentOverride {
|
|
364
|
+
return {
|
|
365
|
+
id: `override-${targetElementId}-${type}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
366
|
+
targetElementId,
|
|
367
|
+
type,
|
|
368
|
+
value,
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Add or update an override in an instance
|
|
374
|
+
* If an override for the same element and type exists, it is updated
|
|
375
|
+
* Otherwise, a new override is added
|
|
376
|
+
*/
|
|
377
|
+
export function upsertOverride(
|
|
378
|
+
instance: ComponentInstance,
|
|
379
|
+
override: Omit<ComponentOverride, 'id'>
|
|
380
|
+
): ComponentInstance {
|
|
381
|
+
const existingIndex = instance.overrides.findIndex(
|
|
382
|
+
(o) =>
|
|
383
|
+
o.targetElementId === override.targetElementId &&
|
|
384
|
+
o.type === override.type
|
|
385
|
+
);
|
|
386
|
+
|
|
387
|
+
const newOverride: ComponentOverride = {
|
|
388
|
+
...override,
|
|
389
|
+
id:
|
|
390
|
+
existingIndex >= 0
|
|
391
|
+
? instance.overrides[existingIndex].id
|
|
392
|
+
: `override-${override.targetElementId}-${override.type}-${Date.now()}`,
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
const newOverrides =
|
|
396
|
+
existingIndex >= 0
|
|
397
|
+
? [
|
|
398
|
+
...instance.overrides.slice(0, existingIndex),
|
|
399
|
+
newOverride,
|
|
400
|
+
...instance.overrides.slice(existingIndex + 1),
|
|
401
|
+
]
|
|
402
|
+
: [...instance.overrides, newOverride];
|
|
403
|
+
|
|
404
|
+
return {
|
|
405
|
+
...instance,
|
|
406
|
+
overrides: newOverrides,
|
|
407
|
+
updatedAt: new Date().toISOString(),
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Remove override indicators from a DOM element
|
|
413
|
+
*/
|
|
414
|
+
export function clearOverrideIndicators(domElement: HTMLElement): void {
|
|
415
|
+
domElement.removeAttribute('data-has-override');
|
|
416
|
+
domElement.removeAttribute('data-override-text');
|
|
417
|
+
domElement.removeAttribute('data-override-fill');
|
|
418
|
+
domElement.removeAttribute('data-override-stroke');
|
|
419
|
+
domElement.removeAttribute('data-override-visibility');
|
|
420
|
+
domElement.removeAttribute('data-override-image');
|
|
421
|
+
domElement.removeAttribute('data-override-style');
|
|
422
|
+
domElement.removeAttribute('data-override-attribute');
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Type guard for OverrideImageValue
|
|
427
|
+
*/
|
|
428
|
+
function isOverrideImageValue(value: unknown): value is OverrideImageValue {
|
|
429
|
+
return (
|
|
430
|
+
typeof value === 'object' &&
|
|
431
|
+
value !== null &&
|
|
432
|
+
'url' in value &&
|
|
433
|
+
typeof (value as OverrideImageValue).url === 'string'
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Type guard for OverrideInstanceSwapValue
|
|
439
|
+
*/
|
|
440
|
+
function isOverrideInstanceSwapValue(value: unknown): value is OverrideInstanceSwapValue {
|
|
441
|
+
return (
|
|
442
|
+
typeof value === 'object' &&
|
|
443
|
+
value !== null &&
|
|
444
|
+
'componentId' in value &&
|
|
445
|
+
typeof (value as OverrideInstanceSwapValue).componentId === 'string'
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Merge overrides from multiple sources (e.g., merging variant overrides with instance overrides)
|
|
451
|
+
* Later overrides in the array take precedence
|
|
452
|
+
*/
|
|
453
|
+
export function mergeOverrides(
|
|
454
|
+
...overrideSets: ComponentOverride[][]
|
|
455
|
+
): ComponentOverride[] {
|
|
456
|
+
const overrideMap = new Map<string, ComponentOverride>();
|
|
457
|
+
|
|
458
|
+
for (const overrides of overrideSets) {
|
|
459
|
+
for (const override of overrides) {
|
|
460
|
+
// Create a key based on target and type
|
|
461
|
+
const key = `${override.targetElementId || override.elementPath}-${override.type}`;
|
|
462
|
+
overrideMap.set(key, override);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
return Array.from(overrideMap.values());
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Check if two overrides are equivalent (same target, type, and value)
|
|
471
|
+
*/
|
|
472
|
+
export function areOverridesEqual(
|
|
473
|
+
a: ComponentOverride,
|
|
474
|
+
b: ComponentOverride
|
|
475
|
+
): boolean {
|
|
476
|
+
if (a.type !== b.type) return false;
|
|
477
|
+
if (a.targetElementId !== b.targetElementId) return false;
|
|
478
|
+
if (a.elementPath !== b.elementPath) return false;
|
|
479
|
+
|
|
480
|
+
// Compare values
|
|
481
|
+
if (typeof a.value !== typeof b.value) return false;
|
|
482
|
+
|
|
483
|
+
if (typeof a.value === 'object' && a.value !== null) {
|
|
484
|
+
return JSON.stringify(a.value) === JSON.stringify(b.value);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
return a.value === b.value;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Get the display name for an override type (for UI)
|
|
492
|
+
*/
|
|
493
|
+
export function getOverrideTypeDisplayName(type: OverrideType): string {
|
|
494
|
+
const displayNames: Record<OverrideType, string> = {
|
|
495
|
+
text: 'テキスト',
|
|
496
|
+
fill: '塗り',
|
|
497
|
+
stroke: '線',
|
|
498
|
+
visibility: '表示',
|
|
499
|
+
image: '画像',
|
|
500
|
+
style: 'スタイル',
|
|
501
|
+
attribute: '属性',
|
|
502
|
+
children: '子要素',
|
|
503
|
+
instanceSwap: 'インスタンス入れ替え',
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
return displayNames[type] || type;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Convert override type to CSS property hint
|
|
511
|
+
*/
|
|
512
|
+
export function getOverrideTypeCSSProperty(type: OverrideType): string | null {
|
|
513
|
+
const cssProperties: Record<OverrideType, string | null> = {
|
|
514
|
+
text: null, // Not a CSS property
|
|
515
|
+
fill: 'background-color',
|
|
516
|
+
stroke: 'border',
|
|
517
|
+
visibility: 'display',
|
|
518
|
+
image: 'background-image',
|
|
519
|
+
style: null, // Multiple properties
|
|
520
|
+
attribute: null, // Not CSS
|
|
521
|
+
children: null, // Not CSS
|
|
522
|
+
instanceSwap: null, // Not CSS
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
return cssProperties[type];
|
|
526
|
+
}
|