@opendata-ai/openchart-core 6.4.1 → 6.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,380 @@
1
+ /* ---------------------------------------------------------------------------
2
+ * Table base
3
+ * --------------------------------------------------------------------------- */
4
+
5
+ .oc-table-wrapper {
6
+ font-family: var(--oc-font-family);
7
+ color: var(--oc-text);
8
+ background: var(--oc-bg);
9
+
10
+ & > .oc-chrome {
11
+ margin-bottom: 16px;
12
+ }
13
+
14
+ & table {
15
+ width: 100%;
16
+ border-collapse: collapse;
17
+ }
18
+
19
+ & th,
20
+ & td {
21
+ padding: 10px 16px;
22
+ text-align: left;
23
+ border-bottom: 1px solid var(--oc-border);
24
+ }
25
+
26
+ & th {
27
+ font-weight: 600;
28
+ font-size: 12px;
29
+ text-transform: uppercase;
30
+ letter-spacing: 0.05em;
31
+ color: var(--oc-text-secondary);
32
+ white-space: nowrap;
33
+ }
34
+
35
+ & thead {
36
+ position: sticky;
37
+ top: 0;
38
+ z-index: 2;
39
+ background: var(--oc-bg);
40
+ }
41
+
42
+ & thead th {
43
+ border-bottom-width: 2px;
44
+ }
45
+
46
+ & td {
47
+ font-size: 14px;
48
+ font-variant-numeric: tabular-nums;
49
+ }
50
+
51
+ & th:focus {
52
+ outline: 2px solid var(--oc-focus);
53
+ outline-offset: -2px;
54
+ }
55
+
56
+ & tbody:focus {
57
+ outline: none;
58
+ }
59
+ }
60
+
61
+ .oc-table-title {
62
+ margin-bottom: 4px;
63
+ font-size: var(--oc-title-computed-size, var(--oc-title-size));
64
+ font-weight: var(--oc-title-computed-weight, var(--oc-title-weight));
65
+ color: var(--oc-title-computed-color, var(--oc-text));
66
+ }
67
+
68
+ .oc-table-subtitle {
69
+ margin-bottom: 8px;
70
+ font-size: var(--oc-subtitle-computed-size, var(--oc-subtitle-size));
71
+ font-weight: var(--oc-subtitle-computed-weight, var(--oc-subtitle-weight));
72
+ color: var(--oc-subtitle-computed-color, var(--oc-text-secondary));
73
+ }
74
+
75
+ .oc-table-source {
76
+ font-size: var(--oc-source-computed-size, var(--oc-source-size));
77
+ color: var(--oc-source-computed-color, var(--oc-text-muted));
78
+ }
79
+
80
+ .oc-table-footer-text {
81
+ font-size: var(--oc-footer-computed-size, var(--oc-source-size));
82
+ color: var(--oc-footer-computed-color, var(--oc-text-muted));
83
+ }
84
+
85
+ .oc-table-scroll {
86
+ overflow-x: auto;
87
+ }
88
+
89
+ /* ---------------------------------------------------------------------------
90
+ * Sticky first column
91
+ * --------------------------------------------------------------------------- */
92
+
93
+ .oc-table--sticky {
94
+ & th:first-child,
95
+ & td:first-child {
96
+ position: sticky;
97
+ left: 0;
98
+ z-index: 1;
99
+ background: var(--oc-bg);
100
+ }
101
+ }
102
+
103
+ /* ---------------------------------------------------------------------------
104
+ * Sort controls
105
+ * --------------------------------------------------------------------------- */
106
+
107
+ .oc-table-sort-btn {
108
+ background: none;
109
+ border: none;
110
+ cursor: pointer;
111
+ padding: 2px;
112
+ margin-left: 6px;
113
+ display: inline-flex;
114
+ flex-direction: column;
115
+ align-items: center;
116
+ vertical-align: middle;
117
+ gap: 2px;
118
+
119
+ &::before,
120
+ &::after {
121
+ content: "";
122
+ display: block;
123
+ width: 0;
124
+ height: 0;
125
+ border-left: 5px solid transparent;
126
+ border-right: 5px solid transparent;
127
+ transition:
128
+ opacity 0.15s,
129
+ border-color 0.15s;
130
+ }
131
+
132
+ &::before {
133
+ border-bottom: 4.5px solid var(--oc-text-secondary);
134
+ opacity: 0.45;
135
+ }
136
+
137
+ &::after {
138
+ border-top: 4.5px solid var(--oc-text-secondary);
139
+ opacity: 0.45;
140
+ }
141
+
142
+ &:hover::before,
143
+ &:hover::after {
144
+ opacity: 0.75;
145
+ }
146
+ }
147
+
148
+ th[aria-sort="ascending"] .oc-table-sort-btn {
149
+ &::before {
150
+ opacity: 1;
151
+ border-bottom-color: var(--oc-text);
152
+ }
153
+
154
+ &::after {
155
+ opacity: 0.15;
156
+ }
157
+ }
158
+
159
+ th[aria-sort="descending"] .oc-table-sort-btn {
160
+ &::after {
161
+ opacity: 1;
162
+ border-top-color: var(--oc-text);
163
+ }
164
+
165
+ &::before {
166
+ opacity: 0.15;
167
+ }
168
+ }
169
+
170
+ /* ---------------------------------------------------------------------------
171
+ * Search
172
+ * --------------------------------------------------------------------------- */
173
+
174
+ .oc-table-search {
175
+ padding: 8px 0;
176
+
177
+ & input {
178
+ width: 100%;
179
+ padding: 8px 12px;
180
+ border: 1px solid var(--oc-border);
181
+ border-radius: 6px;
182
+ font-size: 13px;
183
+ font-family: inherit;
184
+ background: var(--oc-bg);
185
+ color: var(--oc-text);
186
+ box-sizing: border-box;
187
+ transition: border-color 0.15s;
188
+
189
+ &::placeholder {
190
+ color: var(--oc-text-muted);
191
+ font-size: 13px;
192
+ }
193
+
194
+ &:focus {
195
+ outline: none;
196
+ border-color: var(--oc-focus);
197
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
198
+ }
199
+ }
200
+ }
201
+
202
+ /* ---------------------------------------------------------------------------
203
+ * Pagination
204
+ * --------------------------------------------------------------------------- */
205
+
206
+ .oc-table-pagination {
207
+ display: flex;
208
+ align-items: center;
209
+ justify-content: space-between;
210
+ padding: 12px 0 4px;
211
+ font-size: 13px;
212
+ color: var(--oc-text-secondary);
213
+
214
+ & button {
215
+ padding: 6px 14px;
216
+ border: 1px solid var(--oc-border);
217
+ border-radius: 6px;
218
+ background: var(--oc-bg);
219
+ color: var(--oc-text);
220
+ cursor: pointer;
221
+ font-size: 13px;
222
+ font-family: inherit;
223
+ transition:
224
+ background 0.15s,
225
+ border-color 0.15s;
226
+
227
+ &:disabled {
228
+ opacity: 0.35;
229
+ cursor: not-allowed;
230
+ }
231
+
232
+ &:hover:not(:disabled) {
233
+ background: var(--oc-hover-bg);
234
+ border-color: var(--oc-axis);
235
+ }
236
+
237
+ &:focus-visible {
238
+ outline: 2px solid var(--oc-focus);
239
+ outline-offset: 1px;
240
+ }
241
+ }
242
+ }
243
+
244
+ .oc-table-pagination-info {
245
+ font-variant-numeric: tabular-nums;
246
+ }
247
+
248
+ .oc-table-pagination-btns {
249
+ display: flex;
250
+ gap: 8px;
251
+ }
252
+
253
+ /* ---------------------------------------------------------------------------
254
+ * Bar cells
255
+ * --------------------------------------------------------------------------- */
256
+
257
+ .oc-table-bar {
258
+ position: relative;
259
+ }
260
+
261
+ .oc-table-bar-fill {
262
+ position: absolute;
263
+ top: 6px;
264
+ left: 0;
265
+ bottom: 6px;
266
+ border-radius: 2px;
267
+ opacity: 0.15;
268
+ pointer-events: none;
269
+ }
270
+
271
+ .oc-table-bar-value {
272
+ position: relative;
273
+ z-index: 1;
274
+ }
275
+
276
+ /* ---------------------------------------------------------------------------
277
+ * Sparkline cells
278
+ * --------------------------------------------------------------------------- */
279
+
280
+ .oc-table-sparkline {
281
+ display: block;
282
+ width: 100%;
283
+ position: relative;
284
+
285
+ & svg {
286
+ display: block;
287
+ width: 100%;
288
+ overflow: visible;
289
+ }
290
+ }
291
+
292
+ .oc-table-sparkline-dot {
293
+ position: absolute;
294
+ border-radius: 50%;
295
+ width: 5px;
296
+ height: 5px;
297
+ }
298
+
299
+ .oc-table-sparkline-labels {
300
+ display: flex;
301
+ justify-content: space-between;
302
+ font-size: 11px;
303
+ line-height: 1;
304
+ }
305
+
306
+ /* ---------------------------------------------------------------------------
307
+ * Image cells
308
+ * --------------------------------------------------------------------------- */
309
+
310
+ .oc-table-image {
311
+ display: inline-block;
312
+ vertical-align: middle;
313
+
314
+ & img {
315
+ object-fit: cover;
316
+ }
317
+ }
318
+
319
+ .oc-table-image-rounded img {
320
+ border-radius: 50%;
321
+ }
322
+
323
+ /* ---------------------------------------------------------------------------
324
+ * Flag cells
325
+ * --------------------------------------------------------------------------- */
326
+
327
+ .oc-table-flag {
328
+ font-size: 1.2em;
329
+ }
330
+
331
+ /* ---------------------------------------------------------------------------
332
+ * Compact mode
333
+ * --------------------------------------------------------------------------- */
334
+
335
+ .oc-table--compact {
336
+ & th,
337
+ & td {
338
+ padding: 4px 8px;
339
+ font-size: 13px;
340
+ }
341
+
342
+ & th {
343
+ font-size: 11px;
344
+ }
345
+ }
346
+
347
+ /* ---------------------------------------------------------------------------
348
+ * Row hover (when onRowClick is set)
349
+ * --------------------------------------------------------------------------- */
350
+
351
+ .oc-table--clickable tbody {
352
+ & tr {
353
+ cursor: pointer;
354
+ }
355
+
356
+ & tr:hover {
357
+ background: var(--oc-hover-bg);
358
+ }
359
+ }
360
+
361
+ /* ---------------------------------------------------------------------------
362
+ * Keyboard cell focus indicator
363
+ * --------------------------------------------------------------------------- */
364
+
365
+ .oc-table-cell-focus {
366
+ outline: 2px solid var(--oc-focus);
367
+ outline-offset: -2px;
368
+ }
369
+
370
+ /* ---------------------------------------------------------------------------
371
+ * Empty state
372
+ * --------------------------------------------------------------------------- */
373
+
374
+ .oc-table-empty {
375
+ padding: 32px 16px;
376
+ text-align: center;
377
+ color: var(--oc-text-secondary);
378
+ font-size: 14px;
379
+ font-style: italic;
380
+ }
@@ -0,0 +1,93 @@
1
+ /* ---------------------------------------------------------------------------
2
+ * Custom properties (light mode defaults)
3
+ * --------------------------------------------------------------------------- */
4
+
5
+ .oc-root,
6
+ .oc-chart-root,
7
+ .oc-table-wrapper,
8
+ .oc-table-root,
9
+ .oc-graph-wrapper,
10
+ .oc-graph-root {
11
+ --oc-font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
12
+ --oc-font-mono: "JetBrains Mono", "Fira Code", "Cascadia Code", monospace;
13
+
14
+ /* Animation easing presets via CSS linear() */
15
+ --oc-ease-smooth: linear(
16
+ 0,
17
+ 0.157,
18
+ 0.438,
19
+ 0.64,
20
+ 0.766,
21
+ 0.85,
22
+ 0.906,
23
+ 0.941,
24
+ 0.964,
25
+ 0.978,
26
+ 0.988,
27
+ 0.994,
28
+ 0.998,
29
+ 1
30
+ );
31
+ --oc-ease-snappy: linear(
32
+ 0,
33
+ 0.012,
34
+ 0.048,
35
+ 0.108,
36
+ 0.194,
37
+ 0.302,
38
+ 0.426,
39
+ 0.559,
40
+ 0.69,
41
+ 0.808,
42
+ 0.905,
43
+ 0.973,
44
+ 1.013,
45
+ 1.028,
46
+ 1.023,
47
+ 1.006,
48
+ 0.984,
49
+ 0.966,
50
+ 0.957,
51
+ 0.957,
52
+ 0.964,
53
+ 0.975,
54
+ 0.986,
55
+ 0.995,
56
+ 1,
57
+ 1.003,
58
+ 1.002,
59
+ 1,
60
+ 0.998,
61
+ 0.998,
62
+ 0.999,
63
+ 1
64
+ );
65
+
66
+ /* Animation timing defaults */
67
+ --oc-animation-duration: 500ms;
68
+ --oc-animation-stagger: 80ms;
69
+ --oc-annotation-delay: 200ms;
70
+ --oc-title-size: 22px;
71
+ --oc-title-weight: 700;
72
+ --oc-title-tracking: -0.02em;
73
+ --oc-subtitle-size: 14px;
74
+ --oc-subtitle-weight: 400;
75
+ --oc-source-size: 12px;
76
+ --oc-source-weight: 400;
77
+ --oc-body-size: 13px;
78
+ --oc-bg: #ffffff;
79
+ --oc-text: #1d1d1d;
80
+ --oc-text-secondary: #5c5c5c;
81
+ --oc-text-muted: #999999;
82
+ --oc-gridline: #e8e8e8;
83
+ --oc-axis: #888888;
84
+ --oc-border: #e2e2e2;
85
+ --oc-border-radius: 4px;
86
+ --oc-focus: #3b82f6;
87
+ --oc-hover-bg: rgba(0, 0, 0, 0.025);
88
+ --oc-tooltip-bg: rgba(255, 255, 255, 0.88);
89
+ --oc-tooltip-border: rgba(0, 0, 0, 0.08);
90
+ --oc-tooltip-shadow: 0 2px 8px rgba(0, 0, 0, 0.08), 0 0 1px rgba(0, 0, 0, 0.12);
91
+ --oc-tooltip-text: #1d1d1d;
92
+ --oc-legend-text: #555555;
93
+ }
@@ -0,0 +1,88 @@
1
+ /* ---------------------------------------------------------------------------
2
+ * Tooltip
3
+ * --------------------------------------------------------------------------- */
4
+
5
+ .oc-tooltip {
6
+ position: absolute;
7
+ display: none;
8
+ pointer-events: none;
9
+ z-index: 1000;
10
+ background: var(--oc-tooltip-bg);
11
+ backdrop-filter: blur(12px);
12
+ border: 1px solid var(--oc-tooltip-border);
13
+ border-radius: 8px;
14
+ box-shadow: var(--oc-tooltip-shadow);
15
+ color: var(--oc-tooltip-text);
16
+ font-family: var(--oc-font-family);
17
+ font-size: 12px;
18
+ padding: 0;
19
+ max-width: 260px;
20
+ min-width: 140px;
21
+ line-height: 1.4;
22
+ animation: oc-tooltip-in 120ms ease-out;
23
+
24
+ & .oc-tooltip-header {
25
+ display: flex;
26
+ align-items: center;
27
+ gap: 6px;
28
+ padding: 8px 12px 6px;
29
+ }
30
+
31
+ & .oc-tooltip-dot {
32
+ width: 8px;
33
+ height: 8px;
34
+ border-radius: 50%;
35
+ flex-shrink: 0;
36
+ }
37
+
38
+ & .oc-tooltip-title {
39
+ font-weight: 600;
40
+ font-size: 12px;
41
+ letter-spacing: -0.01em;
42
+ color: var(--oc-tooltip-text);
43
+ white-space: nowrap;
44
+ overflow: hidden;
45
+ text-overflow: ellipsis;
46
+ }
47
+
48
+ & .oc-tooltip-body {
49
+ padding: 4px 12px 8px;
50
+ border-top: 1px solid var(--oc-tooltip-border);
51
+ }
52
+
53
+ /* Only add separator when header is present */
54
+ & .oc-tooltip-header + .oc-tooltip-body {
55
+ padding-top: 6px;
56
+ }
57
+
58
+ /* No separator when body is the only child */
59
+ & .oc-tooltip-body:first-child {
60
+ border-top: none;
61
+ padding-top: 8px;
62
+ }
63
+
64
+ & .oc-tooltip-row {
65
+ display: flex;
66
+ align-items: baseline;
67
+ justify-content: space-between;
68
+ gap: 12px;
69
+ padding: 1px 0;
70
+ }
71
+
72
+ & .oc-tooltip-label {
73
+ color: var(--oc-text-muted);
74
+ font-size: 11px;
75
+ white-space: nowrap;
76
+ flex-shrink: 0;
77
+ }
78
+
79
+ & .oc-tooltip-value {
80
+ font-weight: 500;
81
+ font-size: 11px;
82
+ font-variant-numeric: tabular-nums;
83
+ text-align: right;
84
+ overflow: hidden;
85
+ text-overflow: ellipsis;
86
+ white-space: nowrap;
87
+ }
88
+ }
@@ -57,6 +57,7 @@ export type {
57
57
  PointMark,
58
58
  Rect,
59
59
  RectMark,
60
+ ResolvedAnimation,
60
61
  ResolvedAnnotation,
61
62
  ResolvedChrome,
62
63
  ResolvedChromeElement,
@@ -80,6 +81,11 @@ export type {
80
81
  // Spec types (user input)
81
82
  export type {
82
83
  AggregateOp,
84
+ AnimationConfig,
85
+ AnimationEase,
86
+ AnimationPhaseConfig,
87
+ AnimationSpec,
88
+ AnimationStagger,
83
89
  Annotation,
84
90
  AnnotationAnchor,
85
91
  AnnotationOffset,
@@ -217,6 +217,8 @@ export interface LineMark {
217
217
  label?: ResolvedLabel;
218
218
  /** Accessibility attributes. */
219
219
  aria: MarkAria;
220
+ /** Index for stagger animation ordering. */
221
+ animationIndex?: number;
220
222
  }
221
223
 
222
224
  /**
@@ -257,6 +259,8 @@ export interface AreaMark {
257
259
  }>;
258
260
  /** Accessibility attributes. */
259
261
  aria: MarkAria;
262
+ /** Index for stagger animation ordering. */
263
+ animationIndex?: number;
260
264
  }
261
265
 
262
266
  /**
@@ -287,6 +291,14 @@ export interface RectMark {
287
291
  label?: ResolvedLabel;
288
292
  /** Accessibility attributes. */
289
293
  aria: MarkAria;
294
+ /** Index for stagger animation ordering. */
295
+ animationIndex?: number;
296
+ /** Bar orientation for animation direction. Set by the engine based on encoding. */
297
+ orient?: 'horizontal' | 'vertical';
298
+ /** Stacking group key (e.g. category name). Segments sharing this key animate together. */
299
+ stackGroup?: string;
300
+ /** Position of this segment within its stack group (0, 1, 2...). Set by engine for stacked bars. */
301
+ stackPos?: number;
290
302
  }
291
303
 
292
304
  /**
@@ -321,6 +333,8 @@ export interface ArcMark {
321
333
  label?: ResolvedLabel;
322
334
  /** Accessibility attributes. */
323
335
  aria: MarkAria;
336
+ /** Index for stagger animation ordering. */
337
+ animationIndex?: number;
324
338
  }
