@kywi-software/core 0.6.7 → 0.7.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 (102) hide show
  1. package/AGENT-PATTERNS.md +58 -2
  2. package/dist/admin/registry.d.ts +2 -2
  3. package/dist/admin/registry.d.ts.map +1 -1
  4. package/dist/admin/registry.js +41 -19
  5. package/dist/admin/registry.js.map +1 -1
  6. package/dist/admin/styles/admin.css +130 -0
  7. package/dist/admin/surfaces/menus/menu-editor.d.ts +3 -0
  8. package/dist/admin/surfaces/menus/menu-editor.d.ts.map +1 -0
  9. package/dist/admin/surfaces/menus/menu-editor.js +260 -0
  10. package/dist/admin/surfaces/menus/menu-editor.js.map +1 -0
  11. package/dist/admin/surfaces/menus/menu-tree.d.ts +123 -0
  12. package/dist/admin/surfaces/menus/menu-tree.d.ts.map +1 -0
  13. package/dist/admin/surfaces/menus/menu-tree.js +292 -0
  14. package/dist/admin/surfaces/menus/menu-tree.js.map +1 -0
  15. package/dist/admin/surfaces/menus/menus-data.d.ts +101 -0
  16. package/dist/admin/surfaces/menus/menus-data.d.ts.map +1 -0
  17. package/dist/admin/surfaces/menus/menus-data.js +98 -0
  18. package/dist/admin/surfaces/menus/menus-data.js.map +1 -0
  19. package/dist/admin/surfaces/menus/menus-list.d.ts +3 -0
  20. package/dist/admin/surfaces/menus/menus-list.d.ts.map +1 -0
  21. package/dist/admin/surfaces/menus/menus-list.js +105 -0
  22. package/dist/admin/surfaces/menus/menus-list.js.map +1 -0
  23. package/dist/admin/surfaces/menus/page-picker.d.ts +10 -0
  24. package/dist/admin/surfaces/menus/page-picker.d.ts.map +1 -0
  25. package/dist/admin/surfaces/menus/page-picker.js +38 -0
  26. package/dist/admin/surfaces/menus/page-picker.js.map +1 -0
  27. package/dist/admin/surfaces/settings/admin-features.d.ts.map +1 -1
  28. package/dist/admin/surfaces/settings/admin-features.js +1 -0
  29. package/dist/admin/surfaces/settings/admin-features.js.map +1 -1
  30. package/dist/api/feed-routes.d.ts +2 -2
  31. package/dist/api/index.d.ts +1 -0
  32. package/dist/api/index.d.ts.map +1 -1
  33. package/dist/api/index.js +5 -0
  34. package/dist/api/index.js.map +1 -1
  35. package/dist/api/menu-routes.d.ts +46 -0
  36. package/dist/api/menu-routes.d.ts.map +1 -0
  37. package/dist/api/menu-routes.js +247 -0
  38. package/dist/api/menu-routes.js.map +1 -0
  39. package/dist/api/openapi.d.ts.map +1 -1
  40. package/dist/api/openapi.js +105 -0
  41. package/dist/api/openapi.js.map +1 -1
  42. package/dist/cache/cached-queries.d.ts +22 -0
  43. package/dist/cache/cached-queries.d.ts.map +1 -1
  44. package/dist/cache/cached-queries.js +31 -1
  45. package/dist/cache/cached-queries.js.map +1 -1
  46. package/dist/cache/keys.d.ts +2 -0
  47. package/dist/cache/keys.d.ts.map +1 -1
  48. package/dist/cache/keys.js +4 -0
  49. package/dist/cache/keys.js.map +1 -1
  50. package/dist/db/index.d.ts +1 -0
  51. package/dist/db/index.d.ts.map +1 -1
  52. package/dist/db/index.js +1 -0
  53. package/dist/db/index.js.map +1 -1
  54. package/dist/db/menu-queries.d.ts +182 -0
  55. package/dist/db/menu-queries.d.ts.map +1 -0
  56. package/dist/db/menu-queries.js +326 -0
  57. package/dist/db/menu-queries.js.map +1 -0
  58. package/dist/layout/index.d.ts +1 -0
  59. package/dist/layout/index.d.ts.map +1 -1
  60. package/dist/layout/index.js +1 -0
  61. package/dist/layout/index.js.map +1 -1
  62. package/dist/layout/modules-extended.d.ts +32 -2
  63. package/dist/layout/modules-extended.d.ts.map +1 -1
  64. package/dist/layout/modules-extended.js +159 -39
  65. package/dist/layout/modules-extended.js.map +1 -1
  66. package/dist/layout/nav-hydration.d.ts +62 -0
  67. package/dist/layout/nav-hydration.d.ts.map +1 -0
  68. package/dist/layout/nav-hydration.js +149 -0
  69. package/dist/layout/nav-hydration.js.map +1 -0
  70. package/dist/layout/registry.d.ts +0 -26
  71. package/dist/layout/registry.d.ts.map +1 -1
  72. package/dist/layout/registry.js +22 -8
  73. package/dist/layout/registry.js.map +1 -1
  74. package/dist/nav/builder.d.ts +16 -0
  75. package/dist/nav/builder.d.ts.map +1 -1
  76. package/dist/nav/builder.js +21 -2
  77. package/dist/nav/builder.js.map +1 -1
  78. package/dist/nav/index.d.ts +1 -0
  79. package/dist/nav/index.d.ts.map +1 -1
  80. package/dist/nav/index.js +1 -0
  81. package/dist/nav/index.js.map +1 -1
  82. package/dist/nav/menu-resolver.d.ts +52 -0
  83. package/dist/nav/menu-resolver.d.ts.map +1 -0
  84. package/dist/nav/menu-resolver.js +113 -0
  85. package/dist/nav/menu-resolver.js.map +1 -0
  86. package/dist/nav/types.d.ts +30 -0
  87. package/dist/nav/types.d.ts.map +1 -1
  88. package/dist/schema/base.d.ts +331 -0
  89. package/dist/schema/base.d.ts.map +1 -1
  90. package/dist/schema/base.js +45 -0
  91. package/dist/schema/base.js.map +1 -1
  92. package/dist/schema/mysql-tables.d.ts +303 -0
  93. package/dist/schema/mysql-tables.d.ts.map +1 -1
  94. package/dist/schema/mysql-tables.js +25 -0
  95. package/dist/schema/mysql-tables.js.map +1 -1
  96. package/dist/scope/create-scope.d.ts.map +1 -1
  97. package/dist/scope/create-scope.js +27 -1
  98. package/dist/scope/create-scope.js.map +1 -1
  99. package/dist/scope/types.d.ts +28 -1
  100. package/dist/scope/types.d.ts.map +1 -1
  101. package/dist/site/styles.css +380 -0
  102. package/package.json +1 -1
