@byline/host-tanstack-start 3.12.1 → 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.
@@ -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 };