@sybilion/uilib 1.2.26 → 1.3.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 (137) hide show
  1. package/dist/esm/components/ui/Chart/Chart.js +5 -0
  2. package/dist/esm/components/ui/Chart/components/BaseChartWrapper.js +7 -32
  3. package/dist/esm/components/ui/Chart/components/ChartEmptyState/ChartEmptyState.js +21 -0
  4. package/dist/esm/components/ui/Chart/components/ChartEmptyState/ChartEmptyState.styl.js +7 -0
  5. package/dist/esm/components/ui/Chart/lightweight/LightweightForecastChart.js +460 -0
  6. package/dist/esm/components/ui/Chart/lightweight/LightweightForecastChart.styl.js +7 -0
  7. package/dist/esm/components/ui/Chart/lightweight/chartTime.js +16 -0
  8. package/dist/esm/components/ui/Chart/lightweight/lightweightForecastChart.helpers.js +114 -0
  9. package/dist/esm/components/ui/Chart/lightweight/quantileBandCustomSeries.js +147 -0
  10. package/dist/esm/components/ui/Chart/quantileBandConeChartData.js +131 -0
  11. package/dist/esm/components/ui/Chart/tools/chartPlotGeometry.js +65 -0
  12. package/dist/esm/components/ui/ChartAreaInteractive/ChartAreaInteractive.helpers.js +37 -1
  13. package/dist/esm/components/ui/ChartAreaInteractive/ChartAreaInteractive.js +5 -2
  14. package/dist/esm/components/ui/ChartAreaInteractive/TimeRangeBrushLayer.js +205 -0
  15. package/dist/esm/components/ui/ChartAreaInteractive/TimeRangeBrushLayer.styl.js +7 -0
  16. package/dist/esm/components/ui/ChartAreaInteractive/TimeRangeBrushLayout.helpers.js +37 -0
  17. package/dist/esm/components/ui/ChartAreaInteractive/overlays/IntervalsOverlay/IntervalsOverlay.hooks.js +1 -0
  18. package/dist/esm/components/ui/ChartAreaInteractive/overlays/PinOverlay/PinOverlay.js +7 -60
  19. package/dist/esm/components/ui/ChartAreaInteractive/overlays/PinOverlay/PinOverlay.styl.js +2 -2
  20. package/dist/esm/components/ui/ChartAreaInteractive/overlays/ThresholdsOverlay/ThresholdsOverlay.hooks.js +1 -0
  21. package/dist/esm/components/ui/ChartAreaInteractive/overlays/useChartYRange.js +2 -4
  22. package/dist/esm/components/ui/ChartAreaInteractive/overlays/useQuantileBands.js +4 -102
  23. package/dist/esm/components/ui/TimeRangeControls/TimeRangeControls.js +7 -2
  24. package/dist/esm/components/ui/WorldMap/WorldMap.js +11 -0
  25. package/dist/esm/components/ui/WorldMap/WorldMap.styl.js +7 -0
  26. package/dist/esm/components/widgets/DriverCard/DriverCard.js +89 -0
  27. package/dist/esm/components/widgets/DriverCard/DriverCard.styl.js +7 -0
  28. package/dist/esm/components/widgets/DriverCard/DriverPerformanceChart.js +83 -0
  29. package/dist/esm/components/widgets/DriverCard/DriverPerformanceChart.styl.js +7 -0
  30. package/dist/esm/components/widgets/DriverCard/driverPerformanceChartData.js +50 -0
  31. package/dist/esm/components/widgets/DriverMap/DriverMap.js +2 -2
  32. package/dist/esm/components/widgets/DriverMap/DriverMap.styl.js +2 -2
  33. package/dist/esm/index.js +4 -2
  34. package/dist/esm/types/src/components/ui/Chart/Chart.d.ts +2 -0
  35. package/dist/esm/types/src/components/ui/Chart/components/BaseChartWrapper.d.ts +2 -1
  36. package/dist/esm/types/src/components/ui/Chart/components/ChartEmptyState/ChartEmptyState.d.ts +14 -0
  37. package/dist/esm/types/src/components/ui/Chart/lightweight/LightweightForecastChart.d.ts +26 -0
  38. package/dist/esm/types/src/components/ui/Chart/lightweight/chartTime.d.ts +5 -0
  39. package/dist/esm/types/src/components/ui/Chart/lightweight/lightweightForecastChart.helpers.d.ts +13 -0
  40. package/dist/esm/types/src/components/ui/Chart/lightweight/quantileBandCustomSeries.d.ts +24 -0
  41. package/dist/esm/types/src/components/ui/Chart/quantileBandConeChartData.d.ts +7 -0
  42. package/dist/esm/types/src/components/ui/Chart/tools/chartPlotGeometry.d.ts +30 -0
  43. package/dist/esm/types/src/components/ui/ChartAreaInteractive/ChartAreaInteractive.d.ts +1 -1
  44. package/dist/esm/types/src/components/ui/ChartAreaInteractive/ChartAreaInteractive.helpers.d.ts +11 -2
  45. package/dist/esm/types/src/components/ui/ChartAreaInteractive/ChartAreaInteractive.types.d.ts +2 -2
  46. package/dist/esm/types/src/components/ui/ChartAreaInteractive/TimeRangeBrushLayer.d.ts +15 -0
  47. package/dist/esm/types/src/components/ui/ChartAreaInteractive/TimeRangeBrushLayout.helpers.d.ts +14 -0
  48. package/dist/esm/types/src/components/ui/ChartAreaInteractive/overlays/PinOverlay/PinOverlay.d.ts +1 -1
  49. package/dist/esm/types/src/components/ui/Page/PageColumns/PageColumns.d.ts +1 -1
  50. package/dist/esm/types/src/components/ui/TimeRangeControls/TimeRangeControls.d.ts +5 -7
  51. package/dist/esm/types/src/components/ui/WorldMap/WorldMap.d.ts +4 -0
  52. package/dist/esm/types/src/components/ui/WorldMap/index.d.ts +2 -0
  53. package/dist/esm/types/src/components/widgets/DriverCard/DriverCard.d.ts +9 -0
  54. package/dist/esm/types/src/components/widgets/DriverCard/DriverPerformanceChart.d.ts +5 -0
  55. package/dist/esm/types/src/components/widgets/DriverCard/driverPerformanceChartData.d.ts +7 -0
  56. package/dist/esm/types/src/components/widgets/DriverCard/index.d.ts +1 -0
  57. package/dist/esm/types/src/components/widgets/DriverMap/index.d.ts +0 -2
  58. package/dist/esm/types/src/docs/pages/LightweightChartPage.d.ts +1 -0
  59. package/dist/esm/types/src/docs/pages/PageColumnsPage.d.ts +1 -0
  60. package/dist/esm/types/src/docs/pages/WorldMapPage.d.ts +1 -0
  61. package/dist/esm/types/src/index.d.ts +2 -0
  62. package/package.json +3 -2
  63. package/src/components/ui/Chart/Chart.tsx +9 -0
  64. package/src/components/ui/Chart/components/BaseChartWrapper.tsx +8 -41
  65. package/src/components/ui/Chart/components/ChartEmptyState/ChartEmptyState.styl +60 -0
  66. package/src/components/ui/Chart/components/ChartEmptyState/ChartEmptyState.styl.d.ts +15 -0
  67. package/src/components/ui/Chart/components/ChartEmptyState/ChartEmptyState.tsx +66 -0
  68. package/src/components/ui/Chart/lightweight/LightweightForecastChart.styl +25 -0
  69. package/src/components/ui/Chart/lightweight/LightweightForecastChart.styl.d.ts +11 -0
  70. package/src/components/ui/Chart/lightweight/LightweightForecastChart.tsx +721 -0
  71. package/src/components/ui/Chart/lightweight/chartTime.ts +18 -0
  72. package/src/components/ui/Chart/lightweight/lightweightForecastChart.helpers.ts +141 -0
  73. package/src/components/ui/Chart/lightweight/quantileBandCustomSeries.ts +215 -0
  74. package/src/components/ui/Chart/quantileBandConeChartData.ts +171 -0
  75. package/src/components/ui/Chart/tools/chartPlotGeometry.ts +89 -0
  76. package/src/components/ui/ChartAreaInteractive/ChartAreaInteractive.helpers.ts +44 -2
  77. package/src/components/ui/ChartAreaInteractive/ChartAreaInteractive.tsx +14 -1
  78. package/src/components/ui/ChartAreaInteractive/ChartAreaInteractive.types.ts +2 -3
  79. package/src/components/ui/ChartAreaInteractive/TimeRangeBrushLayer.styl +21 -0
  80. package/src/components/{widgets/DriverMap/LoadingSpinner/LoadingSpinner.styl.d.ts → ui/ChartAreaInteractive/TimeRangeBrushLayer.styl.d.ts} +3 -3
  81. package/src/components/ui/ChartAreaInteractive/TimeRangeBrushLayer.tsx +285 -0
  82. package/src/components/ui/ChartAreaInteractive/TimeRangeBrushLayout.helpers.ts +55 -0
  83. package/src/components/ui/ChartAreaInteractive/overlays/IntervalsOverlay/IntervalsOverlay.hooks.ts +1 -0
  84. package/src/components/ui/ChartAreaInteractive/overlays/PinOverlay/PinOverlay.styl +2 -7
  85. package/src/components/ui/ChartAreaInteractive/overlays/PinOverlay/PinOverlay.styl.d.ts +0 -1
  86. package/src/components/ui/ChartAreaInteractive/overlays/PinOverlay/PinOverlay.tsx +7 -71
  87. package/src/components/ui/ChartAreaInteractive/overlays/ThresholdsOverlay/ThresholdsOverlay.hooks.ts +1 -0
  88. package/src/components/ui/ChartAreaInteractive/overlays/useChartYRange.ts +2 -3
  89. package/src/components/ui/ChartAreaInteractive/overlays/useQuantileBands.ts +5 -131
  90. package/src/components/ui/Page/PageColumns/PageColumns.tsx +1 -1
  91. package/src/components/ui/TimeRangeControls/TimeRangeControls.tsx +16 -17
  92. package/src/components/{widgets/DriverMap/MapBackground/MapBackground.styl → ui/WorldMap/WorldMap.styl} +1 -3
  93. package/src/components/{widgets/DriverMap/MapBackground/MapBackground.styl.d.ts → ui/WorldMap/WorldMap.styl.d.ts} +1 -1
  94. package/src/components/ui/WorldMap/WorldMap.tsx +22 -0
  95. package/src/components/ui/WorldMap/index.ts +2 -0
  96. package/src/components/widgets/DriverCard/DriverCard.styl +169 -0
  97. package/src/components/widgets/DriverCard/DriverCard.styl.d.ts +40 -0
  98. package/src/components/widgets/DriverCard/DriverCard.tsx +219 -0
  99. package/src/components/widgets/DriverCard/DriverPerformanceChart.styl +43 -0
  100. package/src/components/widgets/DriverCard/DriverPerformanceChart.styl.d.ts +13 -0
  101. package/src/components/widgets/DriverCard/DriverPerformanceChart.tsx +150 -0
  102. package/src/components/widgets/DriverCard/driverPerformanceChartData.ts +64 -0
  103. package/src/components/widgets/DriverCard/index.ts +1 -0
  104. package/src/components/widgets/DriverMap/DriverIcon/DriverIcon.tsx +0 -2
  105. package/src/components/widgets/DriverMap/DriverMap.styl +6 -1
  106. package/src/components/widgets/DriverMap/DriverMap.styl.d.ts +1 -0
  107. package/src/components/widgets/DriverMap/DriverMap.tsx +2 -4
  108. package/src/components/widgets/DriverMap/driverCategoryIcon.tsx +0 -2
  109. package/src/components/widgets/DriverMap/index.ts +0 -2
  110. package/src/declarations.d.ts +2 -0
  111. package/src/docs/config/webpack.config.js +26 -3
  112. package/src/docs/index.tsx +1 -1
  113. package/src/docs/pages/ChartAreaInteractivePage.tsx +2 -3
  114. package/src/docs/pages/DriverMapPage.tsx +214 -60
  115. package/src/docs/pages/LightweightChartPage.styl +18 -0
  116. package/src/docs/pages/LightweightChartPage.styl.d.ts +10 -0
  117. package/src/docs/pages/LightweightChartPage.tsx +195 -0
  118. package/src/docs/pages/PageColumnsPage.tsx +92 -0
  119. package/src/docs/pages/TimeRangeControlsPage.tsx +2 -3
  120. package/src/docs/pages/WorldMapPage.styl +14 -0
  121. package/src/docs/pages/WorldMapPage.styl.d.ts +8 -0
  122. package/src/docs/pages/WorldMapPage.tsx +26 -0
  123. package/src/docs/registry.ts +19 -1
  124. package/src/index.ts +2 -0
  125. package/dist/esm/components/widgets/DriverMap/LoadingSpinner/LoadingSpinner.js +0 -8
  126. package/dist/esm/components/widgets/DriverMap/LoadingSpinner/LoadingSpinner.styl.js +0 -7
  127. package/dist/esm/components/widgets/DriverMap/MapBackground/MapBackground.js +0 -10
  128. package/dist/esm/components/widgets/DriverMap/MapBackground/MapBackground.styl.js +0 -7
  129. package/dist/esm/types/src/components/widgets/DriverMap/LoadingSpinner/LoadingSpinner.d.ts +0 -1
  130. package/dist/esm/types/src/components/widgets/DriverMap/MapBackground/MapBackground.d.ts +0 -1
  131. package/src/components/widgets/DriverMap/LoadingSpinner/LoadingSpinner.styl +0 -24
  132. package/src/components/widgets/DriverMap/LoadingSpinner/LoadingSpinner.tsx +0 -11
  133. package/src/components/widgets/DriverMap/MapBackground/MapBackground.tsx +0 -18
  134. /package/dist/esm/components/{widgets/DriverMap/MapBackground → ui/WorldMap}/map.svg.js +0 -0
  135. /package/src/components/{widgets/DriverMap/MapBackground → ui/WorldMap}/map.svg +0 -0
  136. /package/src/components/{widgets/DriverMap/MapBackground → ui/WorldMap}/mapAspect.mixin.styl +0 -0
  137. /package/src/components/{widgets/DriverMap/MapBackground → ui/WorldMap}/mapAspect.mixin.styl.d.ts +0 -0
