@columenlabs/lumen-ui 0.1.101 → 0.1.102
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/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lumen-trend.js +3 -1
- package/dist/lumen-view.js +7 -2
- package/dist/lumen-widget.d.ts +1 -1
- package/dist/people/sections.js +10 -2
- package/dist/people/shared.d.ts +4 -1
- package/dist/people/shared.js +20 -10
- package/dist/prefs.d.ts +2 -0
- package/dist/prefs.js +10 -0
- package/dist/schedules.js +52 -11
- package/dist/view-formulas.js +3 -0
- package/dist/view-model.d.ts +1 -1
- package/dist/widget-settings.d.ts +1 -1
- package/dist/widget-values.js +5 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -47,7 +47,7 @@ export { type ChartBand, type ChartMarker, GrowthChart, KEY_DATE_KINDS, KeyDateC
|
|
|
47
47
|
export { FoldGroup, NavGroup, type NavItem, type SectionItem, type SectionSubItem, SidebarSection, } from './nav.js';
|
|
48
48
|
export { type OverviewPageProps, type OverviewStage, type OverviewTone, type OverviewWidget, OverviewList, OverviewPage, ToneDot, } from './overview.js';
|
|
49
49
|
export { type Crumb, IconButton, type IconButtonProps, Page, PageBody, PageHeader, type PageHeaderProps, } from './page.js';
|
|
50
|
-
export { DEFAULT_PREFS, deviceTimeZone, type Prefs, TIME_ZONES, type Theme, type TimeZoneId, getPrefs, resolvedTheme, setPref, timeZoneLabel, usePrefs, ASSISTANT_WIDTH, } from './prefs.js';
|
|
50
|
+
export { DEFAULT_PREFS, deviceTimeZone, type Prefs, TIME_ZONES, type Theme, type TimeZoneId, getPrefs, resolvedTheme, setPref, timeZoneLabel, usePrefs, zoneAbbr, ASSISTANT_WIDTH, } from './prefs.js';
|
|
51
51
|
export { KIT, type KitConfig, configureLumen } from './kit-config.js';
|
|
52
52
|
export { ConnectionBlock, ErrorLine, IntegrationRow, KeyTestLine, type KeyTestResult, TestBadge, errorText, useClock, } from './integrations.js';
|
|
53
53
|
export { ConnectionBadge, type ConnectionField, type ConnectionLine, ConnectionLogo, type ConnectionSignin, type ConnectionStatus, type ConnectionTest, type ConnectionWay, LumenConnection, type LumenConnectionProps, activeWay, connectionStatus, } from './connection.js';
|
package/dist/index.js
CHANGED
|
@@ -54,7 +54,7 @@ export { GrowthChart, KEY_DATE_KINDS, KeyDateChips, PALETTE, benchColour, keyDat
|
|
|
54
54
|
export { FoldGroup, NavGroup, SidebarSection, } from './nav.js';
|
|
55
55
|
export { OverviewList, OverviewPage, ToneDot, } from './overview.js';
|
|
56
56
|
export { IconButton, Page, PageBody, PageHeader, } from './page.js';
|
|
57
|
-
export { DEFAULT_PREFS, deviceTimeZone, TIME_ZONES, getPrefs, resolvedTheme, setPref, timeZoneLabel, usePrefs, ASSISTANT_WIDTH, } from './prefs.js';
|
|
57
|
+
export { DEFAULT_PREFS, deviceTimeZone, TIME_ZONES, getPrefs, resolvedTheme, setPref, timeZoneLabel, usePrefs, zoneAbbr, ASSISTANT_WIDTH, } from './prefs.js';
|
|
58
58
|
export { KIT, configureLumen } from './kit-config.js';
|
|
59
59
|
export { ConnectionBlock, ErrorLine, IntegrationRow, KeyTestLine, TestBadge, errorText, useClock, } from './integrations.js';
|
|
60
60
|
export { ConnectionBadge, ConnectionLogo, LumenConnection, activeWay, connectionStatus, } from './connection.js';
|
package/dist/lumen-trend.js
CHANGED
|
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
// month or year, drawn as a line, area, stacked area, bars over time, combo, a spark or a tracker.
|
|
4
4
|
// With the allocation, donut, bars and card styles they cover every Tremor visualization, drawn on
|
|
5
5
|
// our own Recharts in ChartContainer. Worked out in the browser from the rows; nothing is fetched.
|
|
6
|
-
import { isDollarFormat, } from '@columenlabs/lumen-ui-types/views';
|
|
6
|
+
import { formatBytes, isDollarFormat, } from '@columenlabs/lumen-ui-types/views';
|
|
7
7
|
import { useMemo } from 'react';
|
|
8
8
|
import { Area, AreaChart, Bar, BarChart, CartesianGrid, ComposedChart, Line, LineChart, XAxis, YAxis, } from 'recharts';
|
|
9
9
|
import { cn } from './lib/utils.js';
|
|
@@ -24,6 +24,8 @@ function short(v, format, hidden) {
|
|
|
24
24
|
const abs = Math.abs(v);
|
|
25
25
|
if (format === 'percent' || format === 'weight')
|
|
26
26
|
return `${sign}${abs.toFixed(1)}%`;
|
|
27
|
+
if (format === 'bytes')
|
|
28
|
+
return `${sign}${formatBytes(abs)}`;
|
|
27
29
|
if (isDollarFormat(format))
|
|
28
30
|
return `${sign}$${COMPACT.format(abs)}`;
|
|
29
31
|
return `${sign}${COMPACT.format(abs)}`;
|
package/dist/lumen-view.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { controlOn, isCountAgg, isDollarFormat, viewPageSizes, viewPaginationSchema, } from '@columenlabs/lumen-ui-types/views';
|
|
2
|
+
import { bytesParts, controlOn, isCountAgg, isDollarFormat, viewPageSizes, viewPaginationSchema, } from '@columenlabs/lumen-ui-types/views';
|
|
3
3
|
// Custom views: any number of views on any app, one page for all of them.
|
|
4
4
|
// A data domain (holdings, chains, bars, series, research results) describes its fields once in a
|
|
5
5
|
// `ViewDomain`; a view is a saved `CustomViewConfig` over that domain, written in the page's Edit
|
|
@@ -42,7 +42,7 @@ import { Skeleton } from './ui/skeleton.js';
|
|
|
42
42
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from './ui/table.js';
|
|
43
43
|
import { ToggleGroup, ToggleGroupItem } from './ui/toggle-group.js';
|
|
44
44
|
import { Tooltip, TooltipContent, TooltipTrigger } from './ui/tooltip.js';
|
|
45
|
-
import { UnitColumns } from './unit-value.js';
|
|
45
|
+
import { UnitColumns, UnitValue } from './unit-value.js';
|
|
46
46
|
import { EditCell, EditInputCell, EditManyCell } from './view-edit-cell.js';
|
|
47
47
|
import { fieldOptions } from './view-formulas.js';
|
|
48
48
|
import { CHIP_TONE, FORMULAS_GROUP, chipsOf, computedOf, filterFieldsOf, groupOf, label, pick, rollUp, rollsUp, subgroupOf, withSavedFilters, withViewFields, } from './view-model.js';
|
|
@@ -104,6 +104,11 @@ export function ViewValueCell({ value, format, chip, tone, color, amount, compac
|
|
|
104
104
|
return _jsx("span", { children: text });
|
|
105
105
|
return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "block max-w-[24rem] truncate", children: text }) }), _jsx(TooltipContent, { className: "max-w-md whitespace-normal", children: text })] }));
|
|
106
106
|
}
|
|
107
|
+
if (format === 'bytes' && !hidden) {
|
|
108
|
+
// A size: its number and unit in the column's unit slot, so a column of sizes lines up.
|
|
109
|
+
const [n, unit] = bytesParts(value);
|
|
110
|
+
return _jsx(UnitValue, { value: `${value < 0 ? MINUS : ''}${n}`, unit: unit });
|
|
111
|
+
}
|
|
107
112
|
const abs = hidden ? maskAmount('0') : digitsOf(format, compact).format(Math.abs(value));
|
|
108
113
|
const signed = format === 'signed' || format === 'percent' || format === 'price_signed';
|
|
109
114
|
const sign = value < 0 ? MINUS : signed && value > 0 ? '+' : '';
|
package/dist/lumen-widget.d.ts
CHANGED
|
@@ -197,7 +197,7 @@ export declare function cardsAfter(cards: ViewCard[], i: number, action: CardAct
|
|
|
197
197
|
key: string;
|
|
198
198
|
placement: "column" | "inline";
|
|
199
199
|
}[];
|
|
200
|
-
formats?: Record<string, "compact" | "integer" | "number" | "percent" | "price" | "price_rounded" | "price_signed" | "rounded" | "signed" | "weight"> | undefined;
|
|
200
|
+
formats?: Record<string, "bytes" | "compact" | "integer" | "number" | "percent" | "price" | "price_rounded" | "price_signed" | "rounded" | "signed" | "weight"> | undefined;
|
|
201
201
|
aggs?: Record<string, "count" | "distinct" | "first" | "last" | "max" | "mean" | "min" | "none" | "sum"> | undefined;
|
|
202
202
|
shows?: Record<string, string> | undefined;
|
|
203
203
|
group: {
|
package/dist/people/sections.js
CHANGED
|
@@ -8,6 +8,7 @@ import { useMemo, useState } from 'react';
|
|
|
8
8
|
import { LumenLayout } from '../lumen-layout.js';
|
|
9
9
|
import { LumenSelector } from '../lumen-selector.js';
|
|
10
10
|
import { useLumenLoad } from '../lumen-source.js';
|
|
11
|
+
import { getPrefs } from '../prefs.js';
|
|
11
12
|
import { Tile, TileRow } from '../tile.js';
|
|
12
13
|
import { Button } from '../ui/button.js';
|
|
13
14
|
import { CopyLink } from './invite-drawer.js';
|
|
@@ -258,7 +259,8 @@ export function AuditSection({ frame }) {
|
|
|
258
259
|
key: 'day',
|
|
259
260
|
label: 'Day',
|
|
260
261
|
icon: ClockIcon,
|
|
261
|
-
value: (e) => new Date(e.at).toLocaleDateString(
|
|
262
|
+
value: (e) => new Date(e.at).toLocaleDateString('en-US', {
|
|
263
|
+
timeZone: getPrefs().timeZone,
|
|
262
264
|
weekday: 'short',
|
|
263
265
|
day: 'numeric',
|
|
264
266
|
month: 'short',
|
|
@@ -270,7 +272,13 @@ export function AuditSection({ frame }) {
|
|
|
270
272
|
key: 'time',
|
|
271
273
|
label: 'When',
|
|
272
274
|
icon: ClockIcon,
|
|
273
|
-
value: (e) => new Date(e.at).toLocaleTimeString(
|
|
275
|
+
value: (e) => new Date(e.at).toLocaleTimeString('en-US', {
|
|
276
|
+
timeZone: getPrefs().timeZone,
|
|
277
|
+
// Two-digit hours keep a column of times aligned.
|
|
278
|
+
hour: '2-digit',
|
|
279
|
+
minute: '2-digit',
|
|
280
|
+
timeZoneName: 'shortGeneric',
|
|
281
|
+
}),
|
|
274
282
|
format: 'text',
|
|
275
283
|
},
|
|
276
284
|
{
|
package/dist/people/shared.d.ts
CHANGED
|
@@ -6,7 +6,10 @@ import type { ViewChipTone } from '../view-model.js';
|
|
|
6
6
|
import type { PeopleAdapter, WithSync } from './client.js';
|
|
7
7
|
export declare const STATUS_LABEL: Record<PersonStatus, string>;
|
|
8
8
|
export declare const statusTone: (p: Pick<Person, 'status'>) => ViewChipTone | undefined;
|
|
9
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* `today 9:12 AM PT`, `yesterday 6:42 PM PT`, `Oct 24`, `Oct 24, 2025`; a dash for never. Days and
|
|
11
|
+
* times are the person's time zone (Settings ▸ Preferences), not the device's.
|
|
12
|
+
*/
|
|
10
13
|
export declare function when(ts: number | null | undefined, now?: number): string;
|
|
11
14
|
/** `in 5 d`, `in 3 h`, `2 d ago`: how far a time is from now. */
|
|
12
15
|
export declare function fromNow(ts: number | null | undefined, now?: number): string;
|
package/dist/people/shared.js
CHANGED
|
@@ -8,6 +8,7 @@ import { useCallback, useState } from 'react';
|
|
|
8
8
|
import { errorText } from '../integrations.js';
|
|
9
9
|
import { LumenSelector } from '../lumen-selector.js';
|
|
10
10
|
import { lumenToast } from '../lumen-toast.js';
|
|
11
|
+
import { getPrefs } from '../prefs.js';
|
|
11
12
|
export const STATUS_LABEL = {
|
|
12
13
|
active: 'Active',
|
|
13
14
|
invited: 'Invited',
|
|
@@ -22,24 +23,33 @@ export const statusTone = (p) => p.status === 'active'
|
|
|
22
23
|
? 'amber'
|
|
23
24
|
: 'muted';
|
|
24
25
|
const DAY = 86_400_000;
|
|
25
|
-
/** Non-breaking spaces, so `today
|
|
26
|
+
/** Non-breaking spaces, so `today 7:18 PM PT` and `in 7 d` never split across lines in any text. */
|
|
26
27
|
const glue = (s) => s.replace(/ /g, '\u00a0');
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
/**
|
|
29
|
+
* `today 9:12 AM PT`, `yesterday 6:42 PM PT`, `Oct 24`, `Oct 24, 2025`; a dash for never. Days and
|
|
30
|
+
* times are the person's time zone (Settings ▸ Preferences), not the device's.
|
|
31
|
+
*/
|
|
29
32
|
export function when(ts, now = Date.now()) {
|
|
30
33
|
if (!ts)
|
|
31
34
|
return '—';
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
-
|
|
35
|
+
const { timeZone } = getPrefs();
|
|
36
|
+
const day = (t) => new Date(t).toLocaleDateString('en-US', { timeZone });
|
|
37
|
+
const year = (t) => new Date(t).toLocaleDateString('en-US', { timeZone, year: 'numeric' });
|
|
38
|
+
const time = new Date(ts).toLocaleTimeString('en-US', {
|
|
39
|
+
timeZone,
|
|
40
|
+
hour: 'numeric',
|
|
41
|
+
minute: '2-digit',
|
|
42
|
+
timeZoneName: 'shortGeneric',
|
|
43
|
+
});
|
|
44
|
+
if (day(ts) === day(now))
|
|
36
45
|
return glue(`today ${time}`);
|
|
37
|
-
if (
|
|
46
|
+
if (day(ts) === day(now - DAY))
|
|
38
47
|
return glue(`yesterday ${time}`);
|
|
39
|
-
return glue(
|
|
48
|
+
return glue(new Date(ts).toLocaleDateString('en-US', {
|
|
49
|
+
timeZone,
|
|
40
50
|
day: 'numeric',
|
|
41
51
|
month: 'short',
|
|
42
|
-
...(
|
|
52
|
+
...(year(ts) === year(now) ? {} : { year: 'numeric' }),
|
|
43
53
|
}));
|
|
44
54
|
}
|
|
45
55
|
/** `in 5 d`, `in 3 h`, `2 d ago`: how far a time is from now. */
|
package/dist/prefs.d.ts
CHANGED
|
@@ -69,5 +69,7 @@ export declare const resolvedTheme: (p?: Prefs) => 'light' | 'dark';
|
|
|
69
69
|
export declare function setPref<K extends keyof Prefs>(key: K, value: Prefs[K]): void;
|
|
70
70
|
export declare const getPrefs: () => Prefs;
|
|
71
71
|
export declare function usePrefs(): Prefs;
|
|
72
|
+
/** A zone's short generic name, as people say it next to a time: `PT`, `ET`, `UTC` (never PDT or PST). */
|
|
73
|
+
export declare function zoneAbbr(tz: string, at?: Date): string;
|
|
72
74
|
/** `Pacific`, `UTC`: the short name shown next to times. */
|
|
73
75
|
export declare const timeZoneLabel: (id: TimeZoneId) => "America/Chicago" | "America/Los_Angeles" | "America/New_York" | "Central" | "Eastern" | "Pacific" | "UTC";
|
package/dist/prefs.js
CHANGED
|
@@ -130,5 +130,15 @@ export function usePrefs() {
|
|
|
130
130
|
return () => listeners.delete(cb);
|
|
131
131
|
}, getPrefs, () => DEFAULT_PREFS);
|
|
132
132
|
}
|
|
133
|
+
/** A zone's short generic name, as people say it next to a time: `PT`, `ET`, `UTC` (never PDT or PST). */
|
|
134
|
+
export function zoneAbbr(tz, at = new Date()) {
|
|
135
|
+
// Intl names UTC "GMT+0"; people say UTC.
|
|
136
|
+
if (/^(Etc\/)?(UTC|GMT|Universal|Zulu)$/.test(tz))
|
|
137
|
+
return 'UTC';
|
|
138
|
+
const part = new Intl.DateTimeFormat('en-US', { timeZone: tz, timeZoneName: 'shortGeneric' })
|
|
139
|
+
.formatToParts(at)
|
|
140
|
+
.find((p) => p.type === 'timeZoneName');
|
|
141
|
+
return part?.value ?? tz;
|
|
142
|
+
}
|
|
133
143
|
/** `Pacific`, `UTC`: the short name shown next to times. */
|
|
134
144
|
export const timeZoneLabel = (id) => TIME_ZONES.find((z) => z.id === id)?.label ?? id;
|
package/dist/schedules.js
CHANGED
|
@@ -11,7 +11,7 @@ import { LumenConfirm } from './confirm.js';
|
|
|
11
11
|
import { useIsMobile } from './hooks/use-mobile.js';
|
|
12
12
|
import { LumenSelector } from './lumen-selector.js';
|
|
13
13
|
import { IconButton } from './page.js';
|
|
14
|
-
import {
|
|
14
|
+
import { usePrefs, zoneAbbr } from './prefs.js';
|
|
15
15
|
import { useShell } from './shell.js';
|
|
16
16
|
import { Badge } from './ui/badge.js';
|
|
17
17
|
import { Button } from './ui/button.js';
|
|
@@ -47,14 +47,53 @@ const daysText = (days) => {
|
|
|
47
47
|
return 'Weekdays';
|
|
48
48
|
return d.map((x) => DAYS[x]).join(', ');
|
|
49
49
|
};
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
/** A window that spans the whole day on every day says nothing a person needs. */
|
|
51
|
+
const allDay = (s) => s.days.length === 7 && s.from === '00:00' && (s.to === '23:59' || s.to === '24:00');
|
|
52
|
+
/** The zone's offset from UTC at `at`, in ms (positive east of UTC). */
|
|
53
|
+
const offsetOf = (tz, at) => {
|
|
54
|
+
const p = Object.fromEntries(new Intl.DateTimeFormat('en-US', {
|
|
55
|
+
timeZone: tz,
|
|
56
|
+
hourCycle: 'h23',
|
|
57
|
+
year: 'numeric',
|
|
58
|
+
month: 'numeric',
|
|
59
|
+
day: 'numeric',
|
|
60
|
+
hour: 'numeric',
|
|
61
|
+
minute: 'numeric',
|
|
62
|
+
})
|
|
63
|
+
.formatToParts(at)
|
|
64
|
+
.map((x) => [x.type, Number(x.value)]));
|
|
65
|
+
const n = (k) => p[k] ?? 0;
|
|
66
|
+
return (Date.UTC(n('year'), n('month') - 1, n('day'), n('hour'), n('minute')) -
|
|
67
|
+
Math.floor(at / 60_000) * 60_000);
|
|
68
|
+
};
|
|
69
|
+
/** `03:15` in `from` (today) as the same moment's clock in `to`: `03:15` UTC → `8:15 PM` PT. */
|
|
70
|
+
const clockIn = (hhmm, from, to) => {
|
|
71
|
+
if (from === to)
|
|
72
|
+
return clockText(hhmm);
|
|
73
|
+
const [h = 0, m = 0] = hhmm.split(':').map(Number);
|
|
74
|
+
const now = new Date();
|
|
75
|
+
const wall = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), h, m);
|
|
76
|
+
const at = wall - offsetOf(from, wall);
|
|
77
|
+
return new Date(at).toLocaleTimeString('en-US', {
|
|
78
|
+
timeZone: to,
|
|
79
|
+
hour: 'numeric',
|
|
80
|
+
minute: '2-digit',
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* When it runs, in words, in the viewer's zone (`zone`, the person's preference): `Every 6 h`,
|
|
85
|
+
* `Every 1 h · Weekdays 6:00 AM–2:00 PM PT`, `Daily at 8:15 PM PT`. Days stay the schedule's own.
|
|
86
|
+
*/
|
|
87
|
+
function when(s, zone) {
|
|
53
88
|
if (s.when)
|
|
54
89
|
return s.when;
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
90
|
+
const at = (hhmm) => clockIn(hhmm, s.tz, zone);
|
|
91
|
+
if (s.kind === 'daily')
|
|
92
|
+
return `Daily at ${at(s.from)} ${zoneAbbr(zone)}`;
|
|
93
|
+
const every = `Every ${everyText(s.every)}`;
|
|
94
|
+
if (allDay(s))
|
|
95
|
+
return every;
|
|
96
|
+
return `${every} · ${daysText(s.days)} ${at(s.from)}–${at(s.to)} ${zoneAbbr(zone)}`;
|
|
58
97
|
}
|
|
59
98
|
function useTime() {
|
|
60
99
|
const { timeZone } = usePrefs();
|
|
@@ -63,6 +102,7 @@ function useTime() {
|
|
|
63
102
|
weekday: 'short',
|
|
64
103
|
hour: 'numeric',
|
|
65
104
|
minute: '2-digit',
|
|
105
|
+
timeZoneName: 'shortGeneric',
|
|
66
106
|
});
|
|
67
107
|
}
|
|
68
108
|
function Last({ s, compact = false }) {
|
|
@@ -90,6 +130,7 @@ function TimeFields({ kind, draft, set, }) {
|
|
|
90
130
|
function Editor({ s, onSave, onRun, onRuns, recent, briefs, onClose, }) {
|
|
91
131
|
const assistantName = useAssistantName();
|
|
92
132
|
const time = useTime();
|
|
133
|
+
const { timeZone } = usePrefs();
|
|
93
134
|
const assistant = useAssistant();
|
|
94
135
|
const { closeSettings } = useShell();
|
|
95
136
|
const [brief, setBrief] = useState({
|
|
@@ -141,7 +182,7 @@ function Editor({ s, onSave, onRun, onRuns, recent, briefs, onClose, }) {
|
|
|
141
182
|
setBusy(null);
|
|
142
183
|
}
|
|
143
184
|
};
|
|
144
|
-
return (_jsxs(_Fragment, { children: [_jsxs(SheetHeader, { children: [_jsx(SheetTitle, { children: s.name }), _jsx(SheetDescription, { children: when(s) })] }), _jsxs("div", { className: "grid grid-cols-1 gap-5 overflow-y-auto px-4", children: [_jsx("dl", { className: "grid grid-cols-[auto_1fr] gap-x-3 gap-y-1.5 text-xs", children: [
|
|
185
|
+
return (_jsxs(_Fragment, { children: [_jsxs(SheetHeader, { children: [_jsx(SheetTitle, { children: s.name }), _jsx(SheetDescription, { children: when(s, timeZone) })] }), _jsxs("div", { className: "grid grid-cols-1 gap-5 overflow-y-auto px-4", children: [_jsx("dl", { className: "grid grid-cols-[auto_1fr] gap-x-3 gap-y-1.5 text-xs", children: [
|
|
145
186
|
['Inspects', s.about.inspects],
|
|
146
187
|
['Processes', s.about.processes],
|
|
147
188
|
['Enriches', s.about.enriches],
|
|
@@ -166,6 +207,7 @@ function Editor({ s, onSave, onRun, onRuns, recent, briefs, onClose, }) {
|
|
|
166
207
|
/** The Settings ▸ Schedules section's body. */
|
|
167
208
|
export function SchedulesSection({ schedules, error, onSave, onRun, onRuns, recent, briefs, }) {
|
|
168
209
|
const isMobile = useIsMobile();
|
|
210
|
+
const { timeZone } = usePrefs();
|
|
169
211
|
const { closeSettings } = useShell();
|
|
170
212
|
const [open, setOpen] = useState(null);
|
|
171
213
|
const [adding, setAdding] = useState(false);
|
|
@@ -178,15 +220,14 @@ export function SchedulesSection({ schedules, error, onSave, onRun, onRuns, rece
|
|
|
178
220
|
}
|
|
179
221
|
: undefined;
|
|
180
222
|
const editing = schedules?.find((s) => s.id === open);
|
|
181
|
-
const { timeZone } = usePrefs();
|
|
182
223
|
if (error)
|
|
183
224
|
return _jsx("p", { className: "text-sm text-loss", children: error });
|
|
184
225
|
if (!schedules)
|
|
185
226
|
return _jsx(Skeleton, { className: "h-48 w-full" });
|
|
186
|
-
return (_jsxs(_Fragment, { children: [isMobile ? (_jsx("div", { className: "grid grid-cols-1 divide-y rounded-lg border", children: schedules.map((s) => (_jsxs("button", { type: "button", className: "grid grid-cols-[1fr_auto] items-center gap-x-3 gap-y-1 p-3 text-left hover:bg-muted/50", onClick: () => setOpen(s.id), children: [_jsx("span", { className: "font-medium", children: s.name }), _jsx(OnSwitch, { s: s, onSave: onSave }),
|
|
227
|
+
return (_jsxs(_Fragment, { children: [isMobile ? (_jsx("div", { className: "grid grid-cols-1 divide-y rounded-lg border", children: schedules.map((s) => (_jsxs("button", { type: "button", className: "grid grid-cols-[1fr_auto] items-center gap-x-3 gap-y-1 p-3 text-left hover:bg-muted/50", onClick: () => setOpen(s.id), children: [_jsx("span", { className: "font-medium", children: s.name }), _jsx(OnSwitch, { s: s, onSave: onSave }), _jsx("span", { className: "col-span-2 text-xs text-muted-foreground", children: when(s, timeZone) }), _jsx("span", { className: "col-span-2 text-xs", children: s.waiting ? _jsx("span", { className: "text-warning", children: s.waiting }) : _jsx(Last, { s: s }) })] }, s.id))) })) : (_jsx("div", { className: "overflow-x-auto overflow-y-hidden rounded-lg border", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { className: "w-12", children: "On" }), _jsx(TableHead, { children: "Schedule" }), _jsx(TableHead, { children: "When" }), _jsx(TableHead, { children: "Last run" }), onRuns ? _jsx(TableHead, { className: "w-0" }) : null] }) }), _jsx(TableBody, { children: schedules.map((s) => (_jsxs(TableRow, { className: "cursor-pointer", onClick: () => setOpen(s.id), children: [_jsx(TableCell, { children: _jsx(OnSwitch, { s: s, onSave: onSave }) }), _jsx(TableCell, { className: "font-medium", children: s.name }), _jsx(TableCell, { className: "min-w-48 whitespace-normal text-muted-foreground", children: when(s, timeZone) }), _jsx(TableCell, { className: "max-w-48", children: s.waiting ? (_jsx(Badge, { variant: "outline", className: "text-warning", title: s.waiting, children: "Not connected" })) : (_jsx(Last, { s: s, compact: true })) }), onRuns ? (_jsx(TableCell, { children: _jsx(IconButton, { icon: ListIcon, label: `${s.name} runs`, onClick: (e) => {
|
|
187
228
|
e.stopPropagation();
|
|
188
229
|
toRuns?.(s.id);
|
|
189
|
-
} }) })) : null] }, s.id))) })] }) })), _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [
|
|
230
|
+
} }) })) : null] }, s.id))) })] }) })), _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [onRuns ? (_jsx("p", { className: "text-xs text-muted-foreground", children: "Each run is a line in Logs." })) : (_jsx("span", {})), briefs ? (_jsxs(Button, { variant: "outline", size: "sm", onClick: () => setAdding(true), children: [_jsx(PlusIcon, {}), "Brief"] })) : null] }), briefs ? (_jsx(Sheet, { open: adding, onOpenChange: setAdding, children: _jsx(SheetContent, { side: isMobile ? 'bottom' : 'right', className: "gap-4", children: _jsx(NewBrief, { create: async (b, time) => {
|
|
190
231
|
const id = await briefs.create(b);
|
|
191
232
|
await onSave(id, time);
|
|
192
233
|
return id;
|
package/dist/view-formulas.js
CHANGED
|
@@ -30,6 +30,7 @@ export const FORMAT_LABELS = {
|
|
|
30
30
|
price_signed: 'Currency ±',
|
|
31
31
|
weight: 'Percent',
|
|
32
32
|
percent: 'Percent ±',
|
|
33
|
+
bytes: 'Size',
|
|
33
34
|
text: 'Text',
|
|
34
35
|
};
|
|
35
36
|
/**
|
|
@@ -47,6 +48,7 @@ const FORMAT_GLYPHS = {
|
|
|
47
48
|
price_signed: '±$',
|
|
48
49
|
weight: '%',
|
|
49
50
|
percent: '±%',
|
|
51
|
+
bytes: 'B',
|
|
50
52
|
text: 'Aa',
|
|
51
53
|
};
|
|
52
54
|
/** Text fields whose icon is a calendar are dates (no date format exists; the icon says it). */
|
|
@@ -74,6 +76,7 @@ const FORMAT_EXAMPLES = {
|
|
|
74
76
|
price_signed: '+$1,234.56',
|
|
75
77
|
weight: '12.3%',
|
|
76
78
|
percent: '+12.3%',
|
|
79
|
+
bytes: '812 KB · 13.1 GB',
|
|
77
80
|
text: 'Abc',
|
|
78
81
|
};
|
|
79
82
|
/** The number formats as picker options, each with how it reads. */
|
package/dist/view-model.d.ts
CHANGED
|
@@ -353,7 +353,7 @@ export declare const formulasOf: (c: CustomViewConfig) => {
|
|
|
353
353
|
key: string;
|
|
354
354
|
label: string;
|
|
355
355
|
expr: string;
|
|
356
|
-
format: "compact" | "integer" | "number" | "percent" | "price" | "price_rounded" | "price_signed" | "rounded" | "signed" | "text" | "weight";
|
|
356
|
+
format: "bytes" | "compact" | "integer" | "number" | "percent" | "price" | "price_rounded" | "price_signed" | "rounded" | "signed" | "text" | "weight";
|
|
357
357
|
agg: "count" | "distinct" | "first" | "last" | "max" | "mean" | "min" | "none" | "sum" | null;
|
|
358
358
|
}[];
|
|
359
359
|
/** Fields listed under Columns but not shown, for their Filter and Group ticks; views saved before it have none. */
|
|
@@ -95,7 +95,7 @@ export declare const readsRows: (card: ViewWidget) => card is {
|
|
|
95
95
|
key: string;
|
|
96
96
|
placement: "column" | "inline";
|
|
97
97
|
}[];
|
|
98
|
-
formats?: Record<string, "compact" | "integer" | "number" | "percent" | "price" | "price_rounded" | "price_signed" | "rounded" | "signed" | "weight"> | undefined;
|
|
98
|
+
formats?: Record<string, "bytes" | "compact" | "integer" | "number" | "percent" | "price" | "price_rounded" | "price_signed" | "rounded" | "signed" | "weight"> | undefined;
|
|
99
99
|
aggs?: Record<string, "count" | "distinct" | "first" | "last" | "max" | "mean" | "min" | "none" | "sum"> | undefined;
|
|
100
100
|
shows?: Record<string, string> | undefined;
|
|
101
101
|
group: {
|
package/dist/widget-values.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* field. Shared by the widget frame and its chart bodies, so a lazily loaded body never pulls the
|
|
4
4
|
* frame back in.
|
|
5
5
|
*/
|
|
6
|
-
import { isDollarFormat } from '@columenlabs/lumen-ui-types/views';
|
|
6
|
+
import { formatBytes, isDollarFormat } from '@columenlabs/lumen-ui-types/views';
|
|
7
7
|
import { maskAmount } from './shell.js';
|
|
8
8
|
import { label } from './view-model.js';
|
|
9
9
|
export const MINUS = '−';
|
|
@@ -19,6 +19,8 @@ export function short(v, format, hidden) {
|
|
|
19
19
|
const abs = Math.abs(v);
|
|
20
20
|
if (format === 'percent' || format === 'weight')
|
|
21
21
|
return `${sign}${abs.toFixed(1)}%`;
|
|
22
|
+
if (format === 'bytes')
|
|
23
|
+
return `${sign}${formatBytes(abs)}`;
|
|
22
24
|
if (isDollarFormat(format))
|
|
23
25
|
return `${sign}$${COMPACT.format(abs)}`;
|
|
24
26
|
return `${sign}${COMPACT.format(abs)}`;
|
|
@@ -31,6 +33,8 @@ export function exact(v, format, hidden) {
|
|
|
31
33
|
const abs = Math.abs(v);
|
|
32
34
|
if (format === 'percent' || format === 'weight')
|
|
33
35
|
return `${sign}${abs.toFixed(2)}%`;
|
|
36
|
+
if (format === 'bytes')
|
|
37
|
+
return `${sign}${formatBytes(abs)}`;
|
|
34
38
|
const n = abs.toLocaleString('en-US', {
|
|
35
39
|
maximumFractionDigits: format === 'rounded' || format === 'price_rounded' ? 0 : 2,
|
|
36
40
|
useGrouping: format !== 'integer',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@columenlabs/lumen-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.102",
|
|
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.102",
|
|
53
53
|
"@dnd-kit/core": "6.3.1",
|
|
54
54
|
"@dnd-kit/sortable": "10.0.0",
|
|
55
55
|
"@dnd-kit/utilities": "3.2.2",
|