gridsmith-ui 0.10.0 → 0.11.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/dist/index.js +77 -73
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1220,7 +1220,7 @@ function parseTypedDate(text, dayFirst = localeDayFirst()) {
|
|
|
1220
1220
|
const mi = monthIndex(m[2]);
|
|
1221
1221
|
return mi < 0 ? null : validDate(Number(m[3]), mi, Number(m[1]));
|
|
1222
1222
|
}
|
|
1223
|
-
m = /^(\d{1,2})[
|
|
1223
|
+
m = /^(\d{1,2})[/.-](\d{1,2})[/.-](\d{2,4})$/.exec(t);
|
|
1224
1224
|
if (m) {
|
|
1225
1225
|
const a = Number(m[1]), b = Number(m[2]);
|
|
1226
1226
|
const year = m[3].length === 2 ? 2e3 + Number(m[3]) : Number(m[3]);
|
|
@@ -1246,26 +1246,6 @@ function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, cl
|
|
|
1246
1246
|
const firstDay = getFirstDayOfWeek(year, month);
|
|
1247
1247
|
const today = useMemo(() => /* @__PURE__ */ new Date(), []);
|
|
1248
1248
|
const isTopLayer = useOverlayLayer(open);
|
|
1249
|
-
useEffect(() => {
|
|
1250
|
-
if (!open) return;
|
|
1251
|
-
const handler = (e) => {
|
|
1252
|
-
const t = e.target;
|
|
1253
|
-
if (triggerRef.current?.contains(t) || panelRef.current?.contains(t)) return;
|
|
1254
|
-
setOpen(false);
|
|
1255
|
-
};
|
|
1256
|
-
const keyHandler = (e) => {
|
|
1257
|
-
if (e.key === "Escape" && isTopLayer()) {
|
|
1258
|
-
setOpen(false);
|
|
1259
|
-
focusSegment(0);
|
|
1260
|
-
}
|
|
1261
|
-
};
|
|
1262
|
-
document.addEventListener("mousedown", handler);
|
|
1263
|
-
document.addEventListener("keydown", keyHandler);
|
|
1264
|
-
return () => {
|
|
1265
|
-
document.removeEventListener("mousedown", handler);
|
|
1266
|
-
document.removeEventListener("keydown", keyHandler);
|
|
1267
|
-
};
|
|
1268
|
-
}, [open, isTopLayer]);
|
|
1269
1249
|
const prevMonth = () => setViewDate(new Date(year, month - 1, 1));
|
|
1270
1250
|
const nextMonth = () => setViewDate(new Date(year, month + 1, 1));
|
|
1271
1251
|
const startOf = (d) => new Date(d.getFullYear(), d.getMonth(), d.getDate());
|
|
@@ -1280,18 +1260,39 @@ function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, cl
|
|
|
1280
1260
|
};
|
|
1281
1261
|
const dayFirst = useMemo(() => localeDayFirst(), []);
|
|
1282
1262
|
const [segs, setSegs] = useState(() => value ? { day: value.getDate(), month: value.getMonth() + 1, year: value.getFullYear() } : { day: null, month: null, year: null });
|
|
1283
|
-
const buffers =
|
|
1263
|
+
const [buffers, setBuffers] = useState({});
|
|
1284
1264
|
const lastCommitted = useRef(null);
|
|
1285
1265
|
useEffect(() => {
|
|
1286
1266
|
if (value && value.getTime() === lastCommitted.current) return;
|
|
1287
1267
|
setSegs(value ? { day: value.getDate(), month: value.getMonth() + 1, year: value.getFullYear() } : { day: null, month: null, year: null });
|
|
1288
|
-
|
|
1268
|
+
setBuffers({});
|
|
1289
1269
|
}, [value]);
|
|
1290
1270
|
const specs = dateSegments(dayFirst, segs.month && segs.year ? getDaysInMonth(segs.year, segs.month - 1) : 31);
|
|
1291
1271
|
const segmentRefs = useRef([]);
|
|
1292
1272
|
const focusSegment = (i) => {
|
|
1293
1273
|
segmentRefs.current[Math.max(0, Math.min(specs.length - 1, i))]?.focus();
|
|
1294
1274
|
};
|
|
1275
|
+
const focusSegmentRef = useLatest(focusSegment);
|
|
1276
|
+
useEffect(() => {
|
|
1277
|
+
if (!open) return;
|
|
1278
|
+
const handler = (e) => {
|
|
1279
|
+
const t = e.target;
|
|
1280
|
+
if (triggerRef.current?.contains(t) || panelRef.current?.contains(t)) return;
|
|
1281
|
+
setOpen(false);
|
|
1282
|
+
};
|
|
1283
|
+
const keyHandler = (e) => {
|
|
1284
|
+
if (e.key === "Escape" && isTopLayer()) {
|
|
1285
|
+
setOpen(false);
|
|
1286
|
+
focusSegmentRef.current(0);
|
|
1287
|
+
}
|
|
1288
|
+
};
|
|
1289
|
+
document.addEventListener("mousedown", handler);
|
|
1290
|
+
document.addEventListener("keydown", keyHandler);
|
|
1291
|
+
return () => {
|
|
1292
|
+
document.removeEventListener("mousedown", handler);
|
|
1293
|
+
document.removeEventListener("keydown", keyHandler);
|
|
1294
|
+
};
|
|
1295
|
+
}, [open, isTopLayer, focusSegmentRef]);
|
|
1295
1296
|
const [typedInvalid, setTypedInvalid] = useState(false);
|
|
1296
1297
|
const commitIfComplete = (next) => {
|
|
1297
1298
|
if (next.day === null || next.month === null || next.year === null) {
|
|
@@ -1319,7 +1320,7 @@ function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, cl
|
|
|
1319
1320
|
if (!d) return;
|
|
1320
1321
|
const next = { day: d.getDate(), month: d.getMonth() + 1, year: d.getFullYear() };
|
|
1321
1322
|
setSegs(next);
|
|
1322
|
-
|
|
1323
|
+
setBuffers({});
|
|
1323
1324
|
commitIfComplete(next);
|
|
1324
1325
|
};
|
|
1325
1326
|
const onSegmentKeyDown = (e, index, spec) => {
|
|
@@ -1327,6 +1328,7 @@ function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, cl
|
|
|
1327
1328
|
if (key === "Tab" || key === "Shift" || key === "Escape" || key.startsWith("F")) return;
|
|
1328
1329
|
e.preventDefault();
|
|
1329
1330
|
const k = spec.key;
|
|
1331
|
+
const clearBuffer = () => setBuffers((b) => ({ ...b, [k]: "" }));
|
|
1330
1332
|
if (key === "ArrowLeft") {
|
|
1331
1333
|
focusSegment(index - 1);
|
|
1332
1334
|
return;
|
|
@@ -1336,39 +1338,39 @@ function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, cl
|
|
|
1336
1338
|
return;
|
|
1337
1339
|
}
|
|
1338
1340
|
if (key === "ArrowUp" || key === "ArrowDown") {
|
|
1339
|
-
|
|
1341
|
+
clearBuffer();
|
|
1340
1342
|
setSegment(k, stepValue(spec, segs[k], key === "ArrowUp" ? 1 : -1));
|
|
1341
1343
|
return;
|
|
1342
1344
|
}
|
|
1343
1345
|
if (key === "Home") {
|
|
1344
|
-
|
|
1346
|
+
clearBuffer();
|
|
1345
1347
|
setSegment(k, spec.min);
|
|
1346
1348
|
return;
|
|
1347
1349
|
}
|
|
1348
1350
|
if (key === "End") {
|
|
1349
|
-
|
|
1351
|
+
clearBuffer();
|
|
1350
1352
|
setSegment(k, spec.max);
|
|
1351
1353
|
return;
|
|
1352
1354
|
}
|
|
1353
1355
|
if (key === "Backspace" || key === "Delete") {
|
|
1354
|
-
if (segs[k] === null && !buffers
|
|
1356
|
+
if (segs[k] === null && !buffers[k]) {
|
|
1355
1357
|
focusSegment(index - 1);
|
|
1356
1358
|
return;
|
|
1357
1359
|
}
|
|
1358
|
-
|
|
1360
|
+
clearBuffer();
|
|
1359
1361
|
setSegment(k, null, false);
|
|
1360
1362
|
return;
|
|
1361
1363
|
}
|
|
1362
1364
|
if (/^\d$/.test(key)) {
|
|
1363
|
-
const r = typeDigit(spec, buffers
|
|
1364
|
-
|
|
1365
|
+
const r = typeDigit(spec, buffers[k] ?? "", key);
|
|
1366
|
+
setBuffers((b) => ({ ...b, [k]: r.advance ? "" : r.buffer }));
|
|
1365
1367
|
setSegment(k, r.value);
|
|
1366
1368
|
if (r.advance) focusSegment(index + 1);
|
|
1367
1369
|
}
|
|
1368
1370
|
};
|
|
1369
1371
|
const shown = (spec) => {
|
|
1370
1372
|
const k = spec.key;
|
|
1371
|
-
const b = buffers
|
|
1373
|
+
const b = buffers[k];
|
|
1372
1374
|
if (b && segs[k] === null) return b;
|
|
1373
1375
|
if (segs[k] === null) return spec.placeholder;
|
|
1374
1376
|
return k === "year" ? String(segs[k]) : pad2(segs[k]);
|
|
@@ -1444,7 +1446,7 @@ function DatePicker({ value, onChange, placeholder = "Pick a date", disabled, cl
|
|
|
1444
1446
|
"aria-valuetext": segs[k] === null ? "empty" : String(segs[k]),
|
|
1445
1447
|
onKeyDown: (e) => onSegmentKeyDown(e, i, spec),
|
|
1446
1448
|
onClick: (e) => e.stopPropagation(),
|
|
1447
|
-
className: cn("rounded-[var(--ds-radius-sm)] px-[2px] outline-none focus:bg-[var(--ds-accent-subtle)] focus:text-[var(--ds-accent-primary)] cursor-text", segs[k] === null && !buffers
|
|
1449
|
+
className: cn("rounded-[var(--ds-radius-sm)] px-[2px] outline-none focus:bg-[var(--ds-accent-subtle)] focus:text-[var(--ds-accent-primary)] cursor-text", segs[k] === null && !buffers[k] && "text-[var(--ds-text-muted)]"),
|
|
1448
1450
|
children: shown(spec)
|
|
1449
1451
|
}
|
|
1450
1452
|
)
|
|
@@ -2288,6 +2290,12 @@ function SliderRange({
|
|
|
2288
2290
|
)
|
|
2289
2291
|
] });
|
|
2290
2292
|
}
|
|
2293
|
+
function fromValue(v, use24Hour) {
|
|
2294
|
+
const m = /^(\d{1,2}):(\d{2})$/.exec(v);
|
|
2295
|
+
if (!m) return { hour: null, minute: null, period: null };
|
|
2296
|
+
const h24 = Number(m[1]);
|
|
2297
|
+
return use24Hour ? { hour: h24, minute: Number(m[2]), period: null } : { hour: h24 % 12 === 0 ? 12 : h24 % 12, minute: Number(m[2]), period: h24 >= 12 ? "PM" : "AM" };
|
|
2298
|
+
}
|
|
2291
2299
|
var CONTROL_HEIGHT4 = {
|
|
2292
2300
|
sm: "h-[var(--ds-control-height-sm)]",
|
|
2293
2301
|
md: "h-[var(--ds-control-height-md)]",
|
|
@@ -2365,25 +2373,6 @@ function TimePicker({
|
|
|
2365
2373
|
const minutes = generateMinutes(minuteStep);
|
|
2366
2374
|
const isTopLayer = useOverlayLayer(open);
|
|
2367
2375
|
useCloseOnFocusOut(open, triggerRef, panelRef, () => setOpen(false), isTopLayer);
|
|
2368
|
-
useEffect(() => {
|
|
2369
|
-
if (!open) return;
|
|
2370
|
-
const handler = (e) => {
|
|
2371
|
-
if (triggerRef.current?.contains(e.target) || panelRef.current?.contains(e.target)) return;
|
|
2372
|
-
setOpen(false);
|
|
2373
|
-
};
|
|
2374
|
-
const keyHandler = (e) => {
|
|
2375
|
-
if (e.key === "Escape" && isTopLayer()) {
|
|
2376
|
-
setOpen(false);
|
|
2377
|
-
focusSegment(0);
|
|
2378
|
-
}
|
|
2379
|
-
};
|
|
2380
|
-
document.addEventListener("mousedown", handler);
|
|
2381
|
-
document.addEventListener("keydown", keyHandler);
|
|
2382
|
-
return () => {
|
|
2383
|
-
document.removeEventListener("mousedown", handler);
|
|
2384
|
-
document.removeEventListener("keydown", keyHandler);
|
|
2385
|
-
};
|
|
2386
|
-
}, [open, isTopLayer]);
|
|
2387
2376
|
useEffect(() => {
|
|
2388
2377
|
if (!open) return;
|
|
2389
2378
|
const id2 = requestAnimationFrame(() => {
|
|
@@ -2421,19 +2410,13 @@ function TimePicker({
|
|
|
2421
2410
|
onChange(`${pad(h24)}:${pad(minute)}`);
|
|
2422
2411
|
}
|
|
2423
2412
|
const specs = timeSegments(use24Hour);
|
|
2424
|
-
const
|
|
2425
|
-
|
|
2426
|
-
if (!m) return { hour: null, minute: null, period: null };
|
|
2427
|
-
const h24 = Number(m[1]);
|
|
2428
|
-
return use24Hour ? { hour: h24, minute: Number(m[2]), period: null } : { hour: h24 % 12 === 0 ? 12 : h24 % 12, minute: Number(m[2]), period: h24 >= 12 ? "PM" : "AM" };
|
|
2429
|
-
};
|
|
2430
|
-
const [segs, setSegs] = useState(() => fromValue(value));
|
|
2431
|
-
const buffers = useRef({});
|
|
2413
|
+
const [segs, setSegs] = useState(() => fromValue(value, use24Hour));
|
|
2414
|
+
const [buffers, setBuffers] = useState({});
|
|
2432
2415
|
const lastCommitted = useRef(null);
|
|
2433
2416
|
useEffect(() => {
|
|
2434
2417
|
if (value === lastCommitted.current) return;
|
|
2435
|
-
setSegs(fromValue(value));
|
|
2436
|
-
|
|
2418
|
+
setSegs(fromValue(value, use24Hour));
|
|
2419
|
+
setBuffers({});
|
|
2437
2420
|
}, [value, use24Hour]);
|
|
2438
2421
|
const segmentRefs = useRef([]);
|
|
2439
2422
|
const segmentCount = specs.length + (use24Hour ? 0 : 1);
|
|
@@ -2441,6 +2424,26 @@ function TimePicker({
|
|
|
2441
2424
|
const el = segmentRefs.current[Math.max(0, Math.min(segmentCount - 1, i))];
|
|
2442
2425
|
el?.focus();
|
|
2443
2426
|
};
|
|
2427
|
+
const focusSegmentRef = useLatest(focusSegment);
|
|
2428
|
+
useEffect(() => {
|
|
2429
|
+
if (!open) return;
|
|
2430
|
+
const handler = (e) => {
|
|
2431
|
+
if (triggerRef.current?.contains(e.target) || panelRef.current?.contains(e.target)) return;
|
|
2432
|
+
setOpen(false);
|
|
2433
|
+
};
|
|
2434
|
+
const keyHandler = (e) => {
|
|
2435
|
+
if (e.key === "Escape" && isTopLayer()) {
|
|
2436
|
+
setOpen(false);
|
|
2437
|
+
focusSegmentRef.current(0);
|
|
2438
|
+
}
|
|
2439
|
+
};
|
|
2440
|
+
document.addEventListener("mousedown", handler);
|
|
2441
|
+
document.addEventListener("keydown", keyHandler);
|
|
2442
|
+
return () => {
|
|
2443
|
+
document.removeEventListener("mousedown", handler);
|
|
2444
|
+
document.removeEventListener("keydown", keyHandler);
|
|
2445
|
+
};
|
|
2446
|
+
}, [open, isTopLayer, focusSegmentRef]);
|
|
2444
2447
|
const commitIfComplete = (next) => {
|
|
2445
2448
|
if (next.hour === null || next.minute === null) return;
|
|
2446
2449
|
let h24 = next.hour;
|
|
@@ -2462,9 +2465,9 @@ function TimePicker({
|
|
|
2462
2465
|
const applyTyped = (text) => {
|
|
2463
2466
|
const parsed2 = parseTypedTime(text, use24Hour);
|
|
2464
2467
|
if (!parsed2) return;
|
|
2465
|
-
const next = fromValue(parsed2);
|
|
2468
|
+
const next = fromValue(parsed2, use24Hour);
|
|
2466
2469
|
setSegs(next);
|
|
2467
|
-
|
|
2470
|
+
setBuffers({});
|
|
2468
2471
|
commitIfComplete(next);
|
|
2469
2472
|
};
|
|
2470
2473
|
const onSegmentKeyDown = (e, index, spec) => {
|
|
@@ -2489,33 +2492,34 @@ function TimePicker({
|
|
|
2489
2492
|
return;
|
|
2490
2493
|
}
|
|
2491
2494
|
const k = spec.key;
|
|
2495
|
+
const clearBuffer = () => setBuffers((b) => ({ ...b, [k]: "" }));
|
|
2492
2496
|
if (key === "ArrowUp" || key === "ArrowDown") {
|
|
2493
|
-
|
|
2497
|
+
clearBuffer();
|
|
2494
2498
|
setSegment(k, stepValue(spec, segs[k], key === "ArrowUp" ? 1 : -1));
|
|
2495
2499
|
return;
|
|
2496
2500
|
}
|
|
2497
2501
|
if (key === "Home") {
|
|
2498
|
-
|
|
2502
|
+
clearBuffer();
|
|
2499
2503
|
setSegment(k, spec.min);
|
|
2500
2504
|
return;
|
|
2501
2505
|
}
|
|
2502
2506
|
if (key === "End") {
|
|
2503
|
-
|
|
2507
|
+
clearBuffer();
|
|
2504
2508
|
setSegment(k, spec.max);
|
|
2505
2509
|
return;
|
|
2506
2510
|
}
|
|
2507
2511
|
if (key === "Backspace" || key === "Delete") {
|
|
2508
|
-
if (segs[k] === null && !buffers
|
|
2512
|
+
if (segs[k] === null && !buffers[k]) {
|
|
2509
2513
|
focusSegment(index - 1);
|
|
2510
2514
|
return;
|
|
2511
2515
|
}
|
|
2512
|
-
|
|
2516
|
+
clearBuffer();
|
|
2513
2517
|
setSegment(k, null, false);
|
|
2514
2518
|
return;
|
|
2515
2519
|
}
|
|
2516
2520
|
if (/^\d$/.test(key)) {
|
|
2517
|
-
const r = typeDigit(spec, buffers
|
|
2518
|
-
|
|
2521
|
+
const r = typeDigit(spec, buffers[k] ?? "", key);
|
|
2522
|
+
setBuffers((b) => ({ ...b, [k]: r.advance ? "" : r.buffer }));
|
|
2519
2523
|
setSegment(k, r.value);
|
|
2520
2524
|
if (r.advance) focusSegment(index + 1);
|
|
2521
2525
|
return;
|
|
@@ -2526,7 +2530,7 @@ function TimePicker({
|
|
|
2526
2530
|
}
|
|
2527
2531
|
};
|
|
2528
2532
|
const shown = (k, spec) => {
|
|
2529
|
-
const b = buffers
|
|
2533
|
+
const b = buffers[k];
|
|
2530
2534
|
if (b && segs[k] === null) return b;
|
|
2531
2535
|
return segs[k] === null ? spec.placeholder : pad2(segs[k]);
|
|
2532
2536
|
};
|
|
@@ -2585,7 +2589,7 @@ function TimePicker({
|
|
|
2585
2589
|
"aria-valuetext": segs[k] === null ? "empty" : pad2(segs[k]),
|
|
2586
2590
|
onKeyDown: (e) => onSegmentKeyDown(e, i, spec),
|
|
2587
2591
|
onClick: (e) => e.stopPropagation(),
|
|
2588
|
-
className: cn("rounded-[var(--ds-radius-sm)] px-[2px] outline-none focus:bg-[var(--ds-accent-subtle)] focus:text-[var(--ds-accent-primary)] cursor-text", segs[k] === null && !buffers
|
|
2592
|
+
className: cn("rounded-[var(--ds-radius-sm)] px-[2px] outline-none focus:bg-[var(--ds-accent-subtle)] focus:text-[var(--ds-accent-primary)] cursor-text", segs[k] === null && !buffers[k] && "text-[var(--ds-text-muted)]"),
|
|
2589
2593
|
children: shown(k, spec)
|
|
2590
2594
|
}
|
|
2591
2595
|
)
|