@mano8/astro-media-m8 1.1.0 → 2.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 (123) hide show
  1. package/README.md +54 -11
  2. package/dist/src/integration.d.ts.map +1 -1
  3. package/dist/src/integration.js +1 -0
  4. package/dist/src/integration.js.map +1 -1
  5. package/dist/src/runtime/api/categories.d.ts +14 -8
  6. package/dist/src/runtime/api/categories.d.ts.map +1 -1
  7. package/dist/src/runtime/api/categories.js +21 -8
  8. package/dist/src/runtime/api/categories.js.map +1 -1
  9. package/dist/src/runtime/api/index.d.ts +8 -0
  10. package/dist/src/runtime/api/index.d.ts.map +1 -1
  11. package/dist/src/runtime/api/index.js +8 -0
  12. package/dist/src/runtime/api/index.js.map +1 -1
  13. package/dist/src/runtime/api/transfer.d.ts +19 -0
  14. package/dist/src/runtime/api/transfer.d.ts.map +1 -0
  15. package/dist/src/runtime/api/transfer.js +48 -0
  16. package/dist/src/runtime/api/transfer.js.map +1 -0
  17. package/dist/src/runtime/client.d.ts.map +1 -1
  18. package/dist/src/runtime/client.js +7 -1
  19. package/dist/src/runtime/client.js.map +1 -1
  20. package/dist/src/runtime/compatibility.d.ts +6 -6
  21. package/dist/src/runtime/compatibility.d.ts.map +1 -1
  22. package/dist/src/runtime/compatibility.js +59 -7
  23. package/dist/src/runtime/compatibility.js.map +1 -1
  24. package/dist/src/runtime/errors.d.ts +20 -2
  25. package/dist/src/runtime/errors.d.ts.map +1 -1
  26. package/dist/src/runtime/errors.js +53 -2
  27. package/dist/src/runtime/errors.js.map +1 -1
  28. package/dist/src/runtime/hooks/index.d.ts +2 -0
  29. package/dist/src/runtime/hooks/index.d.ts.map +1 -1
  30. package/dist/src/runtime/hooks/index.js +2 -0
  31. package/dist/src/runtime/hooks/index.js.map +1 -1
  32. package/dist/src/runtime/hooks/useMediaCategories.d.ts +30 -0
  33. package/dist/src/runtime/hooks/useMediaCategories.d.ts.map +1 -0
  34. package/dist/src/runtime/hooks/useMediaCategories.js +56 -0
  35. package/dist/src/runtime/hooks/useMediaCategories.js.map +1 -0
  36. package/dist/src/runtime/hooks/useMediaObjects.d.ts.map +1 -1
  37. package/dist/src/runtime/hooks/useMediaObjects.js +14 -2
  38. package/dist/src/runtime/hooks/useMediaObjects.js.map +1 -1
  39. package/dist/src/runtime/hooks/useMediaTransfer.d.ts +42 -0
  40. package/dist/src/runtime/hooks/useMediaTransfer.d.ts.map +1 -0
  41. package/dist/src/runtime/hooks/useMediaTransfer.js +106 -0
  42. package/dist/src/runtime/hooks/useMediaTransfer.js.map +1 -0
  43. package/dist/src/runtime/queryKeys.d.ts +2 -0
  44. package/dist/src/runtime/queryKeys.d.ts.map +1 -1
  45. package/dist/src/runtime/queryKeys.js +2 -0
  46. package/dist/src/runtime/queryKeys.js.map +1 -1
  47. package/dist/src/runtime/react/CategoryManager.d.ts +10 -0
  48. package/dist/src/runtime/react/CategoryManager.d.ts.map +1 -0
  49. package/dist/src/runtime/react/CategoryManager.js +194 -0
  50. package/dist/src/runtime/react/CategoryManager.js.map +1 -0
  51. package/dist/src/runtime/react/CategoryMultiSelect.d.ts +73 -0
  52. package/dist/src/runtime/react/CategoryMultiSelect.d.ts.map +1 -0
  53. package/dist/src/runtime/react/CategoryMultiSelect.js +179 -0
  54. package/dist/src/runtime/react/CategoryMultiSelect.js.map +1 -0
  55. package/dist/src/runtime/react/MediaCategoryTreePane.d.ts +22 -0
  56. package/dist/src/runtime/react/MediaCategoryTreePane.d.ts.map +1 -0
  57. package/dist/src/runtime/react/MediaCategoryTreePane.js +244 -0
  58. package/dist/src/runtime/react/MediaCategoryTreePane.js.map +1 -0
  59. package/dist/src/runtime/react/MediaErrorBoundary.d.ts +53 -0
  60. package/dist/src/runtime/react/MediaErrorBoundary.d.ts.map +1 -0
  61. package/dist/src/runtime/react/MediaErrorBoundary.js +58 -0
  62. package/dist/src/runtime/react/MediaErrorBoundary.js.map +1 -0
  63. package/dist/src/runtime/react/MediaLibrary.d.ts +16 -1
  64. package/dist/src/runtime/react/MediaLibrary.d.ts.map +1 -1
  65. package/dist/src/runtime/react/MediaLibrary.js +75 -110
  66. package/dist/src/runtime/react/MediaLibrary.js.map +1 -1
  67. package/dist/src/runtime/react/MediaObjectViews.d.ts +47 -0
  68. package/dist/src/runtime/react/MediaObjectViews.d.ts.map +1 -0
  69. package/dist/src/runtime/react/MediaObjectViews.js +131 -0
  70. package/dist/src/runtime/react/MediaObjectViews.js.map +1 -0
  71. package/dist/src/runtime/react/MediaTransferPanel.d.ts +17 -0
  72. package/dist/src/runtime/react/MediaTransferPanel.d.ts.map +1 -0
  73. package/dist/src/runtime/react/MediaTransferPanel.js +93 -0
  74. package/dist/src/runtime/react/MediaTransferPanel.js.map +1 -0
  75. package/dist/src/runtime/react/MediaUploadDropzone.d.ts +36 -1
  76. package/dist/src/runtime/react/MediaUploadDropzone.d.ts.map +1 -1
  77. package/dist/src/runtime/react/MediaUploadDropzone.js +63 -3
  78. package/dist/src/runtime/react/MediaUploadDropzone.js.map +1 -1
  79. package/dist/src/runtime/react/ObjectDetail.d.ts.map +1 -1
  80. package/dist/src/runtime/react/ObjectDetail.js +51 -1
  81. package/dist/src/runtime/react/ObjectDetail.js.map +1 -1
  82. package/dist/src/runtime/react/categoryBranch.d.ts +38 -0
  83. package/dist/src/runtime/react/categoryBranch.d.ts.map +1 -0
  84. package/dist/src/runtime/react/categoryBranch.js +37 -0
  85. package/dist/src/runtime/react/categoryBranch.js.map +1 -0
  86. package/dist/src/runtime/react/default-ui/index.d.ts +6 -2
  87. package/dist/src/runtime/react/default-ui/index.d.ts.map +1 -1
  88. package/dist/src/runtime/react/default-ui/index.js +14 -9
  89. package/dist/src/runtime/react/default-ui/index.js.map +1 -1
  90. package/dist/src/runtime/react/index.d.ts +5 -2
  91. package/dist/src/runtime/react/index.d.ts.map +1 -1
  92. package/dist/src/runtime/react/index.js +3 -0
  93. package/dist/src/runtime/react/index.js.map +1 -1
  94. package/dist/src/runtime/react/mediaLibraryLabels.d.ts +93 -0
  95. package/dist/src/runtime/react/mediaLibraryLabels.d.ts.map +1 -0
  96. package/dist/src/runtime/react/mediaLibraryLabels.js +100 -0
  97. package/dist/src/runtime/react/mediaLibraryLabels.js.map +1 -0
  98. package/dist/src/runtime/react/mediaLibraryStyles.d.ts +44 -0
  99. package/dist/src/runtime/react/mediaLibraryStyles.d.ts.map +1 -0
  100. package/dist/src/runtime/react/mediaLibraryStyles.js +82 -0
  101. package/dist/src/runtime/react/mediaLibraryStyles.js.map +1 -0
  102. package/dist/src/runtime/routes.d.ts +2 -0
  103. package/dist/src/runtime/routes.d.ts.map +1 -1
  104. package/dist/src/runtime/routes.js +2 -0
  105. package/dist/src/runtime/routes.js.map +1 -1
  106. package/dist/src/runtime/schemas.d.ts +265 -0
  107. package/dist/src/runtime/schemas.d.ts.map +1 -1
  108. package/dist/src/runtime/schemas.js +173 -6
  109. package/dist/src/runtime/schemas.js.map +1 -1
  110. package/dist/src/runtime/upload/uploadController.d.ts +7 -0
  111. package/dist/src/runtime/upload/uploadController.d.ts.map +1 -1
  112. package/dist/src/runtime/upload/uploadController.js +10 -5
  113. package/dist/src/runtime/upload/uploadController.js.map +1 -1
  114. package/package.json +37 -30
  115. package/registry/r/media-category-tree.json +21 -0
  116. package/registry/r/media-dashboard-overview.json +1 -1
  117. package/registry/r/media-library-tree.json +25 -0
  118. package/registry/r/media-maintenance-panel.json +3 -2
  119. package/registry/r/media-transfer-dialog.json +26 -0
  120. package/registry/r/registry.json +18 -0
  121. package/registry.json +61 -1
  122. package/src/routes/categories.astro +12 -0
  123. package/src/scaffold/styles/media.css +395 -5
