@ponchia/ui 0.4.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +552 -8
- package/MIGRATIONS.json +106 -0
- package/README.md +34 -8
- package/annotations/index.d.ts +402 -0
- package/annotations/index.d.ts.map +1 -0
- package/annotations/index.js +792 -0
- package/behaviors/carousel.js +198 -0
- package/behaviors/combobox.js +226 -0
- package/behaviors/command.js +190 -0
- package/behaviors/connectors.js +95 -0
- package/behaviors/crosshair.js +57 -0
- package/behaviors/dialog.js +74 -0
- package/behaviors/disclosure.js +26 -0
- package/behaviors/dismissible.js +25 -0
- package/behaviors/forms.js +186 -0
- package/behaviors/glyph.js +108 -0
- package/behaviors/index.d.ts +79 -0
- package/behaviors/index.js +18 -1409
- package/behaviors/internal.js +97 -0
- package/behaviors/legend.js +67 -0
- package/behaviors/menu.js +47 -0
- package/behaviors/popover.js +179 -0
- package/behaviors/spotlight.js +52 -0
- package/behaviors/table.js +136 -0
- package/behaviors/tabs.js +103 -0
- package/behaviors/theme.js +84 -0
- package/behaviors/toast.js +164 -0
- package/classes/classes.json +1857 -0
- package/classes/index.d.ts +306 -13
- package/classes/index.js +339 -12
- package/classes/vscode.css-custom-data.json +12 -0
- package/connectors/index.d.ts +191 -0
- package/connectors/index.d.ts.map +1 -0
- package/connectors/index.js +275 -0
- package/css/analytical.css +21 -0
- package/css/annotations.css +292 -0
- package/css/app.css +43 -13
- package/css/base.css +15 -10
- package/css/command.css +97 -0
- package/css/connectors.css +110 -0
- package/css/content.css +7 -1
- package/css/crosshair.css +100 -0
- package/css/dataviz.css +5 -1
- package/css/disclosure.css +38 -6
- package/css/dots.css +57 -0
- package/css/feedback.css +111 -2
- package/css/fonts.css +11 -7
- package/css/forms.css +42 -1
- package/css/generated.css +117 -0
- package/css/legend.css +272 -0
- package/css/marks.css +174 -0
- package/css/motion.css +24 -44
- package/css/navigation.css +7 -0
- package/css/overlay.css +31 -1
- package/css/primitives.css +109 -5
- package/css/report.css +39 -81
- package/css/selection.css +46 -0
- package/css/site.css +16 -2
- package/css/sources.css +221 -0
- package/css/spotlight.css +104 -0
- package/css/state.css +121 -0
- package/css/tokens.css +60 -37
- package/css/workbench.css +83 -0
- package/dist/bronto.css +1 -1
- package/dist/css/analytical.css +1 -0
- package/dist/css/annotations.css +1 -0
- package/dist/css/app.css +1 -1
- package/dist/css/base.css +1 -1
- package/dist/css/command.css +1 -0
- package/dist/css/connectors.css +1 -0
- package/dist/css/content.css +1 -1
- package/dist/css/crosshair.css +1 -0
- package/dist/css/disclosure.css +1 -1
- package/dist/css/dots.css +1 -1
- package/dist/css/feedback.css +1 -1
- package/dist/css/fonts.css +1 -1
- package/dist/css/forms.css +1 -1
- package/dist/css/generated.css +1 -0
- package/dist/css/legend.css +1 -0
- package/dist/css/marks.css +1 -0
- package/dist/css/motion.css +1 -1
- package/dist/css/navigation.css +1 -1
- package/dist/css/overlay.css +1 -1
- package/dist/css/primitives.css +1 -1
- package/dist/css/report.css +1 -1
- package/dist/css/selection.css +1 -0
- package/dist/css/site.css +1 -1
- package/dist/css/sources.css +1 -0
- package/dist/css/spotlight.css +1 -0
- package/dist/css/state.css +1 -0
- package/dist/css/tokens.css +1 -1
- package/dist/css/workbench.css +1 -0
- package/docs/adr/0003-theme-model.md +7 -4
- package/docs/annotations.md +425 -0
- package/docs/architecture.md +246 -0
- package/docs/command.md +95 -0
- package/docs/connectors.md +91 -0
- package/docs/contrast.md +116 -92
- package/docs/crosshair.md +63 -0
- package/docs/d2.md +195 -0
- package/docs/generated.md +91 -0
- package/docs/legends.md +184 -0
- package/docs/marks.md +93 -0
- package/docs/mermaid.md +152 -0
- package/docs/reference.md +385 -23
- package/docs/reporting.md +436 -63
- package/docs/selection.md +40 -0
- package/docs/sources.md +137 -0
- package/docs/spotlight.md +78 -0
- package/docs/stability.md +24 -2
- package/docs/state.md +85 -0
- package/docs/usage.md +123 -4
- package/docs/vega.md +225 -0
- package/docs/workbench.md +78 -0
- package/fonts/doto-400.woff2 +0 -0
- package/fonts/doto-500.woff2 +0 -0
- package/fonts/doto-600.woff2 +0 -0
- package/fonts/doto-700.woff2 +0 -0
- package/fonts/doto-800.woff2 +0 -0
- package/fonts/doto-900.woff2 +0 -0
- package/glyphs/glyphs.js +6 -4
- package/llms.txt +362 -14
- package/package.json +115 -12
- package/qwik/index.d.ts +42 -54
- package/qwik/index.d.ts.map +1 -0
- package/qwik/index.js +75 -3
- package/react/index.d.ts +39 -56
- package/react/index.d.ts.map +1 -0
- package/react/index.js +67 -3
- package/solid/index.d.ts +64 -56
- package/solid/index.d.ts.map +1 -0
- package/solid/index.js +70 -3
- package/tokens/d2.d.ts +38 -0
- package/tokens/d2.js +71 -0
- package/tokens/d2.json +43 -0
- package/tokens/index.d.ts +5 -5
- package/tokens/index.js +23 -5
- package/tokens/index.json +9 -0
- package/tokens/mermaid.d.ts +23 -0
- package/tokens/mermaid.js +181 -0
- package/tokens/mermaid.json +163 -0
- package/tokens/resolved.json +45 -1
- package/tokens/skins.js +3 -2
- package/tokens/tokens.dtcg.json +26 -0
- package/tokens/vega.d.ts +34 -0
- package/tokens/vega.js +155 -0
- package/tokens/vega.json +179 -0
- package/fonts/doto-400.ttf +0 -0
- package/fonts/doto-500.ttf +0 -0
- package/fonts/doto-600.ttf +0 -0
- package/fonts/doto-700.ttf +0 -0
- package/fonts/doto-800.ttf +0 -0
- package/fonts/doto-900.ttf +0 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ponchia/ui/connectors — dependency-free SVG geometry for connecting two
|
|
3
|
+
* elements (or two points) with a leader line.
|
|
4
|
+
*
|
|
5
|
+
* Pure functions only: they take points/rects and return SVG path strings (or
|
|
6
|
+
* resolved coordinates). They own no DOM, no scales, and no live tracking —
|
|
7
|
+
* that optional glue lives in `@ponchia/ui/behaviors` (`initConnectors`). This
|
|
8
|
+
* is the page-coordinate, element-to-element cousin of the figure-coordinate
|
|
9
|
+
* `@ponchia/ui/annotations` helpers.
|
|
10
|
+
*
|
|
11
|
+
* import { connectRects } from '@ponchia/ui/connectors';
|
|
12
|
+
* const { d } = connectRects({ fromRect: a, toRect: b, shape: 'elbow' });
|
|
13
|
+
*
|
|
14
|
+
* The public types below are JSDoc `@typedef`s; the shipped `index.d.ts` is
|
|
15
|
+
* generated from them (and these signatures) by `tsc --emitDeclarationOnly`.
|
|
16
|
+
*
|
|
17
|
+
* @typedef {{ x: number, y: number }} Point
|
|
18
|
+
* @typedef {{ x: number, y: number, width: number, height: number }} Rect
|
|
19
|
+
* @typedef {'top' | 'right' | 'bottom' | 'left' | 'center'} Side
|
|
20
|
+
* @typedef {'straight' | 'elbow' | 'curve'} ConnectorShape
|
|
21
|
+
*
|
|
22
|
+
* @typedef {object} ConnectorPathOptions
|
|
23
|
+
* @property {Point} from
|
|
24
|
+
* @property {Point} to
|
|
25
|
+
* @property {ConnectorShape} [shape]
|
|
26
|
+
* @property {number} [curvature] Curve control-point reach along the dominant axis (curve shape). Default 0.5.
|
|
27
|
+
* @property {number} [mid] Turn position 0..1 along the span (elbow shape). Default 0.5.
|
|
28
|
+
*
|
|
29
|
+
* @typedef {object} ConnectRectsOptions
|
|
30
|
+
* @property {Rect} fromRect
|
|
31
|
+
* @property {Rect} toRect
|
|
32
|
+
* @property {Side} [fromSide] Anchor edges. Omit both to auto-pick facing edges from the rects.
|
|
33
|
+
* @property {Side} [toSide]
|
|
34
|
+
* @property {ConnectorShape} [shape]
|
|
35
|
+
* @property {number} [curvature]
|
|
36
|
+
* @property {number} [mid]
|
|
37
|
+
*
|
|
38
|
+
* @typedef {object} ConnectRectsResult
|
|
39
|
+
* @property {string} d SVG path data.
|
|
40
|
+
* @property {Point} from
|
|
41
|
+
* @property {Point} to
|
|
42
|
+
* @property {number} angle The path's end-tangent at `to` in radians — the direction the path arrives, so rotating an arrowhead at `to` by this points it along the path. Equals the straight `from`→`to` angle for `shape: 'straight'`; axis-aligned for `elbow`/`curve`.
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
// Shared scalar/geometry primitives. Exported so the annotations layer composes
|
|
46
|
+
// on the SAME kernel instead of copy-pasting it (the copies had silently
|
|
47
|
+
// diverged — see `clamp`). Low-level helpers; the documented API is the path
|
|
48
|
+
// builders below.
|
|
49
|
+
export const PRECISION = 1000;
|
|
50
|
+
|
|
51
|
+
export function finite(name, value, fallback) {
|
|
52
|
+
const v = value ?? fallback;
|
|
53
|
+
if (!Number.isFinite(v)) throw new TypeError(`${name} must be a finite number`);
|
|
54
|
+
return v;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function dimension(name, value, fallback) {
|
|
58
|
+
const v = finite(name, value, fallback);
|
|
59
|
+
if (v < 0) throw new RangeError(`${name} must be greater than or equal to 0`);
|
|
60
|
+
return v;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function fmt(value) {
|
|
64
|
+
const rounded = Math.round((Object.is(value, -0) ? 0 : value) * PRECISION) / PRECISION;
|
|
65
|
+
return String(Object.is(rounded, -0) ? 0 : rounded);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function point(x, y) {
|
|
69
|
+
return `${fmt(x)},${fmt(y)}`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Guarded form (returns min when the range is inverted) — the reconciled body;
|
|
73
|
+
// connectors only ever calls clamp(v, 0, 1) so this is output-identical here.
|
|
74
|
+
export function clamp(value, min, max) {
|
|
75
|
+
if (max < min) return min;
|
|
76
|
+
return Math.min(max, Math.max(min, value));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* A point on a rect's edge (or centre). `rect` is `{ x, y, width, height }`.
|
|
81
|
+
* @param {Rect} rect
|
|
82
|
+
* @param {Side} [side]
|
|
83
|
+
* @returns {Point}
|
|
84
|
+
*/
|
|
85
|
+
export function anchorPoint(rect, side = 'center') {
|
|
86
|
+
const x = finite('rect.x', rect?.x, 0);
|
|
87
|
+
const y = finite('rect.y', rect?.y, 0);
|
|
88
|
+
const w = dimension('rect.width', rect?.width, 0);
|
|
89
|
+
const h = dimension('rect.height', rect?.height, 0);
|
|
90
|
+
switch (side) {
|
|
91
|
+
case 'top':
|
|
92
|
+
return { x: x + w / 2, y };
|
|
93
|
+
case 'bottom':
|
|
94
|
+
return { x: x + w / 2, y: y + h };
|
|
95
|
+
case 'left':
|
|
96
|
+
return { x, y: y + h / 2 };
|
|
97
|
+
case 'right':
|
|
98
|
+
return { x: x + w, y: y + h / 2 };
|
|
99
|
+
case 'center':
|
|
100
|
+
default:
|
|
101
|
+
return { x: x + w / 2, y: y + h / 2 };
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Angle (radians) from `from` to `to`.
|
|
107
|
+
* @param {Point} from
|
|
108
|
+
* @param {Point} to
|
|
109
|
+
* @returns {number}
|
|
110
|
+
*/
|
|
111
|
+
export function angleBetween(from, to) {
|
|
112
|
+
return Math.atan2(
|
|
113
|
+
finite('to.y', to?.y) - finite('from.y', from?.y),
|
|
114
|
+
finite('to.x', to?.x) - finite('from.x', from?.x),
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Straight line from `from` to `to`.
|
|
120
|
+
* @param {Point} from
|
|
121
|
+
* @param {Point} to
|
|
122
|
+
* @returns {string}
|
|
123
|
+
*/
|
|
124
|
+
export function straightPath(from, to) {
|
|
125
|
+
return `M${point(finite('from.x', from?.x), finite('from.y', from?.y))}L${point(
|
|
126
|
+
finite('to.x', to?.x),
|
|
127
|
+
finite('to.y', to?.y),
|
|
128
|
+
)}`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Right-angle dogleg. Turns on the dominant axis at `mid` (0..1) of the span.
|
|
133
|
+
* @param {Point} from
|
|
134
|
+
* @param {Point} to
|
|
135
|
+
* @param {{ mid?: number }} [opts]
|
|
136
|
+
* @returns {string}
|
|
137
|
+
*/
|
|
138
|
+
export function elbowPath(from, to, opts = {}) {
|
|
139
|
+
const fx = finite('from.x', from?.x);
|
|
140
|
+
const fy = finite('from.y', from?.y);
|
|
141
|
+
const tx = finite('to.x', to?.x);
|
|
142
|
+
const ty = finite('to.y', to?.y);
|
|
143
|
+
const mid = clamp(finite('mid', opts.mid, 0.5), 0, 1);
|
|
144
|
+
const dx = tx - fx;
|
|
145
|
+
const dy = ty - fy;
|
|
146
|
+
if (Math.abs(dx) >= Math.abs(dy)) {
|
|
147
|
+
const mx = fx + dx * mid;
|
|
148
|
+
return `M${point(fx, fy)}H${fmt(mx)}V${fmt(ty)}H${fmt(tx)}`;
|
|
149
|
+
}
|
|
150
|
+
const my = fy + dy * mid;
|
|
151
|
+
return `M${point(fx, fy)}V${fmt(my)}H${fmt(tx)}V${fmt(ty)}`;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Cubic curve; control points extend along the dominant axis by `curvature`.
|
|
156
|
+
* @param {Point} from
|
|
157
|
+
* @param {Point} to
|
|
158
|
+
* @param {{ curvature?: number }} [opts]
|
|
159
|
+
* @returns {string}
|
|
160
|
+
*/
|
|
161
|
+
export function curvePath(from, to, opts = {}) {
|
|
162
|
+
const fx = finite('from.x', from?.x);
|
|
163
|
+
const fy = finite('from.y', from?.y);
|
|
164
|
+
const tx = finite('to.x', to?.x);
|
|
165
|
+
const ty = finite('to.y', to?.y);
|
|
166
|
+
const k = finite('curvature', opts.curvature, 0.5);
|
|
167
|
+
const dx = tx - fx;
|
|
168
|
+
const dy = ty - fy;
|
|
169
|
+
const horizontal = Math.abs(dx) >= Math.abs(dy);
|
|
170
|
+
const c1 = horizontal ? { x: fx + dx * k, y: fy } : { x: fx, y: fy + dy * k };
|
|
171
|
+
const c2 = horizontal ? { x: tx - dx * k, y: ty } : { x: tx, y: ty - dy * k };
|
|
172
|
+
return `M${point(fx, fy)}C${point(c1.x, c1.y)} ${point(c2.x, c2.y)} ${point(tx, ty)}`;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Build a path between two points by `shape` (`straight` | `elbow` | `curve`).
|
|
177
|
+
* @param {ConnectorPathOptions} [opts]
|
|
178
|
+
* @returns {string}
|
|
179
|
+
*/
|
|
180
|
+
export function connectorPath(opts = {}) {
|
|
181
|
+
const { from, to, shape = 'straight' } = opts;
|
|
182
|
+
if (shape === 'elbow') return elbowPath(from, to, opts);
|
|
183
|
+
if (shape === 'curve') return curvePath(from, to, opts);
|
|
184
|
+
return straightPath(from, to);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* A filled triangle arrowhead at `p`, pointing along `angle` (radians).
|
|
189
|
+
* @param {Point} p
|
|
190
|
+
* @param {number} angle
|
|
191
|
+
* @param {number} [size]
|
|
192
|
+
* @param {number} [spread] Half-angle of the head in radians (default 0.45).
|
|
193
|
+
* Smaller is crisper/sharper; must be in (0, π/2).
|
|
194
|
+
* @returns {string}
|
|
195
|
+
*/
|
|
196
|
+
export function arrowHead(p, angle, size = 8, spread = 0.45) {
|
|
197
|
+
const px = finite('p.x', p?.x);
|
|
198
|
+
const py = finite('p.y', p?.y);
|
|
199
|
+
const a = finite('angle', angle, 0);
|
|
200
|
+
const s = dimension('size', size, 8);
|
|
201
|
+
const sp = finite('spread', spread, 0.45);
|
|
202
|
+
if (sp <= 0 || sp >= Math.PI / 2) throw new RangeError('spread must be in (0, π/2)');
|
|
203
|
+
const back = a + Math.PI;
|
|
204
|
+
const p1 = { x: px + Math.cos(back - sp) * s, y: py + Math.sin(back - sp) * s };
|
|
205
|
+
const p2 = { x: px + Math.cos(back + sp) * s, y: py + Math.sin(back + sp) * s };
|
|
206
|
+
return `M${point(px, py)}L${point(p1.x, p1.y)}L${point(p2.x, p2.y)}Z`;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* A filled dot at `p`.
|
|
211
|
+
* @param {Point} p
|
|
212
|
+
* @param {number} [radius]
|
|
213
|
+
* @returns {string}
|
|
214
|
+
*/
|
|
215
|
+
export function dotMark(p, radius = 3) {
|
|
216
|
+
const px = finite('p.x', p?.x);
|
|
217
|
+
const py = finite('p.y', p?.y);
|
|
218
|
+
const r = dimension('radius', radius, 3);
|
|
219
|
+
if (r === 0) return '';
|
|
220
|
+
return `M${point(px, py - r)}A${fmt(r)},${fmt(r)} 0 1 1 ${point(px, py + r)}A${fmt(r)},${fmt(
|
|
221
|
+
r,
|
|
222
|
+
)} 0 1 1 ${point(px, py - r)}Z`;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Pick facing edges from the rects' relative centres.
|
|
227
|
+
* @param {Rect} fromRect
|
|
228
|
+
* @param {Rect} toRect
|
|
229
|
+
* @returns {{ from: Side, to: Side }}
|
|
230
|
+
*/
|
|
231
|
+
export function autoSides(fromRect, toRect) {
|
|
232
|
+
const fc = anchorPoint(fromRect, 'center');
|
|
233
|
+
const tc = anchorPoint(toRect, 'center');
|
|
234
|
+
const dx = tc.x - fc.x;
|
|
235
|
+
const dy = tc.y - fc.y;
|
|
236
|
+
if (Math.abs(dx) >= Math.abs(dy)) {
|
|
237
|
+
return dx >= 0 ? { from: 'right', to: 'left' } : { from: 'left', to: 'right' };
|
|
238
|
+
}
|
|
239
|
+
return dy >= 0 ? { from: 'bottom', to: 'top' } : { from: 'top', to: 'bottom' };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Angle (radians) at which a `shape` path *arrives* at `to` — straight is the
|
|
244
|
+
* chord; elbow/curve arrive axis-aligned along the dominant axis. Rotate an
|
|
245
|
+
* end marker by this so it points along the path, not the chord.
|
|
246
|
+
* @param {Point} from
|
|
247
|
+
* @param {Point} to
|
|
248
|
+
* @param {ConnectorShape} [shape]
|
|
249
|
+
* @returns {number}
|
|
250
|
+
*/
|
|
251
|
+
export function endTangentAngle(from, to, shape = 'straight') {
|
|
252
|
+
if (shape === 'straight') return angleBetween(from, to);
|
|
253
|
+
const dx = finite('to.x', to?.x) - finite('from.x', from?.x);
|
|
254
|
+
const dy = finite('to.y', to?.y) - finite('from.y', from?.y);
|
|
255
|
+
if (Math.abs(dx) >= Math.abs(dy)) return dx >= 0 ? 0 : Math.PI;
|
|
256
|
+
return dy >= 0 ? Math.PI / 2 : -Math.PI / 2;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Connect two rects. Resolves anchor points (explicit `fromSide`/`toSide`, else
|
|
261
|
+
* auto), builds the path, and returns `{ d, from, to, angle }` so the caller can
|
|
262
|
+
* place an arrowhead/dot at `to` rotated by `angle`.
|
|
263
|
+
* @param {ConnectRectsOptions} [opts]
|
|
264
|
+
* @returns {ConnectRectsResult}
|
|
265
|
+
*/
|
|
266
|
+
export function connectRects(opts = {}) {
|
|
267
|
+
const { fromRect, toRect, shape = 'straight', curvature, mid } = opts;
|
|
268
|
+
// Honor each side override independently; auto-pick whichever is unset.
|
|
269
|
+
const auto = autoSides(fromRect, toRect);
|
|
270
|
+
const sides = { from: opts.fromSide || auto.from, to: opts.toSide || auto.to };
|
|
271
|
+
const from = anchorPoint(fromRect, sides.from);
|
|
272
|
+
const to = anchorPoint(toRect, sides.to);
|
|
273
|
+
const d = connectorPath({ from, to, shape, curvature, mid });
|
|
274
|
+
return { d, from, to, angle: endTangentAngle(from, to, shape) };
|
|
275
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/* ==========================================================================
|
|
2
|
+
analytical — convenience roll-up of the opt-in analytical-primitive layers.
|
|
3
|
+
|
|
4
|
+
Import this one file instead of the seven leaves when you're building
|
|
5
|
+
analytical / generated-report UI:
|
|
6
|
+
|
|
7
|
+
@import '@ponchia/ui';
|
|
8
|
+
@import '@ponchia/ui/css/analytical.css';
|
|
9
|
+
|
|
10
|
+
Add @ponchia/ui/css/dataviz.css for the chart colour palette (legend swatches
|
|
11
|
+
fall back to the accent without it) and @ponchia/ui/css/report.css for the
|
|
12
|
+
document grammar as needed — those are separate concerns, kept opt-in.
|
|
13
|
+
========================================================================== */
|
|
14
|
+
|
|
15
|
+
@import url('./annotations.css') layer(bronto);
|
|
16
|
+
@import url('./legend.css') layer(bronto);
|
|
17
|
+
@import url('./marks.css') layer(bronto);
|
|
18
|
+
@import url('./connectors.css') layer(bronto);
|
|
19
|
+
@import url('./spotlight.css') layer(bronto);
|
|
20
|
+
@import url('./crosshair.css') layer(bronto);
|
|
21
|
+
@import url('./selection.css') layer(bronto);
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/* ==========================================================================
|
|
2
|
+
annotations — opt-in SVG callouts for analytical figures.
|
|
3
|
+
|
|
4
|
+
Bronto-native take on the subject / connector / note grammar: SVG-first,
|
|
5
|
+
no runtime dependency, no authoring handles. Import only beside charts,
|
|
6
|
+
reports, or generated figures that need explicit callouts.
|
|
7
|
+
========================================================================== */
|
|
8
|
+
|
|
9
|
+
.ui-annotation {
|
|
10
|
+
--annotation-color: var(--accent);
|
|
11
|
+
--annotation-line: var(--line-strong);
|
|
12
|
+
--annotation-note-bg: var(--panel);
|
|
13
|
+
--annotation-subject-fill: color-mix(in srgb, var(--annotation-color) 8%, transparent);
|
|
14
|
+
--annotation-stroke-width: 1.5;
|
|
15
|
+
|
|
16
|
+
color: var(--annotation-color);
|
|
17
|
+
font-family: var(--mono);
|
|
18
|
+
font-size: var(--text-xs);
|
|
19
|
+
overflow: visible;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.ui-annotation--muted {
|
|
23
|
+
--annotation-color: var(--text-dim);
|
|
24
|
+
--annotation-line: var(--line);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.ui-annotation--accent {
|
|
28
|
+
--annotation-color: var(--accent);
|
|
29
|
+
--annotation-line: var(--line-strong);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.ui-annotation--success {
|
|
33
|
+
--annotation-color: var(--success);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.ui-annotation--warning {
|
|
37
|
+
--annotation-color: var(--warning);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.ui-annotation--danger {
|
|
41
|
+
--annotation-color: var(--danger);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.ui-annotation--info {
|
|
45
|
+
--annotation-color: var(--info);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.ui-annotation__subject,
|
|
49
|
+
.ui-annotation__connector,
|
|
50
|
+
.ui-annotation__connector-end,
|
|
51
|
+
.ui-annotation__note-line,
|
|
52
|
+
.ui-annotation__badge {
|
|
53
|
+
vector-effect: non-scaling-stroke;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.ui-annotation__subject {
|
|
57
|
+
fill: var(--annotation-subject-fill);
|
|
58
|
+
stroke: var(--annotation-color);
|
|
59
|
+
stroke-linejoin: miter;
|
|
60
|
+
stroke-width: var(--annotation-stroke-width);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.ui-annotation__connector,
|
|
64
|
+
.ui-annotation__note-line {
|
|
65
|
+
fill: none;
|
|
66
|
+
stroke: var(--annotation-line);
|
|
67
|
+
stroke-linecap: square;
|
|
68
|
+
stroke-linejoin: miter;
|
|
69
|
+
stroke-opacity: 0.86;
|
|
70
|
+
stroke-width: var(--annotation-stroke-width);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.ui-annotation__connector-end {
|
|
74
|
+
fill: var(--annotation-line);
|
|
75
|
+
stroke: none;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.ui-annotation__note {
|
|
79
|
+
color: var(--text-soft);
|
|
80
|
+
fill: currentColor;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.ui-annotation__note > rect {
|
|
84
|
+
fill: var(--annotation-note-bg);
|
|
85
|
+
stroke: var(--line);
|
|
86
|
+
stroke-width: 1;
|
|
87
|
+
vector-effect: non-scaling-stroke;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.ui-annotation__title {
|
|
91
|
+
fill: var(--text);
|
|
92
|
+
font-size: var(--text-xs);
|
|
93
|
+
font-weight: 700;
|
|
94
|
+
letter-spacing: 0;
|
|
95
|
+
paint-order: stroke fill;
|
|
96
|
+
stroke: var(--annotation-note-bg);
|
|
97
|
+
stroke-linejoin: round;
|
|
98
|
+
stroke-width: 3;
|
|
99
|
+
text-transform: uppercase;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.ui-annotation__label {
|
|
103
|
+
fill: var(--text-soft);
|
|
104
|
+
font-size: var(--text-xs);
|
|
105
|
+
letter-spacing: 0;
|
|
106
|
+
paint-order: stroke fill;
|
|
107
|
+
stroke: var(--annotation-note-bg);
|
|
108
|
+
stroke-linejoin: round;
|
|
109
|
+
stroke-width: 3;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.ui-annotation__badge {
|
|
113
|
+
fill: var(--annotation-note-bg);
|
|
114
|
+
stroke: var(--annotation-color);
|
|
115
|
+
stroke-width: var(--annotation-stroke-width);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.ui-annotation--label .ui-annotation__subject,
|
|
119
|
+
.ui-annotation--badge .ui-annotation__connector,
|
|
120
|
+
.ui-annotation--badge .ui-annotation__note-line {
|
|
121
|
+
display: none;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.ui-annotation--callout .ui-annotation__note-line,
|
|
125
|
+
.ui-annotation--elbow .ui-annotation__note-line,
|
|
126
|
+
.ui-annotation--curve .ui-annotation__note-line {
|
|
127
|
+
stroke: var(--annotation-color);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.ui-annotation--elbow .ui-annotation__connector {
|
|
131
|
+
stroke-linejoin: bevel;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.ui-annotation--curve .ui-annotation__connector {
|
|
135
|
+
stroke-linecap: round;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.ui-annotation--circle .ui-annotation__subject {
|
|
139
|
+
stroke-dasharray: 0.01 4;
|
|
140
|
+
stroke-linecap: round;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.ui-annotation--rect .ui-annotation__subject {
|
|
144
|
+
stroke-dasharray: 5 3;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.ui-annotation--threshold .ui-annotation__subject {
|
|
148
|
+
fill: none;
|
|
149
|
+
stroke: var(--annotation-color);
|
|
150
|
+
stroke-dasharray: 6 4;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.ui-annotation--badge .ui-annotation__subject {
|
|
154
|
+
fill: var(--annotation-color);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.ui-annotation--badge .ui-annotation__badge {
|
|
158
|
+
fill: var(--annotation-color);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.ui-annotation--badge .ui-annotation__title,
|
|
162
|
+
.ui-annotation--badge .ui-annotation__label {
|
|
163
|
+
fill: var(--button-text);
|
|
164
|
+
stroke: none;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.ui-annotation--bracket .ui-annotation__subject,
|
|
168
|
+
.ui-annotation--compare .ui-annotation__subject,
|
|
169
|
+
.ui-annotation--axis .ui-annotation__subject,
|
|
170
|
+
.ui-annotation--timeline .ui-annotation__subject {
|
|
171
|
+
fill: none;
|
|
172
|
+
stroke: var(--annotation-color);
|
|
173
|
+
stroke-linecap: square;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.ui-annotation--band .ui-annotation__subject {
|
|
177
|
+
fill: var(--annotation-subject-fill);
|
|
178
|
+
stroke: var(--annotation-color);
|
|
179
|
+
stroke-dasharray: 5 3;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.ui-annotation--slope .ui-annotation__subject {
|
|
183
|
+
fill: none;
|
|
184
|
+
stroke: var(--annotation-color);
|
|
185
|
+
stroke-linecap: round;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.ui-annotation--cluster .ui-annotation__subject {
|
|
189
|
+
fill: var(--annotation-subject-fill);
|
|
190
|
+
stroke: var(--annotation-color);
|
|
191
|
+
stroke-dasharray: 0.01 5;
|
|
192
|
+
stroke-linecap: round;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.ui-annotation--evidence .ui-annotation__badge {
|
|
196
|
+
fill: var(--annotation-note-bg);
|
|
197
|
+
stroke: var(--annotation-color);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.ui-annotation--evidence .ui-annotation__title {
|
|
201
|
+
fill: var(--annotation-color);
|
|
202
|
+
stroke: none;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.ui-annotation--focus {
|
|
206
|
+
--annotation-stroke-width: 2;
|
|
207
|
+
--annotation-subject-fill: color-mix(in srgb, var(--annotation-color) 14%, transparent);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
211
|
+
.ui-annotation--draw .ui-annotation__connector,
|
|
212
|
+
.ui-annotation--draw .ui-annotation__note-line {
|
|
213
|
+
animation: uiAnnotationDraw var(--duration-slow, 600ms) var(--ease-out, ease-out) both;
|
|
214
|
+
animation-delay: var(--annotation-delay, 0ms);
|
|
215
|
+
stroke-dasharray: var(--annotation-dash, 360);
|
|
216
|
+
stroke-dashoffset: var(--annotation-dash, 360);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.ui-annotation--draw .ui-annotation__subject,
|
|
220
|
+
.ui-annotation--draw .ui-annotation__badge {
|
|
221
|
+
animation: uiAnnotationSubjectReveal var(--duration-slow, 600ms) var(--ease-out, ease-out) both;
|
|
222
|
+
animation-delay: var(--annotation-delay, 0ms);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.ui-annotation--reveal .ui-annotation__note {
|
|
226
|
+
animation: uiAnnotationReveal var(--duration-slow, 600ms) var(--ease-out, ease-out) both;
|
|
227
|
+
animation-delay: var(--annotation-delay, 0ms);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.ui-annotation--pulse .ui-annotation__subject,
|
|
231
|
+
.ui-annotation--pulse .ui-annotation__badge {
|
|
232
|
+
animation: uiAnnotationPulse 1.6s var(--ease-out, ease-out) infinite;
|
|
233
|
+
animation-delay: var(--annotation-delay, 0ms);
|
|
234
|
+
transform-box: fill-box;
|
|
235
|
+
transform-origin: center;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
@keyframes uiAnnotationDraw {
|
|
240
|
+
to {
|
|
241
|
+
stroke-dashoffset: 0;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
@keyframes uiAnnotationSubjectReveal {
|
|
246
|
+
from {
|
|
247
|
+
opacity: 0;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
to {
|
|
251
|
+
opacity: 1;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
@keyframes uiAnnotationReveal {
|
|
256
|
+
from {
|
|
257
|
+
opacity: 0;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
to {
|
|
261
|
+
opacity: 1;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
@keyframes uiAnnotationPulse {
|
|
266
|
+
50% {
|
|
267
|
+
opacity: 0.62;
|
|
268
|
+
transform: scale(1.06);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
@media print {
|
|
273
|
+
.ui-annotation__subject,
|
|
274
|
+
.ui-annotation__connector,
|
|
275
|
+
.ui-annotation__note,
|
|
276
|
+
.ui-annotation__note-line,
|
|
277
|
+
.ui-annotation__badge {
|
|
278
|
+
animation: none !important;
|
|
279
|
+
opacity: 1;
|
|
280
|
+
stroke-dashoffset: 0;
|
|
281
|
+
transform: none;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
@media (forced-colors: active) {
|
|
286
|
+
.ui-annotation {
|
|
287
|
+
--annotation-color: CanvasText;
|
|
288
|
+
--annotation-line: CanvasText;
|
|
289
|
+
--annotation-note-bg: Canvas;
|
|
290
|
+
--annotation-subject-fill: Canvas;
|
|
291
|
+
}
|
|
292
|
+
}
|
package/css/app.css
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
.ui-app-shell {
|
|
7
7
|
display: grid;
|
|
8
8
|
grid-template-columns: var(--app-rail, 14rem) minmax(0, 1fr);
|
|
9
|
-
min-block-size:
|
|
9
|
+
min-block-size: 100dvh;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
.ui-app-shell--full {
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
padding: var(--space-md);
|
|
25
25
|
position: sticky;
|
|
26
26
|
inset-block-start: 0;
|
|
27
|
-
block-size:
|
|
27
|
+
block-size: 100svh;
|
|
28
28
|
overflow-y: auto;
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
display: flex;
|
|
35
35
|
font-family: var(--display);
|
|
36
36
|
font-size: 1.05rem;
|
|
37
|
+
font-weight: var(--display-weight);
|
|
37
38
|
gap: 0.5rem;
|
|
38
39
|
letter-spacing: var(--tracking-wide);
|
|
39
40
|
padding: 0.35rem 0.5rem;
|
|
@@ -87,18 +88,33 @@
|
|
|
87
88
|
inline-size: 0.34rem;
|
|
88
89
|
}
|
|
89
90
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
@media (hover: hover) {
|
|
92
|
+
.ui-app-nav a:hover {
|
|
93
|
+
background: var(--bg-accent);
|
|
94
|
+
color: var(--text);
|
|
95
|
+
}
|
|
93
96
|
}
|
|
94
97
|
|
|
95
|
-
|
|
98
|
+
/* Current page honours BOTH the `.is-active` class and `aria-current` — the
|
|
99
|
+
sibling `.ui-sitenav` signals current-page with `aria-current="page"`, so the
|
|
100
|
+
app-shell nav now accepts the same programmatic cue (author it for AT, not
|
|
101
|
+
just the visual class — C19). */
|
|
102
|
+
.ui-app-nav a.is-active,
|
|
103
|
+
.ui-app-nav a[aria-current]:not([aria-current='false']) {
|
|
96
104
|
background: var(--accent-soft);
|
|
97
105
|
border-inline-start-color: var(--accent);
|
|
98
106
|
color: var(--accent-text);
|
|
99
107
|
}
|
|
100
108
|
|
|
101
|
-
|
|
109
|
+
@media (pointer: coarse) {
|
|
110
|
+
.ui-app-nav a {
|
|
111
|
+
min-block-size: 2.9rem;
|
|
112
|
+
padding-inline: 0.9rem;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.ui-app-nav a.is-active::before,
|
|
117
|
+
.ui-app-nav a[aria-current]:not([aria-current='false'])::before {
|
|
102
118
|
opacity: 1;
|
|
103
119
|
}
|
|
104
120
|
|
|
@@ -153,6 +169,7 @@
|
|
|
153
169
|
color: var(--text);
|
|
154
170
|
font-family: var(--display);
|
|
155
171
|
font-size: 1.1rem;
|
|
172
|
+
font-weight: var(--display-weight);
|
|
156
173
|
letter-spacing: var(--tracking-wide);
|
|
157
174
|
margin: 0;
|
|
158
175
|
text-transform: uppercase;
|
|
@@ -207,6 +224,7 @@
|
|
|
207
224
|
color: var(--text);
|
|
208
225
|
font-family: var(--display);
|
|
209
226
|
font-size: 0.95rem;
|
|
227
|
+
font-weight: var(--display-weight);
|
|
210
228
|
letter-spacing: var(--tracking-wide);
|
|
211
229
|
margin: 0;
|
|
212
230
|
text-transform: uppercase;
|
|
@@ -230,11 +248,7 @@
|
|
|
230
248
|
permanent admin-shell alias (grouped on the canonical rules there —
|
|
231
249
|
identical output). Nothing app-specific left to define here. */
|
|
232
250
|
|
|
233
|
-
/* --- Mobile rail
|
|
234
|
-
|
|
235
|
-
.ui-app-rail__toggle {
|
|
236
|
-
display: none;
|
|
237
|
-
}
|
|
251
|
+
/* --- Mobile rail: collapses to a horizontal scrolling strip --- */
|
|
238
252
|
|
|
239
253
|
@media (max-width: 880px) {
|
|
240
254
|
.ui-app-shell {
|
|
@@ -254,6 +268,15 @@
|
|
|
254
268
|
display: none;
|
|
255
269
|
}
|
|
256
270
|
|
|
271
|
+
/* The rail is flex-row here, so the base `margin-block-start: auto` (which
|
|
272
|
+
pushed account to the bottom in the column layout) is inert and account can
|
|
273
|
+
scroll off. Push it to the inline end instead so sign-out stays reachable.
|
|
274
|
+
(layout review C21.) */
|
|
275
|
+
.ui-app-rail__account {
|
|
276
|
+
margin-block-start: 0;
|
|
277
|
+
margin-inline-start: auto;
|
|
278
|
+
}
|
|
279
|
+
|
|
257
280
|
.ui-app-nav {
|
|
258
281
|
grid-auto-flow: column;
|
|
259
282
|
gap: 0.15rem;
|
|
@@ -269,11 +292,18 @@
|
|
|
269
292
|
white-space: nowrap;
|
|
270
293
|
}
|
|
271
294
|
|
|
272
|
-
.ui-app-nav a.is-active
|
|
295
|
+
.ui-app-nav a.is-active,
|
|
296
|
+
.ui-app-nav a[aria-current]:not([aria-current='false']) {
|
|
273
297
|
border-inline-start: 0;
|
|
274
298
|
border-block-end-color: var(--accent);
|
|
275
299
|
}
|
|
276
300
|
|
|
301
|
+
/* No --app-rail-height token exists, so drop the redundant second sticky;
|
|
302
|
+
only the horizontal rail stays pinned. */
|
|
303
|
+
.ui-app-topbar {
|
|
304
|
+
position: static;
|
|
305
|
+
}
|
|
306
|
+
|
|
277
307
|
.ui-app-content {
|
|
278
308
|
padding: var(--space-md);
|
|
279
309
|
}
|