termcast 1.3.48 → 1.3.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +12 -0
- package/dist/build.js.map +1 -1
- package/dist/cli.js +5 -40
- package/dist/cli.js.map +1 -1
- package/dist/colors.d.ts +7 -7
- package/dist/colors.js +7 -7
- package/dist/compile.d.ts +6 -1
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +45 -26
- package/dist/compile.js.map +1 -1
- package/dist/components/actions.js +1 -1
- package/dist/components/actions.js.map +1 -1
- package/dist/components/bar-chart.d.ts +38 -0
- package/dist/components/bar-chart.d.ts.map +1 -0
- package/dist/components/bar-chart.js +158 -0
- package/dist/components/bar-chart.js.map +1 -0
- package/dist/components/bar-graph.d.ts +41 -0
- package/dist/components/bar-graph.d.ts.map +1 -0
- package/dist/components/bar-graph.js +95 -0
- package/dist/components/bar-graph.js.map +1 -0
- package/dist/components/detail.d.ts.map +1 -1
- package/dist/components/detail.js +5 -7
- package/dist/components/detail.js.map +1 -1
- package/dist/components/footer.d.ts.map +1 -1
- package/dist/components/footer.js +8 -9
- package/dist/components/footer.js.map +1 -1
- package/dist/components/form/date-picker.d.ts.map +1 -1
- package/dist/components/form/date-picker.js +7 -1
- package/dist/components/form/date-picker.js.map +1 -1
- package/dist/components/form/dropdown.d.ts.map +1 -1
- package/dist/components/form/dropdown.js +10 -2
- package/dist/components/form/dropdown.js.map +1 -1
- package/dist/components/form/index.d.ts.map +1 -1
- package/dist/components/form/index.js +4 -5
- package/dist/components/form/index.js.map +1 -1
- package/dist/components/form/use-form-navigation.d.ts.map +1 -1
- package/dist/components/form/use-form-navigation.js +6 -0
- package/dist/components/form/use-form-navigation.js.map +1 -1
- package/dist/components/graph.d.ts +111 -0
- package/dist/components/graph.d.ts.map +1 -0
- package/dist/components/graph.js +392 -0
- package/dist/components/graph.js.map +1 -0
- package/dist/components/icon.js +5 -5
- package/dist/components/icon.js.map +1 -1
- package/dist/components/list.d.ts +53 -5
- package/dist/components/list.d.ts.map +1 -1
- package/dist/components/list.js +125 -71
- package/dist/components/list.js.map +1 -1
- package/dist/components/loading-bar.js +3 -3
- package/dist/components/loading-bar.js.map +1 -1
- package/dist/components/loading-text.d.ts +1 -1
- package/dist/components/loading-text.d.ts.map +1 -1
- package/dist/components/loading-text.js +3 -1
- package/dist/components/loading-text.js.map +1 -1
- package/dist/components/metadata.js +2 -2
- package/dist/components/metadata.js.map +1 -1
- package/dist/components/row.d.ts +10 -0
- package/dist/components/row.d.ts.map +1 -0
- package/dist/components/row.js +12 -0
- package/dist/components/row.js.map +1 -0
- package/dist/components/table.d.ts +57 -0
- package/dist/components/table.d.ts.map +1 -0
- package/dist/components/table.js +365 -0
- package/dist/components/table.js.map +1 -0
- package/dist/descendants.js +13 -13
- package/dist/descendants.js.map +1 -1
- package/dist/examples/bar-graph-weekly.d.ts +2 -0
- package/dist/examples/bar-graph-weekly.d.ts.map +1 -0
- package/dist/examples/bar-graph-weekly.js +95 -0
- package/dist/examples/bar-graph-weekly.js.map +1 -0
- package/dist/examples/components-weird-places.d.ts +2 -0
- package/dist/examples/components-weird-places.d.ts.map +1 -0
- package/dist/examples/components-weird-places.js +46 -0
- package/dist/examples/components-weird-places.js.map +1 -0
- package/dist/examples/graph-bar-chart.d.ts +2 -0
- package/dist/examples/graph-bar-chart.d.ts.map +1 -0
- package/dist/examples/graph-bar-chart.js +270 -0
- package/dist/examples/graph-bar-chart.js.map +1 -0
- package/dist/examples/graph-multi-series.d.ts +2 -0
- package/dist/examples/graph-multi-series.d.ts.map +1 -0
- package/dist/examples/graph-multi-series.js +23 -0
- package/dist/examples/graph-multi-series.js.map +1 -0
- package/dist/examples/graph-polymarket.d.ts +2 -0
- package/dist/examples/graph-polymarket.d.ts.map +1 -0
- package/dist/examples/graph-polymarket.js +109 -0
- package/dist/examples/graph-polymarket.js.map +1 -0
- package/dist/examples/graph-row.d.ts +2 -0
- package/dist/examples/graph-row.d.ts.map +1 -0
- package/dist/examples/graph-row.js +226 -0
- package/dist/examples/graph-row.js.map +1 -0
- package/dist/examples/graph-styles.d.ts +2 -0
- package/dist/examples/graph-styles.d.ts.map +1 -0
- package/dist/examples/graph-styles.js +316 -0
- package/dist/examples/graph-styles.js.map +1 -0
- package/dist/examples/list-accessory-table.d.ts +2 -0
- package/dist/examples/list-accessory-table.d.ts.map +1 -0
- package/dist/examples/list-accessory-table.js +46 -0
- package/dist/examples/list-accessory-table.js.map +1 -0
- package/dist/examples/list-item-accessories.d.ts +2 -0
- package/dist/examples/list-item-accessories.d.ts.map +1 -0
- package/dist/examples/list-item-accessories.js +27 -0
- package/dist/examples/list-item-accessories.js.map +1 -0
- package/dist/examples/list-no-actions.d.ts +2 -0
- package/dist/examples/list-no-actions.d.ts.map +1 -0
- package/dist/examples/list-no-actions.js +7 -0
- package/dist/examples/list-no-actions.js.map +1 -0
- package/dist/examples/simple-detail-table.d.ts +2 -0
- package/dist/examples/simple-detail-table.d.ts.map +1 -0
- package/dist/examples/simple-detail-table.js +45 -0
- package/dist/examples/simple-detail-table.js.map +1 -0
- package/dist/examples/simple-graph.d.ts +2 -0
- package/dist/examples/simple-graph.d.ts.map +1 -0
- package/dist/examples/simple-graph.js +32 -0
- package/dist/examples/simple-graph.js.map +1 -0
- package/dist/examples/simple-table-wrap.d.ts +2 -0
- package/dist/examples/simple-table-wrap.d.ts.map +1 -0
- package/dist/examples/simple-table-wrap.js +37 -0
- package/dist/examples/simple-table-wrap.js.map +1 -0
- package/dist/examples/table-edge-cases.d.ts +2 -0
- package/dist/examples/table-edge-cases.d.ts.map +1 -0
- package/dist/examples/table-edge-cases.js +70 -0
- package/dist/examples/table-edge-cases.js.map +1 -0
- package/dist/examples/table-flex-grow.d.ts +2 -0
- package/dist/examples/table-flex-grow.d.ts.map +1 -0
- package/dist/examples/table-flex-grow.js +18 -0
- package/dist/examples/table-flex-grow.js.map +1 -0
- package/dist/extensions/dev.d.ts.map +1 -1
- package/dist/extensions/dev.js +5 -1
- package/dist/extensions/dev.js.map +1 -1
- package/dist/globals.d.ts +1 -0
- package/dist/globals.d.ts.map +1 -1
- package/dist/globals.js +2 -0
- package/dist/globals.js.map +1 -1
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/date-picker-widget.d.ts.map +1 -1
- package/dist/internal/date-picker-widget.js +4 -0
- package/dist/internal/date-picker-widget.js.map +1 -1
- package/dist/internal/providers.d.ts.map +1 -1
- package/dist/internal/providers.js +1 -3
- package/dist/internal/providers.js.map +1 -1
- package/dist/markdown-utils.d.ts +22 -1
- package/dist/markdown-utils.d.ts.map +1 -1
- package/dist/markdown-utils.js +66 -1
- package/dist/markdown-utils.js.map +1 -1
- package/dist/opentui.d.ts +4 -0
- package/dist/opentui.d.ts.map +1 -0
- package/dist/opentui.js +3 -0
- package/dist/opentui.js.map +1 -0
- package/dist/release.d.ts +2 -1
- package/dist/release.d.ts.map +1 -1
- package/dist/release.js +2 -1
- package/dist/release.js.map +1 -1
- package/dist/state.d.ts +1 -0
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +1 -1
- package/dist/state.js.map +1 -1
- package/dist/theme.d.ts +1 -0
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +13 -0
- package/dist/theme.js.map +1 -1
- package/dist/themes/nerv.json +227 -0
- package/dist/themes/termcast.json +72 -71
- package/dist/themes.d.ts +2 -1
- package/dist/themes.d.ts.map +1 -1
- package/dist/themes.js +7 -5
- package/dist/themes.js.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +3 -0
- package/dist/utils.js.map +1 -1
- package/package.json +13 -5
- package/src/build.tsx +13 -0
- package/src/cli.tsx +5 -49
- package/src/colors.tsx +7 -7
- package/src/compile.tsx +52 -29
- package/src/components/actions.tsx +1 -1
- package/src/components/bar-chart.tsx +271 -0
- package/src/components/bar-graph.tsx +214 -0
- package/src/components/detail.tsx +7 -8
- package/src/components/footer.tsx +14 -15
- package/src/components/form/date-picker.tsx +9 -0
- package/src/components/form/dropdown.tsx +13 -3
- package/src/components/form/index.tsx +4 -6
- package/src/components/form/use-form-navigation.tsx +6 -0
- package/src/components/graph.tsx +506 -0
- package/src/components/icon.tsx +5 -5
- package/src/components/list.tsx +210 -102
- package/src/components/loading-bar.tsx +3 -3
- package/src/components/loading-text.tsx +4 -2
- package/src/components/metadata.tsx +2 -2
- package/src/components/row.tsx +31 -0
- package/src/components/table.tsx +511 -0
- package/src/descendants.tsx +13 -13
- package/src/examples/action-shortcut.vitest.tsx +1 -1
- package/src/examples/actions-context.vitest.tsx +1 -1
- package/src/examples/bar-graph-weekly.tsx +264 -0
- package/src/examples/bar-graph-weekly.vitest.tsx +275 -0
- package/src/examples/detail-metadata-showcase.vitest.tsx +8 -8
- package/src/examples/form-basic.vitest.tsx +239 -0
- package/src/examples/form-dropdown.vitest.tsx +29 -29
- package/src/examples/form-tagpicker.vitest.tsx +27 -27
- package/src/examples/github.vitest.tsx +4 -4
- package/src/examples/graph-bar-chart.tsx +408 -0
- package/src/examples/graph-bar-chart.vitest.tsx +283 -0
- package/src/examples/graph-multi-series.tsx +36 -0
- package/src/examples/graph-multi-series.vitest.tsx +89 -0
- package/src/examples/graph-polymarket.tsx +182 -0
- package/src/examples/graph-polymarket.vitest.tsx +130 -0
- package/src/examples/graph-row.tsx +347 -0
- package/src/examples/graph-row.vitest.tsx +295 -0
- package/src/examples/graph-styles.tsx +457 -0
- package/src/examples/graph-styles.vitest.tsx +322 -0
- package/src/examples/list-accessory-table.tsx +77 -0
- package/src/examples/list-detail-metadata.vitest.tsx +21 -21
- package/src/examples/list-dropdown-default.vitest.tsx +12 -12
- package/src/examples/list-item-accessories.tsx +106 -0
- package/src/examples/list-item-accessories.vitest.tsx +115 -0
- package/src/examples/list-no-actions.tsx +18 -0
- package/src/examples/list-no-actions.vitest.tsx +97 -0
- package/src/examples/list-spacing-mode.vitest.tsx +6 -6
- package/src/examples/list-with-detail.vitest.tsx +92 -92
- package/src/examples/list-with-dropdown.vitest.tsx +49 -6
- package/src/examples/list-with-sections.vitest.tsx +61 -56
- package/src/examples/simple-detail-markdown.vitest.tsx +21 -17
- package/src/examples/simple-detail-table.tsx +65 -0
- package/src/examples/simple-detail-table.vitest.tsx +200 -0
- package/src/examples/simple-graph.tsx +51 -0
- package/src/examples/simple-graph.vitest.tsx +124 -0
- package/src/examples/simple-grid.vitest.tsx +3 -3
- package/src/examples/simple-list-search.vitest.tsx +65 -0
- package/src/examples/simple-navigation.vitest.tsx +3 -3
- package/src/examples/simple-table-wrap.tsx +55 -0
- package/src/examples/simple-table-wrap.vitest.tsx +91 -0
- package/src/examples/store.vitest.tsx +1 -1
- package/src/examples/table-edge-cases.tsx +72 -0
- package/src/examples/table-edge-cases.vitest.tsx +307 -0
- package/src/examples/table-flex-grow.tsx +53 -0
- package/src/examples/table-flex-grow.vitest.tsx +124 -0
- package/src/extensions/dev.tsx +7 -1
- package/src/globals.ts +3 -0
- package/src/index.tsx +31 -0
- package/src/internal/date-picker-widget.tsx +4 -0
- package/src/internal/providers.tsx +1 -4
- package/src/markdown-utils.tsx +82 -1
- package/src/opentui.tsx +5 -0
- package/src/release.tsx +3 -0
- package/src/state.tsx +2 -1
- package/src/theme.tsx +14 -0
- package/src/themes/nerv.json +231 -0
- package/src/themes/termcast.json +75 -71
- package/src/themes.ts +8 -5
- package/src/utils.tsx +4 -0
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
// Example: BarChart component showcase with horizontal stacked bars.
|
|
2
|
+
// Demonstrates budget breakdowns, disk usage, portfolios, and stress tests
|
|
3
|
+
// with many segments. Shows BarChart in list detail metadata and full Detail view.
|
|
4
|
+
|
|
5
|
+
import React from 'react'
|
|
6
|
+
import { List, Detail, BarChart, Graph, Color, Action, ActionPanel } from 'termcast'
|
|
7
|
+
import { useNavigation } from 'termcast/src/internal/navigation'
|
|
8
|
+
import { renderWithProviders } from '../utils'
|
|
9
|
+
|
|
10
|
+
// ── Item definitions ─────────────────────────────────────────────────
|
|
11
|
+
|
|
12
|
+
interface BarItem {
|
|
13
|
+
title: string
|
|
14
|
+
subtitle: string
|
|
15
|
+
segments: Array<{ value: number; label?: string; color?: Color.ColorLike }>
|
|
16
|
+
markdown?: string
|
|
17
|
+
meta: Array<{ title: string; text: string; color?: Color.ColorLike }>
|
|
18
|
+
/** Optional line chart data shown alongside the bar */
|
|
19
|
+
lineData?: { data: number[]; xLabels: string[] }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const items: BarItem[] = [
|
|
23
|
+
// ── Budget breakdown (3 segments) ──────────────────────────
|
|
24
|
+
{
|
|
25
|
+
title: 'Monthly Budget',
|
|
26
|
+
subtitle: 'Spent / Remaining / Savings',
|
|
27
|
+
segments: [
|
|
28
|
+
{ value: 4850, label: 'Spent' },
|
|
29
|
+
{ value: 707, label: 'Remaining' },
|
|
30
|
+
{ value: 617, label: 'Savings' },
|
|
31
|
+
],
|
|
32
|
+
markdown: [
|
|
33
|
+
'## Monthly Budget',
|
|
34
|
+
'',
|
|
35
|
+
'Budget allocation for the current month.',
|
|
36
|
+
'',
|
|
37
|
+
'- **Spent**: $4,850 (78.6%)',
|
|
38
|
+
'- **Remaining**: $707 (11.5%)',
|
|
39
|
+
'- **Savings**: $617 (10.0%)',
|
|
40
|
+
].join('\n'),
|
|
41
|
+
meta: [
|
|
42
|
+
{ title: 'Total', text: '$6,174' },
|
|
43
|
+
{ title: 'Spent', text: '78.6%', color: Color.Red },
|
|
44
|
+
{ title: 'Saved', text: '10.0%', color: Color.Green },
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
// ── Disk usage (4 segments) ────────────────────────────────
|
|
49
|
+
{
|
|
50
|
+
title: 'Disk Usage',
|
|
51
|
+
subtitle: 'System / Apps / Media / Free',
|
|
52
|
+
segments: [
|
|
53
|
+
{ value: 120, label: 'System' },
|
|
54
|
+
{ value: 85, label: 'Apps' },
|
|
55
|
+
{ value: 210, label: 'Media' },
|
|
56
|
+
{ value: 85, label: 'Free' },
|
|
57
|
+
],
|
|
58
|
+
meta: [
|
|
59
|
+
{ title: 'Total', text: '500 GB' },
|
|
60
|
+
{ title: 'Used', text: '415 GB (83%)', color: Color.Orange },
|
|
61
|
+
{ title: 'Free', text: '85 GB (17%)', color: Color.Green },
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
// ── Portfolio (5 segments) ─────────────────────────────────
|
|
66
|
+
{
|
|
67
|
+
title: 'Investment Portfolio',
|
|
68
|
+
subtitle: 'Stocks / Bonds / Cash / Crypto / Real Estate',
|
|
69
|
+
segments: [
|
|
70
|
+
{ value: 45000, label: 'Stocks' },
|
|
71
|
+
{ value: 20000, label: 'Bonds' },
|
|
72
|
+
{ value: 10000, label: 'Cash' },
|
|
73
|
+
{ value: 8000, label: 'Crypto' },
|
|
74
|
+
{ value: 17000, label: 'Real Estate' },
|
|
75
|
+
],
|
|
76
|
+
markdown: [
|
|
77
|
+
'## Portfolio Allocation',
|
|
78
|
+
'',
|
|
79
|
+
'Diversified across 5 asset classes.',
|
|
80
|
+
'',
|
|
81
|
+
'| Asset | Value | % |',
|
|
82
|
+
'|-------|-------|---|',
|
|
83
|
+
'| Stocks | $45k | 45% |',
|
|
84
|
+
'| Bonds | $20k | 20% |',
|
|
85
|
+
'| Real Estate | $17k | 17% |',
|
|
86
|
+
'| Cash | $10k | 10% |',
|
|
87
|
+
'| Crypto | $8k | 8% |',
|
|
88
|
+
].join('\n'),
|
|
89
|
+
meta: [
|
|
90
|
+
{ title: 'Total', text: '$100,000' },
|
|
91
|
+
{ title: 'Risk', text: 'Moderate', color: Color.Yellow },
|
|
92
|
+
],
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
// ── CPU breakdown (4 segments) ─────────────────────────────
|
|
96
|
+
{
|
|
97
|
+
title: 'CPU Time',
|
|
98
|
+
subtitle: 'User / System / IO Wait / Idle',
|
|
99
|
+
segments: [
|
|
100
|
+
{ value: 42, label: 'User' },
|
|
101
|
+
{ value: 18, label: 'System' },
|
|
102
|
+
{ value: 5, label: 'IO Wait' },
|
|
103
|
+
{ value: 35, label: 'Idle' },
|
|
104
|
+
],
|
|
105
|
+
lineData: {
|
|
106
|
+
data: [25, 30, 45, 60, 55, 72, 80, 65, 50, 40, 35, 55, 70, 85, 90, 75, 60, 45, 38, 30],
|
|
107
|
+
xLabels: ['0h', '6h', '12h', '18h', '24h'],
|
|
108
|
+
},
|
|
109
|
+
meta: [
|
|
110
|
+
{ title: 'User', text: '42%', color: Color.Blue },
|
|
111
|
+
{ title: 'System', text: '18%', color: Color.Orange },
|
|
112
|
+
{ title: 'Idle', text: '35%', color: Color.Green },
|
|
113
|
+
],
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
// ── Revenue split (6 segments) ─────────────────────────────
|
|
117
|
+
{
|
|
118
|
+
title: 'Revenue by Product',
|
|
119
|
+
subtitle: '6 product lines',
|
|
120
|
+
segments: [
|
|
121
|
+
{ value: 380, label: 'Enterprise' },
|
|
122
|
+
{ value: 240, label: 'Pro' },
|
|
123
|
+
{ value: 150, label: 'Starter' },
|
|
124
|
+
{ value: 90, label: 'API' },
|
|
125
|
+
{ value: 60, label: 'Add-ons' },
|
|
126
|
+
{ value: 30, label: 'Support' },
|
|
127
|
+
],
|
|
128
|
+
meta: [
|
|
129
|
+
{ title: 'Total ARR', text: '$950K' },
|
|
130
|
+
{ title: 'Top product', text: 'Enterprise (40%)', color: Color.Green },
|
|
131
|
+
],
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
// ── Two even (50/50) ───────────────────────────────────────
|
|
135
|
+
{
|
|
136
|
+
title: 'A/B Test Split',
|
|
137
|
+
subtitle: 'Control vs Variant (50/50)',
|
|
138
|
+
segments: [
|
|
139
|
+
{ value: 50, label: 'Control' },
|
|
140
|
+
{ value: 50, label: 'Variant' },
|
|
141
|
+
],
|
|
142
|
+
meta: [
|
|
143
|
+
{ title: 'Control', text: '50%' },
|
|
144
|
+
{ title: 'Variant', text: '50%' },
|
|
145
|
+
{ title: 'p-value', text: '0.043', color: Color.Green },
|
|
146
|
+
],
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
// ── Single segment (100%) ──────────────────────────────────
|
|
150
|
+
{
|
|
151
|
+
title: 'Storage Full',
|
|
152
|
+
subtitle: '100% used',
|
|
153
|
+
segments: [
|
|
154
|
+
{ value: 100, label: 'Used' },
|
|
155
|
+
],
|
|
156
|
+
meta: [
|
|
157
|
+
{ title: 'Status', text: 'FULL', color: Color.Red },
|
|
158
|
+
{ title: 'Action', text: 'Delete files!' },
|
|
159
|
+
],
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
// ── Dominant + tiny segments ───────────────────────────────
|
|
163
|
+
{
|
|
164
|
+
title: 'Market Share',
|
|
165
|
+
subtitle: 'One dominant + many tiny players',
|
|
166
|
+
segments: [
|
|
167
|
+
{ value: 85, label: 'Leader' },
|
|
168
|
+
{ value: 5, label: 'Runner-up' },
|
|
169
|
+
{ value: 3, label: 'Third' },
|
|
170
|
+
{ value: 2, label: 'Fourth' },
|
|
171
|
+
{ value: 1.5, label: 'Fifth' },
|
|
172
|
+
{ value: 1, label: 'Sixth' },
|
|
173
|
+
{ value: 0.8 },
|
|
174
|
+
{ value: 0.5 },
|
|
175
|
+
{ value: 0.4 },
|
|
176
|
+
{ value: 0.3 },
|
|
177
|
+
{ value: 0.2 },
|
|
178
|
+
{ value: 0.1 },
|
|
179
|
+
{ value: 0.1 },
|
|
180
|
+
{ value: 0.1 },
|
|
181
|
+
],
|
|
182
|
+
markdown: [
|
|
183
|
+
'## Market Concentration',
|
|
184
|
+
'',
|
|
185
|
+
'Highly concentrated market with one dominant player at **85%**.',
|
|
186
|
+
'Many tiny segments should be hidden or collapsed.',
|
|
187
|
+
'',
|
|
188
|
+
'Tests behavior with 14 segments where most are < 1%.',
|
|
189
|
+
].join('\n'),
|
|
190
|
+
meta: [
|
|
191
|
+
{ title: 'HHI', text: '7,264 (monopoly)', color: Color.Red },
|
|
192
|
+
{ title: 'Segments', text: '14 total' },
|
|
193
|
+
{ title: 'Visible', text: 'Tiny ones hidden' },
|
|
194
|
+
],
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
// ── Many equal segments ────────────────────────────────────
|
|
198
|
+
{
|
|
199
|
+
title: 'Equal Distribution',
|
|
200
|
+
subtitle: '10 equal segments',
|
|
201
|
+
segments: Array.from({ length: 10 }, (_, i) => ({
|
|
202
|
+
value: 10,
|
|
203
|
+
label: `Slice ${i + 1}`,
|
|
204
|
+
})),
|
|
205
|
+
meta: [
|
|
206
|
+
{ title: 'Segments', text: '10' },
|
|
207
|
+
{ title: 'Each', text: '10%' },
|
|
208
|
+
],
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
// ── Custom colors ──────────────────────────────────────────
|
|
212
|
+
{
|
|
213
|
+
title: 'Custom Colors',
|
|
214
|
+
subtitle: 'Explicit color per segment',
|
|
215
|
+
segments: [
|
|
216
|
+
{ value: 40, label: 'Red', color: Color.Red },
|
|
217
|
+
{ value: 30, label: 'Green', color: Color.Green },
|
|
218
|
+
{ value: 20, label: 'Blue', color: Color.Blue },
|
|
219
|
+
{ value: 10, label: 'Yellow', color: Color.Yellow },
|
|
220
|
+
],
|
|
221
|
+
meta: [
|
|
222
|
+
{ title: 'Mode', text: 'Manual colors' },
|
|
223
|
+
{ title: 'Override', text: 'Per-segment color prop' },
|
|
224
|
+
],
|
|
225
|
+
},
|
|
226
|
+
|
|
227
|
+
// ── Needs/Wants budget ─────────────────────────────────────
|
|
228
|
+
{
|
|
229
|
+
title: 'Needs vs Wants',
|
|
230
|
+
subtitle: '50/30/20 budget rule',
|
|
231
|
+
segments: [
|
|
232
|
+
{ value: 2826, label: 'Needs' },
|
|
233
|
+
{ value: 2023, label: 'Wants' },
|
|
234
|
+
{ value: 617, label: 'Savings' },
|
|
235
|
+
],
|
|
236
|
+
lineData: {
|
|
237
|
+
data: [150, 155, 148, 162, 158, 165, 170, 168, 175, 180, 178, 185, 190, 188, 195, 192, 198, 202, 200, 208],
|
|
238
|
+
xLabels: ['W1', 'W2', 'W3', 'W4'],
|
|
239
|
+
},
|
|
240
|
+
meta: [
|
|
241
|
+
{ title: 'Needs', text: '51.7%', color: Color.Orange },
|
|
242
|
+
{ title: 'Wants', text: '37.0%', color: Color.Blue },
|
|
243
|
+
{ title: 'Savings', text: '11.3%', color: Color.Green },
|
|
244
|
+
],
|
|
245
|
+
},
|
|
246
|
+
|
|
247
|
+
// ── Stress test: 20 segments ───────────────────────────────
|
|
248
|
+
{
|
|
249
|
+
title: 'Stress Test (20 items)',
|
|
250
|
+
subtitle: 'Many small equal segments',
|
|
251
|
+
segments: Array.from({ length: 20 }, (_, i) => ({
|
|
252
|
+
value: 5,
|
|
253
|
+
label: `S${i + 1}`,
|
|
254
|
+
})),
|
|
255
|
+
markdown: [
|
|
256
|
+
'## Stress Test',
|
|
257
|
+
'',
|
|
258
|
+
'20 equal segments at 5% each.',
|
|
259
|
+
'Tests color cycling (7 colors, wraps around).',
|
|
260
|
+
'Labels may be hidden when segments are narrow.',
|
|
261
|
+
].join('\n'),
|
|
262
|
+
meta: [
|
|
263
|
+
{ title: 'Segments', text: '20' },
|
|
264
|
+
{ title: 'Colors', text: '7 (cycling)' },
|
|
265
|
+
],
|
|
266
|
+
},
|
|
267
|
+
]
|
|
268
|
+
|
|
269
|
+
// ── Full Detail view (pushed on Enter) ───────────────────────────────
|
|
270
|
+
|
|
271
|
+
function BarChartDetailView({ item }: { item: BarItem }): any {
|
|
272
|
+
const { pop } = useNavigation()
|
|
273
|
+
|
|
274
|
+
const markdown = [
|
|
275
|
+
`# ${item.title}`,
|
|
276
|
+
'',
|
|
277
|
+
item.subtitle,
|
|
278
|
+
'',
|
|
279
|
+
...(item.markdown ? [item.markdown, ''] : []),
|
|
280
|
+
`**Segments:** ${item.segments.length} `,
|
|
281
|
+
`**Total:** ${item.segments.reduce((s, seg) => s + seg.value, 0).toLocaleString()}`,
|
|
282
|
+
].join('\n')
|
|
283
|
+
|
|
284
|
+
return (
|
|
285
|
+
<Detail
|
|
286
|
+
navigationTitle={item.title}
|
|
287
|
+
markdown={markdown}
|
|
288
|
+
metadata={
|
|
289
|
+
<Detail.Metadata>
|
|
290
|
+
<BarChart height={1}>
|
|
291
|
+
{item.segments.map((seg, i) => {
|
|
292
|
+
return (
|
|
293
|
+
<BarChart.Segment
|
|
294
|
+
key={i}
|
|
295
|
+
value={seg.value}
|
|
296
|
+
label={seg.label}
|
|
297
|
+
color={seg.color}
|
|
298
|
+
/>
|
|
299
|
+
)
|
|
300
|
+
})}
|
|
301
|
+
</BarChart>
|
|
302
|
+
{Boolean(item.lineData) && (
|
|
303
|
+
<>
|
|
304
|
+
<Detail.Metadata.Separator />
|
|
305
|
+
<Graph
|
|
306
|
+
height={10}
|
|
307
|
+
xLabels={item.lineData!.xLabels}
|
|
308
|
+
variant="area"
|
|
309
|
+
yFormat={(v) => v.toFixed(0)}
|
|
310
|
+
>
|
|
311
|
+
<Graph.Line data={item.lineData!.data} color={Color.Orange} />
|
|
312
|
+
</Graph>
|
|
313
|
+
</>
|
|
314
|
+
)}
|
|
315
|
+
<Detail.Metadata.Separator />
|
|
316
|
+
{item.meta.map((m) => {
|
|
317
|
+
return (
|
|
318
|
+
<Detail.Metadata.Label
|
|
319
|
+
key={m.title}
|
|
320
|
+
title={m.title}
|
|
321
|
+
text={m.color ? { value: m.text, color: m.color } : m.text}
|
|
322
|
+
/>
|
|
323
|
+
)
|
|
324
|
+
})}
|
|
325
|
+
</Detail.Metadata>
|
|
326
|
+
}
|
|
327
|
+
actions={
|
|
328
|
+
<ActionPanel>
|
|
329
|
+
<Action title="Go Back" onAction={() => { pop() }} />
|
|
330
|
+
</ActionPanel>
|
|
331
|
+
}
|
|
332
|
+
/>
|
|
333
|
+
)
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// ── Main list ────────────────────────────────────────────────────────
|
|
337
|
+
|
|
338
|
+
function GraphBarChartExample() {
|
|
339
|
+
const { push } = useNavigation()
|
|
340
|
+
|
|
341
|
+
return (
|
|
342
|
+
<List navigationTitle="BarChart Showcase" isShowingDetail={true}>
|
|
343
|
+
{items.map((item) => {
|
|
344
|
+
return (
|
|
345
|
+
<List.Item
|
|
346
|
+
key={item.title}
|
|
347
|
+
title={item.title}
|
|
348
|
+
subtitle={item.subtitle}
|
|
349
|
+
detail={
|
|
350
|
+
<List.Item.Detail
|
|
351
|
+
metadata={
|
|
352
|
+
<List.Item.Detail.Metadata>
|
|
353
|
+
<BarChart height={1}>
|
|
354
|
+
{item.segments.map((seg, i) => {
|
|
355
|
+
return (
|
|
356
|
+
<BarChart.Segment
|
|
357
|
+
key={i}
|
|
358
|
+
value={seg.value}
|
|
359
|
+
label={seg.label}
|
|
360
|
+
color={seg.color}
|
|
361
|
+
/>
|
|
362
|
+
)
|
|
363
|
+
})}
|
|
364
|
+
</BarChart>
|
|
365
|
+
<List.Item.Detail.Metadata.Separator />
|
|
366
|
+
{item.meta.map((m) => {
|
|
367
|
+
return (
|
|
368
|
+
<List.Item.Detail.Metadata.Label
|
|
369
|
+
key={m.title}
|
|
370
|
+
title={m.title}
|
|
371
|
+
text={m.color ? { value: m.text, color: m.color } : m.text}
|
|
372
|
+
/>
|
|
373
|
+
)
|
|
374
|
+
})}
|
|
375
|
+
{Boolean(item.lineData) && (
|
|
376
|
+
<>
|
|
377
|
+
<List.Item.Detail.Metadata.Separator />
|
|
378
|
+
<Graph
|
|
379
|
+
height={6}
|
|
380
|
+
xLabels={item.lineData!.xLabels}
|
|
381
|
+
variant="area"
|
|
382
|
+
yTicks={3}
|
|
383
|
+
yFormat={(v) => v.toFixed(0)}
|
|
384
|
+
>
|
|
385
|
+
<Graph.Line data={item.lineData!.data} color={Color.Orange} />
|
|
386
|
+
</Graph>
|
|
387
|
+
</>
|
|
388
|
+
)}
|
|
389
|
+
</List.Item.Detail.Metadata>
|
|
390
|
+
}
|
|
391
|
+
/>
|
|
392
|
+
}
|
|
393
|
+
actions={
|
|
394
|
+
<ActionPanel>
|
|
395
|
+
<Action
|
|
396
|
+
title="Open Detail"
|
|
397
|
+
onAction={() => { push(<BarChartDetailView item={item} />) }}
|
|
398
|
+
/>
|
|
399
|
+
</ActionPanel>
|
|
400
|
+
}
|
|
401
|
+
/>
|
|
402
|
+
)
|
|
403
|
+
})}
|
|
404
|
+
</List>
|
|
405
|
+
)
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
renderWithProviders(<GraphBarChartExample />)
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { test, expect, afterEach, beforeEach } from 'vitest'
|
|
2
|
+
import { launchTerminal, Session } from 'tuistory/src'
|
|
3
|
+
|
|
4
|
+
let session: Session
|
|
5
|
+
|
|
6
|
+
beforeEach(async () => {
|
|
7
|
+
session = await launchTerminal({
|
|
8
|
+
command: 'bun',
|
|
9
|
+
args: ['src/examples/graph-bar-chart.tsx'],
|
|
10
|
+
cols: 100,
|
|
11
|
+
rows: 30,
|
|
12
|
+
})
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
session?.close()
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
test('initial render shows bar chart for Monthly Budget', async () => {
|
|
20
|
+
const text = await session.text({
|
|
21
|
+
waitFor: (text) => {
|
|
22
|
+
return text.includes('Monthly Budget') && text.includes('Spent')
|
|
23
|
+
},
|
|
24
|
+
timeout: 10000,
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
expect(text).toMatchInlineSnapshot(`
|
|
28
|
+
"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
BarChart Showcase ────────────────────────────────────────────────────────────────────────────
|
|
32
|
+
|
|
33
|
+
> Search...
|
|
34
|
+
|
|
35
|
+
›Monthly Budget Spent / Remaining / Savings │ ┌Spent: 78.6%┐
|
|
36
|
+
Disk Usage System / Apps / Media / Free │
|
|
37
|
+
Investment Portfolio Stocks / Bonds / Cash / C │
|
|
38
|
+
CPU Time User / System / IO Wait / Idle │ ────────────────────────────────────────────
|
|
39
|
+
Revenue by Product 6 product lines │
|
|
40
|
+
A/B Test Split Control vs Variant (50/50) │ Total: $6,174
|
|
41
|
+
Storage Full 100% used │
|
|
42
|
+
Market Share One dominant + many tiny players │ Spent: 78.6%
|
|
43
|
+
Equal Distribution 10 equal segments │
|
|
44
|
+
Custom Colors Explicit color per segment │ Saved: 10.0%
|
|
45
|
+
Needs vs Wants 50/30/20 budget rule │
|
|
46
|
+
Stress Test (20 items) Many small equal segmen │
|
|
47
|
+
│
|
|
48
|
+
│
|
|
49
|
+
↵ open detail ↑↓ navigate ^k actions │
|
|
50
|
+
│
|
|
51
|
+
│
|
|
52
|
+
│
|
|
53
|
+
│
|
|
54
|
+
│
|
|
55
|
+
│
|
|
56
|
+
↵ open detail ↑↓ navigate ^k actions │
|
|
57
|
+
|
|
58
|
+
"
|
|
59
|
+
`)
|
|
60
|
+
expect(text).toContain('Monthly Budget')
|
|
61
|
+
expect(text).toContain('Spent')
|
|
62
|
+
}, 30000)
|
|
63
|
+
|
|
64
|
+
test('navigate to Market Share - dominant + tiny segments', async () => {
|
|
65
|
+
await session.text({
|
|
66
|
+
waitFor: (text) => {
|
|
67
|
+
return text.includes('Monthly Budget')
|
|
68
|
+
},
|
|
69
|
+
timeout: 10000,
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
// Navigate to "Market Share" (8th item, 7 presses down)
|
|
73
|
+
for (let i = 0; i < 7; i++) {
|
|
74
|
+
await session.press('down')
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const text = await session.text({
|
|
78
|
+
waitFor: (text) => {
|
|
79
|
+
return text.includes('›Market Share')
|
|
80
|
+
},
|
|
81
|
+
timeout: 5000,
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
expect(text).toMatchInlineSnapshot(`
|
|
85
|
+
"
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
BarChart Showcase ────────────────────────────────────────────────────────────────────────────
|
|
89
|
+
|
|
90
|
+
> Search...
|
|
91
|
+
|
|
92
|
+
Monthly Budget Spent / Remaining / Savings │ ┌Leader: 85.0%┐
|
|
93
|
+
Disk Usage System / Apps / Media / Free │
|
|
94
|
+
Investment Portfolio Stocks / Bonds / Cash / C │
|
|
95
|
+
CPU Time User / System / IO Wait / Idle │ ────────────────────────────────────────────
|
|
96
|
+
Revenue by Product 6 product lines │
|
|
97
|
+
A/B Test Split Control vs Variant (50/50) │ HHI: 7,264 (monopoly)
|
|
98
|
+
Storage Full 100% used │
|
|
99
|
+
›Market Share One dominant + many tiny players │ Segments: 14 total
|
|
100
|
+
Equal Distribution 10 equal segments │
|
|
101
|
+
Custom Colors Explicit color per segment │ Visible: Tiny ones hidden
|
|
102
|
+
Needs vs Wants 50/30/20 budget rule │
|
|
103
|
+
Stress Test (20 items) Many small equal segmen │
|
|
104
|
+
│
|
|
105
|
+
│
|
|
106
|
+
↵ open detail ↑↓ navigate ^k actions │
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
"
|
|
116
|
+
`)
|
|
117
|
+
expect(text).toContain('Market Share')
|
|
118
|
+
}, 30000)
|
|
119
|
+
|
|
120
|
+
test('navigate to Equal Distribution - 10 segments', async () => {
|
|
121
|
+
await session.text({
|
|
122
|
+
waitFor: (text) => {
|
|
123
|
+
return text.includes('Monthly Budget')
|
|
124
|
+
},
|
|
125
|
+
timeout: 10000,
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
// Navigate to "Equal Distribution" (9th item)
|
|
129
|
+
for (let i = 0; i < 8; i++) {
|
|
130
|
+
await session.press('down')
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const text = await session.text({
|
|
134
|
+
waitFor: (text) => {
|
|
135
|
+
return text.includes('›Equal Distribution')
|
|
136
|
+
},
|
|
137
|
+
timeout: 5000,
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
expect(text).toMatchInlineSnapshot(`
|
|
141
|
+
"
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
BarChart Showcase ────────────────────────────────────────────────────────────────────────────
|
|
145
|
+
|
|
146
|
+
> Search...
|
|
147
|
+
|
|
148
|
+
Monthly Budget Spent / Remaining / Savings │
|
|
149
|
+
Disk Usage System / Apps / Media / Free │
|
|
150
|
+
Investment Portfolio Stocks / Bonds / Cash / C │ ────────────────────────────────────────────
|
|
151
|
+
CPU Time User / System / IO Wait / Idle │
|
|
152
|
+
Revenue by Product 6 product lines │ Segments: 10
|
|
153
|
+
A/B Test Split Control vs Variant (50/50) │
|
|
154
|
+
Storage Full 100% used │ Each: 10%
|
|
155
|
+
Market Share One dominant + many tiny players │
|
|
156
|
+
›Equal Distribution 10 equal segments │
|
|
157
|
+
Custom Colors Explicit color per segment │
|
|
158
|
+
Needs vs Wants 50/30/20 budget rule │
|
|
159
|
+
Stress Test (20 items) Many small equal segmen │
|
|
160
|
+
│
|
|
161
|
+
│
|
|
162
|
+
↵ open detail ↑↓ navigate ^k actions │
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
"
|
|
172
|
+
`)
|
|
173
|
+
expect(text).toContain('Equal Distribution')
|
|
174
|
+
}, 30000)
|
|
175
|
+
|
|
176
|
+
test('navigate to Stress Test - 20 segments', async () => {
|
|
177
|
+
await session.text({
|
|
178
|
+
waitFor: (text) => {
|
|
179
|
+
return text.includes('Monthly Budget')
|
|
180
|
+
},
|
|
181
|
+
timeout: 10000,
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
// Navigate to "Stress Test" (12th item, index 11)
|
|
185
|
+
for (let i = 0; i < 11; i++) {
|
|
186
|
+
await session.press('down')
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const text = await session.text({
|
|
190
|
+
waitFor: (text) => {
|
|
191
|
+
return text.includes('›Stress Test')
|
|
192
|
+
},
|
|
193
|
+
timeout: 5000,
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
expect(text).toMatchInlineSnapshot(`
|
|
197
|
+
"
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
BarChart Showcase ────────────────────────────────────────────────────────────────────────────
|
|
201
|
+
|
|
202
|
+
> Search...
|
|
203
|
+
|
|
204
|
+
Monthly Budget Spent / Remaining / Savings │
|
|
205
|
+
Disk Usage System / Apps / Media / Free │
|
|
206
|
+
Investment Portfolio Stocks / Bonds / Cash / C │ ────────────────────────────────────────────
|
|
207
|
+
CPU Time User / System / IO Wait / Idle │
|
|
208
|
+
Revenue by Product 6 product lines │ Segments: 20
|
|
209
|
+
A/B Test Split Control vs Variant (50/50) │
|
|
210
|
+
Storage Full 100% used │ Colors: 7 (cycling)
|
|
211
|
+
Market Share One dominant + many tiny players │
|
|
212
|
+
Equal Distribution 10 equal segments │
|
|
213
|
+
Custom Colors Explicit color per segment │
|
|
214
|
+
Needs vs Wants 50/30/20 budget rule │
|
|
215
|
+
›Stress Test (20 items) Many small equal segmen │
|
|
216
|
+
│
|
|
217
|
+
│
|
|
218
|
+
↵ open detail ↑↓ navigate ^k actions │
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
"
|
|
228
|
+
`)
|
|
229
|
+
expect(text).toContain('Stress Test')
|
|
230
|
+
}, 30000)
|
|
231
|
+
|
|
232
|
+
test('enter pushes full detail view with bar chart', async () => {
|
|
233
|
+
await session.text({
|
|
234
|
+
waitFor: (text) => {
|
|
235
|
+
return text.includes('Monthly Budget')
|
|
236
|
+
},
|
|
237
|
+
timeout: 10000,
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
await session.press('return')
|
|
241
|
+
|
|
242
|
+
const text = await session.text({
|
|
243
|
+
waitFor: (text) => {
|
|
244
|
+
return text.includes('Monthly Budget') && text.includes('Segments:')
|
|
245
|
+
},
|
|
246
|
+
timeout: 5000,
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
expect(text).toMatchInlineSnapshot(`
|
|
250
|
+
"
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
█
|
|
254
|
+
█
|
|
255
|
+
Monthly Budget █
|
|
256
|
+
▀
|
|
257
|
+
Spent / Remaining / Savings
|
|
258
|
+
|
|
259
|
+
Monthly Budget
|
|
260
|
+
|
|
261
|
+
Budget allocation for the current month.
|
|
262
|
+
|
|
263
|
+
- Spent: $4,850 (78.6%)
|
|
264
|
+
- Remaining: $707 (11.5%)
|
|
265
|
+
- Savings: $617 (10.0%)
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
Segments: 3
|
|
269
|
+
Total: 6,174
|
|
270
|
+
|
|
271
|
+
┌Spent: 78.6%┐
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
────────────────────────────────────────────────────────────────────────────────────────────
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
esc go back ^k actions ↵ Go Back powered by termcast.app
|
|
279
|
+
|
|
280
|
+
"
|
|
281
|
+
`)
|
|
282
|
+
expect(text).toContain('Monthly Budget')
|
|
283
|
+
}, 30000)
|