astryx-dracula 0.3.1 → 0.5.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.
Files changed (59) hide show
  1. package/USAGE.md +25 -0
  2. package/astryx-dracula.d.ts +2 -2
  3. package/astryx-dracula.js +384 -3
  4. package/astryx-dracula.variants.d.ts +2 -14
  5. package/icons.mjs +125 -0
  6. package/package.json +21 -9
  7. package/shared/auth-chrome.tsx +156 -6
  8. package/shared/chaptered-doc.tsx +3 -4
  9. package/shared/chart-hues.ts +0 -5
  10. package/shared/chart-legend.tsx +2 -3
  11. package/shared/data-bar.tsx +5 -5
  12. package/shared/format-money.ts +3 -0
  13. package/shared/metric-card.tsx +127 -0
  14. package/shared/revenue-chart.tsx +63 -7
  15. package/shared/scene-hues.ts +0 -8
  16. package/shared/scene-tile.tsx +3 -11
  17. package/shared/settings-data.ts +71 -1
  18. package/shared/settings-rows.tsx +76 -30
  19. package/shared/stars.tsx +28 -0
  20. package/templates/ai-chat-landing.tsx +105 -67
  21. package/templates/ai-chat.tsx +67 -80
  22. package/templates/centered-hero.tsx +2 -2
  23. package/templates/contact-form.tsx +11 -10
  24. package/templates/dashboard-portfolio.tsx +77 -155
  25. package/templates/dashboard.tsx +24 -137
  26. package/templates/detail-page.tsx +25 -22
  27. package/templates/documentation-design.tsx +39 -478
  28. package/templates/documentation-technical.tsx +13 -31
  29. package/templates/editor.tsx +168 -185
  30. package/templates/file-explorer.tsx +91 -93
  31. package/templates/form-two-column.tsx +1 -1
  32. package/templates/ide.tsx +18 -24
  33. package/templates/incident-console.tsx +67 -42
  34. package/templates/kanban-board.tsx +36 -85
  35. package/templates/library.tsx +42 -66
  36. package/templates/login-card.tsx +33 -123
  37. package/templates/login-split.tsx +96 -119
  38. package/templates/login-sso.tsx +69 -74
  39. package/templates/login.tsx +21 -118
  40. package/templates/messaging-shell.tsx +162 -168
  41. package/templates/payment-form.tsx +63 -71
  42. package/templates/product-detail.tsx +11 -46
  43. package/templates/product-tour.tsx +38 -60
  44. package/templates/settings-dialog.tsx +93 -118
  45. package/templates/settings-sidebar.tsx +122 -143
  46. package/templates/settings.tsx +208 -121
  47. package/templates/shell-nav.tsx +20 -75
  48. package/templates/shell-side-nav.tsx +16 -90
  49. package/templates/shell-top-nav.tsx +20 -53
  50. package/templates/side-gallery.tsx +19 -19
  51. package/templates/table-grouped.tsx +86 -136
  52. package/templates/table-page-chart.tsx +12 -11
  53. package/templates/table-page-heatmap-status.tsx +0 -17
  54. package/templates/table-page-shoe-store-heatmap.tsx +12 -12
  55. package/templates/table-page.tsx +4 -31
  56. package/templates/table.tsx +18 -2
  57. package/templates/tech-report.tsx +59 -73
  58. package/templates/theme-showcase.tsx +124 -415
  59. package/theme.css +211 -119
@@ -1,13 +1,10 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
2
  // XLE (canonical structure, validated with `bunx astryx layout check`):
3
- // L[h=fill] > LC[p=6] > V[g=6] > (H[j=between a=center] > Hd"The night vault"[level=1] + DM"1 year") + (G[c={min:280} g=4] > (C > V[g=2] > Tx"Total value"[t=supporting] + (H[g=2] > Tx"$294,200"[t=display-3] + Tx"+14.8%"[t=body]))*4) + (G[c={min:280} g=4] > (C > V[g=4] > (H[j=between] > Hd"Vault value"[level=2] + Lk"View details") + (C > V[g=3] > Tx"Weekly closes"[t=supporting])) + (V[g=4] > (H[j=between] > Hd"Top holdings"[level=2] + Lk"View all") + UL)) + D + (H[j=between a=start] > (V[g=1] > Hd"Market at midnight"[level=2] + Tx"Past 24 hours under moonlight"[t=body]) + B"View more") + (G[c={min:280} g=4] > (C > V[g=3] > Hd"Index"[level=3] + Tx"$5,200"[t=body])*8) + (C > V[g=4] > Hd"Trending stocks"[level=3] + T)
3
+ // L[h=fill] > LC[p=6] > V[g=6] > (H[j=between a=center] > Hd"The night vault"[level=1] + Tx"1 year"[t=body]) + (G[c={min:280} g=4] > (C > V[g=2] > Tx"Total value"[t=supporting] + (H[g=2] > Tx"$294,200"[t=display-3] + Tx"+14.8%"[t=body]))*4) + (G[c={min:280} g=4] > (C > V[g=4] > (H[j=between] > Hd"Vault value"[level=2] + Lk"View details") + (C > V[g=3] > Tx"Weekly closes"[t=supporting])) + (V[g=4] > (H[j=between] > Hd"Top holdings"[level=2] + Lk"View all") + UL)) + D + (H[j=between a=start] > (V[g=1] > Hd"Market at midnight"[level=2] + Tx"Past 24 hours under moonlight"[t=body]) + B"View more") + (G[c={min:280} g=4] > (C > V[g=3] > Hd"Index"[level=3] + Tx"$5,200"[t=body])*8) + (C > V[g=4] > Hd"Trending stocks"[level=3] + T)
4
4
 
