@alpaca-editor/core 1.0.4014 → 1.0.4017

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.
Files changed (113) hide show
  1. package/dist/components/ui/calendar.js +2 -6
  2. package/dist/components/ui/calendar.js.map +1 -1
  3. package/dist/components/ui/card.d.ts +2 -1
  4. package/dist/components/ui/card.js +2 -2
  5. package/dist/components/ui/card.js.map +1 -1
  6. package/dist/components/ui/copy-button.js +31 -3
  7. package/dist/components/ui/copy-button.js.map +1 -1
  8. package/dist/components/ui/popover.js +6 -3
  9. package/dist/components/ui/popover.js.map +1 -1
  10. package/dist/config/config.js +20 -13
  11. package/dist/config/config.js.map +1 -1
  12. package/dist/editor/FieldList.js +1 -1
  13. package/dist/editor/FieldList.js.map +1 -1
  14. package/dist/editor/FieldListField.js +1 -1
  15. package/dist/editor/FieldListField.js.map +1 -1
  16. package/dist/editor/ScrollingContentTree.d.ts +2 -1
  17. package/dist/editor/ScrollingContentTree.js +2 -2
  18. package/dist/editor/ScrollingContentTree.js.map +1 -1
  19. package/dist/editor/client/editContext.d.ts +1 -0
  20. package/dist/editor/client/editContext.js.map +1 -1
  21. package/dist/editor/client/itemsRepository.d.ts +2 -2
  22. package/dist/editor/client/itemsRepository.js +15 -5
  23. package/dist/editor/client/itemsRepository.js.map +1 -1
  24. package/dist/editor/client/operations.js +18 -6
  25. package/dist/editor/client/operations.js.map +1 -1
  26. package/dist/editor/client/pageModelBuilder.js +2 -1
  27. package/dist/editor/client/pageModelBuilder.js.map +1 -1
  28. package/dist/editor/context-menu/InsertMenu.js +45 -12
  29. package/dist/editor/context-menu/InsertMenu.js.map +1 -1
  30. package/dist/editor/field-types/DateFieldEditor.js +1 -1
  31. package/dist/editor/field-types/DateFieldEditor.js.map +1 -1
  32. package/dist/editor/field-types/DateTimeFieldEditor.d.ts +5 -0
  33. package/dist/editor/field-types/DateTimeFieldEditor.js +151 -0
  34. package/dist/editor/field-types/DateTimeFieldEditor.js.map +1 -0
  35. package/dist/editor/field-types/InternalLinkFieldEditor.js +1 -1
  36. package/dist/editor/field-types/InternalLinkFieldEditor.js.map +1 -1
  37. package/dist/editor/field-types/TreeListEditor.js +17 -6
  38. package/dist/editor/field-types/TreeListEditor.js.map +1 -1
  39. package/dist/editor/fieldTypes.d.ts +6 -0
  40. package/dist/editor/menubar/ToolbarFactory.js +1 -1
  41. package/dist/editor/menubar/ToolbarFactory.js.map +1 -1
  42. package/dist/editor/menubar/toolbar-sections/EditControls.d.ts +7 -1
  43. package/dist/editor/menubar/toolbar-sections/EditControls.js +2 -2
  44. package/dist/editor/menubar/toolbar-sections/EditControls.js.map +1 -1
  45. package/dist/editor/page-viewer/EditorForm.js +3 -1
  46. package/dist/editor/page-viewer/EditorForm.js.map +1 -1
  47. package/dist/editor/page-viewer/PageViewer.d.ts +2 -1
  48. package/dist/editor/page-viewer/PageViewer.js +7 -5
  49. package/dist/editor/page-viewer/PageViewer.js.map +1 -1
  50. package/dist/editor/page-viewer/PageViewerFrame.d.ts +2 -1
  51. package/dist/editor/page-viewer/PageViewerFrame.js +3 -2
  52. package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
  53. package/dist/editor/sidebar/ComponentPalette.js +28 -1
  54. package/dist/editor/sidebar/ComponentPalette.js.map +1 -1
  55. package/dist/editor/sidebar/Debug.js +13 -3
  56. package/dist/editor/sidebar/Debug.js.map +1 -1
  57. package/dist/editor/ui/ItemSearch.d.ts +1 -0
  58. package/dist/editor/ui/ItemSearch.js +5 -5
  59. package/dist/editor/ui/ItemSearch.js.map +1 -1
  60. package/dist/editor/ui/SimpleTabs.d.ts +2 -1
  61. package/dist/editor/ui/SimpleTabs.js +8 -7
  62. package/dist/editor/ui/SimpleTabs.js.map +1 -1
  63. package/dist/editor/ui/Spinner.d.ts +1 -1
  64. package/dist/page-wizard/WizardSteps.js +5 -4
  65. package/dist/page-wizard/WizardSteps.js.map +1 -1
  66. package/dist/page-wizard/steps/CollectStep.js +1 -1
  67. package/dist/page-wizard/steps/CollectStep.js.map +1 -1
  68. package/dist/page-wizard/steps/ContentStep.js +4 -3
  69. package/dist/page-wizard/steps/ContentStep.js.map +1 -1
  70. package/dist/page-wizard/steps/FindItemsStep.js +2 -13
  71. package/dist/page-wizard/steps/FindItemsStep.js.map +1 -1
  72. package/dist/page-wizard/steps/usePageCreator.js +2 -4
  73. package/dist/page-wizard/steps/usePageCreator.js.map +1 -1
  74. package/dist/revision.d.ts +2 -2
  75. package/dist/revision.js +2 -2
  76. package/dist/styles.css +357 -60
  77. package/package.json +1 -1
  78. package/src/components/ui/calendar.tsx +2 -18
  79. package/src/components/ui/card.tsx +5 -0
  80. package/src/components/ui/copy-button.tsx +32 -3
  81. package/src/components/ui/popover.tsx +6 -3
  82. package/src/config/config.tsx +21 -14
  83. package/src/editor/FieldList.tsx +1 -1
  84. package/src/editor/FieldListField.tsx +1 -1
  85. package/src/editor/ScrollingContentTree.tsx +3 -0
  86. package/src/editor/client/editContext.ts +1 -0
  87. package/src/editor/client/itemsRepository.ts +25 -7
  88. package/src/editor/client/operations.ts +19 -5
  89. package/src/editor/client/pageModelBuilder.ts +1 -1
  90. package/src/editor/context-menu/InsertMenu.tsx +77 -30
  91. package/src/editor/field-types/DateFieldEditor.tsx +8 -2
  92. package/src/editor/field-types/DateTimeFieldEditor.tsx +281 -0
  93. package/src/editor/field-types/InternalLinkFieldEditor.tsx +1 -1
  94. package/src/editor/field-types/TreeListEditor.tsx +41 -8
  95. package/src/editor/fieldTypes.ts +8 -0
  96. package/src/editor/menubar/ToolbarFactory.tsx +2 -1
  97. package/src/editor/menubar/toolbar-sections/EditControls.tsx +37 -23
  98. package/src/editor/page-editor-chrome/PlaceholderDropZone.tsx +1 -1
  99. package/src/editor/page-viewer/EditorForm.tsx +3 -1
  100. package/src/editor/page-viewer/PageViewer.tsx +18 -15
  101. package/src/editor/page-viewer/PageViewerFrame.tsx +11 -2
  102. package/src/editor/sidebar/ComponentPalette.tsx +36 -1
  103. package/src/editor/sidebar/Debug.tsx +14 -6
  104. package/src/editor/ui/ItemSearch.tsx +6 -3
  105. package/src/editor/ui/SimpleTabs.tsx +10 -1
  106. package/src/editor/ui/Spinner.tsx +1 -1
  107. package/src/page-wizard/WizardSteps.tsx +2 -3
  108. package/src/page-wizard/steps/CollectStep.tsx +1 -1
  109. package/src/page-wizard/steps/ContentStep.tsx +18 -12
  110. package/src/page-wizard/steps/FindItemsStep.tsx +1 -50
  111. package/src/page-wizard/steps/usePageCreator.ts +2 -3
  112. package/src/revision.ts +2 -2
  113. package/styles.css +1 -0
