@alpaca-editor/core 1.0.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.
Files changed (239) hide show
  1. package/.prettierrc +3 -0
  2. package/eslint.config.mjs +4 -0
  3. package/images/bg-shape-black.webp +0 -0
  4. package/package.json +52 -0
  5. package/src/client-components/api.ts +6 -0
  6. package/src/client-components/index.ts +19 -0
  7. package/src/components/ActionButton.tsx +43 -0
  8. package/src/components/Error.tsx +57 -0
  9. package/src/config/config.tsx +737 -0
  10. package/src/config/types.ts +263 -0
  11. package/src/editor/ComponentInfo.tsx +77 -0
  12. package/src/editor/ConfirmationDialog.tsx +103 -0
  13. package/src/editor/ContentTree.tsx +654 -0
  14. package/src/editor/ContextMenu.tsx +155 -0
  15. package/src/editor/Editor.tsx +91 -0
  16. package/src/editor/EditorWarning.tsx +34 -0
  17. package/src/editor/EditorWarnings.tsx +33 -0
  18. package/src/editor/FieldEditorPopup.tsx +65 -0
  19. package/src/editor/FieldHistory.tsx +74 -0
  20. package/src/editor/FieldList.tsx +190 -0
  21. package/src/editor/FieldListField.tsx +387 -0
  22. package/src/editor/FieldListFieldWithFallbacks.tsx +211 -0
  23. package/src/editor/FloatingToolbar.tsx +163 -0
  24. package/src/editor/ImageEditor.tsx +129 -0
  25. package/src/editor/InsertMenu.tsx +332 -0
  26. package/src/editor/ItemInfo.tsx +90 -0
  27. package/src/editor/LinkEditorDialog.tsx +192 -0
  28. package/src/editor/MainLayout.tsx +94 -0
  29. package/src/editor/NewEditorClient.tsx +11 -0
  30. package/src/editor/PictureCropper.tsx +505 -0
  31. package/src/editor/PictureEditor.tsx +206 -0
  32. package/src/editor/PictureEditorDialog.tsx +381 -0
  33. package/src/editor/PublishDialog.ignore +74 -0
  34. package/src/editor/ScrollingContentTree.tsx +47 -0
  35. package/src/editor/Terminal.tsx +215 -0
  36. package/src/editor/Titlebar.tsx +23 -0
  37. package/src/editor/ai/AiPopup.tsx +59 -0
  38. package/src/editor/ai/AiResponseMessage.tsx +82 -0
  39. package/src/editor/ai/AiTerminal.tsx +450 -0
  40. package/src/editor/ai/AiToolCall.tsx +46 -0
  41. package/src/editor/ai/EditorAiTerminal.tsx +20 -0
  42. package/src/editor/ai/editorAiContext.ts +18 -0
  43. package/src/editor/client/DialogContext.tsx +49 -0
  44. package/src/editor/client/EditorClient.tsx +1831 -0
  45. package/src/editor/client/GenericDialog.tsx +50 -0
  46. package/src/editor/client/editContext.ts +330 -0
  47. package/src/editor/client/helpers.ts +44 -0
  48. package/src/editor/client/itemsRepository.ts +391 -0
  49. package/src/editor/client/operations.ts +610 -0
  50. package/src/editor/client/pageModelBuilder.ts +182 -0
  51. package/src/editor/commands/commands.ts +23 -0
  52. package/src/editor/commands/componentCommands.tsx +408 -0
  53. package/src/editor/commands/createVersionCommand.ts +33 -0
  54. package/src/editor/commands/deleteVersionCommand.ts +71 -0
  55. package/src/editor/commands/itemCommands.tsx +186 -0
  56. package/src/editor/commands/localizeItem/LocalizeItemDialog.tsx +201 -0
  57. package/src/editor/commands/undo.ts +39 -0
  58. package/src/editor/component-designer/ComponentDesigner.tsx +70 -0
  59. package/src/editor/component-designer/ComponentDesignerAiTerminal.tsx +11 -0
  60. package/src/editor/component-designer/ComponentDesignerMenu.tsx +91 -0
  61. package/src/editor/component-designer/ComponentEditor.tsx +97 -0
  62. package/src/editor/component-designer/ComponentRenderingCodeEditor.tsx +31 -0
  63. package/src/editor/component-designer/ComponentRenderingEditor.tsx +104 -0
  64. package/src/editor/component-designer/ComponentsDropdown.tsx +39 -0
  65. package/src/editor/component-designer/PlaceholdersEditor.tsx +183 -0
  66. package/src/editor/component-designer/RenderingsDropdown.tsx +36 -0
  67. package/src/editor/component-designer/TemplateEditor.tsx +236 -0
  68. package/src/editor/component-designer/aiContext.ts +23 -0
  69. package/src/editor/componentTreeHelper.tsx +114 -0
  70. package/src/editor/control-center/ControlCenterMenu.tsx +71 -0
  71. package/src/editor/control-center/IndexOverview.tsx +50 -0
  72. package/src/editor/control-center/IndexSettings.tsx +266 -0
  73. package/src/editor/control-center/Status.tsx +7 -0
  74. package/src/editor/editor-warnings/ItemLocked.tsx +63 -0
  75. package/src/editor/editor-warnings/NoLanguageWriteAccess.tsx +22 -0
  76. package/src/editor/editor-warnings/NoWorkflowWriteAccess.tsx +23 -0
  77. package/src/editor/editor-warnings/NoWriteAccess.tsx +15 -0
  78. package/src/editor/editor-warnings/ValidationErrors.tsx +54 -0
  79. package/src/editor/field-types/AttachmentEditor.tsx +9 -0
  80. package/src/editor/field-types/CheckboxEditor.tsx +47 -0
  81. package/src/editor/field-types/DropLinkEditor.tsx +75 -0
  82. package/src/editor/field-types/DropListEditor.tsx +84 -0
  83. package/src/editor/field-types/ImageFieldEditor.tsx +65 -0
  84. package/src/editor/field-types/InternalLinkFieldEditor.tsx +112 -0
  85. package/src/editor/field-types/LinkFieldEditor.tsx +85 -0
  86. package/src/editor/field-types/MultiLineText.tsx +63 -0
  87. package/src/editor/field-types/PictureFieldEditor.tsx +121 -0
  88. package/src/editor/field-types/RawEditor.tsx +53 -0
  89. package/src/editor/field-types/ReactQuill.tsx +580 -0
  90. package/src/editor/field-types/RichTextEditor.tsx +22 -0
  91. package/src/editor/field-types/RichTextEditorComponent.tsx +108 -0
  92. package/src/editor/field-types/SingleLineText.tsx +150 -0
  93. package/src/editor/field-types/TreeListEditor.tsx +261 -0
  94. package/src/editor/fieldTypes.ts +140 -0
  95. package/src/editor/media-selector/AiImageSearch.tsx +186 -0
  96. package/src/editor/media-selector/AiImageSearchPrompt.tsx +95 -0
  97. package/src/editor/media-selector/MediaSelector.tsx +42 -0
  98. package/src/editor/media-selector/Preview.tsx +14 -0
  99. package/src/editor/media-selector/Thumbnails.tsx +48 -0
  100. package/src/editor/media-selector/TreeSelector.tsx +292 -0
  101. package/src/editor/media-selector/UploadZone.tsx +137 -0
  102. package/src/editor/menubar/ActionsMenu.tsx +47 -0
  103. package/src/editor/menubar/ActiveUsers.tsx +17 -0
  104. package/src/editor/menubar/ApproveAndPublish.tsx +18 -0
  105. package/src/editor/menubar/BrowseHistory.tsx +37 -0
  106. package/src/editor/menubar/ItemLanguageVersion.tsx +52 -0
  107. package/src/editor/menubar/LanguageSelector.tsx +152 -0
  108. package/src/editor/menubar/Menu.tsx +83 -0
  109. package/src/editor/menubar/NavButtons.tsx +74 -0
  110. package/src/editor/menubar/PageSelector.tsx +139 -0
  111. package/src/editor/menubar/PageViewerControls.tsx +99 -0
  112. package/src/editor/menubar/Separator.tsx +12 -0
  113. package/src/editor/menubar/SiteInfo.tsx +53 -0
  114. package/src/editor/menubar/User.tsx +27 -0
  115. package/src/editor/menubar/VersionSelector.tsx +143 -0
  116. package/src/editor/page-editor-chrome/CommentHighlighting.tsx +287 -0
  117. package/src/editor/page-editor-chrome/CommentHighlightings.tsx +35 -0
  118. package/src/editor/page-editor-chrome/FieldActionIndicator.tsx +44 -0
  119. package/src/editor/page-editor-chrome/FieldActionIndicators.tsx +23 -0
  120. package/src/editor/page-editor-chrome/FieldEditedIndicator.tsx +64 -0
  121. package/src/editor/page-editor-chrome/FieldEditedIndicators.tsx +35 -0
  122. package/src/editor/page-editor-chrome/FrameMenu.tsx +263 -0
  123. package/src/editor/page-editor-chrome/FrameMenus.tsx +48 -0
  124. package/src/editor/page-editor-chrome/InlineEditor.tsx +147 -0
  125. package/src/editor/page-editor-chrome/LockedFieldIndicator.tsx +61 -0
  126. package/src/editor/page-editor-chrome/NoLayout.tsx +36 -0
  127. package/src/editor/page-editor-chrome/PageEditorChrome.tsx +119 -0
  128. package/src/editor/page-editor-chrome/PictureEditorOverlay.tsx +154 -0
  129. package/src/editor/page-editor-chrome/PlaceholderDropZone.tsx +171 -0
  130. package/src/editor/page-editor-chrome/PlaceholderDropZones.tsx +233 -0
  131. package/src/editor/page-viewer/DeviceToolbar.tsx +70 -0
  132. package/src/editor/page-viewer/EditorForm.tsx +247 -0
  133. package/src/editor/page-viewer/MiniMap.tsx +351 -0
  134. package/src/editor/page-viewer/PageViewer.tsx +127 -0
  135. package/src/editor/page-viewer/PageViewerFrame.tsx +1030 -0
  136. package/src/editor/page-viewer/pageViewContext.ts +186 -0
  137. package/src/editor/pageModel.ts +191 -0
  138. package/src/editor/picture-shared.tsx +53 -0
  139. package/src/editor/reviews/Comment.tsx +265 -0
  140. package/src/editor/reviews/Comments.tsx +50 -0
  141. package/src/editor/reviews/PreviewInfo.tsx +35 -0
  142. package/src/editor/reviews/Reviews.tsx +280 -0
  143. package/src/editor/reviews/reviewCommands.tsx +47 -0
  144. package/src/editor/reviews/useReviews.tsx +70 -0
  145. package/src/editor/services/aiService.ts +155 -0
  146. package/src/editor/services/componentDesignerService.ts +151 -0
  147. package/src/editor/services/contentService.ts +159 -0
  148. package/src/editor/services/editService.ts +462 -0
  149. package/src/editor/services/indexService.ts +24 -0
  150. package/src/editor/services/reviewsService.ts +45 -0
  151. package/src/editor/services/serviceHelper.ts +95 -0
  152. package/src/editor/services/systemService.ts +5 -0
  153. package/src/editor/services/translationService.ts +21 -0
  154. package/src/editor/services-server/api.ts +150 -0
  155. package/src/editor/services-server/graphQL.ts +106 -0
  156. package/src/editor/sidebar/ComponentPalette.tsx +146 -0
  157. package/src/editor/sidebar/ComponentTree.tsx +512 -0
  158. package/src/editor/sidebar/ComponentTree2.tsxx +490 -0
  159. package/src/editor/sidebar/Debug.tsx +105 -0
  160. package/src/editor/sidebar/DictionaryEditor.tsx +261 -0
  161. package/src/editor/sidebar/EditHistory.tsx +134 -0
  162. package/src/editor/sidebar/GraphQL.tsx +164 -0
  163. package/src/editor/sidebar/Insert.tsx +35 -0
  164. package/src/editor/sidebar/MainContentTree.tsx +95 -0
  165. package/src/editor/sidebar/Performance.tsx +53 -0
  166. package/src/editor/sidebar/Sessions.tsx +35 -0
  167. package/src/editor/sidebar/Sidebar.tsx +20 -0
  168. package/src/editor/sidebar/SidebarView.tsx +150 -0
  169. package/src/editor/sidebar/Translations.tsx +276 -0
  170. package/src/editor/sidebar/Validation.tsx +102 -0
  171. package/src/editor/sidebar/ViewSelector.tsx +49 -0
  172. package/src/editor/sidebar/Workbox.tsx +209 -0
  173. package/src/editor/ui/CenteredMessage.tsx +7 -0
  174. package/src/editor/ui/CopyToClipboardButton.tsx +23 -0
  175. package/src/editor/ui/DialogButtons.tsx +11 -0
  176. package/src/editor/ui/Icons.tsx +585 -0
  177. package/src/editor/ui/ItemNameDialog.tsx +94 -0
  178. package/src/editor/ui/ItemNameDialogNew.tsx +118 -0
  179. package/src/editor/ui/ItemSearch.tsx +173 -0
  180. package/src/editor/ui/PerfectTree.tsx +550 -0
  181. package/src/editor/ui/Section.tsx +35 -0
  182. package/src/editor/ui/SimpleIconButton.tsx +43 -0
  183. package/src/editor/ui/SimpleMenu.tsx +48 -0
  184. package/src/editor/ui/SimpleTable.tsx +63 -0
  185. package/src/editor/ui/SimpleTabs.tsx +55 -0
  186. package/src/editor/ui/SimpleToolbar.tsx +7 -0
  187. package/src/editor/ui/Spinner.tsx +7 -0
  188. package/src/editor/ui/Splitter.tsx +247 -0
  189. package/src/editor/ui/StackedPanels.tsx +134 -0
  190. package/src/editor/ui/Toolbar.tsx +7 -0
  191. package/src/editor/utils/id-helper.ts +3 -0
  192. package/src/editor/utils/insertOptions.ts +69 -0
  193. package/src/editor/utils/itemutils.ts +29 -0
  194. package/src/editor/utils/useMemoDebug.ts +28 -0
  195. package/src/editor/utils.ts +435 -0
  196. package/src/editor/views/CompareView.tsx +256 -0
  197. package/src/editor/views/EditView.tsx +27 -0
  198. package/src/editor/views/ItemEditor.tsx +58 -0
  199. package/src/editor/views/SingleEditView.tsx +44 -0
  200. package/src/fonts/Geist-Black.woff2 +0 -0
  201. package/src/fonts/Geist-Bold.woff2 +0 -0
  202. package/src/fonts/Geist-ExtraBold.woff2 +0 -0
  203. package/src/fonts/Geist-ExtraLight.woff2 +0 -0
  204. package/src/fonts/Geist-Light.woff2 +0 -0
  205. package/src/fonts/Geist-Medium.woff2 +0 -0
  206. package/src/fonts/Geist-Regular.woff2 +0 -0
  207. package/src/fonts/Geist-SemiBold.woff2 +0 -0
  208. package/src/fonts/Geist-Thin.woff2 +0 -0
  209. package/src/fonts/Geist[wght].woff2 +0 -0
  210. package/src/index.ts +7 -0
  211. package/src/page-wizard/PageWizard.tsx +163 -0
  212. package/src/page-wizard/SelectWizard.tsx +109 -0
  213. package/src/page-wizard/WizardSteps.tsx +207 -0
  214. package/src/page-wizard/service.ts +35 -0
  215. package/src/page-wizard/startPageWizardCommand.ts +27 -0
  216. package/src/page-wizard/steps/BuildPageStep.tsx +266 -0
  217. package/src/page-wizard/steps/CollectStep.tsx +233 -0
  218. package/src/page-wizard/steps/ComponentTypesSelector.tsx +443 -0
  219. package/src/page-wizard/steps/Components.tsx +193 -0
  220. package/src/page-wizard/steps/CreatePage.tsx +285 -0
  221. package/src/page-wizard/steps/CreatePageAndLayoutStep.tsx +384 -0
  222. package/src/page-wizard/steps/EditButton.tsx +34 -0
  223. package/src/page-wizard/steps/FieldEditor.tsx +102 -0
  224. package/src/page-wizard/steps/Generate.tsx +32 -0
  225. package/src/page-wizard/steps/ImagesStep.tsx +318 -0
  226. package/src/page-wizard/steps/LayoutStep.tsx +228 -0
  227. package/src/page-wizard/steps/SelectStep.tsx +256 -0
  228. package/src/page-wizard/steps/schema.ts +180 -0
  229. package/src/page-wizard/steps/usePageCreator.ts +279 -0
  230. package/src/splash-screen/NewPage.tsx +232 -0
  231. package/src/splash-screen/SectionHeadline.tsx +21 -0
  232. package/src/splash-screen/SplashScreen.tsx +156 -0
  233. package/src/tour/Tour.tsx +558 -0
  234. package/src/tour/default-tour.tsx +300 -0
  235. package/src/tour/preview-tour.tsx +127 -0
  236. package/src/types.ts +302 -0
  237. package/styles.css +476 -0
  238. package/tsconfig.build.json +21 -0
  239. package/tsconfig.json +11 -0