325
339
 
326
340
  /**
@@ -349,6 +363,8 @@ export interface PointMark {
349
363
  label?: ResolvedLabel;
350
364
  /** Accessibility attributes. */
351
365
  aria: MarkAria;
366
+ /** Index for stagger animation ordering. */
367
+ animationIndex?: number;
352
368
  }
353
369
 
354
370
  /**
@@ -381,6 +397,8 @@ export interface TextMarkLayout {
381
397
  label?: ResolvedLabel;
382
398
  /** Accessibility attributes. */
383
399
  aria: MarkAria;
400
+ /** Index for stagger animation ordering. */
401
+ animationIndex?: number;
384
402
  }
385
403
 
386
404
  /**
@@ -409,6 +427,8 @@ export interface RuleMarkLayout {
409
427
  data: Record<string, unknown>;
410
428
  /** Accessibility attributes. */
411
429
  aria: MarkAria;
430
+ /** Index for stagger animation ordering. */
431
+ animationIndex?: number;
412
432
  }
413
433
 
414
434
  /**
@@ -435,6 +455,8 @@ export interface TickMarkLayout {
435
455
  data: Record<string, unknown>;
436
456
  /** Accessibility attributes. */
437
457
  aria: MarkAria;
458
+ /** Index for stagger animation ordering. */
459
+ animationIndex?: number;
438
460
  }