@@ -1,59 +1,187 @@
1
1
  import { useState } from 'react';
2
2
 
3
- import { PageContentSection } from '#uilib/components/ui/Page';
3
+ import { PageColumns, PageContentSection } from '#uilib/components/ui/Page';
4
4
  import { Switch } from '#uilib/components/ui/Switch';
5
+ import { DriverCard } from '#uilib/components/widgets/DriverCard';
5
6
  import {
6
7
  type DriverData,
7
8
  DriverMap,
9
+ getPreciseCoordinates,
8
10
  } from '#uilib/components/widgets/DriverMap';
11
+ import { CaretLeft, CaretRight } from '@phosphor-icons/react';
9
12
 
10
13
  import { AppPageHeader } from '../components/AppPageHeader/AppPageHeader';
11
14
  import { DocsHeaderActions } from '../docsHeaderActions';
12
15
 
16
+ /** ISO `YYYY-MM` keys, sorted ascending → series for DriverPerformanceChart. */
17
+ function buildMonthlySeries(
18
+ seed: number,
19
+ months: number,
20
+ drift = 0.15,
21
+ ): NonNullable<DriverData['normalized_series']> {
22
+ const out: NonNullable<DriverData['normalized_series']> = {};
23
+ const start = new Date('2019-01-01');
24
+ for (let i = 0; i < months; i++) {
25
+ const d = new Date(start);
26
+ d.setMonth(d.getMonth() + i);
27
+ const key = d.toISOString().slice(0, 7);
28
+ out[key] =
29
+ seed + Math.sin(i * 0.35) * 14 + Math.cos(i * 0.11) * 7 + i * drift;
30
+ }
31
+ return out;
32
+ }
33
+
34
+ function DriverPager({
35
+ drivers,
36
+ selectedDriver,
37
+ onSelect,
38
+ }: {
39
+ drivers: DriverData[];
40
+ selectedDriver: DriverData | null;
41
+ onSelect: (d: DriverData | null) => void;
42
+ }) {
43
+ const index = selectedDriver
44
+ ? drivers.findIndex(d => d.id === selectedDriver.id)
45
+ : -1;
46
+ const total = drivers.length;
47
+ const safeIndex = index >= 0 ? index : 0;
48
+ const displayIndex = index >= 0 ? index + 1 : 0;
49
+
50
+ const go = (nextIdx: number) => {
51
+ if (total === 0) {
52
+ onSelect(null);
53
+ return;
54
+ }
55
+ const wrapped = ((nextIdx % total) + total) % total;
56
+ onSelect(drivers[wrapped] ?? null);
57
+ };
58
+
59
+ return (
60
+ <div
61
+ style={{
62
+ display: 'flex',
63
+ alignItems: 'center',
64
+ gap: 6,
65
+ fontSize: 12,
66
+ color: 'var(--muted-foreground)',
67
+ }}
68
+ >
69
+ <button
70
+ type="button"
71
+ aria-label="Previous driver"
72
+ disabled={total === 0 || !selectedDriver}
73
+ onClick={() => go(safeIndex - 1)}
74
+ style={{
75
+ display: 'inline-flex',
76
+ alignItems: 'center',
77
+ justifyContent: 'center',
78
+ padding: 4,
79
+ borderRadius: 6,
80
+ border: '1px solid var(--border)',
81
+ background: 'var(--background)',
82
+ cursor: total && selectedDriver ? 'pointer' : 'default',
83
+ opacity: total && selectedDriver ? 1 : 0.45,
84
+ }}
85
+ >
86
+ <CaretLeft size={14} />
87
+ </button>
88
+ <span
89
+ aria-live="polite"
90
+ style={{ minWidth: '2.5rem', textAlign: 'center' }}
91
+ >
92
+ {displayIndex}/{total}
93
+ </span>
94
+ <button
95
+ type="button"
96
+ aria-label="Next driver"
97
+ disabled={total === 0 || !selectedDriver}
98
+ onClick={() => go(safeIndex + 1)}
99
+ style={{
100
+ display: 'inline-flex',
101
+ alignItems: 'center',
102
+ justifyContent: 'center',
103
+ padding: 4,
104
+ borderRadius: 6,
105
+ border: '1px solid var(--border)',
106
+ background: 'var(--background)',
107
+ cursor: total && selectedDriver ? 'pointer' : 'default',
108
+ opacity: total && selectedDriver ? 1 : 0.45,
109
+ }}
110
+ >
111
+ <CaretRight size={14} />
112
+ </button>
113
+ </div>
114
+ );
115
+ }
116
+
117
+ // Drivers ordered for pager: long title + USA→World, EU negative + decimal %, Asia positive + large %, global negative.
13
118
  const MOCK_DRIVERS: DriverData[] = [
14
119
  {
15
- id: 'd1',
16
- name: 'EU industrial production',
17
- region: ['Europe', 'Germany'],
120
+ id: 'mock-us-world-long-title',
121
+ name: 'North American manufacturing PMI composite — orders, production, employment, supplier deliveries (SA, quarterly)',
122
+ region: ['North America', 'United States of America'],
18
123
  category: 'Manufacturing',
19
124
  isPublic: true,
20
- importance: 88,
21
- direction: 0.4,
22
- lag: '2 month(s)',
23
- coordinates: { x: 48, y: 34, continent: 'Europe' },
125
+ importance: 84.2,
126
+ direction: 0.55,
127
+ lag: '1 quarter(s)',
128
+ coordinates: getPreciseCoordinates('United States of America'),
129
+ src_region: {
130
+ name: 'United States of America',
131
+ coordinates: { x: 0, y: 0 },
132
+ },
133
+ tgt_region: {
134
+ name: 'World',
135
+ coordinates: { x: 0, y: 0 },
136
+ },
137
+ rawImportance: { overall: { mean: 96 } },
138
+ summary:
139
+ 'U.S. factory activity leads global manufacturing cycles by roughly one quarter: strength here typically coincides with wider world demand, with inventory and orders transmitting across trade links.',
140
+ normalized_series: buildMonthlySeries(48, 42, 0.22),
24
141
  },
25
142
  {
26
- id: 'd2',
27
- name: 'US housing starts',
28
- region: ['North America', 'United States of America'],
29
- category: 'Housing',
143
+ id: 'mock-europe-germany-negative',
144
+ name: 'German industrial orders (domestic + foreign)',
145
+ region: ['Europe', 'Germany'],
146
+ category: 'Industrial',
30
147
  isPublic: true,
31
- importance: 72,
32
- direction: -0.2,
33
- lag: '1 month(s)',
34
- coordinates: { x: 22, y: 36, continent: 'North America' },
148
+ importance: 81.6,
149
+ direction: -0.42,
150
+ lag: '2 month(s)',
151
+ coordinates: getPreciseCoordinates('Germany'),
152
+ summary:
153
+ 'New orders in Germany are a timely read on euro-area capex and export pipelines; pullbacks often precede broader EU production softness before PMI surveys fully reflect the turn.',
154
+ normalized_series: buildMonthlySeries(62, 42, -0.12),
35
155
  },
36
156
  {
37
- id: 'd3',
38
- name: 'Japan export volumes',
157
+ id: 'mock-asia-japan-positive',
158
+ name: 'Japan machinery orders (core private-sector ex. ships)',
39
159
  region: ['Asia', 'Japan'],
40
- category: 'Trade',
160
+ category: 'market',
41
161
  isPublic: true,
42
- importance: 65,
43
- direction: 0.1,
44
- lag: '3 month(s)',
45
- coordinates: { x: 78, y: 38, continent: 'Asia' },
162
+ importance: 77,
163
+ direction: 0.28,
164
+ lag: '~1 month(s)',
165
+ coordinates: getPreciseCoordinates('Japan'),
166
+ rawImportance: { overall: { mean: 99 } },
167
+ summary:
168
+ 'Core machinery orders capture forward-looking domestic investment and Asia supply-chain capex; spikes cluster around fiscal years and robotics/automation cycles.',
169
+ normalized_series: buildMonthlySeries(55, 42, 0.18),
46
170
  },
47
171
  {
48
- id: 'd4',
49
- name: 'Global risk appetite',
172
+ id: 'mock-global-risk-negative',
173
+ name: 'Global equity risk appetite (cross-asset composite)',
50
174
  region: ['World'],
51
- category: 'market',
175
+ category: 'Trade',
52
176
  isPublic: true,
53
- importance: 55,
54
- direction: 0,
177
+ importance: 66.5,
178
+ direction: -0.05,
55
179
  lag: 'Unknown',
56
- coordinates: { x: 50, y: 50, continent: 'Global' },
180
+ coordinates: getPreciseCoordinates('World'),
181
+ rawImportance: { overall: { mean: 91 } },
182
+ summary:
183
+ 'Combines credit spreads, vol surfaces, and cyclical equity factor tilt into a single read on whether markets are paying for growth or for safety; sharp deterioration often coincides with funding stress.',
184
+ normalized_series: buildMonthlySeries(40, 42, 0.08),
57
185
  },
58
186
  ];
