@byline/host-tanstack-start 3.20.2 → 3.20.3
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/chrome/th-sortable.d.ts +11 -1
- package/dist/admin-shell/chrome/th-sortable.js +6 -4
- package/dist/admin-shell/collections/list.js +8 -2
- package/dist/server-fns/collections/list.js +9 -2
- package/package.json +8 -8
- package/src/admin-shell/chrome/th-sortable.tsx +21 -3
- package/src/admin-shell/collections/list.tsx +12 -0
- package/src/server-fns/collections/list.ts +26 -6
|
@@ -15,8 +15,18 @@ export interface TableHeadingCellSortableProps extends TableHeadingCellProps {
|
|
|
15
15
|
desc?: boolean;
|
|
16
16
|
align?: 'left' | 'center' | 'right';
|
|
17
17
|
className?: string;
|
|
18
|
+
/**
|
|
19
|
+
* The *effective* sort column/direction — URL params when present, else
|
|
20
|
+
* the collection's configured `defaultSort` (surfaced via the list
|
|
21
|
+
* response's `meta.order`/`meta.desc`). When provided, these drive the
|
|
22
|
+
* sort indicator instead of reading the URL directly, so a params-less
|
|
23
|
+
* landing still shows which column ordered the rows. Omit for the legacy
|
|
24
|
+
* URL-only behaviour.
|
|
25
|
+
*/
|
|
26
|
+
activeOrder?: string;
|
|
27
|
+
activeDesc?: boolean;
|
|
18
28
|
}
|
|
19
|
-
export declare function TableHeadingCellSortable({ path: _path, fieldName, label, sortable, align, className, ...rest }: TableHeadingCellSortableProps & {
|
|
29
|
+
export declare function TableHeadingCellSortable({ path: _path, fieldName, label, sortable, align, className, activeOrder, activeDesc, ...rest }: TableHeadingCellSortableProps & {
|
|
20
30
|
ref?: React.RefObject<HTMLTableCellElement>;
|
|
21
31
|
}): React.JSX.Element;
|
|
22
32
|
export {};
|
|
@@ -6,7 +6,7 @@ import classnames from "classnames";
|
|
|
6
6
|
import { useNavigate } from "./loose-router.js";
|
|
7
7
|
import { SortAscendingIcon, SortDescendingIcon, SortNeutralIcon } from "./sort-icons.js";
|
|
8
8
|
import th_sortable_module from "./th-sortable.module.js";
|
|
9
|
-
function TableHeadingCellSortable({ path: _path = '/', fieldName, label, sortable = false, align = 'left', className, ...rest }) {
|
|
9
|
+
function TableHeadingCellSortable({ path: _path = '/', fieldName, label, sortable = false, align = 'left', className, activeOrder, activeDesc, ...rest }) {
|
|
10
10
|
const navigate = useNavigate();
|
|
11
11
|
const location = useRouterState({
|
|
12
12
|
select: (s)=>s.location
|
|
@@ -27,13 +27,15 @@ function TableHeadingCellSortable({ path: _path = '/', fieldName, label, sortabl
|
|
|
27
27
|
};
|
|
28
28
|
useEffect(()=>{
|
|
29
29
|
if (null != fieldName) {
|
|
30
|
-
const order = location.search.order;
|
|
31
|
-
const d = location.search.desc;
|
|
30
|
+
const order = void 0 !== activeOrder ? activeOrder : location.search.order;
|
|
31
|
+
const d = void 0 !== activeOrder ? activeDesc : location.search.desc;
|
|
32
32
|
order === fieldName ? setDesc(d ?? false) : setDesc(null);
|
|
33
33
|
}
|
|
34
34
|
}, [
|
|
35
35
|
fieldName,
|
|
36
|
-
location.search
|
|
36
|
+
location.search,
|
|
37
|
+
activeOrder,
|
|
38
|
+
activeDesc
|
|
37
39
|
]);
|
|
38
40
|
const alignClasses = classnames({
|
|
39
41
|
'byline-th-align-left': 'left' === align,
|
|
@@ -89,6 +89,8 @@ const ListView = ({ data, columns, workflowStatuses, useAsTitle, orderable = fal
|
|
|
89
89
|
const searchParams = location.search;
|
|
90
90
|
const isCanonicalView = !searchParams.order && !searchParams.desc && !searchParams.query && !searchParams.status;
|
|
91
91
|
const dragEnabled = orderable && isCanonicalView && !!onReorder;
|
|
92
|
+
const activeOrder = searchParams.order ?? data?.meta.order;
|
|
93
|
+
const activeDesc = null != searchParams.order ? searchParams.desc : data?.meta.desc;
|
|
92
94
|
const sensors = useSensors(useSensor(PointerSensor, {
|
|
93
95
|
activationConstraint: {
|
|
94
96
|
distance: 5
|
|
@@ -306,7 +308,9 @@ const ListView = ({ data, columns, workflowStatuses, useAsTitle, orderable = fal
|
|
|
306
308
|
sortable: column.sortable,
|
|
307
309
|
scope: "col",
|
|
308
310
|
align: column.align,
|
|
309
|
-
className: column.className
|
|
311
|
+
className: column.className,
|
|
312
|
+
activeOrder: activeOrder,
|
|
313
|
+
activeDesc: activeDesc
|
|
310
314
|
}, String(column.fieldName)))
|
|
311
315
|
]
|
|
312
316
|
})
|
|
@@ -351,7 +355,9 @@ const ListView = ({ data, columns, workflowStatuses, useAsTitle, orderable = fal
|
|
|
351
355
|
sortable: column.sortable,
|
|
352
356
|
scope: "col",
|
|
353
357
|
align: column.align,
|
|
354
|
-
className: column.className
|
|
358
|
+
className: column.className,
|
|
359
|
+
activeOrder: activeOrder,
|
|
360
|
+
activeDesc: activeDesc
|
|
355
361
|
}, String(column.fieldName)))
|
|
356
362
|
})
|
|
357
363
|
}),
|
|
@@ -20,8 +20,15 @@ const getCollectionDocuments = createServerFn({
|
|
|
20
20
|
const where = {};
|
|
21
21
|
if (params.status) where.status = params.status;
|
|
22
22
|
if (params.query) where.query = params.query;
|
|
23
|
+
const adminConfig = getCollectionAdminConfig(path);
|
|
24
|
+
const configuredSort = params.order || true === config.definition.orderable || adminConfig?.defaultSort == null ? void 0 : {
|
|
25
|
+
order: String(adminConfig.defaultSort.field),
|
|
26
|
+
desc: 'desc' === adminConfig.defaultSort.direction
|
|
27
|
+
};
|
|
23
28
|
const defaultSort = true === config.definition.orderable ? {
|
|
24
29
|
order_key: 'asc'
|
|
30
|
+
} : null != configuredSort ? {
|
|
31
|
+
[configuredSort.order]: configuredSort.desc ? 'desc' : 'asc'
|
|
25
32
|
} : void 0;
|
|
26
33
|
const sortSpec = params.order ? {
|
|
27
34
|
[params.order]: false === params.desc ? 'asc' : 'desc'
|
|
@@ -57,8 +64,8 @@ const getCollectionDocuments = createServerFn({
|
|
|
57
64
|
docs,
|
|
58
65
|
meta: {
|
|
59
66
|
...result.meta,
|
|
60
|
-
order: params.order,
|
|
61
|
-
desc: params.desc
|
|
67
|
+
order: params.order ?? configuredSort?.order,
|
|
68
|
+
desc: params.desc ?? configuredSort?.desc
|
|
62
69
|
},
|
|
63
70
|
included: {
|
|
64
71
|
collection: {
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"private": false,
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
|
-
"version": "3.20.
|
|
6
|
+
"version": "3.20.3",
|
|
7
7
|
"engines": {
|
|
8
8
|
"node": ">=20.9.0"
|
|
9
9
|
},
|
|
@@ -119,13 +119,13 @@
|
|
|
119
119
|
"react-swipeable": "^7.0.2",
|
|
120
120
|
"uuid": "^14.0.1",
|
|
121
121
|
"zod": "^4.4.3",
|
|
122
|
-
"@byline/
|
|
123
|
-
"@byline/
|
|
124
|
-
"@byline/
|
|
125
|
-
"@byline/
|
|
126
|
-
"@byline/
|
|
127
|
-
"@byline/core": "3.20.
|
|
128
|
-
"@byline/
|
|
122
|
+
"@byline/admin": "3.20.3",
|
|
123
|
+
"@byline/i18n": "3.20.3",
|
|
124
|
+
"@byline/ai": "3.20.3",
|
|
125
|
+
"@byline/ui": "3.20.3",
|
|
126
|
+
"@byline/client": "3.20.3",
|
|
127
|
+
"@byline/core": "3.20.3",
|
|
128
|
+
"@byline/auth": "3.20.3"
|
|
129
129
|
},
|
|
130
130
|
"peerDependencies": {
|
|
131
131
|
"@tanstack/react-router": "^1.167.0",
|
|
@@ -27,6 +27,16 @@ export interface TableHeadingCellSortableProps extends TableHeadingCellProps {
|
|
|
27
27
|
desc?: boolean
|
|
28
28
|
align?: 'left' | 'center' | 'right'
|
|
29
29
|
className?: string
|
|
30
|
+
/**
|
|
31
|
+
* The *effective* sort column/direction — URL params when present, else
|
|
32
|
+
* the collection's configured `defaultSort` (surfaced via the list
|
|
33
|
+
* response's `meta.order`/`meta.desc`). When provided, these drive the
|
|
34
|
+
* sort indicator instead of reading the URL directly, so a params-less
|
|
35
|
+
* landing still shows which column ordered the rows. Omit for the legacy
|
|
36
|
+
* URL-only behaviour.
|
|
37
|
+
*/
|
|
38
|
+
activeOrder?: string
|
|
39
|
+
activeDesc?: boolean
|
|
30
40
|
}
|
|
31
41
|
|
|
32
42
|
export function TableHeadingCellSortable({
|
|
@@ -36,6 +46,8 @@ export function TableHeadingCellSortable({
|
|
|
36
46
|
sortable = false,
|
|
37
47
|
align = 'left',
|
|
38
48
|
className,
|
|
49
|
+
activeOrder,
|
|
50
|
+
activeDesc,
|
|
39
51
|
...rest
|
|
40
52
|
}: TableHeadingCellSortableProps & {
|
|
41
53
|
ref?: React.RefObject<HTMLTableCellElement>
|
|
@@ -61,15 +73,21 @@ export function TableHeadingCellSortable({
|
|
|
61
73
|
|
|
62
74
|
useEffect(() => {
|
|
63
75
|
if (fieldName != null) {
|
|
64
|
-
|
|
65
|
-
|
|
76
|
+
// Prefer the effective sort passed down by the list view (which folds
|
|
77
|
+
// in a configured `defaultSort`); fall back to reading the URL.
|
|
78
|
+
const order =
|
|
79
|
+
activeOrder !== undefined ? activeOrder : (location.search as Record<string, unknown>).order
|
|
80
|
+
const d =
|
|
81
|
+
activeOrder !== undefined
|
|
82
|
+
? activeDesc
|
|
83
|
+
: ((location.search as Record<string, unknown>).desc as boolean | undefined)
|
|
66
84
|
if (order === fieldName) {
|
|
67
85
|
setDesc(d ?? false)
|
|
68
86
|
} else {
|
|
69
87
|
setDesc(null)
|
|
70
88
|
}
|
|
71
89
|
}
|
|
72
|
-
}, [fieldName, location.search])
|
|
90
|
+
}, [fieldName, location.search, activeOrder, activeDesc])
|
|
73
91
|
|
|
74
92
|
const alignClasses = cx({
|
|
75
93
|
'byline-th-align-left': align === 'left',
|
|
@@ -207,6 +207,14 @@ export const ListView = ({
|
|
|
207
207
|
!searchParams.order && !searchParams.desc && !searchParams.query && !searchParams.status
|
|
208
208
|
const dragEnabled = orderable && isCanonicalView && !!onReorder
|
|
209
209
|
|
|
210
|
+
// The *effective* sort for the header indicators: explicit URL params win;
|
|
211
|
+
// otherwise the server echoes a configured `defaultSort` (admin config)
|
|
212
|
+
// through `meta.order`/`meta.desc`, so a params-less landing still shows
|
|
213
|
+
// which column ordered the rows. (Orderable collections never set
|
|
214
|
+
// `meta.order` — their default is the drag order, indicated separately.)
|
|
215
|
+
const activeOrder = searchParams.order ?? data?.meta.order
|
|
216
|
+
const activeDesc = searchParams.order != null ? searchParams.desc : data?.meta.desc
|
|
217
|
+
|
|
210
218
|
const sensors = useSensors(
|
|
211
219
|
useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
|
|
212
220
|
useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })
|
|
@@ -418,6 +426,8 @@ export const ListView = ({
|
|
|
418
426
|
scope="col"
|
|
419
427
|
align={column.align}
|
|
420
428
|
className={column.className}
|
|
429
|
+
activeOrder={activeOrder}
|
|
430
|
+
activeDesc={activeDesc}
|
|
421
431
|
/>
|
|
422
432
|
)
|
|
423
433
|
})}
|
|
@@ -496,6 +506,8 @@ export const ListView = ({
|
|
|
496
506
|
scope="col"
|
|
497
507
|
align={column.align}
|
|
498
508
|
className={column.className}
|
|
509
|
+
activeOrder={activeOrder}
|
|
510
|
+
activeDesc={activeDesc}
|
|
499
511
|
/>
|
|
500
512
|
)
|
|
501
513
|
})}
|
|
@@ -69,11 +69,27 @@ export const getCollectionDocuments = createServerFn({ method: 'GET' })
|
|
|
69
69
|
if (params.status) where.status = params.status
|
|
70
70
|
if (params.query) where.query = params.query
|
|
71
71
|
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
//
|
|
72
|
+
// Sort precedence: the caller's explicit `params.order` always wins (a
|
|
73
|
+
// shared link opens exactly as sent) → `orderable: true` collections
|
|
74
|
+
// default to the fractional `order_key` ascending → the admin config's
|
|
75
|
+
// `defaultSort` (boot-validated; mutually exclusive with `orderable`) →
|
|
76
|
+
// the storage fallback (`created_at desc`). `configuredSort` is also
|
|
77
|
+
// echoed through `meta.order`/`meta.desc` below so the list header can
|
|
78
|
+
// render the effective sort indicator on a params-less landing.
|
|
79
|
+
const adminConfig = getCollectionAdminConfig(path)
|
|
80
|
+
const configuredSort =
|
|
81
|
+
!params.order && config.definition.orderable !== true && adminConfig?.defaultSort != null
|
|
82
|
+
? {
|
|
83
|
+
order: String(adminConfig.defaultSort.field),
|
|
84
|
+
desc: adminConfig.defaultSort.direction === 'desc',
|
|
85
|
+
}
|
|
86
|
+
: undefined
|
|
75
87
|
const defaultSort: Record<string, 'asc' | 'desc'> | undefined =
|
|
76
|
-
config.definition.orderable === true
|
|
88
|
+
config.definition.orderable === true
|
|
89
|
+
? { order_key: 'asc' }
|
|
90
|
+
: configuredSort != null
|
|
91
|
+
? { [configuredSort.order]: configuredSort.desc ? 'desc' : 'asc' }
|
|
92
|
+
: undefined
|
|
77
93
|
const sortSpec: Record<string, 'asc' | 'desc'> | undefined = params.order
|
|
78
94
|
? { [params.order]: params.desc === false ? 'asc' : 'desc' }
|
|
79
95
|
: defaultSort
|
|
@@ -131,8 +147,12 @@ export const getCollectionDocuments = createServerFn({ method: 'GET' })
|
|
|
131
147
|
docs,
|
|
132
148
|
meta: {
|
|
133
149
|
...result.meta,
|
|
134
|
-
|
|
135
|
-
|
|
150
|
+
// The *effective* sort: explicit params, or the configured
|
|
151
|
+
// `defaultSort` when it filled in. The list header renders its
|
|
152
|
+
// sort indicator from this, so a params-less landing still shows
|
|
153
|
+
// which column ordered the rows.
|
|
154
|
+
order: params.order ?? configuredSort?.order,
|
|
155
|
+
desc: params.desc ?? configuredSort?.desc,
|
|
136
156
|
},
|
|
137
157
|
included: {
|
|
138
158
|
collection: {
|