@rogieking/figui3 3.3.0 → 3.4.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/components.css +215 -0
- package/dist/fig.js +38 -38
- package/fig.js +551 -0
- package/package.json +1 -1
package/fig.js
CHANGED
|
@@ -10402,3 +10402,554 @@ class FigFillPicker extends HTMLElement {
|
|
|
10402
10402
|
}
|
|
10403
10403
|
}
|
|
10404
10404
|
customElements.define("fig-fill-picker", FigFillPicker);
|
|
10405
|
+
|
|
10406
|
+
/* Choice */
|
|
10407
|
+
/**
|
|
10408
|
+
* A generic choice container for use within FigChooser.
|
|
10409
|
+
* @attr {string} value - Identifier for this choice
|
|
10410
|
+
* @attr {boolean} selected - Whether this choice is currently selected
|
|
10411
|
+
* @attr {boolean} disabled - Whether this choice is disabled
|
|
10412
|
+
*/
|
|
10413
|
+
class FigChoice extends HTMLElement {
|
|
10414
|
+
static get observedAttributes() {
|
|
10415
|
+
return ["selected", "disabled"];
|
|
10416
|
+
}
|
|
10417
|
+
|
|
10418
|
+
connectedCallback() {
|
|
10419
|
+
this.setAttribute("role", "option");
|
|
10420
|
+
if (!this.hasAttribute("tabindex")) {
|
|
10421
|
+
this.setAttribute("tabindex", "0");
|
|
10422
|
+
}
|
|
10423
|
+
this.setAttribute(
|
|
10424
|
+
"aria-selected",
|
|
10425
|
+
this.hasAttribute("selected") ? "true" : "false",
|
|
10426
|
+
);
|
|
10427
|
+
if (this.hasAttribute("disabled")) {
|
|
10428
|
+
this.setAttribute("aria-disabled", "true");
|
|
10429
|
+
}
|
|
10430
|
+
}
|
|
10431
|
+
|
|
10432
|
+
attributeChangedCallback(name) {
|
|
10433
|
+
if (name === "selected") {
|
|
10434
|
+
this.setAttribute(
|
|
10435
|
+
"aria-selected",
|
|
10436
|
+
this.hasAttribute("selected") ? "true" : "false",
|
|
10437
|
+
);
|
|
10438
|
+
}
|
|
10439
|
+
if (name === "disabled") {
|
|
10440
|
+
const isDisabled =
|
|
10441
|
+
this.hasAttribute("disabled") &&
|
|
10442
|
+
this.getAttribute("disabled") !== "false";
|
|
10443
|
+
if (isDisabled) {
|
|
10444
|
+
this.setAttribute("aria-disabled", "true");
|
|
10445
|
+
this.setAttribute("tabindex", "-1");
|
|
10446
|
+
} else {
|
|
10447
|
+
this.removeAttribute("aria-disabled");
|
|
10448
|
+
this.setAttribute("tabindex", "0");
|
|
10449
|
+
}
|
|
10450
|
+
}
|
|
10451
|
+
}
|
|
10452
|
+
}
|
|
10453
|
+
customElements.define("fig-choice", FigChoice);
|
|
10454
|
+
|
|
10455
|
+
/* Chooser */
|
|
10456
|
+
/**
|
|
10457
|
+
* A selection controller for a list of choice elements.
|
|
10458
|
+
* @attr {string} choice-element - CSS selector for child choices (default: "fig-choice")
|
|
10459
|
+
* @attr {string} layout - Layout mode: "vertical" (default), "horizontal", "grid"
|
|
10460
|
+
* @attr {string} value - Value of the currently selected choice
|
|
10461
|
+
* @attr {boolean} disabled - Whether the chooser is disabled
|
|
10462
|
+
*/
|
|
10463
|
+
class FigChooser extends HTMLElement {
|
|
10464
|
+
#selectedChoice = null;
|
|
10465
|
+
#boundHandleClick = this.#handleClick.bind(this);
|
|
10466
|
+
#boundHandleKeyDown = this.#handleKeyDown.bind(this);
|
|
10467
|
+
#boundSyncOverflow = this.#syncOverflow.bind(this);
|
|
10468
|
+
#mutationObserver = null;
|
|
10469
|
+
#resizeObserver = null;
|
|
10470
|
+
#navStart = null;
|
|
10471
|
+
#navEnd = null;
|
|
10472
|
+
#dragState = null;
|
|
10473
|
+
|
|
10474
|
+
constructor() {
|
|
10475
|
+
super();
|
|
10476
|
+
}
|
|
10477
|
+
|
|
10478
|
+
static get observedAttributes() {
|
|
10479
|
+
return ["value", "disabled", "choice-element", "drag", "overflow", "loop"];
|
|
10480
|
+
}
|
|
10481
|
+
|
|
10482
|
+
get #overflowMode() {
|
|
10483
|
+
return this.getAttribute("overflow") === "scrollbar" ? "scrollbar" : "buttons";
|
|
10484
|
+
}
|
|
10485
|
+
|
|
10486
|
+
get #dragEnabled() {
|
|
10487
|
+
const attr = this.getAttribute("drag");
|
|
10488
|
+
return attr === null || attr !== "false";
|
|
10489
|
+
}
|
|
10490
|
+
|
|
10491
|
+
get #choiceSelector() {
|
|
10492
|
+
return this.getAttribute("choice-element") || "fig-choice";
|
|
10493
|
+
}
|
|
10494
|
+
|
|
10495
|
+
get choices() {
|
|
10496
|
+
return Array.from(this.querySelectorAll(this.#choiceSelector));
|
|
10497
|
+
}
|
|
10498
|
+
|
|
10499
|
+
get selectedChoice() {
|
|
10500
|
+
return this.#selectedChoice;
|
|
10501
|
+
}
|
|
10502
|
+
|
|
10503
|
+
set selectedChoice(element) {
|
|
10504
|
+
if (element && !this.contains(element)) return;
|
|
10505
|
+
const choices = this.choices;
|
|
10506
|
+
for (const choice of choices) {
|
|
10507
|
+
const shouldSelect = choice === element;
|
|
10508
|
+
const isSelected = choice.hasAttribute("selected");
|
|
10509
|
+
if (shouldSelect && !isSelected) {
|
|
10510
|
+
choice.setAttribute("selected", "");
|
|
10511
|
+
} else if (!shouldSelect && isSelected) {
|
|
10512
|
+
choice.removeAttribute("selected");
|
|
10513
|
+
}
|
|
10514
|
+
}
|
|
10515
|
+
this.#selectedChoice = element;
|
|
10516
|
+
const val = element?.getAttribute("value") ?? "";
|
|
10517
|
+
if (this.getAttribute("value") !== val) {
|
|
10518
|
+
if (val) {
|
|
10519
|
+
this.setAttribute("value", val);
|
|
10520
|
+
}
|
|
10521
|
+
}
|
|
10522
|
+
this.#scrollToChoice(element);
|
|
10523
|
+
}
|
|
10524
|
+
|
|
10525
|
+
get value() {
|
|
10526
|
+
return this.#selectedChoice?.getAttribute("value") ?? "";
|
|
10527
|
+
}
|
|
10528
|
+
|
|
10529
|
+
set value(val) {
|
|
10530
|
+
if (val === null || val === undefined || val === "") return;
|
|
10531
|
+
this.setAttribute("value", String(val));
|
|
10532
|
+
}
|
|
10533
|
+
|
|
10534
|
+
connectedCallback() {
|
|
10535
|
+
this.setAttribute("role", "listbox");
|
|
10536
|
+
this.addEventListener("click", this.#boundHandleClick);
|
|
10537
|
+
this.addEventListener("keydown", this.#boundHandleKeyDown);
|
|
10538
|
+
this.addEventListener("scroll", this.#boundSyncOverflow);
|
|
10539
|
+
this.#applyOverflowMode();
|
|
10540
|
+
this.#setupDrag();
|
|
10541
|
+
this.#startObserver();
|
|
10542
|
+
this.#startResizeObserver();
|
|
10543
|
+
|
|
10544
|
+
requestAnimationFrame(() => {
|
|
10545
|
+
this.#syncSelection();
|
|
10546
|
+
this.#syncOverflow();
|
|
10547
|
+
});
|
|
10548
|
+
}
|
|
10549
|
+
|
|
10550
|
+
disconnectedCallback() {
|
|
10551
|
+
this.removeEventListener("click", this.#boundHandleClick);
|
|
10552
|
+
this.removeEventListener("keydown", this.#boundHandleKeyDown);
|
|
10553
|
+
this.removeEventListener("scroll", this.#boundSyncOverflow);
|
|
10554
|
+
this.#teardownDrag();
|
|
10555
|
+
this.#mutationObserver?.disconnect();
|
|
10556
|
+
this.#mutationObserver = null;
|
|
10557
|
+
this.#resizeObserver?.disconnect();
|
|
10558
|
+
this.#resizeObserver = null;
|
|
10559
|
+
this.#removeNavButtons();
|
|
10560
|
+
}
|
|
10561
|
+
|
|
10562
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
10563
|
+
if (name === "value" && newValue !== oldValue && newValue) {
|
|
10564
|
+
this.#selectByValue(newValue);
|
|
10565
|
+
}
|
|
10566
|
+
if (name === "disabled") {
|
|
10567
|
+
const isDisabled = newValue !== null && newValue !== "false";
|
|
10568
|
+
const choices = this.choices;
|
|
10569
|
+
for (const choice of choices) {
|
|
10570
|
+
if (isDisabled) {
|
|
10571
|
+
choice.setAttribute("aria-disabled", "true");
|
|
10572
|
+
choice.setAttribute("tabindex", "-1");
|
|
10573
|
+
} else {
|
|
10574
|
+
choice.removeAttribute("aria-disabled");
|
|
10575
|
+
choice.setAttribute("tabindex", "0");
|
|
10576
|
+
}
|
|
10577
|
+
}
|
|
10578
|
+
}
|
|
10579
|
+
if (name === "choice-element") {
|
|
10580
|
+
requestAnimationFrame(() => this.#syncSelection());
|
|
10581
|
+
}
|
|
10582
|
+
if (name === "drag") {
|
|
10583
|
+
if (this.#dragEnabled) {
|
|
10584
|
+
this.#setupDrag();
|
|
10585
|
+
} else {
|
|
10586
|
+
this.#teardownDrag();
|
|
10587
|
+
}
|
|
10588
|
+
}
|
|
10589
|
+
if (name === "overflow") {
|
|
10590
|
+
this.#applyOverflowMode();
|
|
10591
|
+
}
|
|
10592
|
+
}
|
|
10593
|
+
|
|
10594
|
+
#syncSelection() {
|
|
10595
|
+
const choices = this.choices;
|
|
10596
|
+
if (!choices.length) {
|
|
10597
|
+
this.#selectedChoice = null;
|
|
10598
|
+
return;
|
|
10599
|
+
}
|
|
10600
|
+
|
|
10601
|
+
const valueAttr = this.getAttribute("value");
|
|
10602
|
+
if (valueAttr && this.#selectByValue(valueAttr)) return;
|
|
10603
|
+
|
|
10604
|
+
const alreadySelected = choices.find((c) => c.hasAttribute("selected"));
|
|
10605
|
+
if (alreadySelected) {
|
|
10606
|
+
this.selectedChoice = alreadySelected;
|
|
10607
|
+
return;
|
|
10608
|
+
}
|
|
10609
|
+
|
|
10610
|
+
this.selectedChoice = choices[0];
|
|
10611
|
+
}
|
|
10612
|
+
|
|
10613
|
+
#selectByValue(value) {
|
|
10614
|
+
const choices = this.choices;
|
|
10615
|
+
for (const choice of choices) {
|
|
10616
|
+
if (choice.getAttribute("value") === value) {
|
|
10617
|
+
this.selectedChoice = choice;
|
|
10618
|
+
return true;
|
|
10619
|
+
}
|
|
10620
|
+
}
|
|
10621
|
+
return false;
|
|
10622
|
+
}
|
|
10623
|
+
|
|
10624
|
+
#findChoiceFromTarget(target) {
|
|
10625
|
+
const selector = this.#choiceSelector;
|
|
10626
|
+
let el = target;
|
|
10627
|
+
while (el && el !== this) {
|
|
10628
|
+
if (el.matches(selector)) return el;
|
|
10629
|
+
el = el.parentElement;
|
|
10630
|
+
}
|
|
10631
|
+
return null;
|
|
10632
|
+
}
|
|
10633
|
+
|
|
10634
|
+
#handleClick(event) {
|
|
10635
|
+
if (
|
|
10636
|
+
this.hasAttribute("disabled") &&
|
|
10637
|
+
this.getAttribute("disabled") !== "false"
|
|
10638
|
+
)
|
|
10639
|
+
return;
|
|
10640
|
+
const choice = this.#findChoiceFromTarget(event.target);
|
|
10641
|
+
if (!choice) return;
|
|
10642
|
+
if (
|
|
10643
|
+
choice.hasAttribute("disabled") &&
|
|
10644
|
+
choice.getAttribute("disabled") !== "false"
|
|
10645
|
+
)
|
|
10646
|
+
return;
|
|
10647
|
+
this.selectedChoice = choice;
|
|
10648
|
+
this.#emitEvents();
|
|
10649
|
+
}
|
|
10650
|
+
|
|
10651
|
+
#handleKeyDown(event) {
|
|
10652
|
+
if (
|
|
10653
|
+
this.hasAttribute("disabled") &&
|
|
10654
|
+
this.getAttribute("disabled") !== "false"
|
|
10655
|
+
)
|
|
10656
|
+
return;
|
|
10657
|
+
const choices = this.choices.filter(
|
|
10658
|
+
(c) =>
|
|
10659
|
+
!c.hasAttribute("disabled") || c.getAttribute("disabled") === "false",
|
|
10660
|
+
);
|
|
10661
|
+
if (!choices.length) return;
|
|
10662
|
+
const currentIndex = choices.indexOf(this.#selectedChoice);
|
|
10663
|
+
let nextIndex = currentIndex;
|
|
10664
|
+
|
|
10665
|
+
const loop = this.hasAttribute("loop");
|
|
10666
|
+
|
|
10667
|
+
switch (event.key) {
|
|
10668
|
+
case "ArrowDown":
|
|
10669
|
+
case "ArrowRight":
|
|
10670
|
+
event.preventDefault();
|
|
10671
|
+
if (currentIndex < choices.length - 1) {
|
|
10672
|
+
nextIndex = currentIndex + 1;
|
|
10673
|
+
} else if (loop) {
|
|
10674
|
+
nextIndex = 0;
|
|
10675
|
+
}
|
|
10676
|
+
break;
|
|
10677
|
+
case "ArrowUp":
|
|
10678
|
+
case "ArrowLeft":
|
|
10679
|
+
event.preventDefault();
|
|
10680
|
+
if (currentIndex > 0) {
|
|
10681
|
+
nextIndex = currentIndex - 1;
|
|
10682
|
+
} else if (loop) {
|
|
10683
|
+
nextIndex = choices.length - 1;
|
|
10684
|
+
}
|
|
10685
|
+
break;
|
|
10686
|
+
case "Home":
|
|
10687
|
+
event.preventDefault();
|
|
10688
|
+
nextIndex = 0;
|
|
10689
|
+
break;
|
|
10690
|
+
case "End":
|
|
10691
|
+
event.preventDefault();
|
|
10692
|
+
nextIndex = choices.length - 1;
|
|
10693
|
+
break;
|
|
10694
|
+
case "Enter":
|
|
10695
|
+
case " ":
|
|
10696
|
+
event.preventDefault();
|
|
10697
|
+
if (document.activeElement?.matches(this.#choiceSelector)) {
|
|
10698
|
+
const focused = this.#findChoiceFromTarget(document.activeElement);
|
|
10699
|
+
if (focused && focused !== this.#selectedChoice) {
|
|
10700
|
+
this.selectedChoice = focused;
|
|
10701
|
+
this.#emitEvents();
|
|
10702
|
+
}
|
|
10703
|
+
}
|
|
10704
|
+
return;
|
|
10705
|
+
default:
|
|
10706
|
+
return;
|
|
10707
|
+
}
|
|
10708
|
+
|
|
10709
|
+
if (nextIndex !== currentIndex && choices[nextIndex]) {
|
|
10710
|
+
this.selectedChoice = choices[nextIndex];
|
|
10711
|
+
choices[nextIndex].focus();
|
|
10712
|
+
this.#emitEvents();
|
|
10713
|
+
}
|
|
10714
|
+
}
|
|
10715
|
+
|
|
10716
|
+
#emitEvents() {
|
|
10717
|
+
const val = this.value;
|
|
10718
|
+
this.dispatchEvent(
|
|
10719
|
+
new CustomEvent("input", { detail: val, bubbles: true }),
|
|
10720
|
+
);
|
|
10721
|
+
this.dispatchEvent(
|
|
10722
|
+
new CustomEvent("change", { detail: val, bubbles: true }),
|
|
10723
|
+
);
|
|
10724
|
+
}
|
|
10725
|
+
|
|
10726
|
+
#syncOverflow() {
|
|
10727
|
+
if (this.#overflowMode === "scrollbar") return;
|
|
10728
|
+
const isHorizontal = this.getAttribute("layout") === "horizontal";
|
|
10729
|
+
const threshold = 2;
|
|
10730
|
+
|
|
10731
|
+
if (isHorizontal) {
|
|
10732
|
+
const atStart = this.scrollLeft <= threshold;
|
|
10733
|
+
const atEnd = this.scrollLeft + this.clientWidth >= this.scrollWidth - threshold;
|
|
10734
|
+
this.classList.toggle("overflow-start", !atStart);
|
|
10735
|
+
this.classList.toggle("overflow-end", !atEnd);
|
|
10736
|
+
} else {
|
|
10737
|
+
const atStart = this.scrollTop <= threshold;
|
|
10738
|
+
const atEnd = this.scrollTop + this.clientHeight >= this.scrollHeight - threshold;
|
|
10739
|
+
this.classList.toggle("overflow-start", !atStart);
|
|
10740
|
+
this.classList.toggle("overflow-end", !atEnd);
|
|
10741
|
+
}
|
|
10742
|
+
}
|
|
10743
|
+
|
|
10744
|
+
#startResizeObserver() {
|
|
10745
|
+
this.#resizeObserver?.disconnect();
|
|
10746
|
+
this.#resizeObserver = new ResizeObserver(() => {
|
|
10747
|
+
this.#syncOverflow();
|
|
10748
|
+
});
|
|
10749
|
+
this.#resizeObserver.observe(this);
|
|
10750
|
+
}
|
|
10751
|
+
|
|
10752
|
+
#setupDrag() {
|
|
10753
|
+
if (this.#dragState?.bound) return;
|
|
10754
|
+
if (!this.#dragEnabled) return;
|
|
10755
|
+
|
|
10756
|
+
const onPointerDown = (e) => {
|
|
10757
|
+
if (e.button !== 0) return;
|
|
10758
|
+
const isHorizontal = this.getAttribute("layout") === "horizontal";
|
|
10759
|
+
const hasOverflow = isHorizontal
|
|
10760
|
+
? this.scrollWidth > this.clientWidth
|
|
10761
|
+
: this.scrollHeight > this.clientHeight;
|
|
10762
|
+
if (!hasOverflow) return;
|
|
10763
|
+
|
|
10764
|
+
this.#dragState.active = true;
|
|
10765
|
+
this.#dragState.didDrag = false;
|
|
10766
|
+
this.#dragState.startX = e.clientX;
|
|
10767
|
+
this.#dragState.startY = e.clientY;
|
|
10768
|
+
this.#dragState.scrollLeft = this.scrollLeft;
|
|
10769
|
+
this.#dragState.scrollTop = this.scrollTop;
|
|
10770
|
+
this.style.cursor = "grab";
|
|
10771
|
+
this.style.userSelect = "none";
|
|
10772
|
+
};
|
|
10773
|
+
|
|
10774
|
+
const onPointerMove = (e) => {
|
|
10775
|
+
if (!this.#dragState.active) return;
|
|
10776
|
+
const isHorizontal = this.getAttribute("layout") === "horizontal";
|
|
10777
|
+
const dx = e.clientX - this.#dragState.startX;
|
|
10778
|
+
const dy = e.clientY - this.#dragState.startY;
|
|
10779
|
+
|
|
10780
|
+
if (!this.#dragState.didDrag && Math.abs(isHorizontal ? dx : dy) > 3) {
|
|
10781
|
+
this.#dragState.didDrag = true;
|
|
10782
|
+
this.style.cursor = "grabbing";
|
|
10783
|
+
this.setPointerCapture(e.pointerId);
|
|
10784
|
+
}
|
|
10785
|
+
|
|
10786
|
+
if (!this.#dragState.didDrag) return;
|
|
10787
|
+
|
|
10788
|
+
if (isHorizontal) {
|
|
10789
|
+
this.scrollLeft = this.#dragState.scrollLeft - dx;
|
|
10790
|
+
} else {
|
|
10791
|
+
this.scrollTop = this.#dragState.scrollTop - dy;
|
|
10792
|
+
}
|
|
10793
|
+
};
|
|
10794
|
+
|
|
10795
|
+
const onPointerUp = (e) => {
|
|
10796
|
+
if (!this.#dragState.active) return;
|
|
10797
|
+
const wasDrag = this.#dragState.didDrag;
|
|
10798
|
+
this.#dragState.active = false;
|
|
10799
|
+
this.#dragState.didDrag = false;
|
|
10800
|
+
this.style.cursor = "";
|
|
10801
|
+
this.style.userSelect = "";
|
|
10802
|
+
if (e.pointerId !== undefined) {
|
|
10803
|
+
try { this.releasePointerCapture(e.pointerId); } catch {}
|
|
10804
|
+
}
|
|
10805
|
+
if (wasDrag) {
|
|
10806
|
+
e.preventDefault();
|
|
10807
|
+
e.stopPropagation();
|
|
10808
|
+
}
|
|
10809
|
+
};
|
|
10810
|
+
|
|
10811
|
+
const onClick = (e) => {
|
|
10812
|
+
if (this.#dragState?.suppressClick) {
|
|
10813
|
+
e.stopPropagation();
|
|
10814
|
+
e.preventDefault();
|
|
10815
|
+
this.#dragState.suppressClick = false;
|
|
10816
|
+
}
|
|
10817
|
+
};
|
|
10818
|
+
|
|
10819
|
+
const onPointerUpCapture = (e) => {
|
|
10820
|
+
if (this.#dragState?.didDrag) {
|
|
10821
|
+
this.#dragState.suppressClick = true;
|
|
10822
|
+
setTimeout(() => {
|
|
10823
|
+
if (this.#dragState) this.#dragState.suppressClick = false;
|
|
10824
|
+
}, 0);
|
|
10825
|
+
}
|
|
10826
|
+
};
|
|
10827
|
+
|
|
10828
|
+
this.#dragState = {
|
|
10829
|
+
active: false,
|
|
10830
|
+
didDrag: false,
|
|
10831
|
+
suppressClick: false,
|
|
10832
|
+
startX: 0,
|
|
10833
|
+
startY: 0,
|
|
10834
|
+
scrollLeft: 0,
|
|
10835
|
+
scrollTop: 0,
|
|
10836
|
+
bound: true,
|
|
10837
|
+
onPointerDown,
|
|
10838
|
+
onPointerMove,
|
|
10839
|
+
onPointerUp,
|
|
10840
|
+
onClick,
|
|
10841
|
+
onPointerUpCapture,
|
|
10842
|
+
};
|
|
10843
|
+
|
|
10844
|
+
this.addEventListener("pointerdown", onPointerDown);
|
|
10845
|
+
window.addEventListener("pointermove", onPointerMove);
|
|
10846
|
+
window.addEventListener("pointerup", onPointerUp);
|
|
10847
|
+
this.addEventListener("pointerup", onPointerUpCapture, true);
|
|
10848
|
+
this.addEventListener("click", onClick, true);
|
|
10849
|
+
}
|
|
10850
|
+
|
|
10851
|
+
#teardownDrag() {
|
|
10852
|
+
if (!this.#dragState?.bound) return;
|
|
10853
|
+
this.removeEventListener("pointerdown", this.#dragState.onPointerDown);
|
|
10854
|
+
window.removeEventListener("pointermove", this.#dragState.onPointerMove);
|
|
10855
|
+
window.removeEventListener("pointerup", this.#dragState.onPointerUp);
|
|
10856
|
+
this.removeEventListener("pointerup", this.#dragState.onPointerUpCapture, true);
|
|
10857
|
+
this.removeEventListener("click", this.#dragState.onClick, true);
|
|
10858
|
+
this.style.cursor = "";
|
|
10859
|
+
this.style.userSelect = "";
|
|
10860
|
+
this.#dragState = null;
|
|
10861
|
+
}
|
|
10862
|
+
|
|
10863
|
+
#applyOverflowMode() {
|
|
10864
|
+
if (this.#overflowMode === "scrollbar") {
|
|
10865
|
+
this.#removeNavButtons();
|
|
10866
|
+
} else {
|
|
10867
|
+
this.#createNavButtons();
|
|
10868
|
+
}
|
|
10869
|
+
}
|
|
10870
|
+
|
|
10871
|
+
#removeNavButtons() {
|
|
10872
|
+
this.#navStart?.remove();
|
|
10873
|
+
this.#navEnd?.remove();
|
|
10874
|
+
this.#navStart = null;
|
|
10875
|
+
this.#navEnd = null;
|
|
10876
|
+
this.classList.remove("overflow-start", "overflow-end");
|
|
10877
|
+
}
|
|
10878
|
+
|
|
10879
|
+
#createNavButtons() {
|
|
10880
|
+
if (this.#navStart) return;
|
|
10881
|
+
|
|
10882
|
+
this.#navStart = document.createElement("button");
|
|
10883
|
+
this.#navStart.className = "fig-chooser-nav-start";
|
|
10884
|
+
this.#navStart.setAttribute("tabindex", "-1");
|
|
10885
|
+
this.#navStart.setAttribute("aria-label", "Scroll back");
|
|
10886
|
+
|
|
10887
|
+
this.#navEnd = document.createElement("button");
|
|
10888
|
+
this.#navEnd.className = "fig-chooser-nav-end";
|
|
10889
|
+
this.#navEnd.setAttribute("tabindex", "-1");
|
|
10890
|
+
this.#navEnd.setAttribute("aria-label", "Scroll forward");
|
|
10891
|
+
|
|
10892
|
+
this.#navStart.addEventListener("pointerdown", (e) => {
|
|
10893
|
+
e.stopPropagation();
|
|
10894
|
+
this.#scrollByPage(-1);
|
|
10895
|
+
});
|
|
10896
|
+
|
|
10897
|
+
this.#navEnd.addEventListener("pointerdown", (e) => {
|
|
10898
|
+
e.stopPropagation();
|
|
10899
|
+
this.#scrollByPage(1);
|
|
10900
|
+
});
|
|
10901
|
+
|
|
10902
|
+
this.prepend(this.#navStart);
|
|
10903
|
+
this.append(this.#navEnd);
|
|
10904
|
+
}
|
|
10905
|
+
|
|
10906
|
+
#scrollByPage(direction) {
|
|
10907
|
+
const isHorizontal =
|
|
10908
|
+
this.getAttribute("layout") === "horizontal";
|
|
10909
|
+
const pageSize = isHorizontal ? this.clientWidth : this.clientHeight;
|
|
10910
|
+
const scrollAmount = pageSize * 0.8 * direction;
|
|
10911
|
+
|
|
10912
|
+
this.scrollBy({
|
|
10913
|
+
[isHorizontal ? "left" : "top"]: scrollAmount,
|
|
10914
|
+
behavior: "smooth",
|
|
10915
|
+
});
|
|
10916
|
+
}
|
|
10917
|
+
|
|
10918
|
+
#scrollToChoice(el) {
|
|
10919
|
+
if (!el) return;
|
|
10920
|
+
requestAnimationFrame(() => {
|
|
10921
|
+
const overflowY = this.scrollHeight > this.clientHeight;
|
|
10922
|
+
const overflowX = this.scrollWidth > this.clientWidth;
|
|
10923
|
+
if (!overflowX && !overflowY) return;
|
|
10924
|
+
|
|
10925
|
+
const options = { behavior: "smooth" };
|
|
10926
|
+
|
|
10927
|
+
if (overflowY) {
|
|
10928
|
+
const target = el.offsetTop - this.clientHeight / 2 + el.offsetHeight / 2;
|
|
10929
|
+
options.top = target;
|
|
10930
|
+
}
|
|
10931
|
+
|
|
10932
|
+
if (overflowX) {
|
|
10933
|
+
const target = el.offsetLeft - this.clientWidth / 2 + el.offsetWidth / 2;
|
|
10934
|
+
options.left = target;
|
|
10935
|
+
}
|
|
10936
|
+
|
|
10937
|
+
this.scrollTo(options);
|
|
10938
|
+
});
|
|
10939
|
+
}
|
|
10940
|
+
|
|
10941
|
+
#startObserver() {
|
|
10942
|
+
this.#mutationObserver?.disconnect();
|
|
10943
|
+
this.#mutationObserver = new MutationObserver(() => {
|
|
10944
|
+
const choices = this.choices;
|
|
10945
|
+
if (this.#selectedChoice && !choices.includes(this.#selectedChoice)) {
|
|
10946
|
+
this.#selectedChoice = null;
|
|
10947
|
+
this.#syncSelection();
|
|
10948
|
+
} else if (!this.#selectedChoice && choices.length) {
|
|
10949
|
+
this.#syncSelection();
|
|
10950
|
+
}
|
|
10951
|
+
});
|
|
10952
|
+
this.#mutationObserver.observe(this, { childList: true, subtree: true });
|
|
10953
|
+
}
|
|
10954
|
+
}
|
|
10955
|
+
customElements.define("fig-chooser", FigChooser);
|
package/package.json
CHANGED