@@ -0,0 +1,490 @@
1
+ "use client";
2
+
3
+ import { useEditContext, useEditContextRef } from "../client/editContext";
4
+ // import {
5
+ // Tree,
6
+ // TreeExpandedKeysType,
7
+ // TreeMultipleSelectionKeys,
8
+ // TreeNodeTemplateOptions,
9
+ // } from "primereact/tree";
10
+
11
+ import { TreeNode } from "primereact/treenode";
12
+ import { useEffect, useRef, useState } from "react";
13
+ import { Tag } from "primereact/tag";
14
+ import { confirmDialog } from "primereact/confirmdialog";
15
+ import { getComponentCommands } from "../commands/componentCommands";
16
+ import { MenuItem, MenuItemCommandEvent } from "primereact/menuitem";
17
+ import { Tooltip } from "primereact/tooltip";
18
+
19
+ import { Component, Placeholder } from "../pageModel";
20
+
21
+ type CustomTreeNode = TreeNode & {
22
+ parent?: CustomTreeNode;
23
+ componentId?: string;
24
+ tags?: {
25
+ value: string;
26
+ icon: string;
27
+ severity: "success" | "info" | "warning" | "danger" | null | undefined;
28
+ onClick?: () => void;
29
+ color?: string;
30
+ tooltip?: string;
31
+ id: string;
32
+ }[];
33
+ };
34
+
35
+ export function ComponentTree({}) {
36
+ const [expandedKeys, setExpandedKeys] = useState<TreeExpandedKeysType>({
37
+ "0": true,
38
+ });
39
+
40
+ const [selectedKeys, setSelectedKeys] = useState<TreeMultipleSelectionKeys>();
41
+ const [rootNodes, setRootNodes] = useState<TreeNode[]>([]);
42
+ const [nodeDictionary, setNodeDictionary] = useState<{
43
+ [key: string]: CustomTreeNode;
44
+ }>({});
45
+
46
+ let editContextRef = useEditContextRef();
47
+ const editContext = useEditContext();
48
+
49
+ const treeRef = useRef<HTMLDivElement>(null);
50
+
51
+ useEffect(() => {
52
+ if (treeRef.current) {
53
+ const selectedNodeKey = Object.keys(selectedKeys || {})[0];
54
+
55
+ const selectedNodeElement = treeRef.current.querySelector(
56
+ `[data-key="${selectedNodeKey}"]`
57
+ );
58
+
59
+ if (selectedNodeElement) {
60
+ selectedNodeElement.scrollIntoView({
61
+ behavior: "smooth",
62
+ block: "nearest",
63
+ });
64
+ }
65
+ }
66
+ }, [selectedKeys]);
67
+
68
+ const selection = editContext?.selection || [];
69
+ const page = editContext?.page;
70
+
71
+ useEffect(() => {
72
+ const newSelectedKeys = {} as TreeMultipleSelectionKeys;
73
+ selection.forEach((id) => {
74
+ const nodes = findNodesByComponentId(id, nodeDictionary);
75
+
76
+ nodes.forEach((nodeId) => {
77
+ newSelectedKeys[nodeId] = true;
78
+ });
79
+ });
80
+
81
+ const newExpandedKeys = { ...expandedKeys };
82
+ Object.keys(newSelectedKeys).forEach((id) => {
83
+ const node = nodeDictionary[id];
84
+ if (node) expandParents(node.parent!, newExpandedKeys);
85
+ });
86
+
87
+ if (!hasSameProps(newExpandedKeys, expandedKeys))
88
+ setExpandedKeys(newExpandedKeys);
89
+
90
+ setSelectedKeys(newSelectedKeys);
91
+ }, [selection, nodeDictionary]);
92
+
93
+ function mapPlaceholderNode(
94
+ p: Placeholder,
95
+ parent: CustomTreeNode
96
+ ): CustomTreeNode {
97
+ const node: CustomTreeNode = {
98
+ key: p.key,
99
+ componentId: p.key,
100
+ label: p.name,
101
+ data: p,
102
+ icon: "pi pi-folder",
103
+ parent: parent,
104
+ droppable: false,
105
+ };
106
+ node.children = p.components.map((c) => mapComponentNode(c, node));
107
+ return node;
108
+ }
109
+
110
+ function mapPlaceholders(c: Component, parent: CustomTreeNode): TreeNode[] {
111
+ if (!c?.placeholders) return [];
112
+ if (c.placeholders.length > 1)
113
+ return c.placeholders.map((x) => mapPlaceholderNode(x, parent));
114
+ else if (c.placeholders.length > 0) {
115
+ return c.placeholders[0].components.map((c) =>
116
+ mapComponentNode(c, parent)
117
+ );
118
+ }
119
+ return [];
120
+ }
121
+
122
+ function mapComponentNode(
123
+ c: Component,
124
+ parent: CustomTreeNode
125
+ ): CustomTreeNode {
126
+ const node: CustomTreeNode = {
127
+ key: c.id,
128
+ componentId: c.id,
129
+ label: c.name,
130
+ icon: "pi pi-stop",
131
+ data: c,
132
+ parent: parent,
133
+ tags: [],
134
+ droppable: true,
135
+ className: c.isRemovedFromMasterLanguage ? "a-text-gray-400" : "",
136
+ };
137
+
138
+ // if (c.translations && c.translations.length > 1)
139
+ // node.tags!.push({
140
+ // severity: "info",
141
+ // value: c.translations!.length + " Languages",
142
+ // icon: "pi pi-flag",
143
+ // });
144
+
145
+ if (c.isInheritedFromMasterLanguage) {
146
+ node.tags!.push({
147
+ id: "inherited",
148
+ severity: "info",
149
+ value:
150
+ "Inherited " + (c.isRemovedFromMasterLanguage ? " / removed" : ""),
151
+ tooltip: c.isRemovedFromMasterLanguage
152
+ ? "Component was removed from master language"
153
+ : "Component is inherited from master language",
154
+ icon: "pi pi-caret-right",
155
+ });
156
+ }
157
+
158
+ if (c.layoutId) {
159
+ node.tags!.push({
160
+ id: "layout",
161
+ severity: "warning",
162
+ value: "Layout",
163
+ icon: "",
164
+ color: "rgb(192 132 252)",
165
+ tooltip: "Component is inherited from layout. Click to edit layout.",
166
+ onClick: async () => {
167
+ if (!editContext || !c.layoutId) return;
168
+ const hostItem = await editContext.itemsRepository.getItem({
169
+ id: c.layoutId,
170
+ language: page!.item.language,
171
+ version: 0,
172
+ });
173
+ confirmDialog({
174
+ header: "Edit layout",
175
+ message: "Edit layout " + hostItem.path + "?",
176
+ accept: () =>
177
+ editContext.loadItem({
178
+ id: c.layoutId!,
179
+ language: page!.item.language,
180
+ version: 0,
181
+ }),
182
+ //window.open(c._editor.hostingPageItem!.editUrl, "_blank"),
183
+ });
184
+ },
185
+ });
186
+ }
187
+
188
+ if (c.datasourceItem && c.datasourceItem?.id !== c.id) {
189
+ node.tags!.push({
190
+ id: "linked",
191
+ severity: "warning",
192
+ value: "Shared",
193
+ icon: "",
194
+ tooltip: "Component is linked to " + c.datasourceItem?.id,
195
+ color: "rgb(251, 146, 60)",
196
+ });
197
+ }
198
+
199
+ // if (!c.visible) {
200
+ // node.tags!.push({
201
+ // id: "hidden",
202
+ // severity: "warning",
203
+ // value: "Hidden",
204
+ // icon: "pi pi-exclamation-triangle",
205
+ // color: "rgb(251, 146, 60)",
206
+ // tooltip:
207
+ // "Component is hidden. " +
208
+ // (c.messages ? c.messages.join(", ") : ""),
209
+ // });
210
+ // }
211
+
212
+ node.children = mapPlaceholders(c, node);
213
+
214
+ if (c.items.length > 0 && c.items[0].id !== c.datasourceItem?.id) {
215
+ const itemsNode: CustomTreeNode = {
216
+ key: "items" + c.id,
217
+ label: "items",
218
+ icon: "pi pi-circle",
219
+ parent: node,
220
+ };
221
+
222
+ itemsNode.children = c.items.map((x) => {
223
+ const itemNode: CustomTreeNode = {
224
+ key: x.id,
225
+ label: x.name,
226
+ icon: "pi pi-file",
227
+ parent: itemsNode,
228
+ droppable: false,
229
+ };
230
+ return itemNode;
231
+ });
232
+
233
+ node.children.push(itemsNode);
234
+ }
235
+
236
+ return node;
237
+ }
238
+
239
+ useEffect(() => {
240
+ if (!page) return;
241
+
242
+ const treeNodes = mapPlaceholders(page.rootComponent, {});
243
+ const dict: { [key: string]: CustomTreeNode } = {};
244
+ createMap(treeNodes, dict);
245
+
246
+ const otherNode: CustomTreeNode = {
247
+ key: "other",
248
+ label: "Other",
249
+ icon: "pi pi-file",
250
+ children: [],
251
+ };
252
+
253
+ // editContext.nonComponentItems.forEach((x) => {
254
+ // const parent =
255
+ // (x.parentComponentId && dict[x.parentComponentId]) || otherNode;
256
+
257
+ // const node: CustomTreeNode = {
258
+ // key: x.id,
259
+ // label: x.name,
260
+ // icon: "pi pi-file",
261
+ // data: x,
262
+ // parent: parent,
263
+ // droppable: false,
264
+ // componentId: x.id,
265
+ // };
266
+ // if (!parent.children) parent.children = [];
267
+ // parent.children.push(node);
268
+ // dict[x.id] = node;
269
+ // });
270
+
271
+ if (otherNode.children?.length) treeNodes.push(otherNode);
272
+
273
+ const pageNode: CustomTreeNode = {
274
+ key: "page",
275
+ label: "Page",
276
+ icon: "pi pi-home",
277
+ parent: undefined,
278
+ componentId: page.item.id,
279
+ };
280
+
281
+ setRootNodes([pageNode, ...treeNodes]);
282
+ setNodeDictionary(dict);
283
+ }, [page]);
284
+
285
+ useEffect(() => {
286
+ const container = treeRef.current;
287
+ if (container) {
288
+ const handleDragStart = (e: DragEvent) => {
289
+ console.log("dragstart 232323", e);
290
+ const target = e.target as HTMLElement;
291
+ // Ensure the target is what you expect (e.g., it has a data-key attribute)
292
+ const nodeKey = target.getAttribute("data-key");
293
+ if (nodeKey && nodeDictionary[nodeKey]) {
294
+ console.log("dragstart 232323", nodeKey, nodeDictionary[nodeKey]);
295
+ }
296
+ };
297
+
298
+ // Use the capturing phase to catch the event early
299
+ container.addEventListener("dragstart", handleDragStart, true);
300
+ return () => {
301
+ container.removeEventListener("dragstart", handleDragStart, true);
302
+ };
303
+ }
304
+ }, [treeRef.current, nodeDictionary]);
305
+
306
+ if (!page)
307
+ return (
308
+ <div className="a-flex a-justify-center a-items-center a-h-full a-text-sm a-text-gray-500"></div>
309
+ );
310
+
311
+ const nodeTemplate = (
312
+ node: CustomTreeNode,
313
+ options: TreeNodeTemplateOptions
314
+ ) => {
315
+ let label = (
316
+ <div
317
+ key={node.key}
318
+ data-key={node.key}
319
+ className="a-flex a-items-center"
320
+ onDragStart={(e) => {
321
+ console.log("dragstart", e);
322
+ }}
323
+ >
324
+ <span className={node.className}>{node.label}</span>
325
+ {node.tags?.map((x) => (
326
+ <div key={"x" + node.key + x.id}>
327
+ <Tag
328
+ key={"tag-" + node.key + x.id}
329
+ id={"id-" + node.key + x.id}
330
+ icon={x.icon}
331
+ value={x.value}
332
+ severity={x.severity}
333
+ onClick={x.onClick}
334
+ style={{
335
+ backgroundColor: x.color,
336
+ marginLeft: "8px",
337
+ fontSize: "10px",
338
+ padding: "1px 4px 2px 2px",
339
+ }}
340
+ />
341
+ <Tooltip
342
+ key={"tooltip-" + node.key + x.id}
343
+ target={"#id-" + node.key + x.id}
344
+ content={x.tooltip}
345
+ position="bottom"
346
+ />
347
+ </div>
348
+ ))}
349
+ </div>
350
+ );
351
+
352
+ return (
353
+ <span className={options.className + " a-flex a-items-center"}>
354
+ {label}
355
+ </span>
356
+ );
357
+ };
358
+
359
+ if (!rootNodes?.length) return null;
360
+
361
+ function removeParentReferences(
362
+ nodes: CustomTreeNode[]
363
+ ): Omit<CustomTreeNode, "parent">[] {
364
+ return nodes.map(({ parent, children, ...rest }) => ({
365
+ ...rest,
366
+ // Recursively process children if they exist
367
+ children: children ? removeParentReferences(children) : undefined,
368
+ }));
369
+ }
370
+
371
+ return (
372
+ <div className="a-p-1" ref={treeRef}>
373
+ <Tree
374
+ value={removeParentReferences(rootNodes)}
375
+ selectionMode="multiple"
376
+ selectionKeys={selectedKeys}
377
+ className="a-w-full alpaca-no-border a-text-xs"
378
+ onToggle={(e) => setExpandedKeys(e.value)}
379
+ expandedKeys={expandedKeys}
380
+ nodeTemplate={nodeTemplate}
381
+ dragdropScope="component-tree"
382
+ onSelect={(e) => {
383
+ const mouseEvent = e.originalEvent as unknown as MouseEvent;
384
+ const selection = mouseEvent.ctrlKey ? selectedKeys || {} : {};
385
+ if (e.node.key)
386
+ selection[e.node.key] = !(selection[e.node.key] ? true : false);
387
+ const keys = Object.keys(selection);
388
+
389
+ const selectedComponentIds = keys
390
+ .filter((key) => nodeDictionary[key])
391
+ .map((key) => nodeDictionary[key].componentId) as string[];
392
+ editContextRef.current?.select(selectedComponentIds);
393
+
394
+ if (selectedComponentIds.length === 1)
395
+ editContextRef.current?.setScrollIntoView(selectedComponentIds[0]);
396
+ }}
397
+ onDragDrop={(e) => {
398
+ console.log(e.dragNode);
399
+ console.log(e.dropNode);
400
+ console.log(e.dropIndex);
401
+ }}
402
+ onContextMenu={(e) => {
403
+ const componentId = nodeDictionary[e.node.key as string].componentId;
404
+
405
+ let selectedComponentIds = selection;
406
+
407
+ if (componentId) {
408
+ if (selection.indexOf(componentId) < 0) {
409
+ editContextRef.current?.select([componentId]);
410
+ selectedComponentIds = [componentId];
411
+ }
412
+ }
413
+
414
+ let selectedEntities = selectedComponentIds
415
+ .filter((key) => nodeDictionary[key])
416
+ .map((key) => nodeDictionary[key].data)
417
+ .filter((x) => x);
418
+
419
+ if (selectedEntities.length === 0) {
420
+ selectedEntities = [e.node.data];
421
+ }
422
+
423
+ let commands = getComponentCommands(
424
+ selectedEntities,
425
+ editContextRef.current!
426
+ );
427
+
428
+ const menu: MenuItem[] = commands.map((x) => ({
429
+ label: x.label,
430
+ icon: x.icon,
431
+ disabled: editContextRef.current?.isCommandDisabled({
432
+ command: x,
433
+ }),
434
+ command: (ev: MenuItemCommandEvent) => {
435
+ editContextRef.current?.executeCommand({
436
+ command: x,
437
+ data: { components: selectedEntities },
438
+ event: ev.originalEvent,
439
+ });
440
+ },
441
+ }));
442
+
443
+ editContextRef.current?.showContextMenu(e.originalEvent, menu);
444
+ }}
445
+ />
446
+ </div>
447
+ );
448
+ }
449
+ function createMap(
450
+ treeNodes: CustomTreeNode[],
451
+ nodeDictionary: { [key: string]: CustomTreeNode }
452
+ ) {
453
+ treeNodes.forEach((n) => {
454
+ if (n.key) nodeDictionary[n.key] = n;
455
+ if (n.children) createMap(n.children, nodeDictionary);
456
+ });
457
+ }
458
+
459
+ function findNodesByComponentId(
460
+ id: string,
461
+ nodesDictionary: { [key: string]: CustomTreeNode }
462
+ ): string[] {
463
+ if (!id) return [];
464
+ const allNodes = Object.values(nodesDictionary);
465
+ return allNodes
466
+ .filter((x) => x.componentId === id)
467
+ .map((x) => x.key?.toString() || "");
468
+ }
469
+
470
+ function expandParents(
471
+ node: CustomTreeNode,
472
+ newExpandedKeys: TreeExpandedKeysType
473
+ ) {
474
+ if (!node) return;
475
+ if (node.key) newExpandedKeys[node.key] = true;
476
+ if (node.parent) expandParents(node.parent, newExpandedKeys);
477
+ }
478
+
479
+ function hasSameProps(obj1: any, obj2: any) {
480
+ var obj1Props = Object.keys(obj1),
481
+ obj2Props = Object.keys(obj2);
482
+
483
+ if (obj1Props.length == obj2Props.length) {
484
+ return obj1Props.every(function (prop) {
485
+ return obj2Props.indexOf(prop) >= 0;
486
+ });
487
+ }
488
+
489
+ return false;
490
+ }
@@ -0,0 +1,105 @@
1
+ import { TabView, TabPanel } from "primereact/tabview";
2
+ import { CopyToClipboardButton } from "../ui/CopyToClipboardButton";
3
+ import { useEffect, useState } from "react";
4
+
5
+ import { getComponentByIdFromHeadlessLayout } from "../componentTreeHelper";
6
+
7
+ import { ObjectInspector, ObjectLabel, ObjectRootLabel } from "react-inspector";
8
+ import { Component } from "../pageModel";
9
+ import { useEditContext } from "../client/editContext";
10
+
11
+ import { getPageJson } from "../services/contentService";
12
+
13
+ export function Debug({}: {}) {
14
+ const [activeTab, setActiveTab] = useState<number>(0);
15
+ const [pageLayout, setPageLayout] = useState<any>(null);
16
+
17
+ const editContext = useEditContext();
18
+ if (!editContext) return;
19
+
20
+ useEffect(() => {
21
+ const loadLayout = async () => {
22
+ if (!editContext.currentItemDescriptor) return;
23
+ const layout = await getPageJson(editContext.currentItemDescriptor);
24
+ setPageLayout(layout);
25
+ };
26
+ loadLayout();
27
+ }, [editContext.currentItemDescriptor, editContext.itemsRepository.revision]);
28
+
29
+ const page = pageLayout; // editContext.page;
30
+ let component = null;
31
+ if (page) {
32
+ const selectedComponents = editContext.selection
33
+ .map((x) => getComponentByIdFromHeadlessLayout(x, page))
34
+ .filter((x) => x) as Component[];
35
+ component = selectedComponents.length === 1 ? selectedComponents[0] : null;
36
+ }
37
+ if (!component && !page) return;
38
+ const replacer = function (key: string, value: any) {
39
+ if (
40
+ key === "parentComponent" ||
41
+ key === "placeholderReference" ||
42
+ key === "_editor"
43
+ ) {
44
+ return undefined;
45
+ }
46
+ return value;
47
+ };
48
+ const tabs = [];
49
+ const defaultNodeRenderer = ({ depth, name, data, isNonenumerable }: any) => {
50
+ if (
51
+ name == "__editor" ||
52
+ name == "parentComponent" ||
53
+ name == "placeholderReference"
54
+ )
55
+ return null;
56
+ return depth === 0 ? (
57
+ <ObjectRootLabel name={name} data={data} />
58
+ ) : (
59
+ <ObjectLabel name={name} data={data} isNonenumerable={isNonenumerable} />
60
+ );
61
+ };
62
+ if (component) {
63
+ tabs.push(
64
+ <TabPanel header="Component" className="relative h-full" key="component">
65
+ <div className="relative h-full">
66
+ <div className="absolute inset-0 overflow-auto p-2">
67
+ <ObjectInspector
68
+ data={component}
69
+ nodeRenderer={defaultNodeRenderer}
70
+ />
71
+ </div>
72
+ </div>
73
+ </TabPanel>,
74
+ );
75
+ }
76
+ tabs.push(
77
+ <TabPanel header="Page" className="relative h-full" key="page">
78
+ <div className="absolute inset-0 overflow-auto p-2">
79
+ <ObjectInspector data={page} nodeRenderer={defaultNodeRenderer} />
80
+ </div>
81
+ </TabPanel>,
82
+ );
83
+ return (
84
+ <div className="relative h-[calc(100%-6px)]">
85
+ <TabView
86
+ className="alpaceditor-tabs h-full"
87
+ onTabChange={(ev) => {
88
+ setActiveTab(ev.index);
89
+ }}
90
+ activeIndex={activeTab}
91
+ >
92
+ {tabs}
93
+ </TabView>
94
+ <div className="absolute top-2 right-1">
95
+ <CopyToClipboardButton
96
+ text={JSON.stringify(
97
+ activeTab === 0 && component ? component : page,
98
+ replacer,
99
+ 2,
100
+ )}
101
+ />
102
+ </div>
103
+ </div>
104
+ );
105
+ }