@opendata-ai/openchart-core 7.10.0 → 8.0.0-rc.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.
Files changed (58) hide show
  1. package/dist/index.d.ts +1154 -119
  2. package/dist/index.js +530 -41
  3. package/dist/index.js.map +1 -1
  4. package/dist/styles.css +2 -1
  5. package/dist/styles.css.map +1 -0
  6. package/package.json +9 -3
  7. package/schema/chart.schema.json +8957 -0
  8. package/schema/table.schema.json +861 -0
  9. package/schema/vizspec.schema.json +17694 -0
  10. package/src/accessibility/alt-text.ts +39 -1
  11. package/src/accessibility/aria.ts +4 -2
  12. package/src/colors/__tests__/colorblind.test.ts +12 -0
  13. package/src/colors/colorblind.ts +11 -2
  14. package/src/colors/contrast.ts +11 -0
  15. package/src/colors/index.ts +9 -1
  16. package/src/colors/palettes.ts +47 -0
  17. package/src/helpers/__tests__/deep-merge.test.ts +54 -0
  18. package/src/helpers/__tests__/spec-builders.test.ts +3 -3
  19. package/src/helpers/deep-merge.ts +29 -0
  20. package/src/helpers/spec-builders.ts +7 -7
  21. package/src/index.ts +29 -1
  22. package/src/labels/collision.ts +1 -0
  23. package/src/layout/chrome.ts +24 -0
  24. package/src/layout/index.ts +1 -1
  25. package/src/locale/__tests__/format.test.ts +347 -11
  26. package/src/locale/format.ts +243 -18
  27. package/src/locale/index.ts +10 -1
  28. package/src/responsive/index.ts +2 -0
  29. package/src/responsive/metrics.ts +74 -9
  30. package/src/schema/__tests__/generated-artifacts.test.ts +42 -0
  31. package/src/schema/__tests__/schema.test.ts +179 -0
  32. package/src/styles/__tests__/reduced-motion-selectors.test.ts +28 -0
  33. package/src/styles/animation.css +197 -168
  34. package/src/styles/base.css +89 -62
  35. package/src/styles/chrome.css +173 -179
  36. package/src/styles/dark.css +38 -35
  37. package/src/styles/graph.css +116 -111
  38. package/src/styles/index.css +5 -0
  39. package/src/styles/keyframes.css +115 -100
  40. package/src/styles/legend.css +14 -12
  41. package/src/styles/reduced-motion.css +61 -40
  42. package/src/styles/series-search.css +153 -0
  43. package/src/styles/sparkline.css +14 -12
  44. package/src/styles/table-animation.css +82 -80
  45. package/src/styles/table.css +314 -302
  46. package/src/styles/token-definitions.ts +199 -0
  47. package/src/styles/tokens.css +132 -128
  48. package/src/styles/tooltip.css +109 -115
  49. package/src/styles/you-draw-it.css +126 -0
  50. package/src/types/__tests__/encoding.test.ts +86 -17
  51. package/src/types/__tests__/spec.test.ts +16 -12
  52. package/src/types/encoding.ts +69 -25
  53. package/src/types/events.ts +12 -0
  54. package/src/types/index.ts +39 -3
  55. package/src/types/layout.ts +414 -8
  56. package/src/types/schema-roots.ts +32 -0
  57. package/src/types/spec.ts +643 -84
  58. package/src/types/table.ts +3 -2
