viur-ui 0.0.1 → 0.0.2
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/README.md +67 -41
- package/index.d.ts +18 -9
- package/index.js +6 -3
- package/package.json +3 -5
- package/src/components/Browser/Browser.vue +58 -58
- package/src/components/Browser/TreeNav.vue +32 -32
- package/src/components/Browser/ViurBrowser.d.vue.ts +21 -21
- package/src/components/Browser/ViurBrowser.vue +47 -44
- package/src/components/Browser/dragSession.ts +27 -27
- package/src/components/Browser/index.ts +3 -3
- package/src/components/Browser/types.ts +9 -9
- package/src/components/DataTable/ColumnFilterPanel.vue +7 -7
- package/src/components/DataTable/DataTable.vue +346 -346
- package/src/components/DataTable/ViurDataTable.d.vue.ts +26 -26
- package/src/components/DataTable/ViurDataTable.vue +51 -50
- package/src/components/DataTable/columns.ts +30 -30
- package/src/components/DataTable/index.ts +12 -12
- package/src/components/DataTable/rows.ts +71 -72
- package/src/components/DataTable/types.ts +55 -55
- package/src/components/DataTable/useColumnLayout.ts +28 -28
- package/src/components/DataTable/useGridNavigation.ts +15 -15
- package/src/components/DataTable/useVirtualRows.ts +10 -10
- package/src/components/DataTable/viurColumns.ts +69 -69
- package/src/forms/ViForm.d.vue.ts +32 -32
- package/src/forms/ViForm.vue +17 -16
- package/src/forms/ViFormCategory.vue +3 -1
- package/src/forms/bones/Bone.vue +125 -108
- package/src/forms/bones/BoneActions.vue +21 -21
- package/src/forms/bones/BoneLabel.vue +1 -1
- package/src/forms/bones/BoneMultipleWrapper.vue +3 -3
- package/src/forms/bones/BoneNestedWrapper.vue +3 -3
- package/src/forms/bones/BoneSingleEntry.vue +2 -2
- package/src/forms/bones/FilterBone.vue +3 -3
- package/src/forms/bones/actions/ActionDialog.vue +2 -2
- package/src/forms/bones/actions/Debug.vue +6 -6
- package/src/forms/bones/actions/Delete.vue +2 -2
- package/src/forms/bones/actions/DescribeImage.vue +4 -3
- package/src/forms/bones/actions/Download.vue +4 -4
- package/src/forms/bones/actions/Placeholder.vue +2 -2
- package/src/forms/bones/actions/index.js +72 -74
- package/src/forms/bones/bone.d.ts +19 -19
- package/src/forms/bones/uploadAdapter.js +3 -3
- package/src/forms/bones/utils.js +40 -41
- package/src/forms/bones/view/base_item.vue +1 -1
- package/src/forms/bones/view/boneLogic.js +1 -1
- package/src/forms/bones/widgets/booleanBone.vue +19 -19
- package/src/forms/bones/widgets/booleanBoneChoose.vue +2 -2
- package/src/forms/bones/widgets/booleanBoneSelect.vue +3 -3
- package/src/forms/bones/widgets/codeBone.vue +22 -22
- package/src/forms/bones/widgets/colorBone.vue +18 -18
- package/src/forms/bones/widgets/dateBone.vue +22 -22
- package/src/forms/bones/widgets/emailBone.vue +20 -20
- package/src/forms/bones/widgets/fileBone.vue +32 -31
- package/src/forms/bones/widgets/keyBone.vue +21 -21
- package/src/forms/bones/widgets/numericBone.vue +21 -21
- package/src/forms/bones/widgets/passwordBone.vue +34 -31
- package/src/forms/bones/widgets/phoneBone.vue +20 -20
- package/src/forms/bones/widgets/rawBone.vue +19 -19
- package/src/forms/bones/widgets/recordBone.vue +16 -16
- package/src/forms/bones/widgets/relationalBone.vue +26 -26
- package/src/forms/bones/widgets/selectBone.vue +23 -23
- package/src/forms/bones/widgets/selectBoneChoose.vue +2 -2
- package/src/forms/bones/widgets/spatialBone.vue +21 -21
- package/src/forms/bones/widgets/stringBone.vue +27 -27
- package/src/forms/bones/widgets/textBone.vue +28 -26
- package/src/forms/install.d.ts +1 -1
- package/src/forms/install.js +7 -0
- package/src/forms/layouts/LayloutList2Columns.vue +1 -1
- package/src/forms/layouts/LayoutCategory.vue +1 -1
- package/src/forms/layouts/LayoutList.vue +1 -1
- package/src/forms/utils.js +22 -22
- package/src/request/configStore.js +4 -4
- package/src/request/envelope.js +6 -6
- package/src/request/request.js +22 -22
- package/src/translations/de.js +31 -0
- package/src/translations/en.js +31 -0
- package/src/user/LoginScreen.d.vue.ts +24 -24
- package/src/user/LoginScreen.vue +6 -7
- package/src/user/store.d.ts +16 -16
- package/src/vite/safeHtml.js +73 -0
- package/src/vite/sanitizeVHtml.d.ts +4 -0
- package/src/vite/sanitizeVHtml.js +15 -0
- package/src/webawesomeCompat.css +0 -40
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// Pure row pipeline: filter → search → sort → tree/groups →
|
|
2
|
+
// pagination → flat display rows. Also Vue-free and therefore directly
|
|
3
|
+
// testable; the component just calls the whole thing inside a computed.
|
|
4
4
|
|
|
5
|
-
import { aggregateColumn, defaultText, findColumn, rowMatchesFilters, rowMatchesSearch, sortRows } from "./columns"
|
|
6
|
-
import type { ColumnFacets, DisplayRow, FilterEntry, ResolvedColumn, Row, RowKey, RowNode, SortEntry } from "./types"
|
|
7
|
-
import { computeFacets, isEmptyFilter } from "./columns"
|
|
5
|
+
import { aggregateColumn, defaultText, findColumn, rowMatchesFilters, rowMatchesSearch, sortRows } from "./columns.ts"
|
|
6
|
+
import type { ColumnFacets, DisplayRow, FilterEntry, ResolvedColumn, Row, RowKey, RowNode, SortEntry } from "./types.ts"
|
|
7
|
+
import { computeFacets, isEmptyFilter } from "./columns.ts"
|
|
8
8
|
|
|
9
9
|
export type ChildRowsAccessor<R extends Row = Row> = string | ((row: R) => R[] | undefined | null)
|
|
10
10
|
|
|
@@ -15,24 +15,24 @@ export interface BuildRowsOptions<R extends Row = Row> {
|
|
|
15
15
|
filters?: FilterEntry[]
|
|
16
16
|
search?: string
|
|
17
17
|
searchFn?: ((value: unknown, term: string, row: R) => boolean) | null
|
|
18
|
-
/**
|
|
18
|
+
/** Tree data: field name or function that returns the children. */
|
|
19
19
|
childRows?: ChildRowsAccessor<R> | null
|
|
20
|
-
/**
|
|
20
|
+
/** Parent row stays visible when a descendant matches. */
|
|
21
21
|
filterFromLeafRows?: boolean
|
|
22
|
-
/**
|
|
22
|
+
/** Grouping by one or more column ids (no effect on tree data). */
|
|
23
23
|
groupBy?: string[]
|
|
24
|
-
/**
|
|
24
|
+
/** Field with a stable row id. Without it, index paths are used ("0", "0.1"). */
|
|
25
25
|
rowKey?: string | null
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
export interface BuiltRows<R extends Row = Row> {
|
|
29
|
-
/**
|
|
29
|
+
/** Hierarchy after filtering/sorting/grouping. */
|
|
30
30
|
nodes: RowNode<R>[]
|
|
31
|
-
/**
|
|
31
|
+
/** All data rows in display order (including collapsed ones). */
|
|
32
32
|
dataRows: R[]
|
|
33
|
-
/**
|
|
33
|
+
/** Top level only — for footer functions with tree data. */
|
|
34
34
|
topLevelRows: R[]
|
|
35
|
-
/**
|
|
35
|
+
/** Rows in the result (top level or groups) across all pages. */
|
|
36
36
|
filteredCount: number
|
|
37
37
|
facets: Record<string, ColumnFacets>
|
|
38
38
|
}
|
|
@@ -51,7 +51,7 @@ function keyOf<R extends Row>(row: R, rowKey: string | null | undefined, path: s
|
|
|
51
51
|
return path
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
/**
|
|
54
|
+
/** Convert data rows (with children) into nodes — filtering and sorting included. */
|
|
55
55
|
function buildDataNodes<R extends Row>(
|
|
56
56
|
rows: R[],
|
|
57
57
|
options: BuildRowsOptions<R>,
|
|
@@ -67,8 +67,8 @@ function buildDataNodes<R extends Row>(
|
|
|
67
67
|
const selfMatches = rowMatchesFilters(row, filters, columns) && rowMatchesSearch(row, search, columns, searchFn)
|
|
68
68
|
|
|
69
69
|
const rawChildren = childrenOf(row, childRows)
|
|
70
|
-
//
|
|
71
|
-
//
|
|
70
|
+
// With filterFromLeafRows a descendant's match counts; otherwise a
|
|
71
|
+
// non-matching row takes its whole subtree with it.
|
|
72
72
|
const children = rawChildren.length ? buildDataNodes(rawChildren, options, depth + 1, path) : []
|
|
73
73
|
|
|
74
74
|
if (!selfMatches && !(filterFromLeafRows && children.length)) return
|
|
@@ -84,7 +84,7 @@ function buildDataNodes<R extends Row>(
|
|
|
84
84
|
return nodes
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
/**
|
|
87
|
+
/** Group data nodes by one or more columns. */
|
|
88
88
|
function groupNodes<R extends Row>(
|
|
89
89
|
nodes: RowNode<R>[],
|
|
90
90
|
groupBy: string[],
|
|
@@ -139,7 +139,7 @@ function collectDataRows<R extends Row>(nodes: RowNode<R>[], into: R[] = []): R[
|
|
|
139
139
|
return into
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
/**
|
|
142
|
+
/** Facets per filterable column — each without its own filter. */
|
|
143
143
|
function buildFacets<R extends Row>(options: BuildRowsOptions<R>): Record<string, ColumnFacets> {
|
|
144
144
|
const { columns, filters = [], search = "", searchFn, childRows } = options
|
|
145
145
|
const flat = flattenData(options.data, childRows)
|
|
@@ -182,7 +182,7 @@ export function buildRows<R extends Row>(options: BuildRowsOptions<R>): BuiltRow
|
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
-
/**
|
|
185
|
+
/** Slice one page from the top level (subtrees stay with their parent). */
|
|
186
186
|
export function paginateNodes<R extends Row>(nodes: RowNode<R>[], page: number, pageSize: number): RowNode<R>[] {
|
|
187
187
|
if (pageSize <= 0) return nodes
|
|
188
188
|
const start = page * pageSize
|
|
@@ -191,29 +191,29 @@ export function paginateNodes<R extends Row>(nodes: RowNode<R>[], page: number,
|
|
|
191
191
|
|
|
192
192
|
export interface FlattenOptions<R extends Row = Row> {
|
|
193
193
|
isExpanded: (key: RowKey) => boolean
|
|
194
|
-
/**
|
|
194
|
+
/** Rows get a detail panel (a separate row below the data row). */
|
|
195
195
|
withDetail?: boolean
|
|
196
196
|
/**
|
|
197
|
-
*
|
|
198
|
-
*
|
|
199
|
-
*
|
|
197
|
+
* For lazily loaded levels: the row counts as expandable even though there
|
|
198
|
+
* are no children in the data (yet). When lazy loading, you don't know in
|
|
199
|
+
* advance whether there are any — the arrow must still be there from the start.
|
|
200
200
|
*/
|
|
201
201
|
mayHaveChildren?: (node: RowNode<R>) => boolean
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
/**
|
|
205
|
-
*
|
|
206
|
-
*
|
|
205
|
+
* Turn the hierarchy into a flat render list: collapsed subtrees are
|
|
206
|
+
* dropped, expanded detail panels are added as separate rows.
|
|
207
207
|
*/
|
|
208
208
|
export function flattenNodes<R extends Row>(nodes: RowNode<R>[], options: FlattenOptions<R>): DisplayRow<R>[] {
|
|
209
209
|
const out: DisplayRow<R>[] = []
|
|
210
210
|
let dataIndex = 0
|
|
211
211
|
|
|
212
212
|
/**
|
|
213
|
-
* `lines`
|
|
214
|
-
*
|
|
215
|
-
*
|
|
216
|
-
*
|
|
213
|
+
* `lines` are the pass-through columns left of the row's own connector: one
|
|
214
|
+
* column per entry in which a vertical line runs through, because the ancestor
|
|
215
|
+
* of the NEXT level still has a sibling (see `DisplayRow.ancestorLines`).
|
|
216
|
+
* After the last child the column stays empty.
|
|
217
217
|
*/
|
|
218
218
|
const walk = (list: RowNode<R>[], lines: boolean[]): void => {
|
|
219
219
|
list.forEach((node, position) => {
|
|
@@ -254,8 +254,8 @@ export function flattenNodes<R extends Row>(nodes: RowNode<R>[], options: Flatte
|
|
|
254
254
|
})
|
|
255
255
|
|
|
256
256
|
if (node.kind === "data" && options.withDetail && !hasChildren) out.push(detailRow())
|
|
257
|
-
//
|
|
258
|
-
//
|
|
257
|
+
// Roots create no pass-through column (there is no line between roots);
|
|
258
|
+
// from level 1 on, one is added per level.
|
|
259
259
|
else if (hasChildren) {
|
|
260
260
|
walk(node.children, node.depth === 0 ? [] : [...lines, !isLastChild])
|
|
261
261
|
} else if (options.withDetail) out.push(detailRow())
|
|
@@ -266,9 +266,9 @@ export function flattenNodes<R extends Row>(nodes: RowNode<R>[], options: Flatte
|
|
|
266
266
|
return out
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
-
// ──
|
|
269
|
+
// ── Selection (tree/groups) ───────────────────────────────────────────
|
|
270
270
|
|
|
271
|
-
/**
|
|
271
|
+
/** All data keys of a node including descendants. */
|
|
272
272
|
export function collectKeys<R extends Row>(node: RowNode<R>, into: RowKey[] = []): RowKey[] {
|
|
273
273
|
if (node.kind === "data") into.push(node.key)
|
|
274
274
|
for (const child of node.children) collectKeys(child, into)
|
|
@@ -277,7 +277,7 @@ export function collectKeys<R extends Row>(node: RowNode<R>, into: RowKey[] = []
|
|
|
277
277
|
|
|
278
278
|
export type SelectionState = "checked" | "indeterminate" | "unchecked"
|
|
279
279
|
|
|
280
|
-
/**
|
|
280
|
+
/** Checkbox state of a node (parent mirrors its children). */
|
|
281
281
|
export function selectionStateFor<R extends Row>(node: RowNode<R>, selected: Set<RowKey>): SelectionState {
|
|
282
282
|
const keys = collectKeys(node)
|
|
283
283
|
if (!keys.length) return "unchecked"
|
|
@@ -286,24 +286,23 @@ export function selectionStateFor<R extends Row>(node: RowNode<R>, selected: Set
|
|
|
286
286
|
return hits === keys.length ? "checked" : "indeterminate"
|
|
287
287
|
}
|
|
288
288
|
|
|
289
|
-
// ──
|
|
289
|
+
// ── Flat list to tree ─────────────────────────────────────────────────
|
|
290
290
|
|
|
291
291
|
export interface NestedTree<R extends Row = Row> {
|
|
292
|
-
/**
|
|
292
|
+
/** Top-level rows, in input order. */
|
|
293
293
|
roots: R[]
|
|
294
|
-
/**
|
|
294
|
+
/** Children per row key. */
|
|
295
295
|
children: Map<RowKey, R[]>
|
|
296
296
|
}
|
|
297
297
|
|
|
298
298
|
/**
|
|
299
|
-
*
|
|
300
|
-
*
|
|
299
|
+
* Build the hierarchy from a FLAT list with parent references (ViUR:
|
|
300
|
+
* `parententry`).
|
|
301
301
|
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
* oder Zeilen verschwinden zu lassen.
|
|
302
|
+
* Principle: nothing gets lost. Rows without a parent value are roots; so are
|
|
303
|
+
* rows whose parent isn't in the data (orphans — e.g. because only one page
|
|
304
|
+
* is loaded). Cycles (A → B → A, or a row that is its own parent) also end
|
|
305
|
+
* up as roots, instead of blowing up the list or making rows disappear.
|
|
307
306
|
*/
|
|
308
307
|
export function nestByParent<R extends Row>(rows: R[], rowKey: string, parentField: string): NestedTree<R> {
|
|
309
308
|
const byKey = new Map<RowKey, R>()
|
|
@@ -331,8 +330,8 @@ export function nestByParent<R extends Row>(rows: R[], rowKey: string, parentFie
|
|
|
331
330
|
attached.add(row)
|
|
332
331
|
}
|
|
333
332
|
|
|
334
|
-
//
|
|
335
|
-
//
|
|
333
|
+
// Cycles: anything that has a parent but isn't reachable from any root
|
|
334
|
+
// becomes a root itself — otherwise it would be invisible.
|
|
336
335
|
const reachable = new Set<R>()
|
|
337
336
|
const walk = (list: R[]): void => {
|
|
338
337
|
for (const row of list) {
|
|
@@ -346,7 +345,7 @@ export function nestByParent<R extends Row>(rows: R[], rowKey: string, parentFie
|
|
|
346
345
|
|
|
347
346
|
for (const row of rows) {
|
|
348
347
|
if (!attached.has(row) || reachable.has(row)) continue
|
|
349
|
-
//
|
|
348
|
+
// Cut the edge to the (cyclic) parent and treat it as a root.
|
|
350
349
|
const parent = row[parentField] as RowKey
|
|
351
350
|
const siblings = children.get(parent)
|
|
352
351
|
if (siblings) {
|
|
@@ -360,18 +359,18 @@ export function nestByParent<R extends Row>(rows: R[], rowKey: string, parentFie
|
|
|
360
359
|
return { roots, children }
|
|
361
360
|
}
|
|
362
361
|
|
|
363
|
-
// ── Position
|
|
362
|
+
// ── Position in the tree ──────────────────────────────────────────────
|
|
364
363
|
|
|
365
364
|
export interface NodeLocation<R extends Row = Row> {
|
|
366
365
|
node: RowNode<R>
|
|
367
|
-
/**
|
|
366
|
+
/** Key of the parent row; null on the top level. */
|
|
368
367
|
parentKey: RowKey | null
|
|
369
|
-
/**
|
|
368
|
+
/** Siblings including the node itself, in display order. */
|
|
370
369
|
siblings: RowNode<R>[]
|
|
371
370
|
index: number
|
|
372
371
|
}
|
|
373
372
|
|
|
374
|
-
/**
|
|
373
|
+
/** Find a node along with its parent and siblings. */
|
|
375
374
|
export function locateNode<R extends Row>(
|
|
376
375
|
nodes: RowNode<R>[],
|
|
377
376
|
key: RowKey,
|
|
@@ -388,9 +387,9 @@ export function locateNode<R extends Row>(
|
|
|
388
387
|
}
|
|
389
388
|
|
|
390
389
|
/**
|
|
391
|
-
*
|
|
392
|
-
*
|
|
393
|
-
*
|
|
390
|
+
* Is `candidateKey` in the subtree of `ancestorKey`? Prevents a row from
|
|
391
|
+
* being dragged into its own descendant — the subtree would otherwise hang
|
|
392
|
+
* from itself and disappear from the view.
|
|
394
393
|
*/
|
|
395
394
|
export function isDescendant<R extends Row>(nodes: RowNode<R>[], ancestorKey: RowKey, candidateKey: RowKey): boolean {
|
|
396
395
|
const ancestor = locateNode(nodes, ancestorKey)
|
|
@@ -399,32 +398,32 @@ export function isDescendant<R extends Row>(nodes: RowNode<R>[], ancestorKey: Ro
|
|
|
399
398
|
return walk(ancestor.node.children)
|
|
400
399
|
}
|
|
401
400
|
|
|
402
|
-
// ──
|
|
401
|
+
// ── Fixed order (numeric order column) ────────────────────────────────
|
|
403
402
|
|
|
404
403
|
export interface OrderValue {
|
|
405
|
-
/**
|
|
404
|
+
/** New value for the order column of the moved row. */
|
|
406
405
|
order: number
|
|
407
406
|
/**
|
|
408
|
-
* true
|
|
409
|
-
*
|
|
410
|
-
*
|
|
411
|
-
*
|
|
412
|
-
*
|
|
407
|
+
* true if the two neighbors are NOT distinguishable — in practice whenever
|
|
408
|
+
* several records carry the same order value (e.g. freshly created entries
|
|
409
|
+
* with default 0). The midpoint there would be the same value again, so the
|
|
410
|
+
* row wouldn't visibly move; instead it is placed after the block and the
|
|
411
|
+
* application can renumber.
|
|
413
412
|
*
|
|
414
|
-
*
|
|
415
|
-
*
|
|
413
|
+
* This doesn't happen with spread-out floating-point values: there are about
|
|
414
|
+
* 50 halvings between two values before precision runs out.
|
|
416
415
|
*/
|
|
417
416
|
needsReindex: boolean
|
|
418
417
|
}
|
|
419
418
|
|
|
420
419
|
/**
|
|
421
|
-
*
|
|
422
|
-
*
|
|
420
|
+
* Determine a new order value for an insert position — the same calculation as
|
|
421
|
+
* in admin4: `(prev + next) / 2`, ±step at the edges.
|
|
423
422
|
*
|
|
424
|
-
* `values`
|
|
425
|
-
*
|
|
426
|
-
*
|
|
427
|
-
*
|
|
423
|
+
* `values` are the order values of the target list WITHOUT the moved row,
|
|
424
|
+
* ascending; `index` is the insert position (0 = very front, values.length =
|
|
425
|
+
* very end). Using the midpoint between neighbors means: when moving, exactly
|
|
426
|
+
* ONE record has to be saved, not the whole list.
|
|
428
427
|
*/
|
|
429
428
|
export function orderValueFor(values: number[], index: number, step = 1): OrderValue {
|
|
430
429
|
const clean = values.filter((value) => Number.isFinite(value))
|
|
@@ -439,8 +438,8 @@ export function orderValueFor(values: number[], index: number, step = 1): OrderV
|
|
|
439
438
|
const previous = clean[position - 1]
|
|
440
439
|
const next = clean[position]
|
|
441
440
|
const middle = previous + (next - previous) / 2
|
|
442
|
-
//
|
|
443
|
-
//
|
|
441
|
+
// Equal (or indistinguishable) neighbors: the midpoint would hit one of
|
|
442
|
+
// them — so place it after the block instead of silently doing nothing.
|
|
444
443
|
const needsReindex = middle <= previous || middle >= next
|
|
445
444
|
return { order: needsReindex ? previous + step : middle, needsReindex }
|
|
446
445
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
//
|
|
2
|
-
// v12),
|
|
3
|
-
// Lit
|
|
1
|
+
// Types of the DataTable widget — modeled after <wa-data-grid> (WebAwesome Pro
|
|
2
|
+
// v12), but with Vue means: cell content comes via slots instead of
|
|
3
|
+
// Lit templates, state via v-model instead of properties on the element.
|
|
4
4
|
|
|
5
5
|
export type Row = Record<string, unknown>
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ export type Align = "start" | "center" | "end"
|
|
|
8
8
|
export type PinSide = "start" | "end"
|
|
9
9
|
|
|
10
10
|
export type SortMode =
|
|
11
|
-
| "auto" //
|
|
11
|
+
| "auto" // numbers numerically, dates chronologically, otherwise alphanumerically
|
|
12
12
|
| "text"
|
|
13
13
|
| "textCaseSensitive"
|
|
14
14
|
| "number"
|
|
@@ -16,13 +16,13 @@ export type SortMode =
|
|
|
16
16
|
| "basic"
|
|
17
17
|
|
|
18
18
|
export type FilterType =
|
|
19
|
-
| "text" //
|
|
19
|
+
| "text" // substring, case-insensitive (default)
|
|
20
20
|
| "equals"
|
|
21
21
|
| "number-range" // [min, max]
|
|
22
|
-
| "date-range" // [
|
|
23
|
-
| "set" //
|
|
24
|
-
| "includes-any" //
|
|
25
|
-
| "includes-all" //
|
|
22
|
+
| "date-range" // [from, to], inclusive by calendar day
|
|
23
|
+
| "set" // value is one of the selected ones
|
|
24
|
+
| "includes-any" // array cell contains one of the selected ones
|
|
25
|
+
| "includes-all" // array cell contains all selected ones
|
|
26
26
|
|
|
27
27
|
export type AggregationKind = "sum" | "min" | "max" | "extent" | "mean" | "median" | "count" | "unique" | "uniqueCount"
|
|
28
28
|
|
|
@@ -32,34 +32,34 @@ export interface FilterOption {
|
|
|
32
32
|
count?: number
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
/**
|
|
35
|
+
/** A column definition. Plain data object — reactive, without render logic. */
|
|
36
36
|
export interface DataTableColumn<R extends Row = Row> {
|
|
37
|
-
/**
|
|
37
|
+
/** Path into the row, dot paths allowed ("user.name"). Serves as the id if `id` is missing. */
|
|
38
38
|
field?: string
|
|
39
|
-
/**
|
|
39
|
+
/** Explicit id. Required for columns without `field` (actions, computed columns). */
|
|
40
40
|
id?: string
|
|
41
41
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
42
|
+
* Computes the cell value from the row. Unlike `formatter` (display only),
|
|
43
|
+
* this value is used for sorting, filtering, search and export.
|
|
44
44
|
*/
|
|
45
45
|
value?: (row: R) => unknown
|
|
46
46
|
label?: string
|
|
47
47
|
align?: Align
|
|
48
48
|
headerAlign?: Align
|
|
49
|
-
/** Default: true
|
|
49
|
+
/** Default: true as soon as `field` or `value` is present. */
|
|
50
50
|
sortable?: boolean
|
|
51
51
|
sortFn?: SortMode
|
|
52
|
-
/**
|
|
52
|
+
/** Where empty values sort to. */
|
|
53
53
|
sortUndefined?: false | "first" | "last"
|
|
54
|
-
/**
|
|
54
|
+
/** First sort of this column is descending. */
|
|
55
55
|
sortDescFirst?: boolean
|
|
56
56
|
comparator?: (a: unknown, b: unknown, rowA: R, rowB: R) => number
|
|
57
|
-
/** Default: true
|
|
57
|
+
/** Default: true as soon as `field` or `value` is present. */
|
|
58
58
|
searchable?: boolean
|
|
59
59
|
filterable?: boolean
|
|
60
60
|
filterType?: FilterType
|
|
61
61
|
filterFn?: (value: unknown, filterValue: unknown, row: R) => boolean
|
|
62
|
-
/**
|
|
62
|
+
/** Replaces the computed facets in the set/includes filter (e.g. in server mode). */
|
|
63
63
|
filterOptions?: FilterOption[]
|
|
64
64
|
hidden?: boolean
|
|
65
65
|
hideable?: boolean
|
|
@@ -70,28 +70,28 @@ export interface DataTableColumn<R extends Row = Row> {
|
|
|
70
70
|
width?: number
|
|
71
71
|
minWidth?: number
|
|
72
72
|
maxWidth?: number
|
|
73
|
-
/**
|
|
73
|
+
/** Share of the free space; beats `width`. */
|
|
74
74
|
flex?: number
|
|
75
|
-
/**
|
|
75
|
+
/** Cell text. For rich content use the `cell:<id>` slot. */
|
|
76
76
|
formatter?: (value: unknown, row: R) => string
|
|
77
77
|
/**
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
* `badgesForBone` in viurColumns.ts,
|
|
81
|
-
* select
|
|
78
|
+
* Render value(s) as colored badge(s) instead of text. Positive values
|
|
79
|
+
* green, negative red, everything in between rainbow-graded — see
|
|
80
|
+
* `badgesForBone` in viurColumns.ts, which creates this function for plain
|
|
81
|
+
* select and bool bones.
|
|
82
82
|
*/
|
|
83
83
|
badge?: (value: unknown, row: R) => CellBadge[] | null
|
|
84
|
-
/**
|
|
84
|
+
/** Footer: text or function over the filtered rows (all pages). */
|
|
85
85
|
footer?: string | ((rows: R[]) => string)
|
|
86
86
|
aggregation?: AggregationKind | ((values: unknown[], rows: R[]) => unknown)
|
|
87
87
|
cellClass?: string | ((value: unknown, row: R) => string)
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
/**
|
|
91
|
-
/**
|
|
90
|
+
/** Column with resolved defaults — what the component uses internally. */
|
|
91
|
+
/** A colored cell badge (see DataTableColumn.badge). */
|
|
92
92
|
export interface CellBadge {
|
|
93
93
|
label: string
|
|
94
|
-
/** CSS
|
|
94
|
+
/** CSS color (base tone); the table mixes background/border itself. */
|
|
95
95
|
color: string
|
|
96
96
|
}
|
|
97
97
|
|
|
@@ -107,9 +107,9 @@ export interface ResolvedColumn<R extends Row = Row> {
|
|
|
107
107
|
filterable: boolean
|
|
108
108
|
filterType: FilterType
|
|
109
109
|
hideable: boolean
|
|
110
|
-
/**
|
|
110
|
+
/** Cell value of a row (value() or dot path). */
|
|
111
111
|
get: (row: R) => unknown
|
|
112
|
-
/**
|
|
112
|
+
/** Cell text for display, text search, copying and CSV. */
|
|
113
113
|
text: (row: R) => string
|
|
114
114
|
}
|
|
115
115
|
|
|
@@ -124,13 +124,13 @@ export interface FilterEntry {
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
export interface ColumnFacets {
|
|
127
|
-
/**
|
|
127
|
+
/** Distinct values with hit count, descending by frequency. */
|
|
128
128
|
values: { value: string; count: number }[]
|
|
129
129
|
min?: number
|
|
130
130
|
max?: number
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
/**
|
|
133
|
+
/** Request to the server (dataSource / data-request). */
|
|
134
134
|
export interface DataTableRequest {
|
|
135
135
|
sort: SortEntry[]
|
|
136
136
|
filters: FilterEntry[]
|
|
@@ -145,7 +145,7 @@ export interface DataTableResponse<R extends Row = Row> {
|
|
|
145
145
|
total: number
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
/**
|
|
148
|
+
/** Serializable snapshot of the user-adjustable state. */
|
|
149
149
|
export interface DataTableState {
|
|
150
150
|
version: 1
|
|
151
151
|
columnOrder?: string[]
|
|
@@ -163,7 +163,7 @@ export interface DataTableState {
|
|
|
163
163
|
|
|
164
164
|
export type RowKey = string | number
|
|
165
165
|
|
|
166
|
-
/**
|
|
166
|
+
/** Node of the (tree/group) hierarchy after filtering and sorting. */
|
|
167
167
|
export interface RowNode<R extends Row = Row> {
|
|
168
168
|
kind: "data" | "group"
|
|
169
169
|
key: RowKey
|
|
@@ -179,48 +179,48 @@ export interface RowNode<R extends Row = Row> {
|
|
|
179
179
|
aggregates?: Record<string, unknown>
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
-
/**
|
|
182
|
+
/** A rendered row (flat, including detail panels). */
|
|
183
183
|
export interface DisplayRow<R extends Row = Row> {
|
|
184
184
|
kind: "data" | "group" | "detail"
|
|
185
|
-
/**
|
|
185
|
+
/** Unique within the render pass (detail rows have a suffix). */
|
|
186
186
|
key: string
|
|
187
|
-
/**
|
|
187
|
+
/** Key of the associated row/group. */
|
|
188
188
|
nodeKey: RowKey
|
|
189
189
|
depth: number
|
|
190
190
|
expandable: boolean
|
|
191
191
|
expanded: boolean
|
|
192
|
-
/**
|
|
192
|
+
/** Running index of the data rows (kind "data" only). */
|
|
193
193
|
index: number
|
|
194
194
|
/**
|
|
195
|
-
*
|
|
196
|
-
* `true`
|
|
197
|
-
*
|
|
195
|
+
* The PASS-THROUGH columns left of the row's own connector (length = `depth - 1`).
|
|
196
|
+
* `true` means: a vertical line runs through there, because the ancestor
|
|
197
|
+
* of this column still has a sibling after it.
|
|
198
198
|
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
*
|
|
199
|
+
* The mapping matters: the line in a column does not connect the ancestor
|
|
200
|
+
* of THIS level with its sibling, but the ancestor of the
|
|
201
|
+
* NEXT level — just like in `tree` output:
|
|
202
202
|
*
|
|
203
203
|
* ```
|
|
204
204
|
* A
|
|
205
205
|
* ├── B
|
|
206
|
-
* │ └── C ←
|
|
206
|
+
* │ └── C ← the "│" is here because D still comes after B
|
|
207
207
|
* └── D
|
|
208
208
|
* ```
|
|
209
209
|
*
|
|
210
|
-
*
|
|
211
|
-
*
|
|
210
|
+
* Roots create no pass-through column: no line is drawn between two
|
|
211
|
+
* roots.
|
|
212
212
|
*/
|
|
213
213
|
ancestorLines: boolean[]
|
|
214
|
-
/**
|
|
214
|
+
/** Last child of its parent → connector "└" instead of "├". */
|
|
215
215
|
isLastChild: boolean
|
|
216
216
|
row?: R
|
|
217
217
|
node?: RowNode<R>
|
|
218
218
|
}
|
|
219
219
|
|
|
220
220
|
/**
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
221
|
+
* The component API exposed via `defineExpose`. Use as the type for
|
|
222
|
+
* template refs — `InstanceType<typeof DataTable>` doesn't work with
|
|
223
|
+
* generic components.
|
|
224
224
|
*/
|
|
225
225
|
export interface DataTableApi<R extends Row = Row> {
|
|
226
226
|
getVisibleRows: () => R[]
|
|
@@ -240,11 +240,11 @@ export interface DataTableApi<R extends Row = Row> {
|
|
|
240
240
|
resetColumns: () => void
|
|
241
241
|
getColumnFacets: (id: string) => ColumnFacets
|
|
242
242
|
reload: () => void
|
|
243
|
-
/**
|
|
243
|
+
/** Discard a row's lazily loaded children and fetch them again. */
|
|
244
244
|
reloadChildren: (key: RowKey) => Promise<void>
|
|
245
|
-
/**
|
|
245
|
+
/** Lazily loaded children of a row (undefined = not loaded yet). */
|
|
246
246
|
getLoadedChildren: (key: RowKey) => R[] | undefined
|
|
247
|
-
/**
|
|
247
|
+
/** Replace a row's lazily loaded children without loading. */
|
|
248
248
|
setLoadedChildren: (key: RowKey, rows: R[]) => void
|
|
249
249
|
getDataAsCsv: (options?: {
|
|
250
250
|
columnIds?: string[]
|