@columenlabs/lumen-ui 0.1.109 → 0.1.110
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 +1 -0
- package/dist/lumen-view.js +13 -13
- package/dist/theme.css +37 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -78,6 +78,7 @@ Every export, its props and an example are on the [docs](https://lumen.columenla
|
|
|
78
78
|
- Numbers use tabular figures and a true minus `−`; a gain or loss always shows a sign or arrow.
|
|
79
79
|
- A page's header numbers are metric cards: `TileRow` of `Tile` (or `TileToggleGroup` when they pick what the page shows), with `fill` so they share the row, each with `spark` set to its daily history where it has one. Don't build a card of your own for this.
|
|
80
80
|
- Column widths: the first column (a row's name) grows only to `--lead-col-max` (18rem, theme.css); past it the cell truncates on one line and a plain text value shows in full on hover (a rendered cell brings its own hover card). Cap any other column with the field's `maxWidth` (`'12rem'`), or lift the cap with `maxWidth: false`. In a rendered name cell, put the name in a `truncate` span so the ellipsis lands on it, not on a chip after it.
|
|
81
|
+
- Group, sub-group and Total rows read on one line at every width, a phone's cards included: the group's name shortens in an ellipsis (full name on hover) and its roll-up figure stays on the right. The view marks them `data-rollup` and theme.css keeps them to one line, so a `mark`, `rollRender` or `groupActions` an app passes never wraps them.
|
|
81
82
|
- Column alignment is the table's, not the cell's. Text left, numbers right, and a number column has one right edge for its header, values, dashes and Total. `UnitValue` puts the unit in a fixed slot after the number (`--unit-slot`, 2rem) and its status dot just left of the number. In a LumenView table the first `UnitValue` drawn in a column marks it (`data-units` on every cell of that column, through the `UnitColumns` context and the cell's `data-col`); theme.css then widens that column's right gutter by the slot and hangs each unit in it. So a plain `—`, `Free` or `$0.21` an app draws in the same column sits on the digits' edge, and the header sits over the digits. Don't hand-place units, dots or padding to line a column up; if a column is off, draw its numbers through `UnitValue`. Outside a LumenView table (a hover card, a tile) `UnitValue` keeps its slot inline.
|
|
82
83
|
- A metered column (use against an allowance) shows one number per cell with `UnitValue` (right-aligned number, fixed muted unit slot, status dot), never `value [pct]`. Offer both readings through the field's `showAs` (Amount / % of allowance, picked per column in Edit view, saved as `shows`); default amounts for sizes (storage) and shares for capped counts. Put the other reading, the allowance and the cost formula in a hover card. Totals use `rollRender`: sum amounts with their unit; a share is the rows' total over their allowance, never a sum of percents.
|
|
83
84
|
- A floating bulk-action bar over a table's selection: Apply clears the selection and the bar goes away; the toast's Undo restores the change, not the selection. Cancel keeps the selection.
|
package/dist/lumen-view.js
CHANGED
|
@@ -680,11 +680,11 @@ function LumenTableRows({ domain, columns = domain.fields, rows, groupBy = null,
|
|
|
680
680
|
const foldRow = (key, members, scope) => {
|
|
681
681
|
const k = `under:${scope}\u0000${key}`;
|
|
682
682
|
const ms = members.map((m) => m.original);
|
|
683
|
-
return (_jsxs(Fragment, { children: [_jsxs(TableRow, { "data-row": k, className: "cursor-pointer hover:bg-muted has-aria-expanded:hover:bg-muted", onClick: () => setOpenParts((o) => {
|
|
683
|
+
return (_jsxs(Fragment, { children: [_jsxs(TableRow, { "data-row": k, "data-rollup": "", className: "cursor-pointer hover:bg-muted has-aria-expanded:hover:bg-muted", onClick: () => setOpenParts((o) => {
|
|
684
684
|
const n = new Set(o);
|
|
685
685
|
n.has(k) ? n.delete(k) : n.add(k);
|
|
686
686
|
return n;
|
|
687
|
-
}), children: [selection && _jsx(TableCell, {}), shown.map((f, i) => (_jsx(TableCell, { ...col(f, groupBy && i === 0 && 'pl-8'), children: i === 0 ? (_jsxs("span", { className: "flex items-center gap-1", children: [chevron(k, `${key} ${openLabel()}`), subgroup?.mark?.(key, ms), _jsx("span", { className: "font-medium", title:
|
|
687
|
+
}), children: [selection && _jsx(TableCell, {}), shown.map((f, i) => (_jsx(TableCell, { ...col(f, groupBy && i === 0 && 'pl-8'), children: i === 0 ? (_jsxs("span", { className: "flex min-w-0 items-center gap-1 whitespace-nowrap", children: [chevron(k, `${key} ${openLabel()}`), subgroup?.mark?.(key, ms), _jsx("span", { className: "truncate font-medium", title: key, children: titleOf(key, ms) }), _jsx("span", { className: "num shrink-0 text-xs text-muted-foreground", children: ms.length })] })) : (rollCell(f, ms, 'fold')) }, f.key))), add && _jsx(TableCell, {})] }), openParts.has(k) && ms.map((p) => partRow(p, subgroup?.name))] }, k));
|
|
688
688
|
};
|
|
689
689
|
const rowsOf = (list, scope) => {
|
|
690
690
|
const under = folds ? subgroup?.under : undefined;
|
|
@@ -750,22 +750,22 @@ function LumenTableRows({ domain, columns = domain.fields, rows, groupBy = null,
|
|
|
750
750
|
(!serverGroups || serverGroups.onToggleAll) && (_jsx(IconButton, { icon: anyClosed ? ChevronsUpDownIcon : ChevronsDownUpIcon, size: "icon-xs", className: "-ml-2 mr-1 align-middle", label: anyClosed ? 'Expand all groups' : 'Collapse all groups', onClick: () => setAll(anyClosed) })), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs(Button, { variant: "ghost", size: "xs", className: cn('font-medium', !(i === 0 && groups) && '-mx-2'), style: { color: colorOf(domain, shown[i]) ?? undefined }, onClick: h.column.getCanSort() ? h.column.getToggleSortingHandler() : undefined, children: [shown[i] ? _jsx(FreshnessMark, { domain: domain, field: shown[i] }) : null, flexRender(h.column.columnDef.header, h.getContext()), _jsx(SortIcon, { dir: h.column.getIsSorted() })] }) }), shown[i] && (domain.groups?.length || freshnessOf(domain, shown[i])) ? (_jsxs(TooltipContent, { children: [domain.groups?.length ? _jsx("div", { children: pathOf(domain, shown[i]) }) : null, freshnessLine(domain, shown[i]) ? (_jsx("div", { children: freshnessLine(domain, shown[i]) })) : null] })) : null] })] }, h.id))), add && (_jsx(TableHead, { className: cn('w-8 pl-1', PINNED_HEAD), children: _jsx(AddColumnMenu, { add: add, fieldOpen: fieldOpen, onFieldOpen: setFieldOpen }) }))] }, hg.id))) }), _jsxs(TableBody, { children: [!rows.length && !serverGroups?.list?.length && (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: span, className: "py-6 text-center text-muted-foreground", children: "No rows match." }) })), groups && groupBy
|
|
751
751
|
? groups.map(([key, members], gi) => {
|
|
752
752
|
const open = isOpen(key, gi);
|
|
753
|
-
return (_jsxs(Fragment, { children: [_jsxs(TableRow, { className: ROLLUP, "data-group": key, children: [selection && (_jsx(TableCell, { className: cn('w-8 pr-0', PINNED_GROUP), children: _jsx(Checkbox, { checked: members.every((m) => selection.keys.has(keyOf(m.original)))
|
|
753
|
+
return (_jsxs(Fragment, { children: [_jsxs(TableRow, { className: ROLLUP, "data-group": key, "data-rollup": "", children: [selection && (_jsx(TableCell, { "data-card": "select", className: cn('w-8 pr-0', PINNED_GROUP), children: _jsx(Checkbox, { checked: members.every((m) => selection.keys.has(keyOf(m.original)))
|
|
754
754
|
? true
|
|
755
755
|
: members.some((m) => selection.keys.has(keyOf(m.original)))
|
|
756
756
|
? 'indeterminate'
|
|
757
|
-
: false, onCheckedChange: (on) => toggle(members.map((m) => keyOf(m.original)), on === true), "aria-label": `Select every row of ${key}` }) })), shown.slice(lead - 1).map((f, i) => i === 0 ? (
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
757
|
+
: false, onCheckedChange: (on) => toggle(members.map((m) => keyOf(m.original)), on === true), "aria-label": `Select every row of ${key}` }) })), shown.slice(lead - 1).map((f, i) => i === 0 ? (_jsx(TableCell, { colSpan: leadSpan, "data-card": "lead", className: PINNED_GROUP, children: _jsxs("span", { className: "flex max-w-(--lead-col-max) min-w-0 items-center whitespace-nowrap", children: [_jsxs(Button, { variant: "ghost", size: "xs", title: key, className: "-mx-2 min-w-0 hover:bg-transparent aria-expanded:bg-transparent", "aria-expanded": open, "aria-label": `${open ? 'Collapse' : 'Expand'} ${key}`, onClick: () => serverGroups
|
|
758
|
+
? serverGroups.onToggle(key)
|
|
759
|
+
: setFlipped((c) => {
|
|
760
|
+
const n = new Set(c);
|
|
761
|
+
n.has(key) ? n.delete(key) : n.add(key);
|
|
762
|
+
return n;
|
|
763
|
+
}), children: [_jsx(ChevronRightIcon, { className: cn('transition-transform', open && 'rotate-90') }), _jsx(GroupMark, { field: groupBy, value: key }), _jsx("span", { className: "truncate", children: key }), _jsx("span", { className: "num shrink-0 font-normal text-muted-foreground", children: serverGroups
|
|
764
|
+
? (served.get(key)?.n?.toLocaleString() ?? '')
|
|
765
|
+
: members.length })] }), groupActions?.(key, members.map((m) => m.original))] }) }, f.key)) : (_jsx(TableCell, { ...col(f, PINNED_GROUP), children: !serverGroups &&
|
|
766
766
|
rollCell(f, members.map((m) => m.original), 'group') }, f.key))), add && _jsx(TableCell, { className: PINNED_GROUP })] }), open && (serverGroups ? servedRows(members, key) : drawn(members, key))] }, key));
|
|
767
767
|
})
|
|
768
|
-
: drawn(sorted, ''), serverGroups?.more && !serverGroups.pages && (_jsx(TableRow, { "data-more": true, children: _jsx(TableCell, { colSpan: span, className: "py-1", children: _jsxs(Button, { variant: "ghost", size: "xs", onClick: serverGroups.more, children: [_jsx(ChevronsDownIcon, {}), "More groups"] }) }) })), hasTotal && rows.length > 0 && (_jsxs(TableRow, { className: cn(ROLLUP, 'border-t-2'), "data-total": true, children: [selection && _jsx(TableCell, {}), rollCells(rows).map((cell, i) => i === 0 && !(shown[0] && rollsUp(shown[0])) ? (_jsx(TableCell, { children: "Total" }, "total")) : (cell)), add && _jsx(TableCell, {})] }))] })] }) }) }));
|
|
768
|
+
: drawn(sorted, ''), serverGroups?.more && !serverGroups.pages && (_jsx(TableRow, { "data-more": true, children: _jsx(TableCell, { colSpan: span, className: "py-1", children: _jsxs(Button, { variant: "ghost", size: "xs", onClick: serverGroups.more, children: [_jsx(ChevronsDownIcon, {}), "More groups"] }) }) })), hasTotal && rows.length > 0 && (_jsxs(TableRow, { className: cn(ROLLUP, 'border-t-2'), "data-total": true, "data-rollup": "", children: [selection && _jsx(TableCell, {}), rollCells(rows).map((cell, i) => i === 0 && !(shown[0] && rollsUp(shown[0])) ? (_jsx(TableCell, { "data-card": "lead", children: "Total" }, "total")) : (cell)), add && _jsx(TableCell, {})] }))] })] }) }) }));
|
|
769
769
|
}
|
|
770
770
|
/** One value rolled up into a rows × columns grid, with totals where the roll-up adds. */
|
|
771
771
|
function PivotTable({ domain, rows, pivot, }) {
|
package/dist/theme.css
CHANGED
|
@@ -515,6 +515,43 @@
|
|
|
515
515
|
margin-left: 0.25em;
|
|
516
516
|
font-size: inherit;
|
|
517
517
|
}
|
|
518
|
+
/* Group, fold and Total rows (`data-rollup`) are one line at every width: the name shortens
|
|
519
|
+
in an ellipsis, the row's one figure keeps its width on the right, and the rest waits in the
|
|
520
|
+
open rows. Whatever an app draws in them (a mark, a roll-up, group actions) never wraps.
|
|
521
|
+
The group band pins as a whole row here: a cell can't pin inside a one-line flex row. */
|
|
522
|
+
table[data-cards] tr[data-rollup] {
|
|
523
|
+
flex-wrap: nowrap;
|
|
524
|
+
align-items: center;
|
|
525
|
+
white-space: nowrap;
|
|
526
|
+
}
|
|
527
|
+
table[data-cards] tr[data-rollup]::after {
|
|
528
|
+
display: none;
|
|
529
|
+
}
|
|
530
|
+
table[data-cards] tr[data-rollup] > td {
|
|
531
|
+
white-space: nowrap;
|
|
532
|
+
}
|
|
533
|
+
table[data-cards] tr[data-rollup] > td[data-card="lead"] {
|
|
534
|
+
flex: 1 1 0;
|
|
535
|
+
min-width: 0;
|
|
536
|
+
overflow: hidden;
|
|
537
|
+
}
|
|
538
|
+
table[data-cards]
|
|
539
|
+
tr[data-rollup]
|
|
540
|
+
> td:is([data-card="num"], [data-card="text"], :not([data-card])) {
|
|
541
|
+
display: none;
|
|
542
|
+
}
|
|
543
|
+
table[data-cards] tr[data-group] {
|
|
544
|
+
position: sticky;
|
|
545
|
+
top: var(--pin-top, 0px);
|
|
546
|
+
z-index: 10;
|
|
547
|
+
background: color-mix(in oklab, var(--color-muted) 50%, var(--color-card));
|
|
548
|
+
box-shadow: inset 0 1px 0 var(--color-border), inset 0 -1px 0 var(--color-border);
|
|
549
|
+
}
|
|
550
|
+
table[data-cards] tr[data-group] > td {
|
|
551
|
+
position: static;
|
|
552
|
+
background: transparent;
|
|
553
|
+
box-shadow: none;
|
|
554
|
+
}
|
|
518
555
|
/* A bare count names what it counts (`cardUnit`): no header says it on a card. */
|
|
519
556
|
table[data-cards] td[data-card-unit]::after {
|
|
520
557
|
content: attr(data-card-unit);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@columenlabs/lumen-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.110",
|
|
4
4
|
"description": "Lumen: shared React components (frame, views, widgets, charts, drawers).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"@codemirror/lang-javascript": "6.2.5",
|
|
50
50
|
"@codemirror/lang-json": "6.0.2",
|
|
51
51
|
"@codemirror/lang-sql": "6.10.0",
|
|
52
|
-
"@columenlabs/lumen-ui-types": "0.1.
|
|
52
|
+
"@columenlabs/lumen-ui-types": "0.1.110",
|
|
53
53
|
"@dnd-kit/core": "6.3.1",
|
|
54
54
|
"@dnd-kit/sortable": "10.0.0",
|
|
55
55
|
"@dnd-kit/utilities": "3.2.2",
|