439
461
 
440
462
  /** Discriminated union of all mark types. */
@@ -586,6 +608,26 @@ export interface A11yMetadata {
586
608
  keyboardNavigable: boolean;
587
609
  }
588
610
 
611
+ // ---------------------------------------------------------------------------
612
+ // Animation (resolved)
613
+ // ---------------------------------------------------------------------------
614
+
615
+ /** Resolved entrance animation config with all defaults applied. */
616
+ export interface ResolvedAnimation {
617
+ /** Whether entrance animation is enabled. */
618
+ enabled: boolean;
619
+ /** Duration in ms. */
620
+ duration: number;
621
+ /** Easing preset name. */
622
+ ease: import('./spec').AnimationEase;
623
+ /** Stagger delay between elements in ms. */
624
+ staggerDelay: number;
625
+ /** Stagger ordering. */
626
+ staggerOrder: 'index' | 'value' | 'reverse';
627
+ /** Delay before annotations animate in (ms after marks). */
628
+ annotationDelay: number;
629
+ }
630
+
589
631
  // ---------------------------------------------------------------------------
590
632
  // ChartLayout (the main engine output for charts)
591
633
  // ---------------------------------------------------------------------------
@@ -622,6 +664,8 @@ export interface ChartLayout {
622
664
  theme: ResolvedTheme;
623
665
  /** Total SVG dimensions. */
624
666
  dimensions: { width: number; height: number };
667
+ /** Resolved animation config. Present only when animation is enabled. */
668
+ animation?: ResolvedAnimation;
625
669
  }
626
670
 
627
671
  // ---------------------------------------------------------------------------
@@ -796,6 +840,8 @@ export interface TableLayout {
796
840
  a11y: { caption: string; summary: string };
797
841
  /** The resolved theme. */
798
842
  theme: ResolvedTheme;
843
+ /** Resolved animation config. Present only when animation is enabled. */
844
+ animation?: ResolvedAnimation;
799
845
  }
800
846
 
801
847
  // ---------------------------------------------------------------------------