@columenlabs/lumen-ui 0.0.0-stage → 0.2.1

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.
Files changed (301) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +12 -0
  3. package/README.md +4 -2
  4. package/dist/add-column.d.ts +23 -0
  5. package/dist/add-column.js +62 -0
  6. package/dist/api-fetch.d.ts +9 -0
  7. package/dist/api-fetch.js +60 -0
  8. package/dist/app-switcher.d.ts +21 -0
  9. package/dist/app-switcher.js +12 -0
  10. package/dist/app-views.d.ts +18 -0
  11. package/dist/app-views.js +41 -0
  12. package/dist/assistant-context.d.ts +72 -0
  13. package/dist/assistant-context.js +30 -0
  14. package/dist/assistant-panel.d.ts +58 -0
  15. package/dist/assistant-panel.js +244 -0
  16. package/dist/assistant-screen.d.ts +124 -0
  17. package/dist/assistant-screen.js +252 -0
  18. package/dist/card-switch.d.ts +16 -0
  19. package/dist/card-switch.js +16 -0
  20. package/dist/color-dot.d.ts +14 -0
  21. package/dist/color-dot.js +19 -0
  22. package/dist/confirm.d.ts +18 -0
  23. package/dist/confirm.js +5 -0
  24. package/dist/connections.d.ts +45 -0
  25. package/dist/connections.js +275 -0
  26. package/dist/coverage-calendar-map.d.ts +27 -0
  27. package/dist/coverage-calendar-map.js +84 -0
  28. package/dist/coverage-cells.d.ts +55 -0
  29. package/dist/coverage-cells.js +85 -0
  30. package/dist/coverage-map.d.ts +112 -0
  31. package/dist/coverage-map.js +428 -0
  32. package/dist/details-table.d.ts +20 -0
  33. package/dist/details-table.js +13 -0
  34. package/dist/drawer-section.d.ts +27 -0
  35. package/dist/drawer-section.js +23 -0
  36. package/dist/floating-head.d.ts +2 -0
  37. package/dist/floating-head.js +57 -0
  38. package/dist/fold.d.ts +16 -0
  39. package/dist/fold.js +70 -0
  40. package/dist/form-issues.d.ts +32 -0
  41. package/dist/form-issues.js +60 -0
  42. package/dist/formula-input.d.ts +17 -0
  43. package/dist/formula-input.js +104 -0
  44. package/dist/frame-script.d.ts +7 -0
  45. package/dist/frame-script.js +103 -0
  46. package/dist/freshness.d.ts +24 -0
  47. package/dist/freshness.js +99 -0
  48. package/dist/group-button.d.ts +33 -0
  49. package/dist/group-button.js +67 -0
  50. package/dist/growth-chart.d.ts +77 -0
  51. package/dist/growth-chart.js +195 -0
  52. package/dist/hooks/use-keyboard-fit.d.ts +15 -0
  53. package/dist/hooks/use-keyboard-fit.js +29 -0
  54. package/dist/hooks/use-mobile.d.ts +1 -0
  55. package/dist/hooks/use-mobile.js +15 -0
  56. package/dist/icon-button.d.ts +21 -0
  57. package/dist/icon-button.js +22 -0
  58. package/dist/icon-picker.d.ts +14 -0
  59. package/dist/icon-picker.js +36 -0
  60. package/dist/index.d.ts +83 -0
  61. package/dist/index.js +90 -0
  62. package/dist/integrations.d.ts +57 -0
  63. package/dist/integrations.js +73 -0
  64. package/dist/joins.d.ts +128 -0
  65. package/dist/joins.js +767 -0
  66. package/dist/kit-config.d.ts +13 -0
  67. package/dist/kit-config.js +15 -0
  68. package/dist/lib/utils.d.ts +2 -0
  69. package/dist/lib/utils.js +5 -0
  70. package/dist/lumen-boundary.d.ts +7 -0
  71. package/dist/lumen-boundary.js +10 -0
  72. package/dist/lumen-code-editor-impl.d.ts +2 -0
  73. package/dist/lumen-code-editor-impl.js +16 -0
  74. package/dist/lumen-code-editor.d.ts +11 -0
  75. package/dist/lumen-code-editor.js +11 -0
  76. package/dist/lumen-code.d.ts +22 -0
  77. package/dist/lumen-code.js +75 -0
  78. package/dist/lumen-drawer.d.ts +30 -0
  79. package/dist/lumen-drawer.js +27 -0
  80. package/dist/lumen-echart.d.ts +58 -0
  81. package/dist/lumen-echart.js +129 -0
  82. package/dist/lumen-filter.d.ts +75 -0
  83. package/dist/lumen-filter.js +546 -0
  84. package/dist/lumen-finder.d.ts +62 -0
  85. package/dist/lumen-finder.js +275 -0
  86. package/dist/lumen-form.d.ts +34 -0
  87. package/dist/lumen-form.js +81 -0
  88. package/dist/lumen-geo-map.d.ts +9 -0
  89. package/dist/lumen-geo-map.js +135 -0
  90. package/dist/lumen-grid.d.ts +49 -0
  91. package/dist/lumen-grid.js +203 -0
  92. package/dist/lumen-layout.d.ts +150 -0
  93. package/dist/lumen-layout.js +661 -0
  94. package/dist/lumen-links.d.ts +96 -0
  95. package/dist/lumen-links.js +213 -0
  96. package/dist/lumen-page-state.d.ts +20 -0
  97. package/dist/lumen-page-state.js +15 -0
  98. package/dist/lumen-selector.d.ts +139 -0
  99. package/dist/lumen-selector.js +308 -0
  100. package/dist/lumen-source.d.ts +137 -0
  101. package/dist/lumen-source.js +327 -0
  102. package/dist/lumen-tab-drawer.d.ts +27 -0
  103. package/dist/lumen-tab-drawer.js +26 -0
  104. package/dist/lumen-tabs.d.ts +21 -0
  105. package/dist/lumen-tabs.js +6 -0
  106. package/dist/lumen-toast.d.ts +11 -0
  107. package/dist/lumen-toast.js +16 -0
  108. package/dist/lumen-trend.d.ts +18 -0
  109. package/dist/lumen-trend.js +157 -0
  110. package/dist/lumen-view-card.d.ts +35 -0
  111. package/dist/lumen-view-card.js +50 -0
  112. package/dist/lumen-view.d.ts +172 -0
  113. package/dist/lumen-view.js +645 -0
  114. package/dist/lumen-widget-charts.d.ts +57 -0
  115. package/dist/lumen-widget-charts.js +69 -0
  116. package/dist/lumen-widget.d.ts +720 -0
  117. package/dist/lumen-widget.js +609 -0
  118. package/dist/market/compare-chart.d.ts +34 -0
  119. package/dist/market/compare-chart.js +292 -0
  120. package/dist/market/indicator-picker.d.ts +29 -0
  121. package/dist/market/indicator-picker.js +125 -0
  122. package/dist/market/indicators.d.ts +63 -0
  123. package/dist/market/indicators.js +314 -0
  124. package/dist/market/live.d.ts +120 -0
  125. package/dist/market/live.js +444 -0
  126. package/dist/market/lumen-chart.d.ts +88 -0
  127. package/dist/market/lumen-chart.js +645 -0
  128. package/dist/market/market-widget.d.ts +45 -0
  129. package/dist/market/market-widget.js +346 -0
  130. package/dist/market/option-chain.d.ts +19 -0
  131. package/dist/market/option-chain.js +53 -0
  132. package/dist/market/price-chart.d.ts +128 -0
  133. package/dist/market/price-chart.js +619 -0
  134. package/dist/mcp-settings.d.ts +20 -0
  135. package/dist/mcp-settings.js +65 -0
  136. package/dist/nav.d.ts +77 -0
  137. package/dist/nav.js +73 -0
  138. package/dist/note-line.d.ts +13 -0
  139. package/dist/note-line.js +5 -0
  140. package/dist/notifications.d.ts +9 -0
  141. package/dist/notifications.js +166 -0
  142. package/dist/origin-legend.d.ts +14 -0
  143. package/dist/origin-legend.js +38 -0
  144. package/dist/overview.d.ts +84 -0
  145. package/dist/overview.js +105 -0
  146. package/dist/page-inputs.d.ts +14 -0
  147. package/dist/page-inputs.js +16 -0
  148. package/dist/page.d.ts +32 -0
  149. package/dist/page.js +45 -0
  150. package/dist/pager.d.ts +10 -0
  151. package/dist/pager.js +14 -0
  152. package/dist/prefs.d.ts +71 -0
  153. package/dist/prefs.js +126 -0
  154. package/dist/query-client.d.ts +7 -0
  155. package/dist/query-client.js +19 -0
  156. package/dist/refresh.d.ts +4 -0
  157. package/dist/refresh.js +19 -0
  158. package/dist/rename-field.d.ts +11 -0
  159. package/dist/rename-field.js +26 -0
  160. package/dist/reset-button.d.ts +9 -0
  161. package/dist/reset-button.js +10 -0
  162. package/dist/route-error.d.ts +42 -0
  163. package/dist/route-error.js +229 -0
  164. package/dist/schedules.d.ts +88 -0
  165. package/dist/schedules.js +221 -0
  166. package/dist/settings.d.ts +51 -0
  167. package/dist/settings.js +181 -0
  168. package/dist/shell.d.ts +99 -0
  169. package/dist/shell.js +137 -0
  170. package/dist/source-health.d.ts +12 -0
  171. package/dist/source-health.js +58 -0
  172. package/dist/sourced-card.d.ts +10 -0
  173. package/dist/sourced-card.js +69 -0
  174. package/dist/spotlight.d.ts +23 -0
  175. package/dist/spotlight.js +138 -0
  176. package/dist/status-health.d.ts +16 -0
  177. package/dist/status-health.js +41 -0
  178. package/dist/status.d.ts +9 -0
  179. package/dist/status.js +12 -0
  180. package/dist/summary-cards.d.ts +56 -0
  181. package/dist/summary-cards.js +270 -0
  182. package/dist/symbol-sheet-context.d.ts +4 -0
  183. package/dist/symbol-sheet-context.js +5 -0
  184. package/dist/symbol-sheet.d.ts +9 -0
  185. package/dist/symbol-sheet.js +24 -0
  186. package/dist/tasks.d.ts +29 -0
  187. package/dist/tasks.js +278 -0
  188. package/dist/theme.css +606 -0
  189. package/dist/tile.d.ts +37 -0
  190. package/dist/tile.js +23 -0
  191. package/dist/ui/alert-dialog.d.ts +18 -0
  192. package/dist/ui/alert-dialog.js +41 -0
  193. package/dist/ui/alert.d.ts +10 -0
  194. package/dist/ui/alert.js +27 -0
  195. package/dist/ui/avatar.d.ts +11 -0
  196. package/dist/ui/avatar.js +22 -0
  197. package/dist/ui/badge.d.ts +9 -0
  198. package/dist/ui/badge.js +27 -0
  199. package/dist/ui/breadcrumb.d.ts +11 -0
  200. package/dist/ui/breadcrumb.js +27 -0
  201. package/dist/ui/button-group.d.ts +11 -0
  202. package/dist/ui/button-group.js +27 -0
  203. package/dist/ui/button.d.ts +10 -0
  204. package/dist/ui/button.js +38 -0
  205. package/dist/ui/card.d.ts +11 -0
  206. package/dist/ui/card.js +24 -0
  207. package/dist/ui/chart.d.ts +44 -0
  208. package/dist/ui/chart.js +117 -0
  209. package/dist/ui/checkbox.d.ts +4 -0
  210. package/dist/ui/checkbox.js +8 -0
  211. package/dist/ui/collapsible.d.ts +5 -0
  212. package/dist/ui/collapsible.js +12 -0
  213. package/dist/ui/combobox.d.ts +24 -0
  214. package/dist/ui/combobox.js +60 -0
  215. package/dist/ui/command.d.ts +18 -0
  216. package/dist/ui/command.js +34 -0
  217. package/dist/ui/dialog.d.ts +23 -0
  218. package/dist/ui/dialog.js +50 -0
  219. package/dist/ui/dropdown-menu.d.ts +29 -0
  220. package/dist/ui/dropdown-menu.js +50 -0
  221. package/dist/ui/emoji-picker.d.ts +6 -0
  222. package/dist/ui/emoji-picker.js +28 -0
  223. package/dist/ui/empty.d.ts +11 -0
  224. package/dist/ui/empty.js +33 -0
  225. package/dist/ui/field.d.ts +24 -0
  226. package/dist/ui/field.js +65 -0
  227. package/dist/ui/hover-card.d.ts +6 -0
  228. package/dist/ui/hover-card.js +13 -0
  229. package/dist/ui/input-group.d.ts +16 -0
  230. package/dist/ui/input-group.js +56 -0
  231. package/dist/ui/input.d.ts +3 -0
  232. package/dist/ui/input.js +6 -0
  233. package/dist/ui/item.d.ts +23 -0
  234. package/dist/ui/item.js +67 -0
  235. package/dist/ui/kbd.d.ts +3 -0
  236. package/dist/ui/kbd.js +9 -0
  237. package/dist/ui/label.d.ts +4 -0
  238. package/dist/ui/label.js +7 -0
  239. package/dist/ui/popover.d.ts +10 -0
  240. package/dist/ui/popover.js +25 -0
  241. package/dist/ui/progress.d.ts +4 -0
  242. package/dist/ui/progress.js +9 -0
  243. package/dist/ui/resizable.d.ts +7 -0
  244. package/dist/ui/resizable.js +13 -0
  245. package/dist/ui/scroll-area.d.ts +5 -0
  246. package/dist/ui/scroll-area.js +10 -0
  247. package/dist/ui/select.d.ts +15 -0
  248. package/dist/ui/select.js +36 -0
  249. package/dist/ui/separator.d.ts +4 -0
  250. package/dist/ui/separator.js +7 -0
  251. package/dist/ui/sheet.d.ts +14 -0
  252. package/dist/ui/sheet.js +54 -0
  253. package/dist/ui/sidebar.d.ts +69 -0
  254. package/dist/ui/sidebar.js +206 -0
  255. package/dist/ui/skeleton.d.ts +2 -0
  256. package/dist/ui/skeleton.js +6 -0
  257. package/dist/ui/slider.d.ts +4 -0
  258. package/dist/ui/slider.js +9 -0
  259. package/dist/ui/sonner.d.ts +3 -0
  260. package/dist/ui/sonner.js +24 -0
  261. package/dist/ui/spinner.d.ts +2 -0
  262. package/dist/ui/spinner.js +7 -0
  263. package/dist/ui/switch.d.ts +6 -0
  264. package/dist/ui/switch.js +7 -0
  265. package/dist/ui/table.d.ts +10 -0
  266. package/dist/ui/table.js +31 -0
  267. package/dist/ui/tabs.d.ts +7 -0
  268. package/dist/ui/tabs.js +22 -0
  269. package/dist/ui/textarea.d.ts +3 -0
  270. package/dist/ui/textarea.js +6 -0
  271. package/dist/ui/toggle-group.d.ts +10 -0
  272. package/dist/ui/toggle-group.js +22 -0
  273. package/dist/ui/toggle.d.ts +9 -0
  274. package/dist/ui/toggle.js +25 -0
  275. package/dist/ui/tooltip.d.ts +7 -0
  276. package/dist/ui/tooltip.js +16 -0
  277. package/dist/view-edit-cell.d.ts +49 -0
  278. package/dist/view-edit-cell.js +198 -0
  279. package/dist/view-editor.d.ts +52 -0
  280. package/dist/view-editor.js +1081 -0
  281. package/dist/view-formulas.d.ts +61 -0
  282. package/dist/view-formulas.js +247 -0
  283. package/dist/view-icons.d.ts +20 -0
  284. package/dist/view-icons.js +70 -0
  285. package/dist/view-model.d.ts +390 -0
  286. package/dist/view-model.js +316 -0
  287. package/dist/whats-new.d.ts +3 -0
  288. package/dist/whats-new.js +53 -0
  289. package/dist/widget-controls.d.ts +9 -0
  290. package/dist/widget-controls.js +26 -0
  291. package/dist/widget-data.d.ts +4 -0
  292. package/dist/widget-data.js +32 -0
  293. package/dist/widget-settings.d.ts +516 -0
  294. package/dist/widget-settings.js +290 -0
  295. package/dist/widget-thumbs.d.ts +3 -0
  296. package/dist/widget-thumbs.js +29 -0
  297. package/dist/workspace-keys.d.ts +12 -0
  298. package/dist/workspace-keys.js +13 -0
  299. package/dist/workspaces.d.ts +57 -0
  300. package/dist/workspaces.js +473 -0
  301. package/package.json +82 -4
