@unabridged/midwest 0.8.1 → 0.9.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/README.md +5 -5
- package/app/assets/icons/available-icons.yml +11 -0
- package/app/assets/icons/loader-audio.svg +29 -0
- package/app/assets/icons/loader-ball-triangle.svg +46 -0
- package/app/assets/icons/loader-bars.svg +52 -0
- package/app/assets/icons/loader-circles.svg +20 -0
- package/app/assets/icons/loader-grid.svg +56 -0
- package/app/assets/icons/loader-oval.svg +17 -0
- package/app/assets/icons/loader-puff.svg +37 -0
- package/app/assets/icons/loader-rings.svg +42 -0
- package/app/assets/icons/loader-spinning-circles.svg +55 -0
- package/app/assets/icons/loader-tail-spin.svg +32 -0
- package/app/assets/icons/loader-three-dots.svg +33 -0
- package/app/assets/javascript/midwest/index.ts +2 -10
- package/app/assets/javascript/midwest.js +365 -81
- package/app/assets/javascript/midwest.js.map +1 -1
- package/app/assets/stylesheets/midwest/color-system.css +0 -2
- package/app/assets/stylesheets/midwest.css +1 -1
- package/app/assets/stylesheets/midwest.tailwind.css +3 -2
- package/dist/css/midwest.css +1 -1
- package/dist/javascript/collection/app/assets/javascript/midwest/index.js +2 -10
- package/dist/javascript/collection/app/assets/javascript/midwest/index.js.map +1 -1
- package/dist/javascript/collection/app/components/midwest/card_component/card_component_controller.js +10 -2
- package/dist/javascript/collection/app/components/midwest/card_component/card_component_controller.js.map +1 -1
- package/dist/javascript/collection/app/components/midwest/carousel_component/carousel_component_controller.js +62 -2
- package/dist/javascript/collection/app/components/midwest/carousel_component/carousel_component_controller.js.map +1 -1
- package/dist/javascript/collection/app/components/midwest/dialog_component/dialog_component_controller.js +58 -17
- package/dist/javascript/collection/app/components/midwest/dialog_component/dialog_component_controller.js.map +1 -1
- package/dist/javascript/collection/app/components/midwest/dropdown_component/dropdown_component_controller.js +56 -1
- package/dist/javascript/collection/app/components/midwest/dropdown_component/dropdown_component_controller.js.map +1 -1
- package/dist/javascript/collection/app/components/midwest/form/file_component/file_component_controller.js +49 -22
- package/dist/javascript/collection/app/components/midwest/form/file_component/file_component_controller.js.map +1 -1
- package/dist/javascript/collection/app/components/midwest/form/form_controller.js +36 -0
- package/dist/javascript/collection/app/components/midwest/form/form_controller.js.map +1 -0
- package/dist/javascript/collection/app/components/midwest/form/input_component/input_component_controller.js +48 -0
- package/dist/javascript/collection/app/components/midwest/form/input_component/input_component_controller.js.map +1 -1
- package/dist/javascript/collection/app/components/midwest/prose_component/prose_component_controller.js +2 -1
- package/dist/javascript/collection/app/components/midwest/prose_component/prose_component_controller.js.map +1 -1
- package/dist/javascript/collection/app/components/midwest/tabs_component/tabs_component_controller.js +32 -0
- package/dist/javascript/collection/app/components/midwest/tabs_component/tabs_component_controller.js.map +1 -1
- package/dist/javascript/collection/app/components/midwest/tooltip_component/tooltip_component_controller.js +15 -1
- package/dist/javascript/collection/app/components/midwest/tooltip_component/tooltip_component_controller.js.map +1 -1
- package/dist/javascript/midwest.d.ts +2 -0
- package/dist/javascript/midwest.js +365 -81
- package/dist/javascript/midwest.js.map +1 -1
- package/package.json +2 -1
|
@@ -494,9 +494,16 @@ class Card extends Controller {
|
|
|
494
494
|
originalHeight: { type: Number, default: 0 },
|
|
495
495
|
backHeight: { type: Number, default: 0 }
|
|
496
496
|
};
|
|
497
|
+
resizeHandler = null;
|
|
497
498
|
connect() {
|
|
498
499
|
this.applyReady();
|
|
499
500
|
}
|
|
501
|
+
disconnect() {
|
|
502
|
+
if (this.resizeHandler) {
|
|
503
|
+
window.removeEventListener("resize", this.resizeHandler);
|
|
504
|
+
this.resizeHandler = null;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
500
507
|
get flippable() {
|
|
501
508
|
return this.element.classList.contains("flippable");
|
|
502
509
|
}
|
|
@@ -505,10 +512,11 @@ class Card extends Controller {
|
|
|
505
512
|
this.originalHeightValue = this.frontTarget.offsetHeight;
|
|
506
513
|
this.backHeightValue = this.backTarget.offsetHeight;
|
|
507
514
|
this.element.classList.add("flip-ready");
|
|
508
|
-
|
|
515
|
+
this.resizeHandler = () => {
|
|
509
516
|
this.originalHeightValue = this.frontTarget.offsetHeight;
|
|
510
517
|
this.backHeightValue = this.backTarget.offsetHeight;
|
|
511
|
-
}
|
|
518
|
+
};
|
|
519
|
+
window.addEventListener("resize", this.resizeHandler);
|
|
512
520
|
}
|
|
513
521
|
}
|
|
514
522
|
originalHeightValueChanged() {
|
|
@@ -535,16 +543,18 @@ class Banner extends Controller {
|
|
|
535
543
|
}
|
|
536
544
|
}
|
|
537
545
|
|
|
538
|
-
class Callout extends Controller {
|
|
539
|
-
connect() {
|
|
540
|
-
console.log("Hello from Callout!");
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
|
|
544
546
|
class Input extends Controller {
|
|
545
547
|
static targets = ["input"];
|
|
548
|
+
updateClearButton = null;
|
|
546
549
|
connect() {
|
|
547
550
|
this.applyColor();
|
|
551
|
+
this.applySearchClear();
|
|
552
|
+
}
|
|
553
|
+
disconnect() {
|
|
554
|
+
if (this.updateClearButton) {
|
|
555
|
+
this.inputTarget.removeEventListener("input", this.updateClearButton);
|
|
556
|
+
this.updateClearButton = null;
|
|
557
|
+
}
|
|
548
558
|
}
|
|
549
559
|
applyColor() {
|
|
550
560
|
if (this.inputTarget.type !== "color") {
|
|
@@ -554,36 +564,103 @@ class Input extends Controller {
|
|
|
554
564
|
this.inputTarget.setAttribute("value", this.inputTarget.value);
|
|
555
565
|
});
|
|
556
566
|
}
|
|
567
|
+
applySearchClear() {
|
|
568
|
+
if (this.inputTarget.type !== "search")
|
|
569
|
+
return;
|
|
570
|
+
if (CSS.supports("selector(::-webkit-search-cancel-button)"))
|
|
571
|
+
return;
|
|
572
|
+
const button = document.createElement("button");
|
|
573
|
+
button.type = "button";
|
|
574
|
+
button.setAttribute("aria-label", "Clear search");
|
|
575
|
+
button.classList.add("midwest-input-search-clear");
|
|
576
|
+
button.hidden = this.inputTarget.value.length === 0;
|
|
577
|
+
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
578
|
+
svg.setAttribute("viewBox", "0 0 24 24");
|
|
579
|
+
svg.setAttribute("fill", "none");
|
|
580
|
+
svg.setAttribute("stroke", "currentColor");
|
|
581
|
+
svg.setAttribute("stroke-width", "2");
|
|
582
|
+
svg.setAttribute("aria-hidden", "true");
|
|
583
|
+
svg.setAttribute("width", "16");
|
|
584
|
+
svg.setAttribute("height", "16");
|
|
585
|
+
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
586
|
+
path.setAttribute("stroke-linecap", "round");
|
|
587
|
+
path.setAttribute("stroke-linejoin", "round");
|
|
588
|
+
path.setAttribute("d", "M6 18L18 6M6 6l12 12");
|
|
589
|
+
svg.appendChild(path);
|
|
590
|
+
button.appendChild(svg);
|
|
591
|
+
const wrapper = document.createElement("div");
|
|
592
|
+
wrapper.classList.add("midwest-input-search-wrapper");
|
|
593
|
+
this.inputTarget.parentElement?.insertBefore(wrapper, this.inputTarget);
|
|
594
|
+
wrapper.appendChild(this.inputTarget);
|
|
595
|
+
wrapper.appendChild(button);
|
|
596
|
+
this.updateClearButton = () => {
|
|
597
|
+
button.hidden = this.inputTarget.value.length === 0;
|
|
598
|
+
};
|
|
599
|
+
this.inputTarget.addEventListener("input", this.updateClearButton);
|
|
600
|
+
button.addEventListener("click", () => {
|
|
601
|
+
this.inputTarget.value = "";
|
|
602
|
+
button.hidden = true;
|
|
603
|
+
this.inputTarget.dispatchEvent(new Event("input", { bubbles: true }));
|
|
604
|
+
this.inputTarget.focus();
|
|
605
|
+
});
|
|
606
|
+
}
|
|
557
607
|
}
|
|
558
608
|
|
|
559
609
|
class File extends Controller {
|
|
560
610
|
static targets = ["input", "output"];
|
|
611
|
+
// Arrow function so `this` is stable for add/removeEventListener pairing.
|
|
612
|
+
handleChange = () => {
|
|
613
|
+
this.renderPreviews();
|
|
614
|
+
};
|
|
561
615
|
connect() {
|
|
562
|
-
this.inputTarget.addEventListener("change",
|
|
563
|
-
|
|
564
|
-
|
|
616
|
+
this.inputTarget.addEventListener("change", this.handleChange);
|
|
617
|
+
}
|
|
618
|
+
disconnect() {
|
|
619
|
+
this.inputTarget.removeEventListener("change", this.handleChange);
|
|
620
|
+
}
|
|
621
|
+
renderPreviews() {
|
|
622
|
+
this.outputTarget.innerHTML = "";
|
|
623
|
+
this.files.forEach((file) => {
|
|
624
|
+
this.outputTarget.appendChild(this.buildPreview(file));
|
|
565
625
|
});
|
|
566
626
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
627
|
+
buildPreview(file) {
|
|
628
|
+
const safeType = file.type ? file.type.replace("/", "-") : "unknown";
|
|
629
|
+
const div = document.createElement("div");
|
|
630
|
+
div.className = `file-preview file-preview-${safeType} layer-children`;
|
|
631
|
+
const typeBadge = document.createElement("span");
|
|
632
|
+
typeBadge.className = "midwest-badge size-default theme-primary";
|
|
633
|
+
const typeValue = document.createElement("span");
|
|
634
|
+
typeValue.className = "value";
|
|
635
|
+
typeValue.textContent = file.type || "Unknown";
|
|
636
|
+
typeBadge.appendChild(typeValue);
|
|
637
|
+
div.appendChild(typeBadge);
|
|
638
|
+
const sizeBadge = document.createElement("span");
|
|
639
|
+
sizeBadge.className = "midwest-badge size-default theme-gray ml-auto";
|
|
640
|
+
const sizeValue = document.createElement("span");
|
|
641
|
+
sizeValue.className = "value";
|
|
642
|
+
sizeValue.textContent = new Intl.NumberFormat(void 0, {
|
|
643
|
+
notation: "compact",
|
|
644
|
+
style: "unit",
|
|
645
|
+
unit: "byte",
|
|
646
|
+
unitDisplay: "narrow"
|
|
647
|
+
}).format(file.size);
|
|
648
|
+
sizeBadge.appendChild(sizeValue);
|
|
649
|
+
div.appendChild(sizeBadge);
|
|
650
|
+
if (file.type.startsWith("image/")) {
|
|
651
|
+
const img = document.createElement("img");
|
|
652
|
+
img.src = window.URL.createObjectURL(file);
|
|
653
|
+
div.appendChild(img);
|
|
654
|
+
} else {
|
|
655
|
+
const placeholder = document.createElement("div");
|
|
656
|
+
placeholder.className = "placeholder-file";
|
|
657
|
+
placeholder.innerHTML = file.type.startsWith("video/") ? this.videoIcon : this.fileIcon;
|
|
658
|
+
div.appendChild(placeholder);
|
|
659
|
+
}
|
|
660
|
+
return div;
|
|
586
661
|
}
|
|
662
|
+
fileIcon = '<svg xmlns="http://www.w3.org/2000/svg" class="ionicon" viewBox="0 0 512 512" aria-hidden="true"><path d="M416 221.25V416a48 48 0 01-48 48H144a48 48 0 01-48-48V96a48 48 0 0148-48h98.75a32 32 0 0122.62 9.37l141.26 141.26a32 32 0 019.37 22.62z" fill="none" stroke="currentColor" stroke-linejoin="round" stroke-width="32"/><path d="M256 56v120a32 32 0 0032 32h120" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"/></svg>';
|
|
663
|
+
videoIcon = '<svg xmlns="http://www.w3.org/2000/svg" class="ionicon" viewBox="0 0 512 512" aria-hidden="true"><path d="M374.79 308.78L457.5 367a16 16 0 0022.5-14.62V159.62A16 16 0 00457.5 145l-82.71 58.22A16 16 0 00368 216.3v79.4a16 16 0 006.79 13.08z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"/><path d="M268 384H84a52.15 52.15 0 01-52-52V180a52.15 52.15 0 0152-52h184.48A51.68 51.68 0 01320 179.52V332a52.15 52.15 0 01-52 52z" fill="none" stroke="currentColor" stroke-miterlimit="10" stroke-width="32"/></svg>';
|
|
587
664
|
get files() {
|
|
588
665
|
if (this.inputTarget.files == null) {
|
|
589
666
|
return [];
|
|
@@ -592,12 +669,6 @@ class File extends Controller {
|
|
|
592
669
|
}
|
|
593
670
|
}
|
|
594
671
|
|
|
595
|
-
class Switch extends Controller {
|
|
596
|
-
connect() {
|
|
597
|
-
console.log("Hello from Switch!");
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
|
|
601
672
|
/**
|
|
602
673
|
* Custom positioning reference element.
|
|
603
674
|
* @see https://floating-ui.com/docs/virtual-elements
|
|
@@ -2740,7 +2811,9 @@ class Dropdown extends Controller {
|
|
|
2740
2811
|
}
|
|
2741
2812
|
this.buttonEl.removeEventListener("click", this.unsupportedClickCB);
|
|
2742
2813
|
this.element.removeEventListener("toggle", this.addOpenedClass);
|
|
2814
|
+
this.element.removeEventListener("keydown", this.handleKeydown);
|
|
2743
2815
|
this.element.removeEventListener("turbo:click", this.closeIfOpen);
|
|
2816
|
+
this.element.removeEventListener("popover:close", this.closeIfOpen);
|
|
2744
2817
|
}
|
|
2745
2818
|
unsupportedClickCB = (event) => {
|
|
2746
2819
|
event.preventDefault();
|
|
@@ -2756,8 +2829,61 @@ class Dropdown extends Controller {
|
|
|
2756
2829
|
return this.computedValue || !this.supportedValue;
|
|
2757
2830
|
}
|
|
2758
2831
|
addOpenedClass = (event) => {
|
|
2759
|
-
|
|
2832
|
+
const isOpen = event.newState === "open";
|
|
2833
|
+
this.buttonEl.classList.toggle("opened", isOpen);
|
|
2834
|
+
this.buttonEl.setAttribute("aria-expanded", String(isOpen));
|
|
2835
|
+
if (isOpen) {
|
|
2836
|
+
this.element.addEventListener("keydown", this.handleKeydown);
|
|
2837
|
+
requestAnimationFrame(() => this.menuItems[0]?.focus());
|
|
2838
|
+
} else {
|
|
2839
|
+
this.element.removeEventListener("keydown", this.handleKeydown);
|
|
2840
|
+
}
|
|
2841
|
+
};
|
|
2842
|
+
handleKeydown = (event) => {
|
|
2843
|
+
const items = this.menuItems;
|
|
2844
|
+
if (items.length === 0)
|
|
2845
|
+
return;
|
|
2846
|
+
const currentIndex = items.findIndex((item) => item === document.activeElement);
|
|
2847
|
+
switch (event.key) {
|
|
2848
|
+
case "ArrowDown": {
|
|
2849
|
+
event.preventDefault();
|
|
2850
|
+
const next = currentIndex < items.length - 1 ? currentIndex + 1 : 0;
|
|
2851
|
+
items[next].focus();
|
|
2852
|
+
break;
|
|
2853
|
+
}
|
|
2854
|
+
case "ArrowUp": {
|
|
2855
|
+
event.preventDefault();
|
|
2856
|
+
const prev = currentIndex > 0 ? currentIndex - 1 : items.length - 1;
|
|
2857
|
+
items[prev].focus();
|
|
2858
|
+
break;
|
|
2859
|
+
}
|
|
2860
|
+
case "Home":
|
|
2861
|
+
event.preventDefault();
|
|
2862
|
+
items[0].focus();
|
|
2863
|
+
break;
|
|
2864
|
+
case "End":
|
|
2865
|
+
event.preventDefault();
|
|
2866
|
+
items[items.length - 1].focus();
|
|
2867
|
+
break;
|
|
2868
|
+
case "Escape":
|
|
2869
|
+
event.preventDefault();
|
|
2870
|
+
this.element.hidePopover();
|
|
2871
|
+
this.buttonEl.focus();
|
|
2872
|
+
break;
|
|
2873
|
+
case "Tab":
|
|
2874
|
+
this.element.hidePopover();
|
|
2875
|
+
break;
|
|
2876
|
+
default:
|
|
2877
|
+
return;
|
|
2878
|
+
}
|
|
2760
2879
|
};
|
|
2880
|
+
get menuItems() {
|
|
2881
|
+
return Array.from(
|
|
2882
|
+
this.element.querySelectorAll(
|
|
2883
|
+
'[role="menuitem"]:not([disabled]):not([aria-disabled="true"])'
|
|
2884
|
+
)
|
|
2885
|
+
);
|
|
2886
|
+
}
|
|
2761
2887
|
computedCB(entries) {
|
|
2762
2888
|
entries.forEach(async () => {
|
|
2763
2889
|
this.computation();
|
|
@@ -2801,12 +2927,6 @@ class Dropdown extends Controller {
|
|
|
2801
2927
|
}
|
|
2802
2928
|
}
|
|
2803
2929
|
|
|
2804
|
-
class Avatar extends Controller {
|
|
2805
|
-
connect() {
|
|
2806
|
-
console.log("Hello from Avatar!");
|
|
2807
|
-
}
|
|
2808
|
-
}
|
|
2809
|
-
|
|
2810
2930
|
class Prose extends Controller {
|
|
2811
2931
|
static values = {
|
|
2812
2932
|
initial: { type: Number, default: 1 },
|
|
@@ -2816,7 +2936,8 @@ class Prose extends Controller {
|
|
|
2816
2936
|
if (!this.element.classList.contains("clamp")) {
|
|
2817
2937
|
return;
|
|
2818
2938
|
}
|
|
2819
|
-
|
|
2939
|
+
const clamp = parseInt(getComputedStyle(this.element).getPropertyValue("--clamp"), 10);
|
|
2940
|
+
this.initialValue = isNaN(clamp) ? 1 : clamp;
|
|
2820
2941
|
this.element.addEventListener("click", () => {
|
|
2821
2942
|
this.toggle();
|
|
2822
2943
|
});
|
|
@@ -2836,9 +2957,19 @@ class Dialog extends Controller {
|
|
|
2836
2957
|
static values = {
|
|
2837
2958
|
resetFrame: { type: Boolean, default: false }
|
|
2838
2959
|
};
|
|
2960
|
+
_previousFocus = null;
|
|
2839
2961
|
connect() {
|
|
2840
|
-
document.addEventListener("turbo:frame-load", this.
|
|
2841
|
-
this.
|
|
2962
|
+
document.addEventListener("turbo:frame-load", this.bindToggleEls);
|
|
2963
|
+
document.addEventListener("turbo:before-visit", this.dismiss);
|
|
2964
|
+
document.addEventListener("click", this.handleClickOutside);
|
|
2965
|
+
this.dialog.addEventListener("close", this.onClose);
|
|
2966
|
+
this.bindToggleEls();
|
|
2967
|
+
}
|
|
2968
|
+
disconnect() {
|
|
2969
|
+
document.removeEventListener("turbo:frame-load", this.bindToggleEls);
|
|
2970
|
+
document.removeEventListener("turbo:before-visit", this.dismiss);
|
|
2971
|
+
document.removeEventListener("click", this.handleClickOutside);
|
|
2972
|
+
this.dialog.removeEventListener("close", this.onClose);
|
|
2842
2973
|
}
|
|
2843
2974
|
get dialog() {
|
|
2844
2975
|
return this.element;
|
|
@@ -2852,20 +2983,18 @@ class Dialog extends Controller {
|
|
|
2852
2983
|
get frameEl() {
|
|
2853
2984
|
return this.dialog.querySelector("turbo-frame");
|
|
2854
2985
|
}
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
document.addEventListener("turbo:before-visit", this.dismiss.bind(this));
|
|
2986
|
+
// Arrow function so `this` is stable for add/removeEventListener pairing.
|
|
2987
|
+
handleClickOutside = (event) => {
|
|
2988
|
+
if (event.target !== this.dialog)
|
|
2989
|
+
return;
|
|
2990
|
+
this.dismiss();
|
|
2991
|
+
};
|
|
2992
|
+
// Arrow function so `this` is stable for add/removeEventListener pairing.
|
|
2993
|
+
bindToggleEls = () => {
|
|
2864
2994
|
this.toggleEls.forEach((el) => {
|
|
2865
2995
|
el.addEventListener("click", this.toggle.bind(this));
|
|
2866
2996
|
});
|
|
2867
|
-
|
|
2868
|
-
}
|
|
2997
|
+
};
|
|
2869
2998
|
remoteContentTargetConnected() {
|
|
2870
2999
|
this.show();
|
|
2871
3000
|
}
|
|
@@ -2876,32 +3005,65 @@ class Dialog extends Controller {
|
|
|
2876
3005
|
if (this.dialog.open) {
|
|
2877
3006
|
this.dialog.close();
|
|
2878
3007
|
} else {
|
|
3008
|
+
this._previousFocus = document.activeElement;
|
|
2879
3009
|
this.dialog.showModal();
|
|
3010
|
+
this.dialog.addEventListener("keydown", this.handleFocusTrap);
|
|
2880
3011
|
}
|
|
2881
3012
|
}
|
|
2882
3013
|
show(e = void 0) {
|
|
2883
3014
|
if (e instanceof Event)
|
|
2884
3015
|
e.preventDefault();
|
|
2885
3016
|
this.htmlEl.classList.toggle("overflow-hidden", true);
|
|
3017
|
+
this._previousFocus = document.activeElement;
|
|
2886
3018
|
this.dialog.showModal();
|
|
3019
|
+
this.dialog.addEventListener("keydown", this.handleFocusTrap);
|
|
2887
3020
|
}
|
|
2888
3021
|
hide({ detail }) {
|
|
2889
3022
|
if (!detail.success)
|
|
2890
3023
|
return;
|
|
2891
3024
|
this.dismiss();
|
|
2892
3025
|
}
|
|
2893
|
-
|
|
3026
|
+
// Arrow function so `this` is stable for add/removeEventListener pairing.
|
|
3027
|
+
dismiss = () => {
|
|
2894
3028
|
this.dialog.close();
|
|
2895
|
-
}
|
|
3029
|
+
};
|
|
2896
3030
|
reloadPage() {
|
|
2897
3031
|
window.location.reload();
|
|
2898
3032
|
}
|
|
2899
|
-
|
|
3033
|
+
handleFocusTrap = (event) => {
|
|
3034
|
+
if (event.key !== "Tab")
|
|
3035
|
+
return;
|
|
3036
|
+
const focusable = Array.from(
|
|
3037
|
+
this.dialog.querySelectorAll(
|
|
3038
|
+
'a[href], button:not([disabled]), textarea, input, select, [tabindex]:not([tabindex="-1"])'
|
|
3039
|
+
)
|
|
3040
|
+
).filter((el) => !el.closest("[inert]"));
|
|
3041
|
+
if (focusable.length === 0)
|
|
3042
|
+
return;
|
|
3043
|
+
const first = focusable[0];
|
|
3044
|
+
const last = focusable[focusable.length - 1];
|
|
3045
|
+
if (event.shiftKey) {
|
|
3046
|
+
if (document.activeElement === first) {
|
|
3047
|
+
event.preventDefault();
|
|
3048
|
+
last.focus();
|
|
3049
|
+
}
|
|
3050
|
+
} else {
|
|
3051
|
+
if (document.activeElement === last) {
|
|
3052
|
+
event.preventDefault();
|
|
3053
|
+
first.focus();
|
|
3054
|
+
}
|
|
3055
|
+
}
|
|
3056
|
+
};
|
|
3057
|
+
// Arrow function so `this` is stable for add/removeEventListener pairing.
|
|
3058
|
+
onClose = async () => {
|
|
3059
|
+
this.dialog.removeEventListener("keydown", this.handleFocusTrap);
|
|
3060
|
+
this._previousFocus?.focus();
|
|
3061
|
+
this._previousFocus = null;
|
|
2900
3062
|
this.htmlEl.classList.toggle("overflow-hidden", false);
|
|
2901
3063
|
await this.delay(500);
|
|
2902
3064
|
this.resetForm();
|
|
2903
3065
|
this.resetFrame();
|
|
2904
|
-
}
|
|
3066
|
+
};
|
|
2905
3067
|
resetFrame() {
|
|
2906
3068
|
if (!this.resetFrameValue || this.frameEl == null)
|
|
2907
3069
|
return;
|
|
@@ -2920,18 +3082,6 @@ class Dialog extends Controller {
|
|
|
2920
3082
|
}
|
|
2921
3083
|
}
|
|
2922
3084
|
|
|
2923
|
-
class Video extends Controller {
|
|
2924
|
-
connect() {
|
|
2925
|
-
console.log("Hello from Video!");
|
|
2926
|
-
}
|
|
2927
|
-
}
|
|
2928
|
-
|
|
2929
|
-
class DropdownItem extends Controller {
|
|
2930
|
-
connect() {
|
|
2931
|
-
console.log("Hello from DropdownItem!");
|
|
2932
|
-
}
|
|
2933
|
-
}
|
|
2934
|
-
|
|
2935
3085
|
class Tabs extends Controller {
|
|
2936
3086
|
static targets = ["tab", "panel"];
|
|
2937
3087
|
connect() {
|
|
@@ -2939,6 +3089,10 @@ class Tabs extends Controller {
|
|
|
2939
3089
|
(tab) => tab.getAttribute("aria-selected") === "true"
|
|
2940
3090
|
);
|
|
2941
3091
|
this.activateTab(activeIndex >= 0 ? activeIndex : 0);
|
|
3092
|
+
this.element.addEventListener("keydown", this.handleKeydown);
|
|
3093
|
+
}
|
|
3094
|
+
disconnect() {
|
|
3095
|
+
this.element.removeEventListener("keydown", this.handleKeydown);
|
|
2942
3096
|
}
|
|
2943
3097
|
select(event) {
|
|
2944
3098
|
const tab = event.currentTarget;
|
|
@@ -2946,6 +3100,34 @@ class Tabs extends Controller {
|
|
|
2946
3100
|
if (index >= 0)
|
|
2947
3101
|
this.activateTab(index);
|
|
2948
3102
|
}
|
|
3103
|
+
handleKeydown = (event) => {
|
|
3104
|
+
const tabs = this.tabTargets;
|
|
3105
|
+
if (tabs.length === 0)
|
|
3106
|
+
return;
|
|
3107
|
+
const currentIndex = tabs.findIndex((t) => t === document.activeElement);
|
|
3108
|
+
if (currentIndex < 0)
|
|
3109
|
+
return;
|
|
3110
|
+
let nextIndex;
|
|
3111
|
+
switch (event.key) {
|
|
3112
|
+
case "ArrowLeft":
|
|
3113
|
+
nextIndex = (currentIndex - 1 + tabs.length) % tabs.length;
|
|
3114
|
+
break;
|
|
3115
|
+
case "ArrowRight":
|
|
3116
|
+
nextIndex = (currentIndex + 1) % tabs.length;
|
|
3117
|
+
break;
|
|
3118
|
+
case "Home":
|
|
3119
|
+
nextIndex = 0;
|
|
3120
|
+
break;
|
|
3121
|
+
case "End":
|
|
3122
|
+
nextIndex = tabs.length - 1;
|
|
3123
|
+
break;
|
|
3124
|
+
default:
|
|
3125
|
+
return;
|
|
3126
|
+
}
|
|
3127
|
+
event.preventDefault();
|
|
3128
|
+
this.activateTab(nextIndex);
|
|
3129
|
+
tabs[nextIndex].focus();
|
|
3130
|
+
};
|
|
2949
3131
|
activateTab(index) {
|
|
2950
3132
|
this.tabTargets.forEach((tab, i) => {
|
|
2951
3133
|
const active = i === index;
|
|
@@ -2965,12 +3147,26 @@ class Tabs extends Controller {
|
|
|
2965
3147
|
|
|
2966
3148
|
class Tooltip extends Controller {
|
|
2967
3149
|
static targets = ["popover"];
|
|
3150
|
+
connect() {
|
|
3151
|
+
document.addEventListener("keydown", this.handleKeydown);
|
|
3152
|
+
}
|
|
3153
|
+
disconnect() {
|
|
3154
|
+
document.removeEventListener("keydown", this.handleKeydown);
|
|
3155
|
+
}
|
|
2968
3156
|
show() {
|
|
2969
3157
|
this.popoverTarget.showPopover();
|
|
2970
3158
|
}
|
|
2971
3159
|
hide() {
|
|
2972
|
-
|
|
3160
|
+
try {
|
|
3161
|
+
this.popoverTarget.hidePopover();
|
|
3162
|
+
} catch {
|
|
3163
|
+
}
|
|
2973
3164
|
}
|
|
3165
|
+
handleKeydown = (event) => {
|
|
3166
|
+
if (event.key !== "Escape")
|
|
3167
|
+
return;
|
|
3168
|
+
this.hide();
|
|
3169
|
+
};
|
|
2974
3170
|
}
|
|
2975
3171
|
|
|
2976
3172
|
const NON_SUBMITTING_INPUT_TYPES = /* @__PURE__ */ new Set([
|
|
@@ -2993,16 +3189,24 @@ class Carousel extends Controller {
|
|
|
2993
3189
|
observer = null;
|
|
2994
3190
|
_currentIndex = 0;
|
|
2995
3191
|
_maxUnlocked = 0;
|
|
3192
|
+
_lastTouchX = 0;
|
|
2996
3193
|
connect() {
|
|
2997
3194
|
this.setupObserver();
|
|
2998
3195
|
this.updateNavigation();
|
|
2999
3196
|
if (this.wizardValue) {
|
|
3000
3197
|
this.element.addEventListener("keydown", this.handleKeydown);
|
|
3198
|
+
this.trackTarget.addEventListener("wheel", this.handleWheel, { passive: false });
|
|
3199
|
+
this.trackTarget.addEventListener("touchstart", this.handleTouchStart, { passive: true });
|
|
3200
|
+
this.trackTarget.addEventListener("touchmove", this.handleTouchMove, { passive: false });
|
|
3201
|
+
this.updateSlideAccessibility();
|
|
3001
3202
|
}
|
|
3002
3203
|
}
|
|
3003
3204
|
disconnect() {
|
|
3004
3205
|
this.observer?.disconnect();
|
|
3005
3206
|
this.element.removeEventListener("keydown", this.handleKeydown);
|
|
3207
|
+
this.trackTarget.removeEventListener("wheel", this.handleWheel);
|
|
3208
|
+
this.trackTarget.removeEventListener("touchstart", this.handleTouchStart);
|
|
3209
|
+
this.trackTarget.removeEventListener("touchmove", this.handleTouchMove);
|
|
3006
3210
|
}
|
|
3007
3211
|
next() {
|
|
3008
3212
|
this.trackTarget.scrollBy({ left: this.trackTarget.clientWidth, behavior: "smooth" });
|
|
@@ -3027,10 +3231,13 @@ class Carousel extends Controller {
|
|
|
3027
3231
|
const nextIndex = this._currentIndex + 1;
|
|
3028
3232
|
if (nextIndex > this._maxUnlocked) {
|
|
3029
3233
|
this._maxUnlocked = nextIndex;
|
|
3234
|
+
this.updateSlideAccessibility();
|
|
3030
3235
|
}
|
|
3031
3236
|
this.next();
|
|
3032
3237
|
}
|
|
3033
3238
|
scrolled() {
|
|
3239
|
+
if (this.wizardValue)
|
|
3240
|
+
this.clampToFrontier();
|
|
3034
3241
|
this.updateCurrentIndex();
|
|
3035
3242
|
this.updateNavigation();
|
|
3036
3243
|
}
|
|
@@ -3048,9 +3255,49 @@ class Carousel extends Controller {
|
|
|
3048
3255
|
event.preventDefault();
|
|
3049
3256
|
this.unlockNext();
|
|
3050
3257
|
};
|
|
3258
|
+
// Blocks wheel-based horizontal scrolling past the wizard frontier.
|
|
3259
|
+
handleWheel = (event) => {
|
|
3260
|
+
const maxLeft = this._maxUnlocked * this.trackTarget.clientWidth;
|
|
3261
|
+
if (event.deltaX > 0 && this.trackTarget.scrollLeft >= maxLeft - 2) {
|
|
3262
|
+
event.preventDefault();
|
|
3263
|
+
}
|
|
3264
|
+
};
|
|
3265
|
+
handleTouchStart = (event) => {
|
|
3266
|
+
this._lastTouchX = event.touches[0].clientX;
|
|
3267
|
+
};
|
|
3268
|
+
// Blocks touch-swipe scrolling past the wizard frontier. Tracks incremental
|
|
3269
|
+
// delta per move event so direction is accurate mid-gesture.
|
|
3270
|
+
handleTouchMove = (event) => {
|
|
3271
|
+
const currentX = event.touches[0].clientX;
|
|
3272
|
+
const delta = currentX - this._lastTouchX;
|
|
3273
|
+
this._lastTouchX = currentX;
|
|
3274
|
+
const maxLeft = this._maxUnlocked * this.trackTarget.clientWidth;
|
|
3275
|
+
if (delta < 0 && this.trackTarget.scrollLeft >= maxLeft - 2) {
|
|
3276
|
+
event.preventDefault();
|
|
3277
|
+
}
|
|
3278
|
+
};
|
|
3051
3279
|
get slides() {
|
|
3052
3280
|
return Array.from(this.trackTarget.children);
|
|
3053
3281
|
}
|
|
3282
|
+
// Snaps scroll position back to the frontier if the user somehow scrolled
|
|
3283
|
+
// past it (e.g. momentum after a partially-blocked gesture).
|
|
3284
|
+
clampToFrontier() {
|
|
3285
|
+
const maxLeft = this._maxUnlocked * this.trackTarget.clientWidth;
|
|
3286
|
+
if (this.trackTarget.scrollLeft > maxLeft + 2) {
|
|
3287
|
+
this.trackTarget.scrollLeft = maxLeft;
|
|
3288
|
+
}
|
|
3289
|
+
}
|
|
3290
|
+
// Applies/removes `inert` on each slide based on the wizard frontier,
|
|
3291
|
+
// removing locked slides from the tab order and accessibility tree.
|
|
3292
|
+
updateSlideAccessibility() {
|
|
3293
|
+
this.slides.forEach((slide, i) => {
|
|
3294
|
+
if (i > this._maxUnlocked) {
|
|
3295
|
+
slide.setAttribute("inert", "");
|
|
3296
|
+
} else {
|
|
3297
|
+
slide.removeAttribute("inert");
|
|
3298
|
+
}
|
|
3299
|
+
});
|
|
3300
|
+
}
|
|
3054
3301
|
updateCurrentIndex() {
|
|
3055
3302
|
const track = this.trackTarget;
|
|
3056
3303
|
if (track.clientWidth > 0) {
|
|
@@ -3067,7 +3314,15 @@ class Carousel extends Controller {
|
|
|
3067
3314
|
entries.forEach((entry) => {
|
|
3068
3315
|
const index = this.slides.indexOf(entry.target);
|
|
3069
3316
|
if (index >= 0) {
|
|
3070
|
-
this.dotTargets[index]
|
|
3317
|
+
const dot = this.dotTargets[index];
|
|
3318
|
+
if (!dot)
|
|
3319
|
+
return;
|
|
3320
|
+
dot.classList.toggle("active", entry.isIntersecting);
|
|
3321
|
+
if (entry.isIntersecting) {
|
|
3322
|
+
dot.setAttribute("aria-current", "true");
|
|
3323
|
+
} else {
|
|
3324
|
+
dot.removeAttribute("aria-current");
|
|
3325
|
+
}
|
|
3071
3326
|
}
|
|
3072
3327
|
});
|
|
3073
3328
|
},
|
|
@@ -3082,8 +3337,9 @@ class Carousel extends Controller {
|
|
|
3082
3337
|
if (this.hasPrevTarget)
|
|
3083
3338
|
this.prevTarget.classList.toggle("hide", atStart);
|
|
3084
3339
|
if (this.hasNextTarget) {
|
|
3340
|
+
const atLastSlide = this._currentIndex >= this.slides.length - 1;
|
|
3085
3341
|
const frontierReached = this.wizardValue && this._currentIndex >= this._maxUnlocked;
|
|
3086
|
-
this.nextTarget.classList.toggle("hide", atEnd || frontierReached);
|
|
3342
|
+
this.nextTarget.classList.toggle("hide", atEnd || atLastSlide || frontierReached);
|
|
3087
3343
|
}
|
|
3088
3344
|
if (this.wizardValue && this.hasDotTarget) {
|
|
3089
3345
|
this.dotTargets.forEach((dot, i) => {
|
|
@@ -3224,23 +3480,51 @@ class FormLiveSummary extends Controller {
|
|
|
3224
3480
|
}
|
|
3225
3481
|
}
|
|
3226
3482
|
|
|
3483
|
+
class Form extends Controller {
|
|
3484
|
+
connect() {
|
|
3485
|
+
this.element.addEventListener("submit", this.handleSubmit);
|
|
3486
|
+
this.element.addEventListener("turbo:submit-end", this.handleTurboEnd);
|
|
3487
|
+
}
|
|
3488
|
+
disconnect() {
|
|
3489
|
+
this.element.removeEventListener("submit", this.handleSubmit);
|
|
3490
|
+
this.element.removeEventListener("turbo:submit-end", this.handleTurboEnd);
|
|
3491
|
+
}
|
|
3492
|
+
handleSubmit = () => {
|
|
3493
|
+
this.submitButtons.forEach((btn) => {
|
|
3494
|
+
btn.classList.add("loading");
|
|
3495
|
+
btn.disabled = true;
|
|
3496
|
+
});
|
|
3497
|
+
};
|
|
3498
|
+
// Restore buttons if the submission fails (e.g. a Turbo Frame validation
|
|
3499
|
+
// error that re-renders the form without a full page transition).
|
|
3500
|
+
handleTurboEnd = (event) => {
|
|
3501
|
+
if (!event.detail?.success) {
|
|
3502
|
+
this.submitButtons.forEach((btn) => {
|
|
3503
|
+
btn.classList.remove("loading");
|
|
3504
|
+
btn.disabled = false;
|
|
3505
|
+
});
|
|
3506
|
+
}
|
|
3507
|
+
};
|
|
3508
|
+
get submitButtons() {
|
|
3509
|
+
return Array.from(
|
|
3510
|
+
this.element.querySelectorAll('button[type="submit"], button:not([type])')
|
|
3511
|
+
);
|
|
3512
|
+
}
|
|
3513
|
+
}
|
|
3514
|
+
|
|
3227
3515
|
function registerMidwestControllers(application) {
|
|
3228
3516
|
application.register("midwest-card", Card);
|
|
3229
3517
|
application.register("midwest-banner", Banner);
|
|
3230
|
-
application.register("midwest-callout", Callout);
|
|
3231
3518
|
application.register("midwest-input", Input);
|
|
3232
3519
|
application.register("midwest-file", File);
|
|
3233
|
-
application.register("midwest-switch", Switch);
|
|
3234
3520
|
application.register("midwest-dropdown", Dropdown);
|
|
3235
|
-
application.register("midwest-avatar", Avatar);
|
|
3236
3521
|
application.register("midwest-prose", Prose);
|
|
3237
3522
|
application.register("midwest-dialog", Dialog);
|
|
3238
|
-
application.register("midwest-video", Video);
|
|
3239
|
-
application.register("midwest-dropdown-item", DropdownItem);
|
|
3240
3523
|
application.register("midwest-tabs", Tabs);
|
|
3241
3524
|
application.register("midwest-tooltip", Tooltip);
|
|
3242
3525
|
application.register("midwest-carousel", Carousel);
|
|
3243
3526
|
application.register("midwest-form-live-summary", FormLiveSummary);
|
|
3527
|
+
application.register("midwest-form", Form);
|
|
3244
3528
|
}
|
|
3245
3529
|
|
|
3246
3530
|
export { Banner, Card, registerMidwestControllers };
|