@ricsam/react-spreadsheets 0.0.1 → 0.0.3

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.
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/react-spreadsheets",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "type": "module"
5
5
  }
package/dist/styles.css CHANGED
@@ -4,45 +4,78 @@
4
4
  * Import once in your app:
5
5
  * import "@ricsam/react-spreadsheets/styles.css";
6
6
  *
7
- * Everything is themed through CSS custom properties declared on `.rsp-root`,
8
- * so you can restyle the whole grid without overriding individual rules:
7
+ * ---------------------------------------------------------------------------
8
+ * Theming
9
+ * ---------------------------------------------------------------------------
10
+ * Every visual is driven by `--rsp-*` custom properties. Because the tokens are
11
+ * declared on `:root` (not on `.rsp-root`), you can override them from *any*
12
+ * ancestor and the grid will pick them up:
9
13
  *
10
- * .rsp-root { --rsp-accent: #7c3aed; --rsp-cell-font-size: 13px; }
14
+ * .my-panel { --rsp-accent: #7c3aed; --rsp-cell-font-size: 13px; }
11
15
  *
12
- * Light/dark are handled automatically via `light-dark()` and `color-scheme`.
13
- * Force one mode by setting `color-scheme: light` (or `dark`) on `.rsp-root`.
16
+ * ---------------------------------------------------------------------------
17
+ * Light / dark
18
+ * ---------------------------------------------------------------------------
19
+ * Colors resolve through `light-dark()`, which follows the inherited
20
+ * `color-scheme`. The grid deliberately does NOT declare `color-scheme` on
21
+ * `.rsp-root`, because doing so would override a `color-scheme` pin set by an
22
+ * ancestor and de-sync the grid from the surrounding page.
23
+ *
24
+ * To pin a subtree, set `color-scheme` on any ancestor (or on `.rsp-root`):
25
+ *
26
+ * .workbook-stage { color-scheme: light; } // always light
27
+ * .workbook-stage { color-scheme: dark; } // always dark
28
+ *
29
+ * Or use the helper classes shipped below:
30
+ *
31
+ * <FormulaWorkbook className="rsp-theme-light" />
32
+ * <FormulaWorkbook className="rsp-theme-dark" />
14
33
  */
15
34
 
16
- .rsp-root {
17
- color-scheme: light dark;
18
-
35
+ :root {
19
36
  /* Surfaces */
20
- --rsp-bg: light-dark(#ffffff, #111827);
21
- --rsp-header-bg: light-dark(#f8f9fa, #374151);
22
- --rsp-corner-bg: light-dark(#e0e0e0, #4b5563);
23
- --rsp-editor-bg: light-dark(#ffffff, #1f2937);
37
+ --rsp-bg: light-dark(#ffffff, #0f1523);
38
+ --rsp-surface: light-dark(#ffffff, #151c2c);
39
+ --rsp-header-bg: light-dark(#f7f8fa, #1a2233);
40
+ --rsp-header-active-bg: light-dark(#e9edf5, #24304a);
41
+ --rsp-corner-bg: light-dark(#eef1f6, #1a2233);
42
+ --rsp-editor-bg: light-dark(#ffffff, #1a2233);
24
43
 
25
44
  /* Lines */
26
- --rsp-border: light-dark(#e1e5e9, #4b5563);
27
- --rsp-corner-border: light-dark(#d0d0d0, #374151);
28
- --rsp-gridline: light-dark(#e1e5e9, #4b5563);
45
+ --rsp-border: light-dark(#e3e7ee, #2a3346);
46
+ --rsp-corner-border: light-dark(#d7dce6, #2a3346);
47
+ --rsp-gridline: light-dark(#e8ebf1, #232c3d);
29
48
 
30
49
  /* Text */
31
- --rsp-text: light-dark(#000000, #f9fafb);
32
- --rsp-header-text: light-dark(#374151, #d1d5db);
33
- --rsp-muted-text: light-dark(#6b7280, #9ca3af);
34
-
35
- /* Accent / selection */
36
- --rsp-accent: light-dark(#2196f3, #60a5fa);
50
+ --rsp-text: light-dark(#101828, #e8ecf4);
51
+ --rsp-header-text: light-dark(#5a6478, #99a4bb);
52
+ --rsp-muted-text: light-dark(#6b7280, #8c97ad);
53
+
54
+ /* Accent / selection.
55
+ NOTE: `@ricsam/selection-manager` paints the selection *perimeter* as an
56
+ inline box-shadow using a hardcoded `#2196F3`, which CSS cannot recolor.
57
+ The default accent stays in that blue family so the perimeter agrees with
58
+ the rest of the chrome out of the box. Overriding `--rsp-accent` with a
59
+ distant hue will make the perimeter read slightly differently. */
60
+ --rsp-accent: light-dark(#2196f3, #58a6ff);
37
61
  --rsp-accent-contrast: #ffffff;
38
- --rsp-selection-bg: light-dark(#e3f2fd, #1e3a5f);
39
- --rsp-selection-range-bg: light-dark(#e8f4fd, #1e3a5f);
40
- --rsp-hover-bg: light-dark(#f8f9fa, #374151);
41
- --rsp-focus-ring: light-dark(rgba(33, 150, 243, 0.2), rgba(96, 165, 250, 0.2));
62
+ --rsp-selection-bg: light-dark(#e8f2fe, #17304f);
63
+ --rsp-selection-range-bg: light-dark(#f1f7fe, #14263d);
64
+ --rsp-hover-bg: light-dark(#f5f7fa, #1c2436);
65
+ --rsp-focus-ring: light-dark(rgba(33, 150, 243, 0.22), rgba(88, 166, 255, 0.28));
42
66
 
43
67
  /* Danger (delete buttons, invalid input) */
44
- --rsp-danger: light-dark(#dc2626, #f87171);
45
- --rsp-danger-bg: light-dark(#fef2f2, #7f1d1d);
68
+ --rsp-danger: light-dark(#d92d20, #ff8a80);
69
+ --rsp-danger-bg: light-dark(#fef3f2, #3a1512);
70
+
71
+ /* Tables (consumed by the workbook's inline cell styles) */
72
+ --rsp-table-header-bg: light-dark(#2196f3, #1c3a5e);
73
+ --rsp-table-header-color: light-dark(#ffffff, #e8ecf4);
74
+ --rsp-table-header-border: light-dark(#1877c9, #2a5384);
75
+ --rsp-table-even-row-bg: light-dark(#f7f9fc, #161e2e);
76
+ --rsp-table-odd-row-bg: light-dark(#ffffff, #131a28);
77
+ --rsp-table-border-color: light-dark(#2196f3, #34405c);
78
+ --rsp-table-border-light: light-dark(#dfe4ec, #2a3346);
46
79
 
47
80
  /* Typography */
48
81
  --rsp-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
@@ -50,15 +83,41 @@
50
83
  --rsp-mono-font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo,
51
84
  Consolas, "Liberation Mono", monospace;
52
85
  --rsp-cell-font-size: 12px;
53
- --rsp-header-font-size: 12px;
86
+ --rsp-header-font-size: 11px;
54
87
 
55
- /* Shape */
56
- --rsp-radius: 6px;
88
+ /* Shape + motion */
89
+ --rsp-radius: 8px;
90
+ --rsp-radius-sm: 5px;
91
+ --rsp-cell-padding: 0 7px;
92
+ --rsp-transition: 120ms cubic-bezier(0.4, 0, 0.2, 1);
57
93
 
58
94
  /* Fill handle */
59
95
  --rsp-fill-handle-size: 8px;
60
96
  }
61
97
 
98
+ /* Opt-in scheme pins. Applied to `.rsp-root` (or any ancestor) these force one
99
+ mode regardless of the OS preference, and every `light-dark()` token below
100
+ follows because `color-scheme` inherits. */
101
+ .rsp-theme-light {
102
+ color-scheme: light;
103
+ }
104
+
105
+ .rsp-theme-dark {
106
+ color-scheme: dark;
107
+ }
108
+
109
+ .rsp-root {
110
+ /* No `color-scheme` here on purpose — see the file header. */
111
+ color: var(--rsp-text);
112
+ font-family: var(--rsp-font-family);
113
+ }
114
+
115
+ .rsp-root *,
116
+ .rsp-root *::before,
117
+ .rsp-root *::after {
118
+ box-sizing: border-box;
119
+ }
120
+
62
121
  /* ------------------------------------------------------------------ *
63
122
  * Spreadsheet grid
64
123
  * ------------------------------------------------------------------ */
@@ -77,7 +136,7 @@
77
136
  }
78
137
 
79
138
  .rsp-container.rsp-focused {
80
- box-shadow: 0 0 0 2px var(--rsp-accent);
139
+ box-shadow: inset 0 0 0 1px var(--rsp-accent);
81
140
  }
82
141
 
83
142
  .rsp-canvas,
@@ -92,9 +151,10 @@
92
151
  position: absolute;
93
152
  border: 1px solid transparent;
94
153
  background: transparent;
95
- padding: 4px;
154
+ padding: var(--rsp-cell-padding);
96
155
  font-size: var(--rsp-cell-font-size);
97
156
  font-family: var(--rsp-font-family);
157
+ font-variant-numeric: tabular-nums;
98
158
  overflow: hidden;
99
159
  white-space: pre;
100
160
  text-overflow: ellipsis;
@@ -108,30 +168,47 @@
108
168
 
109
169
  .rsp-cell.rsp-cell-multiline {
110
170
  align-items: flex-start;
171
+ padding-top: 4px;
111
172
  }
112
173
 
113
- .rsp-cell:hover {
174
+ /* Hover tint must never repaint a cell the consumer has colored, so it is
175
+ restricted to "plain" cells (no custom background). */
176
+ .rsp-cell.rsp-cell-plain:hover {
114
177
  background: var(--rsp-hover-bg);
115
178
  }
116
179
 
117
- .rsp-cell.rsp-cell-selected {
118
- border-color: var(--rsp-accent);
119
- }
120
-
121
180
  /* `rsp-cell-plain` marks cells whose background is not overridden by a
122
181
  consumer-supplied custom style, so selection tinting is safe to apply. */
182
+ .rsp-cell.rsp-cell-in-selection.rsp-cell-plain {
183
+ background: var(--rsp-selection-range-bg);
184
+ }
185
+
123
186
  .rsp-cell.rsp-cell-selected.rsp-cell-plain {
124
187
  background: var(--rsp-selection-bg);
125
188
  }
126
189
 
127
- .rsp-cell.rsp-cell-in-selection.rsp-cell-plain {
128
- background: var(--rsp-selection-range-bg);
190
+ /* The selection perimeter is painted by the selection manager as an inline
191
+ `box-shadow` on the individual edge cells, so this rule must NOT declare a
192
+ box-shadow of its own: `rsp-cell-selected` is present on every cell of a
193
+ multi-cell range, and a full outline here would box each interior cell.
194
+ Only the stacking order is raised so the perimeter is not clipped by
195
+ neighbouring cells. */
196
+ .rsp-cell.rsp-cell-selected {
197
+ z-index: 2;
129
198
  }
130
199
 
131
- .rsp-cell.rsp-cell-editing {
200
+ /* The editor surface must win over the selection tint above (equal specificity
201
+ would otherwise leave the selected-cell background showing through), and over
202
+ a consumer background so the text being typed stays legible. */
203
+ .rsp-cell.rsp-cell-editing,
204
+ .rsp-cell.rsp-cell-editing.rsp-cell-plain,
205
+ .rsp-cell.rsp-cell-selected.rsp-cell-editing,
206
+ .rsp-cell.rsp-cell-selected.rsp-cell-editing.rsp-cell-plain {
132
207
  background: var(--rsp-editor-bg);
133
- border: 2px solid var(--rsp-accent);
134
- box-shadow: 0 0 0 2px var(--rsp-focus-ring);
208
+ color: var(--rsp-text);
209
+ box-shadow:
210
+ inset 0 0 0 2px var(--rsp-accent),
211
+ 0 6px 16px -4px var(--rsp-focus-ring);
135
212
  z-index: 999;
136
213
  pointer-events: none;
137
214
  }
@@ -148,6 +225,7 @@
148
225
  font-family: var(--rsp-font-family);
149
226
  background: transparent;
150
227
  color: var(--rsp-text);
228
+ caret-color: var(--rsp-accent);
151
229
  resize: none;
152
230
  overflow: hidden;
153
231
  z-index: 1000;
@@ -157,12 +235,15 @@
157
235
 
158
236
  .rsp-fill-handle {
159
237
  position: absolute;
160
- bottom: 0;
161
- right: 0;
238
+ bottom: -1px;
239
+ right: -1px;
162
240
  width: var(--rsp-fill-handle-size);
163
241
  height: var(--rsp-fill-handle-size);
164
242
  background-color: var(--rsp-accent);
243
+ border: 1px solid var(--rsp-bg);
244
+ border-radius: 1px;
165
245
  cursor: crosshair;
246
+ z-index: 3;
166
247
  }
167
248
 
168
249
  /* Headers ---------------------------------------------------------- */
@@ -175,14 +256,34 @@
175
256
  align-items: center;
176
257
  justify-content: center;
177
258
  font-size: var(--rsp-header-font-size);
259
+ font-weight: 500;
260
+ letter-spacing: 0.02em;
178
261
  cursor: pointer;
179
262
  user-select: none;
180
263
  color: var(--rsp-header-text);
181
264
  box-sizing: border-box;
265
+ transition: background-color var(--rsp-transition), color var(--rsp-transition);
266
+ }
267
+
268
+ .rsp-header:hover {
269
+ background-color: var(--rsp-header-active-bg);
270
+ color: var(--rsp-text);
182
271
  }
183
272
 
184
273
  .rsp-header.rsp-header-selected {
185
- background-color: var(--rsp-selection-bg);
274
+ background-color: var(--rsp-header-active-bg);
275
+ color: var(--rsp-accent);
276
+ font-weight: 600;
277
+ }
278
+
279
+ /* A crisp accent edge on the side that faces the grid marks the active
280
+ row/column without changing the header's box metrics. */
281
+ .rsp-header.rsp-header-col.rsp-header-selected {
282
+ box-shadow: inset 0 -2px 0 var(--rsp-accent);
283
+ }
284
+
285
+ .rsp-header.rsp-header-row.rsp-header-selected {
286
+ box-shadow: inset -2px 0 0 var(--rsp-accent);
186
287
  }
187
288
 
188
289
  .rsp-column-headers,
@@ -231,6 +332,11 @@
231
332
  z-index: 10;
232
333
  }
233
334
 
335
+ .rsp-col-resize-handle:hover,
336
+ .rsp-row-resize-handle:hover {
337
+ background: var(--rsp-accent);
338
+ }
339
+
234
340
  /* Overlays --------------------------------------------------------- */
235
341
 
236
342
  .rsp-overlays {
@@ -249,7 +355,7 @@
249
355
  opacity: 0.5;
250
356
  pointer-events: none;
251
357
  border: 2px dashed var(--rsp-accent);
252
- border-radius: 4px;
358
+ border-radius: var(--rsp-radius-sm);
253
359
  background-color: var(--rsp-focus-ring);
254
360
  }
255
361
 
@@ -283,7 +389,7 @@
283
389
  display: flex;
284
390
  align-items: center;
285
391
  gap: 8px;
286
- padding: 6px 8px;
392
+ padding: 7px 10px;
287
393
  border-bottom: 1px solid var(--rsp-border);
288
394
  background: var(--rsp-header-bg);
289
395
  flex: none;
@@ -291,8 +397,9 @@
291
397
 
292
398
  .rsp-workbook-name {
293
399
  font-weight: 600;
294
- font-size: 13px;
295
- color: var(--rsp-header-text);
400
+ font-size: 12px;
401
+ letter-spacing: -0.01em;
402
+ color: var(--rsp-text);
296
403
  white-space: nowrap;
297
404
  }
298
405
 
@@ -305,8 +412,8 @@
305
412
  .rsp-sheet-tabs {
306
413
  display: flex;
307
414
  align-items: center;
308
- gap: 4px;
309
- padding: 4px 8px;
415
+ gap: 6px;
416
+ padding: 6px 10px;
310
417
  border-top: 1px solid var(--rsp-border);
311
418
  background: var(--rsp-header-bg);
312
419
  flex: none;
@@ -324,27 +431,38 @@
324
431
  display: inline-flex;
325
432
  align-items: center;
326
433
  gap: 6px;
327
- padding: 4px 10px;
434
+ padding: 5px 11px;
328
435
  border: 1px solid transparent;
329
- border-radius: var(--rsp-radius);
436
+ border-radius: var(--rsp-radius-sm);
330
437
  font-size: 12px;
331
438
  line-height: 1.4;
332
439
  cursor: pointer;
333
440
  white-space: nowrap;
334
441
  color: var(--rsp-header-text);
335
442
  background: transparent;
443
+ transition: background-color var(--rsp-transition), color var(--rsp-transition);
336
444
  }
337
445
 
338
446
  .rsp-sheet-tab:hover {
339
447
  background: var(--rsp-hover-bg);
448
+ color: var(--rsp-text);
340
449
  }
341
450
 
342
451
  .rsp-sheet-tab.rsp-sheet-tab-active {
343
- background: var(--rsp-bg);
344
- border-color: var(--rsp-border);
345
- color: var(--rsp-text);
452
+ background: var(--rsp-accent);
453
+ border-color: transparent;
454
+ color: var(--rsp-accent-contrast);
346
455
  font-weight: 600;
347
- box-shadow: inset 0 -2px 0 var(--rsp-accent);
456
+ }
457
+
458
+ /* Icon buttons inside the active tab must read against the accent fill. */
459
+ .rsp-sheet-tab.rsp-sheet-tab-active .rsp-icon-button {
460
+ color: var(--rsp-accent-contrast);
461
+ }
462
+
463
+ .rsp-sheet-tab.rsp-sheet-tab-active .rsp-icon-button:hover {
464
+ background: rgba(255, 255, 255, 0.22);
465
+ color: var(--rsp-accent-contrast);
348
466
  }
349
467
 
350
468
  /* Per-tab action buttons reveal on hover / when the tab is active. */
@@ -353,7 +471,7 @@
353
471
  align-items: center;
354
472
  gap: 2px;
355
473
  opacity: 0;
356
- transition: opacity 0.15s;
474
+ transition: opacity var(--rsp-transition);
357
475
  }
358
476
 
359
477
  .rsp-sheet-tab:hover .sheet-tab-actions,
@@ -364,12 +482,14 @@
364
482
 
365
483
  .rsp-sheet-tab-input {
366
484
  font: inherit;
367
- color: inherit;
485
+ color: var(--rsp-text);
368
486
  background: var(--rsp-editor-bg);
369
487
  border: 1px solid var(--rsp-accent);
370
- border-radius: 4px;
371
- padding: 1px 4px;
488
+ border-radius: var(--rsp-radius-sm);
489
+ padding: 2px 6px;
372
490
  min-width: 80px;
491
+ outline: none;
492
+ box-shadow: 0 0 0 3px var(--rsp-focus-ring);
373
493
  }
374
494
 
375
495
  .rsp-icon-button {
@@ -380,12 +500,13 @@
380
500
  height: 22px;
381
501
  padding: 0;
382
502
  border: none;
383
- border-radius: 4px;
503
+ border-radius: var(--rsp-radius-sm);
384
504
  background: transparent;
385
505
  color: var(--rsp-muted-text);
386
506
  cursor: pointer;
387
507
  font-size: 14px;
388
508
  line-height: 1;
509
+ transition: background-color var(--rsp-transition), color var(--rsp-transition);
389
510
  }
390
511
 
391
512
  .rsp-icon-button:hover {
@@ -393,6 +514,11 @@
393
514
  color: var(--rsp-text);
394
515
  }
395
516
 
517
+ .rsp-icon-button:disabled {
518
+ opacity: 0.4;
519
+ cursor: not-allowed;
520
+ }
521
+
396
522
  .rsp-icon-button.rsp-icon-button-delete:hover {
397
523
  background: var(--rsp-danger-bg);
398
524
  color: var(--rsp-danger);
@@ -403,19 +529,21 @@
403
529
  align-items: center;
404
530
  justify-content: center;
405
531
  gap: 4px;
406
- height: 24px;
407
- padding: 0 10px;
532
+ height: 26px;
533
+ padding: 0 11px;
408
534
  border: 1px dashed var(--rsp-border);
409
- border-radius: var(--rsp-radius);
535
+ border-radius: var(--rsp-radius-sm);
410
536
  background: transparent;
411
537
  color: var(--rsp-header-text);
412
538
  font-size: 12px;
413
539
  cursor: pointer;
414
540
  white-space: nowrap;
541
+ transition: border-color var(--rsp-transition), color var(--rsp-transition);
415
542
  }
416
543
 
417
544
  .rsp-add-sheet-button:hover {
418
545
  border-color: var(--rsp-accent);
546
+ border-style: solid;
419
547
  color: var(--rsp-accent);
420
548
  }
421
549
 
@@ -430,7 +558,7 @@
430
558
  .rsp-zoom-value {
431
559
  min-width: 44px;
432
560
  text-align: center;
433
- font-size: 12px;
561
+ font-size: 11px;
434
562
  color: var(--rsp-muted-text);
435
563
  font-variant-numeric: tabular-nums;
436
564
  }
@@ -441,7 +569,7 @@
441
569
  display: flex;
442
570
  align-items: center;
443
571
  gap: 8px;
444
- padding: 4px 8px;
572
+ padding: 6px 10px;
445
573
  border-bottom: 1px solid var(--rsp-border);
446
574
  background: var(--rsp-bg);
447
575
  flex: none;
@@ -449,12 +577,12 @@
449
577
 
450
578
  .rsp-name-box {
451
579
  min-width: 84px;
452
- padding: 3px 8px;
580
+ padding: 4px 8px;
453
581
  border: 1px solid var(--rsp-border);
454
- border-radius: 4px;
582
+ border-radius: var(--rsp-radius-sm);
455
583
  background: var(--rsp-header-bg);
456
584
  font-family: var(--rsp-mono-font-family);
457
- font-size: 12px;
585
+ font-size: 11px;
458
586
  text-align: center;
459
587
  color: var(--rsp-text);
460
588
  }
@@ -462,22 +590,52 @@
462
590
  .rsp-formula-input {
463
591
  flex: 1;
464
592
  min-width: 0;
465
- padding: 3px 8px;
593
+ padding: 4px 9px;
466
594
  border: 1px solid var(--rsp-border);
467
- border-radius: 4px;
595
+ border-radius: var(--rsp-radius-sm);
468
596
  background: var(--rsp-editor-bg);
469
597
  color: var(--rsp-text);
470
598
  font-family: var(--rsp-mono-font-family);
471
599
  font-size: 12px;
600
+ transition: border-color var(--rsp-transition), box-shadow var(--rsp-transition);
601
+ }
602
+
603
+ .rsp-formula-input::placeholder {
604
+ color: var(--rsp-muted-text);
472
605
  }
473
606
 
474
607
  .rsp-formula-input:focus {
475
608
  outline: none;
476
609
  border-color: var(--rsp-accent);
477
- box-shadow: 0 0 0 2px var(--rsp-focus-ring);
610
+ box-shadow: 0 0 0 3px var(--rsp-focus-ring);
478
611
  }
479
612
 
480
613
  .rsp-formula-input:disabled {
481
614
  opacity: 0.6;
482
615
  cursor: not-allowed;
483
616
  }
617
+
618
+ /* Scrollbars ------------------------------------------------------- */
619
+
620
+ .rsp-root {
621
+ scrollbar-color: var(--rsp-border) transparent;
622
+ }
623
+
624
+ .rsp-root ::-webkit-scrollbar {
625
+ width: 10px;
626
+ height: 10px;
627
+ }
628
+
629
+ .rsp-root ::-webkit-scrollbar-track {
630
+ background: transparent;
631
+ }
632
+
633
+ .rsp-root ::-webkit-scrollbar-thumb {
634
+ background: var(--rsp-border);
635
+ border: 2px solid var(--rsp-bg);
636
+ border-radius: 99px;
637
+ }
638
+
639
+ .rsp-root ::-webkit-scrollbar-thumb:hover {
640
+ background: var(--rsp-muted-text);
641
+ }
@@ -23,4 +23,5 @@ export { cn } from "./utils/cn";
23
23
  export { useResizeObserver } from "./utils/use-resize-observer";
24
24
  export { useColorScheme, type ColorScheme } from "./utils/use-color-scheme";
25
25
  export { useClickAway } from "./utils/use-click-away";
26
+ export { getContrastingTextColor, parseCssColor, relativeLuminance, } from "./utils/contrast-text";
26
27
  export { ViewportStream, type ViewportState, type ResizeHandle } from "./grid/types";
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Contrast helpers for consumer-supplied cell backgrounds.
3
+ *
4
+ * The grid's default text color is theme-aware (`--rsp-text` resolves through
5
+ * `light-dark()`), but a consumer-supplied `backgroundColor` usually is not —
6
+ * spreadsheet fills typically come from a document model that stores one
7
+ * literal color, e.g. `#f1f5f9`.
8
+ *
9
+ * Without help, a light literal fill combined with the dark theme's near-white
10
+ * text renders as white-on-white and the cell becomes unreadable. These helpers
11
+ * pick a readable ink for a known background instead.
12
+ *
13
+ * Only values we can actually parse are considered. Anything dynamic
14
+ * (`var(...)`, `color-mix(...)`, gradients, `transparent`, unknown keywords)
15
+ * returns `undefined` so the caller keeps the inherited themed color.
16
+ */
17
+ type Rgba = {
18
+ r: number;
19
+ g: number;
20
+ b: number;
21
+ a: number;
22
+ };
23
+ /**
24
+ * Parses hex (#rgb, #rgba, #rrggbb, #rrggbbaa), rgb()/rgba() and a small set of
25
+ * common keywords. Returns `undefined` for anything else.
26
+ */
27
+ export declare function parseCssColor(input: string): Rgba | undefined;
28
+ /** Relative luminance per WCAG 2.1. */
29
+ export declare function relativeLuminance({ r, g, b }: Pick<Rgba, "r" | "g" | "b">): number;
30
+ /**
31
+ * Returns a readable ink for `background`, or `undefined` when the background
32
+ * is unparseable or too translucent to classify (in which case the themed
33
+ * default should keep applying).
34
+ */
35
+ export declare function getContrastingTextColor(background: string): string | undefined;
36
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ricsam/react-spreadsheets",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "Headless-friendly React spreadsheet and workbook components: infinite virtualized grid, zoom, resizing, clipboard, fill handles and formula-engine bindings.",
5
5
  "type": "module",
6
6
  "module": "./dist/mjs/lib.mjs",
@@ -18,10 +18,10 @@
18
18
  "dist"
19
19
  ],
20
20
  "scripts": {
21
- "build": "bun build-lib.ts",
21
+ "build": "NODE_ENV=production bun build-lib.ts",
22
22
  "test": "bun test",
23
23
  "typecheck": "tsc --noEmit",
24
- "demo": "bun --hot demo/index.tsx"
24
+ "demo": "bun demo/serve.ts"
25
25
  },
26
26
  "keywords": [
27
27
  "react",