@byline/host-tanstack-start 3.12.2 → 3.13.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.
- package/dist/admin-shell/collections/edit.js +1 -0
- package/dist/admin-shell/collections/tree-list-projection.d.ts +56 -0
- package/dist/admin-shell/collections/tree-list-projection.js +109 -0
- package/dist/admin-shell/collections/tree-list.d.ts +29 -0
- package/dist/admin-shell/collections/tree-list.js +272 -0
- package/dist/admin-shell/collections/tree-list.module.js +17 -0
- package/dist/admin-shell/collections/tree-list_module.css +117 -0
- package/dist/integrations/byline-field-services.js +25 -1
- package/dist/routes/create-collection-list-route.js +27 -2
- package/dist/server-fns/collections/index.d.ts +1 -0
- package/dist/server-fns/collections/index.js +1 -0
- package/dist/server-fns/collections/tree.d.ts +84 -0
- package/dist/server-fns/collections/tree.js +144 -0
- package/package.json +8 -8
- package/src/admin-shell/collections/edit.tsx +1 -0
- package/src/admin-shell/collections/tree-list-projection.test.node.ts +116 -0
- package/src/admin-shell/collections/tree-list-projection.ts +198 -0
- package/src/admin-shell/collections/tree-list.module.css +140 -0
- package/src/admin-shell/collections/tree-list.tsx +354 -0
- package/src/integrations/byline-field-services.ts +29 -0
- package/src/routes/create-collection-list-route.tsx +39 -1
- package/src/server-fns/collections/index.ts +1 -0
- package/src/server-fns/collections/tree.ts +203 -0
|
@@ -496,6 +496,7 @@ const EditView = ({ collectionDefinition, adminConfig, initialData, locale, cont
|
|
|
496
496
|
useAsTitle: collectionDefinition.useAsTitle,
|
|
497
497
|
useAsPath: collectionDefinition.useAsPath,
|
|
498
498
|
advertiseLocales: collectionDefinition.advertiseLocales,
|
|
499
|
+
tree: true === collectionDefinition.tree,
|
|
499
500
|
headingLabel: labels.singular,
|
|
500
501
|
initialLocale: locale,
|
|
501
502
|
onLocaleChange: handleLocaleChange,
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Pure projection helpers for the drag-to-reorder / re-parent tree list
|
|
10
|
+
* (docs/DOCUMENT-TREE.md, phase 2). Adapted from the dnd-kit "sortable tree"
|
|
11
|
+
* pattern: a pre-order flattened list where the drag's **horizontal** offset
|
|
12
|
+
* projects a target depth, clamped to what the neighbouring rows allow, and the
|
|
13
|
+
* target parent + sibling neighbours are resolved from that depth.
|
|
14
|
+
*
|
|
15
|
+
* Kept free of React / dnd-kit so the projection is unit-testable in isolation;
|
|
16
|
+
* the component feeds it the live drag offset and applies the result through the
|
|
17
|
+
* `placeTreeNode` tree command.
|
|
18
|
+
*/
|
|
19
|
+
/** Minimal row shape the projection needs (a slice of `CollectionTreeRow`). */
|
|
20
|
+
export interface TreeProjectionRow {
|
|
21
|
+
id: string;
|
|
22
|
+
parentId: string | null;
|
|
23
|
+
depth: number;
|
|
24
|
+
}
|
|
25
|
+
export interface TreeProjection {
|
|
26
|
+
/** Projected depth of the dragged node at the drop position. */
|
|
27
|
+
depth: number;
|
|
28
|
+
/** Resolved parent at that depth (`null` = root). */
|
|
29
|
+
parentId: string | null;
|
|
30
|
+
/** Sibling immediately *above* the drop within the target parent (or null). */
|
|
31
|
+
beforeId: string | null;
|
|
32
|
+
/** Sibling immediately *below* the drop within the target parent (or null). */
|
|
33
|
+
afterId: string | null;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The ids of `activeId`'s descendants. In a pre-order list a node's descendants
|
|
37
|
+
* are the contiguous following rows with a strictly greater depth — they travel
|
|
38
|
+
* with the node (the adjacency edge to their parent is unchanged), so they are
|
|
39
|
+
* removed from the working list before projecting.
|
|
40
|
+
*/
|
|
41
|
+
export declare function descendantIds(rows: TreeProjectionRow[], activeId: string): Set<string>;
|
|
42
|
+
/**
|
|
43
|
+
* Project where the dragged node would land. `rows` is the full pre-order placed
|
|
44
|
+
* tree; `dragOffsetX` is the pointer's horizontal delta in px; `indentWidth` is
|
|
45
|
+
* the px-per-depth indentation step. Returns `null` for a no-op drag (dropping
|
|
46
|
+
* on itself or into its own subtree).
|
|
47
|
+
*/
|
|
48
|
+
export declare function getTreeProjection(rows: TreeProjectionRow[], activeId: string, overId: string, dragOffsetX: number, indentWidth: number): TreeProjection | null;
|
|
49
|
+
/**
|
|
50
|
+
* Apply a projection to the flat row list — produce the new pre-order ordering
|
|
51
|
+
* for an **optimistic** repaint (the server returns the canonical order on the
|
|
52
|
+
* next read). The dragged node's whole subtree (contiguous deeper rows) travels
|
|
53
|
+
* with it; depths are shifted by the projected delta and the node's `parentId`
|
|
54
|
+
* is updated. Generic so it preserves the caller's full row shape.
|
|
55
|
+
*/
|
|
56
|
+
export declare function applyProjection<T extends TreeProjectionRow>(rows: T[], activeId: string, p: TreeProjection): T[];
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
function clamp(value, min, max) {
|
|
2
|
+
return Math.min(Math.max(value, min), max);
|
|
3
|
+
}
|
|
4
|
+
function arrayMove(items, from, to) {
|
|
5
|
+
const next = items.slice();
|
|
6
|
+
const [moved] = next.splice(from, 1);
|
|
7
|
+
if (void 0 !== moved) next.splice(to, 0, moved);
|
|
8
|
+
return next;
|
|
9
|
+
}
|
|
10
|
+
function descendantIds(rows, activeId) {
|
|
11
|
+
const ids = new Set();
|
|
12
|
+
const start = rows.findIndex((r)=>r.id === activeId);
|
|
13
|
+
if (-1 === start) return ids;
|
|
14
|
+
const baseDepth = rows[start]?.depth;
|
|
15
|
+
for(let i = start + 1; i < rows.length && rows[i]?.depth > baseDepth; i++)ids.add(rows[i]?.id);
|
|
16
|
+
return ids;
|
|
17
|
+
}
|
|
18
|
+
function getTreeProjection(rows, activeId, overId, dragOffsetX, indentWidth) {
|
|
19
|
+
if (activeId === overId) return null;
|
|
20
|
+
const descendants = descendantIds(rows, activeId);
|
|
21
|
+
if (descendants.has(overId)) return null;
|
|
22
|
+
const working = rows.filter((r)=>r.id === activeId || !descendants.has(r.id));
|
|
23
|
+
const activeIndex = working.findIndex((r)=>r.id === activeId);
|
|
24
|
+
const overIndex = working.findIndex((r)=>r.id === overId);
|
|
25
|
+
if (-1 === activeIndex || -1 === overIndex) return null;
|
|
26
|
+
const moved = arrayMove(working, activeIndex, overIndex);
|
|
27
|
+
const previous = moved[overIndex - 1];
|
|
28
|
+
const next = moved[overIndex + 1];
|
|
29
|
+
const dragDepth = Math.round(dragOffsetX / indentWidth);
|
|
30
|
+
const projectedDepth = working[activeIndex]?.depth + dragDepth;
|
|
31
|
+
const maxDepth = previous ? previous.depth + 1 : 0;
|
|
32
|
+
const minDepth = next ? next.depth : 0;
|
|
33
|
+
const depth = clamp(projectedDepth, minDepth, maxDepth);
|
|
34
|
+
const parentId = resolveParentId(moved, overIndex, depth, previous);
|
|
35
|
+
let beforeId = null;
|
|
36
|
+
for(let i = overIndex - 1; i >= 0; i--){
|
|
37
|
+
const row = moved[i];
|
|
38
|
+
if (row.depth < depth) break;
|
|
39
|
+
if (row.depth === depth && row.parentId === parentId) {
|
|
40
|
+
beforeId = row.id;
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
let afterId = null;
|
|
45
|
+
for(let i = overIndex + 1; i < moved.length; i++){
|
|
46
|
+
const row = moved[i];
|
|
47
|
+
if (row.depth < depth) break;
|
|
48
|
+
if (row.depth === depth && row.parentId === parentId) {
|
|
49
|
+
afterId = row.id;
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
depth,
|
|
55
|
+
parentId,
|
|
56
|
+
beforeId,
|
|
57
|
+
afterId
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function applyProjection(rows, activeId, p) {
|
|
61
|
+
const start = rows.findIndex((r)=>r.id === activeId);
|
|
62
|
+
if (-1 === start) return rows;
|
|
63
|
+
const baseDepth = rows[start]?.depth;
|
|
64
|
+
let end = start + 1;
|
|
65
|
+
while(end < rows.length && rows[end]?.depth > baseDepth)end++;
|
|
66
|
+
const delta = p.depth - baseDepth;
|
|
67
|
+
const block = rows.slice(start, end).map((row, i)=>0 === i ? {
|
|
68
|
+
...row,
|
|
69
|
+
depth: row.depth + delta,
|
|
70
|
+
parentId: p.parentId
|
|
71
|
+
} : {
|
|
72
|
+
...row,
|
|
73
|
+
depth: row.depth + delta
|
|
74
|
+
});
|
|
75
|
+
const without = [
|
|
76
|
+
...rows.slice(0, start),
|
|
77
|
+
...rows.slice(end)
|
|
78
|
+
];
|
|
79
|
+
let insertAt;
|
|
80
|
+
if (null != p.beforeId) {
|
|
81
|
+
const bi = without.findIndex((r)=>r.id === p.beforeId);
|
|
82
|
+
if (-1 === bi) insertAt = without.length;
|
|
83
|
+
else {
|
|
84
|
+
let j = bi + 1;
|
|
85
|
+
const bd = without[bi]?.depth;
|
|
86
|
+
while(j < without.length && without[j]?.depth > bd)j++;
|
|
87
|
+
insertAt = j;
|
|
88
|
+
}
|
|
89
|
+
} else if (null != p.afterId) {
|
|
90
|
+
const ai = without.findIndex((r)=>r.id === p.afterId);
|
|
91
|
+
insertAt = -1 === ai ? without.length : ai;
|
|
92
|
+
} else if (null != p.parentId) {
|
|
93
|
+
const pi = without.findIndex((r)=>r.id === p.parentId);
|
|
94
|
+
insertAt = -1 === pi ? without.length : pi + 1;
|
|
95
|
+
} else insertAt = without.length;
|
|
96
|
+
return [
|
|
97
|
+
...without.slice(0, insertAt),
|
|
98
|
+
...block,
|
|
99
|
+
...without.slice(insertAt)
|
|
100
|
+
];
|
|
101
|
+
}
|
|
102
|
+
function resolveParentId(moved, overIndex, depth, previous) {
|
|
103
|
+
if (0 === depth || null == previous) return null;
|
|
104
|
+
if (depth === previous.depth) return previous.parentId;
|
|
105
|
+
if (depth > previous.depth) return previous.id;
|
|
106
|
+
for(let i = overIndex - 1; i >= 0; i--)if (moved[i]?.depth === depth) return moved[i]?.parentId;
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
export { applyProjection, descendantIds, getTreeProjection };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
import type React from 'react';
|
|
9
|
+
import type { ColumnDefinition, WorkflowStatus } from '@byline/core';
|
|
10
|
+
import type { CollectionTreeRow } from '../../server-fns/collections/tree.js';
|
|
11
|
+
export type TreeMoveFn = (params: {
|
|
12
|
+
documentId: string;
|
|
13
|
+
parentDocumentId: string | null;
|
|
14
|
+
beforeDocumentId: string | null;
|
|
15
|
+
afterDocumentId: string | null;
|
|
16
|
+
}) => Promise<unknown>;
|
|
17
|
+
export declare const TreeListView: ({ rows, columns, workflowStatuses, useAsTitle, collection, collectionLabels, onMove, }: {
|
|
18
|
+
rows: CollectionTreeRow[];
|
|
19
|
+
columns: ColumnDefinition[];
|
|
20
|
+
workflowStatuses?: WorkflowStatus[];
|
|
21
|
+
useAsTitle?: string;
|
|
22
|
+
collection: string;
|
|
23
|
+
collectionLabels: {
|
|
24
|
+
singular: string;
|
|
25
|
+
plural: string;
|
|
26
|
+
};
|
|
27
|
+
/** When provided, the placed tree becomes drag-to-reorder / re-parent. */
|
|
28
|
+
onMove?: TreeMoveFn;
|
|
29
|
+
}) => React.JSX.Element;
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useState } from "react";
|
|
3
|
+
import { useRouter } from "@tanstack/react-router";
|
|
4
|
+
import { StatusBadge, renderFormatted } from "@byline/admin/react";
|
|
5
|
+
import { useTranslation } from "@byline/i18n/react";
|
|
6
|
+
import { Container, GripperVerticalIcon, IconButton, PlusIcon, Section, Table, useToastManager } from "@byline/ui/react";
|
|
7
|
+
import { DndContext, KeyboardSensor, PointerSensor, useSensor, useSensors } from "@dnd-kit/core";
|
|
8
|
+
import { SortableContext, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
|
|
9
|
+
import classnames from "classnames";
|
|
10
|
+
import { Link } from "../chrome/loose-router.js";
|
|
11
|
+
import { TableHeadingCellSortable } from "../chrome/th-sortable.js";
|
|
12
|
+
import { formatNumber } from "../chrome/utils.js";
|
|
13
|
+
import tree_list_module from "./tree-list.module.js";
|
|
14
|
+
import { applyProjection, getTreeProjection } from "./tree-list-projection.js";
|
|
15
|
+
const INDENT = 24;
|
|
16
|
+
function getColumnValue(row, fieldName) {
|
|
17
|
+
if (row.fields && fieldName in row.fields) return row.fields[fieldName];
|
|
18
|
+
return row[fieldName];
|
|
19
|
+
}
|
|
20
|
+
function SortableTreeRow({ id, depth, dragging, dragHandleLabel, children }) {
|
|
21
|
+
const { setNodeRef, attributes, listeners, transform, transition, isDragging } = useSortable({
|
|
22
|
+
id
|
|
23
|
+
});
|
|
24
|
+
const style = {
|
|
25
|
+
transform: transform ? `translate3d(0, ${transform.y}px, 0)` : void 0,
|
|
26
|
+
transition,
|
|
27
|
+
position: 'relative',
|
|
28
|
+
zIndex: isDragging ? 10 : 'auto',
|
|
29
|
+
opacity: isDragging ? 0.5 : 1
|
|
30
|
+
};
|
|
31
|
+
return /*#__PURE__*/ jsxs("tr", {
|
|
32
|
+
ref: setNodeRef,
|
|
33
|
+
className: classnames('byline-table-row', {
|
|
34
|
+
[tree_list_module.draggingRow]: dragging
|
|
35
|
+
}),
|
|
36
|
+
style: style,
|
|
37
|
+
children: [
|
|
38
|
+
/*#__PURE__*/ jsx("td", {
|
|
39
|
+
className: classnames('byline-tree-list-drag-cell', tree_list_module.dragCell),
|
|
40
|
+
children: /*#__PURE__*/ jsx("button", {
|
|
41
|
+
type: "button",
|
|
42
|
+
className: classnames('byline-tree-list-drag-handle', tree_list_module.dragHandle),
|
|
43
|
+
"aria-label": dragHandleLabel,
|
|
44
|
+
...attributes,
|
|
45
|
+
...listeners,
|
|
46
|
+
children: /*#__PURE__*/ jsx(GripperVerticalIcon, {})
|
|
47
|
+
})
|
|
48
|
+
}),
|
|
49
|
+
children
|
|
50
|
+
]
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
const TreeListView = ({ rows, columns, workflowStatuses, useAsTitle, collection, collectionLabels, onMove })=>{
|
|
54
|
+
const { t } = useTranslation('byline-admin');
|
|
55
|
+
const router = useRouter();
|
|
56
|
+
const toastManager = useToastManager();
|
|
57
|
+
const unplaced = useMemo(()=>rows.filter((r)=>r.unplaced), [
|
|
58
|
+
rows
|
|
59
|
+
]);
|
|
60
|
+
const [localPlaced, setLocalPlaced] = useState(()=>rows.filter((r)=>!r.unplaced));
|
|
61
|
+
const [isMoving, setIsMoving] = useState(false);
|
|
62
|
+
useEffect(()=>{
|
|
63
|
+
if (!isMoving) setLocalPlaced(rows.filter((r)=>!r.unplaced));
|
|
64
|
+
}, [
|
|
65
|
+
rows,
|
|
66
|
+
isMoving
|
|
67
|
+
]);
|
|
68
|
+
const [activeId, setActiveId] = useState(null);
|
|
69
|
+
const [overId, setOverId] = useState(null);
|
|
70
|
+
const [offsetLeft, setOffsetLeft] = useState(0);
|
|
71
|
+
const sensors = useSensors(useSensor(PointerSensor, {
|
|
72
|
+
activationConstraint: {
|
|
73
|
+
distance: 5
|
|
74
|
+
}
|
|
75
|
+
}), useSensor(KeyboardSensor, {
|
|
76
|
+
coordinateGetter: sortableKeyboardCoordinates
|
|
77
|
+
}));
|
|
78
|
+
const projection = useMemo(()=>{
|
|
79
|
+
if (null == activeId || null == overId) return null;
|
|
80
|
+
return getTreeProjection(localPlaced, activeId, overId, offsetLeft, INDENT);
|
|
81
|
+
}, [
|
|
82
|
+
activeId,
|
|
83
|
+
overId,
|
|
84
|
+
offsetLeft,
|
|
85
|
+
localPlaced
|
|
86
|
+
]);
|
|
87
|
+
const resetDnd = ()=>{
|
|
88
|
+
setActiveId(null);
|
|
89
|
+
setOverId(null);
|
|
90
|
+
setOffsetLeft(0);
|
|
91
|
+
};
|
|
92
|
+
const handleDragStart = ({ active })=>setActiveId(String(active.id));
|
|
93
|
+
const handleDragMove = ({ delta })=>setOffsetLeft(delta.x);
|
|
94
|
+
const handleDragOver = ({ over })=>setOverId(over ? String(over.id) : null);
|
|
95
|
+
const handleDragEnd = async ({ active, over })=>{
|
|
96
|
+
const proj = null != over ? getTreeProjection(localPlaced, String(active.id), String(over.id), offsetLeft, INDENT) : null;
|
|
97
|
+
resetDnd();
|
|
98
|
+
if (!onMove || null == proj) return;
|
|
99
|
+
const documentId = String(active.id);
|
|
100
|
+
const next = applyProjection(localPlaced, documentId, proj);
|
|
101
|
+
const current = localPlaced;
|
|
102
|
+
const unchanged = next.map((r)=>r.id).join() === current.map((r)=>r.id).join() && next.find((r)=>r.id === documentId)?.parentId === current.find((r)=>r.id === documentId)?.parentId;
|
|
103
|
+
if (unchanged) return;
|
|
104
|
+
setLocalPlaced(next);
|
|
105
|
+
setIsMoving(true);
|
|
106
|
+
try {
|
|
107
|
+
await onMove({
|
|
108
|
+
documentId,
|
|
109
|
+
parentDocumentId: proj.parentId,
|
|
110
|
+
beforeDocumentId: proj.beforeId,
|
|
111
|
+
afterDocumentId: proj.afterId
|
|
112
|
+
});
|
|
113
|
+
await router.invalidate();
|
|
114
|
+
} catch {
|
|
115
|
+
setLocalPlaced(current);
|
|
116
|
+
toastManager.add({
|
|
117
|
+
title: t('collections.list.reorderFailedToast'),
|
|
118
|
+
description: t("collections.list.reorderFailedDescription"),
|
|
119
|
+
data: {
|
|
120
|
+
intent: 'danger',
|
|
121
|
+
iconType: 'danger',
|
|
122
|
+
icon: true,
|
|
123
|
+
close: true
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
} finally{
|
|
127
|
+
setIsMoving(false);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
const renderCells = (row, depth)=>columns.map((column)=>{
|
|
131
|
+
const fieldName = column.fieldName;
|
|
132
|
+
const isTitle = null != useAsTitle && fieldName === useAsTitle;
|
|
133
|
+
return /*#__PURE__*/ jsx(Table.Cell, {
|
|
134
|
+
className: classnames({
|
|
135
|
+
[tree_list_module.cellRight]: 'right' === column.align,
|
|
136
|
+
[tree_list_module.cellCenter]: 'center' === column.align
|
|
137
|
+
}),
|
|
138
|
+
children: isTitle ? /*#__PURE__*/ jsxs("span", {
|
|
139
|
+
className: classnames('byline-tree-list-title', tree_list_module.titleCell),
|
|
140
|
+
style: {
|
|
141
|
+
paddingInlineStart: `${depth * INDENT}px`
|
|
142
|
+
},
|
|
143
|
+
children: [
|
|
144
|
+
depth > 0 && /*#__PURE__*/ jsx("span", {
|
|
145
|
+
"aria-hidden": "true",
|
|
146
|
+
className: classnames('byline-tree-list-branch', tree_list_module.branch),
|
|
147
|
+
children: "└─"
|
|
148
|
+
}),
|
|
149
|
+
/*#__PURE__*/ jsx(Link, {
|
|
150
|
+
to: '/admin/collections/$collection/$id',
|
|
151
|
+
params: {
|
|
152
|
+
collection,
|
|
153
|
+
id: row.id
|
|
154
|
+
},
|
|
155
|
+
children: column.formatter ? renderFormatted(getColumnValue(row, fieldName), row, column.formatter) : getColumnValue(row, fieldName) ?? row.path ?? '------'
|
|
156
|
+
})
|
|
157
|
+
]
|
|
158
|
+
}) : column.formatter ? renderFormatted(getColumnValue(row, fieldName), row, column.formatter) : 'status' === fieldName && workflowStatuses ? /*#__PURE__*/ jsx(StatusBadge, {
|
|
159
|
+
status: row.status,
|
|
160
|
+
workflowStatuses: workflowStatuses
|
|
161
|
+
}) : String(getColumnValue(row, fieldName) ?? '')
|
|
162
|
+
}, fieldName);
|
|
163
|
+
});
|
|
164
|
+
const dndEnabled = null != onMove;
|
|
165
|
+
const colSpanLead = dndEnabled ? 1 : 0;
|
|
166
|
+
return /*#__PURE__*/ jsx(Section, {
|
|
167
|
+
children: /*#__PURE__*/ jsxs(Container, {
|
|
168
|
+
children: [
|
|
169
|
+
/*#__PURE__*/ jsxs("div", {
|
|
170
|
+
className: classnames('byline-coll-list-head', tree_list_module.head),
|
|
171
|
+
children: [
|
|
172
|
+
/*#__PURE__*/ jsx("h1", {
|
|
173
|
+
className: classnames('byline-coll-list-title', tree_list_module.title),
|
|
174
|
+
children: collectionLabels.plural
|
|
175
|
+
}),
|
|
176
|
+
/*#__PURE__*/ jsx("span", {
|
|
177
|
+
className: classnames('byline-coll-list-stats', tree_list_module.stats),
|
|
178
|
+
children: formatNumber(rows.length, 0)
|
|
179
|
+
}),
|
|
180
|
+
/*#__PURE__*/ jsx(IconButton, {
|
|
181
|
+
"aria-label": t('collections.list.createAriaLabel'),
|
|
182
|
+
render: /*#__PURE__*/ jsx(Link, {
|
|
183
|
+
to: '/admin/collections/$collection/create',
|
|
184
|
+
params: {
|
|
185
|
+
collection
|
|
186
|
+
}
|
|
187
|
+
}),
|
|
188
|
+
children: /*#__PURE__*/ jsx(PlusIcon, {
|
|
189
|
+
height: "18px",
|
|
190
|
+
width: "18px",
|
|
191
|
+
svgClassName: "stroke-white"
|
|
192
|
+
})
|
|
193
|
+
})
|
|
194
|
+
]
|
|
195
|
+
}),
|
|
196
|
+
/*#__PURE__*/ jsx(Table.Container, {
|
|
197
|
+
className: classnames('byline-tree-list-table-wrap', tree_list_module.tableWrap),
|
|
198
|
+
children: /*#__PURE__*/ jsx(DndContext, {
|
|
199
|
+
sensors: sensors,
|
|
200
|
+
onDragStart: handleDragStart,
|
|
201
|
+
onDragMove: handleDragMove,
|
|
202
|
+
onDragOver: handleDragOver,
|
|
203
|
+
onDragEnd: handleDragEnd,
|
|
204
|
+
onDragCancel: resetDnd,
|
|
205
|
+
children: /*#__PURE__*/ jsxs(Table, {
|
|
206
|
+
children: [
|
|
207
|
+
/*#__PURE__*/ jsx(Table.Header, {
|
|
208
|
+
children: /*#__PURE__*/ jsxs(Table.Row, {
|
|
209
|
+
children: [
|
|
210
|
+
dndEnabled && /*#__PURE__*/ jsx("th", {
|
|
211
|
+
scope: "col",
|
|
212
|
+
className: classnames('byline-tree-list-drag-cell', tree_list_module.dragCell)
|
|
213
|
+
}),
|
|
214
|
+
columns.map((column)=>/*#__PURE__*/ jsx(TableHeadingCellSortable, {
|
|
215
|
+
fieldName: String(column.fieldName),
|
|
216
|
+
label: column.label,
|
|
217
|
+
sortable: false,
|
|
218
|
+
scope: "col",
|
|
219
|
+
align: column.align,
|
|
220
|
+
className: column.className
|
|
221
|
+
}, String(column.fieldName)))
|
|
222
|
+
]
|
|
223
|
+
})
|
|
224
|
+
}),
|
|
225
|
+
/*#__PURE__*/ jsxs(Table.Body, {
|
|
226
|
+
children: [
|
|
227
|
+
/*#__PURE__*/ jsx(SortableContext, {
|
|
228
|
+
items: localPlaced.map((r)=>r.id),
|
|
229
|
+
strategy: verticalListSortingStrategy,
|
|
230
|
+
children: localPlaced.map((row)=>{
|
|
231
|
+
const depth = row.id === activeId && projection ? projection.depth : row.depth;
|
|
232
|
+
return dndEnabled ? /*#__PURE__*/ jsx(SortableTreeRow, {
|
|
233
|
+
id: row.id,
|
|
234
|
+
depth: depth,
|
|
235
|
+
dragging: row.id === activeId,
|
|
236
|
+
dragHandleLabel: t('collections.list.dragHandleAriaLabel'),
|
|
237
|
+
children: renderCells(row, depth)
|
|
238
|
+
}, row.id) : /*#__PURE__*/ jsx(Table.Row, {
|
|
239
|
+
children: renderCells(row, row.depth)
|
|
240
|
+
}, row.id);
|
|
241
|
+
})
|
|
242
|
+
}),
|
|
243
|
+
unplaced.length > 0 && /*#__PURE__*/ jsxs(Table.Row, {
|
|
244
|
+
className: classnames('byline-tree-list-group', tree_list_module.groupRow),
|
|
245
|
+
children: [
|
|
246
|
+
/*#__PURE__*/ jsx(Table.Cell, {
|
|
247
|
+
className: tree_list_module.groupCell,
|
|
248
|
+
colSpan: colSpanLead + 1,
|
|
249
|
+
children: t('treeListView.unplacedHeading')
|
|
250
|
+
}),
|
|
251
|
+
columns.slice(1).map((column)=>/*#__PURE__*/ jsx(Table.Cell, {}, String(column.fieldName)))
|
|
252
|
+
]
|
|
253
|
+
}),
|
|
254
|
+
unplaced.map((row)=>/*#__PURE__*/ jsxs(Table.Row, {
|
|
255
|
+
children: [
|
|
256
|
+
dndEnabled && /*#__PURE__*/ jsx(Table.Cell, {
|
|
257
|
+
className: tree_list_module.dragCell
|
|
258
|
+
}),
|
|
259
|
+
renderCells(row, 0)
|
|
260
|
+
]
|
|
261
|
+
}, row.id))
|
|
262
|
+
]
|
|
263
|
+
})
|
|
264
|
+
]
|
|
265
|
+
})
|
|
266
|
+
})
|
|
267
|
+
})
|
|
268
|
+
]
|
|
269
|
+
})
|
|
270
|
+
});
|
|
271
|
+
};
|
|
272
|
+
export { TreeListView };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import "./tree-list_module.css";
|
|
2
|
+
const tree_list_module = {
|
|
3
|
+
head: "head-umTU3f",
|
|
4
|
+
title: "title-x6vHLV",
|
|
5
|
+
stats: "stats-qfK0D_",
|
|
6
|
+
tableWrap: "tableWrap-Q562es",
|
|
7
|
+
titleCell: "titleCell-rnsKeu",
|
|
8
|
+
branch: "branch-xjhO5g",
|
|
9
|
+
groupRow: "groupRow-DhAUjz",
|
|
10
|
+
groupCell: "groupCell-zyU5dg",
|
|
11
|
+
cellRight: "cellRight-CuqkDJ",
|
|
12
|
+
cellCenter: "cellCenter-L4rz2h",
|
|
13
|
+
dragCell: "dragCell-FProaR",
|
|
14
|
+
dragHandle: "dragHandle-GFe6tC",
|
|
15
|
+
draggingRow: "draggingRow-pnrWrt"
|
|
16
|
+
};
|
|
17
|
+
export default tree_list_module;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
:is(.head-umTU3f, .byline-coll-list-head) {
|
|
2
|
+
align-items: center;
|
|
3
|
+
gap: .75rem;
|
|
4
|
+
display: flex;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
:is(.title-x6vHLV, .byline-coll-list-title) {
|
|
8
|
+
margin: 0;
|
|
9
|
+
padding-bottom: 2px;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
:is(.stats-qfK0D_, .byline-coll-list-stats) {
|
|
13
|
+
white-space: nowrap;
|
|
14
|
+
background-color: var(--gray-25);
|
|
15
|
+
border: var(--border-width-thin) var(--border-style-solid) var(--gray-200);
|
|
16
|
+
border-radius: .375rem;
|
|
17
|
+
justify-content: center;
|
|
18
|
+
align-items: center;
|
|
19
|
+
min-width: 28px;
|
|
20
|
+
height: 28px;
|
|
21
|
+
margin-bottom: -4px;
|
|
22
|
+
margin-right: auto;
|
|
23
|
+
padding: 5px 6px;
|
|
24
|
+
font-size: .875rem;
|
|
25
|
+
line-height: 0;
|
|
26
|
+
display: flex;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
:is(:is([data-theme="dark"], .dark) .stats-qfK0D_, :is([data-theme="dark"], .dark) .byline-coll-list-stats) {
|
|
30
|
+
background-color: var(--canvas-700);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
:is(.tableWrap-Q562es, .byline-tree-list-table-wrap) {
|
|
34
|
+
margin-top: .75rem;
|
|
35
|
+
margin-bottom: .75rem;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
:is(.titleCell-rnsKeu, .byline-tree-list-title) {
|
|
39
|
+
align-items: center;
|
|
40
|
+
gap: .4rem;
|
|
41
|
+
display: inline-flex;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
:is(.branch-xjhO5g, .byline-tree-list-branch) {
|
|
45
|
+
color: var(--gray-400);
|
|
46
|
+
font-family: var(--font-mono, monospace);
|
|
47
|
+
-webkit-user-select: none;
|
|
48
|
+
user-select: none;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
:is(.groupRow-DhAUjz, .byline-tree-list-group) {
|
|
52
|
+
background-color: var(--gray-25);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
:is(:is([data-theme="dark"], .dark) .groupRow-DhAUjz, :is([data-theme="dark"], .dark) .byline-tree-list-group) {
|
|
56
|
+
background-color: var(--canvas-700);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.groupCell-zyU5dg {
|
|
60
|
+
text-transform: uppercase;
|
|
61
|
+
letter-spacing: .04em;
|
|
62
|
+
color: var(--gray-500);
|
|
63
|
+
font-size: .75rem;
|
|
64
|
+
font-weight: 600;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.cellRight-CuqkDJ {
|
|
68
|
+
text-align: right;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.cellCenter-L4rz2h {
|
|
72
|
+
text-align: center;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.dragCell-FProaR {
|
|
76
|
+
text-align: center;
|
|
77
|
+
vertical-align: middle;
|
|
78
|
+
width: 34px;
|
|
79
|
+
padding-inline: .25rem;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.dragHandle-GFe6tC {
|
|
83
|
+
color: var(--gray-400);
|
|
84
|
+
cursor: grab;
|
|
85
|
+
touch-action: none;
|
|
86
|
+
background: none;
|
|
87
|
+
border: none;
|
|
88
|
+
border-radius: 4px;
|
|
89
|
+
justify-content: center;
|
|
90
|
+
align-items: center;
|
|
91
|
+
padding: 4px;
|
|
92
|
+
display: inline-flex;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.dragHandle-GFe6tC:hover {
|
|
96
|
+
color: var(--gray-600);
|
|
97
|
+
background-color: var(--gray-50);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.dragHandle-GFe6tC:active {
|
|
101
|
+
cursor: grabbing;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
:is([data-theme="dark"], .dark) .dragHandle-GFe6tC:hover {
|
|
105
|
+
color: var(--gray-200);
|
|
106
|
+
background-color: var(--canvas-700);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.draggingRow-pnrWrt {
|
|
110
|
+
background-color: var(--gray-25);
|
|
111
|
+
box-shadow: inset 2px 0 0 var(--theme-400, var(--gray-400));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
:is([data-theme="dark"], .dark) .draggingRow-pnrWrt {
|
|
115
|
+
background-color: var(--canvas-700);
|
|
116
|
+
}
|
|
117
|
+
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getCollectionDocuments } from "../server-fns/collections/list.js";
|
|
2
|
+
import { getTreeAncestors, getTreeParent, placeTreeNode, removeFromTree } from "../server-fns/collections/tree.js";
|
|
2
3
|
import { uploadField } from "../server-fns/collections/upload.js";
|
|
3
4
|
const byline_field_services_getCollectionDocuments = ({ collection, params })=>getCollectionDocuments({
|
|
4
5
|
data: {
|
|
@@ -7,8 +8,31 @@ const byline_field_services_getCollectionDocuments = ({ collection, params })=>g
|
|
|
7
8
|
}
|
|
8
9
|
});
|
|
9
10
|
const byline_field_services_uploadField = (collection, formData, createDocument)=>uploadField(collection, formData, createDocument);
|
|
11
|
+
const byline_field_services_placeTreeNode = async (input)=>{
|
|
12
|
+
const { orderKey } = await placeTreeNode({
|
|
13
|
+
data: input
|
|
14
|
+
});
|
|
15
|
+
return {
|
|
16
|
+
orderKey
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
const byline_field_services_removeFromTree = async (input)=>{
|
|
20
|
+
await removeFromTree({
|
|
21
|
+
data: input
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
const byline_field_services_getTreeAncestors = (input)=>getTreeAncestors({
|
|
25
|
+
data: input
|
|
26
|
+
});
|
|
27
|
+
const byline_field_services_getTreeParent = (input)=>getTreeParent({
|
|
28
|
+
data: input
|
|
29
|
+
});
|
|
10
30
|
const bylineFieldServices = {
|
|
11
31
|
getCollectionDocuments: byline_field_services_getCollectionDocuments,
|
|
12
|
-
uploadField: byline_field_services_uploadField
|
|
32
|
+
uploadField: byline_field_services_uploadField,
|
|
33
|
+
placeTreeNode: byline_field_services_placeTreeNode,
|
|
34
|
+
removeFromTree: byline_field_services_removeFromTree,
|
|
35
|
+
getTreeAncestors: byline_field_services_getTreeAncestors,
|
|
36
|
+
getTreeParent: byline_field_services_getTreeParent
|
|
13
37
|
};
|
|
14
38
|
export { bylineFieldServices };
|