dev-api-ui 0.1.7

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 (34) hide show
  1. package/README.md +36 -0
  2. package/package.json +40 -0
  3. package/ui/ThemeProvider.tsx +16 -0
  4. package/ui/components/ArticlePage.tsx +673 -0
  5. package/ui/components/AuthPage.tsx +109 -0
  6. package/ui/components/Callout.tsx +79 -0
  7. package/ui/components/Chart.tsx +100 -0
  8. package/ui/components/CodeTabs.tsx +145 -0
  9. package/ui/components/ComparePage.tsx +364 -0
  10. package/ui/components/DashboardPage.tsx +773 -0
  11. package/ui/components/DocsNav.tsx +80 -0
  12. package/ui/components/Footer.tsx +136 -0
  13. package/ui/components/HubPage.tsx +529 -0
  14. package/ui/components/Modal.tsx +412 -0
  15. package/ui/components/Nav.tsx +162 -0
  16. package/ui/components/OnThisPage.tsx +56 -0
  17. package/ui/components/ParamsTable.tsx +86 -0
  18. package/ui/components/RangeBar.tsx +68 -0
  19. package/ui/components/SeriesChart.tsx +218 -0
  20. package/ui/components/SeriesPage.tsx +461 -0
  21. package/ui/components/StatusMark.tsx +48 -0
  22. package/ui/components/publictrades/ExternalLink.tsx +37 -0
  23. package/ui/components/publictrades/FactsCard.tsx +40 -0
  24. package/ui/components/publictrades/LedgerFooter.tsx +22 -0
  25. package/ui/components/publictrades/LedgerNav.tsx +78 -0
  26. package/ui/components/publictrades/Mark.tsx +40 -0
  27. package/ui/components/publictrades/SourceBlock.tsx +91 -0
  28. package/ui/components/publictrades/Tape.tsx +164 -0
  29. package/ui/components/publictrades/ThreeStamps.tsx +47 -0
  30. package/ui/components/publictrades/types.ts +19 -0
  31. package/ui/data/series.ts +851 -0
  32. package/ui/index.ts +50 -0
  33. package/ui/publictrades.ts +16 -0
  34. package/ui/themes.css +81 -0
