@columenlabs/lumen-ui 0.1.47 → 0.1.49
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/details-table.d.ts +5 -1
- package/dist/details-table.js +9 -2
- package/dist/lumen-view.js +5 -1
- package/dist/nav.d.ts +1 -1
- package/dist/nav.js +4 -2
- package/package.json +2 -2
package/dist/details-table.d.ts
CHANGED
|
@@ -12,8 +12,12 @@ export interface DetailsTableProps {
|
|
|
12
12
|
}[];
|
|
13
13
|
/** An optional total row, one cell per column. */
|
|
14
14
|
foot?: ReactNode[];
|
|
15
|
+
/** The key of the row shown as picked, when rows can be picked. */
|
|
16
|
+
selected?: string;
|
|
17
|
+
/** Makes each row pickable by click or Enter/Space, for example to chart the picked row below. */
|
|
18
|
+
onSelect?: (key: string) => void;
|
|
15
19
|
}
|
|
16
|
-
export declare function DetailsTable({ head, rows, foot }: DetailsTableProps): import("react").JSX.Element;
|
|
20
|
+
export declare function DetailsTable({ head, rows, foot, selected, onSelect }: DetailsTableProps): import("react").JSX.Element;
|
|
17
21
|
/** Facts as a two-row table: the labels as the header, the values under them. */
|
|
18
22
|
export declare function FactsTable({ items }: {
|
|
19
23
|
items: [string, ReactNode][];
|
package/dist/details-table.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { cn } from './lib/utils.js';
|
|
3
3
|
import { Table, TableBody, TableCell, TableFooter, TableHead, TableHeader, TableRow, } from './ui/table.js';
|
|
4
|
-
export function DetailsTable({ head, rows, foot }) {
|
|
4
|
+
export function DetailsTable({ head, rows, foot, selected, onSelect }) {
|
|
5
5
|
const marked = head.findIndex((h) => h.grow);
|
|
6
6
|
const grow = marked >= 0 ? marked : head.length - 1;
|
|
7
7
|
const cell = (i) => cn('num', head[i]?.right && 'text-right', i !== grow && 'w-px');
|
|
8
|
-
return (_jsx("div", { className: "-mx-1 overflow-x-auto overflow-y-hidden rounded-md border", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsx(TableRow, { children: head.map((h, i) => (_jsx(TableHead, { className: cn(h.right && 'text-right', i !== grow && 'w-px'), children: h.label }, h.label))) }) }), _jsx(TableBody, { children: rows.map((r) => (_jsx(TableRow, {
|
|
8
|
+
return (_jsx("div", { className: "-mx-1 overflow-x-auto overflow-y-hidden rounded-md border", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsx(TableRow, { children: head.map((h, i) => (_jsx(TableHead, { className: cn(h.right && 'text-right', i !== grow && 'w-px'), children: h.label }, h.label))) }) }), _jsx(TableBody, { children: rows.map((r) => (_jsx(TableRow, { "data-state": r.key === selected ? 'selected' : undefined, "aria-current": r.key === selected || undefined, tabIndex: onSelect ? 0 : undefined, className: cn(onSelect &&
|
|
9
|
+
'cursor-pointer focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring'), onClick: onSelect && (() => onSelect(r.key)), onKeyDown: onSelect &&
|
|
10
|
+
((e) => {
|
|
11
|
+
if (e.key !== 'Enter' && e.key !== ' ')
|
|
12
|
+
return;
|
|
13
|
+
e.preventDefault();
|
|
14
|
+
onSelect(r.key);
|
|
15
|
+
}), children: r.cells.map((c, i) => (_jsx(TableCell, { className: cell(i), children: c }, i))) }, r.key))) }), foot && (_jsx(TableFooter, { children: _jsx(TableRow, { children: foot.map((c, i) => (_jsx(TableCell, { className: cell(i), children: c }, i))) }) }))] }) }));
|
|
9
16
|
}
|
|
10
17
|
/** Facts as a two-row table: the labels as the header, the values under them. */
|
|
11
18
|
export function FactsTable({ items }) {
|
package/dist/lumen-view.js
CHANGED
|
@@ -367,6 +367,7 @@ function sortingOf(sort, columns, serverSorts) {
|
|
|
367
367
|
return columns.some((f) => f.key === sort.key) ? [{ id: sort.key, desc: sort.desc }] : [];
|
|
368
368
|
}
|
|
369
369
|
function LumenTableRows({ domain, columns = domain.fields, rows, groupBy = null, chips = [], page = null, inView = false, fit = false, onDetails, tapped, selection, groupActions, sort, onSort, serverSorts, total = true, subgroup = null, add, serverGroups = null, }) {
|
|
370
|
+
const isMobile = useIsMobile();
|
|
370
371
|
const [sorting, setSorting] = useState(() => sortingOf(sort, columns, serverSorts));
|
|
371
372
|
// A saved sort patched while the table is open (the assistant's card move) applies at once.
|
|
372
373
|
const savedSort = JSON.stringify(sort ?? null);
|
|
@@ -555,6 +556,9 @@ function LumenTableRows({ domain, columns = domain.fields, rows, groupBy = null,
|
|
|
555
556
|
const lead = Math.max(1, shown.findIndex((f, i) => i > 0 && rolls(f)) < 0
|
|
556
557
|
? shown.length
|
|
557
558
|
: shown.findIndex((f, i) => i > 0 && rolls(f)));
|
|
559
|
+
// On a phone the hideOnPhone columns are gone, so the label spans only the leading ones still
|
|
560
|
+
// drawn; spanning the hidden ones too pushed the group's totals right of their headers.
|
|
561
|
+
const leadSpan = Math.max(1, lead - (isMobile ? shown.slice(0, lead).filter((f) => f.hideOnPhone).length : 0));
|
|
558
562
|
const rollCell = (f, members) => rollsUp(f) && f.rollRender ? (f.rollRender(members)) : rollsUp(f) ? (_jsx(ViewValueCell, { value: computed.roll(f, members), format: f.aggregate && isCountAgg(f.aggregate) ? 'rounded' : f.format, amount: f.aggregate !== 'count' && f.amount, compact: f.compact })) : null;
|
|
559
563
|
const rollCells = (members) => shown.map((f) => (_jsx(TableCell, { className: colOf(f), children: rollCell(f, members) }, f.key)));
|
|
560
564
|
// A row's chevron opens one level under it, closed by default: its panel, else the view's
|
|
@@ -654,7 +658,7 @@ function LumenTableRows({ domain, columns = domain.fields, rows, groupBy = null,
|
|
|
654
658
|
? true
|
|
655
659
|
: members.some((m) => selection.keys.has(keyOf(m.original)))
|
|
656
660
|
? 'indeterminate'
|
|
657
|
-
: 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 ? (_jsxs(TableCell, { colSpan:
|
|
661
|
+
: 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 ? (_jsxs(TableCell, { colSpan: leadSpan, className: PINNED_GROUP, children: [_jsxs(Button, { variant: "ghost", size: "xs", className: "-mx-2 hover:bg-transparent aria-expanded:bg-transparent", "aria-expanded": open, "aria-label": `${open ? 'Collapse' : 'Expand'} ${key}`, onClick: () => serverGroups
|
|
658
662
|
? serverGroups.onToggle(key)
|
|
659
663
|
: setFlipped((c) => {
|
|
660
664
|
const n = new Set(c);
|
package/dist/nav.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ComponentType, type ReactNode } from 'react';
|
|
2
2
|
import { type AssistantPageType } from './assistant-screen.js';
|
|
3
3
|
/** A nav icon: a lucide icon, or a view's emoji drawn at icon size. */
|
|
4
4
|
export type NavIcon = ComponentType<{
|
package/dist/nav.js
CHANGED
|
@@ -6,6 +6,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
6
6
|
// the name, a click folds it, the fold is remembered in the browser, and `+` never folds.
|
|
7
7
|
import { Link, useRouterState } from '@tanstack/react-router';
|
|
8
8
|
import { ChevronRightIcon, PlusIcon, Trash2Icon } from 'lucide-react';
|
|
9
|
+
import { useState } from 'react';
|
|
9
10
|
import { useAssistantPages } from './assistant-screen.js';
|
|
10
11
|
import { LumenConfirm } from './confirm.js';
|
|
11
12
|
import { useFolded } from './fold.js';
|
|
@@ -66,7 +67,8 @@ function SectionRow({ item, activeId, onRemove, removeLabel, }) {
|
|
|
66
67
|
const isActive = activeId === item.id;
|
|
67
68
|
// The open item shows no trash icon.
|
|
68
69
|
const remove = item.fixed || isActive ? undefined : onRemove;
|
|
69
|
-
const
|
|
70
|
+
const [open, setOpen] = useState(Boolean(item.defaultOpen || subActive || isActive));
|
|
71
|
+
const row = (_jsxs(_Fragment, { children: [_jsx(SidebarMenuButton, { asChild: true, tooltip: item.title, isActive: isActive, children: _jsxs(Link, { to: item.to, onClick: subs.length ? () => setOpen((o) => (isActive ? !o : true)) : undefined, children: [_jsx(item.icon, {}), _jsx("span", { children: item.title })] }) }), item.badge !== undefined && item.badge !== null && (_jsx(SidebarMenuBadge, { className: cn(SECTION_BADGE,
|
|
70
72
|
// The remove icon shows on hover from md up, and always below it: make room there.
|
|
71
73
|
remove &&
|
|
72
74
|
'max-md:right-7 md:group-focus-within/menu-item:opacity-0 md:group-hover/menu-item:opacity-0'), children: item.badge })), remove && (_jsx(LumenConfirm, { title: `${removeLabel(item)}?`, action: "Remove", onConfirm: () => remove(item), children: _jsx(SidebarMenuAction, { showOnHover: true, "aria-label": removeLabel(item), title: removeLabel(item),
|
|
@@ -74,7 +76,7 @@ function SectionRow({ item, activeId, onRemove, removeLabel, }) {
|
|
|
74
76
|
className: "text-muted-foreground peer-hover/menu-button:text-muted-foreground hover:bg-transparent hover:text-destructive", children: _jsx(Trash2Icon, {}) }) }))] }));
|
|
75
77
|
if (!subs.length)
|
|
76
78
|
return _jsx(SidebarMenuItem, { children: row });
|
|
77
|
-
return (_jsx(Collapsible, { asChild: true,
|
|
79
|
+
return (_jsx(Collapsible, { asChild: true, open: open, onOpenChange: setOpen, className: "group/collapsible", children: _jsxs(SidebarMenuItem, { children: [row, _jsx(CollapsibleTrigger, { asChild: true, children: _jsx(SidebarMenuAction, { "aria-label": `Show or hide ${item.title}`, className: "right-auto left-1.5 bg-sidebar opacity-0 group-hover/menu-item:opacity-100 focus-visible:opacity-100 peer-data-[active=true]/menu-button:bg-sidebar-accent [&>svg]:transition-transform data-[state=open]:[&>svg]:rotate-90", children: _jsx(ChevronRightIcon, {}) }) }), _jsx(CollapsibleContent, { children: _jsx(SidebarMenuSub, { className: "mr-0 pr-0", children: subs.map((sub) => {
|
|
78
80
|
const hasBadge = sub.badge !== undefined && sub.badge !== null;
|
|
79
81
|
return (_jsxs(SidebarMenuSubItem, { children: [_jsx(SidebarMenuSubButton, { asChild: true, isActive: activeId === `${item.id}/${sub.id}`, className: cn(hasBadge && 'pr-8'), children: _jsxs(Link, { to: sub.to, title: sub.title, children: [sub.icon ? (_jsx("span", { "aria-hidden": true, className: "flex shrink-0 [&>svg]:size-4", style: { color: sub.color }, children: _jsx(sub.icon, {}) })) : (sub.color && (_jsx("span", { "aria-hidden": true, className: "mx-1 size-2 shrink-0 rounded-full", style: { background: sub.color } }))), _jsx("span", { children: sub.title })] }) }), hasBadge && (_jsx("span", { className: cn('pointer-events-none absolute top-1 right-1 flex h-5 min-w-5 items-center justify-center px-1 text-xs text-muted-foreground', typeof sub.badge === 'number' && 'num'), children: sub.badge }))] }, sub.id));
|
|
80
82
|
}) }) })] }) }));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@columenlabs/lumen-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.49",
|
|
4
4
|
"description": "Lumen: shared React components (frame, views, widgets, charts, drawers).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"@codemirror/lang-javascript": "6.2.5",
|
|
42
42
|
"@codemirror/lang-json": "6.0.2",
|
|
43
43
|
"@codemirror/lang-sql": "6.10.0",
|
|
44
|
-
"@columenlabs/lumen-ui-types": "0.1.
|
|
44
|
+
"@columenlabs/lumen-ui-types": "0.1.49",
|
|
45
45
|
"@dnd-kit/core": "6.3.1",
|
|
46
46
|
"@dnd-kit/sortable": "10.0.0",
|
|
47
47
|
"@dnd-kit/utilities": "3.2.2",
|