@alpaca-editor/core 1.0.4008 → 1.0.4010
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/components/SimpleLanguageSelector.d.ts +9 -0
- package/dist/components/SimpleLanguageSelector.js +50 -0
- package/dist/components/SimpleLanguageSelector.js.map +1 -0
- package/dist/config/config.js +6 -8
- package/dist/config/config.js.map +1 -1
- package/dist/editor/ContentTree.d.ts +3 -2
- package/dist/editor/ContentTree.js +4 -2
- package/dist/editor/ContentTree.js.map +1 -1
- package/dist/editor/Editor.d.ts +2 -1
- package/dist/editor/Editor.js +2 -2
- package/dist/editor/Editor.js.map +1 -1
- package/dist/editor/ScrollingContentTree.d.ts +2 -1
- package/dist/editor/ScrollingContentTree.js +7 -3
- package/dist/editor/ScrollingContentTree.js.map +1 -1
- package/dist/editor/client/EditorClient.d.ts +3 -1
- package/dist/editor/client/EditorClient.js +151 -64
- package/dist/editor/client/EditorClient.js.map +1 -1
- package/dist/editor/client/editContext.d.ts +3 -21
- package/dist/editor/client/editContext.js.map +1 -1
- package/dist/editor/client/operations.js +2 -1
- package/dist/editor/client/operations.js.map +1 -1
- package/dist/editor/componentTreeHelper.js.map +1 -1
- package/dist/editor/field-types/InternalLinkFieldEditor.js +18 -78
- package/dist/editor/field-types/InternalLinkFieldEditor.js.map +1 -1
- package/dist/editor/page-editor-chrome/PlaceholderDropZones.js +53 -27
- package/dist/editor/page-editor-chrome/PlaceholderDropZones.js.map +1 -1
- package/dist/editor/page-viewer/PageViewer.js +1 -1
- package/dist/editor/page-viewer/PageViewer.js.map +1 -1
- package/dist/editor/page-viewer/pageModelSkeletonBuilder.js +19 -2
- package/dist/editor/page-viewer/pageModelSkeletonBuilder.js.map +1 -1
- package/dist/editor/services/contentService.d.ts +2 -1
- package/dist/editor/services/contentService.js +6 -1
- package/dist/editor/services/contentService.js.map +1 -1
- package/dist/editor/sidebar/MainContentTree.js +2 -2
- package/dist/editor/sidebar/MainContentTree.js.map +1 -1
- package/dist/editor/ui/CopyMoveTargetSelectorDialog.js +21 -19
- package/dist/editor/ui/CopyMoveTargetSelectorDialog.js.map +1 -1
- package/dist/editor/views/SingleEditView.js +1 -1
- package/dist/editor/views/SingleEditView.js.map +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/page-wizard/PageWizard.js +61 -41
- package/dist/page-wizard/PageWizard.js.map +1 -1
- package/dist/page-wizard/WizardSteps.js +29 -40
- package/dist/page-wizard/WizardSteps.js.map +1 -1
- package/dist/page-wizard/startPageWizardCommand.js +2 -3
- package/dist/page-wizard/startPageWizardCommand.js.map +1 -1
- package/dist/page-wizard/steps/CollectStep.js +12 -18
- package/dist/page-wizard/steps/CollectStep.js.map +1 -1
- package/dist/page-wizard/steps/ContentStep.js +10 -25
- package/dist/page-wizard/steps/ContentStep.js.map +1 -1
- package/dist/page-wizard/steps/Generate.d.ts +2 -1
- package/dist/page-wizard/steps/Generate.js +2 -2
- package/dist/page-wizard/steps/Generate.js.map +1 -1
- package/dist/page-wizard/steps/StructureStep.js +17 -2
- package/dist/page-wizard/steps/StructureStep.js.map +1 -1
- package/dist/page-wizard/utils/dataAccessor.d.ts +11 -0
- package/dist/page-wizard/utils/dataAccessor.js +19 -0
- package/dist/page-wizard/utils/dataAccessor.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/styles.css +10 -3
- package/package.json +1 -1
- package/src/components/SimpleLanguageSelector.tsx +98 -0
- package/src/config/config.tsx +8 -20
- package/src/editor/ContentTree.tsx +6 -3
- package/src/editor/Editor.tsx +5 -1
- package/src/editor/ScrollingContentTree.tsx +10 -4
- package/src/editor/client/EditorClient.tsx +259 -152
- package/src/editor/client/editContext.ts +29 -27
- package/src/editor/client/operations.ts +1 -1
- package/src/editor/componentTreeHelper.tsx +1 -0
- package/src/editor/field-types/InternalLinkFieldEditor.tsx +71 -144
- package/src/editor/page-editor-chrome/PlaceholderDropZones.tsx +66 -39
- package/src/editor/page-viewer/PageViewer.tsx +1 -1
- package/src/editor/page-viewer/pageModelSkeletonBuilder.ts +22 -2
- package/src/editor/services/contentService.ts +9 -3
- package/src/editor/sidebar/MainContentTree.tsx +2 -0
- package/src/editor/ui/CopyMoveTargetSelectorDialog.tsx +52 -39
- package/src/editor/views/SingleEditView.tsx +1 -1
- package/src/index.ts +11 -0
- package/src/page-wizard/PageWizard.tsx +79 -47
- package/src/page-wizard/WizardSteps.tsx +47 -39
- package/src/page-wizard/startPageWizardCommand.ts +2 -3
- package/src/page-wizard/steps/CollectStep.tsx +26 -19
- package/src/page-wizard/steps/ContentStep.tsx +12 -31
- package/src/page-wizard/steps/Generate.tsx +3 -2
- package/src/page-wizard/steps/StructureStep.tsx +26 -3
- package/src/page-wizard/utils/dataAccessor.ts +26 -0
- package/src/revision.ts +2 -2
|
@@ -182,7 +182,7 @@ export type EditContextType = {
|
|
|
182
182
|
mode: MediaSelectorMode;
|
|
183
183
|
}) => Promise<string | null>;
|
|
184
184
|
|
|
185
|
-
updateUrl: (params: Record<string, string>) => void;
|
|
185
|
+
updateUrl: (params: Record<string, string | undefined>) => void;
|
|
186
186
|
|
|
187
187
|
selection: string[];
|
|
188
188
|
select: (ids: string[]) => void;
|
|
@@ -337,32 +337,32 @@ export type EditContextType = {
|
|
|
337
337
|
|
|
338
338
|
openDialog: OpenDialog;
|
|
339
339
|
|
|
340
|
-
pageWizard: {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
};
|
|
340
|
+
// pageWizard: {
|
|
341
|
+
// wizard: Wizard | undefined;
|
|
342
|
+
// setWizard: React.Dispatch<React.SetStateAction<Wizard | undefined>>;
|
|
343
|
+
// parentItem: ItemDescriptor | undefined;
|
|
344
|
+
// setParentItem: React.Dispatch<
|
|
345
|
+
// React.SetStateAction<ItemDescriptor | undefined>
|
|
346
|
+
// >;
|
|
347
|
+
// // Wizard step state
|
|
348
|
+
// currentStepIndex: number;
|
|
349
|
+
// setCurrentStepIndex: React.Dispatch<React.SetStateAction<number>>;
|
|
350
|
+
// data: WizardData;
|
|
351
|
+
// setData: React.Dispatch<React.SetStateAction<WizardData>>;
|
|
352
|
+
// pageModel: WizardPageModel;
|
|
353
|
+
// setPageModel: React.Dispatch<React.SetStateAction<WizardPageModel>>;
|
|
354
|
+
// internalState: any;
|
|
355
|
+
// setInternalState: React.Dispatch<React.SetStateAction<any>>;
|
|
356
|
+
// stepCompleted: number;
|
|
357
|
+
// setStepCompleted: React.Dispatch<React.SetStateAction<number>>;
|
|
358
|
+
// pageItem: ItemDescriptor | undefined;
|
|
359
|
+
// setPageItem: React.Dispatch<
|
|
360
|
+
// React.SetStateAction<ItemDescriptor | undefined>
|
|
361
|
+
// >;
|
|
362
|
+
// beforeNextCallbackRef: React.MutableRefObject<
|
|
363
|
+
// (() => Promise<boolean>) | null
|
|
364
|
+
// >;
|
|
365
|
+
// };
|
|
366
366
|
|
|
367
367
|
webSocketMessages: WebSocketMessage[];
|
|
368
368
|
clearWebSocketMessages: () => void;
|
|
@@ -370,6 +370,8 @@ export type EditContextType = {
|
|
|
370
370
|
setUserPreferences: (preferences: Partial<UserPreferences>) => void;
|
|
371
371
|
favorites: any[];
|
|
372
372
|
loadFavorites: () => Promise<void>;
|
|
373
|
+
currentWizardId: string | null;
|
|
374
|
+
setCurrentWizardId: (wizardId: string | null) => void;
|
|
373
375
|
};
|
|
374
376
|
|
|
375
377
|
const EditContext = React.createContext<EditContextType | undefined>(undefined);
|
|
@@ -208,7 +208,7 @@ export function getOperationsContext(
|
|
|
208
208
|
if (result.type == "error") {
|
|
209
209
|
console.log("error locking field", result);
|
|
210
210
|
}
|
|
211
|
-
|
|
211
|
+
if (handleErrorResult(result, ui, state)) return false;
|
|
212
212
|
if (result.type == "success" && result.data.success) {
|
|
213
213
|
state.setLockedField(field);
|
|
214
214
|
return true;
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { useEffect, useRef, useState } from "react";
|
|
4
|
-
import { ChevronDown } from "lucide-react";
|
|
4
|
+
import { ChevronDown, Trash } from "lucide-react";
|
|
5
5
|
|
|
6
6
|
import { Link } from "../LinkEditorDialog";
|
|
7
7
|
import { useEditContext } from "../client/editContext";
|
|
8
|
-
import
|
|
8
|
+
import { ScrollingContentTree } from "../ScrollingContentTree";
|
|
9
9
|
|
|
10
10
|
import { InternalLinkField } from "../fieldTypes";
|
|
11
11
|
import { getLookupSources } from "../services/editService";
|
|
12
12
|
import { classNames } from "primereact/utils";
|
|
13
13
|
import ItemSearch from "../ui/ItemSearch";
|
|
14
14
|
import { normalizeGuid } from "../utils";
|
|
15
|
+
import {
|
|
16
|
+
Popover,
|
|
17
|
+
PopoverContent,
|
|
18
|
+
PopoverTrigger,
|
|
19
|
+
} from "../../components/ui/popover";
|
|
15
20
|
|
|
16
21
|
export function InternalLinkFieldEditor({
|
|
17
22
|
field,
|
|
@@ -38,10 +43,6 @@ export function InternalLinkFieldEditor({
|
|
|
38
43
|
});
|
|
39
44
|
}, [field]);
|
|
40
45
|
|
|
41
|
-
const dropdownRef = useRef<HTMLDivElement>(null);
|
|
42
|
-
const texboxRef = useRef<HTMLDivElement>(null);
|
|
43
|
-
const containerRef = useRef<HTMLDivElement>(null);
|
|
44
|
-
|
|
45
46
|
if (!editContext) return;
|
|
46
47
|
|
|
47
48
|
// Helper function to normalize GUID formats for comparison
|
|
@@ -49,11 +50,6 @@ export function InternalLinkFieldEditor({
|
|
|
49
50
|
return key.toLowerCase().replace(/[{}]/g, "");
|
|
50
51
|
};
|
|
51
52
|
|
|
52
|
-
const selection = [];
|
|
53
|
-
if (link?.itemId) {
|
|
54
|
-
selection.push(normalizeKey(link.itemId));
|
|
55
|
-
}
|
|
56
|
-
|
|
57
53
|
useEffect(() => {
|
|
58
54
|
const loadLookupSources = async () => {
|
|
59
55
|
const datasources = await getLookupSources(field, editContext.sessionId);
|
|
@@ -70,119 +66,50 @@ export function InternalLinkFieldEditor({
|
|
|
70
66
|
field.descriptor.item.version,
|
|
71
67
|
]);
|
|
72
68
|
|
|
73
|
-
// Close overlay when clicking outside
|
|
74
|
-
useEffect(() => {
|
|
75
|
-
if (!showTree) return;
|
|
76
|
-
|
|
77
|
-
const handleClickOutside = (event: MouseEvent) => {
|
|
78
|
-
if (
|
|
79
|
-
dropdownRef.current &&
|
|
80
|
-
!dropdownRef.current.contains(event.target as Node) &&
|
|
81
|
-
texboxRef.current &&
|
|
82
|
-
!texboxRef.current.contains(event.target as Node)
|
|
83
|
-
) {
|
|
84
|
-
setShowTree(false);
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
document.addEventListener("mousedown", handleClickOutside);
|
|
89
|
-
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
90
|
-
}, [showTree]);
|
|
91
|
-
|
|
92
|
-
// Close overlay when parent containers scroll to prevent misalignment
|
|
93
|
-
useEffect(() => {
|
|
94
|
-
if (!showTree) return;
|
|
95
|
-
|
|
96
|
-
const handleScroll = () => {
|
|
97
|
-
setShowTree(false);
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
// Find scrollable parent elements
|
|
101
|
-
const scrollableParents: Element[] = [];
|
|
102
|
-
let element = texboxRef.current?.parentElement;
|
|
103
|
-
|
|
104
|
-
while (element) {
|
|
105
|
-
const computedStyle = window.getComputedStyle(element);
|
|
106
|
-
const overflowY = computedStyle.overflowY;
|
|
107
|
-
const overflowX = computedStyle.overflowX;
|
|
108
|
-
|
|
109
|
-
if (
|
|
110
|
-
overflowY === "auto" ||
|
|
111
|
-
overflowY === "scroll" ||
|
|
112
|
-
overflowX === "auto" ||
|
|
113
|
-
overflowX === "scroll"
|
|
114
|
-
) {
|
|
115
|
-
scrollableParents.push(element);
|
|
116
|
-
}
|
|
117
|
-
element = element.parentElement;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Also listen to window scroll
|
|
121
|
-
scrollableParents.push(window as any);
|
|
122
|
-
|
|
123
|
-
// Add scroll listeners
|
|
124
|
-
scrollableParents.forEach((parent) => {
|
|
125
|
-
parent.addEventListener("scroll", handleScroll, { passive: true });
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
return () => {
|
|
129
|
-
scrollableParents.forEach((parent) => {
|
|
130
|
-
parent.removeEventListener("scroll", handleScroll);
|
|
131
|
-
});
|
|
132
|
-
};
|
|
133
|
-
}, [showTree]);
|
|
134
|
-
|
|
135
69
|
return (
|
|
136
70
|
<div className="relative">
|
|
137
|
-
<
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
71
|
+
<Popover open={showTree} onOpenChange={setShowTree}>
|
|
72
|
+
<PopoverTrigger asChild disabled={readOnly}>
|
|
73
|
+
<div
|
|
74
|
+
className={classNames(
|
|
75
|
+
"justiy-between focus-shadow bg-gray-5 flex cursor-pointer justify-between border p-1.5 text-xs",
|
|
76
|
+
readOnly ? "cursor-default bg-gray-100" : "",
|
|
77
|
+
)}
|
|
78
|
+
>
|
|
79
|
+
{link?.targetItemName ? (
|
|
80
|
+
<span>{link.targetItemName}</span>
|
|
81
|
+
) : (
|
|
82
|
+
<span> </span>
|
|
83
|
+
)}
|
|
84
|
+
{!readOnly && (
|
|
85
|
+
<ChevronDown
|
|
86
|
+
strokeWidth={1}
|
|
87
|
+
className={`h-4 w-4 transition-transform duration-200 ${
|
|
88
|
+
showTree ? "rotate-180" : "rotate-0"
|
|
89
|
+
}`}
|
|
90
|
+
/>
|
|
91
|
+
)}
|
|
92
|
+
</div>
|
|
93
|
+
</PopoverTrigger>
|
|
94
|
+
|
|
154
95
|
{!readOnly && (
|
|
155
|
-
<
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
96
|
+
<PopoverContent className="w-96 p-0" align="start" side="bottom">
|
|
97
|
+
<div className="border-b border-gray-200 bg-white p-2">
|
|
98
|
+
<ItemSearch
|
|
99
|
+
rootItemIds={rootItemIds.map((x) => normalizeGuid(x))}
|
|
100
|
+
autoFocus={true}
|
|
101
|
+
itemSelected={(item) => {
|
|
102
|
+
setShowTree(false);
|
|
103
|
+
editContext?.operations.editField({
|
|
104
|
+
field: field.descriptor,
|
|
105
|
+
rawValue: item.id,
|
|
106
|
+
});
|
|
107
|
+
}}
|
|
108
|
+
/>
|
|
109
|
+
</div>
|
|
163
110
|
|
|
164
|
-
{showTree && (
|
|
165
|
-
<div
|
|
166
|
-
ref={dropdownRef}
|
|
167
|
-
className="absolute top-full left-0 z-50 mt-1 border border-gray-200 bg-white shadow-lg"
|
|
168
|
-
style={{ width: `${texboxRef.current?.offsetWidth || 100}px` }}
|
|
169
|
-
>
|
|
170
|
-
<div className="border-b border-gray-200 bg-white p-2">
|
|
171
|
-
<ItemSearch
|
|
172
|
-
rootItemIds={rootItemIds.map((x) => normalizeGuid(x))}
|
|
173
|
-
autoFocus={true}
|
|
174
|
-
itemSelected={(item) => {
|
|
175
|
-
setShowTree(false);
|
|
176
|
-
editContext?.operations.editField({
|
|
177
|
-
field: field.descriptor,
|
|
178
|
-
rawValue: item.id,
|
|
179
|
-
});
|
|
180
|
-
}}
|
|
181
|
-
/>
|
|
182
|
-
</div>
|
|
183
|
-
<div className="border-b border-gray-200 bg-gray-50 p-2">
|
|
184
111
|
<button
|
|
185
|
-
className="w-full rounded
|
|
112
|
+
className="w-full cursor-pointer rounded border-b bg-gray-50 p-1 px-4 text-left text-xs hover:bg-gray-100"
|
|
186
113
|
onClick={() => {
|
|
187
114
|
setShowTree(false);
|
|
188
115
|
editContext?.operations.editField({
|
|
@@ -191,32 +118,32 @@ export function InternalLinkFieldEditor({
|
|
|
191
118
|
});
|
|
192
119
|
}}
|
|
193
120
|
>
|
|
194
|
-
|
|
121
|
+
<div className="flex items-center gap-2">
|
|
122
|
+
<Trash className="h-4 w-4" strokeWidth={1} /> Clear
|
|
123
|
+
</div>
|
|
195
124
|
</button>
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
</div>
|
|
219
|
-
)}
|
|
125
|
+
|
|
126
|
+
<div className="relative h-64 border-x border-b">
|
|
127
|
+
<ScrollingContentTree
|
|
128
|
+
selectedItemId={
|
|
129
|
+
link?.itemId ? normalizeKey(link.itemId) : undefined
|
|
130
|
+
}
|
|
131
|
+
rootItemId={rootItemIds[0]}
|
|
132
|
+
expandedItemId={
|
|
133
|
+
link?.itemId ? normalizeKey(link.itemId) : undefined
|
|
134
|
+
}
|
|
135
|
+
onSelectionChange={(nodes) => {
|
|
136
|
+
setShowTree(false);
|
|
137
|
+
editContext?.operations.editField({
|
|
138
|
+
field: field.descriptor,
|
|
139
|
+
rawValue: nodes[0]?.id,
|
|
140
|
+
});
|
|
141
|
+
}}
|
|
142
|
+
/>
|
|
143
|
+
</div>
|
|
144
|
+
</PopoverContent>
|
|
145
|
+
)}
|
|
146
|
+
</Popover>
|
|
220
147
|
</div>
|
|
221
148
|
);
|
|
222
149
|
}
|
|
@@ -71,63 +71,90 @@ export function PlaceholderDropZones({
|
|
|
71
71
|
"[data-placeholder-start='" + placeholder.key + "']",
|
|
72
72
|
);
|
|
73
73
|
|
|
74
|
+
let isUsingParentBounds = false;
|
|
75
|
+
|
|
74
76
|
if (!placeholderElement && placeholder.components.length > 0) {
|
|
75
77
|
placeholderElement = placeholder.components[0]?.firstDOMElement;
|
|
76
78
|
}
|
|
77
79
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
getNextElement(placeholderElement) || placeholderElement.parentElement;
|
|
84
|
-
|
|
85
|
-
let orientation = placeholderElement?.getAttribute("data-orientation");
|
|
80
|
+
// For empty placeholders, use parent component bounds as last resort
|
|
81
|
+
if (!placeholderElement && placeholder.parentComponent) {
|
|
82
|
+
placeholderElement = placeholder.parentComponent.firstDOMElement;
|
|
83
|
+
isUsingParentBounds = true;
|
|
84
|
+
}
|
|
86
85
|
|
|
87
|
-
if (!
|
|
88
|
-
orientation = detectOrientation(
|
|
89
|
-
placeholderElement,
|
|
90
|
-
placeholder.components,
|
|
91
|
-
);
|
|
86
|
+
if (!placeholderElement) return;
|
|
92
87
|
|
|
93
|
-
const rect = (prev ?? nextOrParent)!.getBoundingClientRect();
|
|
94
88
|
const zoom = pageViewContext.zoom;
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
orientation === "horizontal"
|
|
98
|
-
? {
|
|
99
|
-
x: (prev ? rect.x + rect.width : rect.x) * zoom,
|
|
100
|
-
y: (rect.y + rect.height / 2) * zoom,
|
|
101
|
-
}
|
|
102
|
-
: {
|
|
103
|
-
x: (rect.x + rect.width / 2) * zoom,
|
|
104
|
-
y: (prev ? rect.y + rect.height : rect.y) * zoom,
|
|
105
|
-
};
|
|
106
|
-
|
|
89
|
+
let position: { x: number; y: number };
|
|
90
|
+
let anchor: "top" | "bottom" | "left" | "right" = "top";
|
|
107
91
|
let index = 0;
|
|
108
92
|
|
|
109
93
|
const description = placeholderElement.getAttribute("data-description");
|
|
110
94
|
|
|
111
|
-
let
|
|
95
|
+
let referenceElement: Element;
|
|
96
|
+
let orientation: string | null = null;
|
|
97
|
+
|
|
98
|
+
// Special handling for empty placeholders using parent bounds
|
|
99
|
+
if (isUsingParentBounds) {
|
|
100
|
+
const rect = placeholderElement.getBoundingClientRect();
|
|
101
|
+
// Center the drop zone in the middle of the parent
|
|
102
|
+
position = {
|
|
103
|
+
x: (rect.x + rect.width / 2) * zoom,
|
|
104
|
+
y: (rect.y + rect.height / 2) * zoom,
|
|
105
|
+
};
|
|
106
|
+
anchor = "top"; // Default anchor for centered position
|
|
107
|
+
referenceElement = placeholderElement;
|
|
108
|
+
orientation = "vertical"; // Default for centered placeholders
|
|
109
|
+
} else {
|
|
110
|
+
// Original positioning logic for normal placeholders
|
|
111
|
+
const prev = getPreviousElement(placeholderElement);
|
|
112
|
+
|
|
113
|
+
const nextOrParent =
|
|
114
|
+
getNextElement(placeholderElement) || placeholderElement.parentElement;
|
|
115
|
+
|
|
116
|
+
orientation = placeholderElement?.getAttribute("data-orientation");
|
|
117
|
+
|
|
118
|
+
if (!orientation)
|
|
119
|
+
orientation = detectOrientation(
|
|
120
|
+
placeholderElement,
|
|
121
|
+
placeholder.components,
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
const rect = (prev ?? nextOrParent)!.getBoundingClientRect();
|
|
125
|
+
|
|
126
|
+
position =
|
|
127
|
+
orientation === "horizontal"
|
|
128
|
+
? {
|
|
129
|
+
x: (prev ? rect.x + rect.width : rect.x) * zoom,
|
|
130
|
+
y: (rect.y + rect.height / 2) * zoom,
|
|
131
|
+
}
|
|
132
|
+
: {
|
|
133
|
+
x: (rect.x + rect.width / 2) * zoom,
|
|
134
|
+
y: (prev ? rect.y + rect.height : rect.y) * zoom,
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
if (prev && orientation === "horizontal") {
|
|
138
|
+
anchor = "right";
|
|
139
|
+
}
|
|
140
|
+
if (prev && orientation !== "horizontal") {
|
|
141
|
+
anchor = "bottom";
|
|
142
|
+
}
|
|
143
|
+
if (!prev && orientation === "horizontal") {
|
|
144
|
+
anchor = "left";
|
|
145
|
+
}
|
|
146
|
+
if (!prev && orientation !== "horizontal") {
|
|
147
|
+
anchor = "top";
|
|
148
|
+
}
|
|
112
149
|
|
|
113
|
-
|
|
114
|
-
anchor = "right";
|
|
115
|
-
}
|
|
116
|
-
if (prev && orientation !== "horizontal") {
|
|
117
|
-
anchor = "bottom";
|
|
118
|
-
}
|
|
119
|
-
if (!prev && orientation === "horizontal") {
|
|
120
|
-
anchor = "left";
|
|
121
|
-
}
|
|
122
|
-
if (!prev && orientation !== "horizontal") {
|
|
123
|
-
anchor = "top";
|
|
150
|
+
referenceElement = prev ?? nextOrParent!;
|
|
124
151
|
}
|
|
125
152
|
|
|
126
153
|
zones.push({
|
|
127
154
|
placeholder,
|
|
128
155
|
position,
|
|
129
156
|
index,
|
|
130
|
-
element:
|
|
157
|
+
element: referenceElement,
|
|
131
158
|
anchor,
|
|
132
159
|
description,
|
|
133
160
|
});
|
|
@@ -47,7 +47,7 @@ export function PageViewer({
|
|
|
47
47
|
|
|
48
48
|
if (!lastEdit) return;
|
|
49
49
|
|
|
50
|
-
if (!lastEdit.user.ai
|
|
50
|
+
if (!lastEdit.user.ai || lastEdit.user.name !== editContext.user?.name)
|
|
51
51
|
return;
|
|
52
52
|
|
|
53
53
|
if (editContext.selection.indexOf(lastEdit.item.id) === -1) {
|
|
@@ -180,7 +180,27 @@ export function buildPageModelSkeleton(
|
|
|
180
180
|
currentPlaceholder.components.push(currentComponent);
|
|
181
181
|
}
|
|
182
182
|
}
|
|
183
|
-
|
|
183
|
+
|
|
184
|
+
// Check if the new component should have an implicit placeholder
|
|
185
|
+
const hasImplicitPlaceholder =
|
|
186
|
+
element.getAttribute("data-has-implicit-placeholder") === "true";
|
|
187
|
+
|
|
188
|
+
if (hasImplicitPlaceholder && currentComponent) {
|
|
189
|
+
// Add an implicit placeholder to the newly created component
|
|
190
|
+
const implicitPlaceholder: PlaceholderSkeleton = {
|
|
191
|
+
key: "implicit_" + currentComponent.datasourceItem?.id,
|
|
192
|
+
name: "Implicit placeholder: " + currentComponent.datasourceItem?.id,
|
|
193
|
+
description: "",
|
|
194
|
+
components: [],
|
|
195
|
+
parentComponent: currentComponent,
|
|
196
|
+
editable: element.getAttribute("data-editable") !== "false",
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
currentComponent.placeholders.push(implicitPlaceholder);
|
|
200
|
+
currentPlaceholder = implicitPlaceholder;
|
|
201
|
+
} else {
|
|
202
|
+
currentPlaceholder = undefined;
|
|
203
|
+
}
|
|
184
204
|
return currentComponent;
|
|
185
205
|
}
|
|
186
206
|
};
|
|
@@ -422,6 +442,6 @@ export function buildPageModelSkeleton(
|
|
|
422
442
|
|
|
423
443
|
const time = performance.now() - start;
|
|
424
444
|
|
|
425
|
-
console.log("PAGE MODEL SKELETON", page, time);
|
|
445
|
+
console.log("PAGE MODEL SKELETON: ", page, time);
|
|
426
446
|
pageViewContextRef.current?.setPageSkeleton(page);
|
|
427
447
|
}
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
WorkboxItem,
|
|
9
9
|
ContentEditorWarning,
|
|
10
10
|
} from "../../types";
|
|
11
|
-
import { FullItem, ItemDescriptor, ItemStub } from "../pageModel";
|
|
11
|
+
import { FullItem, ItemDescriptor, ItemStub, Language } from "../pageModel";
|
|
12
12
|
|
|
13
13
|
export type Thumbnail = {
|
|
14
14
|
id: string;
|
|
@@ -236,5 +236,11 @@ export type RichTextProfileResponse = {
|
|
|
236
236
|
};
|
|
237
237
|
|
|
238
238
|
export async function getRichTextProfile(itemPath: string) {
|
|
239
|
-
return await post<RichTextProfileResponse>(`/alpaca/editor/RichTextProfile`, {
|
|
240
|
-
|
|
239
|
+
return await post<RichTextProfileResponse>(`/alpaca/editor/RichTextProfile`, {
|
|
240
|
+
profile: itemPath,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export async function getLanguages() {
|
|
245
|
+
return await get<Language[]>("/alpaca/editor/languages");
|
|
246
|
+
}
|
|
@@ -77,6 +77,7 @@ export function MainContentTree({
|
|
|
77
77
|
<div className="relative flex-1">
|
|
78
78
|
<div className="absolute inset-1 overflow-auto">
|
|
79
79
|
<ContentTree
|
|
80
|
+
enableDragAndDrop={mode == "normal"}
|
|
80
81
|
language={editContext.currentItemDescriptor?.language ?? "en"}
|
|
81
82
|
rootItemId={rootItemId ?? "{11111111-1111-1111-1111-111111111111}"}
|
|
82
83
|
expandIdPath={
|
|
@@ -102,6 +103,7 @@ export function MainContentTree({
|
|
|
102
103
|
}}
|
|
103
104
|
showGrabCursorForDraggableNodes={mode === "insert-component"}
|
|
104
105
|
isDraggable={isDraggable}
|
|
106
|
+
enableContextMenu={mode === "normal"}
|
|
105
107
|
renderNode={(node, defaultRenderer) => (
|
|
106
108
|
<div className="group flex w-full gap-4">
|
|
107
109
|
{defaultRenderer(node)}
|