@@ -0,0 +1,40 @@
1
+ 'use client';
2
+
3
+ // Status mark: colored dot + colored text. No pill, no chip, no border.
4
+ interface MarkProps {
5
+ label: string;
6
+ color: string; // hex — e.g. C.buy
7
+ size?: number; // dot px, default 7
8
+ fontSize?: number;
9
+ justify?: 'start' | 'end';
10
+ eyebrow?: boolean; // mono uppercase sourced signal mode
11
+ }
12
+
13
+ export default function Mark({ label, color, size = 7, fontSize = 13, justify = 'start', eyebrow = false }: MarkProps) {
14
+ return (
15
+ <span style={{
16
+ display: 'inline-flex',
17
+ alignItems: 'center',
18
+ gap: 7,
19
+ fontSize,
20
+ fontWeight: 500,
21
+ color,
22
+ justifyContent: justify === 'end' ? 'flex-end' : 'flex-start',
23
+ ...(eyebrow ? {
24
+ fontFamily: 'var(--mono)',
25
+ textTransform: 'uppercase' as const,
26
+ letterSpacing: '.03em',
27
+ } : {}),
28
+ }}>
29
+ <span style={{
30
+ width: size,
31
+ height: size,
32
+ borderRadius: '50%',
33
+ background: color,
34
+ flex: 'none',
35
+ display: 'inline-block',
36
+ }} />
37
+ {label}
38
+ </span>
39
+ );
40
+ }
@@ -0,0 +1,91 @@
1
+ 'use client';
2
+
3
+
4
+ interface SourceBlockProps {
5
+ label: string;
6
+ accession: string;
7
+ parserVersion: string;
8
+ href?: string;
9
+ }
10
+
11
+ const ExternalSvg = () => (
12
+ <svg
13
+ viewBox="0 0 24 24"
14
+ width={14}
15
+ height={14}
16
+ fill="none"
17
+ stroke="currentColor"
18
+ strokeWidth="1.8"
19
+ strokeLinecap="round"
20
+ strokeLinejoin="round"
21
+ aria-hidden="true"
22
+ style={{ verticalAlign: '-1px' }}
23
+ >
24
+ <path d="M13 5h6v6" />
25
+ <path d="M19 5l-8 8" />
26
+ <path d="M19 13v5a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1h5" />
27
+ </svg>
28
+ );
29
+
30
+ export default function SourceBlock({ label, accession, parserVersion, href = '#' }: SourceBlockProps) {
31
+ return (
32
+ <>
33
+ <div style={{
34
+ marginTop: 18,
35
+ background: 'var(--surface)',
36
+ border: '.5px solid var(--hairline-2)',
37
+ borderRadius: 8,
38
+ padding: '18px 22px',
39
+ display: 'flex',
40
+ alignItems: 'center',
41
+ justifyContent: 'space-between',
42
+ gap: 18,
43
+ flexWrap: 'wrap',
44
+ }}>
45
+ <div>
46
+ <div style={{ fontSize: 11, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '.07em', color: 'var(--tertiary)' }}>
47
+ {label}
48
+ </div>
49
+ <div style={{ fontSize: 15, marginTop: 5, fontFamily: 'var(--mono)', letterSpacing: '-.01em' }}>
50
+ {accession}
51
+ </div>
52
+ <div style={{ fontSize: 12, color: 'var(--tertiary)', marginTop: 4 }}>
53
+ {parserVersion}
54
+ </div>
55
+ </div>
56
+ <a
57
+ href={href}
58
+ target="_blank"
59
+ rel="noopener noreferrer"
60
+ style={{
61
+ display: 'inline-flex',
62
+ alignItems: 'center',
63
+ gap: 7,
64
+ fontSize: 14,
65
+ fontWeight: 500,
66
+ padding: '9px 15px',
67
+ borderRadius: 4,
68
+ cursor: 'pointer',
69
+ border: '.5px solid transparent',
70
+ background: 'var(--accent)',
71
+ color: '#fff',
72
+ textDecoration: 'none',
73
+ }}
74
+ >
75
+ View original disclosure <ExternalSvg />
76
+ </a>
77
+ </div>
78
+ <div style={{
79
+ marginTop: 14,
80
+ fontSize: 13,
81
+ color: 'var(--secondary)',
82
+ display: 'flex',
83
+ alignItems: 'center',
84
+ gap: 8,
85
+ }}>
86
+ <span style={{ color: '#1B7A4B', fontWeight: 600 }}>✓</span>
87
+ {' '}Verify it yourself. Every number on this page reconciles against the primary record.
88
+ </div>
89
+ </>
90
+ );
91
+ }
@@ -0,0 +1,164 @@
1
+ 'use client';
2
+
3
+ import Mark from './Mark';
4
+ import { TapeRow } from './types';
5
+
6
+ interface TapeProps {
7
+ rows: TapeRow[];
8
+ // column mode: 'entity' shows sym+name, 'asset' shows actor+sym, 'global' shows actor+sym
9
+ mode: 'entity' | 'asset' | 'global';
10
+ onRowClick?: (row: TapeRow) => void;
11
+ onActorClick?: (row: TapeRow, e: React.MouseEvent | React.KeyboardEvent) => void;
12
+ onAssetClick?: (row: TapeRow, e: React.MouseEvent | React.KeyboardEvent) => void;
13
+ }
14
+
15
+ const EXT_SVG = (
16
+ <svg
17
+ viewBox="0 0 24 24"
18
+ width={14}
19
+ height={14}
20
+ fill="none"
21
+ stroke="currentColor"
22
+ strokeWidth="1.8"
23
+ strokeLinecap="round"
24
+ strokeLinejoin="round"
25
+ aria-hidden="true"
26
+ >
27
+ <path d="M13 5h6v6" />
28
+ <path d="M19 5l-8 8" />
29
+ <path d="M19 13v5a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1h5" />
30
+ </svg>
31
+ );
32
+
33
+ const GRID = '104px 1fr 96px 116px 20px';
34
+
35
+ function TapeHeader({ actorCol }: { actorCol: string }) {
36
+ const cellStyle: React.CSSProperties = {
37
+ fontSize: 10,
38
+ fontWeight: 600,
39
+ textTransform: 'uppercase',
40
+ letterSpacing: '.07em',
41
+ color: 'var(--tertiary)',
42
+ };
43
+ return (
44
+ <div style={{
45
+ display: 'grid',
46
+ gridTemplateColumns: GRID,
47
+ alignItems: 'center',
48
+ gap: 16,
49
+ padding: '9px 18px',
50
+ borderBottom: '.5px solid var(--hairline-2)',
51
+ background: 'var(--paper)',
52
+ }}>
53
+ <span style={cellStyle}>Date</span>
54
+ <span style={cellStyle}>{actorCol}</span>
55
+ <span style={{ ...cellStyle, textAlign: 'right' }}>Action</span>
56
+ <span style={{ ...cellStyle, textAlign: 'right' }}>Amount</span>
57
+ <span />
58
+ </div>
59
+ );
60
+ }
61
+
62
+ export default function Tape({ rows, mode, onRowClick, onActorClick, onAssetClick }: TapeProps) {
63
+ const actorCol = mode === 'entity' ? 'Asset' : 'Actor';
64
+
65
+ return (
66
+ <div style={{
67
+ background: 'var(--surface)',
68
+ border: '.5px solid var(--hairline-2)',
69
+ borderRadius: 8,
70
+ overflow: 'hidden',
71
+ }}>
72
+ <TapeHeader actorCol={actorCol} />
73
+ {rows.map((row, i) => (
74
+ <div
75
+ key={i}
76
+ onClick={() => onRowClick?.(row)}
77
+ role={onRowClick ? 'button' : undefined}
78
+ tabIndex={onRowClick ? 0 : undefined}
79
+ onKeyDown={onRowClick ? (e) => { if (e.key === 'Enter' || e.key === ' ') onRowClick(row); } : undefined}
80
+ style={{
81
+ display: 'grid',
82
+ gridTemplateColumns: GRID,
83
+ alignItems: 'center',
84
+ gap: 16,
85
+ padding: '13px 18px',
86
+ borderBottom: i < rows.length - 1 ? '.5px solid var(--hairline)' : 'none',
87
+ fontSize: 13,
88
+ cursor: onRowClick ? 'pointer' : undefined,
89
+ }}
90
+ onMouseEnter={e => { (e.currentTarget as HTMLDivElement).style.background = 'var(--paper)'; }}
91
+ onMouseLeave={e => { (e.currentTarget as HTMLDivElement).style.background = ''; }}
92
+ >
93
+ {/* Date */}
94
+ <span style={{ fontFamily: 'var(--mono)', letterSpacing: '-.01em', color: 'var(--secondary)' }}>
95
+ {row.date}
96
+ </span>
97
+
98
+ {/* Actor / Asset cell */}
99
+ {mode === 'entity' ? (
100
+ // entity mode: ticker (mono/600) then company name (tertiary)
101
+ <span>
102
+ <span
103
+ onClick={e => onAssetClick?.(row, e)}
104
+ role={onAssetClick ? 'button' : undefined}
105
+ tabIndex={onAssetClick ? 0 : undefined}
106
+ onKeyDown={onAssetClick ? (e) => { if (e.key === 'Enter' || e.key === ' ') { e.stopPropagation(); onAssetClick(row, e); } } : undefined}
107
+ style={{ fontFamily: 'var(--mono)', fontWeight: 600, color: 'var(--ink)', cursor: onAssetClick ? 'pointer' : undefined }}
108
+ >
109
+ {row.sym}
110
+ </span>
111
+ {row.name && (
112
+ <span style={{ color: 'var(--tertiary)', marginLeft: 8 }}>{row.name}</span>
113
+ )}
114
+ </span>
115
+ ) : (
116
+ // asset / global mode: actor name then sym mono
117
+ <span>
118
+ <span
119
+ onClick={e => onActorClick?.(row, e)}
120
+ role={onActorClick ? 'button' : undefined}
121
+ tabIndex={onActorClick ? 0 : undefined}
122
+ onKeyDown={onActorClick ? (e) => { if (e.key === 'Enter' || e.key === ' ') { e.stopPropagation(); onActorClick(row, e); } } : undefined}
123
+ style={{ fontWeight: 600, color: 'var(--ink)', cursor: onActorClick ? 'pointer' : undefined }}
124
+ >
125
+ {row.actor}
126
+ </span>
127
+ <span
128
+ onClick={e => onAssetClick?.(row, e)}
129
+ role={onAssetClick ? 'button' : undefined}
130
+ tabIndex={onAssetClick ? 0 : undefined}
131
+ onKeyDown={onAssetClick ? (e) => { if (e.key === 'Enter' || e.key === ' ') { e.stopPropagation(); onAssetClick(row, e); } } : undefined}
132
+ style={{ color: 'var(--tertiary)', fontFamily: 'var(--mono)', letterSpacing: '-.01em', marginLeft: 8, cursor: onAssetClick ? 'pointer' : undefined }}
133
+ >
134
+ {row.sym}
135
+ </span>
136
+ </span>
137
+ )}
138
+
139
+ {/* Action — right-justified mark */}
140
+ <span style={{ textAlign: 'right' }}>
141
+ <Mark label={row.actLabel} color={row.actColor} justify="end" />
142
+ </span>
143
+
144
+ {/* Amount */}
145
+ <span style={{ textAlign: 'right', color: 'var(--secondary)', fontFamily: 'var(--mono)', letterSpacing: '-.01em' }}>
146
+ {row.amount}
147
+ </span>
148
+
149
+ {/* External link */}
150
+ <span style={{ textAlign: 'right', display: 'inline-flex', justifyContent: 'flex-end', color: 'var(--accent)' }}>
151
+ <a
152
+ href="#"
153
+ aria-label={`View filing for ${row.actor ? row.actor + ' · ' : ''}${row.sym}`}
154
+ style={{ display: 'inline-flex', color: 'var(--accent)' }}
155
+ onClick={e => e.stopPropagation()}
156
+ >
157
+ {EXT_SVG}
158
+ </a>
159
+ </span>
160
+ </div>
161
+ ))}
162
+ </div>
163
+ );
164
+ }
@@ -0,0 +1,47 @@
1
+ 'use client';
2
+
3
+ interface StampData {
4
+ label: string;
5
+ date: string;
6
+ hint: string;
7
+ pit?: boolean; // "Became public" cell tint
8
+ }
9
+
10
+ interface ThreeStampsProps {
11
+ stamps: [StampData, StampData, StampData];
12
+ }
13
+
14
+ export default function ThreeStamps({ stamps }: ThreeStampsProps) {
15
+ return (
16
+ <div style={{
17
+ display: 'grid',
18
+ gridTemplateColumns: '1fr 1fr 1fr',
19
+ marginTop: 18,
20
+ border: '.5px solid var(--hairline-2)',
21
+ borderRadius: 8,
22
+ overflow: 'hidden',
23
+ background: 'var(--surface)',
24
+ }}>
25
+ {stamps.map((s, i) => (
26
+ <div
27
+ key={i}
28
+ style={{
29
+ padding: '14px 18px',
30
+ borderRight: i < 2 ? '.5px solid var(--hairline)' : 'none',
31
+ background: s.pit ? 'var(--paper)' : undefined,
32
+ }}
33
+ >
34
+ <div style={{ fontSize: 11, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '.07em', color: 'var(--tertiary)' }}>
35
+ {s.label}
36
+ </div>
37
+ <div style={{ fontSize: 18, marginTop: 5, letterSpacing: '-.01em', fontFamily: 'var(--mono)' }}>
38
+ {s.date}
39
+ </div>
40
+ <div style={{ fontSize: 11, color: s.pit ? 'var(--accent)' : 'var(--tertiary)', marginTop: 3 }}>
41
+ {s.hint}
42
+ </div>
43
+ </div>
44
+ ))}
45
+ </div>
46
+ );
47
+ }
@@ -0,0 +1,19 @@
1
+ // PublicTrades-local semantic colors — NOT in shared themes.css
2
+ export const C = {
3
+ buy: '#1B7A4B',
4
+ sell: '#B23B3B',
5
+ cong: '#946A1B',
6
+ f4: '#1E3A5F', // same as accent; kept local for clarity
7
+ } as const;
8
+
9
+ export type ActionVariant = 'buy' | 'sell' | 'congress' | 'form4';
10
+
11
+ export interface TapeRow {
12
+ date: string;
13
+ actor?: string; // entity pages
14
+ sym: string;
15
+ name?: string; // asset pages
16
+ actLabel: string;
17
+ actColor: string;
18
+ amount: string;
19
+ }