59
187
 
@@ -62,52 +190,78 @@ export default function DriverMapPage() {
62
190
  MOCK_DRIVERS[0],
63
191
  );
64
192
  const [isLoading, setIsLoading] = useState(false);
193
+ // DriverCard checks `inQueue` before `isLoading` — toggles queue empty state independent of mocks.
194
+ const [inQueue, setInQueue] = useState(false);
65
195
 
66
196
  return (
67
197
  <>
68
198
  <AppPageHeader
69
- breadcrumbs={[{ label: 'DriverMap' }]}
70
- title="DriverMap"
71
- subheader="World map with driver badges, transitions, and keyboard navigation (arrow keys)."
199
+ breadcrumbs={[{ label: 'Driver map' }]}
200
+ title="DriverMap & DriverCard"
201
+ subheader="World map with driver badges and the driver detail card — map clicks and the selector sync selection; arrow keys navigate the map."
72
202
  actions={<DocsHeaderActions />}
73
203
  />
74
204
  <PageContentSection>
75
205
  <div
76
206
  style={{
77
207
  display: 'flex',
208
+ flexWrap: 'wrap',
78
209
  alignItems: 'center',
79
- gap: 12,
210
+ gap: 16,
80
211
  marginBottom: 16,
81
212
  }}
82
213
  >
83
- <Switch
84
- id="loading-overlay"
85
- checked={isLoading}
86
- onCheckedChange={setIsLoading}
87
- />
88
- <label htmlFor="loading-overlay">Loading overlay</label>
89
- </div>
90
- <div
91
- style={{
92
- width: 720,
93
- maxWidth: '100%',
94
- // @ts-ignore
95
- '--shimmer-color': 'var(--page-color)',
96
- }}
97
- >
98
- <DriverMap
99
- drivers={MOCK_DRIVERS}
100
- isLoading={isLoading}
101
- selectedDriver={selectedDriver}
102
- setSelectedDriver={setSelectedDriver}
103
- />
214
+ <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
215
+ <Switch
216
+ id="loading-overlay"
217
+ checked={isLoading}
218
+ onCheckedChange={setIsLoading}
219
+ />
220
+ <label htmlFor="loading-overlay">Loading state</label>
221
+ </div>
222
+ <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
223
+ <Switch
224
+ id="in-queue"
225
+ checked={inQueue}
226
+ onCheckedChange={setInQueue}
227
+ />
228
+ <label htmlFor="in-queue">In queue (empty state)</label>
229
+ </div>
104
230
  </div>
105
- {selectedDriver ? (
106
- <p style={{ marginTop: 16, fontSize: 14 }}>
107
- Selected: <strong>{selectedDriver.name}</strong> (
108
- {selectedDriver.id})
109
- </p>
110
- ) : null}
231
+ <PageColumns
232
+ fill="left"
233
+ columns={[
234
+ <div
235
+ key="map"
236
+ style={{
237
+ width: '100%',
238
+ maxWidth: '100%',
239
+ // @ts-expect-error CSS custom property on div
240
+ '--shimmer-color': 'var(--page-color)',
241
+ }}
242
+ >
243
+ <DriverMap
244
+ drivers={MOCK_DRIVERS}
245
+ isLoading={isLoading}
246
+ selectedDriver={selectedDriver}
247
+ setSelectedDriver={setSelectedDriver}
248
+ />
249
+ </div>,
250
+ <DriverCard
251
+ key="card"
252
+ selectedDriver={selectedDriver}
253
+ isLoading={isLoading}
254
+ inQueue={inQueue}
255
+ driverSelector={
256
+ <DriverPager
257
+ drivers={MOCK_DRIVERS}
258
+ selectedDriver={selectedDriver}
259
+ onSelect={setSelectedDriver}
260
+ />
261
+ }
262
+ />,
263
+ ]}
264
+ />
111
265
  </PageContentSection>
112
266
  </>
113
267
  );
