@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,65 @@
1
+ "use client";
2
+
3
+ import { useEditContext } from "../client/editContext";
4
+ import { InputText } from "primereact/inputtext";
5
+ import { ImageField } from "../fieldTypes";
6
+ import { useEffect, useState } from "react";
7
+ import { ImageEditor } from "../ImageEditor";
8
+
9
+ export function ImageFieldEditor({
10
+ field,
11
+ readOnly,
12
+ }: {
13
+ field: ImageField;
14
+ readOnly?: boolean;
15
+ }) {
16
+ const editContext = useEditContext();
17
+ const [altText, setAltText] = useState<string>("");
18
+
19
+ if (!field || !editContext) return null;
20
+
21
+ function updateAltText(rawValue: string, newAltText: string): string {
22
+ const parser = new DOMParser();
23
+ const xmlDoc = parser.parseFromString(rawValue, 'text/xml');
24
+ const imageElement = xmlDoc.documentElement;
25
+
26
+ imageElement.setAttribute('alt', newAltText);
27
+
28
+ return new XMLSerializer().serializeToString(xmlDoc);
29
+ }
30
+
31
+ var item = field.descriptor.item;
32
+
33
+ useEffect(() => {
34
+ setAltText(field.value?.alt || "");
35
+ }, [field.value?.alt]);
36
+
37
+ return (
38
+ <div>
39
+ <div className="flex gap-2 h-full focus-shadow flex-wrap">
40
+ <div className="h-48 w-48 border border-gray-300 relative cursor-pointer flex justify-center items-center">
41
+ {!readOnly && <ImageEditor field={field} />}
42
+ </div>
43
+ </div>
44
+ <div className="mt-2">
45
+ <div className="mb-1">Alt Text</div>
46
+ <InputText
47
+ key={item!.id + field.id + item!.language + item!.version}
48
+ value={altText}
49
+ disabled={readOnly}
50
+ className="p-2 focus-shadow text-sm"
51
+ style={{ width: "100%" }}
52
+ onChange={(e) => {
53
+ setAltText(e.target.value);
54
+ if (field.value) field.value.alt = e.target.value;
55
+ editContext.operations.editField({
56
+ field: field.descriptor,
57
+ rawValue: updateAltText(field.rawValue || "", e.target.value),
58
+ refresh: "waitForQuietPeriod",
59
+ });
60
+ }}
61
+ />
62
+ </div>
63
+ </div>
64
+ );
65
+ }
@@ -0,0 +1,112 @@
1
+ "use client";
2
+
3
+ import { useEffect, useRef, useState } from "react";
4
+
5
+ import { Link } from "../LinkEditorDialog";
6
+ import { useEditContext } from "../client/editContext";
7
+ import ContentTree from "../ContentTree";
8
+
9
+ import { OverlayPanel } from "primereact/overlaypanel";
10
+ import { InternalLinkField } from "../fieldTypes";
11
+ import { getLookupSources } from "../services/editService";
12
+ import { classNames } from "primereact/utils";
13
+
14
+ export function InternalLinkFieldEditor({
15
+ field,
16
+ readOnly,
17
+ }: {
18
+ field: InternalLinkField;
19
+ readOnly?: boolean;
20
+ }) {
21
+ const editContext = useEditContext();
22
+
23
+ const [link, setLink] = useState<Link>();
24
+ const [showTree, setShowTree] = useState(false);
25
+ const [rootItemIds, setRootItemIds] = useState<string[]>([]);
26
+
27
+ useEffect(() => {
28
+ setLink({
29
+ url: field.value.url,
30
+ target: field.value.target,
31
+ targetItemName: field.value.targetItemName,
32
+ targetItemLongId: field.value.targetItemLongId,
33
+ type: field.value.type,
34
+ itemId: field.value.targetItemLongId?.split("/").pop(),
35
+ queryString: field.value.queryString,
36
+ });
37
+ }, [field]);
38
+
39
+ const overlayPanelRef = useRef<OverlayPanel>(null);
40
+ const dropdownRef = useRef<HTMLDivElement>(null);
41
+ const texboxRef = useRef<HTMLDivElement>(null);
42
+
43
+ if (!editContext) return;
44
+
45
+ const selection = [];
46
+ if (link?.itemId) selection.push(link.itemId);
47
+
48
+ useEffect(() => {
49
+ const loadLookupSources = async () => {
50
+ const datasources = await getLookupSources(field, editContext.sessionId);
51
+
52
+ if (datasources.length > 0) {
53
+ setRootItemIds(datasources.map((x) => x.id));
54
+ } else setRootItemIds(["11111111-1111-1111-1111-111111111111"]);
55
+ };
56
+ loadLookupSources();
57
+ }, [field.descriptor]);
58
+
59
+ return (
60
+ <>
61
+ <div>
62
+ <div
63
+ ref={texboxRef}
64
+ className={classNames(
65
+ "p-1.5 border flex justify-between text-sm cursor-pointer justiy-between focus-shadow",
66
+ readOnly ? "bg-gray-50" : " bg-white"
67
+ )}
68
+ onClick={(e) => {
69
+ if (readOnly) return;
70
+ overlayPanelRef.current?.toggle(null, texboxRef.current);
71
+ setShowTree((show) => !show);
72
+ e.preventDefault();
73
+ }}
74
+ >
75
+ {link?.targetItemName ? (
76
+ <span>{link.targetItemName}</span>
77
+ ) : (
78
+ <span>&nbsp;</span>
79
+ )}
80
+ {!readOnly && (
81
+ <i
82
+ className={`pi ${showTree ? "pi-angle-up" : "pi-angle-down"} `}
83
+ ></i>
84
+ )}
85
+ </div>
86
+ <div ref={dropdownRef} className="w-0"></div>
87
+ </div>
88
+ <OverlayPanel ref={overlayPanelRef}>
89
+ <div
90
+ className="h-64 overflow-auto resize border-b border-x p-2"
91
+ style={{ width: `${texboxRef.current?.offsetWidth || 100}px` }}
92
+ >
93
+ <ContentTree
94
+ language={editContext.contentEditorItem!.language}
95
+ expandIdPath={link?.targetItemLongId}
96
+ rootItemIds={rootItemIds}
97
+ selectionMode="single"
98
+ selectedItemIds={selection}
99
+ onNodeClick={(node) => {
100
+ setShowTree(false);
101
+ overlayPanelRef.current?.hide();
102
+ editContext?.operations.editField({
103
+ field: field.descriptor,
104
+ rawValue: node.id,
105
+ });
106
+ }}
107
+ />
108
+ </div>
109
+ </OverlayPanel>
110
+ </>
111
+ );
112
+ }
@@ -0,0 +1,85 @@
1
+ "use client";
2
+
3
+ import { useEffect, useState } from "react";
4
+
5
+ import { Link, LinkEditorDialog } from "../LinkEditorDialog";
6
+ import { useEditContext } from "../client/editContext";
7
+ import { LinkField } from "../fieldTypes";
8
+ import { classNames } from "primereact/utils";
9
+
10
+ export function LinkFieldEditor({
11
+ field,
12
+ readOnly,
13
+ }: {
14
+ field: LinkField;
15
+ readOnly?: boolean;
16
+ }) {
17
+ const editContext = useEditContext();
18
+ const [showDialog, setShowDialog] = useState(false);
19
+ const [link, setLink] = useState<Link>();
20
+ useEffect(() => {
21
+ if (!field.value) return;
22
+ setLink({
23
+ url: field.value.url,
24
+ target: field.value.target,
25
+ targetItemLongId: field.value.targetItemLongId,
26
+ type: field.value.type,
27
+ itemId: field.value.targetItemLongId?.split("/").pop(),
28
+ queryString: field.value.queryString,
29
+ });
30
+ }, [field]);
31
+ if (!field.value) return;
32
+
33
+ return (
34
+ <>
35
+ <div
36
+ className={classNames(
37
+ "focus-shadow flex justify-between border border-gray-300 p-2",
38
+ readOnly ? "bg-gray-100" : "cursor-pointer bg-white",
39
+ )}
40
+ onClick={(e) => {
41
+ if (readOnly) return;
42
+ setShowDialog(true);
43
+ e.preventDefault();
44
+ }}
45
+ >
46
+ <div>{field.value.url}</div>
47
+ <i className="pi pi-link"></i>
48
+ </div>
49
+ {showDialog && (
50
+ <LinkEditorDialog
51
+ linkValue={link || { type: "internal" }}
52
+ onOk={(link) => {
53
+ const xml =
54
+ link.type == "internal"
55
+ ? '<link linktype="internal" querystring="' +
56
+ (link.queryString ?? "") +
57
+ '" id="' +
58
+ link.itemId +
59
+ '" target="' +
60
+ link.target +
61
+ '" />'
62
+ : '<link linktype="external" querystring="' +
63
+ (link.queryString ?? "") +
64
+ '" url="' +
65
+ link.url +
66
+ '" target="' +
67
+ link.target +
68
+ '" />';
69
+
70
+ editContext?.operations.editField({
71
+ field: field.descriptor,
72
+ rawValue: xml,
73
+ refresh: "immediate",
74
+ });
75
+
76
+ setShowDialog(false);
77
+ }}
78
+ onCancel={() => {
79
+ setShowDialog(false);
80
+ }}
81
+ />
82
+ )}
83
+ </>
84
+ );
85
+ }
@@ -0,0 +1,63 @@
1
+ "use client";
2
+
3
+ import { InputTextarea } from "primereact/inputtextarea";
4
+ import {
5
+ useEditContextRef,
6
+ useModifiedFieldsContext,
7
+ } from "../client/editContext";
8
+
9
+ import { useEffect, useRef, useState } from "react";
10
+
11
+ import { Field } from "../pageModel";
12
+
13
+ export function MultiLineText({
14
+ field,
15
+ readOnly,
16
+ updateFieldValue,
17
+ }: {
18
+ field: Field;
19
+ readOnly?: boolean;
20
+ updateFieldValue: (value: string) => void;
21
+ }) {
22
+ const editContextRef = useEditContextRef();
23
+ const inputRef = useRef<HTMLTextAreaElement>(null);
24
+
25
+ const fieldItem = field.descriptor.item;
26
+ const [value, setValue] = useState(field.value as string);
27
+
28
+ const modifiedFieldsContext = useModifiedFieldsContext();
29
+ const modifiedField = modifiedFieldsContext?.modifiedFields.find(
30
+ (x) =>
31
+ x.fieldId === field.id &&
32
+ x.item.id === fieldItem.id &&
33
+ x.item.language === fieldItem.language &&
34
+ x.item.version === fieldItem.version
35
+ );
36
+
37
+ useEffect(() => {
38
+ if (inputRef.current !== document.activeElement) {
39
+ if (field.isHistoric) setValue(field.value as string);
40
+ else setValue(modifiedField?.value ?? (field.value as string));
41
+ }
42
+ }, [field.value, modifiedFieldsContext?.modifiedFields]);
43
+
44
+ if (!editContextRef.current) return;
45
+
46
+ if (!fieldItem) return;
47
+
48
+ return (
49
+ <InputTextarea
50
+ ref={inputRef}
51
+ key={fieldItem.id + field.id + fieldItem.language + fieldItem.version}
52
+ value={value}
53
+ disabled={readOnly}
54
+ className="p-2 focus-shadow text-sm"
55
+ style={{ width: "100%" }}
56
+ rows={12}
57
+ onChange={(e) => {
58
+ setValue(e.target.value);
59
+ updateFieldValue(e.target.value as string);
60
+ }}
61
+ />
62
+ );
63
+ }
@@ -0,0 +1,121 @@
1
+ "use client";
2
+
3
+ import { useEditContext } from "../client/editContext";
4
+
5
+ import { InputText } from "primereact/inputtext";
6
+
7
+ import { PictureEditor } from "../PictureEditor";
8
+ import { PictureField, PictureRawValue } from "../fieldTypes";
9
+ import { useEffect, useState } from "react";
10
+ import { SimpleIconButton } from "../ui/SimpleIconButton";
11
+
12
+ export function PictureFieldEditor({
13
+ field,
14
+ readOnly,
15
+ }: {
16
+ field: PictureField;
17
+ readOnly?: boolean;
18
+ }) {
19
+ const editContext = useEditContext();
20
+
21
+ const [altText, setAltText] = useState<string>("");
22
+
23
+ if (!field || !editContext) return;
24
+
25
+ var item = field.descriptor.item;
26
+
27
+ useEffect(() => {
28
+ setAltText(field.value?.alt || "");
29
+ }, [field.value?.alt]);
30
+
31
+ if (field.value?.error) {
32
+ return <div>{field.value?.error}</div>;
33
+ }
34
+
35
+ return (
36
+ <div>
37
+ <div className="flex gap-2 h-full focus-shadow flex-wrap">
38
+ {field?.value?.variants?.map((variant) => (
39
+ <div key={variant.name}>
40
+ <div
41
+ className={
42
+ "h-48 w-48 border border-gray-300 relative cursor-pointer flex justify-center items-center"
43
+ }
44
+ >
45
+ {variant.src && <img src={variant.thumbSrc} />}
46
+ {!readOnly && (
47
+ <PictureEditor field={field} variantName={variant.name} />
48
+ )}
49
+ </div>
50
+ <div className="p-1">
51
+ <div className="text-xs">{variant.name}</div>
52
+
53
+ <div className="text-xs">{variant.aspectRatioLockText}</div>
54
+ </div>
55
+ {variant.videoUrl && (
56
+ <div className="text-xs">
57
+ Video: {variant.videoUrl}
58
+ <SimpleIconButton
59
+ onClick={() => {
60
+ const raw = field.rawValue
61
+ ? (JSON.parse(field?.rawValue) as PictureRawValue)
62
+ : ({ Variants: [] } as PictureRawValue);
63
+
64
+ if (!raw.Variants) return;
65
+
66
+ let rawVariant = raw.Variants.find(
67
+ (v) => v.Name === variant.name
68
+ );
69
+
70
+ if (rawVariant) {
71
+ rawVariant.VideoId = undefined;
72
+ } else {
73
+ rawVariant = {
74
+ Name: variant.name,
75
+ VideoId: undefined,
76
+ };
77
+ raw.Variants.push(rawVariant);
78
+ }
79
+
80
+ editContext!.operations.editField({
81
+ field: field.descriptor,
82
+ rawValue: JSON.stringify(raw),
83
+ refresh: "immediate",
84
+ });
85
+ }}
86
+ icon="pi pi-trash"
87
+ label="Remove"
88
+ />
89
+ </div>
90
+ )}
91
+ </div>
92
+ ))}
93
+ </div>
94
+ <div className="mt-2">
95
+ <div className="mb-1">Alt Text</div>
96
+ <InputText
97
+ key={item!.id + field.id + item!.language + item!.version}
98
+ value={altText}
99
+ disabled={readOnly}
100
+ className="p-2 focus-shadow text-sm"
101
+ style={{ width: "100%" }}
102
+ onChange={(e) => {
103
+ setAltText(e.target.value);
104
+ const raw = field?.rawValue
105
+ ? (JSON.parse(field?.rawValue) as PictureRawValue)
106
+ : ({ Variants: [] } as PictureRawValue);
107
+ raw.AltText = e.target.value as string;
108
+
109
+ if (field.value) field.value.alt = e.target.value as string;
110
+
111
+ editContext.operations.editField({
112
+ field: field.descriptor,
113
+ rawValue: JSON.stringify(raw),
114
+ refresh: "waitForQuietPeriod",
115
+ });
116
+ }}
117
+ />
118
+ </div>
119
+ </div>
120
+ );
121
+ }
@@ -0,0 +1,53 @@
1
+ "use client";
2
+
3
+ import { InputTextarea } from "primereact/inputtextarea";
4
+ import { useEditContext } from "../client/editContext";
5
+ import { useEffect, useRef, useState } from "react";
6
+ import { useDebouncedCallback } from "use-debounce";
7
+
8
+ import { Field } from "../pageModel";
9
+
10
+ export function RawEditor({
11
+ field,
12
+ readOnly,
13
+ }: {
14
+ field: Field;
15
+ readOnly?: boolean;
16
+ }) {
17
+ const editContext = useEditContext();
18
+ const inputRef = useRef<HTMLTextAreaElement>(null);
19
+
20
+ if (!editContext) return;
21
+ const [value, setValue] = useState(field.rawValue);
22
+
23
+ useEffect(() => {
24
+ if (inputRef.current !== document.activeElement) setValue(field.rawValue);
25
+ }, [field.rawValue]);
26
+
27
+ const debouncedSetFieldvalue = useDebouncedCallback((value) => {
28
+ editContext.operations.editField({
29
+ field: field.descriptor,
30
+ rawValue: value,
31
+ refresh: "waitForQuietPeriod",
32
+ });
33
+ }, 200);
34
+
35
+ const fieldItem = field.descriptor.item;
36
+
37
+ return (
38
+ <InputTextarea
39
+ ref={inputRef}
40
+ key={fieldItem.id + field.id + fieldItem.language + fieldItem.version}
41
+ value={value || ""}
42
+ disabled={readOnly}
43
+ className="p-2 focus-shadow text-sm"
44
+ style={{ width: "100%" }}
45
+ rows={12}
46
+ onChange={(e) => {
47
+ setValue(e.target.value);
48
+ debouncedSetFieldvalue(e.target.value as string);
49
+ // field.value = e.target.value;
50
+ }}
51
+ />
52
+ );
53
+ }