@columenlabs/lumen-ui 0.1.107 → 0.1.109
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/coverage-map.js +41 -6
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/ink.d.ts +7 -0
- package/dist/ink.js +39 -0
- package/dist/lumen-layout.js +3 -1
- package/dist/lumen-tabs.js +3 -1
- package/dist/lumen-view.js +13 -2
- package/dist/lumen-widget.d.ts +4 -1
- package/dist/lumen-widget.js +2 -2
- package/dist/scroll-fade.d.ts +10 -0
- package/dist/scroll-fade.js +32 -0
- package/dist/shell.js +1 -1
- package/dist/theme.css +34 -0
- package/dist/view-model.d.ts +4 -0
- package/package.json +2 -2
package/dist/coverage-map.js
CHANGED
|
@@ -9,6 +9,7 @@ import { cn } from './lib/utils.js';
|
|
|
9
9
|
import { LumenStates, useLumenSource } from './lumen-source.js';
|
|
10
10
|
import { LumenWidget } from './lumen-widget.js';
|
|
11
11
|
import { LumenPager } from './pager.js';
|
|
12
|
+
import { useScrollFade } from './scroll-fade.js';
|
|
12
13
|
import { Badge } from './ui/badge.js';
|
|
13
14
|
import { Button } from './ui/button.js';
|
|
14
15
|
import { Skeleton } from './ui/skeleton.js';
|
|
@@ -185,7 +186,9 @@ const MapRow = memo(function MapRow({ r, cols, marks, picks, start, previews, to
|
|
|
185
186
|
const link = look.link?.(r.row);
|
|
186
187
|
return (_jsxs(_Fragment, { children: [_jsxs("span", {
|
|
187
188
|
// Rows sit indented under their group's name.
|
|
188
|
-
className: cn(
|
|
189
|
+
className: cn(look.mobile
|
|
190
|
+
? 'col-span-full -mb-0.5 flex min-w-0'
|
|
191
|
+
: 'sticky left-0 z-1 flex min-w-0 flex-col bg-card pr-2', look.groupBy && (look.mobile ? 'pl-1.5' : 'pl-3')), children: [_jsxs("span", { className: cn('flex min-w-0 items-center gap-1.5', look.mobile && 'sticky left-0 max-w-[calc(var(--view-w,100vw)-4rem)] text-xs', look.mobile && look.inset && 'left-8'), children: [link ? (_jsx(Link, { ...link, className: "num truncate font-medium hover:text-primary hover:underline", children: text })) : (_jsx("span", { className: "num truncate font-medium", children: text })), r.badge] }), r.sub && !look.mobile && (_jsx("span", { className: "truncate text-[11px] text-muted-foreground", children: r.sub }))] }), cols.map((k, i) => {
|
|
189
192
|
const m = marks[i];
|
|
190
193
|
if (!m)
|
|
191
194
|
return _jsx("span", {}, k);
|
|
@@ -223,6 +226,7 @@ export function LumenCoverageMap({ source, title, icon = Grid3x3Icon, rows: rowS
|
|
|
223
226
|
setStart(null);
|
|
224
227
|
}, [preselect]);
|
|
225
228
|
const scroller = useRef(null);
|
|
229
|
+
const edge = useScrollFade(scroller);
|
|
226
230
|
const [pageSize, setPageSize] = useState(initialPageSize ?? 0);
|
|
227
231
|
const [pageIndex, setPageIndex] = useState(0);
|
|
228
232
|
const folded = useMemo(() => (data ? fold(data, block) : null), [data, block]);
|
|
@@ -242,7 +246,8 @@ export function LumenCoverageMap({ source, title, icon = Grid3x3Icon, rows: rowS
|
|
|
242
246
|
useLayoutEffect(() => {
|
|
243
247
|
const el = scroller.current;
|
|
244
248
|
if (el && layout === 'grid')
|
|
245
|
-
el
|
|
249
|
+
snapToEnd(el);
|
|
250
|
+
edge.update();
|
|
246
251
|
}, [strip]);
|
|
247
252
|
const sums = mobile ? summary.filter((s) => s === 'complete') : summary;
|
|
248
253
|
const rowsHead = `${unit[0]?.toUpperCase() ?? ''}${unit.slice(1)}`;
|
|
@@ -444,7 +449,8 @@ export function LumenCoverageMap({ source, title, icon = Grid3x3Icon, rows: rowS
|
|
|
444
449
|
// sideways with the names column pinned, opening at today.
|
|
445
450
|
const cell = `minmax(${CELL_MIN}px, 1fr)`;
|
|
446
451
|
const grid = {
|
|
447
|
-
|
|
452
|
+
// A phone puts each name on its own line above its blocks, so blocks never run under it.
|
|
453
|
+
gridTemplateColumns: `${mobile ? '' : `${label} `}repeat(${cols.length}, ${cell}) ${sums.map((s) => SUMMARY_WIDTH[s]).join(' ')}`,
|
|
448
454
|
};
|
|
449
455
|
const allRows = data.rows.length
|
|
450
456
|
? data.rows
|
|
@@ -460,9 +466,17 @@ export function LumenCoverageMap({ source, title, icon = Grid3x3Icon, rows: rowS
|
|
|
460
466
|
const row = id.slice(0, cut);
|
|
461
467
|
pickedBy.set(row, [...(pickedBy.get(row) ?? []), id.slice(cut + 1)]);
|
|
462
468
|
}
|
|
463
|
-
const look = {
|
|
469
|
+
const look = {
|
|
470
|
+
mobile,
|
|
471
|
+
inset: edge.side === 'start' || edge.side === 'both',
|
|
472
|
+
groupBy,
|
|
473
|
+
select,
|
|
474
|
+
sums,
|
|
475
|
+
drilled,
|
|
476
|
+
link: rowSpec.link,
|
|
477
|
+
};
|
|
464
478
|
let group;
|
|
465
|
-
body = (_jsxs("div", { className: cn('grid min-w-min items-center gap-[3px]', fade), style: grid, ...routes, children: [_jsx("span", { className: "sticky left-0 z-1 bg-card text-xs text-muted-foreground", children:
|
|
479
|
+
body = (_jsxs("div", { className: cn('grid min-w-min items-center gap-[3px]', fade), style: grid, ...routes, children: [!mobile && (_jsx("span", { className: "sticky left-0 z-1 bg-card text-xs text-muted-foreground", children: rowSpec.title })), cols.map((k) => (_jsx("span", { "data-col": "", className: "num h-4 overflow-visible text-[11px] whitespace-nowrap text-muted-foreground", children: headLabel(k, block, cols.length) }, k))), sums.map((s) => (_jsx("span", { className: "pl-3 text-right text-xs text-muted-foreground", children: s === 'rows' || s === 'counts' ? rowsHead : SUMMARY_HEAD[s] }, s))), rowList.flatMap((r) => {
|
|
466
480
|
const out = [];
|
|
467
481
|
const shut = groupBy && folds.has(foldKey + (r.group ?? ''));
|
|
468
482
|
if (groupBy && r.group !== group) {
|
|
@@ -496,6 +510,7 @@ export function LumenCoverageMap({ source, title, icon = Grid3x3Icon, rows: rowS
|
|
|
496
510
|
r.last,
|
|
497
511
|
JSON.stringify(rowSpec.link?.(r.row) ?? null),
|
|
498
512
|
mobile,
|
|
513
|
+
look.inset,
|
|
499
514
|
groupBy,
|
|
500
515
|
select,
|
|
501
516
|
sums.join(),
|
|
@@ -545,10 +560,30 @@ export function LumenCoverageMap({ source, title, icon = Grid3x3Icon, rows: rowS
|
|
|
545
560
|
: `Tap a ${word}, then another to pick the range` })) }), pickedCount > 0 ? (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: () => {
|
|
546
561
|
setPicked(new Set());
|
|
547
562
|
setStart(null);
|
|
548
|
-
}, children: "Clear" }), removeCount > 0 && (_jsxs(Button, { variant: "outline", size: "sm", disabled: queueing, onClick: () => void queue(true), children: [_jsx(ListXIcon, {}), " Remove from queue \u00B7 ", months(removeCount)] })), queueCount > 0 && (_jsxs(Button, { size: "sm", disabled: queueing || !onQueue, onClick: () => void queue(), children: [_jsx(ListPlusIcon, {}), " ", queueLabel, " \u00B7 ", months(queueCount)] }))] })) : (pickTools)] })), _jsx("div", { ref: scroller, className: "overflow-x-auto overflow-y-hidden pb-1", children: _jsx(CoverageTip, { content: tipFor, className: layout === 'months' ? 'min-w-0' : 'w-max min-w-full', children: body }) }), _jsx("div", { className: "border-t pt-3", children: _jsx(CoverageLegend, { levels: levels, labels: stateLabels, hint: hint ??
|
|
563
|
+
}, children: "Clear" }), removeCount > 0 && (_jsxs(Button, { variant: "outline", size: "sm", disabled: queueing, onClick: () => void queue(true), children: [_jsx(ListXIcon, {}), " Remove from queue \u00B7 ", months(removeCount)] })), queueCount > 0 && (_jsxs(Button, { size: "sm", disabled: queueing || !onQueue, onClick: () => void queue(), children: [_jsx(ListPlusIcon, {}), " ", queueLabel, " \u00B7 ", months(queueCount)] }))] })) : (pickTools)] })), _jsx("div", { ref: scroller, onScroll: edge.update, "data-fade": edge.side, className: "overflow-x-auto overflow-y-hidden pb-1", children: _jsx(CoverageTip, { content: tipFor, className: layout === 'months' ? 'min-w-0' : 'w-max min-w-full', children: body }) }), _jsx("div", { className: "border-t pt-3", children: _jsx(CoverageLegend, { levels: levels, labels: stateLabels, hint: hint ??
|
|
549
564
|
(select === 'backfill'
|
|
550
565
|
? 'Tap a block, then another for a range'
|
|
551
566
|
: select === 'drill'
|
|
552
567
|
? 'Click a block to see its days'
|
|
553
568
|
: undefined) }) })] }) }) }) }));
|
|
554
569
|
}
|
|
570
|
+
/** Scrolls a sideways strip to its end (today) so its left edge falls on a whole block, not mid
|
|
571
|
+
* block with its label cut: the strip's content takes just enough room at its end to land there. */
|
|
572
|
+
function snapToEnd(el) {
|
|
573
|
+
const content = el.firstElementChild;
|
|
574
|
+
if (!content)
|
|
575
|
+
return;
|
|
576
|
+
content.style.paddingRight = '';
|
|
577
|
+
el.scrollLeft = el.scrollWidth;
|
|
578
|
+
const box = el.getBoundingClientRect().left;
|
|
579
|
+
const lefts = [...el.querySelectorAll('[data-col]')].map((c) => c.getBoundingClientRect().left - box);
|
|
580
|
+
// The first block cut by the left edge; the strip moves on to the block after it.
|
|
581
|
+
const cut = lefts.find((x, i) => x < -0.5 && (lefts[i + 1] ?? 1) > 0.5);
|
|
582
|
+
if (cut === undefined)
|
|
583
|
+
return;
|
|
584
|
+
const next = lefts[lefts.indexOf(cut) + 1];
|
|
585
|
+
if (next === undefined || next <= 0.5)
|
|
586
|
+
return;
|
|
587
|
+
content.style.paddingRight = `${Math.ceil(next)}px`;
|
|
588
|
+
el.scrollLeft = el.scrollWidth;
|
|
589
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -28,6 +28,7 @@ export { RankedBars, StackedBars, StackedColumns, TimeLines } from './lumen-widg
|
|
|
28
28
|
export { type ShareSegment, ShareMeter } from './share-meter.js';
|
|
29
29
|
export { type Pool, poolShare } from './pool-share.js';
|
|
30
30
|
export { METER_LEVEL_COLOR, meterLevel, meterValueTone } from './meter-level.js';
|
|
31
|
+
export { contrast, readableInk } from './ink.js';
|
|
31
32
|
export { UnitValue } from './unit-value.js';
|
|
32
33
|
export { type BurnRate, burnRate, durationText, PACE_WINDOWS, type Pace, type PaceWindow, paceHours, setDefaultPaceWindow, timeToCap, usePaceWindow, } from './pace.js';
|
|
33
34
|
export { PaceWindowPicker, paceText, TimeToCap } from './time-to-cap.js';
|
package/dist/index.js
CHANGED
|
@@ -35,6 +35,7 @@ export { RankedBars, StackedBars, StackedColumns, TimeLines } from './lumen-widg
|
|
|
35
35
|
export { ShareMeter } from './share-meter.js';
|
|
36
36
|
export { poolShare } from './pool-share.js';
|
|
37
37
|
export { METER_LEVEL_COLOR, meterLevel, meterValueTone } from './meter-level.js';
|
|
38
|
+
export { contrast, readableInk } from './ink.js';
|
|
38
39
|
export { UnitValue } from './unit-value.js';
|
|
39
40
|
export { burnRate, durationText, PACE_WINDOWS, paceHours, setDefaultPaceWindow, timeToCap, usePaceWindow, } from './pace.js';
|
|
40
41
|
export { PaceWindowPicker, paceText, TimeToCap } from './time-to-cap.js';
|
package/dist/ink.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** WCAG contrast of two hex colours, 1 to 21; null when either is not a hex colour. */
|
|
2
|
+
export declare function contrast(a: string, b: string): number | null;
|
|
3
|
+
/**
|
|
4
|
+
* The text colour for a chip of background `bg`: its own `ink` when that reads at `min`:1 (WCAG AA,
|
|
5
|
+
* 4.5), otherwise near-black or white, whichever reads better. A colour that is not hex keeps `ink`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function readableInk(bg: string, ink?: string, min?: number): string | undefined;
|
package/dist/ink.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Text that stays readable on a colour the app does not choose (a mailbox's own label colours).
|
|
2
|
+
const hex = (c) => {
|
|
3
|
+
const m = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(c.trim());
|
|
4
|
+
const d = m?.[1];
|
|
5
|
+
if (!d)
|
|
6
|
+
return null;
|
|
7
|
+
const h = d.length === 3 ? [...d].map((x) => x + x).join('') : d;
|
|
8
|
+
return [0, 2, 4].map((i) => Number.parseInt(h.slice(i, i + 2), 16));
|
|
9
|
+
};
|
|
10
|
+
const luminance = ([r, g, b]) => {
|
|
11
|
+
const lin = (v) => {
|
|
12
|
+
const s = v / 255;
|
|
13
|
+
return s <= 0.03928 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;
|
|
14
|
+
};
|
|
15
|
+
return 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b);
|
|
16
|
+
};
|
|
17
|
+
/** WCAG contrast of two hex colours, 1 to 21; null when either is not a hex colour. */
|
|
18
|
+
export function contrast(a, b) {
|
|
19
|
+
const x = hex(a);
|
|
20
|
+
const y = hex(b);
|
|
21
|
+
if (!x || !y)
|
|
22
|
+
return null;
|
|
23
|
+
const p = luminance(x);
|
|
24
|
+
const q = luminance(y);
|
|
25
|
+
return (Math.max(p, q) + 0.05) / (Math.min(p, q) + 0.05);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The text colour for a chip of background `bg`: its own `ink` when that reads at `min`:1 (WCAG AA,
|
|
29
|
+
* 4.5), otherwise near-black or white, whichever reads better. A colour that is not hex keeps `ink`.
|
|
30
|
+
*/
|
|
31
|
+
export function readableInk(bg, ink, min = 4.5) {
|
|
32
|
+
if (ink && (contrast(bg, ink) ?? min) >= min)
|
|
33
|
+
return ink;
|
|
34
|
+
const dark = contrast(bg, '#111111');
|
|
35
|
+
const light = contrast(bg, '#ffffff');
|
|
36
|
+
if (dark === null || light === null)
|
|
37
|
+
return ink;
|
|
38
|
+
return dark >= light ? '#111111' : '#ffffff';
|
|
39
|
+
}
|
package/dist/lumen-layout.js
CHANGED
|
@@ -33,6 +33,7 @@ import { LumenViewCard } from './lumen-view-card.js';
|
|
|
33
33
|
import { AddWidgetDialog, LayoutActions, LayoutChip, LumenSavedWidget, LumenWidget, cardFilterFields, cardTitle, cardsAfter, } from './lumen-widget.js';
|
|
34
34
|
import { OriginLegend } from './origin-legend.js';
|
|
35
35
|
import { IconButton, Page } from './page.js';
|
|
36
|
+
import { useScrollFade } from './scroll-fade.js';
|
|
36
37
|
import { SourcedCard } from './sourced-card.js';
|
|
37
38
|
import { LumenCard, SummaryTiles } from './summary-cards.js';
|
|
38
39
|
import { computedOf, filterFieldsOf, formulaBlanks, pick, subgroupOf, summaryCash, takesFormulas, withSavedFilters, withViewFields, } from './view-model.js';
|
|
@@ -109,6 +110,7 @@ function Scroller({ fit, children }) {
|
|
|
109
110
|
// it (theme.css): this lines that paint up with the fixed wash and stops pinned headers below its
|
|
110
111
|
// fade. Nothing here runs on scroll: the row is sticky, so it never moves on screen.
|
|
111
112
|
const ref = useRef(null);
|
|
113
|
+
const edge = useScrollFade(ref);
|
|
112
114
|
const watched = useRef(null);
|
|
113
115
|
useEffect(() => {
|
|
114
116
|
const el = ref.current;
|
|
@@ -163,7 +165,7 @@ function Scroller({ fit, children }) {
|
|
|
163
165
|
seen.observe(el.firstElementChild);
|
|
164
166
|
return () => seen.disconnect();
|
|
165
167
|
}, []);
|
|
166
|
-
return (_jsx("div", { ref: ref, "data-slot": "view-scroll", className: VIEW_SCROLL, children: _jsx("div", { className: cn('flex flex-col gap-(--shell-section-gap)', fit ? 'w-full' : 'w-max min-w-full'), children: children }) }));
|
|
168
|
+
return (_jsx("div", { ref: ref, "data-slot": "view-scroll", "data-fade": edge.side, onScroll: edge.update, className: cn(VIEW_SCROLL, '[--fade-inset:var(--view-sb,0px)]'), children: _jsx("div", { className: cn('flex flex-col gap-(--shell-section-gap)', fit ? 'w-full' : 'w-max min-w-full'), children: children }) }));
|
|
167
169
|
}
|
|
168
170
|
const codeSlots = (cards, scope) => (cards ?? []).map((c) => ({
|
|
169
171
|
id: c.id,
|
package/dist/lumen-tabs.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useScrollFade } from './scroll-fade.js';
|
|
2
3
|
import { Tabs, TabsList, TabsTrigger } from './ui/tabs.js';
|
|
3
4
|
export function LumenTabs({ tabs, value, onChange, actions, }) {
|
|
4
5
|
const shown = tabs.filter((t) => !t.hidden);
|
|
5
|
-
|
|
6
|
+
const fade = useScrollFade();
|
|
7
|
+
return (_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("div", { className: "min-w-48 max-w-full flex-1", children: shown.length ? (_jsx(Tabs, { value: value, onValueChange: (v) => onChange(v), children: _jsx(TabsList, { ref: fade.ref, onScroll: fade.update, "data-fade": fade.side, className: "max-w-full overflow-x-auto", children: shown.map(({ value: v, label, icon: Icon, after }) => (_jsxs(TabsTrigger, { value: v, children: [_jsx(Icon, {}), label, after] }, v))) }) })) : null }), actions ? _jsx("div", { className: "ml-auto flex shrink-0 items-center gap-0.5", children: actions }) : null] }));
|
|
6
8
|
}
|
package/dist/lumen-view.js
CHANGED
|
@@ -301,7 +301,9 @@ export function LumenView({ domain, config, rows, total = rows.length, paging: s
|
|
|
301
301
|
const table = pivot && config.pivot ? (_jsx(PivotTable, { domain: domain, rows: rows, pivot: config.pivot })) : (_jsx(LumenTable, { domain: domain, columns: columns, rows: tableRows, groupBy: groupBy, subgroup: sub, chips: chips, page: paging ? { index: page, size: pageSize } : null, inView: inLayout, fit: fit, onDetails: onDetails, tapped: tapped, selection: selection, groupActions: groupActions, sort: sort, onSort: sorted, serverSorts: server ? (server.sorts ?? []) : undefined, serverGroups: served, total: config.total !== false, add: onAddColumn || onColumns
|
|
302
302
|
? { ...onAddColumn, field: onColumns && { domain, columns, onColumns } }
|
|
303
303
|
: undefined }));
|
|
304
|
-
return (_jsx(LumenWidget, { id: cardId, kind: "table", style: "table", title: title, controls: controls, tools: state ? (tools) : (_jsxs(_Fragment, { children: [tools, compare, pager, onColumns && !onAddColumn && on('columns') && (_jsx(ColumnPicker, { domain: domain, columns: columns, onColumns: onColumns })), !pivot && on('download') && (_jsx(IconButton, { icon: DownloadIcon, label: "Download CSV",
|
|
304
|
+
return (_jsx(LumenWidget, { id: cardId, kind: "table", style: "table", title: title, phoneTitle: false, controls: controls, tools: state ? (tools) : (_jsxs(_Fragment, { children: [tools, compare, pager, onColumns && !onAddColumn && on('columns') && (_jsx(ColumnPicker, { domain: domain, columns: columns, onColumns: onColumns })), !pivot && on('download') && (_jsx(IconButton, { icon: DownloadIcon, label: "Download CSV",
|
|
305
|
+
// A phone's card header has room for the pager, filter, refresh and menu only.
|
|
306
|
+
className: "max-md:hidden", onClick: () => downloadCsv({
|
|
305
307
|
name: title,
|
|
306
308
|
columns,
|
|
307
309
|
rows: tableRows,
|
|
@@ -425,8 +427,16 @@ function LumenTableRows({ domain, columns = domain.fields, rows, groupBy = null,
|
|
|
425
427
|
// The grouped field is the group row's label, so its column hides (a flat lead stays).
|
|
426
428
|
// Each key once: two columns under one React key can lose a cell on a re-render, and every
|
|
427
429
|
// row after it slides left by one.
|
|
430
|
+
// A `hideEmpty` column with nothing in any row here leaves the table.
|
|
431
|
+
const empty = (f) => !!f.hideEmpty &&
|
|
432
|
+
rows.length > 0 &&
|
|
433
|
+
rows.every((r) => {
|
|
434
|
+
const v = f.value(r);
|
|
435
|
+
return v == null || v === '' || v === 0;
|
|
436
|
+
});
|
|
428
437
|
const visible = columns.filter((f, i) => (f.key !== groupBy?.key || (flat && i === 0)) &&
|
|
429
|
-
columns.findIndex((x) => x.key === f.key) === i
|
|
438
|
+
columns.findIndex((x) => x.key === f.key) === i &&
|
|
439
|
+
!empty(f));
|
|
430
440
|
// Inline chips sit on the first column, next to the row's name, not in their own.
|
|
431
441
|
const inline = visible.filter((f, i) => i > 0 && chips.some((c) => c.key === f.key && c.placement === 'inline'));
|
|
432
442
|
const shown = visible.filter((f) => !inline.includes(f));
|
|
@@ -440,6 +450,7 @@ function LumenTableRows({ domain, columns = domain.fields, rows, groupBy = null,
|
|
|
440
450
|
const col = (f, ...more) => ({
|
|
441
451
|
...colProps(f, ...more),
|
|
442
452
|
'data-card': f ? cardOf(f) : undefined,
|
|
453
|
+
'data-card-unit': f?.cardUnit,
|
|
443
454
|
});
|
|
444
455
|
const computed = useMemo(() => computedOf(domain, rows, groupBy), [domain, rows, groupBy]);
|
|
445
456
|
// Values per editable field, offered when a cell is edited: the field's own list when it has
|
package/dist/lumen-widget.d.ts
CHANGED
|
@@ -52,7 +52,7 @@ export interface CardLink {
|
|
|
52
52
|
/** Its own filter's linked chips (the phone's count beside the funnel). */
|
|
53
53
|
linked?: number;
|
|
54
54
|
}
|
|
55
|
-
export declare function LumenWidget({ id, kind, icon, title, note, controls, tools, filter, filterCount, load, rows, loadNote, pad, pinned, compact, className, children, link: given, help, assistant, strip, shown, style, }: {
|
|
55
|
+
export declare function LumenWidget({ id, kind, icon, title, note, controls, tools, phoneTitle, filter, filterCount, load, rows, loadNote, pad, pinned, compact, className, children, link: given, help, assistant, strip, shown, style, }: {
|
|
56
56
|
id: string;
|
|
57
57
|
/** Its controls (Edit widget ▸ Controls): Ask the assistant, Refresh, Filter box and
|
|
58
58
|
* Footer show or hide here; missing keys take the type's default. */
|
|
@@ -78,6 +78,9 @@ export declare function LumenWidget({ id, kind, icon, title, note, controls, too
|
|
|
78
78
|
controls?: CardControls;
|
|
79
79
|
/** Buttons before the ↻, e.g. a view's pager. */
|
|
80
80
|
tools?: ReactNode;
|
|
81
|
+
/** False: a phone leaves the title out and gives the header to `tools` (a view whose
|
|
82
|
+
* page header names it already). */
|
|
83
|
+
phoneTitle?: boolean;
|
|
81
84
|
/** The card's own filter (a view's LumenTokenizedFilter and Group): on the title's line
|
|
82
85
|
* when the header is wide, on a line under it when narrow, behind a funnel button on phone. */
|
|
83
86
|
filter?: ReactNode;
|
package/dist/lumen-widget.js
CHANGED
|
@@ -180,7 +180,7 @@ function useWidth() {
|
|
|
180
180
|
}, []);
|
|
181
181
|
return [ref, width];
|
|
182
182
|
}
|
|
183
|
-
export function LumenWidget({ id, kind, icon, title, note, controls, tools, filter, filterCount = 0, load, rows, loadNote, pad = true, pinned = false, compact = false, className, children, link: given, help, assistant, strip, shown, style, }) {
|
|
183
|
+
export function LumenWidget({ id, kind, icon, title, note, controls, tools, phoneTitle, filter, filterCount = 0, load, rows, loadNote, pad = true, pinned = false, compact = false, className, children, link: given, help, assistant, strip, shown, style, }) {
|
|
184
184
|
const [removing, setRemoving] = useState(false);
|
|
185
185
|
const cell = useGridCell();
|
|
186
186
|
const links = useLinks();
|
|
@@ -232,7 +232,7 @@ export function LumenWidget({ id, kind, icon, title, note, controls, tools, filt
|
|
|
232
232
|
layout &&
|
|
233
233
|
"relative after:pointer-events-none after:absolute after:inset-0 after:z-30 after:rounded-[inherit] after:border-2 after:border-primary/40 after:border-dashed after:content-['']", flat && 'border-0 bg-transparent shadow-none',
|
|
234
234
|
// The corner ⋯ of a card with its header off sits over the body's top right.
|
|
235
|
-
headless && 'relative', className), children: [!headless && (_jsxs("div", { ref: head, className: cn('flex shrink-0 items-center gap-2 px-3', compact ? 'h-8' : 'h-10 border-b', (folded || below) && 'border-b-0', pinned && 'sticky left-0 w-(--view-w) max-w-full'), children: [layout && (_jsx(Button, { variant: "ghost", size: "icon-xs", className: "lumen-drag -ml-1 cursor-grab touch-none text-muted-foreground active:cursor-grabbing", "aria-label": `Drag ${title}`, children: _jsx(GripVerticalIcon, {}) })), link && !bare && (_jsx(LinkPicker, { color: link.color, onPick: link.onPick, label: title, className: "-mr-1" })), !compact && _jsx(Icon, { className: "size-4 shrink-0 text-muted-foreground" }), _jsx("span", { className: cn('truncate', compact ? 'text-xs text-muted-foreground' : 'text-sm font-semibold'), children: title }), link?.color && link.follows && !folded && (_jsxs("span", { className: "min-w-0 shrink truncate text-xs", style: { color: linkVar(link.color) }, title: `Follows ${link.follows}`, children: ["\u00B7 ", link.follows] })), layout && headerOff && (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs(Badge, { variant: "secondary", className: "shrink-0 gap-1 text-muted-foreground", children: [_jsx(EyeOffIcon, {}), " Header off"] }) }), _jsx(TooltipContent, { children: "Shown only in Edit layout. Change it in Edit widget \u25B8 Controls." })] })), !layout && !bare && (_jsx(FoldChevron, { folded: folded, label: title, onToggle: toggle, className: "-ml-1" })), folded && rows != null && (_jsxs("span", { className: "shrink-0 text-xs text-muted-foreground num", children: [rows.toLocaleString(), " ", rows === 1 ? 'row' : 'rows'] })), inline && (_jsx("div", { "data-card-filter": true, className: "flex min-w-40 max-w-xl flex-1 items-center gap-2", children: filter })), note && !layout && !folded && (_jsx("span", { className: "hidden truncate text-xs text-muted-foreground sm:inline", children: note })), _jsxs("span", { className: cn('ml-auto flex shrink-0 items-center gap-1', bare && 'hidden'), children: [!layout && load?.updated && _jsx(LumenFreshness, { load: load }), !layout && !folded && tools, showFilter && mobile && link?.color && !!link.linked && (_jsxs("span", { className: "num flex items-center gap-1 text-xs text-muted-foreground", title: `${link.linked} linked`, children: [_jsx(LinkDot, { color: link.color, className: "size-2" }), link.linked] })), showFilter && mobile && (_jsxs("span", { className: "relative", children: [_jsx(IconButton, { icon: FunnelIcon, label: filterOpen ? 'Hide filter' : 'Filter', "aria-expanded": filterOpen, className: cn(filterOpen && 'text-primary'), onClick: () => setFilterOpen(!filterOpen) }), filterCount > 0 && (_jsx(Badge, { className: "num pointer-events-none absolute -top-1 -right-1 h-4 min-w-4 px-1 text-[10px]", children: filterCount }))] })), !layout && !folded && help && _jsx(CardHelp, { title: title, children: help }), !layout && load && on('refresh') && (_jsx(LumenRefresh, { load: load, label: `Refresh ${title}` })), (controls?.editable || (!layout && (ask || load?.editScript))) && (_jsxs(DropdownMenu, { modal: false, children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(IconButton, { icon: MoreHorizontalIcon, label: `${title} menu` }) }), _jsxs(DropdownMenuContent, { align: "end", className: "w-44", children: [ask && !layout && (_jsxs(DropdownMenuItem, { onSelect: ask, children: [_jsx(SparklesIcon, {}), " Ask ", assistantName] })), load?.editScript && !layout && (_jsxs(DropdownMenuItem, { onSelect: load.editScript, children: [_jsx(CodeIcon, {}), " Script"] })), (ask || load?.editScript) && !layout && controls?.editable && (_jsx(DropdownMenuSeparator, {})), controls?.editable && (_jsxs(_Fragment, { children: [!controls.fixed && (_jsxs(_Fragment, { children: [_jsxs(DropdownMenuItem, { onSelect: () => controls.onAction('edit'), children: [_jsx(PencilIcon, {}), " Edit widget"] }), _jsxs(DropdownMenuItem, { disabled: !own, onSelect: () => controls.onAction('duplicate'), children: [_jsx(CopyIcon, {}), " Duplicate"] })] })), _jsxs(DropdownMenuItem, { disabled: controls.first, onSelect: () => controls.onAction('up'), children: [_jsx(ArrowUpIcon, {}), " Move up"] }), _jsxs(DropdownMenuItem, { disabled: controls.last, onSelect: () => controls.onAction('down'), children: [_jsx(ArrowDownIcon, {}), " Move down"] }), controls.onFit && (_jsxs(DropdownMenuItem, { onSelect: controls.onFit, children: [_jsx(FoldVerticalIcon, {}), " Fit height"] })), !controls.fixed && (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { className: "text-muted-foreground focus:text-destructive", onSelect: () => setRemoving(true), children: [_jsx(Trash2Icon, {}), " Remove"] })] }))] }))] })] }))] })] })), below && !headless && (_jsx("div", { "data-card-filter": true, className: cn('flex items-center gap-2 border-b px-3 pb-2', pinned && 'sticky left-0 w-(--view-w) max-w-full'), children: filter })), strip && !layout && !folded && !headless ? (_jsx("div", { "data-card-strip": true, className: cn('flex min-w-0 items-center gap-2 border-b px-3 py-1.5 empty:hidden', pinned && 'sticky left-0 w-(--view-w) max-w-full'), children: strip })) : null, _jsx("div", { hidden: folded, className: cn('relative min-w-0',
|
|
235
|
+
headless && 'relative', className), children: [!headless && (_jsxs("div", { ref: head, className: cn('flex shrink-0 items-center gap-2 px-3', compact ? 'h-8' : 'h-10 border-b', (folded || below) && 'border-b-0', pinned && 'sticky left-0 w-(--view-w) max-w-full'), children: [layout && (_jsx(Button, { variant: "ghost", size: "icon-xs", className: "lumen-drag -ml-1 cursor-grab touch-none text-muted-foreground active:cursor-grabbing", "aria-label": `Drag ${title}`, children: _jsx(GripVerticalIcon, {}) })), link && !bare && (_jsx(LinkPicker, { color: link.color, onPick: link.onPick, label: title, className: "-mr-1" })), !compact && _jsx(Icon, { className: "size-4 shrink-0 text-muted-foreground" }), _jsx("span", { className: cn('truncate', compact ? 'text-xs text-muted-foreground' : 'text-sm font-semibold', phoneTitle === false && !layout && 'max-md:hidden'), children: title }), link?.color && link.follows && !folded && (_jsxs("span", { className: "min-w-0 shrink truncate text-xs", style: { color: linkVar(link.color) }, title: `Follows ${link.follows}`, children: ["\u00B7 ", link.follows] })), layout && headerOff && (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs(Badge, { variant: "secondary", className: "shrink-0 gap-1 text-muted-foreground", children: [_jsx(EyeOffIcon, {}), " Header off"] }) }), _jsx(TooltipContent, { children: "Shown only in Edit layout. Change it in Edit widget \u25B8 Controls." })] })), !layout && !bare && (_jsx(FoldChevron, { folded: folded, label: title, onToggle: toggle, className: "-ml-1" })), folded && rows != null && (_jsxs("span", { className: "shrink-0 text-xs text-muted-foreground num", children: [rows.toLocaleString(), " ", rows === 1 ? 'row' : 'rows'] })), inline && (_jsx("div", { "data-card-filter": true, className: "flex min-w-40 max-w-xl flex-1 items-center gap-2", children: filter })), note && !layout && !folded && (_jsx("span", { className: "hidden truncate text-xs text-muted-foreground sm:inline", children: note })), _jsxs("span", { className: cn('ml-auto flex shrink-0 items-center gap-1', bare && 'hidden'), children: [!layout && load?.updated && _jsx(LumenFreshness, { load: load }), !layout && !folded && tools, showFilter && mobile && link?.color && !!link.linked && (_jsxs("span", { className: "num flex items-center gap-1 text-xs text-muted-foreground", title: `${link.linked} linked`, children: [_jsx(LinkDot, { color: link.color, className: "size-2" }), link.linked] })), showFilter && mobile && (_jsxs("span", { className: "relative", children: [_jsx(IconButton, { icon: FunnelIcon, label: filterOpen ? 'Hide filter' : 'Filter', "aria-expanded": filterOpen, className: cn(filterOpen && 'text-primary'), onClick: () => setFilterOpen(!filterOpen) }), filterCount > 0 && (_jsx(Badge, { className: "num pointer-events-none absolute -top-1 -right-1 h-4 min-w-4 px-1 text-[10px]", children: filterCount }))] })), !layout && !folded && help && _jsx(CardHelp, { title: title, children: help }), !layout && load && on('refresh') && (_jsx(LumenRefresh, { load: load, label: `Refresh ${title}` })), (controls?.editable || (!layout && (ask || load?.editScript))) && (_jsxs(DropdownMenu, { modal: false, children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(IconButton, { icon: MoreHorizontalIcon, label: `${title} menu` }) }), _jsxs(DropdownMenuContent, { align: "end", className: "w-44", children: [ask && !layout && (_jsxs(DropdownMenuItem, { onSelect: ask, children: [_jsx(SparklesIcon, {}), " Ask ", assistantName] })), load?.editScript && !layout && (_jsxs(DropdownMenuItem, { onSelect: load.editScript, children: [_jsx(CodeIcon, {}), " Script"] })), (ask || load?.editScript) && !layout && controls?.editable && (_jsx(DropdownMenuSeparator, {})), controls?.editable && (_jsxs(_Fragment, { children: [!controls.fixed && (_jsxs(_Fragment, { children: [_jsxs(DropdownMenuItem, { onSelect: () => controls.onAction('edit'), children: [_jsx(PencilIcon, {}), " Edit widget"] }), _jsxs(DropdownMenuItem, { disabled: !own, onSelect: () => controls.onAction('duplicate'), children: [_jsx(CopyIcon, {}), " Duplicate"] })] })), _jsxs(DropdownMenuItem, { disabled: controls.first, onSelect: () => controls.onAction('up'), children: [_jsx(ArrowUpIcon, {}), " Move up"] }), _jsxs(DropdownMenuItem, { disabled: controls.last, onSelect: () => controls.onAction('down'), children: [_jsx(ArrowDownIcon, {}), " Move down"] }), controls.onFit && (_jsxs(DropdownMenuItem, { onSelect: controls.onFit, children: [_jsx(FoldVerticalIcon, {}), " Fit height"] })), !controls.fixed && (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { className: "text-muted-foreground focus:text-destructive", onSelect: () => setRemoving(true), children: [_jsx(Trash2Icon, {}), " Remove"] })] }))] }))] })] }))] })] })), below && !headless && (_jsx("div", { "data-card-filter": true, className: cn('flex items-center gap-2 border-b px-3 pb-2', pinned && 'sticky left-0 w-(--view-w) max-w-full'), children: filter })), strip && !layout && !folded && !headless ? (_jsx("div", { "data-card-strip": true, className: cn('flex min-w-0 items-center gap-2 border-b px-3 py-1.5 empty:hidden', pinned && 'sticky left-0 w-(--view-w) max-w-full'), children: strip })) : null, _jsx("div", { hidden: folded, className: cn('relative min-w-0',
|
|
236
236
|
// On the grid a card fills its cell: with a height of its own its body scrolls; one as
|
|
237
237
|
// tall as it draws keeps its body's height, so the grid can measure it.
|
|
238
238
|
!cell ? 'flex-1' : cell.fixed && 'min-h-0 flex-1 overflow-auto', pad && !table && !flat && (compact ? 'px-3 pb-3' : 'p-3')), children: _jsx(LumenBoundary, { where: id, children: children }) }), headless && (_jsx(HeaderlessMenu, { title: title, ask: ask, askLabel: `Ask ${assistantName}`, load: load && on('refresh') ? load : undefined, editScript: load?.editScript, filter: showFilter ? filter : undefined, filterCount: filterCount })), !layout && !folded && load && on('footer') && (_jsx("div", { className: cn('mt-auto shrink-0', pinned && 'sticky left-0 w-(--view-w) max-w-full'), children: _jsx(LumenLoadFooter, { load: load, rows: rows, note: loadNote }) })), controls && (_jsx(LumenConfirm, { open: removing, onOpenChange: setRemoving, title: `Remove ${title}?`, description: table ? 'Add widget brings a table back.' : 'Add widget brings it back.', action: "Remove", onConfirm: () => controls.onAction('remove') }))] }) }));
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Which edges of a sideways-scrolling row have more beyond them (`start`, `end`, `both`), for
|
|
4
|
+
* theme.css (`data-fade`) to fade there: a cut-off tab or block reads as "scroll for more", not as a clipped label.
|
|
5
|
+
*/
|
|
6
|
+
export declare function useScrollFade(given?: RefObject<HTMLDivElement | null>): {
|
|
7
|
+
ref: RefObject<HTMLDivElement | null>;
|
|
8
|
+
side: "both" | "end" | "start" | undefined;
|
|
9
|
+
update: () => void;
|
|
10
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Fades a sideways-scrolling row at the edges with more beyond them.
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* Which edges of a sideways-scrolling row have more beyond them (`start`, `end`, `both`), for
|
|
5
|
+
* theme.css (`data-fade`) to fade there: a cut-off tab or block reads as "scroll for more", not as a clipped label.
|
|
6
|
+
*/
|
|
7
|
+
export function useScrollFade(given) {
|
|
8
|
+
const own = useRef(null);
|
|
9
|
+
const ref = given ?? own;
|
|
10
|
+
const [side, setSide] = useState();
|
|
11
|
+
const update = useCallback(() => {
|
|
12
|
+
const el = ref.current;
|
|
13
|
+
if (!el)
|
|
14
|
+
return;
|
|
15
|
+
const start = el.scrollLeft > 1;
|
|
16
|
+
const end = el.scrollLeft + el.clientWidth < el.scrollWidth - 1;
|
|
17
|
+
setSide(start && end ? 'both' : start ? 'start' : end ? 'end' : undefined);
|
|
18
|
+
}, [ref]);
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
const el = ref.current;
|
|
21
|
+
if (!el)
|
|
22
|
+
return;
|
|
23
|
+
update();
|
|
24
|
+
// The row's content can grow while the row keeps its size (a new page of wider rows).
|
|
25
|
+
const seen = new ResizeObserver(update);
|
|
26
|
+
seen.observe(el);
|
|
27
|
+
if (el.firstElementChild)
|
|
28
|
+
seen.observe(el.firstElementChild);
|
|
29
|
+
return () => seen.disconnect();
|
|
30
|
+
}, [ref, update]);
|
|
31
|
+
return { ref, side, update };
|
|
32
|
+
}
|
package/dist/shell.js
CHANGED
|
@@ -143,5 +143,5 @@ export function LumenShell({ app, appLabel, brand, nav, status, health, statusEn
|
|
|
143
143
|
}
|
|
144
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' : '
|
|
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' : 'shrink-0', children: label }), value !== undefined && (_jsxs(_Fragment, { children: [_jsx("span", { "aria-hidden": true, children: "\u00B7" }), _jsx("span", { className: "shrink-0 tabular-nums text-foreground/80", children: value })] }))] }));
|
|
147
147
|
}
|
package/dist/theme.css
CHANGED
|
@@ -414,6 +414,30 @@
|
|
|
414
414
|
left: 100%;
|
|
415
415
|
}
|
|
416
416
|
|
|
417
|
+
/* A row that scrolls sideways (LumenTabs, a view's page) fades at an edge with more beyond it.
|
|
418
|
+
`--fade-inset` keeps a vertical scrollbar at the end edge out of the fade. */
|
|
419
|
+
[data-fade="end"] {
|
|
420
|
+
mask-image: linear-gradient(
|
|
421
|
+
to left,
|
|
422
|
+
#000 var(--fade-inset, 0px),
|
|
423
|
+
transparent var(--fade-inset, 0px),
|
|
424
|
+
#000 calc(var(--fade-inset, 0px) + 2rem)
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
[data-fade="start"] {
|
|
428
|
+
mask-image: linear-gradient(to left, #000 calc(100% - 2rem), transparent);
|
|
429
|
+
}
|
|
430
|
+
[data-fade="both"] {
|
|
431
|
+
mask-image: linear-gradient(
|
|
432
|
+
to left,
|
|
433
|
+
#000 var(--fade-inset, 0px),
|
|
434
|
+
transparent var(--fade-inset, 0px),
|
|
435
|
+
#000 calc(var(--fade-inset, 0px) + 2rem),
|
|
436
|
+
#000 calc(100% - 2rem),
|
|
437
|
+
transparent
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
|
|
417
441
|
/* A LumenView table on a narrow phone (`data-cards`): each row is a card, so nothing runs off
|
|
418
442
|
the right edge. Line 1: the row's name (shortens) and one figure (`data-card="meta"`); line 2:
|
|
419
443
|
the other values, small and muted. Numbers, dates and their units keep their width; only text
|
|
@@ -486,7 +510,17 @@
|
|
|
486
510
|
}
|
|
487
511
|
table[data-cards] [data-slot="unit-value-unit"] {
|
|
488
512
|
position: static;
|
|
513
|
+
width: auto;
|
|
514
|
+
padding-left: 0;
|
|
489
515
|
margin-left: 0.25em;
|
|
516
|
+
font-size: inherit;
|
|
517
|
+
}
|
|
518
|
+
/* A bare count names what it counts (`cardUnit`): no header says it on a card. */
|
|
519
|
+
table[data-cards] td[data-card-unit]::after {
|
|
520
|
+
content: attr(data-card-unit);
|
|
521
|
+
margin-left: 0.25em;
|
|
522
|
+
color: var(--muted-foreground);
|
|
523
|
+
font-weight: 400;
|
|
490
524
|
}
|
|
491
525
|
}
|
|
492
526
|
|
package/dist/view-model.d.ts
CHANGED
|
@@ -139,6 +139,10 @@ export interface ViewField<T> {
|
|
|
139
139
|
maxWidth?: string | false;
|
|
140
140
|
/** Left out on phones, where the table has no room for it (a returns table's longer windows). */
|
|
141
141
|
hideOnPhone?: boolean;
|
|
142
|
+
/** Left out of a table when no row in it has a value (none, empty or 0): an Attachments column on mail with none. */
|
|
143
|
+
hideEmpty?: boolean;
|
|
144
|
+
/** On a phone card, where no header names it: the word after a bare count (`msgs`, so `5,861 msgs`). */
|
|
145
|
+
cardUnit?: string;
|
|
142
146
|
/**
|
|
143
147
|
* How the value updates (live domains): `live` moves with the market while it
|
|
144
148
|
* trades (a quote), `asof` is a daily or snapshot value (a close, a fact, the assistant frame's column).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@columenlabs/lumen-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.109",
|
|
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.109",
|
|
53
53
|
"@dnd-kit/core": "6.3.1",
|
|
54
54
|
"@dnd-kit/sortable": "10.0.0",
|
|
55
55
|
"@dnd-kit/utilities": "3.2.2",
|