@@ -0,0 +1,203 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // LumenGrid: an
3
+ // LumenLayout's cards on a real grid, react-grid-layout underneath. A desktop has 6 columns, a phone
4
+ // 2; rows are 8px. A card is placed at x, y and sized w by h; a card with no h is as tall as what it
5
+ // shows (measured), and cards that start on the same row match the tallest of them. While the layout
6
+ // is edited, cards move by their grip and size from their corner; a card resized keeps its height.
7
+ // Outside Edit layout a full-width table stays out of the grid, so it keeps scrolling sideways with
8
+ // the page while the cards around it stay put.
9
+ import { GRID_BLOCK_ROWS, GRID_COLS, GRID_PHONE_COLS, GRID_ROW_PX, } from '@columenlabs/lumen-ui-types/views';
10
+ import { createContext, useCallback, useContext, useLayoutEffect, useMemo, useRef, useState, } from 'react';
11
+ import { GridLayout, useContainerWidth, verticalCompactor, } from 'react-grid-layout';
12
+ import { cn } from './lib/utils.js';
13
+ import { LumenBoundary } from './lumen-boundary.js';
14
+ /** The gap between cards, the shell's section gap. */
15
+ export const GRID_GAP = 12;
16
+ /** Narrower than this the grid has the phone's 2 columns. */
17
+ const PHONE_BELOW = 720;
18
+ /** A card's height before it is measured. */
19
+ const GUESS = GRID_BLOCK_ROWS * 2;
20
+ /** Inside a grid cell: `fixed` when the cell has a height of its own, so the card fills it. */
21
+ const GridCellContext = createContext(null);
22
+ export const useGridCell = () => useContext(GridCellContext);
23
+ const CARD = '[data-slot=lumen-card]';
24
+ const rowsOf = (px) => Math.max(2, Math.ceil((px + GRID_GAP) / GRID_ROW_PX));
25
+ const byPlace = (a, b) => a.y - b.y || a.x - b.x;
26
+ /** Cards placed where they say and, in order after them, the ones that do not, row by row. */
27
+ function arrange(cards, cols, top) {
28
+ const out = [];
29
+ let bottom = top;
30
+ for (const c of cards)
31
+ if (c.x !== undefined && c.y !== undefined) {
32
+ const w = Math.min(c.w, cols);
33
+ out.push({ i: c.id, x: Math.min(c.x, cols - w), y: top + c.y, w, h: c.h });
34
+ bottom = Math.max(bottom, top + c.y + c.h);
35
+ }
36
+ let x = 0;
37
+ let row = 0;
38
+ for (const c of cards) {
39
+ if (c.x !== undefined && c.y !== undefined)
40
+ continue;
41
+ const w = Math.min(c.w, cols);
42
+ if (x + w > cols) {
43
+ x = 0;
44
+ row += 1;
45
+ }
46
+ // Rows far apart: compacting pulls each up under the row before it.
47
+ out.push({ i: c.id, x, y: bottom + row * 10_000, w, h: c.h });
48
+ x += w;
49
+ }
50
+ return out;
51
+ }
52
+ /** The highest row a laid-out set reaches. */
53
+ const bottomOf = (items) => items.reduce((m, it) => Math.max(m, it.y + it.h), 0);
54
+ /**
55
+ * The grid's layout: the page's lead cards, then the saved ones where they were placed, then the
56
+ * page's other cards, compacted upward; cards of measured height starting on the same row take the
57
+ * tallest's height.
58
+ */
59
+ export function gridLayout(groups, heights, phone) {
60
+ const cols = phone ? GRID_PHONE_COLS : GRID_COLS;
61
+ const auto = new Set();
62
+ const sized = (c) => {
63
+ const own = phone ? c.phone?.h : c.h;
64
+ if (own === undefined)
65
+ auto.add(c.id);
66
+ const h = own ?? heights[c.id] ?? GUESS;
67
+ if (!phone)
68
+ return { id: c.id, w: c.w, h, x: c.x, y: c.y };
69
+ if (c.phone)
70
+ return { id: c.id, w: c.phone.w, h, x: c.phone.x, y: c.phone.y };
71
+ return { id: c.id, w: c.w <= 1 ? 1 : GRID_PHONE_COLS, h };
72
+ };
73
+ // A phone with no places of its own keeps the desktop's reading order.
74
+ const ordered = phone
75
+ ? groups.map((g) => {
76
+ if (g.some((c) => c.phone))
77
+ return g;
78
+ const at = new Map(gridLayout([g], heights, false).map((it) => [it.i, it]));
79
+ return [...g].sort((a, b) => {
80
+ const pa = at.get(a.id);
81
+ const pb = at.get(b.id);
82
+ return pa && pb ? byPlace(pa, pb) : 0;
83
+ });
84
+ })
85
+ : groups;
86
+ let items = [];
87
+ for (const g of ordered)
88
+ items = [...items, ...arrange(g.map(sized), cols, bottomOf(items))];
89
+ // A row settles only once the one above it has, so match heights and compact until nothing moves.
90
+ let laid = [...verticalCompactor.compact(items, cols)];
91
+ for (let pass = 0; pass < laid.length; pass++) {
92
+ const tallest = new Map();
93
+ for (const it of laid)
94
+ tallest.set(it.y, Math.max(tallest.get(it.y) ?? 0, it.h));
95
+ let changed = false;
96
+ const even = laid.map((it) => {
97
+ const h = auto.has(it.i) ? (tallest.get(it.y) ?? it.h) : it.h;
98
+ if (h !== it.h)
99
+ changed = true;
100
+ return h === it.h ? it : { ...it, h };
101
+ });
102
+ if (!changed)
103
+ break;
104
+ laid = [...verticalCompactor.compact(even, cols)];
105
+ }
106
+ return laid.sort(byPlace);
107
+ }
108
+ /** A cell measures what it draws when it has no height of its own (a frame: its parts). */
109
+ function Cell({ id, fixed, report, children, }) {
110
+ const ref = useRef(null);
111
+ useLayoutEffect(() => {
112
+ const cell = ref.current;
113
+ if (fixed || !cell)
114
+ return;
115
+ const measure = () => {
116
+ const el = cell.firstElementChild;
117
+ if (!el)
118
+ return report(id, 0);
119
+ // A card fills its cell (through a wrapper too), so its height is its parts': header,
120
+ // filter, body and footer, plus whatever else the wrapper holds.
121
+ const card = el.matches(CARD) ? el : el.querySelector(`:scope > ${CARD}`);
122
+ const px = card
123
+ ? [...card.children].reduce((s, c) => s + c.offsetHeight, 0) +
124
+ (card.offsetHeight - card.clientHeight) +
125
+ (el.offsetHeight - card.offsetHeight)
126
+ : el.offsetHeight;
127
+ report(id, px);
128
+ };
129
+ measure();
130
+ const ro = new ResizeObserver(measure);
131
+ const el = cell.firstElementChild;
132
+ const card = el?.matches(CARD) ? el : el?.querySelector(`:scope > ${CARD}`);
133
+ if (el)
134
+ ro.observe(el);
135
+ if (card)
136
+ for (const c of card.children)
137
+ ro.observe(c);
138
+ return () => ro.disconnect();
139
+ });
140
+ const value = useMemo(() => ({ fixed }), [fixed]);
141
+ return (_jsx(GridCellContext.Provider, { value: value, children: _jsx("div", { ref: ref, className: "h-full [&>*:has(>[data-slot=lumen-card])]:h-full", style: { paddingBottom: GRID_GAP }, children: children }) }));
142
+ }
143
+ const gridConfig = (phone) => ({
144
+ cols: phone ? GRID_PHONE_COLS : GRID_COLS,
145
+ rowHeight: GRID_ROW_PX,
146
+ margin: [GRID_GAP, 0],
147
+ containerPadding: [0, 0],
148
+ });
149
+ /** The page's cards on the grid; `onPlace` makes them movable and sizable (Edit layout). */
150
+ export function LumenGrid({ lead = [], cards, tail = [], editing, onPlace, }) {
151
+ const { width, containerRef, mounted } = useContainerWidth({ measureBeforeMount: true });
152
+ const phone = width < PHONE_BELOW;
153
+ const [heights, setHeights] = useState({});
154
+ const report = useCallback((id, px) => setHeights((h) => {
155
+ const r = rowsOf(px);
156
+ return h[id] === r ? h : { ...h, [id]: r };
157
+ }), []);
158
+ const all = [...lead, ...cards, ...tail];
159
+ const byId = new Map(all.map((c) => [c.id, c]));
160
+ const layout = gridLayout([lead, cards, tail], heights, phone);
161
+ const own = (c) => (phone ? c.phone?.h : c.h);
162
+ const cell = (c) => (_jsx("div", { "data-locked": c.locked || undefined, children: _jsx(Cell, { id: c.id, fixed: own(c) !== undefined, report: report, children: _jsx(LumenBoundary, { where: c.id, children: c.node }) }) }, c.id));
163
+ const measure = (_jsx("div", { ref: containerRef, "aria-hidden": true, className: "sticky left-0 h-0 w-(--view-w) -mb-3" }));
164
+ if (editing) {
165
+ const commit = (l, resized) => onPlace?.([...l]
166
+ .filter((it) => !byId.get(it.i)?.locked)
167
+ .sort(byPlace)
168
+ .map((it) => {
169
+ const c = byId.get(it.i);
170
+ const h = it.i === resized ? it.h : c && own(c);
171
+ return { id: it.i, x: it.x, y: it.y, w: it.w, ...(h === undefined ? {} : { h }) };
172
+ }), phone);
173
+ return (_jsxs(_Fragment, { children: [measure, _jsx("div", { className: "sticky left-0 w-(--view-w)", style: { marginBottom: -GRID_GAP }, children: mounted && (_jsx(GridLayout, { width: width, layout: layout.map((it) => byId.get(it.i)?.locked ? { ...it, isDraggable: false, isResizable: false } : it), gridConfig: gridConfig(phone), dragConfig: { handle: '.lumen-drag', threshold: 4 }, resizeConfig: { enabled: true, handles: ['se'] }, compactor: verticalCompactor, className: "lumen-grid lumen-grid-edit", onDragStop: (l) => commit(l), onResizeStop: (l, _o, n) => commit(l, n?.i), children: layout.flatMap((it) => {
174
+ const c = byId.get(it.i);
175
+ return c ? [cell(c)] : [];
176
+ }) })) })] }));
177
+ }
178
+ // Outside Edit layout: runs of cards on the grid, with each full-width table between them.
179
+ const parts = [];
180
+ for (const it of layout) {
181
+ const c = byId.get(it.i);
182
+ if (!c)
183
+ continue;
184
+ const last = parts[parts.length - 1];
185
+ if (c.wide)
186
+ parts.push(c);
187
+ else if (Array.isArray(last))
188
+ last.push(it);
189
+ else
190
+ parts.push([it]);
191
+ }
192
+ return (_jsxs(_Fragment, { children: [measure, mounted &&
193
+ parts.map((p) => Array.isArray(p) ? (_jsx(GridRun, { items: p, phone: phone, width: width, cell: (id) => {
194
+ const c = byId.get(id);
195
+ return c ? cell(c) : null;
196
+ } }, p.map((it) => it.i).join())) : (_jsx("div", { className: "not-has-[table]:sticky not-has-[table]:left-0 not-has-[table]:w-(--view-w)", children: p.node }, p.id)))] }));
197
+ }
198
+ /** Cards between two full-width tables: their own grid, from the row the first starts on. */
199
+ function GridRun({ items, phone, width, cell, }) {
200
+ const top = Math.min(...items.map((it) => it.y));
201
+ const layout = items.map((it) => ({ ...it, y: it.y - top, static: true }));
202
+ return (_jsx("div", { className: cn('sticky left-0 w-(--view-w)'), style: { marginBottom: -GRID_GAP }, children: _jsx(GridLayout, { width: width, layout: layout, gridConfig: gridConfig(phone), dragConfig: { enabled: false }, resizeConfig: { enabled: false }, compactor: verticalCompactor, className: "lumen-grid", children: items.map((it) => cell(it.i)) }) }));
203
+ }
@@ -0,0 +1,150 @@
1
+ import { type CustomViewConfig } from '@columenlabs/lumen-ui-types/views';
2
+ import type { RowData } from '@tanstack/react-table';
3
+ import { type LucideIcon } from 'lucide-react';
4
+ import { type ReactElement, type ReactNode } from 'react';
5
+ import { type FilterToken } from './lumen-filter.js';
6
+ import { type LinkColor } from './lumen-links.js';
7
+ import { type LumenLoad } from './lumen-source.js';
8
+ import { type LumenViewProps } from './lumen-view.js';
9
+ import { type CardControls, type CardLink } from './lumen-widget.js';
10
+ import { type Crumb } from './page.js';
11
+ import { type ViewEditorSection } from './view-editor.js';
12
+ import { type ViewDomain } from './view-model.js';
13
+ /** A card a page builds in code, after the saved view's cards: a view of other data, a list. */
14
+ export interface LumenLayoutCard {
15
+ id: string;
16
+ title: string;
17
+ icon?: LucideIcon;
18
+ /** Tables (and grids) take the full width and show a progress bar; widgets a skeleton.
19
+ * `own`: a body that draws its own frame (LumenCoverageMap, LumenCoverageCalendarMap). */
20
+ kind?: 'table' | 'widget' | 'own';
21
+ /** Columns of 6 it spans (left out: all 6), and rows of 8px it is tall (left out: as it draws). */
22
+ w?: number;
23
+ h?: number;
24
+ /** Its source's loading state: its ↻, states and load time. */
25
+ load?: LumenLoad;
26
+ rows?: number;
27
+ /** Buttons or links in the card's header, before its ↻ ("All jobs"). */
28
+ tools?: ReactNode;
29
+ /** ⓘ How to read: a few lines on what the card shows and what to look for. */
30
+ help?: ReactNode;
31
+ /** What ✦ Ask the assistant adds to its question (the record to read); false: no ✦ on this card. */
32
+ assistant?: string | false;
33
+ /** Every card carries the link square: left out, the page keeps its colour;
34
+ * false for a form (setup, what-if), which has no data to link; a CardLink to drive it by hand. */
35
+ link?: CardLink | false;
36
+ /** Its colour when the page opens (left out: none). */
37
+ color?: LinkColor;
38
+ /** Chips it shares with the cards of its colour: what a control in its header or a tap in its
39
+ * body picked (`{key: 'book', value: 'Live'}`). */
40
+ publishes?: FilterToken[];
41
+ /** The keys it narrows on: of its colour's chips it follows these, says them after its title
42
+ * and hands them to `children` when that is a function. */
43
+ reads?: string[];
44
+ /** Shown while the first load runs (a row of tile placeholders). */
45
+ skeleton?: ReactNode;
46
+ children?: ReactNode | ((follows: FilterToken[]) => ReactNode);
47
+ /** An `own` card that takes its place in a saved view's layout (LumenViewCard): drawn with the
48
+ * layout's controls and the width the layout keeps for it. Its id must be a field-style key
49
+ * (lowercase, digits, _) that no card of the view uses. */
50
+ frame?: (f: {
51
+ controls?: CardControls;
52
+ }) => ReactNode;
53
+ }
54
+ interface PageProps {
55
+ crumbs: Crumb[];
56
+ actions?: ReactNode;
57
+ /** A short status line beside the breadcrumb ("Updated 18:22", a warnings badge). */
58
+ meta?: ReactNode;
59
+ status?: ReactNode;
60
+ /** Above the cards: tabs or a lead the app shows with this page. */
61
+ intro?: ReactNode;
62
+ /** The page's own cards, after the saved view's. One with a `frame` joins the saved layout as a
63
+ * `page` card: moved and sized like the rest, last until it is placed. */
64
+ cards?: LumenLayoutCard[];
65
+ /** The page's own cards before the saved view's (a chart over the list it draws from). */
66
+ lead?: LumenLayoutCard[];
67
+ /** Cards take the page's width and wide ones scroll inside themselves (coverage pages). */
68
+ fit?: boolean;
69
+ }
70
+ /** A saved view on the page: its domain, config and rows. */
71
+ export interface LumenLayoutViewProps<T> {
72
+ domain: ViewDomain<T>;
73
+ config: CustomViewConfig;
74
+ rows: T[];
75
+ /** Saves an edited view; without it the page has no Edit button. */
76
+ onConfigChange?: (config: CustomViewConfig) => void;
77
+ /**
78
+ * Whether the page's layout can be edited (every page is a saved view, with
79
+ * this flag set in code for now). False locks it: no Edit, no + Widget, no column changes, even
80
+ * with `onConfigChange` given. Left out: editable where `onConfigChange` is.
81
+ */
82
+ editable?: boolean;
83
+ /** An app's own section in the Edit dialog (an explorer view's columns). */
84
+ editor?: ViewEditorSection;
85
+ /** Duplicate and Delete, offered in the Edit dialog's More section. */
86
+ onDuplicate?: () => void;
87
+ onDelete?: () => void;
88
+ /** A built-in page the person changed (`useAppView`): Edit layout offers Reset to default. */
89
+ onReset?: () => void;
90
+ /** The rows' loading state (`useLumenLoad` over the queries the rows come from): every view and
91
+ * widget shows its ↻, skeleton, error and load time from it, so it is required. */
92
+ load: LumenLoad;
93
+ /** Shown in place of the table instead of the rows, e.g. an empty state ("No accounts yet"). */
94
+ state?: ReactNode;
95
+ /** Draws one of the domain's tiles over the view's rows (not the table's filter); the
96
+ * view's `tiles` pick which, in order. */
97
+ tile?: (key: string, rows: T[]) => ReactNode;
98
+ /** The details drawer title for a row. */
99
+ rowTitle?: (row: T) => string;
100
+ /** The saved view's id, shown in the Edit dialog to copy for an MCP call. */
101
+ viewId?: string;
102
+ /** Told the rows the view shows after its filters, every page, whenever they change, so an app
103
+ * loads live data for those rows only. */
104
+ onShownRows?: (rows: T[]) => void;
105
+ /** Told every row the page's Summaries draw, so an app loads live data for those too
106
+ * : a Summary not linked to the table covers rows the table hides. */
107
+ onSummaryRows?: (rows: T[]) => void;
108
+ selection?: LumenViewProps<T>['selection'];
109
+ /** A row click opens it (a page of its own) instead of the details drawer. */
110
+ onOpen?: (row: T) => void;
111
+ /** The `rowLink` value tapped when the page opens, so linked cards start narrowed to one row
112
+ * (a playbook's Results open on its Default variant). */
113
+ rowPick?: string;
114
+ groupActions?: LumenViewProps<T>['groupActions'];
115
+ /** Told the filter's tokens whenever they change, so an app can load rows for what they pick
116
+ * (the Symbols view's `in` filter). */
117
+ onTokens?: (tokens: FilterToken[]) => void;
118
+ /** Buttons in the view card's header ("+ Add time"). */
119
+ viewTools?: ReactNode;
120
+ /** False leaves Field out of the header's + Add column (a table whose columns are fixed, a
121
+ * Tune's tries); otherwise every editable view offers the Columns picker there. */
122
+ columnPicker?: boolean;
123
+ /** No filter box (a short list, e.g. a playbook's variants). */
124
+ hideFilter?: boolean;
125
+ /**
126
+ * How the person's chips, sort and grouping save (rule C, by surface):
127
+ * `document` (a saved view, the default) holds them for the visit and shows Changed with Save
128
+ * view and Revert; `sheet` (a workspace tab) saves every change at once through
129
+ * `onConfigChange`, like a cell in a sheet.
130
+ */
131
+ save?: 'sheet' | 'document';
132
+ }
133
+ export type LumenLayoutProps<T> = PageProps & (LumenLayoutViewProps<T> | {
134
+ domain?: undefined;
135
+ config?: undefined;
136
+ rows?: undefined;
137
+ });
138
+ /** A page of cards: a saved view's (its filter, tiles, view and widgets) and the page's own. */
139
+ export declare function LumenLayout<T extends RowData>(props: PageProps & LumenLayoutViewProps<T>): ReactElement;
140
+ /** A page of cards built in code only. */
141
+ export declare function LumenLayout(props: PageProps): ReactElement;
142
+ /** The keys a code card over a domain's rows reads (its `reads`). */
143
+ export declare const domainKeys: (domain: {
144
+ fields: {
145
+ key: string;
146
+ }[];
147
+ }) => string[];
148
+ /** A code card's rows narrowed by the chips it follows, read on the domain's fields. */
149
+ export declare function followRows<T>(domain: ViewDomain<T>, rows: T[], follows: FilterToken[]): T[];
150
+ export {};