@alpaca-editor/core 1.0.4000 → 1.0.4008
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/client-components/index.d.ts +1 -0
- package/dist/client-components/index.js +1 -0
- package/dist/client-components/index.js.map +1 -1
- package/dist/components/ui/calendar.d.ts +7 -0
- package/dist/components/ui/calendar.js +62 -0
- package/dist/components/ui/calendar.js.map +1 -0
- package/dist/config/config.js +11 -0
- package/dist/config/config.js.map +1 -1
- package/dist/editor/ContentTree.d.ts +2 -1
- package/dist/editor/ContentTree.js +2 -2
- package/dist/editor/ContentTree.js.map +1 -1
- package/dist/editor/FieldList.js +37 -10
- package/dist/editor/FieldList.js.map +1 -1
- package/dist/editor/FieldListField.js +21 -10
- package/dist/editor/FieldListField.js.map +1 -1
- package/dist/editor/client/EditorClient.js +5 -3
- package/dist/editor/client/EditorClient.js.map +1 -1
- package/dist/editor/client/itemsRepository.js +57 -9
- package/dist/editor/client/itemsRepository.js.map +1 -1
- package/dist/editor/client/pageModelBuilder.js +1 -1
- package/dist/editor/client/pageModelBuilder.js.map +1 -1
- package/dist/editor/commands/itemCommands.js +1 -1
- package/dist/editor/commands/itemCommands.js.map +1 -1
- package/dist/editor/field-types/DateFieldEditor.d.ts +5 -0
- package/dist/editor/field-types/DateFieldEditor.js +93 -0
- package/dist/editor/field-types/DateFieldEditor.js.map +1 -0
- package/dist/editor/field-types/ImageFieldEditor.js +1 -1
- package/dist/editor/field-types/ImageFieldEditor.js.map +1 -1
- package/dist/editor/field-types/InternalLinkFieldEditor.js +1 -1
- package/dist/editor/field-types/InternalLinkFieldEditor.js.map +1 -1
- package/dist/editor/field-types/LinkFieldEditor.js +1 -1
- package/dist/editor/field-types/LinkFieldEditor.js.map +1 -1
- package/dist/editor/field-types/MultiLineText.js +3 -1
- package/dist/editor/field-types/MultiLineText.js.map +1 -1
- package/dist/editor/field-types/PictureFieldEditor.js +1 -1
- package/dist/editor/field-types/PictureFieldEditor.js.map +1 -1
- package/dist/editor/field-types/SingleLineText.js +3 -1
- package/dist/editor/field-types/SingleLineText.js.map +1 -1
- package/dist/editor/field-types/TreeListEditor.js +71 -6
- package/dist/editor/field-types/TreeListEditor.js.map +1 -1
- package/dist/editor/field-types/richtext/components/ReactSlate.js +1 -1
- package/dist/editor/field-types/richtext/components/ReactSlate.js.map +1 -1
- package/dist/editor/fieldTypes.d.ts +1 -1
- package/dist/editor/media-selector/AiImageSearch.js +2 -1
- package/dist/editor/media-selector/AiImageSearch.js.map +1 -1
- package/dist/editor/media-selector/AiImageSearchPrompt.js +2 -1
- package/dist/editor/media-selector/AiImageSearchPrompt.js.map +1 -1
- package/dist/editor/page-viewer/DeviceToolbar.js +3 -2
- package/dist/editor/page-viewer/DeviceToolbar.js.map +1 -1
- package/dist/editor/page-viewer/MiniMap.js +6 -1
- package/dist/editor/page-viewer/MiniMap.js.map +1 -1
- package/dist/editor/services/aiService.d.ts +9 -1
- package/dist/editor/services/aiService.js.map +1 -1
- package/dist/editor/sidebar/Insert.js +1 -1
- package/dist/editor/sidebar/Insert.js.map +1 -1
- package/dist/editor/sidebar/MainContentTree.d.ts +1 -1
- package/dist/editor/sidebar/MainContentTree.js +19 -7
- package/dist/editor/sidebar/MainContentTree.js.map +1 -1
- package/dist/editor/ui/Icons.js +1 -1
- package/dist/editor/ui/Icons.js.map +1 -1
- package/dist/editor/ui/ItemNameDialogNew.js +14 -10
- package/dist/editor/ui/ItemNameDialogNew.js.map +1 -1
- package/dist/editor/ui/PerfectTree.d.ts +2 -0
- package/dist/editor/ui/PerfectTree.js +1 -1
- package/dist/editor/ui/PerfectTree.js.map +1 -1
- package/dist/editor/ui/Spinner.js +1 -1
- package/dist/editor/ui/Spinner.js.map +1 -1
- package/dist/page-wizard/steps/ContentStep.js +56 -17
- package/dist/page-wizard/steps/ContentStep.js.map +1 -1
- package/dist/page-wizard/steps/FindItemsStep.d.ts +2 -0
- package/dist/page-wizard/steps/FindItemsStep.js +293 -0
- package/dist/page-wizard/steps/FindItemsStep.js.map +1 -0
- package/dist/page-wizard/steps/ImagesStep.js +9 -1
- package/dist/page-wizard/steps/ImagesStep.js.map +1 -1
- package/dist/page-wizard/steps/LayoutStep.js +24 -14
- package/dist/page-wizard/steps/LayoutStep.js.map +1 -1
- package/dist/page-wizard/steps/MetaDataStep.js +21 -11
- package/dist/page-wizard/steps/MetaDataStep.js.map +1 -1
- package/dist/page-wizard/steps/SelectStep.js +38 -31
- package/dist/page-wizard/steps/SelectStep.js.map +1 -1
- package/dist/page-wizard/steps/StructureStep.d.ts +2 -0
- package/dist/page-wizard/steps/StructureStep.js +156 -0
- package/dist/page-wizard/steps/StructureStep.js.map +1 -0
- package/dist/page-wizard/utils/dataAccessor.d.ts +39 -0
- package/dist/page-wizard/utils/dataAccessor.js +222 -0
- package/dist/page-wizard/utils/dataAccessor.js.map +1 -0
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/splash-screen/RecentPages.js +14 -3
- package/dist/splash-screen/RecentPages.js.map +1 -1
- package/dist/styles.css +178 -0
- package/package.json +3 -1
- package/src/client-components/index.ts +1 -3
- package/src/components/ui/calendar.tsx +175 -0
- package/src/config/config.tsx +11 -1
- package/src/editor/ContentTree.tsx +4 -1
- package/src/editor/FieldList.tsx +44 -18
- package/src/editor/FieldListField.tsx +98 -23
- package/src/editor/client/EditorClient.tsx +6 -3
- package/src/editor/client/itemsRepository.ts +74 -10
- package/src/editor/client/pageModelBuilder.ts +1 -1
- package/src/editor/commands/itemCommands.tsx +2 -3
- package/src/editor/field-types/DateFieldEditor.tsx +132 -0
- package/src/editor/field-types/ImageFieldEditor.tsx +1 -1
- package/src/editor/field-types/InternalLinkFieldEditor.tsx +2 -2
- package/src/editor/field-types/LinkFieldEditor.tsx +2 -2
- package/src/editor/field-types/MultiLineText.tsx +5 -1
- package/src/editor/field-types/PictureFieldEditor.tsx +1 -1
- package/src/editor/field-types/SingleLineText.tsx +5 -1
- package/src/editor/field-types/TreeListEditor.tsx +136 -50
- package/src/editor/field-types/richtext/components/ReactSlate.tsx +5 -0
- package/src/editor/fieldTypes.ts +1 -1
- package/src/editor/media-selector/AiImageSearch.tsx +2 -1
- package/src/editor/media-selector/AiImageSearchPrompt.tsx +2 -1
- package/src/editor/page-viewer/DeviceToolbar.tsx +17 -5
- package/src/editor/page-viewer/MiniMap.tsx +7 -2
- package/src/editor/services/aiService.ts +10 -1
- package/src/editor/sidebar/Insert.tsx +1 -1
- package/src/editor/sidebar/MainContentTree.tsx +26 -14
- package/src/editor/ui/Icons.tsx +16 -5
- package/src/editor/ui/ItemNameDialogNew.tsx +42 -30
- package/src/editor/ui/PerfectTree.tsx +3 -1
- package/src/editor/ui/Spinner.tsx +3 -1
- package/src/page-wizard/steps/ContentStep.tsx +68 -20
- package/src/page-wizard/steps/FindItemsStep.tsx +546 -0
- package/src/page-wizard/steps/ImagesStep.tsx +13 -1
- package/src/page-wizard/steps/LayoutStep.tsx +27 -16
- package/src/page-wizard/steps/MetaDataStep.tsx +26 -13
- package/src/page-wizard/steps/SelectStep.tsx +61 -32
- package/src/page-wizard/steps/StructureStep.tsx +350 -0
- package/src/page-wizard/utils/dataAccessor.ts +275 -0
- package/src/revision.ts +2 -2
- package/src/splash-screen/RecentPages.tsx +22 -3
- package/dist/editor/ai/GhostWriter.d.ts +0 -1
- package/dist/editor/ai/GhostWriter.js +0 -347
- package/dist/editor/ai/GhostWriter.js.map +0 -1
- package/dist/editor/component-designer/ComponentDesignerAiTerminal.d.ts +0 -1
- package/dist/editor/component-designer/ComponentDesignerAiTerminal.js +0 -7
- package/dist/editor/component-designer/ComponentDesignerAiTerminal.js.map +0 -1
- /package/src/editor/ai/{GhostWriter.tsx → GhostWriter.tsx_} +0 -0
- /package/src/editor/component-designer/{ComponentDesignerAiTerminal.tsx → ComponentDesignerAiTerminal.tsx_} +0 -0
package/src/editor/FieldList.tsx
CHANGED
|
@@ -154,6 +154,21 @@ function FieldListForSingleItem({
|
|
|
154
154
|
? (fields as Field[])
|
|
155
155
|
: (Object.values(fields) as Field[]);
|
|
156
156
|
|
|
157
|
+
// Create section order mapping before any filtering to preserve original order
|
|
158
|
+
const originalSectionOrder = new Map<string, number>();
|
|
159
|
+
fieldsArray.forEach((field) => {
|
|
160
|
+
const section = field.section || "";
|
|
161
|
+
const sectionSortOrder = field.sectionSortOrder || 0;
|
|
162
|
+
|
|
163
|
+
// Use the minimum sectionSortOrder for each section to ensure consistent ordering
|
|
164
|
+
if (!originalSectionOrder.has(section)) {
|
|
165
|
+
originalSectionOrder.set(section, sectionSortOrder);
|
|
166
|
+
} else {
|
|
167
|
+
const existing = originalSectionOrder.get(section)!;
|
|
168
|
+
originalSectionOrder.set(section, Math.min(existing, sectionSortOrder));
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
157
172
|
if (filter) fieldsArray = fieldsArray.filter(filter);
|
|
158
173
|
|
|
159
174
|
if (searchFilter) {
|
|
@@ -173,21 +188,29 @@ function FieldListForSingleItem({
|
|
|
173
188
|
// Group initialization
|
|
174
189
|
const section = value.section || "";
|
|
175
190
|
|
|
176
|
-
if (
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
//
|
|
181
|
-
|
|
191
|
+
if (!acc[section]) {
|
|
192
|
+
acc[section] = [];
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Only add fields that have a valid descriptor item id
|
|
196
|
+
if (value.descriptor.item.id) {
|
|
197
|
+
acc[section].push(value);
|
|
198
|
+
}
|
|
182
199
|
|
|
183
200
|
return acc;
|
|
184
201
|
}, {} as any);
|
|
185
202
|
|
|
186
203
|
const sectionList: Field[][] = Object.values(sectionsGrouping);
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
204
|
+
|
|
205
|
+
// Sort sections using the original section order, not the first field after filtering
|
|
206
|
+
sectionList.sort((a: Field[], b: Field[]) => {
|
|
207
|
+
const sectionA = a[0]?.section || "";
|
|
208
|
+
const sectionB = b[0]?.section || "";
|
|
209
|
+
const orderA = originalSectionOrder.get(sectionA) || 0;
|
|
210
|
+
const orderB = originalSectionOrder.get(sectionB) || 0;
|
|
211
|
+
|
|
212
|
+
return orderA - orderB;
|
|
213
|
+
});
|
|
191
214
|
|
|
192
215
|
function getSectionFields(fields: Field[]) {
|
|
193
216
|
fields.sort((a: any, b: any) => a?.sortOrder - b?.sortOrder);
|
|
@@ -204,15 +227,18 @@ function FieldListForSingleItem({
|
|
|
204
227
|
));
|
|
205
228
|
}
|
|
206
229
|
return sectionList
|
|
207
|
-
.
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
230
|
+
.filter((sectionFields) => {
|
|
231
|
+
const section = sectionFields[0]?.section;
|
|
232
|
+
return !sections || sections.includes(section!);
|
|
233
|
+
})
|
|
234
|
+
.map((sectionFields) => {
|
|
235
|
+
const section = sectionFields[0]?.section;
|
|
236
|
+
return simplified ? (
|
|
237
|
+
getSectionFields(sectionFields)
|
|
212
238
|
) : (
|
|
213
239
|
<Section key={section} title={section!}>
|
|
214
|
-
{getSectionFields(
|
|
240
|
+
{getSectionFields(sectionFields)}
|
|
215
241
|
</Section>
|
|
216
|
-
)
|
|
217
|
-
);
|
|
242
|
+
);
|
|
243
|
+
});
|
|
218
244
|
}
|
|
@@ -5,7 +5,6 @@ import { EditorConfiguration } from "../config/types";
|
|
|
5
5
|
|
|
6
6
|
import { useEffect, useRef, useState } from "react";
|
|
7
7
|
|
|
8
|
-
import { OverlayPanel } from "primereact/overlaypanel";
|
|
9
8
|
import { FieldHistoryItem, SingleValidatorResult } from "../types";
|
|
10
9
|
|
|
11
10
|
import { getSessionWithFieldLock } from "./utils";
|
|
@@ -19,9 +18,20 @@ import {
|
|
|
19
18
|
import { useThrottledCallback } from "use-debounce";
|
|
20
19
|
import { SimpleIconButton } from "./ui/SimpleIconButton";
|
|
21
20
|
import { FieldHistory } from "./FieldHistory";
|
|
22
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
History,
|
|
23
|
+
MessageCircle,
|
|
24
|
+
WandSparkles,
|
|
25
|
+
MoreVertical,
|
|
26
|
+
ExternalLink,
|
|
27
|
+
} from "lucide-react";
|
|
23
28
|
import { Checkbox } from "../components/ui/checkbox";
|
|
24
29
|
import { CopyButton } from "../components/ui/copy-button";
|
|
30
|
+
import {
|
|
31
|
+
Popover,
|
|
32
|
+
PopoverContent,
|
|
33
|
+
PopoverTrigger,
|
|
34
|
+
} from "../components/ui/popover";
|
|
25
35
|
|
|
26
36
|
export default function FieldListField({
|
|
27
37
|
field,
|
|
@@ -50,8 +60,10 @@ export default function FieldListField({
|
|
|
50
60
|
const fieldItem = field.descriptor.item;
|
|
51
61
|
|
|
52
62
|
const [showRawValue, setShowRawValue] = useState(false);
|
|
63
|
+
const [showFieldJson, setShowFieldJson] = useState(false);
|
|
64
|
+
const [fieldMenuOpen, setFieldMenuOpen] = useState(false);
|
|
65
|
+
const [fieldHistoryOpen, setFieldHistoryOpen] = useState(false);
|
|
53
66
|
const generatorsOverlay = useRef<FieldActionsOverlayRef>(null);
|
|
54
|
-
const fieldHistoryOverlay = useRef<OverlayPanel>(null);
|
|
55
67
|
const [generatorButtons, setButtons] = useState<FieldButton[]>();
|
|
56
68
|
const [isModified, setIsModified] = useState(false);
|
|
57
69
|
const [isSaved, setIsSaved] = useState(false);
|
|
@@ -134,7 +146,7 @@ export default function FieldListField({
|
|
|
134
146
|
fieldLockedBySession.sessionId !== editContext.sessionId) ||
|
|
135
147
|
false;
|
|
136
148
|
|
|
137
|
-
let classNames = "
|
|
149
|
+
let classNames = "flex items-stretch";
|
|
138
150
|
if (executingAction?.state == "running") classNames += " executing-action ";
|
|
139
151
|
else {
|
|
140
152
|
if (isFocusedField) classNames += " focused-field ";
|
|
@@ -197,6 +209,21 @@ export default function FieldListField({
|
|
|
197
209
|
|
|
198
210
|
const config = editContext.configuration.fieldTypes[field.type];
|
|
199
211
|
|
|
212
|
+
const handleOpenFieldItem = () => {
|
|
213
|
+
// Load the field item in the editor
|
|
214
|
+
editContext.loadItem({
|
|
215
|
+
id: field.id,
|
|
216
|
+
language: "en",
|
|
217
|
+
version: 0,
|
|
218
|
+
});
|
|
219
|
+
setFieldMenuOpen(false);
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const handleShowFieldJson = () => {
|
|
223
|
+
setShowFieldJson(!showFieldJson);
|
|
224
|
+
setFieldMenuOpen(false);
|
|
225
|
+
};
|
|
226
|
+
|
|
200
227
|
return (
|
|
201
228
|
<div
|
|
202
229
|
key={
|
|
@@ -228,9 +255,35 @@ export default function FieldListField({
|
|
|
228
255
|
|
|
229
256
|
<div className="flex-1">
|
|
230
257
|
<div className="flex min-h-7 flex-wrap items-center gap-x-3 gap-y-1">
|
|
231
|
-
|
|
232
|
-
{
|
|
233
|
-
|
|
258
|
+
{!simplified ? (
|
|
259
|
+
<Popover open={fieldMenuOpen} onOpenChange={setFieldMenuOpen}>
|
|
260
|
+
<PopoverTrigger asChild>
|
|
261
|
+
<button className="rounded py-0.5 text-left text-xs font-medium transition-colors hover:bg-gray-100">
|
|
262
|
+
{title || field.displayName || field.name}
|
|
263
|
+
</button>
|
|
264
|
+
</PopoverTrigger>
|
|
265
|
+
<PopoverContent className="w-56 p-1" align="start" side="bottom">
|
|
266
|
+
<button
|
|
267
|
+
className="flex w-full items-center gap-2 rounded px-3 py-2 text-sm transition-colors hover:bg-gray-100"
|
|
268
|
+
onClick={handleOpenFieldItem}
|
|
269
|
+
>
|
|
270
|
+
<ExternalLink strokeWidth={1} className="h-4 w-4" />
|
|
271
|
+
Open Field Item
|
|
272
|
+
</button>
|
|
273
|
+
<button
|
|
274
|
+
className="flex w-full items-center gap-2 rounded px-3 py-2 text-sm transition-colors hover:bg-gray-100"
|
|
275
|
+
onClick={handleShowFieldJson}
|
|
276
|
+
>
|
|
277
|
+
<span className="font-mono text-sm">{"{}"}</span>
|
|
278
|
+
{showFieldJson ? "Hide" : "Show"} Field JSON
|
|
279
|
+
</button>
|
|
280
|
+
</PopoverContent>
|
|
281
|
+
</Popover>
|
|
282
|
+
) : (
|
|
283
|
+
<label className="block text-xs font-medium select-text">
|
|
284
|
+
{title || field.displayName || field.name}
|
|
285
|
+
</label>
|
|
286
|
+
)}
|
|
234
287
|
|
|
235
288
|
{fieldLockedBySession &&
|
|
236
289
|
fieldLockedBySession.sessionId !== editContext.sessionId && (
|
|
@@ -308,22 +361,30 @@ export default function FieldListField({
|
|
|
308
361
|
label="Add Comment"
|
|
309
362
|
/>
|
|
310
363
|
{!readonly && renderGeneratorButtons()}
|
|
311
|
-
<
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
364
|
+
<Popover
|
|
365
|
+
open={fieldHistoryOpen}
|
|
366
|
+
onOpenChange={setFieldHistoryOpen}
|
|
367
|
+
>
|
|
368
|
+
<PopoverTrigger asChild>
|
|
369
|
+
<button
|
|
370
|
+
className="rounded p-1 transition-colors hover:bg-gray-100"
|
|
371
|
+
aria-label="Field History"
|
|
372
|
+
>
|
|
373
|
+
<History strokeWidth={1} className="h-3.5 w-3.5" />
|
|
374
|
+
</button>
|
|
375
|
+
</PopoverTrigger>
|
|
376
|
+
<PopoverContent
|
|
377
|
+
className="w-80 p-2"
|
|
378
|
+
align="end"
|
|
379
|
+
side="bottom"
|
|
380
|
+
>
|
|
381
|
+
<FieldHistory
|
|
382
|
+
field={field}
|
|
383
|
+
onHover={(x) => setHistoryEntry(x)}
|
|
384
|
+
onRevert={() => setFieldHistoryOpen(false)}
|
|
385
|
+
/>
|
|
386
|
+
</PopoverContent>
|
|
387
|
+
</Popover>
|
|
327
388
|
</div>
|
|
328
389
|
</>
|
|
329
390
|
)}
|
|
@@ -333,6 +394,20 @@ export default function FieldListField({
|
|
|
333
394
|
{executingAction?.message}
|
|
334
395
|
</div>
|
|
335
396
|
)}
|
|
397
|
+
|
|
398
|
+
{/* Field JSON Display */}
|
|
399
|
+
{showFieldJson && (
|
|
400
|
+
<div className="mt-2 mb-2 rounded border bg-gray-50 p-2 text-xs">
|
|
401
|
+
<div className="mb-2 flex items-center justify-between">
|
|
402
|
+
<span className="font-medium">Field JSON</span>
|
|
403
|
+
<CopyButton textToCopy={JSON.stringify(field, null, 2)} />
|
|
404
|
+
</div>
|
|
405
|
+
<pre className="max-h-40 overflow-auto text-xs whitespace-pre-wrap">
|
|
406
|
+
{JSON.stringify(field, null, 2)}
|
|
407
|
+
</pre>
|
|
408
|
+
</div>
|
|
409
|
+
)}
|
|
410
|
+
|
|
336
411
|
{!compareToField && (
|
|
337
412
|
<div className="mt-1 p-0 text-xs">
|
|
338
413
|
{getFieldEditor(
|
|
@@ -1796,14 +1796,16 @@ export function EditorClient({
|
|
|
1796
1796
|
return;
|
|
1797
1797
|
}
|
|
1798
1798
|
|
|
1799
|
+
console.log("dragObject", dragObject);
|
|
1800
|
+
|
|
1799
1801
|
if (
|
|
1800
1802
|
dragObject.type == "component" ||
|
|
1801
|
-
(dragObject.type == "
|
|
1803
|
+
(dragObject.type == "items" && dragObject.items)
|
|
1802
1804
|
) {
|
|
1803
1805
|
const parentComponent =
|
|
1804
1806
|
parentId && page ? getComponentById(parentId, page) : null;
|
|
1805
1807
|
|
|
1806
|
-
if (dragObject.type == "
|
|
1808
|
+
if (dragObject.type == "items" && dragObject.items) {
|
|
1807
1809
|
op = {
|
|
1808
1810
|
type: "link-component",
|
|
1809
1811
|
mainItem: page!.item.descriptor,
|
|
@@ -1817,9 +1819,10 @@ export function EditorClient({
|
|
|
1817
1819
|
placeholderIndex: index,
|
|
1818
1820
|
date: new Date().toISOString(),
|
|
1819
1821
|
id: uuid(),
|
|
1820
|
-
linkedComponentItem: dragObject.
|
|
1822
|
+
linkedComponentItem: dragObject.items[0],
|
|
1821
1823
|
description: "Link component",
|
|
1822
1824
|
} as LinkComponentOperation;
|
|
1825
|
+
console.log("op", op);
|
|
1823
1826
|
} else {
|
|
1824
1827
|
if (!dragObject.component) return;
|
|
1825
1828
|
op = {
|
|
@@ -11,6 +11,7 @@ import { fetchItems, fetchItemStubs } from "../services/contentService";
|
|
|
11
11
|
import { getItemDescriptor } from "../utils";
|
|
12
12
|
import { EditedField, ModifiedField } from "./editContext";
|
|
13
13
|
import { User } from "../../types";
|
|
14
|
+
import { templatesRootItemId } from "../../config/config";
|
|
14
15
|
|
|
15
16
|
export type ItemChange = {
|
|
16
17
|
item: ItemDescriptor;
|
|
@@ -43,6 +44,52 @@ export type ItemsRepository = {
|
|
|
43
44
|
clear: () => void;
|
|
44
45
|
};
|
|
45
46
|
|
|
47
|
+
// Standard Sitecore template IDs for template-related items
|
|
48
|
+
const TEMPLATE_TEMPLATE_ID = "AB86861A-6030-46C5-B394-E8F99E8B87DB"; // Template template
|
|
49
|
+
const TEMPLATE_FIELD_TEMPLATE_ID = "455A3E98-A627-4B40-8035-E683A0331AC7"; // Template field template
|
|
50
|
+
const TEMPLATE_SECTION_TEMPLATE_ID = "E269FBB5-3750-427A-9149-7AA950B49301"; // Template section template
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Checks if an item change represents a template-related modification that should trigger cache clearing
|
|
54
|
+
*/
|
|
55
|
+
const isTemplateChange = async (
|
|
56
|
+
change: ItemChange,
|
|
57
|
+
getItem: (descriptor: ItemDescriptor) => Promise<FullItem | undefined>,
|
|
58
|
+
): Promise<boolean> => {
|
|
59
|
+
try {
|
|
60
|
+
const item = await getItem(change.item);
|
|
61
|
+
if (!item) return false;
|
|
62
|
+
|
|
63
|
+
// Check if the item is under the /sitecore/templates path
|
|
64
|
+
if (
|
|
65
|
+
item.path &&
|
|
66
|
+
item.path.toLowerCase().startsWith("/sitecore/templates")
|
|
67
|
+
) {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Check if the item's templateId indicates it's a template-related item
|
|
72
|
+
const templateId = item.templateId?.toLowerCase();
|
|
73
|
+
if (
|
|
74
|
+
templateId === TEMPLATE_TEMPLATE_ID.toLowerCase() ||
|
|
75
|
+
templateId === TEMPLATE_FIELD_TEMPLATE_ID.toLowerCase() ||
|
|
76
|
+
templateId === TEMPLATE_SECTION_TEMPLATE_ID.toLowerCase()
|
|
77
|
+
) {
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Check if the item is under the templates root using the configured templates root ID
|
|
82
|
+
if (item.path && item.path.includes(templatesRootItemId)) {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return false;
|
|
87
|
+
} catch (error) {
|
|
88
|
+
console.error("Error checking if item change is template-related:", error);
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
46
93
|
export function useItemsRepository(
|
|
47
94
|
setModifiedFields: React.Dispatch<React.SetStateAction<ModifiedField[]>>,
|
|
48
95
|
setLastEditedFields: React.Dispatch<React.SetStateAction<EditedField[]>>,
|
|
@@ -509,16 +556,40 @@ export function useItemsRepository(
|
|
|
509
556
|
[setRevision],
|
|
510
557
|
);
|
|
511
558
|
|
|
559
|
+
const clear = useCallback(() => {
|
|
560
|
+
itemsMap.current.clear();
|
|
561
|
+
stubsMap.current.clear();
|
|
562
|
+
fallbackDependencyMap.current.clear();
|
|
563
|
+
//setRevision((x) => x + 1);
|
|
564
|
+
}, []);
|
|
565
|
+
|
|
512
566
|
const subscribeItemsChanged = useCallback(
|
|
513
567
|
(callback: (changes: ItemChange[]) => void) => {
|
|
514
|
-
|
|
568
|
+
const wrappedCallback = async (changes: ItemChange[]) => {
|
|
569
|
+
// Check if any of the changes are template-related
|
|
570
|
+
const templateChanges = await Promise.all(
|
|
571
|
+
changes.map((change) => isTemplateChange(change, getItem)),
|
|
572
|
+
);
|
|
573
|
+
|
|
574
|
+
// If any template changes are detected, clear the cache
|
|
575
|
+
if (templateChanges.some((isTemplate) => isTemplate)) {
|
|
576
|
+
console.log("Template changes detected, clearing cache...");
|
|
577
|
+
clear();
|
|
578
|
+
setRevision((prev) => prev + 1);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// Call the original callback
|
|
582
|
+
callback(changes);
|
|
583
|
+
};
|
|
584
|
+
|
|
585
|
+
itemsChangedListeners.current.add(wrappedCallback);
|
|
515
586
|
|
|
516
587
|
// Return an unsubscribe function
|
|
517
588
|
return () => {
|
|
518
|
-
itemsChangedListeners.current.delete(
|
|
589
|
+
itemsChangedListeners.current.delete(wrappedCallback);
|
|
519
590
|
};
|
|
520
591
|
},
|
|
521
|
-
[],
|
|
592
|
+
[clear, getItem],
|
|
522
593
|
);
|
|
523
594
|
|
|
524
595
|
const triggerItemsChangedEvent = (changes: ItemChange[]) => {
|
|
@@ -528,13 +599,6 @@ export function useItemsRepository(
|
|
|
528
599
|
}
|
|
529
600
|
};
|
|
530
601
|
|
|
531
|
-
const clear = useCallback(() => {
|
|
532
|
-
itemsMap.current.clear();
|
|
533
|
-
stubsMap.current.clear();
|
|
534
|
-
fallbackDependencyMap.current.clear();
|
|
535
|
-
//setRevision((x) => x + 1);
|
|
536
|
-
}, []);
|
|
537
|
-
|
|
538
602
|
const repo = useMemo(() => {
|
|
539
603
|
return {
|
|
540
604
|
getItems,
|
|
@@ -161,7 +161,7 @@ export function usePageModel(
|
|
|
161
161
|
datasourceItem,
|
|
162
162
|
items,
|
|
163
163
|
placeholders,
|
|
164
|
-
isShared: !
|
|
164
|
+
isShared: !datasourceItem?.path?.startsWith(pageItem.path) || false,
|
|
165
165
|
parentPlaceholder,
|
|
166
166
|
editable: skeleton.editable ?? true,
|
|
167
167
|
type: datasourceItem?.templateName || name, // TODO: We need the name for AI page wizard - we should not use the template name
|
|
@@ -41,8 +41,8 @@ export const deleteItemCommand: ItemCommand = {
|
|
|
41
41
|
message: (
|
|
42
42
|
<div>
|
|
43
43
|
Are you sure you want to delete{" "}
|
|
44
|
-
<em>{items.map((x) => x.name).join(", ")}</em>?
|
|
45
|
-
undone
|
|
44
|
+
<em>{items.map((x) => x.name).join(", ")}</em>?
|
|
45
|
+
<div className="mt-4">This cannot be undone.</div>
|
|
46
46
|
</div>
|
|
47
47
|
),
|
|
48
48
|
header: "Confirmation",
|
|
@@ -358,7 +358,6 @@ export const publishItemCommand: ItemCommand = {
|
|
|
358
358
|
},
|
|
359
359
|
};
|
|
360
360
|
|
|
361
|
-
|
|
362
361
|
export const exportItemsCommand: ItemCommand = {
|
|
363
362
|
id: "exportItem",
|
|
364
363
|
label: "Export",
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { format } from "date-fns";
|
|
5
|
+
import { Calendar as CalendarIcon } from "lucide-react";
|
|
6
|
+
import { useEditContext } from "../client/editContext";
|
|
7
|
+
import { cn } from "../../lib/utils";
|
|
8
|
+
import { Button } from "../../components/ui/button";
|
|
9
|
+
import {
|
|
10
|
+
Popover,
|
|
11
|
+
PopoverContent,
|
|
12
|
+
PopoverTrigger,
|
|
13
|
+
} from "../../components/ui/popover";
|
|
14
|
+
import { Calendar } from "../../components/ui/calendar";
|
|
15
|
+
import { useEffect, useState } from "react";
|
|
16
|
+
import { ProgressSpinner } from "primereact/progressspinner";
|
|
17
|
+
import { DateField, DateFieldValue } from "../fieldTypes";
|
|
18
|
+
|
|
19
|
+
// Helper function to format date to compact ISO format: YYYYMMDDTHHMMSSZ
|
|
20
|
+
const formatDateToCompactISO = (date: Date): string => {
|
|
21
|
+
const year = date.getUTCFullYear().toString();
|
|
22
|
+
const month = (date.getUTCMonth() + 1).toString().padStart(2, "0");
|
|
23
|
+
const day = date.getUTCDate().toString().padStart(2, "0");
|
|
24
|
+
const hours = date.getUTCHours().toString().padStart(2, "0");
|
|
25
|
+
const minutes = date.getUTCMinutes().toString().padStart(2, "0");
|
|
26
|
+
const seconds = date.getUTCSeconds().toString().padStart(2, "0");
|
|
27
|
+
|
|
28
|
+
return `${year}${month}${day}T${hours}${minutes}${seconds}Z`;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export function DateFieldEditor({
|
|
32
|
+
field,
|
|
33
|
+
readOnly,
|
|
34
|
+
}: {
|
|
35
|
+
field: DateField;
|
|
36
|
+
readOnly?: boolean;
|
|
37
|
+
}) {
|
|
38
|
+
const editContext = useEditContext();
|
|
39
|
+
const [isUpdating, setIsUpdating] = useState(false);
|
|
40
|
+
const [date, setDate] = useState<Date | undefined>(undefined);
|
|
41
|
+
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
// Parse the date from the field rawValue (compact ISO format: YYYYMMDDTHHMMSSZ)
|
|
44
|
+
const dateString = field.rawValue;
|
|
45
|
+
|
|
46
|
+
if (dateString && dateString.trim() !== "") {
|
|
47
|
+
try {
|
|
48
|
+
// Handle compact ISO format: 20250714T220000Z
|
|
49
|
+
let parsedDate: Date;
|
|
50
|
+
if (dateString.match(/^\d{8}T\d{6}Z$/)) {
|
|
51
|
+
// Convert compact format to standard ISO: 20250714T220000Z -> 2025-07-14T22:00:00Z
|
|
52
|
+
const standardISO = dateString.replace(
|
|
53
|
+
/(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})Z/,
|
|
54
|
+
"$1-$2-$3T$4:$5:$6Z",
|
|
55
|
+
);
|
|
56
|
+
parsedDate = new Date(standardISO);
|
|
57
|
+
} else {
|
|
58
|
+
// Fallback to direct parsing
|
|
59
|
+
parsedDate = new Date(dateString);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!isNaN(parsedDate.getTime())) {
|
|
63
|
+
setDate(parsedDate);
|
|
64
|
+
} else {
|
|
65
|
+
setDate(undefined);
|
|
66
|
+
}
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.warn("Failed to parse date from field value:", dateString);
|
|
69
|
+
setDate(undefined);
|
|
70
|
+
}
|
|
71
|
+
} else {
|
|
72
|
+
setDate(undefined);
|
|
73
|
+
}
|
|
74
|
+
}, [field.rawValue]);
|
|
75
|
+
|
|
76
|
+
if (!editContext) return null;
|
|
77
|
+
|
|
78
|
+
const fieldItem = field.descriptor.item;
|
|
79
|
+
if (!fieldItem) return null;
|
|
80
|
+
|
|
81
|
+
if (isUpdating) {
|
|
82
|
+
return <ProgressSpinner style={{ width: "18px", height: "18px" }} />;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const handleDateSelect = async (selectedDate: Date | undefined) => {
|
|
86
|
+
if (readOnly) return;
|
|
87
|
+
|
|
88
|
+
setIsUpdating(true);
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
// Format date to compact ISO format: YYYYMMDDTHHMMSSZ
|
|
92
|
+
const rawValue = selectedDate ? formatDateToCompactISO(selectedDate) : "";
|
|
93
|
+
|
|
94
|
+
await editContext.operations.editField({
|
|
95
|
+
field: field.descriptor,
|
|
96
|
+
rawValue: rawValue,
|
|
97
|
+
value: rawValue,
|
|
98
|
+
refresh: "immediate",
|
|
99
|
+
});
|
|
100
|
+
} catch (error) {
|
|
101
|
+
console.error("Failed to update date field:", error);
|
|
102
|
+
// Show error to user if possible
|
|
103
|
+
if (editContext.showToast) {
|
|
104
|
+
editContext.showToast("Failed to update date. Please try again.");
|
|
105
|
+
}
|
|
106
|
+
} finally {
|
|
107
|
+
setIsUpdating(false);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<Popover>
|
|
113
|
+
<PopoverTrigger asChild>
|
|
114
|
+
<Button
|
|
115
|
+
variant="outline"
|
|
116
|
+
data-empty={!date}
|
|
117
|
+
className={cn(
|
|
118
|
+
"bg-gray-5 justify-start p-1 text-left text-xs font-normal",
|
|
119
|
+
!date && "text-muted-foreground",
|
|
120
|
+
)}
|
|
121
|
+
disabled={readOnly}
|
|
122
|
+
>
|
|
123
|
+
<CalendarIcon className="mr-2 h-4 w-4" />
|
|
124
|
+
{date ? format(date, "PPP") : <span>Pick a date!</span>}
|
|
125
|
+
</Button>
|
|
126
|
+
</PopoverTrigger>
|
|
127
|
+
<PopoverContent className="w-auto p-0" align="start">
|
|
128
|
+
<Calendar mode="single" selected={date} onSelect={handleDateSelect} />
|
|
129
|
+
</PopoverContent>
|
|
130
|
+
</Popover>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
@@ -36,7 +36,7 @@ export function ImageFieldEditor({
|
|
|
36
36
|
|
|
37
37
|
return (
|
|
38
38
|
<div>
|
|
39
|
-
<div className="focus-shadow flex h-full flex-wrap gap-2">
|
|
39
|
+
<div className="focus-shadow bg-gray-5 flex h-full flex-wrap gap-2">
|
|
40
40
|
<div className="relative flex h-48 w-48 cursor-pointer items-center justify-center border border-gray-300">
|
|
41
41
|
{!readOnly && <ImageEditor field={field} />}
|
|
42
42
|
</div>
|
|
@@ -137,8 +137,8 @@ export function InternalLinkFieldEditor({
|
|
|
137
137
|
<div
|
|
138
138
|
ref={texboxRef}
|
|
139
139
|
className={classNames(
|
|
140
|
-
"justiy-between focus-shadow flex cursor-pointer justify-between border p-1.5 text-xs",
|
|
141
|
-
readOnly ? "bg-gray-
|
|
140
|
+
"justiy-between focus-shadow bg-gray-5 flex cursor-pointer justify-between border p-1.5 text-xs",
|
|
141
|
+
readOnly ? "bg-gray-100" : "",
|
|
142
142
|
)}
|
|
143
143
|
onClick={(e) => {
|
|
144
144
|
if (readOnly) return;
|
|
@@ -34,8 +34,8 @@ export function LinkFieldEditor({
|
|
|
34
34
|
<>
|
|
35
35
|
<div
|
|
36
36
|
className={classNames(
|
|
37
|
-
"focus-shadow border-gray-3 bg-gray-5 flex justify-between border p-
|
|
38
|
-
readOnly ? "bg-gray-100" : "cursor-pointer
|
|
37
|
+
"focus-shadow border-gray-3 bg-gray-5 flex justify-between border p-1.5",
|
|
38
|
+
readOnly ? "bg-gray-100" : "cursor-pointer",
|
|
39
39
|
)}
|
|
40
40
|
onClick={(e) => {
|
|
41
41
|
if (readOnly) return;
|
|
@@ -57,7 +57,11 @@ export function MultiLineText({
|
|
|
57
57
|
fieldItem.language &&
|
|
58
58
|
editContextRef.current?.focusedField?.item.version === fieldItem.version
|
|
59
59
|
) {
|
|
60
|
-
if (
|
|
60
|
+
// Only focus if no other element currently has focus (e.g., popover buttons)
|
|
61
|
+
if (
|
|
62
|
+
inputRef.current &&
|
|
63
|
+
(!document.activeElement || document.activeElement === document.body)
|
|
64
|
+
) {
|
|
61
65
|
inputRef.current.focus();
|
|
62
66
|
}
|
|
63
67
|
}
|
|
@@ -34,7 +34,7 @@ export function PictureFieldEditor({
|
|
|
34
34
|
|
|
35
35
|
return (
|
|
36
36
|
<div>
|
|
37
|
-
<div className="focus-shadow flex h-full flex-wrap gap-2">
|
|
37
|
+
<div className="focus-shadow bg-gray-5 flex h-full flex-wrap gap-2">
|
|
38
38
|
{field?.value?.variants?.map((variant) => (
|
|
39
39
|
<div key={variant.name}>
|
|
40
40
|
<div
|
|
@@ -141,7 +141,11 @@ export function SingleLineText({
|
|
|
141
141
|
fieldItem.language &&
|
|
142
142
|
editContextRef.current?.focusedField?.item.version === fieldItem.version
|
|
143
143
|
) {
|
|
144
|
-
if (
|
|
144
|
+
// Only focus if no other element currently has focus (e.g., popover buttons)
|
|
145
|
+
if (
|
|
146
|
+
inputRef.current &&
|
|
147
|
+
(!document.activeElement || document.activeElement === document.body)
|
|
148
|
+
) {
|
|
145
149
|
inputRef.current.focus();
|
|
146
150
|
}
|
|
147
151
|
}
|