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.
- package/README.md +36 -0
- package/package.json +40 -0
- package/ui/ThemeProvider.tsx +16 -0
- package/ui/components/ArticlePage.tsx +673 -0
- package/ui/components/AuthPage.tsx +109 -0
- package/ui/components/Callout.tsx +79 -0
- package/ui/components/Chart.tsx +100 -0
- package/ui/components/CodeTabs.tsx +145 -0
- package/ui/components/ComparePage.tsx +364 -0
- package/ui/components/DashboardPage.tsx +773 -0
- package/ui/components/DocsNav.tsx +80 -0
- package/ui/components/Footer.tsx +136 -0
- package/ui/components/HubPage.tsx +529 -0
- package/ui/components/Modal.tsx +412 -0
- package/ui/components/Nav.tsx +162 -0
- package/ui/components/OnThisPage.tsx +56 -0
- package/ui/components/ParamsTable.tsx +86 -0
- package/ui/components/RangeBar.tsx +68 -0
- package/ui/components/SeriesChart.tsx +218 -0
- package/ui/components/SeriesPage.tsx +461 -0
- package/ui/components/StatusMark.tsx +48 -0
- package/ui/components/publictrades/ExternalLink.tsx +37 -0
- package/ui/components/publictrades/FactsCard.tsx +40 -0
- package/ui/components/publictrades/LedgerFooter.tsx +22 -0
- package/ui/components/publictrades/LedgerNav.tsx +78 -0
- package/ui/components/publictrades/Mark.tsx +40 -0
- package/ui/components/publictrades/SourceBlock.tsx +91 -0
- package/ui/components/publictrades/Tape.tsx +164 -0
- package/ui/components/publictrades/ThreeStamps.tsx +47 -0
- package/ui/components/publictrades/types.ts +19 -0
- package/ui/data/series.ts +851 -0
- package/ui/index.ts +50 -0
- package/ui/publictrades.ts +16 -0
- 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
|
+
}
|