@@ -0,0 +1,260 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ /**
4
+ * `menus.detail` surface — the hierarchical menu item editor (#112).
5
+ *
6
+ * The tree is held client-side for the whole editing session and written back
7
+ * with one nested `PUT /menus/:id/items` (a full replace), so reordering,
8
+ * reparenting, renaming and removing are all local until Save. That is what
9
+ * lets sibling order simply BE array order — see `menu-tree.ts` for why this
10
+ * fixes the site tree's sortOrder-assignment reorder.
11
+ *
12
+ * The dnd machinery (DndContext + a flat SortableContext + DragOverlay + the
13
+ * `kywi-tree-*` classes) is the site tree's, with one deliberate change: the
14
+ * rows are flattened rather than nested, because a flat sortable list is what
15
+ * makes a horizontal drag readable as a depth change — i.e. reparenting.
16
+ */
17
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
18
+ import { DndContext, closestCenter, KeyboardSensor, PointerSensor, useSensor, useSensors, DragOverlay, } from '@dnd-kit/core';
19
+ import { SortableContext, sortableKeyboardCoordinates, verticalListSortingStrategy, useSortable, } from '@dnd-kit/sortable';
20
+ import { CSS } from '@dnd-kit/utilities';
21
+ import { useAdminContext } from '../../context.js';
22
+ import { LoadingState, ErrorState, ConfirmDialog, Modal } from '../../primitives.js';
23
+ import { statusLabel } from '../../status-label.js';
24
+ import { loadMenus, loadMenuItems, saveMenuItems, loadContentTree, loadNavLabels, } from './menus-data.js';
25
+ import { fromServerItems, flattenTree, findItem, makeItem, appendItem, updateItem, removeItem, moveItem, indentItem, outdentItem, toPayload, itemsWithoutTarget, countItems, subtreeHeight, INDENT_WIDTH, MAX_MENU_DEPTH, } from './menu-tree.js';
26
+ import { PagePicker } from './page-picker.js';
27
+ function MenuItemRow({ item, depth, selected, onSelect }) {
28
+ const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
29
+ id: item.key,
30
+ });
31
+ const broken = item.contentMissing || (!item.contentId && !item.externalUrl);
32
+ const unpublished = !broken && item.contentStatus !== null && item.contentStatus !== 'published';
33
+ const classes = [
34
+ 'kywi-tree-node',
35
+ 'kywi-menu-item-row',
36
+ selected ? 'kywi-tree-selected' : '',
37
+ isDragging ? 'kywi-tree-dragging' : '',
38
+ broken ? 'kywi-menu-item-broken' : '',
39
+ unpublished ? 'kywi-menu-item-unpublished' : '',
40
+ ].filter(Boolean).join(' ');
41
+ return (_jsxs("div", { ref: setNodeRef, className: classes, "data-menu-item": item.key,
42
+ // Selecting a row is what opens the settings panel, so the row has to be
43
+ // reachable without a pointer — hence the tabIndex + Enter/Space handler.
44
+ role: "treeitem", "aria-level": depth + 1, "aria-selected": selected, tabIndex: 0, style: {
45
+ transform: CSS.Transform.toString(transform),
46
+ transition,
47
+ paddingLeft: `${depth * INDENT_WIDTH + 8}px`,
48
+ }, onClick: () => onSelect(item.key), onKeyDown: e => {
49
+ if (e.key === 'Enter' || e.key === ' ') {
50
+ e.preventDefault();
51
+ onSelect(item.key);
52
+ }
53
+ }, children: [_jsx("button", { type: "button", className: "kywi-menu-item-handle", "aria-label": `Reorder ${item.label}`, onClick: e => e.stopPropagation(), ...attributes, ...listeners, children: '≡' }), _jsx("span", { className: "kywi-tree-title", children: item.label }), _jsx("span", { className: "kywi-menu-item-target", children: item.contentId ? item.href ?? '—' : item.externalUrl ?? '—' }), item.externalUrl && _jsx("span", { className: "kywi-chip", children: "External" }), item.openInNewWindow && _jsx("span", { className: "kywi-chip", children: "New window" }), unpublished && (_jsx("span", { className: `kywi-status-badge kywi-status-${item.contentStatus}`, children: statusLabel(item.contentStatus) })), broken && (_jsxs("span", { className: "kywi-menu-item-warning", role: "status", children: ['⚠', " Link broken"] }))] }));
54
+ }
55
+ // ── External-link dialog ──────────────────────────────────────────────────────
56
+ function ExternalLinkModal({ onAdd, onClose, }) {
57
+ const [label, setLabel] = useState('');
58
+ const [url, setUrl] = useState('');
59
+ const [openInNewWindow, setOpenInNewWindow] = useState(true);
60
+ const [error, setError] = useState(null);
61
+ const submit = () => {
62
+ if (!label.trim()) {
63
+ setError('A label is required.');
64
+ return;
65
+ }
66
+ if (!url.trim()) {
67
+ setError('A URL is required.');
68
+ return;
69
+ }
70
+ onAdd({ label: label.trim(), url: url.trim(), openInNewWindow });
71
+ };
72
+ return (_jsxs(Modal, { title: "Add an external link", onClose: onClose, labelledBy: "kywi-menu-external-title", children: [_jsxs("div", { className: "kywi-field", children: [_jsx("label", { htmlFor: "kywi-menu-external-label", className: "kywi-field-label", children: "Label" }), _jsx("input", { id: "kywi-menu-external-label", type: "text", className: "kywi-input", value: label, placeholder: "Documentation", onChange: e => setLabel(e.target.value) })] }), _jsxs("div", { className: "kywi-field", children: [_jsx("label", { htmlFor: "kywi-menu-external-url", className: "kywi-field-label", children: "URL" }), _jsx("input", { id: "kywi-menu-external-url", type: "text", className: "kywi-input", value: url, placeholder: "https://example.com/docs", onChange: e => setUrl(e.target.value) })] }), _jsxs("label", { className: "kywi-checkbox-label", children: [_jsx("input", { type: "checkbox", checked: openInNewWindow, onChange: e => setOpenInNewWindow(e.target.checked) }), _jsx("span", { children: "Open in a new window" })] }), error && _jsx(ErrorState, { message: error }), _jsxs("div", { className: "kywi-admin-form-actions", children: [_jsx("button", { type: "button", className: "kywi-btn kywi-btn-secondary", onClick: onClose, children: "Cancel" }), _jsx("button", { type: "button", className: "kywi-btn kywi-btn-primary", onClick: submit, children: "Add link" })] })] }));
73
+ }
74
+ // ── Surface ───────────────────────────────────────────────────────────────────
75
+ export default function MenuEditorSurface({ params }) {
76
+ const { fetch: adminFetch, navigate } = useAdminContext();
77
+ const menuId = params.id ?? '';
78
+ const [menu, setMenu] = useState(null);
79
+ const [items, setItems] = useState([]);
80
+ /** Serialized form of the last state the server acknowledged — the dirty base. */
81
+ const [savedPayload, setSavedPayload] = useState('[]');
82
+ const [loading, setLoading] = useState(true);
83
+ const [error, setError] = useState(null);
84
+ const [saving, setSaving] = useState(false);
85
+ const [saveError, setSaveError] = useState(null);
86
+ const [saved, setSaved] = useState(false);
87
+ const [selectedKey, setSelectedKey] = useState(null);
88
+ const [activeKey, setActiveKey] = useState(null);
89
+ const [picker, setPicker] = useState(null);
90
+ const [pickerError, setPickerError] = useState(null);
91
+ const [contentTree, setContentTree] = useState(null);
92
+ const [navLabels, setNavLabels] = useState({});
93
+ const [pendingRemove, setPendingRemove] = useState(null);
94
+ // Declared before the loading/error early-returns — sensors are hooks.
95
+ const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 5 } }), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }));
96
+ const load = useCallback(async () => {
97
+ setLoading(true);
98
+ setError(null);
99
+ setSaveError(null);
100
+ setSaved(false);
101
+ try {
102
+ const [menus, nodes] = await Promise.all([
103
+ loadMenus(adminFetch),
104
+ loadMenuItems(adminFetch, menuId),
105
+ ]);
106
+ setMenu(menus.find(m => m.id === menuId) ?? null);
107
+ const loaded = fromServerItems(nodes);
108
+ setItems(loaded);
109
+ setSavedPayload(JSON.stringify(toPayload(loaded)));
110
+ setSelectedKey(null);
111
+ }
112
+ catch (e) {
113
+ setError(e instanceof Error ? e.message : 'Failed to load menu');
114
+ }
115
+ finally {
116
+ setLoading(false);
117
+ }
118
+ }, [adminFetch, menuId]);
119
+ useEffect(() => { load(); }, [load]);
120
+ const payload = useMemo(() => toPayload(items), [items]);
121
+ const dirty = JSON.stringify(payload) !== savedPayload;
122
+ // Unsaved-changes guard for a full page unload / external link. In-app
123
+ // navigation is guarded by `leave()` below (the router never leaves the page).
124
+ useEffect(() => {
125
+ if (!dirty)
126
+ return;
127
+ const onBeforeUnload = (e) => {
128
+ e.preventDefault();
129
+ e.returnValue = '';
130
+ };
131
+ window.addEventListener('beforeunload', onBeforeUnload);
132
+ return () => window.removeEventListener('beforeunload', onBeforeUnload);
133
+ }, [dirty]);
134
+ const dirtyRef = useRef(dirty);
135
+ dirtyRef.current = dirty;
136
+ const leave = useCallback(() => {
137
+ if (dirtyRef.current && !window.confirm('You have unsaved menu changes. Leave without saving?'))
138
+ return;
139
+ navigate('/admin/menus');
140
+ }, [navigate]);
141
+ const flat = useMemo(() => flattenTree(items), [items]);
142
+ const selected = selectedKey ? findItem(items, selectedKey) : null;
143
+ const selectedEntry = selectedKey ? flat.find(f => f.key === selectedKey) : undefined;
144
+ // ── Mutations
145
+ const patchSelected = useCallback((patch) => {
146
+ if (!selectedKey)
147
+ return;
148
+ setSaved(false);
149
+ setItems(prev => updateItem(prev, selectedKey, patch));
150
+ }, [selectedKey]);
151
+ const handleDragStart = useCallback((event) => {
152
+ setActiveKey(String(event.active.id));
153
+ }, []);
154
+ const handleDragEnd = useCallback((event) => {
155
+ setActiveKey(null);
156
+ const { active, over, delta } = event;
157
+ if (!over || active.id === over.id)
158
+ return;
159
+ setSaved(false);
160
+ setItems(prev => moveItem(prev, String(active.id), String(over.id), delta?.x ?? 0));
161
+ }, []);
162
+ const openPagePicker = useCallback(async () => {
163
+ setPickerError(null);
164
+ setPicker('page');
165
+ if (contentTree)
166
+ return;
167
+ try {
168
+ const [tree, labels] = await Promise.all([
169
+ loadContentTree(adminFetch),
170
+ loadNavLabels(adminFetch).catch(() => ({})),
171
+ ]);
172
+ setContentTree(tree);
173
+ setNavLabels(labels);
174
+ }
175
+ catch (e) {
176
+ setPickerError(e instanceof Error ? e.message : 'Failed to load content');
177
+ setContentTree([]);
178
+ }
179
+ }, [adminFetch, contentTree]);
180
+ const addPageItem = useCallback((node, label) => {
181
+ const item = makeItem({
182
+ label,
183
+ contentId: node.id,
184
+ href: node.path,
185
+ contentStatus: node.status,
186
+ });
187
+ setSaved(false);
188
+ setItems(prev => appendItem(prev, item));
189
+ setSelectedKey(item.key);
190
+ setPicker(null);
191
+ }, []);
192
+ const addExternalItem = useCallback((values) => {
193
+ const item = makeItem({
194
+ label: values.label,
195
+ externalUrl: values.url,
196
+ href: values.url,
197
+ openInNewWindow: values.openInNewWindow,
198
+ });
199
+ setSaved(false);
200
+ setItems(prev => appendItem(prev, item));
201
+ setSelectedKey(item.key);
202
+ setPicker(null);
203
+ }, []);
204
+ const confirmRemove = useCallback(() => {
205
+ if (!pendingRemove)
206
+ return;
207
+ setSaved(false);
208
+ setItems(prev => removeItem(prev, pendingRemove.key).items);
209
+ setSelectedKey(null);
210
+ setPendingRemove(null);
211
+ }, [pendingRemove]);
212
+ const handleRemove = useCallback((item) => {
213
+ // Removing a parent takes its children with it — always confirm that case.
214
+ if (item.children.length > 0) {
215
+ setPendingRemove(item);
216
+ return;
217
+ }
218
+ setSaved(false);
219
+ setItems(prev => removeItem(prev, item.key).items);
220
+ setSelectedKey(null);
221
+ }, []);
222
+ const handleSave = useCallback(async () => {
223
+ setSaveError(null);
224
+ const orphans = itemsWithoutTarget(items);
225
+ if (orphans.length > 0) {
226
+ setSaveError(`${orphans.length} item${orphans.length === 1 ? '' : 's'} (${orphans
227
+ .map(i => `“${i.label}”`)
228
+ .join(', ')}) no longer link to anything. Remove them or give them a URL before saving.`);
229
+ return;
230
+ }
231
+ setSaving(true);
232
+ try {
233
+ const result = await saveMenuItems(adminFetch, menuId, payload);
234
+ const adopted = fromServerItems(result);
235
+ setItems(adopted);
236
+ setSavedPayload(JSON.stringify(toPayload(adopted)));
237
+ setSelectedKey(null);
238
+ setSaved(true);
239
+ }
240
+ catch (e) {
241
+ setSaveError(e instanceof Error ? e.message : 'Save failed');
242
+ }
243
+ finally {
244
+ setSaving(false);
245
+ }
246
+ }, [adminFetch, menuId, items, payload]);
247
+ // ── Render
248
+ if (loading)
249
+ return _jsx(LoadingState, { label: "Loading menu\u2026" });
250
+ if (error)
251
+ return _jsx(ErrorState, { message: error, onRetry: load });
252
+ const canIndent = selectedEntry !== undefined &&
253
+ flat.filter(f => f.parentKey === selectedEntry.parentKey)[0]?.key !== selectedEntry.key &&
254
+ selectedEntry.depth + 1 + subtreeHeight(selectedEntry.item) <= MAX_MENU_DEPTH - 1;
255
+ const canOutdent = selectedEntry !== undefined && selectedEntry.parentKey !== null;
256
+ return (_jsxs("div", { className: "kywi-menu-editor", children: [_jsxs("div", { className: "kywi-admin-page-header", children: [_jsxs("div", { children: [_jsx("h1", { children: menu ? `Edit menu: ${menu.name}` : 'Edit menu' }), _jsxs("p", { className: "kywi-text-muted", children: [countItems(items), " item", countItems(items) === 1 ? '' : 's', menu ? _jsxs(_Fragment, { children: [" \u00B7 slug ", _jsx("code", { children: menu.slug })] }) : null, " \u00B7 drag a row to reorder, drag it sideways to nest it under the row above (up to ", MAX_MENU_DEPTH, " levels)."] })] }), _jsxs("div", { className: "kywi-admin-button-row", children: [_jsx("button", { type: "button", className: "kywi-btn kywi-btn-ghost", onClick: leave, children: "Back to menus" }), _jsx("button", { type: "button", className: "kywi-btn kywi-btn-secondary", onClick: load, disabled: saving || !dirty, children: "Cancel" }), _jsx("button", { type: "button", className: "kywi-btn kywi-btn-primary", onClick: handleSave, disabled: saving || !dirty, children: saving ? 'Saving…' : 'Save menu' })] })] }), dirty && _jsx("p", { className: "kywi-menu-editor-dirty", role: "status", children: "Unsaved changes" }), saved && !dirty && (_jsx("p", { className: "kywi-menu-editor-saved", role: "status", children: "Menu saved." })), saveError && _jsx(ErrorState, { message: saveError }), _jsxs("div", { className: "kywi-menu-editor-grid", children: [_jsxs("section", { className: "kywi-menu-editor-tree", "aria-label": "Menu structure", children: [flat.length === 0 && (_jsx("div", { className: "kywi-empty-state", children: _jsx("p", { children: "This menu has no items yet. Add a page or an external link below." }) })), _jsxs(DndContext, { sensors: sensors, collisionDetection: closestCenter, onDragStart: handleDragStart, onDragEnd: handleDragEnd, children: [_jsx(SortableContext, { items: flat.map(f => f.key), strategy: verticalListSortingStrategy, children: _jsx("div", { className: "kywi-menu-item-list", role: "tree", "aria-label": "Menu items", children: flat.map(entry => (_jsx(MenuItemRow, { item: entry.item, depth: entry.depth, selected: entry.key === selectedKey, onSelect: setSelectedKey }, entry.key))) }) }), _jsx(DragOverlay, { children: activeKey ? (_jsx("div", { className: "kywi-tree-drag-overlay", children: _jsx("span", { className: "kywi-tree-title", children: findItem(items, activeKey)?.label }) })) : null })] }), _jsxs("div", { className: "kywi-menu-editor-add", children: [_jsx("button", { type: "button", className: "kywi-btn kywi-btn-secondary kywi-btn-sm", onClick: openPagePicker, children: "Add page link" }), _jsx("button", { type: "button", className: "kywi-btn kywi-btn-secondary kywi-btn-sm", onClick: () => setPicker('external'), children: "Add external link" })] })] }), _jsx("aside", { className: "kywi-menu-item-panel", "aria-label": "Item settings", children: !selected ? (_jsx("div", { className: "kywi-empty-state", children: _jsx("p", { children: "Select an item to edit its label, description and link behaviour." }) })) : (_jsxs(_Fragment, { children: [_jsx("h2", { className: "kywi-section-heading", children: "Item" }), selected.contentMissing && (_jsxs("p", { className: "kywi-menu-item-warning", role: "alert", children: ['⚠', " The page this item linked to has been deleted. Remove the item or turn it into an external link."] })), !selected.contentMissing &&
257
+ selected.contentStatus !== null &&
258
+ selected.contentStatus !== 'published' && (_jsxs("p", { className: "kywi-menu-item-warning", role: "status", children: ['⚠', " This page is ", statusLabel(selected.contentStatus).toLowerCase(), ", so the item is hidden from visitors until it is published."] })), _jsxs("div", { className: "kywi-field", children: [_jsx("label", { htmlFor: "kywi-menu-item-label", className: "kywi-field-label", children: "Label" }), _jsx("input", { id: "kywi-menu-item-label", type: "text", className: "kywi-input", value: selected.label, onChange: e => patchSelected({ label: e.target.value }) })] }), _jsxs("div", { className: "kywi-field", children: [_jsx("span", { className: "kywi-field-label", children: "Link" }), selected.contentId ? (_jsxs("p", { className: "kywi-field-hint", children: ["Page \u00B7 ", _jsx("code", { children: selected.href ?? 'unresolved' }), " \u2014 the link follows the page if it is renamed or moved."] })) : (_jsx("input", { type: "text", className: "kywi-input", "aria-label": "External URL", value: selected.externalUrl ?? '', onChange: e => patchSelected({ externalUrl: e.target.value, href: e.target.value }) }))] }), _jsxs("div", { className: "kywi-field", children: [_jsx("label", { htmlFor: "kywi-menu-item-description", className: "kywi-field-label", children: "Description" }), _jsx("textarea", { id: "kywi-menu-item-description", className: "kywi-input kywi-textarea", rows: 3, value: selected.description, onChange: e => patchSelected({ description: e.target.value }) }), _jsx("span", { className: "kywi-field-hint", children: "Shown beside the label in mega-menu panels. Ignored by compact nav variants." })] }), _jsxs("label", { className: "kywi-checkbox-label", children: [_jsx("input", { type: "checkbox", checked: selected.openInNewWindow, onChange: e => patchSelected({ openInNewWindow: e.target.checked }) }), _jsx("span", { children: "Open in a new window" })] }), _jsxs("div", { className: "kywi-admin-form-actions kywi-menu-item-nesting", children: [_jsx("button", { type: "button", className: "kywi-btn kywi-btn-ghost kywi-btn-sm", disabled: !canOutdent, onClick: () => { setSaved(false); setItems(prev => outdentItem(prev, selected.key)); }, children: "Move out one level" }), _jsx("button", { type: "button", className: "kywi-btn kywi-btn-ghost kywi-btn-sm", disabled: !canIndent, onClick: () => { setSaved(false); setItems(prev => indentItem(prev, selected.key)); }, children: "Nest under previous" })] }), _jsx("div", { className: "kywi-admin-form-actions", children: _jsx("button", { type: "button", className: "kywi-btn kywi-btn-danger kywi-btn-sm", onClick: () => handleRemove(selected), children: "Remove item" }) })] })) })] }), picker === 'page' && (pickerError ? (_jsx(Modal, { title: "Link to a page", onClose: () => setPicker(null), children: _jsx(ErrorState, { message: pickerError }) })) : contentTree === null ? (_jsx(Modal, { title: "Link to a page", onClose: () => setPicker(null), children: _jsx(LoadingState, { label: "Loading content\u2026" }) })) : (_jsx(PagePicker, { nodes: contentTree, navLabels: navLabels, onSelect: addPageItem, onClose: () => setPicker(null) }))), picker === 'external' && (_jsx(ExternalLinkModal, { onAdd: addExternalItem, onClose: () => setPicker(null) })), pendingRemove && (_jsx(ConfirmDialog, { title: "Remove item", message: _jsxs(_Fragment, { children: ["Remove \u201C", pendingRemove.label, "\u201D and the ", countItems(pendingRemove.children), " item", countItems(pendingRemove.children) === 1 ? '' : 's', " nested under it? The change applies when you save the menu."] }), confirmLabel: "Remove", onCancel: () => setPendingRemove(null), onConfirm: confirmRemove }))] }));
259
+ }
260
+ //# sourceMappingURL=menu-editor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"menu-editor.js","sourceRoot":"","sources":["../../../../src/admin/surfaces/menus/menu-editor.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,123 @@
1
+ /**
2
+ * menu-tree.ts — the client-side item model behind the Menus editor (#112).
3
+ *
4
+ * Why a client-side tree at all: `PUT /menus/:id/items` is a NESTED FULL
5
+ * REPLACE, so the editor owns the whole tree between loads and serializes it
6
+ * once on save. That is what lets this module fix the site tree's reorder
7
+ * semantics rather than inherit them.
8
+ *
9
+ * site-tree.tsx (naive): on drop, the dragged node is handed the *target's*
10
+ * `sortOrder`, leaving two siblings sharing an order and the rest of the group
11
+ * untouched — the resulting sequence depends on the server's tie-break.
12
+ *
13
+ * here: a drop splices the dragged subtree into its new sibling array, and
14
+ * sibling order IS array order. Serialization walks the arrays in order, so
15
+ * every affected group comes out resequenced 0..n by construction, with no
16
+ * duplicate or gapped positions possible.
17
+ *
18
+ * Everything in this module is pure and immutable (a move returns a new tree),
19
+ * which is what makes the drag semantics testable without dnd-kit.
20
+ */
21
+ import type { MenuItemNode, MenuItemPayload } from './menus-data.js';
22
+ /**
23
+ * Maximum nesting the nav renderers handle: 3 levels ⇒ depths 0, 1 and 2.
24
+ * Drops that would go deeper are clamped, never rejected (see {@link moveItem}).
25
+ */
26
+ export declare const MAX_MENU_DEPTH = 3;
27
+ /** Pixels of horizontal drag per depth step; also the row's indent unit. */
28
+ export declare const INDENT_WIDTH = 24;
29
+ /**
30
+ * One editable item. `key` is a stable client identity — the server row id for
31
+ * a loaded item, a generated key for a new one — because a full replace means
32
+ * server ids are reissued on every save and cannot identify a row mid-edit.
33
+ */
34
+ export interface MenuEditorItem {
35
+ key: string;
36
+ label: string;
37
+ description: string;
38
+ openInNewWindow: boolean;
39
+ /** Set for a page link. Mutually exclusive with `externalUrl`. */
40
+ contentId: string | null;
41
+ /** Set for an external link. Mutually exclusive with `contentId`. */
42
+ externalUrl: string | null;
43
+ /** Server-resolved href, for display only (null for a broken target). */
44
+ href: string | null;
45
+ /** Linked content's status, e.g. `'draft'`. Null for external/new items. */
46
+ contentStatus: string | null;
47
+ /** Server flag: the linked content is gone (deleted) and there is no URL. */
48
+ contentMissing: boolean;
49
+ children: MenuEditorItem[];
50
+ }
51
+ /** A flattened row: what the editor renders and what drops are computed over. */
52
+ export interface FlatMenuItem {
53
+ item: MenuEditorItem;
54
+ key: string;
55
+ depth: number;
56
+ parentKey: string | null;
57
+ }
58
+ /** Fresh client key for an item that has no server row yet. */
59
+ export declare function newItemKey(): string;
60
+ /** Adopt the server's flagged admin tree as editor state. */
61
+ export declare function fromServerItems(nodes: MenuItemNode[]): MenuEditorItem[];
62
+ /** A blank item of either kind, ready to append to the tree. */
63
+ export declare function makeItem(init: Partial<MenuEditorItem> & Pick<MenuEditorItem, 'label'>): MenuEditorItem;
64
+ /**
65
+ * Serialize to the nested `PUT /menus/:id/items` body.
66
+ *
67
+ * Optional keys are omitted rather than sent empty: the route's zod schema is
68
+ * `.strict()`, and an omitted `description` is meaningfully different from an
69
+ * empty string. Sibling order is array order — the resequencing contract.
70
+ */
71
+ export declare function toPayload(items: MenuEditorItem[]): MenuItemPayload[];
72
+ /** Depth-first flatten; the render order and the drop-projection basis. */
73
+ export declare function flattenTree(items: MenuEditorItem[], parentKey?: string | null, depth?: number): FlatMenuItem[];
74
+ export declare function findItem(items: MenuEditorItem[], key: string): MenuEditorItem | null;
75
+ /** Total items at every depth. */
76
+ export declare function countItems(items: MenuEditorItem[]): number;
77
+ /** Levels of nesting BELOW an item (a leaf is 0). */
78
+ export declare function subtreeHeight(item: MenuEditorItem): number;
79
+ /** Items with neither a content link nor a URL — unsavable, and always broken. */
80
+ export declare function itemsWithoutTarget(items: MenuEditorItem[]): MenuEditorItem[];
81
+ export declare function updateItem(items: MenuEditorItem[], key: string, patch: Partial<Omit<MenuEditorItem, 'key' | 'children'>>): MenuEditorItem[];
82
+ /** Remove an item AND its subtree. Returns the new tree plus what was cut. */
83
+ export declare function removeItem(items: MenuEditorItem[], key: string): {
84
+ items: MenuEditorItem[];
85
+ removed: MenuEditorItem | null;
86
+ };
87
+ /** Splice an item into a parent's children (root when `parentKey` is null). */
88
+ export declare function insertItem(items: MenuEditorItem[], parentKey: string | null, index: number, item: MenuEditorItem): MenuEditorItem[];
89
+ /** Append a new item at the end of the root list. */
90
+ export declare function appendItem(items: MenuEditorItem[], item: MenuEditorItem): MenuEditorItem[];
91
+ /** Where a drop lands: the new parent and the index within that parent. */
92
+ export interface DropTarget {
93
+ parentKey: string | null;
94
+ index: number;
95
+ depth: number;
96
+ }
97
+ /**
98
+ * Resolve a drop into a concrete position.
99
+ *
100
+ * Vertical position comes from the item dropped ON (dnd-kit's `over`), depth
101
+ * from how far the pointer travelled horizontally (`offsetX`) — the standard
102
+ * dnd-kit sortable-tree projection, which is the only way a flat sortable list
103
+ * can express "reparent" as well as "reorder".
104
+ *
105
+ * The projected depth is clamped three ways, so a drop is always *graceful*:
106
+ * - it can be at most one level deeper than the row above it (you cannot
107
+ * create an orphan level),
108
+ * - never shallower than the row below it (which would jump that row's group),
109
+ * - and never so deep that the dragged item's own subtree would exceed
110
+ * {@link MAX_MENU_DEPTH}.
111
+ */
112
+ export declare function projectDrop(items: MenuEditorItem[], activeKey: string, overKey: string, offsetX: number): DropTarget | null;
113
+ /** Apply a drop. Returns the original tree unchanged when it cannot land. */
114
+ export declare function moveItem(items: MenuEditorItem[], activeKey: string, overKey: string, offsetX: number): MenuEditorItem[];
115
+ /**
116
+ * Keyboard/button equivalent of dragging right: become the last child of the
117
+ * previous sibling. No-op for a first child, or when the move would exceed
118
+ * {@link MAX_MENU_DEPTH}.
119
+ */
120
+ export declare function indentItem(items: MenuEditorItem[], key: string): MenuEditorItem[];
121
+ /** Become the next sibling of the current parent. No-op at the root level. */
122
+ export declare function outdentItem(items: MenuEditorItem[], key: string): MenuEditorItem[];
123
+ //# sourceMappingURL=menu-tree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"menu-tree.d.ts","sourceRoot":"","sources":["../../../../src/admin/surfaces/menus/menu-tree.ts"],"names":[],"mappings":"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"}