@@ -16,6 +16,7 @@ import { DropListEditor } from "../editor/field-types/DropListEditor";
16
16
  import { RawEditor } from "../editor/field-types/RawEditor";
17
17
  import { CheckboxEditor } from "../editor/field-types/CheckboxEditor";
18
18
  import { DateFieldEditor } from "../editor/field-types/DateFieldEditor";
19
+ import { DateTimeFieldEditor } from "../editor/field-types/DateTimeFieldEditor";
19
20
 
20
21
  import { ItemLocked } from "../editor/editor-warnings/ItemLocked";
21
22
  import { NoWriteAccess } from "../editor/editor-warnings/NoWriteAccess";
@@ -116,6 +117,7 @@ import {
116
117
  Bug,
117
118
  Timer,
118
119
  WandSparkles,
120
+ BadgeCheck,
119
121
  } from "lucide-react";
120
122
 
121
123
  import { Completions } from "../editor/sidebar/Completions";
@@ -284,6 +286,9 @@ export const getConfiguration = (): EditorConfiguration => {
284
286
  date: {
285
287
  editor: DateFieldEditor,
286
288
  },
289
+ datetime: {
290
+ editor: DateTimeFieldEditor,
291
+ },
287
292
  droptree: {
288
293
  editor: InternalLinkFieldEditor,
289
294
  },
@@ -499,8 +504,8 @@ export const getConfiguration = (): EditorConfiguration => {
499
504
  ...pageEditorViewBase,
500
505
  },
501
506
  {
502
- name: "reviews",
503
- title: "Reviews",
507
+ name: "comments",
508
+ title: "Comments",
504
509
  icon: <MessageCircleMore strokeWidth={1} />,
505
510
  leftSidebar: {
506
511
  panels: [
@@ -509,15 +514,25 @@ export const getConfiguration = (): EditorConfiguration => {
509
514
  icon: "pi pi-comments",
510
515
  title: "Comments & Suggestions",
511
516
  content: <Comments />,
512
- initialSize: 65,
517
+ initialSize: 100,
513
518
  noOverflow: true,
514
519
  },
520
+ ],
521
+ },
522
+ ...pageEditorViewBase,
523
+ },
524
+ {
525
+ name: "reviews",
526
+ title: "Reviews",
527
+ icon: <BadgeCheck strokeWidth={1} />,
528
+ leftSidebar: {
529
+ panels: [
515
530
  {
516
531
  name: "reviews",
517
- icon: "pi pi-users",
532
+
518
533
  title: "Reviews",
519
534
  content: <Reviews />,
520
- initialSize: 35,
535
+ initialSize: 100,
521
536
  noOverflow: true,
522
537
  },
523
538
  ],
@@ -947,18 +962,10 @@ export function configureForUser(
947
962
  editor: {
948
963
  ...configuration.editor,
949
964
  views: configuration.editor.views
950
- .filter((x) => x.name === "reviews")
965
+ .filter((x) => x.name === "comments" || x.name === "reviews")
951
966
  .map((x) => {
952
967
  return {
953
968
  ...x,
954
- leftSidebar: {
955
- ...x.leftSidebar,
956
- panels: [
957
- ...(x.leftSidebar?.panels ?? []).filter(
958
- (x) => x.name !== "reviews",
959
- ),
960
- ],
961
- },
962
969
  toolbarFactory: previewToolbar,
963
970
  };
964
971
  }),
@@ -55,7 +55,7 @@ export function FieldList({
55
55
 
56
56
  return (
57
57
  <div className="text-dark">
58
- <div className="tour-field-list sticky top-0 z-10 ml-[1px] flex items-center gap-2 border-b border-gray-200 bg-white p-2">
58
+ <div className="tour-field-list sticky top-0 z-10 flex items-center gap-2 border-b border-gray-200 bg-white p-2">
59
59
  <FilterInput
60
60
  className="flex-1"
61
61
  value={searchFilter}
@@ -402,7 +402,7 @@ export default function FieldListField({
402
402
  <span className="font-medium">Field JSON</span>
403
403
  <CopyButton textToCopy={JSON.stringify(field, null, 2)} />
404
404
  </div>
405
- <pre className="max-h-40 overflow-auto text-xs whitespace-pre-wrap">
405
+ <pre className="max-h-40 overflow-auto text-xs whitespace-pre-wrap select-text">
406
406
  {JSON.stringify(field, null, 2)}
407
407
  </pre>
408
408
  </div>
@@ -10,12 +10,14 @@ export function ScrollingContentTree({
10
10
  selectedItemId,
11
11
  onSelectionChange,
12
12
  rootItemId,
13
+ rootItemIds,
13
14
  expandedItemId,
14
15
  scrollToSelected = true,
15
16
  }: {
16
17
  selectedItemId?: string;
17
18
  onSelectionChange?: (itemIds: ItemTreeNodeData[]) => void;
18
19
  rootItemId?: string;
20
+ rootItemIds?: string[];
19
21
  expandedItemId?: string;
20
22
  scrollToSelected?: boolean;
21
23
  }) {
@@ -62,6 +64,7 @@ export function ScrollingContentTree({
62
64
  <ContentTree
63
65
  language={editContext!.currentItemDescriptor?.language ?? "en"}
64
66
  rootItemId={rootItemId ?? contentItemId}
67
+ rootItemIds={rootItemIds}
65
68
  expandIdPath={expandedItem?.idPath}
66
69
  selectedItemIds={selectedItemIds}
67
70
  selectionMode="single"
@@ -402,6 +402,7 @@ export type ModifiedField = FieldDescriptor & {
402
402
  isDirty: boolean;
403
403
  modifiedBy: User;
404
404
  timestamp: number;
405
+ saveSequence: number;
405
406
  };
406
407
 
407
408
  export type ModifiedFieldsContextType = {
@@ -31,11 +31,15 @@ export type ItemsRepository = {
31
31
  isDirty: boolean,
32
32
  value: any | null,
33
33
  rawValue?: string | null,
34
- ) => void;
34
+ ) => Promise<number>;
35
35
 
36
36
  refreshItems: (items: ItemDescriptor[]) => Promise<void>;
37
37
  revision: number;
38
- onFieldSaved: (field: FieldDescriptor, value: string | null) => void;
38
+ onFieldSaved: (
39
+ field: FieldDescriptor,
40
+ value: string | null,
41
+ saveSequence: number,
42
+ ) => void;
39
43
  onItemsDeleted: (items: DeletedItem[]) => void;
40
44
  //addToModifiedFields: (fieldDescriptors: FieldDescriptor[]) => void;
41
45
  subscribeItemsChanged: (
@@ -101,6 +105,7 @@ export function useItemsRepository(
101
105
  );
102
106
  const pendingRequests = useRef<Map<string, Promise<void>>>(new Map());
103
107
  const pendingStubsRequests = useRef<Map<string, Promise<void>>>(new Map());
108
+ const saveSequenceCounter = useRef<number>(0);
104
109
 
105
110
  const [revision, setRevision] = useState(0);
106
111
  const itemsChangedListeners = useRef<Set<(changes: ItemChange[]) => void>>(
@@ -366,10 +371,10 @@ export function useItemsRepository(
366
371
  rawValue?: string | null,
367
372
  ) => {
368
373
  const item = await getItem(field.item);
369
- if (!item) return;
374
+ if (!item) return 0;
370
375
 
371
376
  const fieldToUpdate = item.fields.find((x) => x.id === field.fieldId);
372
- if (!fieldToUpdate) return;
377
+ if (!fieldToUpdate) return 0;
373
378
 
374
379
  // Update the field value in the item copy
375
380
  const updatedItem = {
@@ -387,8 +392,10 @@ export function useItemsRepository(
387
392
 
388
393
  itemsMap.current.set(generateKey(field.item), updatedItem);
389
394
 
395
+ const val = rawValue !== undefined ? rawValue : value;
396
+ const currentSaveSequence = ++saveSequenceCounter.current;
397
+
390
398
  setModifiedFields((prevModifiedFields) => {
391
- const val = rawValue !== undefined ? rawValue : value;
392
399
  const modifiedField = prevModifiedFields.find(
393
400
  (x) =>
394
401
  x.fieldId === field.fieldId &&
@@ -405,6 +412,7 @@ export function useItemsRepository(
405
412
  isDirty: isDirty,
406
413
  modifiedBy: user,
407
414
  timestamp: Date.now(),
415
+ saveSequence: currentSaveSequence,
408
416
  },
409
417
  ];
410
418
  } else {
@@ -412,6 +420,7 @@ export function useItemsRepository(
412
420
  modifiedField.value = val;
413
421
  modifiedField.modifiedBy = user;
414
422
  modifiedField.timestamp = Date.now();
423
+ modifiedField.saveSequence = currentSaveSequence;
415
424
  }
416
425
 
417
426
  return [...prevModifiedFields];
@@ -432,6 +441,7 @@ export function useItemsRepository(
432
441
  });
433
442
 
434
443
  // setRevision((prev) => prev + 1);
444
+ return currentSaveSequence;
435
445
  },
436
446
  [getItem, setModifiedFields, setLastEditedFields],
437
447
  );
@@ -510,7 +520,7 @@ export function useItemsRepository(
510
520
  );
511
521
 
512
522
  const onFieldSaved = useCallback(
513
- (field: FieldDescriptor, value: string | null) => {
523
+ (field: FieldDescriptor, value: string | null, saveSequence: number) => {
514
524
  setModifiedFields((prevModifiedFields) => {
515
525
  const modifiedField = prevModifiedFields.find(
516
526
  (x) =>
@@ -520,7 +530,15 @@ export function useItemsRepository(
520
530
  x.item.version === field.item.version,
521
531
  );
522
532
  if (modifiedField) {
523
- if (modifiedField.value === value) modifiedField.isDirty = false;
533
+ // Only mark as not dirty if this save operation corresponds to the most recent edit
534
+ // or if the value matches and this is the latest save operation
535
+ if (
536
+ modifiedField.saveSequence === saveSequence ||
537
+ (modifiedField.value === value &&
538
+ modifiedField.saveSequence <= saveSequence)
539
+ ) {
540
+ modifiedField.isDirty = false;
541
+ }
524
542
  return [...prevModifiedFields];
525
543
  }
526
544
  return prevModifiedFields;
@@ -344,7 +344,7 @@ export function getOperationsContext(
344
344
  return;
345
345
  }
346
346
 
347
- state.itemsRepository.updateFieldValue(
347
+ const saveSequence = await state.itemsRepository.updateFieldValue(
348
348
  field,
349
349
  state.user ?? { name: "unknown", ai: false },
350
350
  true,
@@ -353,7 +353,13 @@ export function getOperationsContext(
353
353
  );
354
354
 
355
355
  if (refresh === "immediate") {
356
- return editFieldImmediate({ field, value, rawValue, refresh });
356
+ return editFieldImmediate({
357
+ field,
358
+ value,
359
+ rawValue,
360
+ refresh,
361
+ saveSequence,
362
+ });
357
363
  }
358
364
  if (
359
365
  lastEditField.current?.fieldId !== field.fieldId ||
@@ -382,11 +388,13 @@ export function getOperationsContext(
382
388
  value,
383
389
  rawValue,
384
390
  refresh = "immediate",
391
+ saveSequence,
385
392
  }: {
386
393
  field: FieldDescriptor;
387
394
  value?: string;
388
395
  rawValue?: string | null;
389
396
  refresh?: "none" | "immediate" | "delayed" | "waitForQuietPeriod";
397
+ saveSequence?: number;
390
398
  }): Promise<void> => {
391
399
  // state.itemsRepository.updateFieldValue(
392
400
  // field,
@@ -404,7 +412,7 @@ export function getOperationsContext(
404
412
  if (op) {
405
413
  await executeOp(op, { refresh });
406
414
  await state.itemsRepository.refreshItems([field.item]);
407
- state.itemsRepository.onFieldSaved(field, val);
415
+ state.itemsRepository.onFieldSaved(field, val, saveSequence || 0);
408
416
  }
409
417
  },
410
418
  [state.itemsRepository, executeOp],
@@ -422,14 +430,20 @@ export function getOperationsContext(
422
430
  rawValue?: string | null;
423
431
  refresh?: "none" | "immediate" | "delayed" | "waitForQuietPeriod";
424
432
  }) => {
425
- state.itemsRepository.updateFieldValue(
433
+ const saveSequence = await state.itemsRepository.updateFieldValue(
426
434
  field,
427
435
  state.user ?? { name: "unknown", ai: false },
428
436
  true,
429
437
  value,
430
438
  rawValue,
431
439
  );
432
- return editFieldImmediate({ field, value, rawValue, refresh });
440
+ return editFieldImmediate({
441
+ field,
442
+ value,
443
+ rawValue,
444
+ refresh,
445
+ saveSequence,
446
+ });
433
447
  },
434
448
  state.configuration.debounceFieldEditsInterval * 2,
435
449
  { trailing: true },
@@ -43,7 +43,7 @@ export function usePageModel(
43
43
  const keys: string[] = [];
44
44
  const collect = (c: ComponentSkeleton) => {
45
45
  c.placeholders.forEach((ph) => {
46
- keys.push(ph.key);
46
+ if (ph.editable) keys.push(ph.key);
47
47
  ph.components.forEach(collect);
48
48
  });
49
49
  };
@@ -31,24 +31,42 @@ export const InsertMenuTemplate = ({
31
31
  }, [activeTab]);
32
32
 
33
33
  const [recentTemplates, setRecentTemplates] = useState<string[]>([]);
34
-
35
34
  const [recentItems, setRecentItems] = useState<FullItem[]>([]);
36
35
  const [hoveredItem, setHoveredItem] = useState<ResultItem>();
37
36
  const [wizards, setWizards] = useState<Wizard[]>([]);
38
37
 
38
+ // Loading states
39
+ const [isLoadingRecent, setIsLoadingRecent] = useState(false);
40
+ const [isLoadingWizards, setIsLoadingWizards] = useState(false);
41
+
39
42
  const hideTimeoutRef = useRef<NodeJS.Timeout>(undefined);
40
43
  const opRef = useRef<HTMLDivElement>(null);
44
+
41
45
  useEffect(() => {
42
46
  const loadRecentItems = async () => {
43
- const items = await editContext?.itemsRepository.getItems(
44
- recentTemplates.map((id) => ({
45
- id,
46
- version: 0,
47
- language: "en",
48
- })),
49
- );
47
+ if (recentTemplates.length === 0) return;
48
+
49
+ setIsLoadingRecent(true);
50
+ try {
51
+ const items = await editContext?.itemsRepository.getItems(
52
+ recentTemplates.map((id) => ({
53
+ id,
54
+ version: 0,
55
+ language: "en",
56
+ })),
57
+ );
50
58
 
51
- if (items) setRecentItems(items);
59
+ if (items) {
60
+ // Remove duplicates based on item.id to prevent React key conflicts
61
+ const uniqueItems = items.filter(
62
+ (item, index, self) =>
63
+ self.findIndex((i) => i.id === item.id) === index,
64
+ );
65
+ setRecentItems(uniqueItems);
66
+ }
67
+ } finally {
68
+ setIsLoadingRecent(false);
69
+ }
52
70
  };
53
71
  loadRecentItems();
54
72
  }, [recentTemplates]);
@@ -57,16 +75,37 @@ export const InsertMenuTemplate = ({
57
75
  const stored = localStorage.getItem("editor.recentTemplates");
58
76
  if (stored) {
59
77
  const parsed = JSON.parse(stored);
60
- setRecentTemplates(parsed);
78
+ // Ensure the parsed data is a string array and remove duplicates
79
+ const templateIds = Array.isArray(parsed)
80
+ ? parsed.filter((id): id is string => typeof id === "string")
81
+ : [];
82
+ const uniqueTemplates = [...new Set(templateIds)];
83
+ setRecentTemplates(uniqueTemplates);
84
+
85
+ // Update localStorage if duplicates were found or invalid data was cleaned
86
+ if (
87
+ uniqueTemplates.length !== templateIds.length ||
88
+ templateIds.length !== parsed.length
89
+ ) {
90
+ localStorage.setItem(
91
+ "editor.recentTemplates",
92
+ JSON.stringify(uniqueTemplates),
93
+ );
94
+ }
61
95
  }
62
96
  }, [insertOptions]);
63
97
 
64
98
  useEffect(() => {
65
99
  const loadWizards = async () => {
66
- const wizards = await editContext?.configuration.pageWizard.getWizards(
67
- item.descriptor,
68
- );
69
- setWizards(wizards ?? []);
100
+ setIsLoadingWizards(true);
101
+ try {
102
+ const wizards = await editContext?.configuration.pageWizard.getWizards(
103
+ item.descriptor,
104
+ );
105
+ setWizards(wizards ?? []);
106
+ } finally {
107
+ setIsLoadingWizards(false);
108
+ }
70
109
  };
71
110
  loadWizards();
72
111
  }, [item]);
@@ -106,21 +145,26 @@ export const InsertMenuTemplate = ({
106
145
  const options =
107
146
  insertOptions.length === 0
108
147
  ? undefined
109
- : insertOptions.map((x) => ({
110
- label: x.name,
111
- id: x.id,
112
- icon: (
113
- <img
114
- className="p-menuitem-icon"
115
- src={x.icon}
116
- style={{ height: "16px" }}
117
- width="16"
118
- height="16"
119
- onDragStart={(e) => e.preventDefault()}
120
- ></img>
121
- ),
122
- command: getCommand(x),
123
- }));
148
+ : insertOptions
149
+ .filter(
150
+ (x, index, self) =>
151
+ self.findIndex((item) => item.id === x.id) === index,
152
+ ) // Remove duplicates
153
+ .map((x) => ({
154
+ label: x.name,
155
+ id: x.id,
156
+ icon: (
157
+ <img
158
+ className="p-menuitem-icon"
159
+ src={x.icon}
160
+ style={{ height: "16px" }}
161
+ width="16"
162
+ height="16"
163
+ onDragStart={(e) => e.preventDefault()}
164
+ ></img>
165
+ ),
166
+ command: getCommand(x),
167
+ }));
124
168
 
125
169
  const tabs: Tab[] = [
126
170
  {
@@ -283,6 +327,8 @@ export const InsertMenuTemplate = ({
283
327
  });
284
328
  }
285
329
 
330
+ const isAnyTabLoading = isLoadingRecent || isLoadingWizards;
331
+
286
332
  return (
287
333
  <div
288
334
  className="p-2"
@@ -331,7 +377,8 @@ export const InsertMenuTemplate = ({
331
377
  tabs={tabs}
332
378
  setActiveTab={setActiveTab}
333
379
  activeTab={activeTab}
334
- ></SimpleTabs>
380
+ isLoading={isAnyTabLoading}
381
+ />
335
382
  </div>
336
383
  </div>
337
384
  </div>
@@ -109,7 +109,7 @@ export function DateFieldEditor({
109
109
  };
110
110
 
111
111
  return (
112
- <Popover>
112
+ <Popover enableIframeClickDetection={false}>
113
113
  <PopoverTrigger asChild>
114
114
  <Button
115
115
  variant="outline"
@@ -125,7 +125,13 @@ export function DateFieldEditor({
125
125
  </Button>
126
126
  </PopoverTrigger>
127
127
  <PopoverContent className="w-auto p-0" align="start">
128
- <Calendar mode="single" selected={date} onSelect={handleDateSelect} />
128
+ <div
129
+ className="p-3"
130
+ onMouseDown={(e) => e.stopPropagation()}
131
+ onClick={(e) => e.stopPropagation()}
132
+ >
133
+ <Calendar mode="single" selected={date} onSelect={handleDateSelect} />
134
+ </div>
129
135
  </PopoverContent>
130
136
  </Popover>
131
137
  );