5
5
  /**
6
6
  * Portfolio Dashboard — the night vault: KPI tiles, a weekly value chart, the holdings list, and the market board. (Frame/responsive/container: see XLE header above.)
7
7
  */
8
-
9
- import {useState} from 'react';
10
-
11
8
  import {VStack, HStack, Layout, LayoutContent} from '@astryxdesign/core/Layout';
12
9
  import {Text, Heading} from '@astryxdesign/core/Text';
13
10
  import {Card} from '@astryxdesign/core/Card';
@@ -15,10 +12,10 @@ import {Grid} from '@astryxdesign/core/Grid';
15
12
  import {Link} from '@astryxdesign/core/Link';
16
13
  import {Avatar} from '@astryxdesign/core/Avatar';
17
14
  import {List, ListItem} from '@astryxdesign/core/List';
18
- import {DropdownMenu} from '@astryxdesign/core/DropdownMenu';
19
15
  import {Button} from '@astryxdesign/core/Button';
20
- import {Table, proportional, pixel} from '@astryxdesign/core/Table';
21
- import type {TableColumn} from '@astryxdesign/core/Table';
16
+ import {proportional, pixel} from '@astryxdesign/core/Table';
17
+ import type {TableColumn, PixelWidth} from '@astryxdesign/core/Table';
18
+ import {MetricCard, TableCard} from 'astryx-dracula/shared/metric-card';
22
19
  import {Divider} from '@astryxdesign/core/Divider';
23
20
  import {MetricDelta} from 'astryx-dracula/shared/metric-delta';
24
21
  import {Sparkline, type SparkPoint} from 'astryx-dracula/shared/sparkline';
@@ -142,39 +139,13 @@ const topAssets = [
142
139
  ];
143
140
 
144
141
  // 96 points per series = one tick every 15 minutes across a 24h window.
