@aviala-design/spiral 2.6.0 → 2.6.2
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/component-changelogs.json +108 -8
- package/dist/index.cjs +370 -121
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +386 -137
- package/dist/index.js.map +1 -1
- package/dist/styles.css +231 -187
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -6304,7 +6304,70 @@ var import_react43 = require("react");
|
|
|
6304
6304
|
// src/lib/sticky-wheel.ts
|
|
6305
6305
|
var WHEEL_STICKY_AMOUNT = 0.45;
|
|
6306
6306
|
var WHEEL_SCROLL_END_MS = 100;
|
|
6307
|
-
var WHEEL_STEP_THRESHOLD_FACTOR = 0.
|
|
6307
|
+
var WHEEL_STEP_THRESHOLD_FACTOR = 0.35;
|
|
6308
|
+
var WHEEL_ACCUM_IDLE_MS = 140;
|
|
6309
|
+
var WHEEL_LOOP_SECTIONS = 3;
|
|
6310
|
+
var WHEEL_LOOP_MIDDLE_SECTION = 1;
|
|
6311
|
+
function consumeWheelSteps(accum, deltaY, threshold) {
|
|
6312
|
+
if (!(threshold > 0) || !Number.isFinite(deltaY) || deltaY === 0) {
|
|
6313
|
+
return { nextAccum: accum, steps: 0 };
|
|
6314
|
+
}
|
|
6315
|
+
let nextAccum = accum;
|
|
6316
|
+
if (nextAccum !== 0 && Math.sign(nextAccum) !== Math.sign(deltaY)) {
|
|
6317
|
+
nextAccum = 0;
|
|
6318
|
+
}
|
|
6319
|
+
nextAccum += deltaY;
|
|
6320
|
+
const steps = Math.trunc(nextAccum / threshold);
|
|
6321
|
+
if (steps === 0) {
|
|
6322
|
+
return { nextAccum, steps: 0 };
|
|
6323
|
+
}
|
|
6324
|
+
nextAccum -= steps * threshold;
|
|
6325
|
+
return { nextAccum, steps };
|
|
6326
|
+
}
|
|
6327
|
+
function normalizeWheelIndex(index, length) {
|
|
6328
|
+
if (length <= 0) return 0;
|
|
6329
|
+
return (index % length + length) % length;
|
|
6330
|
+
}
|
|
6331
|
+
function recenterLoopIndex(rawIndex, length, middleSection = WHEEL_LOOP_MIDDLE_SECTION) {
|
|
6332
|
+
if (length <= 0) return 0;
|
|
6333
|
+
return middleSection * length + normalizeWheelIndex(rawIndex, length);
|
|
6334
|
+
}
|
|
6335
|
+
function nearestLoopIndex(currentRawIndex, valueIndex, length, sections = WHEEL_LOOP_SECTIONS, preferDelta = 0) {
|
|
6336
|
+
if (length <= 0 || sections <= 0) return 0;
|
|
6337
|
+
const slot = normalizeWheelIndex(valueIndex, length);
|
|
6338
|
+
let best = slot;
|
|
6339
|
+
let bestScore = Number.POSITIVE_INFINITY;
|
|
6340
|
+
for (let section = 0; section < sections; section += 1) {
|
|
6341
|
+
const candidate = section * length + slot;
|
|
6342
|
+
const distance = Math.abs(candidate - currentRawIndex);
|
|
6343
|
+
const directionBias = preferDelta === 0 ? 0 : Math.sign(candidate - currentRawIndex) === Math.sign(preferDelta) ? -0.25 : 0.25;
|
|
6344
|
+
const score = distance + directionBias;
|
|
6345
|
+
if (score < bestScore) {
|
|
6346
|
+
bestScore = score;
|
|
6347
|
+
best = candidate;
|
|
6348
|
+
}
|
|
6349
|
+
}
|
|
6350
|
+
return best;
|
|
6351
|
+
}
|
|
6352
|
+
function stepLoopRawIndex(currentRawIndex, steps, length, sections = WHEEL_LOOP_SECTIONS, middleSection = WHEEL_LOOP_MIDDLE_SECTION) {
|
|
6353
|
+
if (length <= 0) return { rawIndex: 0, didRecenter: false };
|
|
6354
|
+
const maxIndex = sections * length - 1;
|
|
6355
|
+
let rawIndex = currentRawIndex;
|
|
6356
|
+
let didRecenter = false;
|
|
6357
|
+
const section = Math.floor(rawIndex / length);
|
|
6358
|
+
if (section !== middleSection) {
|
|
6359
|
+
rawIndex = recenterLoopIndex(rawIndex, length, middleSection);
|
|
6360
|
+
didRecenter = true;
|
|
6361
|
+
}
|
|
6362
|
+
const next = rawIndex + steps;
|
|
6363
|
+
if (next < 0 || next > maxIndex) {
|
|
6364
|
+
return {
|
|
6365
|
+
rawIndex: recenterLoopIndex(next, length, middleSection),
|
|
6366
|
+
didRecenter: true
|
|
6367
|
+
};
|
|
6368
|
+
}
|
|
6369
|
+
return { rawIndex: next, didRecenter };
|
|
6370
|
+
}
|
|
6308
6371
|
function stickyRemapProgress(t, amount) {
|
|
6309
6372
|
const clamped = Math.max(0, Math.min(1, t));
|
|
6310
6373
|
if (amount <= 0) return clamped;
|
|
@@ -6323,14 +6386,11 @@ function stickyRoundIndex(fractionalIndex, stickiness = WHEEL_STICKY_AMOUNT) {
|
|
|
6323
6386
|
|
|
6324
6387
|
// src/components/date-picker/date-picker-time-wheel.tsx
|
|
6325
6388
|
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
6326
|
-
var LOOP_SECTIONS =
|
|
6327
|
-
var MIDDLE_SECTION =
|
|
6389
|
+
var LOOP_SECTIONS = WHEEL_LOOP_SECTIONS;
|
|
6390
|
+
var MIDDLE_SECTION = WHEEL_LOOP_MIDDLE_SECTION;
|
|
6328
6391
|
function formatWheelValue(value) {
|
|
6329
6392
|
return String(value).padStart(2, "0");
|
|
6330
6393
|
}
|
|
6331
|
-
function normalizeIndex(index, length) {
|
|
6332
|
-
return (index % length + length) % length;
|
|
6333
|
-
}
|
|
6334
6394
|
function getValueIndex(values, value) {
|
|
6335
6395
|
const index = values.indexOf(value);
|
|
6336
6396
|
if (index >= 0) return index;
|
|
@@ -6368,17 +6428,28 @@ function DatePickerTimeWheelColumn({
|
|
|
6368
6428
|
layoutKey = 0
|
|
6369
6429
|
}) {
|
|
6370
6430
|
const scrollRef = (0, import_react43.useRef)(null);
|
|
6431
|
+
const clipTrackRef = (0, import_react43.useRef)(null);
|
|
6371
6432
|
const isUserScrollingRef = (0, import_react43.useRef)(false);
|
|
6372
6433
|
const isProgrammaticScrollRef = (0, import_react43.useRef)(false);
|
|
6373
6434
|
const smoothTargetValueRef = (0, import_react43.useRef)(null);
|
|
6374
6435
|
const scrollEndTimerRef = (0, import_react43.useRef)(void 0);
|
|
6375
6436
|
const wheelAccumRef = (0, import_react43.useRef)(0);
|
|
6437
|
+
const wheelAccumIdleTimerRef = (0, import_react43.useRef)(void 0);
|
|
6376
6438
|
const selectedValueRef = (0, import_react43.useRef)(value);
|
|
6377
6439
|
selectedValueRef.current = value;
|
|
6378
6440
|
const repeatedValues = (0, import_react43.useMemo)(() => {
|
|
6379
6441
|
if (!loop) return [...values];
|
|
6380
6442
|
return Array.from({ length: LOOP_SECTIONS }, () => values).flat();
|
|
6381
6443
|
}, [loop, values]);
|
|
6444
|
+
const readRawIndex2 = (0, import_react43.useCallback)((container, itemHeight) => {
|
|
6445
|
+
return Math.round(container.scrollTop / itemHeight);
|
|
6446
|
+
}, []);
|
|
6447
|
+
const syncClipTrack = (0, import_react43.useCallback)(() => {
|
|
6448
|
+
const container = scrollRef.current;
|
|
6449
|
+
const track = clipTrackRef.current;
|
|
6450
|
+
if (!container || !track) return;
|
|
6451
|
+
track.style.transform = `translate3d(0, ${-container.scrollTop}px, 0)`;
|
|
6452
|
+
}, []);
|
|
6382
6453
|
const scrollToIndex = (0, import_react43.useCallback)(
|
|
6383
6454
|
(index, behavior = "auto") => {
|
|
6384
6455
|
const container = scrollRef.current;
|
|
@@ -6387,21 +6458,13 @@ function DatePickerTimeWheelColumn({
|
|
|
6387
6458
|
if (itemHeight <= 0) return;
|
|
6388
6459
|
isProgrammaticScrollRef.current = true;
|
|
6389
6460
|
container.scrollTo({ top: index * itemHeight, behavior });
|
|
6390
|
-
|
|
6391
|
-
|
|
6392
|
-
);
|
|
6393
|
-
const scrollToValue = (0, import_react43.useCallback)(
|
|
6394
|
-
(nextValue, behavior = "smooth") => {
|
|
6395
|
-
if (behavior === "smooth") {
|
|
6396
|
-
smoothTargetValueRef.current = nextValue;
|
|
6461
|
+
if (behavior === "auto") {
|
|
6462
|
+
syncClipTrack();
|
|
6397
6463
|
} else {
|
|
6398
|
-
|
|
6464
|
+
requestAnimationFrame(syncClipTrack);
|
|
6399
6465
|
}
|
|
6400
|
-
const valueIndex = getValueIndex(values, nextValue);
|
|
6401
|
-
const targetIndex = loop ? values.length * MIDDLE_SECTION + valueIndex : valueIndex;
|
|
6402
|
-
scrollToIndex(targetIndex, behavior);
|
|
6403
6466
|
},
|
|
6404
|
-
[
|
|
6467
|
+
[syncClipTrack]
|
|
6405
6468
|
);
|
|
6406
6469
|
const syncScrollToValue = (0, import_react43.useCallback)(
|
|
6407
6470
|
(behavior = "auto") => {
|
|
@@ -6414,24 +6477,27 @@ function DatePickerTimeWheelColumn({
|
|
|
6414
6477
|
const itemHeight = readItemHeight(container);
|
|
6415
6478
|
if (itemHeight <= 0) return;
|
|
6416
6479
|
const valueIndex = getValueIndex(values, selectedValueRef.current);
|
|
6417
|
-
const
|
|
6418
|
-
const
|
|
6419
|
-
|
|
6420
|
-
|
|
6421
|
-
|
|
6480
|
+
const currentIndex = readRawIndex2(container, itemHeight);
|
|
6481
|
+
const currentValueIndex = loop ? normalizeWheelIndex(currentIndex, values.length) : Math.min(Math.max(currentIndex, 0), values.length - 1);
|
|
6482
|
+
if (currentValueIndex === valueIndex) {
|
|
6483
|
+
syncClipTrack();
|
|
6484
|
+
return;
|
|
6485
|
+
}
|
|
6486
|
+
const targetIndex = loop ? nearestLoopIndex(currentIndex, valueIndex, values.length, LOOP_SECTIONS) : valueIndex;
|
|
6422
6487
|
scrollToIndex(targetIndex, behavior);
|
|
6423
6488
|
},
|
|
6424
|
-
[loop, scrollToIndex, values]
|
|
6489
|
+
[loop, readRawIndex2, scrollToIndex, syncClipTrack, values]
|
|
6425
6490
|
);
|
|
6426
6491
|
const settleScroll = (0, import_react43.useCallback)(() => {
|
|
6427
6492
|
const container = scrollRef.current;
|
|
6428
|
-
if (!container
|
|
6429
|
-
|
|
6493
|
+
if (!container) return;
|
|
6494
|
+
const wasProgrammatic = isProgrammaticScrollRef.current;
|
|
6495
|
+
isProgrammaticScrollRef.current = false;
|
|
6496
|
+
const itemHeight = readItemHeight(container);
|
|
6497
|
+
if (itemHeight <= 0 || values.length === 0) {
|
|
6430
6498
|
smoothTargetValueRef.current = null;
|
|
6431
6499
|
return;
|
|
6432
6500
|
}
|
|
6433
|
-
const itemHeight = readItemHeight(container);
|
|
6434
|
-
if (itemHeight <= 0 || values.length === 0) return;
|
|
6435
6501
|
const lastIndex = values.length - 1;
|
|
6436
6502
|
const maxScroll = Math.max(container.scrollHeight - container.clientHeight, 0);
|
|
6437
6503
|
const fractionalIndex = container.scrollTop / itemHeight;
|
|
@@ -6443,12 +6509,21 @@ function DatePickerTimeWheelColumn({
|
|
|
6443
6509
|
rawIndex = 0;
|
|
6444
6510
|
}
|
|
6445
6511
|
}
|
|
6446
|
-
const valueIndex = loop ?
|
|
6512
|
+
const valueIndex = loop ? normalizeWheelIndex(rawIndex, values.length) : Math.min(Math.max(rawIndex, 0), lastIndex);
|
|
6447
6513
|
const nextValue = values[valueIndex];
|
|
6448
|
-
const targetIndex = loop ? values.length
|
|
6514
|
+
const targetIndex = loop ? recenterLoopIndex(rawIndex, values.length, MIDDLE_SECTION) : valueIndex;
|
|
6449
6515
|
const targetTop = targetIndex * itemHeight;
|
|
6450
6516
|
const section = loop ? Math.floor(rawIndex / values.length) : MIDDLE_SECTION;
|
|
6451
6517
|
const needsLoopRecenter = loop && section !== MIDDLE_SECTION;
|
|
6518
|
+
if (wasProgrammatic) {
|
|
6519
|
+
if (needsLoopRecenter) {
|
|
6520
|
+
isProgrammaticScrollRef.current = true;
|
|
6521
|
+
container.scrollTop = targetTop;
|
|
6522
|
+
}
|
|
6523
|
+
smoothTargetValueRef.current = null;
|
|
6524
|
+
syncClipTrack();
|
|
6525
|
+
return;
|
|
6526
|
+
}
|
|
6452
6527
|
const needsSnap = Math.abs(container.scrollTop - targetTop) > 1;
|
|
6453
6528
|
if (needsLoopRecenter) {
|
|
6454
6529
|
isProgrammaticScrollRef.current = true;
|
|
@@ -6466,22 +6541,26 @@ function DatePickerTimeWheelColumn({
|
|
|
6466
6541
|
}
|
|
6467
6542
|
isUserScrollingRef.current = false;
|
|
6468
6543
|
wheelAccumRef.current = 0;
|
|
6469
|
-
|
|
6544
|
+
syncClipTrack();
|
|
6545
|
+
}, [loop, onChange, syncClipTrack, values]);
|
|
6470
6546
|
const handleScroll = (0, import_react43.useCallback)(() => {
|
|
6547
|
+
syncClipTrack();
|
|
6471
6548
|
if (isProgrammaticScrollRef.current) return;
|
|
6472
6549
|
isUserScrollingRef.current = true;
|
|
6473
6550
|
if (scrollEndTimerRef.current) {
|
|
6474
6551
|
clearTimeout(scrollEndTimerRef.current);
|
|
6475
6552
|
}
|
|
6476
6553
|
scrollEndTimerRef.current = setTimeout(settleScroll, WHEEL_SCROLL_END_MS);
|
|
6477
|
-
}, [settleScroll]);
|
|
6554
|
+
}, [settleScroll, syncClipTrack]);
|
|
6478
6555
|
(0, import_react43.useLayoutEffect)(() => {
|
|
6479
6556
|
syncScrollToValue("auto");
|
|
6557
|
+
syncClipTrack();
|
|
6480
6558
|
const raf = requestAnimationFrame(() => {
|
|
6481
6559
|
syncScrollToValue("auto");
|
|
6560
|
+
syncClipTrack();
|
|
6482
6561
|
});
|
|
6483
6562
|
return () => cancelAnimationFrame(raf);
|
|
6484
|
-
}, [layoutKey, syncScrollToValue, value]);
|
|
6563
|
+
}, [layoutKey, syncClipTrack, syncScrollToValue, value]);
|
|
6485
6564
|
(0, import_react43.useEffect)(() => {
|
|
6486
6565
|
const container = scrollRef.current;
|
|
6487
6566
|
if (!container) return;
|
|
@@ -6509,23 +6588,48 @@ function DatePickerTimeWheelColumn({
|
|
|
6509
6588
|
if (!container) return;
|
|
6510
6589
|
const onWheel = (event) => {
|
|
6511
6590
|
event.preventDefault();
|
|
6512
|
-
if (
|
|
6591
|
+
if (values.length === 0) return;
|
|
6513
6592
|
const itemHeight = readItemHeight(container);
|
|
6514
6593
|
if (itemHeight <= 0) return;
|
|
6515
|
-
|
|
6594
|
+
if (wheelAccumIdleTimerRef.current) {
|
|
6595
|
+
clearTimeout(wheelAccumIdleTimerRef.current);
|
|
6596
|
+
}
|
|
6597
|
+
wheelAccumIdleTimerRef.current = setTimeout(() => {
|
|
6598
|
+
wheelAccumRef.current = 0;
|
|
6599
|
+
}, WHEEL_ACCUM_IDLE_MS);
|
|
6516
6600
|
const threshold = itemHeight * WHEEL_STEP_THRESHOLD_FACTOR;
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6601
|
+
const { nextAccum, steps } = consumeWheelSteps(
|
|
6602
|
+
wheelAccumRef.current,
|
|
6603
|
+
event.deltaY,
|
|
6604
|
+
threshold
|
|
6605
|
+
);
|
|
6606
|
+
wheelAccumRef.current = nextAccum;
|
|
6607
|
+
if (steps === 0) return;
|
|
6608
|
+
const currentRaw = readRawIndex2(container, itemHeight);
|
|
6609
|
+
let targetRaw;
|
|
6610
|
+
if (loop) {
|
|
6611
|
+
if (Math.floor(currentRaw / values.length) !== MIDDLE_SECTION) {
|
|
6612
|
+
isProgrammaticScrollRef.current = true;
|
|
6613
|
+
container.scrollTop = recenterLoopIndex(currentRaw, values.length, MIDDLE_SECTION) * itemHeight;
|
|
6614
|
+
}
|
|
6615
|
+
targetRaw = stepLoopRawIndex(
|
|
6616
|
+
readRawIndex2(container, itemHeight),
|
|
6617
|
+
steps,
|
|
6618
|
+
values.length,
|
|
6619
|
+
LOOP_SECTIONS,
|
|
6620
|
+
MIDDLE_SECTION
|
|
6621
|
+
).rawIndex;
|
|
6622
|
+
} else {
|
|
6623
|
+
targetRaw = Math.min(Math.max(currentRaw + steps, 0), values.length - 1);
|
|
6624
|
+
if (targetRaw === currentRaw) return;
|
|
6625
|
+
}
|
|
6523
6626
|
if (scrollEndTimerRef.current) {
|
|
6524
6627
|
clearTimeout(scrollEndTimerRef.current);
|
|
6525
6628
|
}
|
|
6526
|
-
const nextValue = values[
|
|
6629
|
+
const nextValue = values[normalizeWheelIndex(targetRaw, values.length)];
|
|
6527
6630
|
isUserScrollingRef.current = false;
|
|
6528
|
-
|
|
6631
|
+
smoothTargetValueRef.current = nextValue;
|
|
6632
|
+
scrollToIndex(targetRaw, "smooth");
|
|
6529
6633
|
if (nextValue !== selectedValueRef.current) {
|
|
6530
6634
|
onChange(nextValue);
|
|
6531
6635
|
}
|
|
@@ -6533,35 +6637,49 @@ function DatePickerTimeWheelColumn({
|
|
|
6533
6637
|
container.addEventListener("wheel", onWheel, { passive: false });
|
|
6534
6638
|
return () => {
|
|
6535
6639
|
container.removeEventListener("wheel", onWheel);
|
|
6640
|
+
if (wheelAccumIdleTimerRef.current) {
|
|
6641
|
+
clearTimeout(wheelAccumIdleTimerRef.current);
|
|
6642
|
+
}
|
|
6536
6643
|
};
|
|
6537
|
-
}, [loop, onChange,
|
|
6644
|
+
}, [loop, onChange, readRawIndex2, scrollToIndex, values]);
|
|
6538
6645
|
const handleKeyDown = (event) => {
|
|
6539
|
-
const
|
|
6540
|
-
|
|
6646
|
+
const container = scrollRef.current;
|
|
6647
|
+
if (!container || values.length === 0) return;
|
|
6648
|
+
const itemHeight = readItemHeight(container);
|
|
6649
|
+
if (itemHeight <= 0) return;
|
|
6650
|
+
const currentRaw = readRawIndex2(container, itemHeight);
|
|
6651
|
+
let targetRaw = null;
|
|
6652
|
+
let preferDelta = 0;
|
|
6541
6653
|
switch (event.key) {
|
|
6542
6654
|
case "ArrowUp":
|
|
6543
|
-
|
|
6655
|
+
preferDelta = -1;
|
|
6656
|
+
targetRaw = loop ? stepLoopRawIndex(currentRaw, -1, values.length, LOOP_SECTIONS, MIDDLE_SECTION).rawIndex : Math.max(currentRaw - 1, 0);
|
|
6544
6657
|
break;
|
|
6545
6658
|
case "ArrowDown":
|
|
6546
|
-
|
|
6659
|
+
preferDelta = 1;
|
|
6660
|
+
targetRaw = loop ? stepLoopRawIndex(currentRaw, 1, values.length, LOOP_SECTIONS, MIDDLE_SECTION).rawIndex : Math.min(currentRaw + 1, values.length - 1);
|
|
6547
6661
|
break;
|
|
6548
6662
|
case "Home":
|
|
6549
|
-
|
|
6663
|
+
targetRaw = loop ? nearestLoopIndex(currentRaw, 0, values.length, LOOP_SECTIONS, -1) : 0;
|
|
6550
6664
|
break;
|
|
6551
6665
|
case "End":
|
|
6552
|
-
|
|
6666
|
+
targetRaw = loop ? nearestLoopIndex(currentRaw, values.length - 1, values.length, LOOP_SECTIONS, 1) : values.length - 1;
|
|
6553
6667
|
break;
|
|
6554
6668
|
default:
|
|
6555
6669
|
return;
|
|
6556
6670
|
}
|
|
6557
6671
|
event.preventDefault();
|
|
6558
|
-
|
|
6559
|
-
|
|
6672
|
+
if (targetRaw === currentRaw && preferDelta === 0) return;
|
|
6673
|
+
const nextValue = values[normalizeWheelIndex(targetRaw, values.length)];
|
|
6674
|
+
isUserScrollingRef.current = false;
|
|
6675
|
+
smoothTargetValueRef.current = nextValue;
|
|
6676
|
+
scrollToIndex(targetRaw, "smooth");
|
|
6560
6677
|
if (nextValue !== selectedValueRef.current) {
|
|
6561
6678
|
onChange(nextValue);
|
|
6562
6679
|
}
|
|
6563
6680
|
};
|
|
6564
|
-
const
|
|
6681
|
+
const committedIndex = getValueIndex(values, value);
|
|
6682
|
+
const selectedOptionId = loop ? `${ariaLabel}-${value}-${values.length * MIDDLE_SECTION + committedIndex}` : `${ariaLabel}-${value}`;
|
|
6565
6683
|
return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: cn("aviala-datepicker-time__wheel", className), children: [
|
|
6566
6684
|
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "aviala-datepicker-time__wheel-highlight", "aria-hidden": true }),
|
|
6567
6685
|
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
@@ -6590,15 +6708,15 @@ function DatePickerTimeWheelColumn({
|
|
|
6590
6708
|
tabIndex: 0,
|
|
6591
6709
|
onKeyDown: handleKeyDown,
|
|
6592
6710
|
children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("ul", { className: "aviala-datepicker-time__wheel-list", children: repeatedValues.map((itemValue, index) => {
|
|
6593
|
-
const
|
|
6711
|
+
const isCommitted = itemValue === value;
|
|
6594
6712
|
const optionId = loop ? `${ariaLabel}-${itemValue}-${index}` : `${ariaLabel}-${itemValue}`;
|
|
6595
6713
|
return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
6596
6714
|
"li",
|
|
6597
6715
|
{
|
|
6598
|
-
id: optionId,
|
|
6716
|
+
id: loop && isCommitted && Math.floor(index / values.length) === MIDDLE_SECTION ? selectedOptionId : optionId,
|
|
6599
6717
|
className: "aviala-datepicker-time__wheel-item",
|
|
6600
6718
|
role: "option",
|
|
6601
|
-
"aria-selected":
|
|
6719
|
+
"aria-selected": isCommitted,
|
|
6602
6720
|
children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
6603
6721
|
"button",
|
|
6604
6722
|
{
|
|
@@ -6607,10 +6725,12 @@ function DatePickerTimeWheelColumn({
|
|
|
6607
6725
|
"aviala-datepicker-time__wheel-option",
|
|
6608
6726
|
typographyVariants({ level: "text" })
|
|
6609
6727
|
),
|
|
6610
|
-
"data-selected":
|
|
6728
|
+
"data-selected": isCommitted ? "true" : void 0,
|
|
6611
6729
|
tabIndex: -1,
|
|
6612
6730
|
onClick: () => {
|
|
6613
|
-
|
|
6731
|
+
isUserScrollingRef.current = false;
|
|
6732
|
+
smoothTargetValueRef.current = itemValue;
|
|
6733
|
+
scrollToIndex(index, "smooth");
|
|
6614
6734
|
if (itemValue !== selectedValueRef.current) {
|
|
6615
6735
|
onChange(itemValue);
|
|
6616
6736
|
}
|
|
@@ -6623,7 +6743,18 @@ function DatePickerTimeWheelColumn({
|
|
|
6623
6743
|
);
|
|
6624
6744
|
}) })
|
|
6625
6745
|
}
|
|
6626
|
-
)
|
|
6746
|
+
),
|
|
6747
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "aviala-datepicker-time__wheel-clip", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { ref: clipTrackRef, className: "aviala-datepicker-time__wheel-clip-track", children: repeatedValues.map((itemValue, index) => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
6748
|
+
"div",
|
|
6749
|
+
{
|
|
6750
|
+
className: cn(
|
|
6751
|
+
"aviala-datepicker-time__wheel-clip-item",
|
|
6752
|
+
typographyVariants({ level: "text" })
|
|
6753
|
+
),
|
|
6754
|
+
children: formatValue(itemValue)
|
|
6755
|
+
},
|
|
6756
|
+
`clip-${loop ? `${itemValue}-${index}` : itemValue}`
|
|
6757
|
+
)) }) })
|
|
6627
6758
|
] });
|
|
6628
6759
|
}
|
|
6629
6760
|
|
|
@@ -7359,8 +7490,8 @@ function DatePickerCalendar({ className }) {
|
|
|
7359
7490
|
const { code } = useLocale();
|
|
7360
7491
|
const locale = useLocaleMessages("DatePicker");
|
|
7361
7492
|
const rtl = useRtl();
|
|
7362
|
-
const PrevIcon = rtl ? import_icons12.
|
|
7363
|
-
const NextIcon = rtl ? import_icons12.
|
|
7493
|
+
const PrevIcon = rtl ? import_icons12.DirectionArrowRightLight : import_icons12.DirectionArrowLeftLight;
|
|
7494
|
+
const NextIcon = rtl ? import_icons12.DirectionArrowLeftLight : import_icons12.DirectionArrowRightLight;
|
|
7364
7495
|
const {
|
|
7365
7496
|
mode,
|
|
7366
7497
|
viewMonth,
|
|
@@ -7382,6 +7513,10 @@ function DatePickerCalendar({ className }) {
|
|
|
7382
7513
|
const dayGridRef = (0, import_react45.useRef)(null);
|
|
7383
7514
|
const pendingDayFocusRef = (0, import_react45.useRef)(false);
|
|
7384
7515
|
const [monthSlide, setMonthSlide] = (0, import_react45.useState)(null);
|
|
7516
|
+
const [prevNavAnimKey, setPrevNavAnimKey] = (0, import_react45.useState)(0);
|
|
7517
|
+
const [nextNavAnimKey, setNextNavAnimKey] = (0, import_react45.useState)(0);
|
|
7518
|
+
const [prevNavAnimating, setPrevNavAnimating] = (0, import_react45.useState)(false);
|
|
7519
|
+
const [nextNavAnimating, setNextNavAnimating] = (0, import_react45.useState)(false);
|
|
7385
7520
|
const isMonthPanel = activePanel === "month";
|
|
7386
7521
|
const isTimePanel = activePanel === "time" && enableTime;
|
|
7387
7522
|
const targetContentView = isMonthPanel ? "month" : "date";
|
|
@@ -7632,8 +7767,23 @@ function DatePickerCalendar({ className }) {
|
|
|
7632
7767
|
type: "button",
|
|
7633
7768
|
className: "aviala-datepicker-calendar__nav aviala-focus-ring",
|
|
7634
7769
|
"aria-label": isMonthPanel ? locale.previousYear : locale.previousMonth,
|
|
7635
|
-
onClick: () =>
|
|
7636
|
-
|
|
7770
|
+
onClick: () => {
|
|
7771
|
+
if (!prefersReducedMotion2()) {
|
|
7772
|
+
setPrevNavAnimKey((n) => n + 1);
|
|
7773
|
+
setPrevNavAnimating(true);
|
|
7774
|
+
}
|
|
7775
|
+
setViewMonth(addMonths(viewMonth, -stepMonth));
|
|
7776
|
+
},
|
|
7777
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "aviala-datepicker-calendar__nav-icon", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
7778
|
+
"span",
|
|
7779
|
+
{
|
|
7780
|
+
className: "aviala-datepicker-calendar__nav-icon-inner",
|
|
7781
|
+
"data-anim": prevNavAnimating ? "prev" : void 0,
|
|
7782
|
+
onAnimationEnd: () => setPrevNavAnimating(false),
|
|
7783
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(PrevIcon, { level: "text", biggerSize: true, "aria-hidden": true })
|
|
7784
|
+
},
|
|
7785
|
+
prevNavAnimKey
|
|
7786
|
+
) })
|
|
7637
7787
|
}
|
|
7638
7788
|
),
|
|
7639
7789
|
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
@@ -7666,8 +7816,23 @@ function DatePickerCalendar({ className }) {
|
|
|
7666
7816
|
type: "button",
|
|
7667
7817
|
className: "aviala-datepicker-calendar__nav aviala-focus-ring",
|
|
7668
7818
|
"aria-label": isMonthPanel ? locale.nextYear : locale.nextMonth,
|
|
7669
|
-
onClick: () =>
|
|
7670
|
-
|
|
7819
|
+
onClick: () => {
|
|
7820
|
+
if (!prefersReducedMotion2()) {
|
|
7821
|
+
setNextNavAnimKey((n) => n + 1);
|
|
7822
|
+
setNextNavAnimating(true);
|
|
7823
|
+
}
|
|
7824
|
+
setViewMonth(addMonths(viewMonth, stepMonth));
|
|
7825
|
+
},
|
|
7826
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "aviala-datepicker-calendar__nav-icon", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
7827
|
+
"span",
|
|
7828
|
+
{
|
|
7829
|
+
className: "aviala-datepicker-calendar__nav-icon-inner",
|
|
7830
|
+
"data-anim": nextNavAnimating ? "next" : void 0,
|
|
7831
|
+
onAnimationEnd: () => setNextNavAnimating(false),
|
|
7832
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(NextIcon, { level: "text", biggerSize: true, "aria-hidden": true })
|
|
7833
|
+
},
|
|
7834
|
+
nextNavAnimKey
|
|
7835
|
+
) })
|
|
7671
7836
|
}
|
|
7672
7837
|
)
|
|
7673
7838
|
] }),
|
|
@@ -10444,11 +10609,8 @@ Upload.displayName = "Upload";
|
|
|
10444
10609
|
// src/components/scroll-picker.tsx
|
|
10445
10610
|
var import_react60 = require("react");
|
|
10446
10611
|
var import_jsx_runtime57 = require("react/jsx-runtime");
|
|
10447
|
-
var LOOP_SECTIONS2 =
|
|
10448
|
-
var MIDDLE_SECTION2 =
|
|
10449
|
-
function normalizeIndex2(index, length) {
|
|
10450
|
-
return (index % length + length) % length;
|
|
10451
|
-
}
|
|
10612
|
+
var LOOP_SECTIONS2 = WHEEL_LOOP_SECTIONS;
|
|
10613
|
+
var MIDDLE_SECTION2 = WHEEL_LOOP_MIDDLE_SECTION;
|
|
10452
10614
|
function readMetrics(container) {
|
|
10453
10615
|
const items = container.querySelectorAll(".aviala-scroll-picker-item");
|
|
10454
10616
|
const first = items[0];
|
|
@@ -10470,6 +10632,9 @@ function scrollTopForIndex(container, metrics, index) {
|
|
|
10470
10632
|
function fractionalIndexAtScrollTop(metrics, scrollTop) {
|
|
10471
10633
|
return (scrollTop - metrics.originTop) / metrics.pitch;
|
|
10472
10634
|
}
|
|
10635
|
+
function readRawIndex(container, metrics) {
|
|
10636
|
+
return Math.round(fractionalIndexAtScrollTop(metrics, container.scrollTop));
|
|
10637
|
+
}
|
|
10473
10638
|
function ScrollPicker({ className, children, ...props }) {
|
|
10474
10639
|
return /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("div", { className: cn("aviala-scroll-picker", className), ...props, children });
|
|
10475
10640
|
}
|
|
@@ -10484,11 +10649,13 @@ function ScrollPickerColumn({
|
|
|
10484
10649
|
getValueKey
|
|
10485
10650
|
}) {
|
|
10486
10651
|
const scrollRef = (0, import_react60.useRef)(null);
|
|
10652
|
+
const clipTrackRef = (0, import_react60.useRef)(null);
|
|
10487
10653
|
const isUserScrollingRef = (0, import_react60.useRef)(false);
|
|
10488
10654
|
const isProgrammaticScrollRef = (0, import_react60.useRef)(false);
|
|
10489
10655
|
const smoothTargetValueRef = (0, import_react60.useRef)(null);
|
|
10490
10656
|
const scrollEndTimerRef = (0, import_react60.useRef)(void 0);
|
|
10491
10657
|
const wheelAccumRef = (0, import_react60.useRef)(0);
|
|
10658
|
+
const wheelAccumIdleTimerRef = (0, import_react60.useRef)(void 0);
|
|
10492
10659
|
const selectedValueRef = (0, import_react60.useRef)(value);
|
|
10493
10660
|
const reactId = (0, import_react60.useId)();
|
|
10494
10661
|
selectedValueRef.current = value;
|
|
@@ -10503,36 +10670,43 @@ function ScrollPickerColumn({
|
|
|
10503
10670
|
if (!loop) return [...values];
|
|
10504
10671
|
return Array.from({ length: LOOP_SECTIONS2 }, () => values).flat();
|
|
10505
10672
|
}, [loop, values]);
|
|
10506
|
-
const
|
|
10673
|
+
const syncClipTrack = (0, import_react60.useCallback)(() => {
|
|
10507
10674
|
const container = scrollRef.current;
|
|
10508
|
-
|
|
10509
|
-
|
|
10510
|
-
|
|
10511
|
-
|
|
10512
|
-
|
|
10675
|
+
const track = clipTrackRef.current;
|
|
10676
|
+
if (!container || !track) return;
|
|
10677
|
+
const band = container.parentElement?.querySelector(
|
|
10678
|
+
".aviala-scroll-picker-column__highlight"
|
|
10679
|
+
);
|
|
10680
|
+
const bandHeight = band?.offsetHeight || 26;
|
|
10681
|
+
const offset = container.scrollTop + (container.clientHeight - bandHeight) / 2;
|
|
10682
|
+
track.style.transform = `translate3d(0, ${-offset}px, 0)`;
|
|
10513
10683
|
}, []);
|
|
10514
|
-
const
|
|
10515
|
-
(
|
|
10516
|
-
|
|
10517
|
-
|
|
10684
|
+
const scrollToIndex = (0, import_react60.useCallback)(
|
|
10685
|
+
(index, behavior = "auto") => {
|
|
10686
|
+
const container = scrollRef.current;
|
|
10687
|
+
if (!container) return;
|
|
10688
|
+
const metrics = readMetrics(container);
|
|
10689
|
+
if (!metrics) return;
|
|
10690
|
+
isProgrammaticScrollRef.current = true;
|
|
10691
|
+
container.scrollTo({ top: scrollTopForIndex(container, metrics, index), behavior });
|
|
10692
|
+
if (behavior === "auto") {
|
|
10693
|
+
syncClipTrack();
|
|
10518
10694
|
} else {
|
|
10519
|
-
|
|
10695
|
+
requestAnimationFrame(syncClipTrack);
|
|
10520
10696
|
}
|
|
10521
|
-
const valueIndex = getIndex(nextValue);
|
|
10522
|
-
const targetIndex = loop ? values.length * MIDDLE_SECTION2 + valueIndex : valueIndex;
|
|
10523
|
-
scrollToIndex(targetIndex, behavior);
|
|
10524
10697
|
},
|
|
10525
|
-
[
|
|
10698
|
+
[syncClipTrack]
|
|
10526
10699
|
);
|
|
10527
10700
|
const settleScroll = (0, import_react60.useCallback)(() => {
|
|
10528
10701
|
const container = scrollRef.current;
|
|
10529
|
-
if (!container
|
|
10530
|
-
|
|
10702
|
+
if (!container) return;
|
|
10703
|
+
const wasProgrammatic = isProgrammaticScrollRef.current;
|
|
10704
|
+
isProgrammaticScrollRef.current = false;
|
|
10705
|
+
const metrics = readMetrics(container);
|
|
10706
|
+
if (!metrics || values.length === 0) {
|
|
10531
10707
|
smoothTargetValueRef.current = null;
|
|
10532
10708
|
return;
|
|
10533
10709
|
}
|
|
10534
|
-
const metrics = readMetrics(container);
|
|
10535
|
-
if (!metrics || values.length === 0) return;
|
|
10536
10710
|
const lastIndex = values.length - 1;
|
|
10537
10711
|
const maxScroll = Math.max(container.scrollHeight - container.clientHeight, 0);
|
|
10538
10712
|
let rawIndex = stickyRoundIndex(
|
|
@@ -10546,12 +10720,21 @@ function ScrollPickerColumn({
|
|
|
10546
10720
|
rawIndex = 0;
|
|
10547
10721
|
}
|
|
10548
10722
|
}
|
|
10549
|
-
const valueIndex = loop ?
|
|
10723
|
+
const valueIndex = loop ? normalizeWheelIndex(rawIndex, values.length) : Math.min(Math.max(rawIndex, 0), lastIndex);
|
|
10550
10724
|
const nextValue = values[valueIndex];
|
|
10551
|
-
const targetIndex = loop ? values.length
|
|
10725
|
+
const targetIndex = loop ? recenterLoopIndex(rawIndex, values.length, MIDDLE_SECTION2) : valueIndex;
|
|
10552
10726
|
const targetTop = scrollTopForIndex(container, metrics, targetIndex);
|
|
10553
10727
|
const section = loop ? Math.floor(rawIndex / values.length) : MIDDLE_SECTION2;
|
|
10554
10728
|
const needsLoopRecenter = loop && section !== MIDDLE_SECTION2;
|
|
10729
|
+
if (wasProgrammatic) {
|
|
10730
|
+
if (needsLoopRecenter) {
|
|
10731
|
+
isProgrammaticScrollRef.current = true;
|
|
10732
|
+
container.scrollTop = targetTop;
|
|
10733
|
+
}
|
|
10734
|
+
smoothTargetValueRef.current = null;
|
|
10735
|
+
syncClipTrack();
|
|
10736
|
+
return;
|
|
10737
|
+
}
|
|
10555
10738
|
const needsSnap = Math.abs(container.scrollTop - targetTop) > 1;
|
|
10556
10739
|
if (needsLoopRecenter) {
|
|
10557
10740
|
isProgrammaticScrollRef.current = true;
|
|
@@ -10569,15 +10752,17 @@ function ScrollPickerColumn({
|
|
|
10569
10752
|
}
|
|
10570
10753
|
isUserScrollingRef.current = false;
|
|
10571
10754
|
wheelAccumRef.current = 0;
|
|
10572
|
-
|
|
10755
|
+
syncClipTrack();
|
|
10756
|
+
}, [loop, onChange, syncClipTrack, values]);
|
|
10573
10757
|
const handleScroll = (0, import_react60.useCallback)(() => {
|
|
10758
|
+
syncClipTrack();
|
|
10574
10759
|
if (isProgrammaticScrollRef.current) return;
|
|
10575
10760
|
isUserScrollingRef.current = true;
|
|
10576
10761
|
if (scrollEndTimerRef.current) {
|
|
10577
10762
|
clearTimeout(scrollEndTimerRef.current);
|
|
10578
10763
|
}
|
|
10579
10764
|
scrollEndTimerRef.current = setTimeout(settleScroll, WHEEL_SCROLL_END_MS);
|
|
10580
|
-
}, [settleScroll]);
|
|
10765
|
+
}, [settleScroll, syncClipTrack]);
|
|
10581
10766
|
(0, import_react60.useLayoutEffect)(() => {
|
|
10582
10767
|
if (isUserScrollingRef.current) return;
|
|
10583
10768
|
if (smoothTargetValueRef.current != null && Object.is(smoothTargetValueRef.current, value)) {
|
|
@@ -10588,11 +10773,20 @@ function ScrollPickerColumn({
|
|
|
10588
10773
|
const metrics = readMetrics(container);
|
|
10589
10774
|
if (!metrics) return;
|
|
10590
10775
|
const valueIndex = getIndex(value);
|
|
10591
|
-
const
|
|
10776
|
+
const currentRaw = readRawIndex(container, metrics);
|
|
10777
|
+
const currentValueIndex = loop ? normalizeWheelIndex(currentRaw, values.length) : Math.min(Math.max(currentRaw, 0), values.length - 1);
|
|
10778
|
+
if (currentValueIndex === valueIndex) {
|
|
10779
|
+
syncClipTrack();
|
|
10780
|
+
return;
|
|
10781
|
+
}
|
|
10782
|
+
const targetIndex = loop ? nearestLoopIndex(currentRaw, valueIndex, values.length, LOOP_SECTIONS2) : valueIndex;
|
|
10592
10783
|
const targetTop = scrollTopForIndex(container, metrics, targetIndex);
|
|
10593
|
-
if (Math.abs(container.scrollTop - targetTop) <= 1)
|
|
10784
|
+
if (Math.abs(container.scrollTop - targetTop) <= 1) {
|
|
10785
|
+
syncClipTrack();
|
|
10786
|
+
return;
|
|
10787
|
+
}
|
|
10594
10788
|
scrollToIndex(targetIndex, "auto");
|
|
10595
|
-
}, [getIndex, loop, scrollToIndex, value, values.length]);
|
|
10789
|
+
}, [getIndex, loop, scrollToIndex, syncClipTrack, value, values.length]);
|
|
10596
10790
|
(0, import_react60.useEffect)(() => {
|
|
10597
10791
|
const container = scrollRef.current;
|
|
10598
10792
|
if (!container) return;
|
|
@@ -10615,23 +10809,52 @@ function ScrollPickerColumn({
|
|
|
10615
10809
|
if (!container) return;
|
|
10616
10810
|
const onWheel = (event) => {
|
|
10617
10811
|
event.preventDefault();
|
|
10618
|
-
if (
|
|
10812
|
+
if (values.length === 0) return;
|
|
10619
10813
|
const metrics = readMetrics(container);
|
|
10620
10814
|
if (!metrics) return;
|
|
10621
|
-
|
|
10815
|
+
if (wheelAccumIdleTimerRef.current) {
|
|
10816
|
+
clearTimeout(wheelAccumIdleTimerRef.current);
|
|
10817
|
+
}
|
|
10818
|
+
wheelAccumIdleTimerRef.current = setTimeout(() => {
|
|
10819
|
+
wheelAccumRef.current = 0;
|
|
10820
|
+
}, WHEEL_ACCUM_IDLE_MS);
|
|
10622
10821
|
const threshold = metrics.pitch * WHEEL_STEP_THRESHOLD_FACTOR;
|
|
10623
|
-
|
|
10624
|
-
|
|
10625
|
-
|
|
10626
|
-
|
|
10627
|
-
|
|
10628
|
-
|
|
10822
|
+
const { nextAccum, steps } = consumeWheelSteps(
|
|
10823
|
+
wheelAccumRef.current,
|
|
10824
|
+
event.deltaY,
|
|
10825
|
+
threshold
|
|
10826
|
+
);
|
|
10827
|
+
wheelAccumRef.current = nextAccum;
|
|
10828
|
+
if (steps === 0) return;
|
|
10829
|
+
const currentRaw = readRawIndex(container, metrics);
|
|
10830
|
+
let targetRaw;
|
|
10831
|
+
if (loop) {
|
|
10832
|
+
if (Math.floor(currentRaw / values.length) !== MIDDLE_SECTION2) {
|
|
10833
|
+
isProgrammaticScrollRef.current = true;
|
|
10834
|
+
container.scrollTop = scrollTopForIndex(
|
|
10835
|
+
container,
|
|
10836
|
+
metrics,
|
|
10837
|
+
recenterLoopIndex(currentRaw, values.length, MIDDLE_SECTION2)
|
|
10838
|
+
);
|
|
10839
|
+
}
|
|
10840
|
+
targetRaw = stepLoopRawIndex(
|
|
10841
|
+
readRawIndex(container, metrics),
|
|
10842
|
+
steps,
|
|
10843
|
+
values.length,
|
|
10844
|
+
LOOP_SECTIONS2,
|
|
10845
|
+
MIDDLE_SECTION2
|
|
10846
|
+
).rawIndex;
|
|
10847
|
+
} else {
|
|
10848
|
+
targetRaw = Math.min(Math.max(currentRaw + steps, 0), values.length - 1);
|
|
10849
|
+
if (targetRaw === currentRaw) return;
|
|
10850
|
+
}
|
|
10629
10851
|
if (scrollEndTimerRef.current) {
|
|
10630
10852
|
clearTimeout(scrollEndTimerRef.current);
|
|
10631
10853
|
}
|
|
10632
|
-
const nextValue = values[
|
|
10854
|
+
const nextValue = values[normalizeWheelIndex(targetRaw, values.length)];
|
|
10633
10855
|
isUserScrollingRef.current = false;
|
|
10634
|
-
|
|
10856
|
+
smoothTargetValueRef.current = nextValue;
|
|
10857
|
+
scrollToIndex(targetRaw, "smooth");
|
|
10635
10858
|
if (!Object.is(nextValue, selectedValueRef.current)) {
|
|
10636
10859
|
onChange(nextValue);
|
|
10637
10860
|
}
|
|
@@ -10639,30 +10862,39 @@ function ScrollPickerColumn({
|
|
|
10639
10862
|
container.addEventListener("wheel", onWheel, { passive: false });
|
|
10640
10863
|
return () => {
|
|
10641
10864
|
container.removeEventListener("wheel", onWheel);
|
|
10865
|
+
if (wheelAccumIdleTimerRef.current) {
|
|
10866
|
+
clearTimeout(wheelAccumIdleTimerRef.current);
|
|
10867
|
+
}
|
|
10642
10868
|
};
|
|
10643
|
-
}, [
|
|
10869
|
+
}, [loop, onChange, scrollToIndex, values]);
|
|
10644
10870
|
const handleKeyDown = (event) => {
|
|
10645
|
-
const
|
|
10646
|
-
|
|
10871
|
+
const container = scrollRef.current;
|
|
10872
|
+
if (!container || values.length === 0) return;
|
|
10873
|
+
const metrics = readMetrics(container);
|
|
10874
|
+
if (!metrics) return;
|
|
10875
|
+
const currentRaw = readRawIndex(container, metrics);
|
|
10876
|
+
let targetRaw = null;
|
|
10647
10877
|
switch (event.key) {
|
|
10648
10878
|
case "ArrowUp":
|
|
10649
|
-
|
|
10879
|
+
targetRaw = loop ? stepLoopRawIndex(currentRaw, -1, values.length, LOOP_SECTIONS2, MIDDLE_SECTION2).rawIndex : Math.max(currentRaw - 1, 0);
|
|
10650
10880
|
break;
|
|
10651
10881
|
case "ArrowDown":
|
|
10652
|
-
|
|
10882
|
+
targetRaw = loop ? stepLoopRawIndex(currentRaw, 1, values.length, LOOP_SECTIONS2, MIDDLE_SECTION2).rawIndex : Math.min(currentRaw + 1, values.length - 1);
|
|
10653
10883
|
break;
|
|
10654
10884
|
case "Home":
|
|
10655
|
-
|
|
10885
|
+
targetRaw = loop ? nearestLoopIndex(currentRaw, 0, values.length, LOOP_SECTIONS2, -1) : 0;
|
|
10656
10886
|
break;
|
|
10657
10887
|
case "End":
|
|
10658
|
-
|
|
10888
|
+
targetRaw = loop ? nearestLoopIndex(currentRaw, values.length - 1, values.length, LOOP_SECTIONS2, 1) : values.length - 1;
|
|
10659
10889
|
break;
|
|
10660
10890
|
default:
|
|
10661
10891
|
return;
|
|
10662
10892
|
}
|
|
10663
10893
|
event.preventDefault();
|
|
10664
|
-
const nextValue = values[
|
|
10665
|
-
|
|
10894
|
+
const nextValue = values[normalizeWheelIndex(targetRaw, values.length)];
|
|
10895
|
+
isUserScrollingRef.current = false;
|
|
10896
|
+
smoothTargetValueRef.current = nextValue;
|
|
10897
|
+
scrollToIndex(targetRaw, "smooth");
|
|
10666
10898
|
if (!Object.is(nextValue, selectedValueRef.current)) {
|
|
10667
10899
|
onChange(nextValue);
|
|
10668
10900
|
}
|
|
@@ -10682,17 +10914,20 @@ function ScrollPickerColumn({
|
|
|
10682
10914
|
tabIndex: 0,
|
|
10683
10915
|
onKeyDown: handleKeyDown,
|
|
10684
10916
|
children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("ul", { className: "aviala-scroll-picker-column__list", children: repeatedValues.map((itemValue, index) => {
|
|
10685
|
-
const
|
|
10686
|
-
const valueIndex = loop ?
|
|
10917
|
+
const isCommitted = Object.is(itemValue, value);
|
|
10918
|
+
const valueIndex = loop ? normalizeWheelIndex(index, values.length) : index;
|
|
10687
10919
|
const optionId = `${reactId}-${loop ? index : valueIndex}`;
|
|
10688
10920
|
const key = getValueKey?.(itemValue, index) ?? `${String(itemValue)}-${index}`;
|
|
10921
|
+
const useSelectedId = isCommitted && (!loop || Math.floor(index / values.length) === MIDDLE_SECTION2);
|
|
10689
10922
|
return /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("li", { className: "contents", children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
|
|
10690
10923
|
ScrollPickerItem,
|
|
10691
10924
|
{
|
|
10692
|
-
id:
|
|
10693
|
-
selected:
|
|
10925
|
+
id: useSelectedId ? selectedOptionId : optionId,
|
|
10926
|
+
selected: isCommitted,
|
|
10694
10927
|
onSelect: () => {
|
|
10695
|
-
|
|
10928
|
+
isUserScrollingRef.current = false;
|
|
10929
|
+
smoothTargetValueRef.current = itemValue;
|
|
10930
|
+
scrollToIndex(index, "smooth");
|
|
10696
10931
|
if (!Object.is(itemValue, selectedValueRef.current)) {
|
|
10697
10932
|
onChange(itemValue);
|
|
10698
10933
|
}
|
|
@@ -10702,7 +10937,21 @@ function ScrollPickerColumn({
|
|
|
10702
10937
|
) }, key);
|
|
10703
10938
|
}) })
|
|
10704
10939
|
}
|
|
10705
|
-
)
|
|
10940
|
+
),
|
|
10941
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)("div", { className: "aviala-scroll-picker-column__clip", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("div", { ref: clipTrackRef, className: "aviala-scroll-picker-column__clip-track", children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("div", { className: "aviala-scroll-picker-column__list", children: repeatedValues.map((itemValue, index) => {
|
|
10942
|
+
const key = getValueKey?.(itemValue, index) ?? `clip-${String(itemValue)}-${index}`;
|
|
10943
|
+
return /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
|
|
10944
|
+
"div",
|
|
10945
|
+
{
|
|
10946
|
+
className: cn(
|
|
10947
|
+
"aviala-scroll-picker-column__clip-item",
|
|
10948
|
+
typographyVariants({ level: "text" })
|
|
10949
|
+
),
|
|
10950
|
+
children: formatValue ? formatValue(itemValue) : String(itemValue)
|
|
10951
|
+
},
|
|
10952
|
+
key
|
|
10953
|
+
);
|
|
10954
|
+
}) }) }) })
|
|
10706
10955
|
] }) });
|
|
10707
10956
|
}
|
|
10708
10957
|
function ScrollPickerItem({
|