@@ -0,0 +1,18 @@
1
+ .sectionTitle
2
+ font-size var(--font-size-heading-5, 14px)
3
+ font-weight 600
4
+ margin 0 0 8px
5
+
6
+ .caption
7
+ margin 0 0 10px
8
+ color var(--muted-foreground, #71717a)
9
+ font-size var(--font-size-body-5, 12px)
10
+
11
+ .demo
12
+ width 100%
13
+ max-width 56rem
14
+
15
+ .stack
16
+ display flex
17
+ flex-direction column
18
+ gap 28px
@@ -0,0 +1,10 @@
1
+ // This file is automatically generated.
2
+ // Please do not change this file!
3
+ interface CssExports {
4
+ 'caption': string;
5
+ 'demo': string;
6
+ 'sectionTitle': string;
7
+ 'stack': string;
8
+ }
9
+ export const cssExports: CssExports;
10
+ export default cssExports;
@@ -0,0 +1,195 @@
1
+ import { useMemo } from 'react';
2
+
3
+ import { LightweightForecastChart } from '#uilib/components/ui/Chart';
4
+ import type { QuantileBandConfig } from '#uilib/components/ui/Chart/chartForecastVisualization.types';
5
+ import type { ChartDataPoint } from '#uilib/components/ui/ChartAreaInteractive/ChartAreaInteractive.types';
6
+ import type { ForecastItemData } from '#uilib/components/ui/ChartAreaInteractive/ChartLines';
7
+ import { PageContentSection } from '#uilib/components/ui/Page';
8
+ import { useTheme } from '#uilib/contexts/theme-context';
9
+
10
+ import { AppPageHeader } from '../components/AppPageHeader/AppPageHeader';
11
+ import { DocsHeaderActions } from '../docsHeaderActions';
12
+ import S from './LightweightChartPage.styl';
13
+
14
+ const DEMO_FORECAST_ID = 1;
15
+
16
+ const DEMO_FORECAST_DATES = ['2023-06-01', '2023-07-01', '2023-08-01'] as const;
17
+
18
+ const DEMO_OVERLAY_QUANTILES = {
19
+ '0.1': [12.5, 13.2, 14],
20
+ '0.25': [13, 13.6, 14.5],
21
+ '0.5': [13.5, 14, 15],
22
+ '0.75': [14, 14.5, 15.5],
23
+ '0.9': [14.5, 15, 16.2],
24
+ } as const;
25
+
26
+ const DEMO_BASE: ChartDataPoint[] = [
27
+ { date: '2021-01-01', historical: 6 },
28
+ { date: '2021-02-01', historical: 8 },
29
+ { date: '2021-03-01', historical: 7 },
30
+ { date: '2021-04-01', historical: 10 },
31
+ { date: '2021-05-01', historical: 9 },
32
+ { date: '2021-06-01', historical: 10 },
33
+ { date: '2021-07-01', historical: 11 },
34
+ { date: '2021-08-01', historical: 10 },
35
+ { date: '2021-09-01', historical: 12 },
36
+ { date: '2021-10-01', historical: 11 },
37
+ { date: '2021-11-01', historical: 13 },
38
+ { date: '2021-12-01', historical: 12 },
39
+ { date: '2022-01-01', historical: 8 },
40
+ { date: '2022-02-01', historical: 10 },
41
+ { date: '2022-03-01', historical: 9 },
42
+ { date: '2022-04-01', historical: 12 },
43
+ { date: '2022-05-01', historical: 11 },
44
+ { date: '2022-06-01', historical: 12 },
45
+ { date: '2022-07-01', historical: 13 },
46
+ { date: '2022-08-01', historical: 12 },
47
+ { date: '2022-09-01', historical: 14 },
48
+ { date: '2022-10-01', historical: 13 },
49
+ { date: '2022-11-01', historical: 15 },
50
+ { date: '2022-12-01', historical: 14 },
51
+ { date: '2023-01-01', historical: 10 },
52
+ { date: '2023-02-01', historical: 12 },
53
+ { date: '2023-03-01', historical: 11 },
54
+ { date: '2023-04-01', historical: 14 },
55
+ { date: '2023-05-01', historical: 13 },
56
+ { date: '2023-06-01', [`forecast_${DEMO_FORECAST_ID}`]: 13.5 },
57
+ { date: '2023-07-01', [`forecast_${DEMO_FORECAST_ID}`]: 14 },
58
+ { date: '2023-08-01', [`forecast_${DEMO_FORECAST_ID}`]: 15 },
59
+ ];
60
+
61
+ const DEMO_FORECAST_ITEMS: ForecastItemData[] = [
62
+ { id: DEMO_FORECAST_ID, name: 'My custom forecast' },
63
+ ];
64
+
65
+ function withDemoBands(data: ChartDataPoint[]): ChartDataPoint[] {
66
+ return data.map(row => {
67
+ const idx = (DEMO_FORECAST_DATES as readonly string[]).indexOf(row.date);
68
+ if (idx === -1) return row;
69
+ const lowerWide = DEMO_OVERLAY_QUANTILES['0.1'][idx];
70
+ const upperWide = DEMO_OVERLAY_QUANTILES['0.9'][idx];
71
+ const lowerMid = DEMO_OVERLAY_QUANTILES['0.25'][idx];
72
+ const upperMid = DEMO_OVERLAY_QUANTILES['0.75'][idx];
73
+ return {
74
+ ...row,
75
+ demo_band_wide: [lowerWide, upperWide],
76
+ demo_band_mid: [lowerMid, upperMid],
77
+ };
78
+ });
79
+ }
80
+
81
+ const DEMO_QUANTILE_BANDS: QuantileBandConfig[] = [
82
+ {
83
+ key: 'demo_band_wide',
84
+ quantiles: ['0.1', '0.9'],
85
+ opacity: 0.35,
86
+ name: 'P10–P90',
87
+ type: 'monotone',
88
+ color: 'rgba(100,190,220,0.35)',
89
+ strokeWidth: 0,
90
+ },
91
+ {
92
+ key: 'demo_band_mid',
93
+ quantiles: ['0.25', '0.75'],
94
+ opacity: 0.45,
95
+ name: 'P25–P75',
96
+ type: 'monotone',
97
+ color: 'rgba(65,165,238,0.45)',
98
+ strokeWidth: 0,
99
+ },
100
+ ];
101
+
102
+ export default function LightweightChartPage() {
103
+ const { isDarkMode } = useTheme();
104
+ const historicalOnlyData = useMemo(
105
+ () => DEMO_BASE.filter(r => r.date <= '2023-05-01'),
106
+ [],
107
+ );
108
+ const bandsData = useMemo(() => withDemoBands(DEMO_BASE), []);
109
+
110
+ return (
111
+ <>
112
+ <AppPageHeader
113
+ breadcrumbs={[{ label: 'LightweightForecastChart' }]}
114
+ title="LightweightForecastChart"
115
+ subheader={
116
+ <>
117
+ TradingView{' '}
118
+ <a href="https://www.tradingview.com/lightweight-charts/">
119
+ Lightweight Charts
120
+ </a>{' '}
121
+ with historical + forecast lines, quantile fills, tooltip, legend,
122
+ and shared <code>ensureChartForecastBridge</code> preprocessing.
123
+ </>
124
+ }
125
+ actions={<DocsHeaderActions />}
126
+ />
127
+ <PageContentSection>
128
+ <div className={S.stack}>
129
+ <div>
130
+ <h3 className={S.sectionTitle}>Historical line</h3>
131
+ <p className={S.caption}>Subset of demo months through May 2023.</p>
132
+ <div className={S.demo}>
133
+ <LightweightForecastChart
134
+ chartData={historicalOnlyData}
135
+ forecastData={[]}
136
+ isDarkTheme={isDarkMode}
137
+ height={320}
138
+ showLegend={false}
139
+ formatNumber={v =>
140
+ Number.isFinite(v) ? v.toFixed(2) : String(v)
141
+ }
142
+ />
143
+ </div>
144
+ </div>
145
+
146
+ <div>
147
+ <h3 className={S.sectionTitle}>
148
+ Historical + dashed forecast + bridge helper
149
+ </h3>
150
+ <p className={S.caption}>
151
+ Rows use raw forecast anchors; the chart applies{' '}
152
+ <code>ensureChartForecastBridge</code> internally (same defaults
153
+ as <code>ChartAreaInteractive</code>).
154
+ </p>
155
+ <div className={S.demo}>
156
+ <LightweightForecastChart
157
+ chartData={DEMO_BASE}
158
+ forecastData={DEMO_FORECAST_ITEMS}
159
+ isDarkTheme={isDarkMode}
160
+ height={320}
161
+ forecastLineStyle="dashed"
162
+ formatNumber={v =>
163
+ Number.isFinite(v) ? v.toFixed(2) : String(v)
164
+ }
165
+ />
166
+ </div>
167
+ </div>
168
+
169
+ <div>
170
+ <h3 className={S.sectionTitle}>
171
+ Forecast lines + stacked quantile bands
172
+ </h3>
173
+ <p className={S.caption}>
174
+ Each band key maps to tuples on forecast months; overlays from{' '}
175
+ <code>ChartAreaInteractive</code> are intentionally omitted here.
176
+ </p>
177
+ <div className={S.demo}>
178
+ <LightweightForecastChart
179
+ chartData={bandsData}
180
+ forecastData={DEMO_FORECAST_ITEMS}
181
+ quantileBands={DEMO_QUANTILE_BANDS}
182
+ isDarkTheme={isDarkMode}
183
+ height={360}
184
+ forecastLineStyle="dashed"
185
+ formatNumber={v =>
186
+ Number.isFinite(v) ? v.toFixed(2) : String(v)
187
+ }
188
+ />
189
+ </div>
190
+ </div>
191
+ </div>
192
+ </PageContentSection>
193
+ </>
194
+ );
195
+ }
@@ -0,0 +1,92 @@
1
+ import type { CSSProperties } from 'react';
2
+
3
+ import { PageColumns, PageContentSection } from '#uilib/components/ui/Page';
4
+
5
+ import { AppPageHeader } from '../components/AppPageHeader/AppPageHeader';
6
+ import { DocsHeaderActions } from '../docsHeaderActions';
7
+
8
+ const demoBlockStyle: CSSProperties = {
9
+ margin: 0,
10
+ padding: 'var(--p-4)',
11
+ borderRadius: 'var(--p-3)',
12
+ border: '1px solid var(--border)',
13
+ backgroundColor: 'var(--muted)',
14
+ };
15
+
16
+ function ColumnDemo({ label }: { label: string }) {
17
+ return <p style={demoBlockStyle}>{label}</p>;
18
+ }
19
+
20
+ export default function PageColumnsPage() {
21
+ return (
22
+ <>
23
+ <AppPageHeader
24
+ breadcrumbs={[{ label: 'PageColumns' }]}
25
+ title="PageColumns"
26
+ subheader="Responsive row of columns; fill prop controls which column(s) grow past the default width cap on wide viewports."
27
+ actions={<DocsHeaderActions />}
28
+ />
29
+ <PageContentSection
30
+ style={{ display: 'flex', flexDirection: 'column', gap: '2rem' }}
31
+ >
32
+ <section>
33
+ <h3 style={{ marginBottom: '0.75rem' }}>
34
+ fill=&quot;all&quot; (default)
35
+ </h3>
36
+ <p
37
+ style={{
38
+ marginBottom: '1rem',
39
+ color: 'var(--muted-foreground)',
40
+ }}
41
+ >
42
+ Both columns grow; max-width cap is removed on stretched columns.
43
+ </p>
44
+ <PageColumns
45
+ columns={[
46
+ <ColumnDemo key="a" label="Column A" />,
47
+ <ColumnDemo key="b" label="Column B" />,
48
+ ]}
49
+ />
50
+ </section>
51
+ <section>
52
+ <h3 style={{ marginBottom: '0.75rem' }}>fill=&quot;left&quot;</h3>
53
+ <p
54
+ style={{
55
+ marginBottom: '1rem',
56
+ color: 'var(--muted-foreground)',
57
+ }}
58
+ >
59
+ First column grows; second stays at the default fixed width on wide
60
+ screens.
61
+ </p>
62
+ <PageColumns
63
+ fill="left"
64
+ columns={[
65
+ <ColumnDemo key="a" label="Growing left column" />,
66
+ <ColumnDemo key="b" label="Fixed sidebar" />,
67
+ ]}
68
+ />
69
+ </section>
70
+ <section>
71
+ <h3 style={{ marginBottom: '0.75rem' }}>fill=&quot;right&quot;</h3>
72
+ <p
73
+ style={{
74
+ marginBottom: '1rem',
75
+ color: 'var(--muted-foreground)',
76
+ }}
77
+ >
78
+ Last column grows; first stays at the default fixed width on wide
79
+ screens.
80
+ </p>
81
+ <PageColumns
82
+ fill="right"
83
+ columns={[
84
+ <ColumnDemo key="a" label="Fixed sidebar" />,
85
+ <ColumnDemo key="b" label="Growing right column" />,
86
+ ]}
87
+ />
88
+ </section>
89
+ </PageContentSection>
90
+ </>
91
+ );
92
+ }
@@ -2,13 +2,12 @@ import { useState } from 'react';
2
2
 
3
3
  import { PageContentSection } from '#uilib/components/ui/Page';
4
4
  import { TimeRangeControls } from '#uilib/components/ui/TimeRangeControls';
5
- import type { TimeRange } from '#uilib/components/ui/TimeRangeControls/TimeRangeControls.types';
6
5
 
7
6
  import { AppPageHeader } from '../components/AppPageHeader/AppPageHeader';
8
7
  import { DocsHeaderActions } from '../docsHeaderActions';
9
8
 
10
9
  export default function TimeRangeControlsPage() {
11
- const [range, setRange] = useState<TimeRange>('1y');
10
+ const [range, setRange] = useState<string>('1y');
12
11
 
13
12
  return (
14
13
  <>
@@ -21,7 +20,7 @@ export default function TimeRangeControlsPage() {
21
20
  <PageContentSection>
22
21
  <TimeRangeControls
23
22
  timeRange={range}
24
- onTimeRangeChange={v => setRange(v as TimeRange)}
23
+ onTimeRangeChange={setRange}
25
24
  loading={false}
26
25
  />
27
26
  </PageContentSection>
@@ -0,0 +1,14 @@
1
+ @import '../../components/ui/WorldMap/mapAspect.mixin.styl'
2
+
3
+ .demoFrame
4
+ position relative
5
+ width 100%
6
+ max-width 42rem
7
+ mapAspect()
8
+ border-radius var(--p-4)
9
+ overflow hidden
10
+
11
+ .demoFill
12
+ position absolute
13
+ inset 0
14
+ box-sizing border-box