145
- // Deterministic LCG so the sparklines are stable across renders. Each point
146
- // carries the slot it was generated for, so the bars key off data rather than
147
- // off the array position.
148
- function genSpark(
149
- seed: number,
150
- start: number,
151
- end: number,
152
- volatility: number,
153
- N: number = 96,
154
- ): SparkPoint[] {
155
- let s = seed >>> 0;
156
- const rand = () => {
157
- s = (Math.imul(s, 1664525) + 1013904223) >>> 0;
158
- return s / 0x100000000;
159
- };
160
- const points: SparkPoint[] = [];
161
- let drift = 0;
162
- for (let i = 0; i < N; i++) {
163
- const t = i / (N - 1);
164
- const trend = start + (end - start) * t;
165
- // Multiple overlaid waves at different frequencies for session-like rhythm
166
- // plus high-frequency chop.
167
- const wave =
168
- Math.sin(t * Math.PI * 2.3 + seed * 0.13) * volatility * 1.4 +
169
- Math.sin(t * Math.PI * 5.7 + seed * 0.41) * volatility * 0.9 +
170
- Math.sin(t * Math.PI * 13.1 + seed * 0.07) * volatility * 0.5;
171
- // Loosely-correlated random walk so adjacent ticks jitter rather than glide.
172
- drift = drift * 0.55 + (rand() - 0.5) * volatility * 2.2;
173
- // Occasional sharper spike to mimic news-driven moves.
174
- const spike = rand() < 0.04 ? (rand() - 0.5) * volatility * 4 : 0;
175
- points.push({id: `${seed}-${i}`, value: trend + wave + drift + spike});
176
- }
177
- return points;
142
+ // Static arrays: the dashboard metric tiles already establish the toSparkSeries
143
+ // precedent (static number rows + stable ids), so these print the same LCG
144
+ // output the generator produced instead of rerunning it per render. Every
145
+ // sample carries the slot it was generated for, so the bars key off data
146
+ // rather than off the array position.
147
+ function toSparkSeries(seed: number, values: number[]): SparkPoint[] {
148
+ return values.map((value, i) => ({id: `${seed}-${i}`, value}));
178
149
  }
179
150
 
180
151
  // Market index cards — 24h sparkline data (every 15min, 96 points)
@@ -185,7 +156,8 @@ const marketIndices = [
185
156
  price: '43,821.67',
186
157
  change: '+0.42%',
187
158
  positive: true,
188
- spark: genSpark(1337, 78, 84, 3.2),
159
+ // prettier-ignore
160
+ spark: toSparkSeries(1337, [77.87, 75.96, 76.35, 77.99, 78.95, 77.57, 78.14, 75.12, 74.24, 74.03, 72.88, 71.93, 74.65, 71.43, 69.56, 74.19, 72.66, 72.02, 76.09, 74.07, 75.82, 78.14, 77.91, 80.15, 79.18, 76.96, 76.57, 81.07, 83.59, 83.21, 81.58, 83.82, 84.82, 88.56, 87.03, 89.64, 87.35, 89.63, 90.67, 87.45, 87.4, 86.09, 87.85, 82.63, 81.81, 84.78, 82.26, 83.25, 80.04, 80.63, 83.56, 84.28, 83.96, 84.49, 80.85, 79.3, 80.45, 84.58, 81.83, 83.45, 87.92, 90.46, 89.1, 88.1, 90.6, 87.26, 84.82, 84.27, 86.7, 82.89, 84.38, 89.24, 82.39, 82.18, 82.24, 81.38, 82.38, 78.55, 76.91, 75.31, 78.55, 76.46, 73.89, 76.36, 77.01, 79.15, 78.98, 79.79, 78.68, 77.35, 76.01, 84.41, 82.31, 81.97, 80.42, 80.29]),
189
161
  },
190
162
  {
191
163
  name: 'NASDAQ',
@@ -193,7 +165,8 @@ const marketIndices = [
193
165
  price: '18,942.18',
194
166
  change: '-0.50%',
195
167
  positive: false,
196
- spark: genSpark(2042, 86, 78, 3.8),
168
+ // prettier-ignore
169
+ spark: toSparkSeries(2042, [88.89, 94.82, 92.53, 93.22, 91.88, 91.07, 90.86, 89.3, 89.42, 85.69, 88.02, 82.52, 84.79, 86.08, 81.15, 78.83, 79.56, 80.01, 83.14, 84.1, 88.09, 84.29, 82.48, 84.9, 84.15, 86.04, 87.12, 86.85, 82.29, 84.8, 81.87, 79.88, 79.57, 78.77, 82.32, 83.19, 85.35, 86.71, 87.68, 80, 74.7, 73.28, 77.07, 71.96, 68.62, 70.7, 73.72, 74.67, 68.28, 71.29, 69.49, 71.73, 72.22, 73.75, 77.29, 79.86, 76.32, 78.94, 78.77, 77.52, 78.62, 80.01, 85.09, 86.36, 83.71, 88.49, 89.99, 86.02, 89.11, 83.46, 84.72, 85.43, 85.97, 84.73, 86.18, 88.06, 82.82, 82.32, 87.12, 82.77, 79.32, 84.78, 84.46, 80.3, 84.21, 82.15, 84.84, 75.1, 82.07, 85.92, 78.74, 82.77, 82.99, 87.84, 88.57, 90.07]),
197
170
  },
198
171
  {
199
172
  name: 'S&P 500',
@@ -201,7 +174,8 @@ const marketIndices = [
201
174
  price: '5,918.33',
202
175
  change: '+0.21%',
203
176
  positive: true,
204
- spark: genSpark(3155, 71, 75, 2.8),
177
+ // prettier-ignore
178
+ spark: toSparkSeries(3155, [73.95, 72.03, 71.08, 71.6, 73.2, 72.67, 74.14, 73.45, 76.38, 75.1, 77.61, 79.72, 77.52, 79.27, 78.25, 75.63, 76.23, 76.58, 72.42, 72.4, 69.84, 66.76, 67.12, 67.73, 64.69, 68.27, 65.01, 64.02, 66.05, 69.6, 68.7, 70.05, 69.54, 65.52, 66.42, 66.58, 69.36, 71.28, 71.66, 73.08, 70.32, 69.85, 72.45, 72.94, 72.02, 74.88, 71.61, 71.7, 72.69, 74.3, 74.46, 71.99, 70.45, 70.11, 72.53, 69.96, 71.6, 72, 74.4, 70.78, 72.74, 71.44, 71.37, 73.54, 71.05, 72.86, 70.79, 69.5, 73.43, 72.36, 73.76, 75.05, 77.58, 77.17, 78.5, 82.25, 78.78, 78.42, 78.07, 82.01, 78.82, 78.48, 78.36, 79.8, 79, 76.6, 75.18, 75.82, 78.39, 80.59, 80.93, 79.71, 78.44, 74.41, 74.09, 75.98]),
205
179
  },
206
180
  {
207
181
  name: 'NYSE Composite',
@@ -209,7 +183,8 @@ const marketIndices = [
209
183
  price: '19,752.41',
210
184
  change: '-0.20%',
211
185
  positive: false,
212
- spark: genSpark(4287, 70, 65, 3.0),
186
+ // prettier-ignore
187
+ spark: toSparkSeries(4287, [64.45, 61.11, 62.04, 60.88, 61.84, 66.42, 65.95, 68.68, 66.53, 66.2, 64.61, 66.78, 65.37, 64.69, 64.48, 60.48, 70.11, 69.62, 67.37, 70.31, 73.61, 74.74, 71.95, 68.83, 68.85, 66.69, 65.72, 64.01, 65.91, 64.38, 68.51, 63.62, 68.71, 66.45, 66.61, 70.22, 67.86, 68.96, 72.44, 74.53, 74.4, 75.52, 72.63, 69.2, 67.72, 72.46, 75.34, 74.64, 76.13, 73.74, 73.53, 74.47, 76.73, 76.4, 74.91, 74.34, 73.92, 70.94, 71.5, 68.68, 68.44, 65.97, 68.07, 69.63, 70.45, 68.45, 65.12, 63.93, 64.71, 66.68, 61.08, 58, 61.79, 63.61, 65.05, 62.4, 68.06, 65.87, 66.63, 68.18, 68.06, 67.32, 64.23, 65.72, 63.78, 62.09, 63.94, 61.55, 59.14, 59.95, 58.74, 59.44, 61.25, 63.96, 60.62, 59.61]),
213
188
  },
214
189
  {
215
190
  name: 'NVIDIA Corp.',
@@ -217,7 +192,8 @@ const marketIndices = [
217
192
  price: '$177.39',
218
193
  change: '+0.93%',
219
194
  positive: true,
220
- spark: genSpark(5519, 60, 67, 3.6),
195
+ // prettier-ignore
196
+ spark: toSparkSeries(5519, [66.26, 68.28, 68.39, 65.49, 62.97, 68.15, 71.28, 67.14, 71.53, 68.43, 69.39, 73.97, 67.92, 57.1, 61.58, 64.45, 59.15, 55.38, 56.78, 60.73, 63.46, 59.3, 56.58, 56.55, 59.43, 58.93, 57.88, 57.25, 56.11, 61.79, 62.87, 65.09, 66.15, 59.93, 60.64, 63.97, 65.3, 65.5, 66.02, 67.5, 67.43, 61.9, 59.69, 59.48, 59.88, 61.71, 59.95, 54.99, 52.18, 51.09, 52.02, 53.2, 55.88, 60.58, 56.65, 58.89, 55.9, 58.03, 60.31, 62.14, 57.94, 61.24, 60.94, 59.09, 64.39, 63.59, 64.45, 65.84, 66.83, 69.01, 68.41, 71.9, 74.79, 69.82, 67.76, 68.77, 67.7, 65.13, 62.97, 66.86, 69.31, 69.64, 67.68, 67.14, 67.12, 68.13, 61.27, 66.8, 63.53, 68.48, 64.28, 68.53, 69.09, 66.14, 66.67, 72.57]),
221
197
  },
222
198
  {
223
199
  name: 'Intel Corp.',
@@ -225,7 +201,8 @@ const marketIndices = [
225
201
  price: '$50.38',
226
202
  change: '+4.89%',
227
203
  positive: true,
228
- spark: genSpark(6701, 40, 51, 2.6),
204
+ // prettier-ignore
205
+ spark: toSparkSeries(6701, [40.29, 36.52, 38.65, 38.66, 38.73, 39.59, 39.74, 40.27, 37.68, 37.74, 38.19, 38.45, 34.69, 33.53, 36.19, 35.7, 37.18, 36.76, 36.8, 38.82, 38.54, 36.97, 38.21, 39.09, 43.08, 45.5, 42.94, 40.59, 40.6, 43.51, 42.51, 44.7, 47.77, 46.53, 44.78, 45.66, 45.29, 47.54, 45.72, 47.71, 47.55, 49.13, 47.53, 43.52, 41.69, 40.58, 41.74, 46.68, 47.53, 49.99, 46.64, 49.91, 49.01, 51.21, 47.53, 45.66, 47.51, 48.7, 50.76, 53.95, 48.88, 50.92, 51.45, 51.69, 49.34, 52.86, 55.07, 51.08, 50.1, 49.78, 49.18, 49.55, 49.15, 45.34, 47.77, 45.99, 43.61, 44.84, 48.01, 46.33, 43.42, 46.93, 46.35, 47.6, 45.09, 45.85, 43.72, 41.2, 42.89, 42.83, 43.48, 46.41, 49.01, 48.49, 48.68, 51.78]),
229
206
  },
230
207
  {
231
208
  name: 'Nokia Corp.',
@@ -233,7 +210,8 @@ const marketIndices = [
233
210
  price: '$8.82',
234
211
  change: '+6.65%',
235
212
  positive: true,
236
- spark: genSpark(7823, 30, 41, 2.4),
213
+ // prettier-ignore
214
+ spark: toSparkSeries(7823, [27.46, 26.98, 25.8, 25.95, 22.92, 23.25, 27.1, 28.27, 26.12, 28.96, 28.95, 27.78, 29.41, 30.28, 30.85, 32.49, 31.78, 32.11, 34.47, 36.23, 36.59, 37.96, 35.38, 36.79, 37.14, 37.87, 36.61, 35.2, 39.04, 40.18, 41.09, 41.92, 38.86, 36.21, 33.95, 35.27, 35.61, 35.38, 37.29, 37.02, 36.76, 34.27, 34.9, 34.79, 33.48, 34.53, 35.99, 38.24, 37.76, 39.23, 38.1, 36.08, 35.04, 36.38, 37.29, 39.2, 41.21, 42.11, 41.35, 38.23, 36.64, 37.97, 39.03, 39.17, 36.98, 35.69, 33.04, 31.86, 38.61, 35.08, 35.33, 36.35, 34.24, 36.73, 32.08, 32.11, 32.87, 31.23, 33.2, 33.36, 32.8, 31.44, 32.34, 29.65, 36.02, 38.33, 39.14, 42.58, 44.72, 43.06, 40.97, 40.47, 41.21, 40.21, 40.21, 40.11]),
237
215
  },
238
216
  {
239
217
  name: 'Tesla, Inc.',
@@ -241,7 +219,8 @@ const marketIndices = [
241
219
  price: '$360.59',
242
220
  change: '-5.42%',
243
221
  positive: false,
244
- spark: genSpark(8945, 90, 76, 4.2),
222
+ // prettier-ignore
223
+ spark: toSparkSeries(8945, [89.21, 88.42, 91.95, 94.9, 97.2, 93.76, 95.02, 93.99, 96.41, 93.71, 92.92, 95.4, 91.94, 97.48, 96.94, 95.45, 92.4, 93.76, 96.33, 97.28, 96.74, 92.99, 92.26, 90.48, 93.76, 92.11, 86.82, 83.2, 87.28, 88.08, 86.09, 81.46, 77.09, 79.91, 85.21, 80.13, 85.23, 80.03, 81.33, 84.31, 84.52, 84.57, 81.19, 81.81, 75.98, 77.99, 77.95, 75.93, 76.87, 81.09, 83.04, 82.12, 78.12, 78.69, 77.63, 79.19, 74.18, 75.44, 77.03, 77.73, 71.89, 76.98, 74.25, 77.95, 77.4, 81.04, 78.54, 77.9, 78.6, 76.53, 73.58, 71.02, 71.9, 70.44, 76.85, 77.69, 76.22, 81.78, 82.78, 85.32, 87.59, 91.37, 88.55, 86.27, 84.09, 86.52, 85.12, 84.64, 86.29, 82.58, 81.71, 86.28, 88.26, 83.47, 80.1, 79.92]),
245
224
  },
246
225
  ];
247
226
 
@@ -264,7 +243,8 @@ const trendingStocks: StockRow[] = [
264
243
  dailyPts: 1.35,
265
244
  dailyPct: 0.72,
266
245
  weekChg: 22.4,
267
- spark: genSpark(11023, 60, 70, 2.6, 48),
246
+ // prettier-ignore
247
+ spark: toSparkSeries(11023, [62.59, 62.87, 66.28, 66.94, 63.05, 60.71, 60.87, 63.01, 65.63, 66.72, 66.14, 65.67, 63.28, 63.24, 64.03, 65.31, 69.18, 67.03, 67.02, 65.16, 63.09, 63.23, 59.79, 57.91, 60.58, 62.7, 59.12, 60.59, 60.07, 64.53, 64.61, 68.12, 66.44, 67.6, 65.99, 65.37, 67.56, 64.35, 65.35, 66.14, 65.38, 65.53, 63.89, 68.35, 70.22, 72.78, 73.31, 77.18]),
268
248
  },
269
249
  {
270
250
  id: '2',
@@ -273,7 +253,8 @@ const trendingStocks: StockRow[] = [
273
253
  dailyPts: 3.2,
274
254
  dailyPct: 0.78,
275
255
  weekChg: 18.6,
276
- spark: genSpark(12591, 55, 65, 2.4, 48),
256
+ // prettier-ignore
257
+ spark: toSparkSeries(12591, [52.61, 54.67, 54.37, 52.39, 53.13, 54.24, 53.77, 56.96, 54.21, 56.36, 52.87, 52.91, 52.91, 53.06, 54.05, 56.77, 55.22, 53.5, 55.24, 55.21, 55.57, 57.52, 58.11, 60.48, 62.67, 64.05, 67.62, 67.64, 70.11, 69.7, 67.71, 66.07, 64.31, 61.5, 60.25, 60.01, 62.04, 63.71, 64.69, 65.02, 63.57, 64.63, 65.9, 61.94, 64.71, 63.25, 60.84, 62.99]),
277
258
  },
278
259
  {
279
260
  id: '3',
@@ -282,7 +263,8 @@ const trendingStocks: StockRow[] = [
282
263
  dailyPts: 1.65,
283
264
  dailyPct: 0.93,
284
265
  weekChg: 45.2,
285
- spark: genSpark(13744, 40, 68, 3.8, 48),
266
+ // prettier-ignore
267
+ spark: toSparkSeries(13744, [42.97, 43.84, 42.55, 43.07, 41.45, 45.69, 47.46, 45.15, 47.75, 43.08, 44.46, 37.8, 37.94, 35.72, 35.14, 32.33, 41.42, 45.22, 46.61, 45, 44.99, 48.42, 56.57, 59.5, 54.67, 55.56, 53.34, 54.15, 56.29, 57.95, 59.47, 59.02, 58.39, 61.6, 59.14, 66.24, 68.92, 69.13, 73.86, 76.15, 73.14, 74.89, 71.4, 71.95, 74.65, 69.61, 63.98, 63.68]),
286
268
  },
287
269
  {
288
270
  id: '4',
@@ -291,7 +273,8 @@ const trendingStocks: StockRow[] = [
291
273
  dailyPts: -0.8,
292
274
  dailyPct: -0.43,
293
275
  weekChg: 15.3,
294
- spark: genSpark(14882, 70, 63, 2.5, 48),
276
+ // prettier-ignore
277
+ spark: toSparkSeries(14882, [65.57, 69.23, 72.54, 70.58, 69.7, 67.79, 66.66, 69.84, 72.05, 73.69, 71.33, 72.56, 68.97, 67.02, 69.25, 68.04, 70.12, 73.38, 71.49, 70.24, 70.59, 69.81, 70.5, 66.45, 63.72, 61.63, 63.06, 60.22, 61.87, 61.91, 61.4, 61.43, 62.03, 63.27, 65.56, 61.76, 62.77, 62.52, 63.4, 65.86, 61.54, 60.71, 61.09, 66, 62.46, 64, 67.5, 65.32]),
295
278
  },
296
279
  {
297
280
  id: '5',
@@ -300,7 +283,8 @@ const trendingStocks: StockRow[] = [
300
283
  dailyPts: 2.1,
301
284
  dailyPct: 1.37,
302
285
  weekChg: 12.8,
303
- spark: genSpark(15967, 50, 60, 2.7, 48),
286
+ // prettier-ignore
287
+ spark: toSparkSeries(15967, [50.19, 52.48, 56.14, 57.13, 53.58, 52.4, 49.76, 51.71, 53.73, 52.83, 47.01, 48.64, 46.49, 44.71, 45.84, 47.51, 51.21, 51.75, 47.07, 53.25, 53.02, 56.04, 53.9, 54.27, 53.09, 56.7, 54.86, 53.61, 54.39, 55.27, 58.44, 59.21, 58.89, 60.97, 63.5, 64.41, 64.28, 65.48, 63.86, 62.88, 65.82, 64.06, 60.89, 57.93, 60.15, 57.94, 57.86, 54.45]),
304
288
  },
305
289
  {
306
290
  id: '6',
@@ -309,7 +293,8 @@ const trendingStocks: StockRow[] = [
309
293
  dailyPts: 4.5,
310
294
  dailyPct: 0.9,
311
295
  weekChg: 35.1,
312
- spark: genSpark(17105, 45, 61, 3.0, 48),
296
+ // prettier-ignore
297
+ spark: toSparkSeries(17105, [46.75, 47.76, 46.13, 45.91, 45.34, 49.3, 50.87, 48.9, 46.85, 49.07, 50.88, 54.37, 54.34, 54.17, 52.62, 52.71, 55.65, 55.18, 57.28, 59.71, 56.07, 57.36, 56.8, 52.95, 49.59, 50.35, 50.11, 48.92, 51.6, 52.4, 50.29, 52.17, 54.87, 55.44, 54.27, 52.27, 53.76, 55.31, 55.75, 56.16, 57.8, 58.73, 54.6, 53.58, 55.27, 55.62, 62.45, 62.94]),
313
298
  },
314
299
  {
315
300
  id: '7',
@@ -318,7 +303,8 @@ const trendingStocks: StockRow[] = [
318
303
  dailyPts: -20.67,
319
304
  dailyPct: -5.42,
320
305
  weekChg: -8.3,
321
- spark: genSpark(18249, 90, 64, 4.4, 48),
306
+ // prettier-ignore
307
+ spark: toSparkSeries(18249, [83.75, 78.44, 81.1, 79.12, 76.61, 84.38, 84.64, 87.7, 86.31, 80.02, 74.82, 75.1, 75.36, 73.06, 74.86, 75.38, 78.07, 78.02, 75.42, 81.25, 85.54, 86.68, 84.46, 86.89, 80.57, 84.1, 82.48, 85.83, 87.06, 79.95, 74.26, 68.41, 64.98, 70.44, 71.17, 74.21, 69.56, 73.59, 70.47, 70.24, 65.12, 64.41, 69.18, 62.39, 63.5, 59.78, 59.96, 60.64]),
322
308
  },
323
309
  {
324
310
  id: '8',
@@ -327,7 +313,8 @@ const trendingStocks: StockRow[] = [
327
313
  dailyPts: 2.35,
328
314
  dailyPct: 4.89,
329
315
  weekChg: -12.5,
330
- spark: genSpark(19388, 65, 57, 2.8, 48),
316
+ // prettier-ignore
317
+ spark: toSparkSeries(19388, [71.45, 75.23, 76.21, 71.96, 69.34, 69.92, 69.21, 65.68, 62.87, 64.07, 64.44, 62.98, 59.15, 61.12, 62.74, 64.13, 62.29, 65.14, 60.86, 58.23, 56.22, 57.6, 60.39, 57.87, 57.04, 54.84, 55.29, 56.38, 56.92, 58.48, 61.09, 59.01, 58.65, 61.44, 61.55, 58.75, 61.48, 60.6, 58.99, 61.4, 56.7, 57.42, 59.98, 57.74, 64.44, 59.11, 59.16, 59.45]),
331
318
  },
332
319
  {
333
320
  id: '9',
@@ -336,7 +323,8 @@ const trendingStocks: StockRow[] = [
336
323
  dailyPts: -1.2,
337
324
  dailyPct: -0.73,
338
325
  weekChg: 28.7,
339
- spark: genSpark(20471, 50, 63, 2.9, 48),
326
+ // prettier-ignore
327
+ spark: toSparkSeries(20471, [44.8, 46.66, 43.7, 46.12, 47.65, 50.65, 48.05, 52.96, 54.41, 54.91, 53.77, 49.18, 50.01, 52.14, 49.46, 49.81, 52.08, 52.56, 50.02, 50.61, 56.27, 56.79, 62.63, 61.99, 65.99, 62.88, 59.6, 57.44, 58.61, 62.78, 62, 59.58, 57.36, 54.92, 59.1, 55.37, 64.03, 63.13, 63.88, 65.3, 65.22, 60.4, 62.84, 60.12, 59.42, 65.11, 58, 57.8]),
340
328
  },
341
329
  {
342
330
  id: '10',
@@ -345,7 +333,8 @@ const trendingStocks: StockRow[] = [
345
333
  dailyPts: 5.4,
346
334
  dailyPct: 0.87,
347
335
  weekChg: 42.1,
348
- spark: genSpark(21556, 42, 58, 2.6, 48),
336
+ // prettier-ignore
337
+ spark: toSparkSeries(21556, [42.07, 41.72, 43.08, 43.95, 42.71, 43, 47.8, 51.53, 49.82, 51.72, 51.75, 54.86, 54.64, 54.28, 55.52, 55.2, 53.51, 49.34, 45.58, 51.95, 44.7, 46.57, 48.92, 47.16, 49.62, 47.2, 47.47, 49.84, 51.12, 53.9, 53.39, 51.34, 51.56, 47.93, 50.31, 50.69, 50.32, 50.04, 53.93, 52.8, 52.46, 55.01, 56.66, 58.11, 59.39, 60.81, 62.81, 64]),
349
338
  },
350
339
  ];
351
340
 
@@ -479,6 +468,17 @@ function MarketCard({
479
468
  const formatSigned = (value: number, digits: number, suffix = '') =>
480
469
  `${value >= 0 ? '+' : ''}${value.toFixed(digits)}${suffix}`;
481
470
 
471
+ const chgColumns: Array<{
472
+ key: 'dailyPts' | 'dailyPct' | 'weekChg';
473
+ header: string;
474
+ width: PixelWidth;
475
+ digits: number;
476
+ suffix: string;
477
+ }> = [
478
+ {key: 'dailyPts', header: 'Chg (pts)', width: pixel(104), digits: 2, suffix: ''},
479
+ {key: 'dailyPct', header: 'Chg (%)', width: pixel(96), digits: 2, suffix: '%'},
480
+ {key: 'weekChg', header: '52W (%)', width: pixel(96), digits: 1, suffix: '%'},
481
+ ];
482
482
  const trendingColumns: TableColumn<StockRow>[] = [
483
483
  {
484
484
  key: 'ticker',
@@ -500,39 +500,17 @@ const trendingColumns: TableColumn<StockRow>[] = [
500
500
  </Text>
501
501
  ),
502
502
  },
503
- {
504
- key: 'dailyPts',
505
- header: 'Chg (pts)',
506
- width: pixel(104),
507
- renderCell: (row: StockRow) => (
508
- <MetricDelta
509
- value={formatSigned(row.dailyPts, 2)}
510
- positive={row.dailyPts >= 0}
511
- />
512
- ),
513
- },
514
- {
515
- key: 'dailyPct',
516
- header: 'Chg (%)',
517
- width: pixel(96),
518
- renderCell: (row: StockRow) => (
519
- <MetricDelta
520
- value={formatSigned(row.dailyPct, 2, '%')}
521
- positive={row.dailyPct >= 0}
522
- />
523
- ),
524
- },
525
- {
526
- key: 'weekChg',
527
- header: '52W (%)',
528
- width: pixel(96),
529
- renderCell: (row: StockRow) => (
530
- <MetricDelta
531
- value={formatSigned(row.weekChg, 1, '%')}
532
- positive={row.weekChg >= 0}
533
- />
534
- ),
535
- },
503
+ ...chgColumns.map(column => ({
504
+ key: column.key,
505
+ header: column.header,
506
+ width: column.width,
507
+ renderCell: (row: StockRow) => {
508
+ const value = row[column.key] as number;
509
+ return (
510
+ <MetricDelta value={formatSigned(value, column.digits, column.suffix)} positive={value >= 0} />
511
+ );
512
+ },
513
+ })),
536
514
  {
537
515
  key: 'spark',
538
516
  header: '24h Trend',
@@ -549,37 +527,6 @@ const trendingColumns: TableColumn<StockRow>[] = [
549
527
  },
550
528
  ];
551
529
 
552
- function MetricCard({
553
- value,
554
- change,
555
- label,
556
- caption,
557
- }: {
558
- value: string;
559
- change: string;
560
- label: string;
561
- caption: string;
562
- }) {
563
- const positive = !change.startsWith('-');
564
- return (
565
- <Card>
566
- <VStack gap={2}>
567
- <Text type="supporting" color="secondary">
568
- {label}
569
- </Text>
570
- <HStack gap={2} vAlign="center">
571
- <Text type="display-3" weight="semibold" hasTabularNumbers>
572
- {value}
573
- </Text>
574
- <MetricDelta value={change} positive={positive} />
575
- </HStack>
576
- <Text type="supporting" color="secondary">
577
- {caption}
578
- </Text>
579
- </VStack>
580
- </Card>
581
- );
582
- }
583
530
 
584
531
  function AssetRow({
585
532
  ticker,
@@ -610,13 +557,9 @@ function AssetRow({
610
557
  );
611
558
  }
612
559
 
613
- // ============= SIDENAV =============
614
-
615
560
  // ============= MAIN COMPONENT =============
616
561
 
617
562
  export default function DashboardPortfolio() {
618
- const [timeRange, setTimeRange] = useState('1 year');
619
-
620
563
  return (
621
564
  <Layout
622
565
  height="fill"
@@ -626,22 +569,9 @@ export default function DashboardPortfolio() {
626
569
  {/* Page header */}
627
570
  <HStack hAlign="between" vAlign="center">
628
571
  <Heading level={1}>The night vault</Heading>
629
- <DropdownMenu
630
- button={{
631
- label: timeRange,
632
- variant: 'secondary',
633
- size: 'lg',
634
- }}
635
- hasChevron
636
- items={[
637
- {label: '1 month', onClick: () => setTimeRange('1 month')},
638
- {label: '3 months', onClick: () => setTimeRange('3 months')},
639
- {label: '6 months', onClick: () => setTimeRange('6 months')},
640
- {label: '1 year', onClick: () => setTimeRange('1 year')},
641
- {label: '5 years', onClick: () => setTimeRange('5 years')},
642
- {label: 'All time', onClick: () => setTimeRange('All time')},
643
- ]}
644
- />
572
+ <Text type="body" color="secondary">
573
+ 1 year
574
+ </Text>
645
575
  </HStack>
646
576
 
647
577
  {/* KPI metric cards */}
@@ -698,19 +628,11 @@ export default function DashboardPortfolio() {
698
628
  </Grid>
699
629
 
700
630
  {/* Trending stocks table */}
701
- <Card>
702
- <VStack gap={4}>
703
- <Heading level={3}>Trending stocks</Heading>
704
- <Table<StockRow>
705
- data={trendingStocks}
706
- columns={trendingColumns}
707
- idKey="id"
708
- textOverflow="truncate"
709
- hasHover
710
- dividers="rows"
711
- />
712
- </VStack>
713
- </Card>
631
+ <TableCard
632
+ title="Trending stocks"
633
+ data={trendingStocks}
634
+ columns={trendingColumns}
635
+ />
714
636
  </VStack>
715
637
  </LayoutContent>
716
638
  }