@@ -0,0 +1,194 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo, useState } from "react";
3
+ import { useCategoryTree } from "../hooks/useMediaCategories.js";
4
+ import { ApiError, messageFromDetail } from "../errors.js";
5
+ import { collectCategoryPaths } from "./CategoryMultiSelect.js";
6
+ const inputClassName = "fa-media-control h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40";
7
+ const labelClassName = "fa-media-label flex items-center gap-2 pb-2 text-sm leading-none font-medium select-none";
8
+ /**
9
+ * Copy for the refusals the category CRUD surface can answer (`U4`): an
10
+ * unknown/foreign parent, or a structural conflict — a cycle, a cross-tenant
11
+ * parent, a taken sibling name, or a delete blocked by existing children. The
12
+ * 409 case always surfaces the server's own message: those four conflicts
13
+ * each read as a distinct, already-human sentence
14
+ * ("Category has child categories; reparent or delete them first.", "That
15
+ * name is taken among its siblings.", …), and re-wording them here would
16
+ * drift from whichever one the server actually raised.
17
+ */
18
+ function describeCategoryError(error) {
19
+ if (error instanceof ApiError) {
20
+ if (error.status === 404)
21
+ return "That parent category no longer exists. Refresh and try again.";
22
+ if (error.status === 403)
23
+ return "That parent category is not yours to use.";
24
+ if (error.status === 409) {
25
+ return messageFromDetail(error.detail) ?? "That change conflicts with the existing category tree.";
26
+ }
27
+ return messageFromDetail(error.detail) ?? error.message;
28
+ }
29
+ return error instanceof Error ? error.message : "Failed to save the category";
30
+ }
31
+ /** Every category as a flattened `{id, path}` option, plus a root option. */
32
+ function parentOptions(tree) {
33
+ const paths = collectCategoryPaths(tree);
34
+ const options = [{ id: null, label: "— Root —" }];
35
+ for (const option of paths.values()) {
36
+ options.push({ id: option.id, label: option.path });
37
+ }
38
+ return options;
39
+ }
40
+ function findNode(nodes, id) {
41
+ for (const node of nodes) {
42
+ if (node.id === id)
43
+ return node;
44
+ const found = findNode(node.children, id);
45
+ if (found)
46
+ return found;
47
+ }
48
+ return undefined;
49
+ }
50
+ /**
51
+ * One category row plus its subtree. Rename, reparent and delete are each a
52
+ * plain form action — no drag-and-drop (decided 2026-08-18) — so a cycle,
53
+ * cross-tenant parent, taken-sibling-name or has-children refusal is just the
54
+ * mutation's promise rejecting; the server stays the single source of truth
55
+ * for every structural guard, this row only surfaces what it says.
56
+ */
57
+ /**
58
+ * The busy/error state every row mutation shares, plus the three mutations
59
+ * themselves. Each one reports the server's refusal through `rowError` rather
60
+ * than throwing on: a rejected rename, reparent or delete is a message on the
61
+ * row it belongs to, never a torn-down subtree.
62
+ */
63
+ function useRowMutations(node, props) {
64
+ const [busy, setBusy] = useState(false);
65
+ const [rowError, setRowError] = useState(null);
66
+ async function guard(action) {
67
+ setBusy(true);
68
+ setRowError(null);
69
+ try {
70
+ await action();
71
+ return true;
72
+ }
73
+ catch (error) {
74
+ setRowError(describeCategoryError(error));
75
+ return false;
76
+ }
77
+ finally {
78
+ setBusy(false);
79
+ }
80
+ }
81
+ return {
82
+ busy,
83
+ rowError,
84
+ rename: (name) => guard(() => props.onRename(node.id, name)),
85
+ reparent: (parentId) => guard(() => props.onReparent(node.id, parentId)),
86
+ remove: () => guard(() => props.onDelete(node.id))
87
+ };
88
+ }
89
+ /** The row in its rename state: a single text input and its two actions. */
90
+ function RowRenameForm({ node, busy, onSave, onCancel }) {
91
+ const [draftName, setDraftName] = useState(node.name);
92
+ const nameInputId = `fa-media-category-manager-name-${node.id}`;
93
+ return (_jsxs(_Fragment, { children: [_jsx("label", { className: "fa-media-category-manager-sr-label", htmlFor: nameInputId, children: `Rename ${node.name}` }), _jsx("input", { id: nameInputId, className: inputClassName, value: draftName, disabled: busy, onChange: (event) => setDraftName(event.currentTarget.value) }), _jsx("button", { type: "button", disabled: busy, onClick: () => onSave(draftName), children: "Save" }), _jsx("button", { type: "button", disabled: busy, onClick: onCancel, children: "Cancel" })] }));
94
+ }
95
+ /** The row at rest: name, count, and the rename/add-child/reparent/delete actions. */
96
+ function RowActions({ node, options, busy, onStartRename, onAddChild, onReparent, onDelete }) {
97
+ // A node cannot be its own parent — the one cycle worth foreclosing
98
+ // client-side. Every deeper cycle (reparenting under a descendant) is left
99
+ // to the server's 409, surfaced on the row.
100
+ const reparentOptions = options.filter((option) => option.id !== node.id);
101
+ const parentSelectId = `fa-media-category-manager-parent-${node.id}`;
102
+ return (_jsxs(_Fragment, { children: [_jsx("span", { className: "fa-media-category-manager-name", children: node.name }), _jsx("span", { className: "fa-media-badge fa-media-category-count", children: node.total_object_count }), _jsx("button", { type: "button", disabled: busy, onClick: onStartRename, children: "Rename" }), _jsx("button", { type: "button", disabled: busy, onClick: () => onAddChild(node.id), children: "Add child" }), _jsxs("label", { className: "fa-media-category-manager-reparent-label", htmlFor: parentSelectId, children: ["Parent", _jsx("select", { id: parentSelectId, className: inputClassName, disabled: busy, value: node.parent_id ?? "", onChange: (event) => {
103
+ const value = event.currentTarget.value;
104
+ onReparent(value === "" ? null : Number(value));
105
+ }, children: reparentOptions.map((option) => (_jsx("option", { value: option.id ?? "", children: option.label }, option.id ?? "root"))) })] }), _jsx("button", { type: "button", disabled: busy, onClick: onDelete, children: "Delete" })] }));
106
+ }
107
+ function CategoryManagerRow(props) {
108
+ const { node, depth, options, onAddChild } = props;
109
+ const [renaming, setRenaming] = useState(false);
110
+ const { busy, rowError, rename, reparent, remove } = useRowMutations(node, props);
111
+ // An empty or unchanged name is not a rename — leave the row as it was
112
+ // rather than sending the server a no-op it would have to answer.
113
+ async function saveRename(draft) {
114
+ const trimmed = draft.trim();
115
+ if (!trimmed || trimmed === node.name) {
116
+ setRenaming(false);
117
+ return;
118
+ }
119
+ if (await rename(trimmed))
120
+ setRenaming(false);
121
+ }
122
+ return (_jsxs("li", { className: "fa-media-category-manager-node", "aria-level": depth, children: [_jsx("div", { className: "fa-media-category-manager-row", children: renaming ? (_jsx(RowRenameForm, { node: node, busy: busy, onSave: (draft) => void saveRename(draft), onCancel: () => setRenaming(false) }, node.name)) : (_jsx(RowActions, { node: node, options: options, busy: busy, onStartRename: () => setRenaming(true), onAddChild: onAddChild, onReparent: (parentId) => {
123
+ if (parentId !== node.parent_id)
124
+ void reparent(parentId);
125
+ }, onDelete: () => void remove() })) }), rowError ? (_jsx("p", { role: "alert", className: "fa-media-category-manager-error", children: rowError })) : null, node.children.length > 0 ? (_jsx("ul", { className: "fa-media-category-manager-children", children: node.children.map((child) => (_jsx(CategoryManagerRow, { ...props, node: child, depth: depth + 1 }, child.id))) })) : null] }));
126
+ }
127
+ /**
128
+ * Create/rename/reparent/delete over the caller's user category tree
129
+ * (`useCategoryTree`). Reparenting and deleting are plain form actions, not
130
+ * drag-and-drop, per the 2026-08-18 decision — this keeps the manager
131
+ * Sonnet-scale and sidesteps the a11y/touch surface a draggable tree would
132
+ * need. This component owns CRUD only; browsing/filtering the tree is `U7`'s
133
+ * job on `MediaLibrary`.
134
+ */
135
+ export function CategoryManager() {
136
+ const { tree, count, loading, error, create, update, remove } = useCategoryTree();
137
+ const options = useMemo(() => parentOptions(tree), [tree]);
138
+ const createForm = useCreateForm(create);
139
+ const loadErrorMessage = describeLoadError(error);
140
+ // Rename and reparent are the same `PUT` with one half held constant, so each
141
+ // reads the node's current other half back out of the tree first.
142
+ async function rename(id, name) {
143
+ await update(id, { name, parent_id: findNode(tree, id)?.parent_id ?? null });
144
+ }
145
+ async function reparent(id, parentId) {
146
+ await update(id, { name: findNode(tree, id)?.name ?? "", parent_id: parentId });
147
+ }
148
+ return (_jsxs("section", { className: "not-content fa-media-panel", children: [_jsx("h2", { children: "Categories" }), loadErrorMessage ? _jsx("p", { role: "alert", children: loadErrorMessage }) : null, !loading && !loadErrorMessage && tree.length === 0 ? (_jsx("p", { className: "fa-media-category-hint", children: "No user categories yet. Create one below." })) : null, tree.length > 0 ? (_jsx("ul", { className: "fa-media-category-manager-tree", children: tree.map((node) => (_jsx(CategoryManagerRow, { node: node, depth: 1, options: options, onRename: rename, onReparent: reparent, onDelete: remove, onAddChild: createForm.addChildTo }, node.id))) })) : null, _jsx(CategoryCreateForm, { form: createForm, options: options, count: count })] }));
149
+ }
150
+ function describeLoadError(error) {
151
+ if (!error)
152
+ return null;
153
+ const detail = error instanceof ApiError ? messageFromDetail(error.detail) : null;
154
+ if (detail)
155
+ return detail;
156
+ return error instanceof Error ? error.message : "Failed to load categories";
157
+ }
158
+ /**
159
+ * The one create form the manager owns. "Add child" on a row pre-selects that
160
+ * row as this form's parent — one form, not a second inline one per node.
161
+ */
162
+ function useCreateForm(create) {
163
+ const [name, setName] = useState("");
164
+ const [parentId, setParentId] = useState("");
165
+ const [busy, setBusy] = useState(false);
166
+ const [error, setError] = useState(null);
167
+ async function submit() {
168
+ const trimmed = name.trim();
169
+ if (!trimmed)
170
+ return;
171
+ setBusy(true);
172
+ setError(null);
173
+ try {
174
+ await create({ name: trimmed, parent_id: parentId === "" ? null : Number(parentId) });
175
+ setName("");
176
+ setParentId("");
177
+ }
178
+ catch (failure) {
179
+ setError(describeCategoryError(failure));
180
+ }
181
+ finally {
182
+ setBusy(false);
183
+ }
184
+ }
185
+ function addChildTo(nextParentId) {
186
+ setParentId(String(nextParentId));
187
+ setError(null);
188
+ }
189
+ return { name, setName, parentId, setParentId, busy, error, submit, addChildTo };
190
+ }
191
+ function CategoryCreateForm({ form, options, count }) {
192
+ return (_jsxs("fieldset", { className: "fa-media-category-manager-create", children: [_jsx("legend", { children: `New category (${count} total)` }), _jsxs("div", { className: "fa-media-field-control", children: [_jsx("label", { className: labelClassName, htmlFor: "fa-media-category-manager-new-name", children: "Name" }), _jsx("input", { id: "fa-media-category-manager-new-name", className: inputClassName, value: form.name, disabled: form.busy, onChange: (event) => form.setName(event.currentTarget.value) })] }), _jsxs("div", { className: "fa-media-field-control", children: [_jsx("label", { className: labelClassName, htmlFor: "fa-media-category-manager-new-parent", children: "Parent" }), _jsx("select", { id: "fa-media-category-manager-new-parent", className: inputClassName, value: form.parentId, disabled: form.busy, onChange: (event) => form.setParentId(event.currentTarget.value), children: options.map((option) => (_jsx("option", { value: option.id ?? "", children: option.label }, option.id ?? "root"))) })] }), _jsx("button", { type: "button", disabled: form.busy || !form.name.trim(), onClick: () => void form.submit(), children: form.busy ? "Creating…" : "Add category" }), form.error ? _jsx("p", { role: "alert", children: form.error }) : null] }));
193
+ }
194
+ //# sourceMappingURL=CategoryManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CategoryManager.js","sourceRoot":"","sources":["../../../../src/runtime/react/CategoryManager.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ import type { CategoryNode } from "../schemas.js";
2
+ /**
3
+ * A resolved selection entry: the category id plus the slash-joined name path
4
+ * used as chip copy (`"Invoices / 2026"`), mirroring the server's
5
+ * `MediaObjectCategoryRef.path`.
6
+ */
7
+ export type CategoryPathOption = {
8
+ id: number;
9
+ name: string;
10
+ path: string;
11
+ };
12
+ /**
13
+ * Flatten a nested tree into an id -> `{name, path}` map. Kept as a `Map` (not
14
+ * a plain object) so a lookup never becomes computed member access on a
15
+ * server-supplied key — `eslint-plugin-security`'s object-injection rule is on
16
+ * here, and a `Map` is the right structure for a numeric key anyway.
17
+ */
18
+ export declare function collectCategoryPaths(nodes: readonly CategoryNode[], prefix?: string, into?: Map<number, CategoryPathOption>): Map<number, CategoryPathOption>;
19
+ /**
20
+ * Compare two `category_ids` selections as sets, so a caller can tell "the user
21
+ * changed the filing" from "the user toggled one off and back on". Exported
22
+ * because `ObjectDetail` needs exactly this to drive its dirty state, and
23
+ * because `PATCH` set semantics (`U4`) make order meaningless on the wire.
24
+ */
25
+ export declare function isSameCategorySelection(left: readonly number[], right: readonly number[]): boolean;
26
+ export interface CategoryMultiSelectLabels {
27
+ legend: string;
28
+ emptyHint: string;
29
+ expand: (name: string) => string;
30
+ collapse: (name: string) => string;
31
+ descendantSelected: string;
32
+ loadError: string;
33
+ loading: string;
34
+ selected: string;
35
+ remove: (path: string) => string;
36
+ clearAll: string;
37
+ noneSelected: string;
38
+ }
39
+ export type CategoryMultiSelectViewProps = {
40
+ /** The currently selected user-category ids — the `category_ids` payload. */
41
+ value: readonly number[];
42
+ onChange: (next: number[]) => void;
43
+ tree: readonly CategoryNode[];
44
+ loading?: boolean;
45
+ error?: unknown;
46
+ disabled?: boolean;
47
+ /** Fieldset legend; the picker is optional everywhere it is used today. */
48
+ legend?: string;
49
+ /** Copy shown when the caller has no user categories yet. */
50
+ emptyHint?: string;
51
+ /**
52
+ * Labels for ids that are selected but absent from the tree — e.g. an object
53
+ * filed into a category the tree request has not resolved yet. Keeps a chip
54
+ * from degrading to a bare id.
55
+ */
56
+ fallbackLabels?: ReadonlyMap<number, string>;
57
+ /** Prefix for the generated element ids; must be unique per mounted picker. */
58
+ idPrefix?: string;
59
+ labels?: Partial<CategoryMultiSelectLabels>;
60
+ };
61
+ /**
62
+ * Presentational multi-select nested category picker: a checkbox tree plus a
63
+ * chip list of the current selection. Fully controlled — it holds no selection
64
+ * state of its own, only expand/collapse.
65
+ */
66
+ export declare function CategoryMultiSelectView(props: CategoryMultiSelectViewProps): import("react").JSX.Element;
67
+ export type CategoryMultiSelectProps = Omit<CategoryMultiSelectViewProps, "tree" | "loading" | "error">;
68
+ /**
69
+ * The picker wired to `useCategoryTree()`. Use this on a page; use
70
+ * `CategoryMultiSelectView` when the tree is already in hand (or under test).
71
+ */
72
+ export declare function CategoryMultiSelect(props: CategoryMultiSelectProps): import("react").JSX.Element;
73
+ //# sourceMappingURL=CategoryMultiSelect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CategoryMultiSelect.d.ts","sourceRoot":"","sources":["../../../../src/runtime/react/CategoryMultiSelect.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAKlD;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,SAAS,YAAY,EAAE,EAC9B,MAAM,SAAK,EACX,IAAI,GAAE,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAa,GAChD,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAOjC;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAIlG;AA2BD,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACjC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACnC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB;AAsGD,MAAM,MAAM,4BAA4B,GAAG;IACzC,6EAA6E;IAC7E,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACnC,IAAI,EAAE,SAAS,YAAY,EAAE,CAAC;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,cAAc,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC,yBAAyB,CAAC,CAAC;CAC7C,CAAC;AA6BF;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,4BAA4B,+BAoD1E;AAyHD,MAAM,MAAM,wBAAwB,GAAG,IAAI,CACzC,4BAA4B,EAC5B,MAAM,GAAG,SAAS,GAAG,OAAO,CAC7B,CAAC;AAEF;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,wBAAwB,+BAGlE"}
@@ -0,0 +1,179 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useCallback, useMemo, useState } from "react";
3
+ import { useCategoryTree } from "../hooks/useMediaCategories.js";
4
+ import { ApiError, messageFromDetail } from "../errors.js";
5
+ const labelClassName = "fa-media-label flex items-center gap-2 text-sm leading-none font-medium select-none";
6
+ /**
7
+ * Flatten a nested tree into an id -> `{name, path}` map. Kept as a `Map` (not
8
+ * a plain object) so a lookup never becomes computed member access on a
9
+ * server-supplied key — `eslint-plugin-security`'s object-injection rule is on
10
+ * here, and a `Map` is the right structure for a numeric key anyway.
11
+ */
12
+ export function collectCategoryPaths(nodes, prefix = "", into = new Map()) {
13
+ for (const node of nodes) {
14
+ const path = prefix ? `${prefix} / ${node.name}` : node.name;
15
+ into.set(node.id, { id: node.id, name: node.name, path });
16
+ collectCategoryPaths(node.children, path, into);
17
+ }
18
+ return into;
19
+ }
20
+ /**
21
+ * Compare two `category_ids` selections as sets, so a caller can tell "the user
22
+ * changed the filing" from "the user toggled one off and back on". Exported
23
+ * because `ObjectDetail` needs exactly this to drive its dirty state, and
24
+ * because `PATCH` set semantics (`U4`) make order meaningless on the wire.
25
+ */
26
+ export function isSameCategorySelection(left, right) {
27
+ if (left.length !== right.length)
28
+ return false;
29
+ const seen = new Set(right);
30
+ return left.every((id) => seen.has(id));
31
+ }
32
+ function hasSelectedDescendant(nodes, selected) {
33
+ return nodes.some((node) => selected.has(node.id) || hasSelectedDescendant(node.children, selected));
34
+ }
35
+ const DEFAULT_LABELS = {
36
+ legend: "User categories (optional)",
37
+ emptyHint: "No user categories yet. Create one from the category manager to file media here.",
38
+ expand: (name) => `Expand ${name}`,
39
+ collapse: (name) => `Collapse ${name}`,
40
+ descendantSelected: "A category below this one is selected",
41
+ loadError: "Failed to load categories",
42
+ loading: "Loading categories…",
43
+ selected: "Selected categories",
44
+ remove: (path) => `Remove ${path}`,
45
+ clearAll: "Clear all",
46
+ noneSelected: "No user categories selected."
47
+ };
48
+ /**
49
+ * One tree row plus its subtree.
50
+ *
51
+ * The picker is a **form control**, not the navigation tree `U6`/`U7` own, so
52
+ * it is built from native checkboxes inside nested lists rather than
53
+ * `role="tree"` + roving tabindex: a native checkbox already carries focus,
54
+ * keyboard toggling and checked-state announcements, and layering a
55
+ * single-selection tree pattern over it would take those away. Nesting and
56
+ * depth stay conveyable through plain nested `<ul>`s plus `aria-level` — the
57
+ * list role is left implicit on purpose, since an explicit `role="group"` on
58
+ * the `<ul>` would cost its `<li>` children the `listitem` role that
59
+ * `aria-level` is defined against.
60
+ *
61
+ * Selection does **not** cascade to children. The server stores an explicit set
62
+ * of assignments (`U4` set semantics), so checking "Invoices" must not silently
63
+ * file the object into "Invoices / 2026"; a collapsed-or-not branch holding a
64
+ * selected descendant is marked with a dot instead.
65
+ */
66
+ function BranchToggle({ node, ctx, groupId }) {
67
+ if (node.children.length === 0) {
68
+ return _jsx("span", { className: "fa-media-category-toggle-spacer", "aria-hidden": "true" });
69
+ }
70
+ const isCollapsed = ctx.collapsed.has(node.id);
71
+ return (_jsx("button", { type: "button", className: "fa-media-category-toggle", "aria-expanded": !isCollapsed, "aria-controls": groupId, "aria-label": isCollapsed ? ctx.labels.expand(node.name) : ctx.labels.collapse(node.name), onClick: () => ctx.onToggleCollapsed(node.id), children: isCollapsed ? "▸" : "▾" }));
72
+ }
73
+ function BranchCheckbox({ node, ctx }) {
74
+ const inputId = `${ctx.idPrefix}-node-${node.id}`;
75
+ const descendantSelected = node.children.length > 0 &&
76
+ !ctx.selected.has(node.id) &&
77
+ hasSelectedDescendant(node.children, ctx.selected);
78
+ return (_jsxs("label", { className: labelClassName, htmlFor: inputId, children: [_jsx("input", { id: inputId, type: "checkbox", checked: ctx.selected.has(node.id), disabled: ctx.disabled, onChange: () => ctx.onToggleSelected(node.id) }), _jsx("span", { className: "fa-media-category-name", children: node.name }), descendantSelected ? (_jsx("span", { className: "fa-media-category-marker", title: ctx.labels.descendantSelected, children: "•" })) : null, _jsx("span", { className: "fa-media-badge fa-media-category-count", children: node.total_object_count })] }));
79
+ }
80
+ function CategoryBranch({ node, depth, ctx }) {
81
+ const hasChildren = node.children.length > 0;
82
+ const groupId = `${ctx.idPrefix}-group-${node.id}`;
83
+ return (_jsxs("li", { className: "fa-media-category-node", "aria-level": depth, children: [_jsxs("div", { className: "fa-media-category-row", children: [_jsx(BranchToggle, { node: node, ctx: ctx, groupId: groupId }), _jsx(BranchCheckbox, { node: node, ctx: ctx })] }), hasChildren && !ctx.collapsed.has(node.id) ? (_jsx("ul", { id: groupId, className: "fa-media-category-children", children: node.children.map((child) => (_jsx(CategoryBranch, { node: child, depth: depth + 1, ctx: ctx }, child.id))) })) : null] }));
84
+ }
85
+ /**
86
+ * The picker's only local state: which branches are collapsed. Selection stays
87
+ * the caller's — `toggleSelected` reports the next set rather than storing it —
88
+ * so the control remains fully controlled (`U4` set semantics).
89
+ */
90
+ function usePickerState(value, onChange) {
91
+ const [collapsed, setCollapsed] = useState(() => new Set());
92
+ const selected = useMemo(() => new Set(value), [value]);
93
+ const toggleSelected = useCallback((id) => {
94
+ onChange(value.includes(id) ? value.filter((current) => current !== id) : [...value, id]);
95
+ }, [onChange, value]);
96
+ const toggleCollapsed = useCallback((id) => {
97
+ setCollapsed((previous) => {
98
+ const next = new Set(previous);
99
+ if (!next.delete(id))
100
+ next.add(id);
101
+ return next;
102
+ });
103
+ }, []);
104
+ return { collapsed, selected, toggleSelected, toggleCollapsed };
105
+ }
106
+ /**
107
+ * Presentational multi-select nested category picker: a checkbox tree plus a
108
+ * chip list of the current selection. Fully controlled — it holds no selection
109
+ * state of its own, only expand/collapse.
110
+ */
111
+ export function CategoryMultiSelectView(props) {
112
+ const { value, onChange, tree, loading = false, error = null, disabled = false, legend, emptyHint, fallbackLabels, idPrefix = "fa-media-categories", labels: labelOverrides } = props;
113
+ const labels = { ...DEFAULT_LABELS, ...labelOverrides };
114
+ const { collapsed, selected, toggleSelected, toggleCollapsed } = usePickerState(value, onChange);
115
+ const paths = useMemo(() => collectCategoryPaths(tree), [tree]);
116
+ const chips = resolveChips(value, paths, fallbackLabels);
117
+ const errorMessage = resolveErrorMessage(error, labels.loadError);
118
+ const ctx = {
119
+ idPrefix,
120
+ selected,
121
+ collapsed,
122
+ disabled,
123
+ onToggleSelected: toggleSelected,
124
+ onToggleCollapsed: toggleCollapsed,
125
+ labels
126
+ };
127
+ return (_jsxs("fieldset", { className: "fa-media-category-picker", children: [_jsx("legend", { children: legend ?? labels.legend }), _jsx(CategoryPickerStatus, { errorMessage: errorMessage, loading: loading, isEmpty: tree.length === 0, emptyHint: emptyHint ?? labels.emptyHint, loadingLabel: labels.loading }), _jsx(CategoryTreeList, { tree: tree, ctx: ctx }), _jsx(CategorySelection, { chips: chips, labels: labels, disabled: disabled, onRemove: toggleSelected, onClear: () => onChange([]) })] }));
128
+ }
129
+ /** The root list. Renders nothing at all when the account has no categories. */
130
+ function CategoryTreeList({ tree, ctx }) {
131
+ if (tree.length === 0)
132
+ return null;
133
+ return (_jsx("ul", { className: "fa-media-category-tree", children: tree.map((node) => (_jsx(CategoryBranch, { node: node, depth: 1, ctx: ctx }, node.id))) }));
134
+ }
135
+ /**
136
+ * The picker's three mutually informative status lines: a load error, the
137
+ * first-load hint while the tree is still resolving, and the "nothing to pick
138
+ * from" hint. The empty hint stays suppressed while an error is showing, so a
139
+ * failed request never reads as an empty account.
140
+ */
141
+ function CategoryPickerStatus({ errorMessage, loading, isEmpty, emptyHint, loadingLabel }) {
142
+ return (_jsxs(_Fragment, { children: [errorMessage ? (_jsx("p", { role: "alert", className: "fa-media-category-error", children: errorMessage })) : null, loading && isEmpty ? _jsx("p", { className: "fa-media-category-hint", children: loadingLabel }) : null, !loading && !errorMessage && isEmpty ? (_jsx("p", { className: "fa-media-category-hint", children: emptyHint })) : null] }));
143
+ }
144
+ /**
145
+ * Resolve each selected id to its chip copy: the tree's slash-joined path when
146
+ * the tree holds it, the caller's fallback label when it does not, and `#id` as
147
+ * the last resort so a chip never renders empty.
148
+ */
149
+ function resolveChips(value, paths, fallbackLabels) {
150
+ return value.map((id) => {
151
+ const known = paths.get(id);
152
+ if (known)
153
+ return known;
154
+ const fallback = fallbackLabels?.get(id) ?? `#${id}`;
155
+ return { id, name: fallback, path: fallback };
156
+ });
157
+ }
158
+ function resolveErrorMessage(error, loadError) {
159
+ if (!error)
160
+ return null;
161
+ const detail = error instanceof ApiError ? messageFromDetail(error.detail) : null;
162
+ return detail ?? (error instanceof Error ? error.message : loadError);
163
+ }
164
+ /** The chip list of the current selection, plus its clear-all action. */
165
+ function CategorySelection({ chips, labels, disabled, onRemove, onClear }) {
166
+ if (chips.length === 0) {
167
+ return (_jsx("div", { className: "fa-media-category-selection", children: _jsx("p", { className: "fa-media-category-hint", children: labels.noneSelected }) }));
168
+ }
169
+ return (_jsxs("div", { className: "fa-media-category-selection", children: [_jsx("ul", { className: "fa-media-category-chips", "aria-label": labels.selected, children: chips.map((chip) => (_jsxs("li", { className: "fa-media-badge fa-media-category-chip", children: [_jsx("span", { children: chip.path }), _jsx("button", { type: "button", className: "fa-media-category-chip-remove", "aria-label": labels.remove(chip.path), disabled: disabled, onClick: () => onRemove(chip.id), children: "×" })] }, chip.id))) }), _jsx("button", { type: "button", className: "fa-media-category-clear", disabled: disabled, onClick: onClear, children: labels.clearAll })] }));
170
+ }
171
+ /**
172
+ * The picker wired to `useCategoryTree()`. Use this on a page; use
173
+ * `CategoryMultiSelectView` when the tree is already in hand (or under test).
174
+ */
175
+ export function CategoryMultiSelect(props) {
176
+ const { tree, loading, error } = useCategoryTree();
177
+ return _jsx(CategoryMultiSelectView, { ...props, tree: tree, loading: loading, error: error });
178
+ }
179
+ //# sourceMappingURL=CategoryMultiSelect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CategoryMultiSelect.js","sourceRoot":"","sources":["../../../../src/runtime/react/CategoryMultiSelect.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { CategoryBranchSelection } from "./categoryBranch.js";
2
+ import type { MediaLibraryLabels } from "./mediaLibraryLabels.js";
3
+ /**
4
+ * The left pane of the tree view: the caller's nested user categories with
5
+ * their counts.
6
+ *
7
+ * `useCategoryTree()` lives **here** rather than in `MediaLibrary` so the
8
+ * category request is only issued once the user actually opens the tree view —
9
+ * the list/grid/masonry views must not pay for a fetch they never render.
10
+ *
11
+ * The a11y contract matches `astro-ui-m8`'s `tree-view` registry block so the
12
+ * runtime pane and the registry skin do not diverge: `role="tree"` /
13
+ * `role="group"` / `role="treeitem"`, `aria-level`, `aria-selected`,
14
+ * `aria-expanded` on parents, a roving tabindex holding exactly one tab stop,
15
+ * and ArrowUp/ArrowDown/Home/End/ArrowRight/ArrowLeft/Enter/Space.
16
+ */
17
+ export declare function MediaCategoryTreePane({ selection, onSelect, labels }: {
18
+ selection: CategoryBranchSelection;
19
+ onSelect: (selection: CategoryBranchSelection) => void;
20
+ labels: MediaLibraryLabels["tree"];
21
+ }): import("react").JSX.Element;
22
+ //# sourceMappingURL=MediaCategoryTreePane.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MediaCategoryTreePane.d.ts","sourceRoot":"","sources":["../../../../src/runtime/react/MediaCategoryTreePane.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AACnE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAsOlE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,SAAS,EACT,QAAQ,EACR,MAAM,EACP,EAAE;IACD,SAAS,EAAE,uBAAuB,CAAC;IACnC,QAAQ,EAAE,CAAC,SAAS,EAAE,uBAAuB,KAAK,IAAI,CAAC;IACvD,MAAM,EAAE,kBAAkB,CAAC,MAAM,CAAC,CAAC;CACpC,+BA6JA"}