@prenta/admin 2.0.3 → 2.2.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 (61) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/AdminRoot.d.ts.map +1 -1
  3. package/dist/AdminRoot.js +2 -6
  4. package/dist/AdminRoot.js.map +1 -1
  5. package/dist/__tests__/components/document-locale-switcher.test.d.ts +2 -0
  6. package/dist/__tests__/components/document-locale-switcher.test.d.ts.map +1 -0
  7. package/dist/__tests__/components/document-locale-switcher.test.js +78 -0
  8. package/dist/__tests__/components/document-locale-switcher.test.js.map +1 -0
  9. package/dist/__tests__/lib/admin-shortcuts.test.d.ts +2 -0
  10. package/dist/__tests__/lib/admin-shortcuts.test.d.ts.map +1 -0
  11. package/dist/__tests__/lib/admin-shortcuts.test.js +11 -0
  12. package/dist/__tests__/lib/admin-shortcuts.test.js.map +1 -0
  13. package/dist/__tests__/lib/editor-history.test.d.ts +2 -0
  14. package/dist/__tests__/lib/editor-history.test.d.ts.map +1 -0
  15. package/dist/__tests__/lib/editor-history.test.js +76 -0
  16. package/dist/__tests__/lib/editor-history.test.js.map +1 -0
  17. package/dist/__tests__/views/editor-top-bar.render.test.js +17 -0
  18. package/dist/__tests__/views/editor-top-bar.render.test.js.map +1 -1
  19. package/dist/__tests__/views/post-section-editor.test.js +16 -0
  20. package/dist/__tests__/views/post-section-editor.test.js.map +1 -1
  21. package/dist/components/DocumentLocaleSwitcher.d.ts +15 -0
  22. package/dist/components/DocumentLocaleSwitcher.d.ts.map +1 -0
  23. package/dist/components/DocumentLocaleSwitcher.js +66 -0
  24. package/dist/components/DocumentLocaleSwitcher.js.map +1 -0
  25. package/dist/hooks/use-editor-history.d.ts +19 -0
  26. package/dist/hooks/use-editor-history.d.ts.map +1 -0
  27. package/dist/hooks/use-editor-history.js +94 -0
  28. package/dist/hooks/use-editor-history.js.map +1 -0
  29. package/dist/lib/admin-shortcuts.d.ts +6 -0
  30. package/dist/lib/admin-shortcuts.d.ts.map +1 -0
  31. package/dist/lib/admin-shortcuts.js +11 -0
  32. package/dist/lib/admin-shortcuts.js.map +1 -0
  33. package/dist/lib/editor-history.d.ts +24 -0
  34. package/dist/lib/editor-history.d.ts.map +1 -0
  35. package/dist/lib/editor-history.js +61 -0
  36. package/dist/lib/editor-history.js.map +1 -0
  37. package/dist/prenta-admin.css +1 -1
  38. package/dist/views/page-editor/EditorTopBar.d.ts +9 -1
  39. package/dist/views/page-editor/EditorTopBar.d.ts.map +1 -1
  40. package/dist/views/page-editor/EditorTopBar.js +3 -3
  41. package/dist/views/page-editor/EditorTopBar.js.map +1 -1
  42. package/dist/views/page-editor/PageSectionEditor.d.ts.map +1 -1
  43. package/dist/views/page-editor/PageSectionEditor.js +38 -9
  44. package/dist/views/page-editor/PageSectionEditor.js.map +1 -1
  45. package/dist/views/post-editor/PostSectionEditor.d.ts.map +1 -1
  46. package/dist/views/post-editor/PostSectionEditor.js +45 -11
  47. package/dist/views/post-editor/PostSectionEditor.js.map +1 -1
  48. package/package.json +4 -4
  49. package/src/AdminRoot.tsx +5 -9
  50. package/src/__tests__/components/document-locale-switcher.test.tsx +111 -0
  51. package/src/__tests__/lib/admin-shortcuts.test.ts +11 -0
  52. package/src/__tests__/lib/editor-history.test.ts +99 -0
  53. package/src/__tests__/views/editor-top-bar.render.test.tsx +27 -0
  54. package/src/__tests__/views/post-section-editor.test.tsx +30 -0
  55. package/src/components/DocumentLocaleSwitcher.tsx +140 -0
  56. package/src/hooks/use-editor-history.ts +102 -0
  57. package/src/lib/admin-shortcuts.ts +10 -0
  58. package/src/lib/editor-history.ts +83 -0
  59. package/src/views/page-editor/EditorTopBar.tsx +39 -0
  60. package/src/views/page-editor/PageSectionEditor.tsx +65 -10
  61. package/src/views/post-editor/PostSectionEditor.tsx +76 -14