@@ -1,188 +1,190 @@
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
- padding-left: 16px;
13
- padding-right: 16px;
14
- }
15
-
16
- & > .oc-chrome:first-child {
17
- padding-top: 4px;
18
- }
19
-
20
- & table {
21
- width: 100%;
22
- border-collapse: collapse;
23
- }
24
-
25
- & th,
26
- & td {
27
- padding: 10px 16px;
28
- text-align: left;
29
- border-bottom: 1px solid var(--oc-border);
30
- }
31
-
32
- & th {
33
- font-weight: 600;
34
- font-size: 12px;
35
- text-transform: uppercase;
36
- letter-spacing: 0.05em;
37
- color: var(--oc-text-secondary);
38
- white-space: nowrap;
39
- }
1
+ @layer oc.components {
2
+ /* ---------------------------------------------------------------------------
3
+ * Table base
4
+ * --------------------------------------------------------------------------- */
40
5
 
41
- & thead {
42
- position: sticky;
43
- top: 0;
44
- z-index: 2;
6
+ .oc-table-wrapper {
7
+ font-family: var(--oc-font-family);
8
+ color: var(--oc-text);
45
9
  background: var(--oc-bg);
46
- }
47
10
 
48
- & thead th {
49
- border-bottom-width: 2px;
50
- }
51
-
52
- & td {
53
- font-size: 14px;
54
- font-variant-numeric: tabular-nums;
55
- }
11
+ & > .oc-chrome {
12
+ margin-bottom: 16px;
13
+ padding-left: 16px;
14
+ padding-right: 16px;
15
+ }
56
16
 
57
- & th:focus {
58
- outline: 2px solid var(--oc-focus);
59
- outline-offset: -2px;
60
- }
17
+ & > .oc-chrome:first-child {
18
+ padding-top: 4px;
19
+ }
61
20
 
62
- & tbody:focus {
63
- outline: none;
64
- }
65
- }
21
+ & table {
22
+ width: 100%;
23
+ border-collapse: collapse;
24
+ }
66
25
 
67
- .oc-table-title {
68
- margin-bottom: 4px;
69
- font-size: var(--oc-title-computed-size, var(--oc-title-size));
70
- font-weight: var(--oc-title-computed-weight, var(--oc-title-weight));
71
- color: var(--oc-title-computed-color, var(--oc-text));
72
- }
26
+ & th,
27
+ & td {
28
+ padding: 10px 16px;
29
+ text-align: left;
30
+ border-bottom: 1px solid var(--oc-border);
31
+ }
73
32
 
74
- .oc-table-subtitle {
75
- margin-bottom: 8px;
76
- font-size: var(--oc-subtitle-computed-size, var(--oc-subtitle-size));
77
- font-weight: var(--oc-subtitle-computed-weight, var(--oc-subtitle-weight));
78
- color: var(--oc-subtitle-computed-color, var(--oc-text-secondary));
79
- }
33
+ & th {
34
+ font-weight: 600;
35
+ font-size: 12px;
36
+ text-transform: uppercase;
37
+ letter-spacing: 0.05em;
38
+ color: var(--oc-text-secondary);
39
+ white-space: nowrap;
40
+ }
80
41
 
81
- .oc-table-source {
82
- font-size: var(--oc-source-computed-size, var(--oc-source-size));
83
- color: var(--oc-source-computed-color, var(--oc-text-muted));
84
- }
42
+ & thead {
43
+ position: sticky;
44
+ top: 0;
45
+ z-index: 2;
46
+ background: var(--oc-bg);
47
+ }
85
48
 
86
- .oc-table-footer-text {
87
- font-size: var(--oc-footer-computed-size, var(--oc-source-size));
88
- color: var(--oc-footer-computed-color, var(--oc-text-muted));
89
- }
49
+ & thead th {
50
+ border-bottom-width: 2px;
51
+ }
90
52
 
91
- .oc-table-scroll {
92
- overflow-x: auto;
93
- }
53
+ & td {
54
+ font-size: 14px;
55
+ font-variant-numeric: tabular-nums;
56
+ }
94
57
 
95
- /* ---------------------------------------------------------------------------
96
- * Sticky first column
97
- * --------------------------------------------------------------------------- */
58
+ & th:focus {
59
+ outline: 2px solid var(--oc-focus);
60
+ outline-offset: -2px;
61
+ }
98
62
 
99
- .oc-table--sticky {
100
- & th:first-child,
101
- & td:first-child {
102
- position: sticky;
103
- left: 0;
104
- z-index: 1;
105
- background: var(--oc-bg);
63
+ & tbody:focus {
64
+ outline: none;
65
+ }
106
66
  }
107
- }
108
67
 
109
- /* ---------------------------------------------------------------------------
110
- * Sort controls
111
- * --------------------------------------------------------------------------- */
112
-
113
- .oc-table-sort-btn {
114
- background: none;
115
- border: none;
116
- cursor: pointer;
117
- padding: 2px;
118
- margin-left: 6px;
119
- display: inline-flex;
120
- flex-direction: column;
121
- align-items: center;
122
- vertical-align: middle;
123
- gap: 2px;
124
-
125
- &::before,
126
- &::after {
127
- content: "";
128
- display: block;
129
- width: 0;
130
- height: 0;
131
- border-left: 5px solid transparent;
132
- border-right: 5px solid transparent;
133
- transition:
134
- opacity 0.15s,
135
- border-color 0.15s;
68
+ .oc-table-title {
69
+ margin-bottom: 4px;
70
+ font-size: var(--oc-title-computed-size, var(--oc-title-size));
71
+ font-weight: var(--oc-title-computed-weight, var(--oc-title-weight));
72
+ color: var(--oc-title-computed-color, var(--oc-text));
136
73
  }
137
74
 
138
- &::before {
139
- border-bottom: 4.5px solid var(--oc-text-secondary);
140
- opacity: 0.45;
75
+ .oc-table-subtitle {
76
+ margin-bottom: 8px;
77
+ font-size: var(--oc-subtitle-computed-size, var(--oc-subtitle-size));
78
+ font-weight: var(--oc-subtitle-computed-weight, var(--oc-subtitle-weight));
79
+ color: var(--oc-subtitle-computed-color, var(--oc-text-secondary));
141
80
  }
142
81
 
143
- &::after {
144
- border-top: 4.5px solid var(--oc-text-secondary);
145
- opacity: 0.45;
82
+ .oc-table-source {
83
+ font-size: var(--oc-source-computed-size, var(--oc-source-size));
84
+ color: var(--oc-source-computed-color, var(--oc-text-muted));
146
85
  }
147
86
 
148
- &:hover::before,
149
- &:hover::after {
150
- opacity: 0.75;
87
+ .oc-table-footer-text {
88
+ font-size: var(--oc-footer-computed-size, var(--oc-source-size));
89
+ color: var(--oc-footer-computed-color, var(--oc-text-muted));
151
90
  }
152
- }
153
91
 
154
- th[aria-sort="ascending"] .oc-table-sort-btn {
155
- &::before {
156
- opacity: 1;
157
- border-bottom-color: var(--oc-text);
92
+ .oc-table-scroll {
93
+ overflow-x: auto;
158
94
  }
159
95
 
160
- &::after {
161
- opacity: 0.15;
96
+ /* ---------------------------------------------------------------------------
97
+ * Sticky first column
98
+ * --------------------------------------------------------------------------- */
99
+
100
+ .oc-table--sticky {
101
+ & th:first-child,
102
+ & td:first-child {
103
+ position: sticky;
104
+ left: 0;
105
+ z-index: 1;
106
+ background: var(--oc-bg);
107
+ }
162
108
  }
163
- }
164
109
 
165
- th[aria-sort="descending"] .oc-table-sort-btn {
166
- &::after {
167
- opacity: 1;
168
- border-top-color: var(--oc-text);
110
+ /* ---------------------------------------------------------------------------
111
+ * Sort controls
112
+ * --------------------------------------------------------------------------- */
113
+
114
+ .oc-table-sort-btn {
115
+ background: none;
116
+ border: none;
117
+ cursor: pointer;
118
+ padding: 2px;
119
+ margin-left: 6px;
120
+ display: inline-flex;
121
+ flex-direction: column;
122
+ align-items: center;
123
+ vertical-align: middle;
124
+ gap: 2px;
125
+
126
+ &::before,
127
+ &::after {
128
+ content: "";
129
+ display: block;
130
+ width: 0;
131
+ height: 0;
132
+ border-left: 5px solid transparent;
133
+ border-right: 5px solid transparent;
134
+ transition:
135
+ opacity 0.15s,
136
+ border-color 0.15s;
137
+ }
138
+
139
+ &::before {
140
+ border-bottom: 4.5px solid var(--oc-text-secondary);
141
+ opacity: 0.45;
142
+ }
143
+
144
+ &::after {
145
+ border-top: 4.5px solid var(--oc-text-secondary);
146
+ opacity: 0.45;
147
+ }
148
+
149
+ &:hover::before,
150
+ &:hover::after {
151
+ opacity: 0.75;
152
+ }
169
153
  }
170
154
 
171
- &::before {
172
- opacity: 0.15;
155
+ th[aria-sort="ascending"] .oc-table-sort-btn {
156
+ &::before {
157
+ opacity: 1;
158
+ border-bottom-color: var(--oc-text);
159
+ }
160
+
161
+ &::after {
162
+ opacity: 0.15;
163
+ }
173
164
  }
174
- }
175
165
 
176
- /* ---------------------------------------------------------------------------
177
- * Search
178
- * --------------------------------------------------------------------------- */
166
+ th[aria-sort="descending"] .oc-table-sort-btn {
167
+ &::after {
168
+ opacity: 1;
169
+ border-top-color: var(--oc-text);
170
+ }
171
+
172
+ &::before {
173
+ opacity: 0.15;
174
+ }
175
+ }
179
176
 
180
- .oc-table-search {
181
- padding: 8px 0;
177
+ /* ---------------------------------------------------------------------------
178
+ * Search
179
+ * --------------------------------------------------------------------------- */
182
180
 
183
- & input {
181
+ /* Shared search-input skin (table + series-search).
182
+ Padding excluded: it diverges (table: 8px 12px; series: 0 10px 0 28px)
183
+ and series-search.css is imported before table.css — a padding declaration
184
+ here would beat the series-search override by source order. */
185
+ .oc-table-search input,
186
+ .oc-series-search-input {
184
187
  width: 100%;
185
- padding: 8px 12px;
186
188
  border: 1px solid var(--oc-border);
187
189
  border-radius: 6px;
188
190
  font-size: 13px;
@@ -191,196 +193,206 @@ th[aria-sort="descending"] .oc-table-sort-btn {
191
193
  color: var(--oc-text);
192
194
  box-sizing: border-box;
193
195
  transition: border-color 0.15s;
194
-
195
- &::placeholder {
196
- color: var(--oc-text-muted);
197
- font-size: 13px;
198
- }
199
-
200
- &:focus {
201
- outline: none;
202
- border-color: var(--oc-focus);
203
- box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
204
- }
205
196
  }
206
- }
207
197
 
208
- /* ---------------------------------------------------------------------------
209
- * Pagination
210
- * --------------------------------------------------------------------------- */
198
+ .oc-table-search input::placeholder,
199
+ .oc-series-search-input::placeholder {
200
+ color: var(--oc-text-muted);
201
+ font-size: 13px;
202
+ }
211
203
 
212
- .oc-table-pagination {
213
- display: flex;
214
- align-items: center;
215
- justify-content: space-between;
216
- padding: 12px 0 4px;
217
- font-size: 13px;
218
- color: var(--oc-text-secondary);
204
+ .oc-table-search input:focus,
205
+ .oc-series-search-input:focus {
206
+ outline: none;
207
+ border-color: var(--oc-focus);
208
+ box-shadow: 0 0 0 3px var(--oc-focus-ring);
209
+ }
219
210
 
220
- & button {
221
- padding: 6px 14px;
222
- border: 1px solid var(--oc-border);
223
- border-radius: 6px;
224
- background: var(--oc-bg);
225
- color: var(--oc-text);
226
- cursor: pointer;
227
- font-size: 13px;
228
- font-family: inherit;
229
- transition:
230
- background 0.15s,
231
- border-color 0.15s;
211
+ .oc-table-search {
212
+ padding: 8px 0;
232
213
 
233
- &:disabled {
234
- opacity: 0.35;
235
- cursor: not-allowed;
214
+ & input {
215
+ padding: 8px 12px;
236
216
  }
217
+ }
237
218
 
238
- &:hover:not(:disabled) {
239
- background: var(--oc-hover-bg);
240
- border-color: var(--oc-axis);
241
- }
219
+ /* ---------------------------------------------------------------------------
220
+ * Pagination
221
+ * --------------------------------------------------------------------------- */
242
222
 
243
- &:focus-visible {
244
- outline: 2px solid var(--oc-focus);
245
- outline-offset: 1px;
223
+ .oc-table-pagination {
224
+ display: flex;
225
+ align-items: center;
226
+ justify-content: space-between;
227
+ padding: 12px 0 4px;
228
+ font-size: 13px;
229
+ color: var(--oc-text-secondary);
230
+
231
+ & button {
232
+ padding: 6px 14px;
233
+ border: 1px solid var(--oc-border);
234
+ border-radius: 6px;
235
+ background: var(--oc-bg);
236
+ color: var(--oc-text);
237
+ cursor: pointer;
238
+ font-size: 13px;
239
+ font-family: inherit;
240
+ transition:
241
+ background 0.15s,
242
+ border-color 0.15s;
243
+
244
+ &:disabled {
245
+ opacity: 0.35;
246
+ cursor: not-allowed;
247
+ }
248
+
249
+ &:hover:not(:disabled) {
250
+ background: var(--oc-hover-bg);
251
+ border-color: var(--oc-axis);
252
+ }
253
+
254
+ &:focus-visible {
255
+ outline: 2px solid var(--oc-focus);
256
+ outline-offset: 1px;
257
+ }
246
258
  }
247
259
  }
248
- }
249
260
 
250
- .oc-table-pagination-info {
251
- font-variant-numeric: tabular-nums;
252
- }
253
-
254
- .oc-table-pagination-btns {
255
- display: flex;
256
- gap: 8px;
257
- }
261
+ .oc-table-pagination-info {
262
+ font-variant-numeric: tabular-nums;
263
+ }
258
264
 
259
- /* ---------------------------------------------------------------------------
260
- * Bar cells
261
- * --------------------------------------------------------------------------- */
265
+ .oc-table-pagination-btns {
266
+ display: flex;
267
+ gap: 8px;
268
+ }
262
269
 
263
- .oc-table-bar {
264
- position: relative;
265
- }
270
+ /* ---------------------------------------------------------------------------
271
+ * Bar cells
272
+ * --------------------------------------------------------------------------- */
266
273
 
267
- .oc-table-bar-fill {
268
- position: absolute;
269
- top: 6px;
270
- left: 0;
271
- bottom: 6px;
272
- border-radius: 2px;
273
- opacity: 0.15;
274
- pointer-events: none;
275
- }
274
+ .oc-table-bar {
275
+ position: relative;
276
+ }
276
277
 
277
- .oc-table-bar-value {
278
- position: relative;
279
- z-index: 1;
280
- }
278
+ .oc-table-bar-fill {
279
+ position: absolute;
280
+ top: 6px;
281
+ left: 0;
282
+ bottom: 6px;
283
+ border-radius: 2px;
284
+ opacity: 0.15;
285
+ pointer-events: none;
286
+ }
281
287
 
282
- /* ---------------------------------------------------------------------------
283
- * Sparkline cells
284
- * --------------------------------------------------------------------------- */
288
+ .oc-table-bar-value {
289
+ position: relative;
290
+ z-index: 1;
291
+ }
285
292
 
286
- .oc-table-sparkline {
287
- display: block;
288
- width: 100%;
289
- position: relative;
293
+ /* ---------------------------------------------------------------------------
294
+ * Sparkline cells
295
+ * --------------------------------------------------------------------------- */
290
296
 
291
- & svg {
297
+ .oc-table-sparkline {
292
298
  display: block;
293
299
  width: 100%;
294
- overflow: visible;
300
+ position: relative;
301
+
302
+ & svg {
303
+ display: block;
304
+ width: 100%;
305
+ overflow: visible;
306
+ }
295
307
  }
296
- }
297
308
 
298
- .oc-table-sparkline-dot {
299
- position: absolute;
300
- border-radius: 50%;
301
- width: 5px;
302
- height: 5px;
303
- }
309
+ .oc-table-sparkline-dot {
310
+ position: absolute;
311
+ border-radius: 50%;
312
+ width: 5px;
313
+ height: 5px;
314
+ }
304
315
 
305
- .oc-table-sparkline-labels {
306
- display: flex;
307
- justify-content: space-between;
308
- font-size: 11px;
309
- line-height: 1;
310
- }
316
+ .oc-table-sparkline-labels {
317
+ display: flex;
318
+ justify-content: space-between;
319
+ font-size: 11px;
320
+ line-height: 1;
321
+ }
311
322
 
312
- /* ---------------------------------------------------------------------------
313
- * Image cells
314
- * --------------------------------------------------------------------------- */
323
+ /* ---------------------------------------------------------------------------
324
+ * Image cells
325
+ * --------------------------------------------------------------------------- */
315
326
 
316
- .oc-table-image {
317
- display: inline-block;
318
- vertical-align: middle;
327
+ .oc-table-image {
328
+ display: inline-block;
329
+ vertical-align: middle;
319
330
 
320
- & img {
321
- object-fit: cover;
331
+ & img {
332
+ object-fit: cover;
333
+ }
322
334
  }
323
- }
324
335
 
325
- .oc-table-image-rounded img {
326
- border-radius: 50%;
327
- }
336
+ .oc-table-image-rounded img {
337
+ border-radius: 50%;
338
+ }
328
339
 
329
- /* ---------------------------------------------------------------------------
330
- * Flag cells
331
- * --------------------------------------------------------------------------- */
340
+ /* ---------------------------------------------------------------------------
341
+ * Flag cells
342
+ * --------------------------------------------------------------------------- */
332
343
 
333
- .oc-table-flag {
334
- font-size: 1.2em;
335
- }
344
+ .oc-table-flag {
345
+ font-size: 1.2em;
346
+ }
336
347
 
337
- /* ---------------------------------------------------------------------------
338
- * Compact mode
339
- * --------------------------------------------------------------------------- */
348
+ /* ---------------------------------------------------------------------------
349
+ * Compact mode
350
+ * --------------------------------------------------------------------------- */
340
351
 
341
- .oc-table--compact {
342
- & th,
343
- & td {
344
- padding: 4px 8px;
345
- font-size: 13px;
346
- }
352
+ .oc-table--compact {
353
+ & th,
354
+ & td {
355
+ padding: 4px 8px;
356
+ font-size: 13px;
357
+ }
347
358
 
348
- & th {
349
- font-size: 11px;
359
+ & th {
360
+ font-size: 11px;
361
+ }
350
362
  }
351
- }
352
363
 
353
- /* ---------------------------------------------------------------------------
354
- * Row hover (when onRowClick is set)
355
- * --------------------------------------------------------------------------- */
364
+ /* ---------------------------------------------------------------------------
365
+ * Row hover (when onRowClick is set)
366
+ * --------------------------------------------------------------------------- */
356
367
 
357
- .oc-table--clickable tbody {
358
- & tr {
359
- cursor: pointer;
360
- }
368
+ .oc-table--clickable tbody {
369
+ & tr {
370
+ cursor: pointer;
371
+ }
361
372
 
362
- & tr:hover {
363
- background: var(--oc-hover-bg);
373
+ & tr:hover {
374
+ background: var(--oc-hover-bg);
375
+ }
364
376
  }
365
- }
366
377
 
367
- /* ---------------------------------------------------------------------------
368
- * Keyboard cell focus indicator
369
- * --------------------------------------------------------------------------- */
378
+ /* ---------------------------------------------------------------------------
379
+ * Keyboard cell focus indicator
380
+ * --------------------------------------------------------------------------- */
370
381
 
371
- .oc-table-cell-focus {
372
- outline: 2px solid var(--oc-focus);
373
- outline-offset: -2px;
374
- }
382
+ .oc-table-cell-focus {
383
+ outline: 2px solid var(--oc-focus);
384
+ outline-offset: -2px;
385
+ }
375
386
 
376
- /* ---------------------------------------------------------------------------
377
- * Empty state
378
- * --------------------------------------------------------------------------- */
387
+ /* ---------------------------------------------------------------------------
388
+ * Empty state
389
+ * --------------------------------------------------------------------------- */
379
390
 
380
- .oc-table-empty {
381
- padding: 32px 16px;
382
- text-align: center;
383
- color: var(--oc-text-secondary);
384
- font-size: 14px;
385
- font-style: italic;
391
+ .oc-table-empty {
392
+ padding: 32px 16px;
393
+ text-align: center;
394
+ color: var(--oc-text-secondary);
395
+ font-size: 14px;
396
+ font-style: italic;
397
+ }
386
398
  }