@columenlabs/lumen-ui 0.0.0-stage → 0.2.0
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/LICENSE +21 -0
- package/NOTICE +12 -0
- package/README.md +4 -2
- package/dist/add-column.d.ts +23 -0
- package/dist/add-column.js +62 -0
- package/dist/api-fetch.d.ts +9 -0
- package/dist/api-fetch.js +60 -0
- package/dist/app-switcher.d.ts +21 -0
- package/dist/app-switcher.js +12 -0
- package/dist/app-views.d.ts +18 -0
- package/dist/app-views.js +41 -0
- package/dist/assistant-context.d.ts +72 -0
- package/dist/assistant-context.js +30 -0
- package/dist/assistant-panel.d.ts +58 -0
- package/dist/assistant-panel.js +244 -0
- package/dist/assistant-screen.d.ts +124 -0
- package/dist/assistant-screen.js +252 -0
- package/dist/card-switch.d.ts +16 -0
- package/dist/card-switch.js +16 -0
- package/dist/color-dot.d.ts +14 -0
- package/dist/color-dot.js +19 -0
- package/dist/confirm.d.ts +18 -0
- package/dist/confirm.js +5 -0
- package/dist/connections.d.ts +45 -0
- package/dist/connections.js +275 -0
- package/dist/coverage-calendar-map.d.ts +27 -0
- package/dist/coverage-calendar-map.js +84 -0
- package/dist/coverage-cells.d.ts +55 -0
- package/dist/coverage-cells.js +85 -0
- package/dist/coverage-map.d.ts +112 -0
- package/dist/coverage-map.js +428 -0
- package/dist/details-table.d.ts +20 -0
- package/dist/details-table.js +13 -0
- package/dist/drawer-section.d.ts +27 -0
- package/dist/drawer-section.js +23 -0
- package/dist/floating-head.d.ts +2 -0
- package/dist/floating-head.js +57 -0
- package/dist/fold.d.ts +16 -0
- package/dist/fold.js +70 -0
- package/dist/form-issues.d.ts +32 -0
- package/dist/form-issues.js +60 -0
- package/dist/formula-input.d.ts +17 -0
- package/dist/formula-input.js +104 -0
- package/dist/frame-script.d.ts +7 -0
- package/dist/frame-script.js +103 -0
- package/dist/freshness.d.ts +24 -0
- package/dist/freshness.js +99 -0
- package/dist/group-button.d.ts +33 -0
- package/dist/group-button.js +67 -0
- package/dist/growth-chart.d.ts +77 -0
- package/dist/growth-chart.js +195 -0
- package/dist/hooks/use-keyboard-fit.d.ts +15 -0
- package/dist/hooks/use-keyboard-fit.js +29 -0
- package/dist/hooks/use-mobile.d.ts +1 -0
- package/dist/hooks/use-mobile.js +15 -0
- package/dist/icon-button.d.ts +21 -0
- package/dist/icon-button.js +22 -0
- package/dist/icon-picker.d.ts +14 -0
- package/dist/icon-picker.js +36 -0
- package/dist/index.d.ts +83 -0
- package/dist/index.js +90 -0
- package/dist/integrations.d.ts +57 -0
- package/dist/integrations.js +73 -0
- package/dist/joins.d.ts +128 -0
- package/dist/joins.js +767 -0
- package/dist/kit-config.d.ts +13 -0
- package/dist/kit-config.js +15 -0
- package/dist/lib/utils.d.ts +2 -0
- package/dist/lib/utils.js +5 -0
- package/dist/lumen-boundary.d.ts +7 -0
- package/dist/lumen-boundary.js +10 -0
- package/dist/lumen-code-editor-impl.d.ts +2 -0
- package/dist/lumen-code-editor-impl.js +16 -0
- package/dist/lumen-code-editor.d.ts +11 -0
- package/dist/lumen-code-editor.js +11 -0
- package/dist/lumen-code.d.ts +22 -0
- package/dist/lumen-code.js +75 -0
- package/dist/lumen-drawer.d.ts +30 -0
- package/dist/lumen-drawer.js +27 -0
- package/dist/lumen-echart.d.ts +58 -0
- package/dist/lumen-echart.js +129 -0
- package/dist/lumen-filter.d.ts +75 -0
- package/dist/lumen-filter.js +546 -0
- package/dist/lumen-finder.d.ts +62 -0
- package/dist/lumen-finder.js +275 -0
- package/dist/lumen-form.d.ts +34 -0
- package/dist/lumen-form.js +81 -0
- package/dist/lumen-geo-map.d.ts +9 -0
- package/dist/lumen-geo-map.js +135 -0
- package/dist/lumen-grid.d.ts +49 -0
- package/dist/lumen-grid.js +203 -0
- package/dist/lumen-layout.d.ts +150 -0
- package/dist/lumen-layout.js +658 -0
- package/dist/lumen-links.d.ts +96 -0
- package/dist/lumen-links.js +213 -0
- package/dist/lumen-page-state.d.ts +20 -0
- package/dist/lumen-page-state.js +15 -0
- package/dist/lumen-selector.d.ts +139 -0
- package/dist/lumen-selector.js +308 -0
- package/dist/lumen-source.d.ts +137 -0
- package/dist/lumen-source.js +327 -0
- package/dist/lumen-tab-drawer.d.ts +27 -0
- package/dist/lumen-tab-drawer.js +26 -0
- package/dist/lumen-tabs.d.ts +21 -0
- package/dist/lumen-tabs.js +6 -0
- package/dist/lumen-toast.d.ts +11 -0
- package/dist/lumen-toast.js +16 -0
- package/dist/lumen-trend.d.ts +18 -0
- package/dist/lumen-trend.js +157 -0
- package/dist/lumen-view-card.d.ts +35 -0
- package/dist/lumen-view-card.js +50 -0
- package/dist/lumen-view.d.ts +172 -0
- package/dist/lumen-view.js +645 -0
- package/dist/lumen-widget-charts.d.ts +57 -0
- package/dist/lumen-widget-charts.js +69 -0
- package/dist/lumen-widget.d.ts +614 -0
- package/dist/lumen-widget.js +594 -0
- package/dist/market/compare-chart.d.ts +34 -0
- package/dist/market/compare-chart.js +292 -0
- package/dist/market/indicator-picker.d.ts +29 -0
- package/dist/market/indicator-picker.js +125 -0
- package/dist/market/indicators.d.ts +63 -0
- package/dist/market/indicators.js +314 -0
- package/dist/market/live.d.ts +120 -0
- package/dist/market/live.js +444 -0
- package/dist/market/lumen-chart.d.ts +88 -0
- package/dist/market/lumen-chart.js +645 -0
- package/dist/market/market-widget.d.ts +45 -0
- package/dist/market/market-widget.js +346 -0
- package/dist/market/option-chain.d.ts +19 -0
- package/dist/market/option-chain.js +53 -0
- package/dist/market/price-chart.d.ts +128 -0
- package/dist/market/price-chart.js +619 -0
- package/dist/mcp-settings.d.ts +20 -0
- package/dist/mcp-settings.js +65 -0
- package/dist/nav.d.ts +77 -0
- package/dist/nav.js +73 -0
- package/dist/note-line.d.ts +13 -0
- package/dist/note-line.js +5 -0
- package/dist/notifications.d.ts +9 -0
- package/dist/notifications.js +166 -0
- package/dist/origin-legend.d.ts +14 -0
- package/dist/origin-legend.js +38 -0
- package/dist/overview.d.ts +84 -0
- package/dist/overview.js +105 -0
- package/dist/page-inputs.d.ts +14 -0
- package/dist/page-inputs.js +16 -0
- package/dist/page.d.ts +32 -0
- package/dist/page.js +45 -0
- package/dist/pager.d.ts +10 -0
- package/dist/pager.js +14 -0
- package/dist/prefs.d.ts +71 -0
- package/dist/prefs.js +126 -0
- package/dist/query-client.d.ts +7 -0
- package/dist/query-client.js +19 -0
- package/dist/refresh.d.ts +4 -0
- package/dist/refresh.js +19 -0
- package/dist/rename-field.d.ts +11 -0
- package/dist/rename-field.js +26 -0
- package/dist/reset-button.d.ts +9 -0
- package/dist/reset-button.js +10 -0
- package/dist/route-error.d.ts +42 -0
- package/dist/route-error.js +229 -0
- package/dist/schedules.d.ts +88 -0
- package/dist/schedules.js +221 -0
- package/dist/settings.d.ts +51 -0
- package/dist/settings.js +181 -0
- package/dist/shell.d.ts +99 -0
- package/dist/shell.js +137 -0
- package/dist/source-health.d.ts +12 -0
- package/dist/source-health.js +58 -0
- package/dist/spotlight.d.ts +23 -0
- package/dist/spotlight.js +138 -0
- package/dist/status-health.d.ts +16 -0
- package/dist/status-health.js +41 -0
- package/dist/status.d.ts +9 -0
- package/dist/status.js +12 -0
- package/dist/summary-cards.d.ts +56 -0
- package/dist/summary-cards.js +270 -0
- package/dist/symbol-sheet-context.d.ts +4 -0
- package/dist/symbol-sheet-context.js +5 -0
- package/dist/symbol-sheet.d.ts +9 -0
- package/dist/symbol-sheet.js +24 -0
- package/dist/tasks.d.ts +29 -0
- package/dist/tasks.js +278 -0
- package/dist/theme.css +606 -0
- package/dist/tile.d.ts +37 -0
- package/dist/tile.js +23 -0
- package/dist/ui/alert-dialog.d.ts +18 -0
- package/dist/ui/alert-dialog.js +41 -0
- package/dist/ui/alert.d.ts +10 -0
- package/dist/ui/alert.js +27 -0
- package/dist/ui/avatar.d.ts +11 -0
- package/dist/ui/avatar.js +22 -0
- package/dist/ui/badge.d.ts +9 -0
- package/dist/ui/badge.js +27 -0
- package/dist/ui/breadcrumb.d.ts +11 -0
- package/dist/ui/breadcrumb.js +27 -0
- package/dist/ui/button-group.d.ts +11 -0
- package/dist/ui/button-group.js +27 -0
- package/dist/ui/button.d.ts +10 -0
- package/dist/ui/button.js +38 -0
- package/dist/ui/card.d.ts +11 -0
- package/dist/ui/card.js +24 -0
- package/dist/ui/chart.d.ts +44 -0
- package/dist/ui/chart.js +117 -0
- package/dist/ui/checkbox.d.ts +4 -0
- package/dist/ui/checkbox.js +8 -0
- package/dist/ui/collapsible.d.ts +5 -0
- package/dist/ui/collapsible.js +12 -0
- package/dist/ui/combobox.d.ts +24 -0
- package/dist/ui/combobox.js +60 -0
- package/dist/ui/command.d.ts +18 -0
- package/dist/ui/command.js +34 -0
- package/dist/ui/dialog.d.ts +23 -0
- package/dist/ui/dialog.js +50 -0
- package/dist/ui/dropdown-menu.d.ts +29 -0
- package/dist/ui/dropdown-menu.js +50 -0
- package/dist/ui/emoji-picker.d.ts +6 -0
- package/dist/ui/emoji-picker.js +28 -0
- package/dist/ui/empty.d.ts +11 -0
- package/dist/ui/empty.js +33 -0
- package/dist/ui/field.d.ts +24 -0
- package/dist/ui/field.js +65 -0
- package/dist/ui/hover-card.d.ts +6 -0
- package/dist/ui/hover-card.js +13 -0
- package/dist/ui/input-group.d.ts +16 -0
- package/dist/ui/input-group.js +56 -0
- package/dist/ui/input.d.ts +3 -0
- package/dist/ui/input.js +6 -0
- package/dist/ui/item.d.ts +23 -0
- package/dist/ui/item.js +67 -0
- package/dist/ui/kbd.d.ts +3 -0
- package/dist/ui/kbd.js +9 -0
- package/dist/ui/label.d.ts +4 -0
- package/dist/ui/label.js +7 -0
- package/dist/ui/popover.d.ts +10 -0
- package/dist/ui/popover.js +25 -0
- package/dist/ui/progress.d.ts +4 -0
- package/dist/ui/progress.js +9 -0
- package/dist/ui/resizable.d.ts +7 -0
- package/dist/ui/resizable.js +13 -0
- package/dist/ui/scroll-area.d.ts +5 -0
- package/dist/ui/scroll-area.js +10 -0
- package/dist/ui/select.d.ts +15 -0
- package/dist/ui/select.js +36 -0
- package/dist/ui/separator.d.ts +4 -0
- package/dist/ui/separator.js +7 -0
- package/dist/ui/sheet.d.ts +14 -0
- package/dist/ui/sheet.js +54 -0
- package/dist/ui/sidebar.d.ts +69 -0
- package/dist/ui/sidebar.js +206 -0
- package/dist/ui/skeleton.d.ts +2 -0
- package/dist/ui/skeleton.js +6 -0
- package/dist/ui/slider.d.ts +4 -0
- package/dist/ui/slider.js +9 -0
- package/dist/ui/sonner.d.ts +3 -0
- package/dist/ui/sonner.js +24 -0
- package/dist/ui/spinner.d.ts +2 -0
- package/dist/ui/spinner.js +7 -0
- package/dist/ui/switch.d.ts +6 -0
- package/dist/ui/switch.js +7 -0
- package/dist/ui/table.d.ts +10 -0
- package/dist/ui/table.js +31 -0
- package/dist/ui/tabs.d.ts +7 -0
- package/dist/ui/tabs.js +22 -0
- package/dist/ui/textarea.d.ts +3 -0
- package/dist/ui/textarea.js +6 -0
- package/dist/ui/toggle-group.d.ts +10 -0
- package/dist/ui/toggle-group.js +22 -0
- package/dist/ui/toggle.d.ts +9 -0
- package/dist/ui/toggle.js +25 -0
- package/dist/ui/tooltip.d.ts +7 -0
- package/dist/ui/tooltip.js +16 -0
- package/dist/view-edit-cell.d.ts +49 -0
- package/dist/view-edit-cell.js +198 -0
- package/dist/view-editor.d.ts +52 -0
- package/dist/view-editor.js +940 -0
- package/dist/view-formulas.d.ts +61 -0
- package/dist/view-formulas.js +247 -0
- package/dist/view-icons.d.ts +20 -0
- package/dist/view-icons.js +70 -0
- package/dist/view-model.d.ts +390 -0
- package/dist/view-model.js +316 -0
- package/dist/whats-new.d.ts +3 -0
- package/dist/whats-new.js +53 -0
- package/dist/widget-controls.d.ts +9 -0
- package/dist/widget-controls.js +26 -0
- package/dist/widget-settings.d.ts +436 -0
- package/dist/widget-settings.js +290 -0
- package/dist/widget-thumbs.d.ts +3 -0
- package/dist/widget-thumbs.js +29 -0
- package/dist/workspace-keys.d.ts +12 -0
- package/dist/workspace-keys.js +13 -0
- package/dist/workspaces.d.ts +57 -0
- package/dist/workspaces.js +473 -0
- package/package.json +82 -4
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// Edit widget's own parts (one editor for every widget). Every widget opens the
|
|
3
|
+
// same editor (view-editor.tsx): Widget (type, name, width, link colour), Fields, Settings, Rows,
|
|
4
|
+
// Formulas, Controls, with the field tree beside it. A table's Fields are its columns; any other
|
|
5
|
+
// widget's are its slots (By and Value, Date and Values, a card's number), which the tree fills the
|
|
6
|
+
// same way it ticks a column. Settings are the picked style's own options. Switching the type
|
|
7
|
+
// swaps only these two parts.
|
|
8
|
+
import { OUTLOOK_INDEXES, marketWindows, } from '@columenlabs/lumen-ui-types/market';
|
|
9
|
+
import { ALLOCATION_MAX, isOwnTile, isRowCard, isTableCard, trendBuckets, widgetStyleId, } from '@columenlabs/lumen-ui-types/views';
|
|
10
|
+
import { ArrowDownIcon, ArrowUpIcon } from 'lucide-react';
|
|
11
|
+
import { useState } from 'react';
|
|
12
|
+
import { LumenSelector } from './lumen-selector.js';
|
|
13
|
+
import { SummaryCardsEditor, summaryCardIssues } from './summary-cards.js';
|
|
14
|
+
import { Input } from './ui/input.js';
|
|
15
|
+
import { Label } from './ui/label.js';
|
|
16
|
+
import { Switch } from './ui/switch.js';
|
|
17
|
+
import { ToggleGroup, ToggleGroupItem } from './ui/toggle-group.js';
|
|
18
|
+
import { MAX_TILES, fieldOptions } from './view-formulas.js';
|
|
19
|
+
/** A label beside its control, the same in every section of Edit widget. */
|
|
20
|
+
export function EditorRow({ label, children }) {
|
|
21
|
+
return (_jsxs("div", { className: "grid grid-cols-[5rem_minmax(0,1fr)] items-center gap-2", children: [_jsx("span", { className: "text-sm text-muted-foreground", children: label }), _jsx("div", { className: "min-w-0", children: children })] }));
|
|
22
|
+
}
|
|
23
|
+
export const isTextField = (f) => f.format === 'text' && !f.render;
|
|
24
|
+
export const isNumberField = (f) => f.format !== 'text' && !f.render;
|
|
25
|
+
const DATE_LIKE = /(^|_)(date|day|at|time|month|week|year)($|_)/i;
|
|
26
|
+
/** A trend's date candidates: text fields, the date-like ones first. */
|
|
27
|
+
export const dateFields = (domain) => {
|
|
28
|
+
// A rendered text field still reads its raw value (a report date drawn as Tue, Oct 20).
|
|
29
|
+
const texts = domain.fields.filter((f) => f.format === 'text');
|
|
30
|
+
return [
|
|
31
|
+
...texts.filter((f) => DATE_LIKE.test(f.key)),
|
|
32
|
+
...texts.filter((f) => !DATE_LIKE.test(f.key)),
|
|
33
|
+
];
|
|
34
|
+
};
|
|
35
|
+
/** The most columns a Top N shows. */
|
|
36
|
+
const TOP_N_COLUMNS = 8;
|
|
37
|
+
/** How many values a trend draws, a spark or tracker one. */
|
|
38
|
+
const TREND_VALUES = 4;
|
|
39
|
+
/** A free Summary card key. */
|
|
40
|
+
const freeTileKey = (tiles, taken) => {
|
|
41
|
+
let n = tiles.length + 1;
|
|
42
|
+
while (tiles.some((t) => t.key === `tile_${n}`) || taken.includes(`tile_${n}`))
|
|
43
|
+
n++;
|
|
44
|
+
return `tile_${n}`;
|
|
45
|
+
};
|
|
46
|
+
/** A widget's slots, in the order the editor lists them; none for a chart of symbols. */
|
|
47
|
+
export function widgetSlots(card, domain) {
|
|
48
|
+
const label = (k) => domain.fields.find((f) => f.key === k)?.label ?? k;
|
|
49
|
+
const text = { accepts: isTextField, why: 'Takes a text field' };
|
|
50
|
+
const number = { accepts: isNumberField, why: 'Takes a number' };
|
|
51
|
+
if ('by' in card)
|
|
52
|
+
return [
|
|
53
|
+
{
|
|
54
|
+
id: 'by',
|
|
55
|
+
label: 'By',
|
|
56
|
+
max: 1,
|
|
57
|
+
...text,
|
|
58
|
+
keys: [card.by],
|
|
59
|
+
set: ([by]) => ({ ...card, by: by ?? card.by }),
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: 'value',
|
|
63
|
+
label: 'Value',
|
|
64
|
+
max: 1,
|
|
65
|
+
...number,
|
|
66
|
+
keys: [card.value],
|
|
67
|
+
set: ([value]) => ({ ...card, value: value ?? card.value }),
|
|
68
|
+
},
|
|
69
|
+
];
|
|
70
|
+
if (card.kind === 'table')
|
|
71
|
+
return [
|
|
72
|
+
{
|
|
73
|
+
id: 'columns',
|
|
74
|
+
label: 'Columns',
|
|
75
|
+
max: TOP_N_COLUMNS,
|
|
76
|
+
accepts: (f) => !f.render,
|
|
77
|
+
why: 'Drawn by the page only',
|
|
78
|
+
keys: card.columns,
|
|
79
|
+
set: (columns) => (columns.length ? { ...card, columns } : card),
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
id: 'sort',
|
|
83
|
+
label: 'Sort by',
|
|
84
|
+
max: 1,
|
|
85
|
+
accepts: (f) => !f.render,
|
|
86
|
+
why: 'Drawn by the page only',
|
|
87
|
+
keys: [card.sort],
|
|
88
|
+
set: ([sort]) => ({ ...card, sort: sort ?? card.sort }),
|
|
89
|
+
},
|
|
90
|
+
];
|
|
91
|
+
if ('date' in card) {
|
|
92
|
+
const one = card.style === 'spark' || card.style === 'tracker';
|
|
93
|
+
const dates = new Set(dateFields(domain).map((f) => f.key));
|
|
94
|
+
return [
|
|
95
|
+
{
|
|
96
|
+
id: 'date',
|
|
97
|
+
label: 'Date',
|
|
98
|
+
max: 1,
|
|
99
|
+
accepts: (f) => dates.has(f.key),
|
|
100
|
+
why: 'Takes a date',
|
|
101
|
+
keys: [card.date],
|
|
102
|
+
set: ([date]) => ({ ...card, date: date ?? card.date }),
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
id: 'values',
|
|
106
|
+
label: 'Values',
|
|
107
|
+
max: one ? 1 : TREND_VALUES,
|
|
108
|
+
...number,
|
|
109
|
+
keys: card.values,
|
|
110
|
+
set: (values) => (values.length ? { ...card, values } : card),
|
|
111
|
+
},
|
|
112
|
+
];
|
|
113
|
+
}
|
|
114
|
+
if ('tile' in card) {
|
|
115
|
+
const t = card.tile;
|
|
116
|
+
return [
|
|
117
|
+
{
|
|
118
|
+
id: 'number',
|
|
119
|
+
label: 'Number',
|
|
120
|
+
max: 1,
|
|
121
|
+
...number,
|
|
122
|
+
keys: isOwnTile(t) ? [t.field] : [],
|
|
123
|
+
set: ([field]) => field
|
|
124
|
+
? {
|
|
125
|
+
...card,
|
|
126
|
+
tile: isOwnTile(t)
|
|
127
|
+
? { ...t, field }
|
|
128
|
+
: {
|
|
129
|
+
key: t.key,
|
|
130
|
+
label: label(field),
|
|
131
|
+
field,
|
|
132
|
+
agg: 'sum',
|
|
133
|
+
filter: [],
|
|
134
|
+
sign: false,
|
|
135
|
+
lines: [],
|
|
136
|
+
},
|
|
137
|
+
}
|
|
138
|
+
: card,
|
|
139
|
+
},
|
|
140
|
+
];
|
|
141
|
+
}
|
|
142
|
+
if (isRowCard(card)) {
|
|
143
|
+
const tiles = card.tiles ?? [];
|
|
144
|
+
const builtIns = (domain.tiles ?? []).map((w) => w.key);
|
|
145
|
+
return [
|
|
146
|
+
{
|
|
147
|
+
id: 'cards',
|
|
148
|
+
label: 'Cards',
|
|
149
|
+
max: MAX_TILES,
|
|
150
|
+
...number,
|
|
151
|
+
keys: tiles.filter(isOwnTile).map((t) => t.field),
|
|
152
|
+
// A field ticked adds a card of its sum; one unticked takes its cards out.
|
|
153
|
+
set: (keys) => {
|
|
154
|
+
const kept = tiles.filter((t) => !isOwnTile(t) || keys.includes(t.field));
|
|
155
|
+
const next = [...kept];
|
|
156
|
+
for (const field of keys) {
|
|
157
|
+
if (next.some((t) => isOwnTile(t) && t.field === field))
|
|
158
|
+
continue;
|
|
159
|
+
next.push({
|
|
160
|
+
key: freeTileKey(next, builtIns),
|
|
161
|
+
label: label(field),
|
|
162
|
+
field,
|
|
163
|
+
agg: 'sum',
|
|
164
|
+
filter: [],
|
|
165
|
+
sign: false,
|
|
166
|
+
lines: [],
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
return { ...card, tiles: next };
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
];
|
|
173
|
+
}
|
|
174
|
+
return [];
|
|
175
|
+
}
|
|
176
|
+
/** A slot's keys after the tree ticks or unticks `key`: one replaces, a list adds up to its max. */
|
|
177
|
+
export function slotKeys(slot, key, on) {
|
|
178
|
+
if (slot.max === 1)
|
|
179
|
+
return on ? [key] : slot.keys;
|
|
180
|
+
if (!on)
|
|
181
|
+
return slot.keys.filter((k) => k !== key);
|
|
182
|
+
return slot.keys.length < slot.max ? [...slot.keys, key] : slot.keys;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Fields: each slot as a picker (the tree beside fills the same ones), a number card's or a
|
|
186
|
+
* Summary's cards, or a chart's symbols.
|
|
187
|
+
*/
|
|
188
|
+
export function WidgetFields({ card, set, domain, rows, fields, }) {
|
|
189
|
+
const [symbols, setSymbols] = useState(card.kind === 'chart' && card.style === 'market' ? card.symbols.join(', ') : '');
|
|
190
|
+
const keys = domain.fields.map((f) => f.key);
|
|
191
|
+
const domainTiles = (domain.tiles ?? []).map((w) => w.key);
|
|
192
|
+
if ('tile' in card)
|
|
193
|
+
return (_jsx(SummaryCardsEditor, { one: true, domain: domain, tiles: [card.tile], onChange: (tiles) => {
|
|
194
|
+
const tile = tiles[tiles.length - 1];
|
|
195
|
+
if (tile)
|
|
196
|
+
set({ ...card, tile });
|
|
197
|
+
}, rows: rows, fields: fields, problems: summaryCardIssues([card.tile], keys, domainTiles) }));
|
|
198
|
+
if (isRowCard(card))
|
|
199
|
+
return (_jsx(SummaryCardsEditor, { domain: domain, tiles: card.tiles ?? [], onChange: (tiles) => set({ ...card, tiles }), rows: rows, fields: fields, problems: summaryCardIssues(card.tiles ?? [], keys, domainTiles) }));
|
|
200
|
+
if (card.kind === 'chart' && card.style === 'market')
|
|
201
|
+
return (_jsx(EditorRow, { label: "Symbols", children: _jsx(Input, { "aria-label": "Symbols, comma separated", value: symbols, placeholder: OUTLOOK_INDEXES.join(', '), onChange: (e) => {
|
|
202
|
+
setSymbols(e.target.value);
|
|
203
|
+
const list = [
|
|
204
|
+
...new Set(e.target.value
|
|
205
|
+
.toUpperCase()
|
|
206
|
+
.split(/[\s,]+/)
|
|
207
|
+
.filter(Boolean)),
|
|
208
|
+
];
|
|
209
|
+
if (list.length)
|
|
210
|
+
set({ ...card, symbols: list });
|
|
211
|
+
} }) }));
|
|
212
|
+
if (card.kind === 'chart' && card.style === 'price')
|
|
213
|
+
return (_jsx(EditorRow, { label: "Symbol", children: _jsx(Input, { "aria-label": "Symbol", value: card.symbol, placeholder: "SPX", onChange: (e) => set({ ...card, symbol: e.target.value.trim().toUpperCase() }) }) }));
|
|
214
|
+
const slots = widgetSlots(card, domain);
|
|
215
|
+
if (!slots.length)
|
|
216
|
+
return _jsx("p", { className: "text-sm text-muted-foreground", children: "It draws each shown row's own curve." });
|
|
217
|
+
return (_jsx("div", { className: "flex max-w-xl flex-col gap-3", children: slots.map((s) => (_jsx(EditorRow, { label: s.label, children: _jsx(LumenSelector, { label: s.label, size: "sm", className: "w-full min-w-0", placeholder: "Pick a field", options: fieldOptions(domain.fields.filter(s.accepts), domain), ...(s.max === 1
|
|
218
|
+
? {
|
|
219
|
+
value: s.keys[0] ?? '',
|
|
220
|
+
onChange: (k) => set(s.set([k])),
|
|
221
|
+
}
|
|
222
|
+
: {
|
|
223
|
+
multiple: true,
|
|
224
|
+
max: s.max,
|
|
225
|
+
value: s.keys,
|
|
226
|
+
onChange: (ks) => set(s.set(ks)),
|
|
227
|
+
}) }) }, s.id))) }));
|
|
228
|
+
}
|
|
229
|
+
const NO_SPLIT = '__none';
|
|
230
|
+
const BUCKET_NAMES = {
|
|
231
|
+
day: 'Day',
|
|
232
|
+
week: 'Week',
|
|
233
|
+
month: 'Month',
|
|
234
|
+
year: 'Year',
|
|
235
|
+
};
|
|
236
|
+
const TREND_AGGS = [
|
|
237
|
+
['sum', 'Sum'],
|
|
238
|
+
['mean', 'Average'],
|
|
239
|
+
['last', 'Last'],
|
|
240
|
+
['count', 'Count'],
|
|
241
|
+
];
|
|
242
|
+
/** The most slices a donut or a bar shows on their own; the most rows a list ranks. */
|
|
243
|
+
const MAX_SLICES = { donut: 12, bar: 50 };
|
|
244
|
+
const MAX_RANKED = { bar_list: 30, top_n: 50 };
|
|
245
|
+
function Choice({ label, value, options, onChange, }) {
|
|
246
|
+
return (_jsx(ToggleGroup, { type: "single", size: "sm", variant: "outline", spacing: 0, value: value, onValueChange: (v) => v && onChange(v), "aria-label": label, className: "flex-wrap justify-self-start", children: options.map(([v, l]) => (_jsx(ToggleGroupItem, { value: v, className: "px-2.5", children: l }, v))) }));
|
|
247
|
+
}
|
|
248
|
+
const ORDERS = [
|
|
249
|
+
[
|
|
250
|
+
'desc',
|
|
251
|
+
_jsxs("span", { className: "contents", children: [_jsx(ArrowDownIcon, {}), " Largest"] }, "desc"),
|
|
252
|
+
],
|
|
253
|
+
[
|
|
254
|
+
'asc',
|
|
255
|
+
_jsxs("span", { className: "contents", children: [_jsx(ArrowUpIcon, {}), " Smallest"] }, "asc"),
|
|
256
|
+
],
|
|
257
|
+
];
|
|
258
|
+
/** Whether a widget has Settings of its own. */
|
|
259
|
+
export const hasWidgetSettings = (card) => !(card.kind === 'metric' && (card.style === 'number' || card.style === 'row'));
|
|
260
|
+
/** Settings: the picked style's own options. */
|
|
261
|
+
export function WidgetSettings({ card, set, domain, }) {
|
|
262
|
+
const sid = widgetStyleId(card);
|
|
263
|
+
const texts = domain.fields.filter(isTextField);
|
|
264
|
+
return (_jsxs("div", { className: "flex max-w-xl flex-col gap-3", children: [card.kind === 'allocation' && card.style !== 'bar_list' && (_jsx(EditorRow, { label: "Slices", children: _jsx(Input, { "aria-label": "Slices (the rest are Other)", type: "number", className: "w-24", min: 2, max: MAX_SLICES[card.style], value: card.max ?? ALLOCATION_MAX, onChange: (e) => set({ ...card, max: Number(e.target.value) }) }) })), (sid === 'allocation:bar_list' || sid === 'table:top_n') && 'n' in card && (_jsxs(_Fragment, { children: [_jsx(EditorRow, { label: "Order", children: _jsx(Choice, { label: "Order", value: card.desc ? 'desc' : 'asc', options: ORDERS, onChange: (v) => set({ ...card, desc: v === 'desc' }) }) }), _jsx(EditorRow, { label: sid === 'allocation:bar_list' ? 'Bars' : 'Rows', children: _jsx(Input, { "aria-label": "How many", type: "number", className: "w-24", min: 1, max: sid === 'allocation:bar_list' ? MAX_RANKED.bar_list : MAX_RANKED.top_n, value: card.n, onChange: (e) => set({ ...card, n: Number(e.target.value) }) }) })] })), card.kind === 'allocation' && card.style === 'bar_list' && (_jsxs(_Fragment, { children: [_jsx(EditorRow, { label: "Colour", children: _jsxs(Label, { className: "flex items-center gap-2 font-normal", children: [_jsx(Switch, { checked: card.signed, onCheckedChange: (signed) => set({ ...card, signed, ...(signed ? { stack: undefined } : {}) }) }), "Gains green, losses red"] }) }), !card.signed && (_jsx(EditorRow, { label: "Split by", children: _jsx(LumenSelector, { label: "Split each bar by", size: "sm", className: "w-full min-w-0", options: [
|
|
265
|
+
{ value: NO_SPLIT, label: 'No split' },
|
|
266
|
+
...fieldOptions(texts.filter((f) => f.key !== card.by), domain),
|
|
267
|
+
], value: card.stack ?? NO_SPLIT, onChange: (v) => set({ ...card, stack: v === NO_SPLIT ? undefined : v }) }) }))] })), card.kind === 'chart' && card.style === 'market' && (_jsx(EditorRow, { label: "Style", children: _jsx(Choice, { label: "Style", value: card.layout, options: [
|
|
268
|
+
['lines', 'One chart'],
|
|
269
|
+
['tiles', 'A widget each'],
|
|
270
|
+
['today', 'Up or down today'],
|
|
271
|
+
], onChange: (layout) => set({ ...card, layout }) }) })), card.kind === 'chart' && card.style === 'price' && (_jsxs(_Fragment, { children: [_jsx(EditorRow, { label: "Window", children: _jsx(Choice, { label: "Window", value: card.window, options: marketWindows.map((w) => [w, w]), onChange: (window) => set({ ...card, window }) }) }), _jsx(EditorRow, { label: "Type", children: _jsx(Choice, { label: "Chart type", value: card.type, options: [
|
|
272
|
+
['line', 'Line'],
|
|
273
|
+
['candles', 'Candles'],
|
|
274
|
+
], onChange: (type) => set({ ...card, type }) }) })] })), card.kind === 'chart' && card.style === 'growth' && (_jsx(EditorRow, { label: "Scale", children: _jsx(Choice, { label: "Scale", value: card.scale, options: [
|
|
275
|
+
['log', 'Log'],
|
|
276
|
+
['linear', 'Linear'],
|
|
277
|
+
], onChange: (scale) => set({ ...card, scale }) }) })), 'date' in card && (_jsxs(_Fragment, { children: [_jsx(EditorRow, { label: "Each", children: _jsx(Choice, { label: "Each", value: card.bucket, options: trendBuckets.map((b) => [b, BUCKET_NAMES[b]]), onChange: (bucket) => set({ ...card, bucket }) }) }), _jsx(EditorRow, { label: "Roll up", children: _jsx(Choice, { label: "Roll up", value: card.agg, options: TREND_AGGS, onChange: (agg) => set({ ...card, agg }) }) })] })), 'tile' in card && card.style !== 'number' && (_jsx(EditorRow, { label: "Target", children: _jsx(Input, { "aria-label": "Target (the bar's 100%)", type: "number", min: 0, value: card.target ?? '', placeholder: "The bar's 100%", onChange: (e) => {
|
|
278
|
+
const n = Number(e.target.value);
|
|
279
|
+
set({ ...card, target: e.target.value && n > 0 ? n : undefined });
|
|
280
|
+
} }) })), 'tile' in card && card.style !== 'number' && (_jsx(EditorRow, { label: "Colour", children: _jsx(Choice, { label: "Colour by meaning", value: card.meter ?? 'none', options: METER_CHOICES, onChange: (m) => set({ ...card, meter: m === 'none' ? undefined : m }) }) }))] }));
|
|
281
|
+
}
|
|
282
|
+
/** A progress card's colour: plain, health (better high) or usage (better low). */
|
|
283
|
+
const METER_CHOICES = [
|
|
284
|
+
['none', 'Plain'],
|
|
285
|
+
['health', 'Health: 100% green'],
|
|
286
|
+
['usage', 'Usage: 75% orange, 90% red'],
|
|
287
|
+
];
|
|
288
|
+
/** Whether a widget reads the view's rows (a chart of symbols reads its own data). */
|
|
289
|
+
export const readsRows = (card) => isTableCard(card) ||
|
|
290
|
+
!(card.kind === 'chart' && (card.style === 'market' || card.style === 'price'));
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
/** A style's thumbnail (`WIDGET_STYLES` id); a style with none draws nothing. */
|
|
3
|
+
export declare const widgetThumb: (id: string) => string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | Iterable<ReactNode> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | import("react").ReactPortal | null | undefined> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | null;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
const C1 = 'var(--chart-1)';
|
|
3
|
+
const C2 = 'var(--chart-2)';
|
|
4
|
+
const C3 = 'var(--chart-3)';
|
|
5
|
+
const MUTED = 'var(--muted-foreground)';
|
|
6
|
+
const box = (children) => (_jsxs("svg", { viewBox: "0 0 40 28", className: "h-7 w-10 rounded-[4px] bg-muted/60", "aria-hidden": true, focusable: "false", children: [_jsx("title", { children: "Widget style" }), children] }));
|
|
7
|
+
const line = (d, stroke = C1) => (_jsx("path", { d: d, fill: "none", stroke: stroke, strokeWidth: 1.6, strokeLinecap: "round" }));
|
|
8
|
+
const THUMBS = {
|
|
9
|
+
top_n: box([6, 12, 18].map((y) => (_jsxs("g", { children: [_jsx("rect", { x: 5, y: y, width: 16, height: 3, rx: 1, fill: MUTED, opacity: 0.6 }), _jsx("rect", { x: 26, y: y, width: 9, height: 3, rx: 1, fill: C1 })] }, y)))),
|
|
10
|
+
card: box(_jsxs(_Fragment, { children: [_jsx("rect", { x: 6, y: 7, width: 20, height: 6, rx: 1.5, fill: C1 }), _jsx("rect", { x: 6, y: 17, width: 14, height: 3, rx: 1, fill: MUTED, opacity: 0.6 })] })),
|
|
11
|
+
tiles: box([4, 16, 28].map((x) => (_jsxs("g", { children: [_jsx("rect", { x: x, y: 9, width: 8, height: 5, rx: 1, fill: C1 }), _jsx("rect", { x: x, y: 17, width: 6, height: 2, rx: 1, fill: MUTED, opacity: 0.6 })] }, x)))),
|
|
12
|
+
'card:progress_bar': box(_jsxs(_Fragment, { children: [_jsx("rect", { x: 6, y: 7, width: 14, height: 5, rx: 1.5, fill: C1 }), _jsx("rect", { x: 6, y: 17, width: 28, height: 3, rx: 1.5, fill: MUTED, opacity: 0.3 }), _jsx("rect", { x: 6, y: 17, width: 19, height: 3, rx: 1.5, fill: C1 })] })),
|
|
13
|
+
'card:progress_circle': box(_jsxs(_Fragment, { children: [_jsx("circle", { cx: 20, cy: 14, r: 8, fill: "none", stroke: MUTED, strokeOpacity: 0.3, strokeWidth: 3 }), _jsx("path", { d: "M20 6 A8 8 0 1 1 12.4 16.5", fill: "none", stroke: C1, strokeWidth: 3 })] })),
|
|
14
|
+
'trend:spark': box(_jsxs(_Fragment, { children: [_jsx("rect", { x: 5, y: 5, width: 12, height: 5, rx: 1.5, fill: C1 }), line('M5 22 L12 19 L18 21 L25 15 L30 17 L35 12')] })),
|
|
15
|
+
'trend:tracker': box([0, 1, 2, 3, 4, 5, 6, 7].map((i) => (_jsx("rect", { x: 4 + i * 4, y: 10, width: 3, height: 8, rx: 0.8, fill: i === 2 || i === 5 ? 'var(--loss)' : 'var(--gain)' }, i)))),
|
|
16
|
+
allocation: box(_jsxs(_Fragment, { children: [_jsx("rect", { x: 4, y: 11, width: 14, height: 6, rx: 1.5, fill: C1 }), _jsx("rect", { x: 19, y: 11, width: 9, height: 6, rx: 1.5, fill: C2 }), _jsx("rect", { x: 29, y: 11, width: 7, height: 6, rx: 1.5, fill: C3 })] })),
|
|
17
|
+
donut: box(_jsxs(_Fragment, { children: [_jsx("circle", { cx: 20, cy: 14, r: 8, fill: "none", stroke: C2, strokeWidth: 4 }), _jsx("path", { d: "M20 6 A8 8 0 0 1 27.6 16.5", fill: "none", stroke: C1, strokeWidth: 4 })] })),
|
|
18
|
+
bars: box(_jsxs(_Fragment, { children: [_jsx("rect", { x: 5, y: 6, width: 28, height: 3.5, rx: 1.5, fill: C1 }), _jsx("rect", { x: 5, y: 12, width: 20, height: 3.5, rx: 1.5, fill: C1, opacity: 0.75 }), _jsx("rect", { x: 5, y: 18, width: 12, height: 3.5, rx: 1.5, fill: C1, opacity: 0.5 })] })),
|
|
19
|
+
'trend:line': box(_jsxs(_Fragment, { children: [line('M5 21 L12 16 L18 18 L25 10 L35 7'), line('M5 23 L14 21 L22 22 L30 17 L35 18', C2)] })),
|
|
20
|
+
'trend:area': box(_jsxs(_Fragment, { children: [_jsx("path", { d: "M5 21 L12 15 L19 17 L26 9 L35 7 L35 24 L5 24 Z", fill: C1, opacity: 0.25 }), line('M5 21 L12 15 L19 17 L26 9 L35 7')] })),
|
|
21
|
+
'trend:stacked': box(_jsxs(_Fragment, { children: [_jsx("path", { d: "M5 24 L5 19 L14 17 L24 18 L35 14 L35 24 Z", fill: C2, opacity: 0.5 }), _jsx("path", { d: "M5 19 L14 17 L24 18 L35 14 L35 8 L24 11 L14 9 L5 13 Z", fill: C1, opacity: 0.5 })] })),
|
|
22
|
+
'trend:bars': box([6, 12, 18, 24, 30].map((x, i) => (_jsx("rect", { x: x, y: 24 - ([8, 12, 9, 15, 13][i] ?? 0), width: 4, height: [8, 12, 9, 15, 13][i], rx: 1, fill: C1 }, x)))),
|
|
23
|
+
'trend:combo': box(_jsxs(_Fragment, { children: [[6, 13, 20, 27].map((x, i) => (_jsx("rect", { x: x, y: 24 - ([7, 10, 8, 12][i] ?? 0), width: 5, height: [7, 10, 8, 12][i], rx: 1, fill: C1, opacity: 0.6 }, x))), line('M7 12 L15 9 L22 11 L30 6', C2)] })),
|
|
24
|
+
chart: box([8, 14, 20, 26, 32].map((x, i) => (_jsxs("g", { children: [_jsx("path", { d: `M${x} ${[8, 10, 6, 9, 5][i]} V${[20, 21, 17, 20, 15][i]}`, stroke: MUTED, strokeWidth: 0.8 }), _jsx("rect", { x: x - 1.5, y: [11, 13, 9, 12, 8][i], width: 3, height: 5, fill: i % 2 ? 'var(--loss)' : 'var(--gain)' })] }, x)))),
|
|
25
|
+
growth: box(_jsxs(_Fragment, { children: [line('M5 22 Q16 20 22 14 T35 6'), line('M5 22 Q18 21 26 18 T35 15', MUTED)] })),
|
|
26
|
+
market: box(_jsxs(_Fragment, { children: [_jsx("rect", { x: 4, y: 6, width: 15, height: 16, rx: 2, fill: "none", stroke: MUTED, strokeOpacity: 0.5 }), _jsx("rect", { x: 21, y: 6, width: 15, height: 16, rx: 2, fill: "none", stroke: MUTED, strokeOpacity: 0.5 }), line('M6 18 L10 14 L13 16 L17 10'), line('M23 12 L27 16 L30 14 L34 18', 'var(--loss)')] })),
|
|
27
|
+
};
|
|
28
|
+
/** A style's thumbnail (`WIDGET_STYLES` id); a style with none draws nothing. */
|
|
29
|
+
export const widgetThumb = (id) => THUMBS[id] ?? null;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { WorkspaceSummary } from '@columenlabs/lumen-ui-types/workspaces';
|
|
2
|
+
/** Every workspace query's key starts here: the assistant's tab moves invalidate them all. */
|
|
3
|
+
export declare const WORKSPACES_KEY: readonly ['workspaces'];
|
|
4
|
+
/** A frame's rows; a remade frame invalidates it. */
|
|
5
|
+
export declare const frameKey: (api: string, id: string) => string[];
|
|
6
|
+
/** The workspaces as the sidebar and the @ picker list them. */
|
|
7
|
+
export declare const workspacesListQuery: (api: string) => {
|
|
8
|
+
queryKey: string[];
|
|
9
|
+
queryFn: () => Promise<{
|
|
10
|
+
workspaces: WorkspaceSummary[];
|
|
11
|
+
}>;
|
|
12
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { apiJson } from './api-fetch.js';
|
|
2
|
+
// Query keys of frames and the workspaces that show them, shared by every card that attaches a
|
|
3
|
+
// frame, a product's workspace page and its assistant (whose tab moves and remade frames
|
|
4
|
+
// invalidate them).
|
|
5
|
+
/** Every workspace query's key starts here: the assistant's tab moves invalidate them all. */
|
|
6
|
+
export const WORKSPACES_KEY = ['workspaces'];
|
|
7
|
+
/** A frame's rows; a remade frame invalidates it. */
|
|
8
|
+
export const frameKey = (api, id) => [...WORKSPACES_KEY, api, 'frame', id];
|
|
9
|
+
/** The workspaces as the sidebar and the @ picker list them. */
|
|
10
|
+
export const workspacesListQuery = (api) => ({
|
|
11
|
+
queryKey: [...WORKSPACES_KEY, api, 'list'],
|
|
12
|
+
queryFn: () => apiJson(`${api}/workspaces`, {}, 'workspaces.list'),
|
|
13
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type CustomViewConfig } from '@columenlabs/lumen-ui-types/views';
|
|
2
|
+
import { type ComponentType, type ReactNode } from 'react';
|
|
3
|
+
import { type Crumb } from './page.js';
|
|
4
|
+
type Icon = ComponentType<{
|
|
5
|
+
className?: string;
|
|
6
|
+
}>;
|
|
7
|
+
/** An app a workspace can belong to (the product's apps). */
|
|
8
|
+
export interface WorkspaceApp {
|
|
9
|
+
id: string;
|
|
10
|
+
label: string;
|
|
11
|
+
/** Its address; a workspace there is at `${href}workspaces/<id>`. */
|
|
12
|
+
href: string;
|
|
13
|
+
icon?: Icon;
|
|
14
|
+
}
|
|
15
|
+
/** What the product tells its workspaces; every field may be left out by a one-app product. */
|
|
16
|
+
export interface WorkspacesConfig {
|
|
17
|
+
/** The apps a workspace may move to that this person can open, this one included. */
|
|
18
|
+
apps?: WorkspaceApp[];
|
|
19
|
+
/** What the product calls an app: e.g. `desk`. */
|
|
20
|
+
appNoun?: string;
|
|
21
|
+
/** Which app draws a tab over a domain; one it does not say is this app's. */
|
|
22
|
+
domainApp?: (domain: string) => string | undefined;
|
|
23
|
+
/** A domain tab's starting layout. */
|
|
24
|
+
defaultConfig?: (domain: string) => CustomViewConfig | null;
|
|
25
|
+
/** The empty workspace's example ask, on an app: `put my holdings with their earnings dates in`. */
|
|
26
|
+
example?: (app: string) => string;
|
|
27
|
+
}
|
|
28
|
+
/** The product's apps and domains for its workspaces. */
|
|
29
|
+
export declare const WorkspacesProvider: import("react").Provider<WorkspacesConfig>;
|
|
30
|
+
/**
|
|
31
|
+
* Workspaces in the sidebar: one item per workspace, newest first; `+` starts an empty one, the
|
|
32
|
+
* trash icon deletes one (its frames stay until no tab shows them). Hidden where the app has no
|
|
33
|
+
* assistant.
|
|
34
|
+
*/
|
|
35
|
+
export declare function WorkspacesSection(): import("react").JSX.Element | null;
|
|
36
|
+
/** What an app draws for a tab over one of its own domains, inside the workspace page. */
|
|
37
|
+
export interface WorkspaceDraftProps {
|
|
38
|
+
domain: string;
|
|
39
|
+
/** The tab's layout: the person's and the assistant's edits, else the domain's default view. */
|
|
40
|
+
config: CustomViewConfig;
|
|
41
|
+
onChange: (config: CustomViewConfig) => void;
|
|
42
|
+
crumbs: Crumb[];
|
|
43
|
+
/** The tab row, drawn above the cards. */
|
|
44
|
+
intro: ReactNode;
|
|
45
|
+
actions: ReactNode;
|
|
46
|
+
tabKey: string;
|
|
47
|
+
/** A workspace tab is a sheet (rule C): chips, sort and grouping save at once. */
|
|
48
|
+
save: 'sheet';
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* `/workspaces/$id` on every app with an assistant. `draft` draws a tab over one of the app's own
|
|
52
|
+
* domains (an app passes its portfolio view); a tab over another app's domain links there.
|
|
53
|
+
*/
|
|
54
|
+
export declare function WorkspacePage({ draft }: {
|
|
55
|
+
draft?: (p: WorkspaceDraftProps) => ReactNode;
|
|
56
|
+
}): import("react").JSX.Element;
|
|
57
|
+
export {};
|