@@ -0,0 +1,66 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useState } from 'react';
4
+ import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
5
+ import { ChevronDown, Globe } from 'lucide-react';
6
+ import { toast } from 'sonner';
7
+ import { cmsApi } from '../lib/api.js';
8
+ import { adminPortalContainer } from '../lib/portal-container.js';
9
+ import { useLocale } from './LocaleProvider.js';
10
+ /**
11
+ * Switches the open document to another locale, creating a draft translation
12
+ * when that locale does not exist yet. Hidden when the site has fewer than
13
+ * two locales — the header language control is a different setting.
14
+ */
15
+ export function DocumentLocaleSwitcher({ collection, documentId, canCreate, editorPath, onNavigate, }) {
16
+ const { locales } = useLocale();
17
+ const enabled = locales.length > 1;
18
+ const [currentLocale, setCurrentLocale] = useState(null);
19
+ const [siblings, setSiblings] = useState([]);
20
+ const [busy, setBusy] = useState(false);
21
+ useEffect(() => {
22
+ if (!enabled)
23
+ return;
24
+ let cancelled = false;
25
+ cmsApi(`/collections/${encodeURIComponent(collection)}/${encodeURIComponent(documentId)}/translations`).then((result) => {
26
+ if (cancelled || !result.data)
27
+ return;
28
+ setCurrentLocale(result.data.locale);
29
+ setSiblings(result.data.translations);
30
+ });
31
+ return () => {
32
+ cancelled = true;
33
+ };
34
+ }, [collection, documentId, enabled]);
35
+ if (!enabled)
36
+ return null;
37
+ const current = locales.find((locale) => locale.code === currentLocale);
38
+ const label = current?.code.toUpperCase() ?? 'Locale';
39
+ async function choose(code) {
40
+ const sibling = siblings.find((row) => row.locale === code);
41
+ if (sibling) {
42
+ if (sibling.id !== documentId)
43
+ onNavigate(editorPath(sibling.id));
44
+ return;
45
+ }
46
+ if (!canCreate || busy)
47
+ return;
48
+ setBusy(true);
49
+ const result = await cmsApi(`/collections/${encodeURIComponent(collection)}/${encodeURIComponent(documentId)}/translations`, { method: 'POST', body: JSON.stringify({ locale: code }) });
50
+ setBusy(false);
51
+ if (result.error || !result.data?.id) {
52
+ toast.error(result.error ?? 'Could not create the translation');
53
+ return;
54
+ }
55
+ onNavigate(editorPath(result.data.id));
56
+ }
57
+ return (_jsxs(DropdownMenu.Root, { children: [_jsx(DropdownMenu.Trigger, { asChild: true, children: _jsxs("button", { type: "button", disabled: busy, "aria-label": `Translations, ${current?.label ?? 'locale not set'}`, title: "Open or create a translation", className: "text-muted-foreground hover:text-foreground hover:bg-accent inline-flex shrink-0 items-center gap-1 rounded-lg px-2 py-1 text-sm disabled:cursor-not-allowed disabled:opacity-50", children: [_jsx(Globe, { className: "h-4 w-4", "aria-hidden": true }), label, _jsx(ChevronDown, { className: "h-3.5 w-3.5", "aria-hidden": true })] }) }), _jsx(DropdownMenu.Portal, { container: adminPortalContainer(), children: _jsx(DropdownMenu.Content, { align: "start", sideOffset: 6, className: "border-border bg-popover z-[120] min-w-40 rounded-lg border p-1 shadow-md", children: locales.map((locale) => {
58
+ const sibling = siblings.find((row) => row.locale === locale.code);
59
+ const isCurrent = locale.code === currentLocale;
60
+ const canChoose = Boolean(sibling) || canCreate;
61
+ return (_jsxs(DropdownMenu.Item, { disabled: !canChoose || busy, onSelect: () => {
62
+ void choose(locale.code);
63
+ }, className: "hover:bg-accent focus:bg-accent data-[disabled]:text-muted-foreground flex cursor-pointer items-center justify-between gap-3 rounded-md px-2.5 py-1.5 text-sm outline-none data-[disabled]:cursor-not-allowed", children: [_jsx("span", { children: locale.label }), _jsx("span", { className: "text-muted-foreground text-xs", children: isCurrent ? 'Current' : sibling ? sibling.status : 'New draft' })] }, locale.code));
64
+ }) }) })] }));
65
+ }
66
+ //# sourceMappingURL=DocumentLocaleSwitcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DocumentLocaleSwitcher.js","sourceRoot":"","sources":["../../src/components/DocumentLocaleSwitcher.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Snapshot stack for the section editor. `note` records the document from
3
+ * before a change and is safe to call inside a state updater (it only writes
4
+ * a ref). The stack commits after that render. Rapid notes with the same key
5
+ * collapse into one step.
6
+ */
7
+ export declare function useEditorHistory<T>(current: T | null, resetKey: string | null): {
8
+ note: (snapshot: T, key: string, coalesce?: boolean) => void;
9
+ undo: () => T | null;
10
+ redo: () => T | null;
11
+ canUndo: boolean;
12
+ canRedo: boolean;
13
+ };
14
+ export declare function useSectionEditorShortcuts(input: {
15
+ enabled: boolean;
16
+ onUndo: () => void;
17
+ onRedo: () => void;
18
+ }): void;
19
+ //# sourceMappingURL=use-editor-history.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-editor-history.d.ts","sourceRoot":"","sources":["../../src/hooks/use-editor-history.ts"],"names":[],"mappings":"AAeA;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;qBAKxC,CAAC,OAAO,MAAM;gBA0BrB,CAAC,GAAG,IAAI;gBASR,CAAC,GAAG,IAAI;;;EAgBtC;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE;IAC/C,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,MAAM,EAAE,MAAM,IAAI,CAAA;CACnB,QAkBA"}
@@ -0,0 +1,94 @@
1
+ 'use client';
2
+ import { useCallback, useEffect, useRef, useState } from 'react';
3
+ import { createEditorHistory, isNativeEditingTarget, pushEditorHistory, redoEditorHistory, undoEditorHistory, } from '../lib/editor-history.js';
4
+ function cloneSnapshot(value) {
5
+ return JSON.parse(JSON.stringify(value));
6
+ }
7
+ /**
8
+ * Snapshot stack for the section editor. `note` records the document from
9
+ * before a change and is safe to call inside a state updater (it only writes
10
+ * a ref). The stack commits after that render. Rapid notes with the same key
11
+ * collapse into one step.
12
+ */
13
+ export function useEditorHistory(current, resetKey) {
14
+ const historyRef = useRef(createEditorHistory());
15
+ const pendingRef = useRef([]);
16
+ const [, setRevision] = useState(0);
17
+ const note = useCallback((snapshot, key, coalesce = false) => {
18
+ pendingRef.current.push({ snapshot: cloneSnapshot(snapshot), key, coalesce });
19
+ }, []);
20
+ useEffect(() => {
21
+ historyRef.current = createEditorHistory();
22
+ pendingRef.current = [];
23
+ setRevision((n) => n + 1);
24
+ }, [resetKey]);
25
+ useEffect(() => {
26
+ if (pendingRef.current.length === 0)
27
+ return;
28
+ const now = Date.now();
29
+ let next = historyRef.current;
30
+ for (const item of pendingRef.current) {
31
+ next = pushEditorHistory(next, item.snapshot, {
32
+ key: item.key,
33
+ now,
34
+ coalesce: item.coalesce,
35
+ });
36
+ }
37
+ pendingRef.current = [];
38
+ historyRef.current = next;
39
+ setRevision((n) => n + 1);
40
+ }, [current]);
41
+ const undo = useCallback(() => {
42
+ if (current == null)
43
+ return null;
44
+ const result = undoEditorHistory(historyRef.current, cloneSnapshot(current));
45
+ if (!result)
46
+ return null;
47
+ historyRef.current = result.history;
48
+ setRevision((n) => n + 1);
49
+ return result.value;
50
+ }, [current]);
51
+ const redo = useCallback(() => {
52
+ if (current == null)
53
+ return null;
54
+ const result = redoEditorHistory(historyRef.current, cloneSnapshot(current));
55
+ if (!result)
56
+ return null;
57
+ historyRef.current = result.history;
58
+ setRevision((n) => n + 1);
59
+ return result.value;
60
+ }, [current]);
61
+ return {
62
+ note,
63
+ undo,
64
+ redo,
65
+ canUndo: historyRef.current.past.length > 0,
66
+ canRedo: historyRef.current.future.length > 0,
67
+ };
68
+ }
69
+ export function useSectionEditorShortcuts(input) {
70
+ const { enabled, onUndo, onRedo } = input;
71
+ useEffect(() => {
72
+ if (!enabled)
73
+ return;
74
+ function onKey(event) {
75
+ if (isNativeEditingTarget(event.target))
76
+ return;
77
+ if (!(event.metaKey || event.ctrlKey) || event.altKey)
78
+ return;
79
+ const key = event.key.toLowerCase();
80
+ const redoKey = key === 'y' || (key === 'z' && event.shiftKey);
81
+ const undoKey = key === 'z' && !event.shiftKey;
82
+ if (!undoKey && !redoKey)
83
+ return;
84
+ event.preventDefault();
85
+ if (redoKey)
86
+ onRedo();
87
+ else
88
+ onUndo();
89
+ }
90
+ document.addEventListener('keydown', onKey);
91
+ return () => document.removeEventListener('keydown', onKey);
92
+ }, [enabled, onUndo, onRedo]);
93
+ }
94
+ //# sourceMappingURL=use-editor-history.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-editor-history.js","sourceRoot":"","sources":["../../src/hooks/use-editor-history.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /** Shown in the keyboard-shortcuts dialog (⌘ /). */
2
+ export declare const ADMIN_KEYBOARD_SHORTCUTS: ReadonlyArray<{
3
+ keys: string;
4
+ label: string;
5
+ }>;
6
+ //# sourceMappingURL=admin-shortcuts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-shortcuts.d.ts","sourceRoot":"","sources":["../../src/lib/admin-shortcuts.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,eAAO,MAAM,wBAAwB,EAAE,aAAa,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAQnF,CAAA"}
@@ -0,0 +1,11 @@
1
+ /** Shown in the keyboard-shortcuts dialog (⌘ /). */
2
+ export const ADMIN_KEYBOARD_SHORTCUTS = [
3
+ { keys: '⌘ K', label: 'Open search' },
4
+ { keys: '⌘ S', label: 'Save document' },
5
+ { keys: '⌘ Z', label: 'Undo in the section editor' },
6
+ { keys: '⌘ ⇧ Z', label: 'Redo in the section editor' },
7
+ { keys: '⌘ Y', label: 'Redo in the section editor' },
8
+ { keys: '⌘ /', label: 'Toggle this help' },
9
+ { keys: 'Esc', label: 'Close panel / modal' },
10
+ ];
11
+ //# sourceMappingURL=admin-shortcuts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-shortcuts.js","sourceRoot":"","sources":["../../src/lib/admin-shortcuts.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,MAAM,CAAC,MAAM,wBAAwB,GAAmD;IACtF,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE;IACrC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,eAAe,EAAE;IACvC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,4BAA4B,EAAE;IACpD,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,4BAA4B,EAAE;IACtD,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,4BAA4B,EAAE;IACpD,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,kBAAkB,EAAE;IAC1C,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,qBAAqB,EAAE;CAC9C,CAAA"}
@@ -0,0 +1,24 @@
1
+ interface EditorHistory<T> {
2
+ past: T[];
3
+ future: T[];
4
+ lastKey: string | null;
5
+ lastAt: number;
6
+ }
7
+ export declare function createEditorHistory<T>(): EditorHistory<T>;
8
+ export declare function pushEditorHistory<T>(history: EditorHistory<T>, snapshot: T, options: {
9
+ key: string;
10
+ now: number;
11
+ coalesce?: boolean;
12
+ }): EditorHistory<T>;
13
+ export declare function undoEditorHistory<T>(history: EditorHistory<T>, current: T): {
14
+ history: EditorHistory<T>;
15
+ value: T;
16
+ } | null;
17
+ export declare function redoEditorHistory<T>(history: EditorHistory<T>, current: T): {
18
+ history: EditorHistory<T>;
19
+ value: T;
20
+ } | null;
21
+ /** Text fields and rich text keep their own undo. The page stack must not steal it. */
22
+ export declare function isNativeEditingTarget(target: EventTarget | null): boolean;
23
+ export {};
24
+ //# sourceMappingURL=editor-history.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editor-history.d.ts","sourceRoot":"","sources":["../../src/lib/editor-history.ts"],"names":[],"mappings":"AAMA,UAAU,aAAa,CAAC,CAAC;IACvB,IAAI,EAAE,CAAC,EAAE,CAAA;IACT,MAAM,EAAE,CAAC,EAAE,CAAA;IACX,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,MAAM,EAAE,MAAM,CAAA;CACf;AAED,wBAAgB,mBAAmB,CAAC,CAAC,KAAK,aAAa,CAAC,CAAC,CAAC,CAEzD;AAED,wBAAgB,iBAAiB,CAAC,CAAC,EACjC,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,EACzB,QAAQ,EAAE,CAAC,EACX,OAAO,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GACxD,aAAa,CAAC,CAAC,CAAC,CAmBlB;AAED,wBAAgB,iBAAiB,CAAC,CAAC,EACjC,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,EACzB,OAAO,EAAE,CAAC,GACT;IAAE,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAAG,IAAI,CAYhD;AAED,wBAAgB,iBAAiB,CAAC,CAAC,EACjC,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,EACzB,OAAO,EAAE,CAAC,GACT;IAAE,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAAG,IAAI,CAYhD;AAED,uFAAuF;AACvF,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,GAAG,OAAO,CAKzE"}
@@ -0,0 +1,61 @@
1
+ /** How long edits with the same key collapse into one undo step. */
2
+ const EDITOR_HISTORY_COALESCE_MS = 800;
3
+ /** Cap so a long editing session cannot grow the stack without bound. */
4
+ const EDITOR_HISTORY_LIMIT = 50;
5
+ export function createEditorHistory() {
6
+ return { past: [], future: [], lastKey: null, lastAt: 0 };
7
+ }
8
+ export function pushEditorHistory(history, snapshot, options) {
9
+ const coalesce = options.coalesce === true;
10
+ if (coalesce &&
11
+ history.lastKey === options.key &&
12
+ history.past.length > 0 &&
13
+ options.now - history.lastAt < EDITOR_HISTORY_COALESCE_MS) {
14
+ return { ...history, lastAt: options.now };
15
+ }
16
+ const past = [...history.past, snapshot];
17
+ return {
18
+ past: past.length > EDITOR_HISTORY_LIMIT ? past.slice(past.length - EDITOR_HISTORY_LIMIT) : past,
19
+ future: [],
20
+ lastKey: options.key,
21
+ lastAt: options.now,
22
+ };
23
+ }
24
+ export function undoEditorHistory(history, current) {
25
+ if (history.past.length === 0)
26
+ return null;
27
+ const value = history.past[history.past.length - 1];
28
+ return {
29
+ value,
30
+ history: {
31
+ past: history.past.slice(0, -1),
32
+ future: [...history.future, current],
33
+ lastKey: null,
34
+ lastAt: 0,
35
+ },
36
+ };
37
+ }
38
+ export function redoEditorHistory(history, current) {
39
+ if (history.future.length === 0)
40
+ return null;
41
+ const value = history.future[history.future.length - 1];
42
+ return {
43
+ value,
44
+ history: {
45
+ past: [...history.past, current],
46
+ future: history.future.slice(0, -1),
47
+ lastKey: null,
48
+ lastAt: 0,
49
+ },
50
+ };
51
+ }
52
+ /** Text fields and rich text keep their own undo. The page stack must not steal it. */
53
+ export function isNativeEditingTarget(target) {
54
+ if (!(target instanceof HTMLElement))
55
+ return false;
56
+ if (target.isContentEditable)
57
+ return true;
58
+ const tag = target.tagName;
59
+ return tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT';
60
+ }
61
+ //# sourceMappingURL=editor-history.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editor-history.js","sourceRoot":"","sources":["../../src/lib/editor-history.ts"],"names":[],"mappings":"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"}