@columenlabs/lumen-ui 0.1.103 → 0.1.105
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/lumen-selector.d.ts +2 -0
- package/dist/lumen-selector.js +6 -2
- package/dist/lumen-view.js +14 -3
- package/dist/shell.d.ts +1 -1
- package/dist/shell.js +2 -2
- package/dist/status.d.ts +3 -1
- package/dist/status.js +4 -4
- package/dist/theme.css +76 -0
- package/dist/tile.js +3 -1
- package/dist/ui/sidebar.js +1 -1
- package/dist/whats-new.js +2 -1
- package/package.json +2 -2
package/dist/lumen-selector.d.ts
CHANGED
|
@@ -47,6 +47,8 @@ interface Common<V extends string> {
|
|
|
47
47
|
/** The create row's text; `Add "…"` by default. */
|
|
48
48
|
createLabel?: (query: string) => string;
|
|
49
49
|
placeholder?: string;
|
|
50
|
+
/** What the closed field shows instead of the picked option's label (a zone's short name, PT). */
|
|
51
|
+
display?: ReactNode;
|
|
50
52
|
/** Shown when nothing matches. */
|
|
51
53
|
empty?: string;
|
|
52
54
|
/** Accessible name; also the phone sheet's title. */
|
package/dist/lumen-selector.js
CHANGED
|
@@ -149,7 +149,7 @@ function Rows({ shown, picked, multiple, full, expand, }) {
|
|
|
149
149
|
const FIELD = "flex w-fit min-w-0 items-center justify-between gap-1.5 rounded-lg border border-input bg-transparent pr-2 pl-2.5 text-sm whitespace-nowrap transition-colors outline-none select-none dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md),10px)] [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4";
|
|
150
150
|
/** The one picker. See the file header for what every use gets. */
|
|
151
151
|
export function LumenSelector(props) {
|
|
152
|
-
const { options, groups, trigger = 'field', icon: LeadIcon, search = 'auto', create, createLabel = (q) => `Add “${q}”`, placeholder = 'Pick one', empty = 'Nothing matches', label, size = 'default', className, disabled, header, align = 'start', id, } = props;
|
|
152
|
+
const { options, groups, trigger = 'field', icon: LeadIcon, search = 'auto', create, createLabel = (q) => `Add “${q}”`, placeholder = 'Pick one', display, empty = 'Nothing matches', label, size = 'default', className, disabled, header, align = 'start', id, } = props;
|
|
153
153
|
const multiple = props.multiple === true;
|
|
154
154
|
const values = multiple
|
|
155
155
|
? props.value
|
|
@@ -236,7 +236,11 @@ export function LumenSelector(props) {
|
|
|
236
236
|
};
|
|
237
237
|
const selected = multiple ? null : (byValue.get(values[0] ?? '') ?? null);
|
|
238
238
|
const Lead = selected?.icon ?? LeadIcon;
|
|
239
|
-
const fieldInside = (_jsxs(_Fragment, { children: [Lead && _jsx(Lead, { className: "text-muted-foreground" }), _jsx("span", { className: cn('min-w-0 flex-1 truncate text-left', !selected && !values.length && 'text-muted-foreground'), children: selected
|
|
239
|
+
const fieldInside = (_jsxs(_Fragment, { children: [Lead && _jsx(Lead, { className: "text-muted-foreground" }), _jsx("span", { className: cn('min-w-0 flex-1 truncate text-left', !selected && !values.length && 'text-muted-foreground'), children: selected
|
|
240
|
+
? (display ?? selected.content ?? selected.label)
|
|
241
|
+
: values[0]
|
|
242
|
+
? values[0]
|
|
243
|
+
: placeholder }), _jsx(ChevronDownIcon, { className: "pointer-events-none size-4 text-muted-foreground" })] }));
|
|
240
244
|
// On a desktop the popup is a column as tall as the screen allows: the search, header and
|
|
241
245
|
// footer keep their height and the list between them scrolls.
|
|
242
246
|
const list = (_jsxs(_Fragment, { children: [header && _jsx("div", { className: "shrink-0", children: header }), _jsx(Base.List, { className: cn('min-h-0 overscroll-contain p-1 data-empty:p-0', phone ? 'flex-1 overflow-y-auto pb-4' : 'max-h-88 flex-1 scroll-pt-8 overflow-y-auto'), children: _jsx(Rows, { shown: rows, picked: picked, multiple: multiple, full: full, expand: expand }) }), _jsx(Base.Empty, { className: "hidden px-2 py-3 text-center text-sm text-muted-foreground group-data-empty/selector:block", children: empty })] }));
|
package/dist/lumen-view.js
CHANGED
|
@@ -416,7 +416,7 @@ function LumenTableRows({ domain, columns = domain.fields, rows, groupBy = null,
|
|
|
416
416
|
// Columns a UnitValue has drawn in (`UnitColumns`): their cells share the numbers' edge.
|
|
417
417
|
const [units, setUnits] = useState(() => new Set());
|
|
418
418
|
const markUnits = useCallback((key) => setUnits((s) => (s.has(key) ? s : new Set(s).add(key))), []);
|
|
419
|
-
const
|
|
419
|
+
const colProps = colPropsOf(units);
|
|
420
420
|
const [flipped, setFlipped] = useState(new Set());
|
|
421
421
|
const [more, setMore] = useState(new Map());
|
|
422
422
|
// Under a sub-group's `flat` grouping each row names itself by its sub-group key instead of
|
|
@@ -430,6 +430,17 @@ function LumenTableRows({ domain, columns = domain.fields, rows, groupBy = null,
|
|
|
430
430
|
// Inline chips sit on the first column, next to the row's name, not in their own.
|
|
431
431
|
const inline = visible.filter((f, i) => i > 0 && chips.some((c) => c.key === f.key && c.placement === 'inline'));
|
|
432
432
|
const shown = visible.filter((f) => !inline.includes(f));
|
|
433
|
+
// On a narrow phone each row reads as a card (theme.css, `data-card`): its name and one figure
|
|
434
|
+
// (the sorted one, else the first number or date) on the first line, the rest on the second.
|
|
435
|
+
// Numbers and dates keep their width there; only text shortens.
|
|
436
|
+
const short = (f) => f.format !== 'text' || (rows[0] !== undefined && isDateText(f.value(rows[0])));
|
|
437
|
+
const meta = (shown.find((f, i) => i > 0 && f.key === sorting[0]?.id && short(f)) ??
|
|
438
|
+
shown.find((f, i) => i > 0 && short(f)))?.key;
|
|
439
|
+
const cardOf = (f) => f === shown[0] ? 'lead' : f.key === meta ? 'meta' : short(f) ? 'num' : 'text';
|
|
440
|
+
const col = (f, ...more) => ({
|
|
441
|
+
...colProps(f, ...more),
|
|
442
|
+
'data-card': f ? cardOf(f) : undefined,
|
|
443
|
+
});
|
|
433
444
|
const computed = useMemo(() => computedOf(domain, rows, groupBy), [domain, rows, groupBy]);
|
|
434
445
|
// Values per editable field, offered when a cell is edited: the field's own list when it has
|
|
435
446
|
// one (every value, in use or not), else the values in the shown rows.
|
|
@@ -585,7 +596,7 @@ function LumenTableRows({ domain, columns = domain.fields, rows, groupBy = null,
|
|
|
585
596
|
const allKeys = rows.map(keyOf);
|
|
586
597
|
const allOn = allKeys.length > 0 && allKeys.every((k) => selection?.keys.has(k));
|
|
587
598
|
const someOn = allKeys.some((k) => selection?.keys.has(k));
|
|
588
|
-
const selectCell = (key, name) => selection && (_jsx(TableCell, { className: "w-8 pr-0", onClick: (e) => e.stopPropagation(), children: _jsx(Checkbox, { checked: selection.keys.has(key), onCheckedChange: (on) => toggle([key], on === true), "aria-label": `Select ${name}` }) }));
|
|
599
|
+
const selectCell = (key, name) => selection && (_jsx(TableCell, { "data-card": "select", className: "w-8 pr-0", onClick: (e) => e.stopPropagation(), children: _jsx(Checkbox, { checked: selection.keys.has(key), onCheckedChange: (on) => toggle([key], on === true), "aria-label": `Select ${name}` }) }));
|
|
589
600
|
// The rows whose open state the person flipped: a sub-group that starts open (`open`) lists
|
|
590
601
|
// the closed ones, any other the open ones.
|
|
591
602
|
const [partFlips, setOpenParts] = useState(new Set());
|
|
@@ -709,7 +720,7 @@ function LumenTableRows({ domain, columns = domain.fields, rows, groupBy = null,
|
|
|
709
720
|
};
|
|
710
721
|
const box = useRef(null);
|
|
711
722
|
useFloatingHead(box, !inView);
|
|
712
|
-
return (_jsx("div", { ref: box, className: cn(inView ? IN_VIEW : 'overflow-x-auto overflow-y-hidden [--pin-top:0px]', fit && FIT), children: _jsx(UnitColumns.Provider, { value: markUnits, children: _jsxs(Table, { children: [_jsx(TableHeader, { children: table.getHeaderGroups().map((hg) => (_jsxs(TableRow, { children: [selection && (_jsx(TableHead, { className: cn('w-8 pr-0', PINNED_HEAD), children: _jsx(Checkbox, { checked: allOn ? true : someOn ? 'indeterminate' : false, onCheckedChange: (on) => toggle(allKeys, on === true), "aria-label": "Select every row" }) })), hg.headers.map((h, i) => (_jsxs(TableHead, { ...col(shown[i], PINNED_HEAD), style: ruleOf(colorOf(domain, shown[i])), onPointerDown: pressStart, onPointerUp: pressEnd, onPointerLeave: pressEnd, onPointerCancel: pressEnd, onContextMenu: (e) => {
|
|
723
|
+
return (_jsx("div", { ref: box, className: cn(inView ? IN_VIEW : 'overflow-x-auto overflow-y-hidden [--pin-top:0px]', fit && FIT), children: _jsx(UnitColumns.Provider, { value: markUnits, children: _jsxs(Table, { "data-cards": "", children: [_jsx(TableHeader, { children: table.getHeaderGroups().map((hg) => (_jsxs(TableRow, { children: [selection && (_jsx(TableHead, { className: cn('w-8 pr-0', PINNED_HEAD), children: _jsx(Checkbox, { checked: allOn ? true : someOn ? 'indeterminate' : false, onCheckedChange: (on) => toggle(allKeys, on === true), "aria-label": "Select every row" }) })), hg.headers.map((h, i) => (_jsxs(TableHead, { ...col(shown[i], PINNED_HEAD), style: ruleOf(colorOf(domain, shown[i])), onPointerDown: pressStart, onPointerUp: pressEnd, onPointerLeave: pressEnd, onPointerCancel: pressEnd, onContextMenu: (e) => {
|
|
713
724
|
if (press.current)
|
|
714
725
|
e.preventDefault();
|
|
715
726
|
}, "aria-sort": h.column.getIsSorted() === 'asc'
|
package/dist/shell.d.ts
CHANGED
|
@@ -98,7 +98,7 @@ export interface LumenShellProps {
|
|
|
98
98
|
children: ReactNode;
|
|
99
99
|
}
|
|
100
100
|
export declare function LumenShell({ app, appLabel, brand, nav, status, health, statusEnd, releases, releaseApps, settings, shared, sharedLabel, settingsGroups, user, signOutUrl, profile, security, onRefresh, hideAmounts, onLogs, headerActions, panel, finder, wrap, children, }: LumenShellProps): import("react").JSX.Element;
|
|
101
|
-
/** A status bar entry: an optional health dot, a label and
|
|
101
|
+
/** A status bar entry: an optional health dot, a label and its value ("Mail · Up to date"). */
|
|
102
102
|
export declare function StatusItem({ label, value, ok, className, }: {
|
|
103
103
|
label: ReactNode;
|
|
104
104
|
value?: ReactNode;
|
package/dist/shell.js
CHANGED
|
@@ -141,7 +141,7 @@ export function LumenShell({ app, appLabel, brand, nav, status, health, statusEn
|
|
|
141
141
|
headerActions,
|
|
142
142
|
}, children: _jsx(SourceHealthProvider, { children: wrap(_jsx(TooltipProvider, { delayDuration: 200, children: _jsx(SidebarProvider, { defaultOpen: sidebarOpen(), children: _jsxs(LumenFinder, { app: app, settings: findSettings, openSettings: openSettings, config: finder, children: [_jsx(CloseDrawerOnNavigate, {}), _jsxs(Sidebar, { collapsible: "icon", children: [_jsx(SidebarHeader, { children: brand }), _jsx(SidebarContent, { children: nav }), _jsx(SidebarFooter, { children: _jsx(NavUser, { who: user, signOutUrl: signOutUrl, onSettings: () => openSettings(), onLogs: onLogs }) }), _jsx(SidebarRail, {})] }), _jsxs(SidebarInset, { className: "flex min-h-svh min-w-0 flex-col", children: [_jsx("div", { className: "flex min-w-0 flex-1 flex-col", children: children }, epoch), _jsxs("footer", { "data-slot": "shell-status", "aria-label": "Status", className: "sticky bottom-0 z-10 flex h-(--shell-status-height) shrink-0 items-center gap-4 overflow-x-auto overflow-y-hidden border-t bg-background px-(--shell-gutter) text-xs whitespace-nowrap text-muted-foreground", children: [status, _jsx("div", { ref: setStatusSlot, className: "flex shrink-0 items-center gap-4" }), _jsxs("span", { className: "ml-auto flex shrink-0 items-center gap-1", children: [health, releases && _jsx(WhatsNew, { api: releases, apps: releaseApps }), statusEnd, _jsx(StatusTimeZone, {})] })] })] }), panel, _jsx(LumenSettings, { open: settingsAt !== null, section: settingsAt ?? lastSection, onOpen: openSettings, onClose: () => setSettingsAt(null), appLabel: appLabel, profile: profile, security: security, sections: settings, shared: shared, sharedLabel: sharedLabel, groups: settingsGroups }), _jsx(Toaster, { position: "bottom-right" })] }) }) })) }) }));
|
|
143
143
|
}
|
|
144
|
-
/** A status bar entry: an optional health dot, a label and
|
|
144
|
+
/** A status bar entry: an optional health dot, a label and its value ("Mail · Up to date"). */
|
|
145
145
|
export function StatusItem({ label, value, ok, className, }) {
|
|
146
|
-
return (_jsxs("span", { className: cn('flex items-center gap-1.5', value === undefined && 'min-w-0', className), children: [ok !== undefined && (_jsx("span", { "aria-hidden": true, className: cn('size-2 shrink-0 rounded-full', ok === null ? 'bg-muted-foreground/40' : ok ? 'bg-gain' : 'bg-destructive') })), _jsx("span", { className: value === undefined ? 'min-w-0 truncate' : 'max-md:hidden', children: label }), value !== undefined && (_jsxs(_Fragment, { children: [
|
|
146
|
+
return (_jsxs("span", { className: cn('flex items-center gap-1.5', value === undefined && 'min-w-0', className), children: [ok !== undefined && (_jsx("span", { "aria-hidden": true, className: cn('size-2 shrink-0 rounded-full', ok === null ? 'bg-muted-foreground/40' : ok ? 'bg-gain' : 'bg-destructive') })), _jsx("span", { className: value === undefined ? 'min-w-0 truncate' : 'max-md:hidden', children: label }), value !== undefined && (_jsxs(_Fragment, { children: [_jsx("span", { "aria-hidden": true, className: "max-md:hidden", children: "\u00B7" }), _jsx("span", { className: "shrink-0 tabular-nums text-foreground/80", children: value })] }))] }));
|
|
147
147
|
}
|
package/dist/status.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/** Time zone picker: on the right of the status bar, and in Settings. */
|
|
2
|
-
export declare function TimeZoneSelect({ className, }: {
|
|
2
|
+
export declare function TimeZoneSelect({ className, short, }: {
|
|
3
3
|
className?: string;
|
|
4
|
+
/** Shows the zone's short name (PT), the way every time on the page names it. */
|
|
5
|
+
short?: boolean;
|
|
4
6
|
/** Kept for callers; the list flips upward by itself where there is no room below. */
|
|
5
7
|
side?: 'top' | 'bottom';
|
|
6
8
|
}): import("react").JSX.Element;
|
package/dist/status.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { cn } from './lib/utils.js';
|
|
3
3
|
import { LumenSelector } from './lumen-selector.js';
|
|
4
|
-
import { TIME_ZONES, setPref, usePrefs } from './prefs.js';
|
|
4
|
+
import { TIME_ZONES, setPref, usePrefs, zoneAbbr } from './prefs.js';
|
|
5
5
|
/** Time zone picker: on the right of the status bar, and in Settings. */
|
|
6
|
-
export function TimeZoneSelect({ className, }) {
|
|
6
|
+
export function TimeZoneSelect({ className, short, }) {
|
|
7
7
|
const { timeZone } = usePrefs();
|
|
8
|
-
return (_jsx(LumenSelector, { label: "Time zone", size: "sm", className: className, options: TIME_ZONES.map((z) => ({ value: z.id, label: z.label })), value: timeZone, onChange: (v) => setPref('timeZone', v) }));
|
|
8
|
+
return (_jsx(LumenSelector, { label: "Time zone", size: "sm", className: className, options: TIME_ZONES.map((z) => ({ value: z.id, label: z.label })), value: timeZone, display: short ? zoneAbbr(timeZone) : undefined, onChange: (v) => setPref('timeZone', v) }));
|
|
9
9
|
}
|
|
10
10
|
export function StatusTimeZone({ className }) {
|
|
11
|
-
return (_jsx("span", { className: cn('flex items-center', className), children: _jsx(TimeZoneSelect, { side: "top", className: "h-6 border-0 px-1.5 text-xs shadow-none" }) }));
|
|
11
|
+
return (_jsx("span", { className: cn('flex items-center', className), children: _jsx(TimeZoneSelect, { side: "top", short: true, className: "h-6 border-0 px-1.5 text-xs shadow-none" }) }));
|
|
12
12
|
}
|
package/dist/theme.css
CHANGED
|
@@ -414,6 +414,82 @@
|
|
|
414
414
|
left: 100%;
|
|
415
415
|
}
|
|
416
416
|
|
|
417
|
+
/* A LumenView table on a narrow phone (`data-cards`): each row is a card, so nothing runs off
|
|
418
|
+
the right edge. Line 1: the row's name (shortens) and one figure (`data-card="meta"`); line 2:
|
|
419
|
+
the other values, small and muted. Numbers, dates and their units keep their width; only text
|
|
420
|
+
shortens, in an ellipsis. Group, Total and More rows span the card's width. */
|
|
421
|
+
@media (max-width: 639px) {
|
|
422
|
+
table[data-cards],
|
|
423
|
+
table[data-cards] > tbody,
|
|
424
|
+
table[data-cards] > tfoot {
|
|
425
|
+
display: block;
|
|
426
|
+
width: 100%;
|
|
427
|
+
}
|
|
428
|
+
/* A wide table widens its card (the page scrolls sideways); a card table fits the view. */
|
|
429
|
+
table[data-cards] {
|
|
430
|
+
max-width: calc(var(--view-w, 100%) - 2px);
|
|
431
|
+
}
|
|
432
|
+
table[data-cards] > thead {
|
|
433
|
+
display: none;
|
|
434
|
+
}
|
|
435
|
+
table[data-cards] tr {
|
|
436
|
+
display: flex;
|
|
437
|
+
flex-wrap: wrap;
|
|
438
|
+
align-items: baseline;
|
|
439
|
+
column-gap: 0.75rem;
|
|
440
|
+
row-gap: 0.125rem;
|
|
441
|
+
padding: 0.5rem var(--cell-x);
|
|
442
|
+
}
|
|
443
|
+
table[data-cards] td {
|
|
444
|
+
padding: 0 !important;
|
|
445
|
+
width: auto !important;
|
|
446
|
+
min-width: 0;
|
|
447
|
+
}
|
|
448
|
+
table[data-cards] td[colspan] {
|
|
449
|
+
flex: 1 1 100%;
|
|
450
|
+
}
|
|
451
|
+
table[data-cards] td:not([data-card]):not([colspan]) {
|
|
452
|
+
display: none;
|
|
453
|
+
}
|
|
454
|
+
table[data-cards] td[data-card="select"] {
|
|
455
|
+
order: 0;
|
|
456
|
+
flex: none;
|
|
457
|
+
align-self: center;
|
|
458
|
+
}
|
|
459
|
+
table[data-cards] td[data-card="lead"] {
|
|
460
|
+
order: 1;
|
|
461
|
+
flex: 1 1 0;
|
|
462
|
+
overflow: hidden;
|
|
463
|
+
font-weight: 500;
|
|
464
|
+
}
|
|
465
|
+
table[data-cards] td[data-card="meta"] {
|
|
466
|
+
order: 2;
|
|
467
|
+
flex: none;
|
|
468
|
+
}
|
|
469
|
+
table[data-cards] tr:has(> td[data-card="lead"])::after {
|
|
470
|
+
content: "";
|
|
471
|
+
order: 3;
|
|
472
|
+
flex-basis: 100%;
|
|
473
|
+
}
|
|
474
|
+
table[data-cards] td:is([data-card="num"], [data-card="text"]) {
|
|
475
|
+
order: 4;
|
|
476
|
+
font-size: 0.8125rem;
|
|
477
|
+
color: var(--muted-foreground);
|
|
478
|
+
}
|
|
479
|
+
table[data-cards] td[data-card="num"] {
|
|
480
|
+
flex: none;
|
|
481
|
+
}
|
|
482
|
+
table[data-cards] td[data-card="text"] {
|
|
483
|
+
flex: 0 1 auto;
|
|
484
|
+
overflow: hidden;
|
|
485
|
+
text-overflow: ellipsis;
|
|
486
|
+
}
|
|
487
|
+
table[data-cards] [data-slot="unit-value-unit"] {
|
|
488
|
+
position: static;
|
|
489
|
+
margin-left: 0.25em;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
417
493
|
@layer utilities {
|
|
418
494
|
.bg-gap-hatch {
|
|
419
495
|
background: repeating-linear-gradient(135deg, var(--gap) 0 3px, var(--gap-soft) 3px 6px);
|
package/dist/tile.js
CHANGED
|
@@ -15,8 +15,10 @@ export function TileSpark({ values, color = 'var(--chart-1)', className, }) {
|
|
|
15
15
|
// Inline size: a parent's `[&_svg]:size-4` (toggle items, buttons) must not shrink it.
|
|
16
16
|
style: { width: '100%', height: '1.5rem' }, className: cn('overflow-visible', className), children: [_jsx("polygon", { points: `0,24 ${pts.join(' ')} 100,24`, fill: color, fillOpacity: 0.15 }), _jsx("polyline", { points: pts.join(' '), fill: "none", stroke: color, strokeWidth: 1.5, strokeLinejoin: "round", vectorEffect: "non-scaling-stroke" })] }));
|
|
17
17
|
}
|
|
18
|
+
/** A value with no digit in it ("Workers Paid", "Healthy") is a word, not a number. */
|
|
19
|
+
const isWord = (v) => typeof v === 'string' && !/\d/.test(v);
|
|
18
20
|
function TileBody({ label, value, caption, lines, valueClassName, spark, sparkColor, }) {
|
|
19
|
-
return (_jsxs(_Fragment, { children: [_jsx("span", { className: "w-full truncate text-xs text-muted-foreground", children: label }), _jsx("span", { className: cn('num truncate text-xl leading-7 font-semibold', valueClassName), children: value }), caption ? (_jsx("span", { className: "
|
|
21
|
+
return (_jsxs(_Fragment, { children: [_jsx("span", { className: "w-full truncate text-xs text-muted-foreground", children: label }), _jsx("span", { className: cn(!isWord(value) && 'num', 'truncate text-xl leading-7 font-semibold', valueClassName), children: value }), caption ? (_jsx("span", { className: "line-clamp-2 text-xs text-muted-foreground tabular-nums", children: caption })) : null, lines?.map((l, i) => (_jsx("span", { className: "num line-clamp-2 text-xs text-muted-foreground", children: l }, i))), spark ? _jsx(TileSpark, { values: spark, color: sparkColor, className: "mt-1 self-stretch" }) : null] }));
|
|
20
22
|
}
|
|
21
23
|
export function Tile({ label, value, caption, lines, className, valueClassName, spark, sparkColor, }) {
|
|
22
24
|
return (_jsx(Card, { size: "sm", className: cn('min-w-0 flex-1 basis-2/5 gap-0.5 px-3 lg:basis-auto', TILE_WIDTH, className), children: _jsx(TileBody, { label: label, value: value, caption: caption, lines: lines, valueClassName: valueClassName, spark: spark, sparkColor: sparkColor }) }));
|
package/dist/ui/sidebar.js
CHANGED
|
@@ -82,7 +82,7 @@ function Sidebar({ side = 'left', variant = 'sidebar', collapsible = 'offcanvas'
|
|
|
82
82
|
return (_jsx("div", { "data-slot": "sidebar", className: cn('flex h-full w-(--sidebar-width) flex-col bg-sidebar text-sidebar-foreground', className), ...props, children: children }));
|
|
83
83
|
}
|
|
84
84
|
if (isMobile) {
|
|
85
|
-
return (_jsxs(_Fragment, { children: [!openMobile && _jsx("div", { hidden: true, children: children }), _jsx(Sheet, { open: openMobile, onOpenChange: setOpenMobile, ...props, children: _jsxs(SheetContent, { dir: dir, "data-sidebar": "sidebar", "data-slot": "sidebar", "data-mobile": "true", className: "w-(--sidebar-width) bg-sidebar p-0 text-sidebar-foreground [
|
|
85
|
+
return (_jsxs(_Fragment, { children: [!openMobile && _jsx("div", { hidden: true, children: children }), _jsx(Sheet, { open: openMobile, onOpenChange: setOpenMobile, ...props, children: _jsxs(SheetContent, { dir: dir, "data-sidebar": "sidebar", "data-slot": "sidebar", "data-mobile": "true", className: "w-(--sidebar-width) bg-sidebar p-0 text-sidebar-foreground [&_[data-sidebar=header]]:pr-11", style: {
|
|
86
86
|
'--sidebar-width': SIDEBAR_WIDTH_MOBILE,
|
|
87
87
|
}, side: side, children: [_jsxs(SheetHeader, { className: "sr-only", children: [_jsx(SheetTitle, { children: "Sidebar" }), _jsx(SheetDescription, { children: "Displays the mobile sidebar." })] }), _jsx("div", { className: "flex h-full w-full flex-col", children: children })] }) })] }));
|
|
88
88
|
}
|
package/dist/whats-new.js
CHANGED
|
@@ -8,6 +8,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
8
8
|
// takeaway carries a chip of the app it is about (`apps` on the takeaway; none: everyone), and a
|
|
9
9
|
// release where one app has `GROUP_AT` or more is split into one sub-section per app.
|
|
10
10
|
import { useInfiniteQuery } from '@tanstack/react-query';
|
|
11
|
+
import { SparklesIcon } from 'lucide-react';
|
|
11
12
|
import { useState } from 'react';
|
|
12
13
|
import Markdown from 'react-markdown';
|
|
13
14
|
import { DrawerSection } from './drawer-section.js';
|
|
@@ -57,7 +58,7 @@ export function WhatsNew({ api, apps, everyone = 'Global', }) {
|
|
|
57
58
|
const releases = q.data.pages.flatMap((p) => p ?? []);
|
|
58
59
|
const latest = releases[0];
|
|
59
60
|
const day = (at) => new Date(at).toLocaleDateString('en-US', { month: 'short', day: 'numeric', timeZone });
|
|
60
|
-
return (_jsxs(_Fragment, { children: [
|
|
61
|
+
return (_jsxs(_Fragment, { children: [_jsxs(Button, { variant: "ghost", size: "sm", className: "h-6 gap-1 px-1.5 text-muted-foreground text-xs", onClick: () => setOpen(true), title: latest ? `What's new · ${latest.tag}` : "What's new", "aria-label": "What's new", children: [_jsx(SparklesIcon, { className: "size-3.5" }), _jsx("span", { className: "max-md:hidden", children: "What's new" })] }), _jsxs(LumenDrawer, { open: open, onClose: () => setOpen(false), title: "What's new", description: "Key takeaways of each release, newest first", footerStart: releases.length ? `${releases.length} releases` : undefined, footer: _jsxs(_Fragment, { children: [q.hasNextPage && (_jsx(Button, { variant: "outline", size: "sm", disabled: q.isFetchingNextPage, onClick: () => q.fetchNextPage(), children: "Show more" })), _jsx(Button, { size: "sm", onClick: () => setOpen(false), children: "Close" })] }), children: [!releases.length && (_jsx("p", { className: "py-2 text-muted-foreground text-sm", children: "No releases yet. Each release's key takeaways show here once it ships." })), releases.map((r, i) => (_jsx(DrawerSection, { title: r.tag, hint: day(r.published_at), defaultOpen: i === 0, children: r.takeaways.length ? (_jsx(Notes, { takeaways: r.takeaways, apps: apps, everyone: everyone })) : (_jsx("p", { className: "py-1 pl-1 text-muted-foreground text-sm", children: "Nothing new for you in this one." })) }, r.tag)))] })] }));
|
|
61
62
|
}
|
|
62
63
|
/** One release's takeaways: a flat list, or one sub-section per app once one app has many. */
|
|
63
64
|
function Notes({ takeaways, apps, everyone, }) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@columenlabs/lumen-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.105",
|
|
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.105",
|
|
53
53
|
"@dnd-kit/core": "6.3.1",
|
|
54
54
|
"@dnd-kit/sortable": "10.0.0",
|
|
55
55
|
"@dnd-kit/utilities": "3.2.2",
|