@unabridged/midwest 0.16.0 → 0.17.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/app/assets/javascript/midwest/index.ts +2 -0
- package/app/assets/javascript/midwest.js +754 -97
- package/app/assets/javascript/midwest.js.map +1 -1
- package/app/assets/stylesheets/midwest.css +1 -1
- package/app/assets/stylesheets/midwest.tailwind.css +3 -1
- package/dist/css/midwest.css +1 -1
- package/dist/javascript/collection/app/assets/javascript/midwest/index.js +2 -0
- package/dist/javascript/collection/app/assets/javascript/midwest/index.js.map +1 -1
- package/dist/javascript/collection/app/components/midwest/carousel_component/carousel_component_controller.js +17 -5
- package/dist/javascript/collection/app/components/midwest/carousel_component/carousel_component_controller.js.map +1 -1
- package/dist/javascript/collection/app/components/midwest/form/autocomplete_component/autocomplete_component_controller.js +180 -92
- package/dist/javascript/collection/app/components/midwest/form/autocomplete_component/autocomplete_component_controller.js.map +1 -1
- package/dist/javascript/collection/app/components/midwest/form/live_summary_component/live_summary_component_controller.js +12 -0
- package/dist/javascript/collection/app/components/midwest/form/live_summary_component/live_summary_component_controller.js.map +1 -1
- package/dist/javascript/collection/app/components/midwest/image_component/color_thief.js +490 -0
- package/dist/javascript/collection/app/components/midwest/image_component/color_thief.js.map +1 -0
- package/dist/javascript/collection/app/components/midwest/image_component/image_component_controller.js +47 -0
- package/dist/javascript/collection/app/components/midwest/image_component/image_component_controller.js.map +1 -0
- package/dist/javascript/collection/app/components/midwest/tabs_component/tabs_component_controller.js +15 -1
- package/dist/javascript/collection/app/components/midwest/tabs_component/tabs_component_controller.js.map +1 -1
- package/dist/javascript/midwest.js +754 -97
- package/dist/javascript/midwest.js.map +1 -1
- package/package.json +1 -1
|
@@ -22,6 +22,7 @@ export { default as Chart } from '../../../components/midwest/chart_component/ch
|
|
|
22
22
|
import Range from '../../../components/midwest/form/range_component/range_component_controller.js';
|
|
23
23
|
import Rating from '../../../components/midwest/rating_component/rating_component_controller.js';
|
|
24
24
|
import Autocomplete from '../../../components/midwest/form/autocomplete_component/autocomplete_component_controller.js';
|
|
25
|
+
import Image from '../../../components/midwest/image_component/image_component_controller.js';
|
|
25
26
|
|
|
26
27
|
function registerMidwestControllers(application) {
|
|
27
28
|
application.register("midwest-card", Card);
|
|
@@ -44,6 +45,7 @@ function registerMidwestControllers(application) {
|
|
|
44
45
|
application.register("midwest-range", Range);
|
|
45
46
|
application.register("midwest-rating", Rating);
|
|
46
47
|
application.register("midwest-autocomplete", Autocomplete);
|
|
48
|
+
application.register("midwest-image", Image);
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
export { registerMidwestControllers };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../../../../../app/assets/javascript/midwest/index.ts"],"sourcesContent":["import Card from '../../../components/midwest/card_component/card_component_controller'\nimport Banner from '../../../components/midwest/banner_component/banner_component_controller'\nimport Input from '../../../components/midwest/form/input_component/input_component_controller'\nimport File from '../../../components/midwest/form/file_component/file_component_controller'\nimport Dropdown from '../../../components/midwest/dropdown_component/dropdown_component_controller'\nimport Prose from '../../../components/midwest/prose_component/prose_component_controller'\nimport Dialog from '../../../components/midwest/dialog_component/dialog_component_controller'\nimport Tabs from '../../../components/midwest/tabs_component/tabs_component_controller'\nimport Tooltip from '../../../components/midwest/tooltip_component/tooltip_component_controller'\nimport Carousel from '../../../components/midwest/carousel_component/carousel_component_controller'\nimport FormLiveSummary from '../../../components/midwest/form/live_summary_component/live_summary_component_controller'\nimport Form from '../../../components/midwest/form/form_controller'\nimport CountdownTimer from '../../../components/midwest/countdown_timer_component/countdown_timer_component_controller'\nimport Badge from '../../../components/midwest/badge_component/badge_component_controller'\nimport Confirmation from '../../../components/midwest/confirmation_component/confirmation_component_controller'\nimport AvatarGroup from '../../../components/midwest/avatar_group_component/avatar_group_component_controller'\nimport Chart from '../../../components/midwest/chart_component/chart_component_controller'\nimport Range from '../../../components/midwest/form/range_component/range_component_controller'\nimport Rating from '../../../components/midwest/rating_component/rating_component_controller'\nimport Autocomplete from '../../../components/midwest/form/autocomplete_component/autocomplete_component_controller'\n/* IMPORTS */\n\nexport { Card, Banner, CountdownTimer, Chart }\n\nexport function registerMidwestControllers (application: any) {\n application.register('midwest-card', Card)\n application.register('midwest-banner', Banner)\n application.register('midwest-countdown-timer', CountdownTimer)\n application.register('midwest-input', Input)\n application.register('midwest-file', File)\n application.register('midwest-dropdown', Dropdown)\n application.register('midwest-prose', Prose)\n application.register('midwest-dialog', Dialog)\n application.register('midwest-tabs', Tabs)\n application.register('midwest-tooltip', Tooltip)\n application.register('midwest-carousel', Carousel)\n application.register('midwest-form-live-summary', FormLiveSummary)\n application.register('midwest-form', Form)\n application.register('midwest-badge', Badge)\n application.register('midwest-confirmation', Confirmation)\n application.register('midwest-avatar-group', AvatarGroup)\n application.register('midwest-chart', Chart)\n application.register('midwest-range', Range)\n application.register('midwest-rating', Rating)\n application.register('midwest-autocomplete', Autocomplete)\n /* EXPORTS */\n}\n"],"names":["Confirmation"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../../../../app/assets/javascript/midwest/index.ts"],"sourcesContent":["import Card from '../../../components/midwest/card_component/card_component_controller'\nimport Banner from '../../../components/midwest/banner_component/banner_component_controller'\nimport Input from '../../../components/midwest/form/input_component/input_component_controller'\nimport File from '../../../components/midwest/form/file_component/file_component_controller'\nimport Dropdown from '../../../components/midwest/dropdown_component/dropdown_component_controller'\nimport Prose from '../../../components/midwest/prose_component/prose_component_controller'\nimport Dialog from '../../../components/midwest/dialog_component/dialog_component_controller'\nimport Tabs from '../../../components/midwest/tabs_component/tabs_component_controller'\nimport Tooltip from '../../../components/midwest/tooltip_component/tooltip_component_controller'\nimport Carousel from '../../../components/midwest/carousel_component/carousel_component_controller'\nimport FormLiveSummary from '../../../components/midwest/form/live_summary_component/live_summary_component_controller'\nimport Form from '../../../components/midwest/form/form_controller'\nimport CountdownTimer from '../../../components/midwest/countdown_timer_component/countdown_timer_component_controller'\nimport Badge from '../../../components/midwest/badge_component/badge_component_controller'\nimport Confirmation from '../../../components/midwest/confirmation_component/confirmation_component_controller'\nimport AvatarGroup from '../../../components/midwest/avatar_group_component/avatar_group_component_controller'\nimport Chart from '../../../components/midwest/chart_component/chart_component_controller'\nimport Range from '../../../components/midwest/form/range_component/range_component_controller'\nimport Rating from '../../../components/midwest/rating_component/rating_component_controller'\nimport Autocomplete from '../../../components/midwest/form/autocomplete_component/autocomplete_component_controller'\nimport Image from '../../../components/midwest/image_component/image_component_controller'\n/* IMPORTS */\n\nexport { Card, Banner, CountdownTimer, Chart }\n\nexport function registerMidwestControllers (application: any) {\n application.register('midwest-card', Card)\n application.register('midwest-banner', Banner)\n application.register('midwest-countdown-timer', CountdownTimer)\n application.register('midwest-input', Input)\n application.register('midwest-file', File)\n application.register('midwest-dropdown', Dropdown)\n application.register('midwest-prose', Prose)\n application.register('midwest-dialog', Dialog)\n application.register('midwest-tabs', Tabs)\n application.register('midwest-tooltip', Tooltip)\n application.register('midwest-carousel', Carousel)\n application.register('midwest-form-live-summary', FormLiveSummary)\n application.register('midwest-form', Form)\n application.register('midwest-badge', Badge)\n application.register('midwest-confirmation', Confirmation)\n application.register('midwest-avatar-group', AvatarGroup)\n application.register('midwest-chart', Chart)\n application.register('midwest-range', Range)\n application.register('midwest-rating', Rating)\n application.register('midwest-autocomplete', Autocomplete)\n application.register('midwest-image', Image)\n /* EXPORTS */\n}\n"],"names":["Confirmation"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAyBO,SAAS,2BAA4B,WAAkB,EAAA;AAC5D,EAAY,WAAA,CAAA,QAAA,CAAS,gBAAgB,IAAI,CAAA,CAAA;AACzC,EAAY,WAAA,CAAA,QAAA,CAAS,kBAAkB,MAAM,CAAA,CAAA;AAC7C,EAAY,WAAA,CAAA,QAAA,CAAS,2BAA2B,cAAc,CAAA,CAAA;AAC9D,EAAY,WAAA,CAAA,QAAA,CAAS,iBAAiB,KAAK,CAAA,CAAA;AAC3C,EAAY,WAAA,CAAA,QAAA,CAAS,gBAAgB,IAAI,CAAA,CAAA;AACzC,EAAY,WAAA,CAAA,QAAA,CAAS,oBAAoB,QAAQ,CAAA,CAAA;AACjD,EAAY,WAAA,CAAA,QAAA,CAAS,iBAAiB,KAAK,CAAA,CAAA;AAC3C,EAAY,WAAA,CAAA,QAAA,CAAS,kBAAkB,MAAM,CAAA,CAAA;AAC7C,EAAY,WAAA,CAAA,QAAA,CAAS,gBAAgB,IAAI,CAAA,CAAA;AACzC,EAAY,WAAA,CAAA,QAAA,CAAS,mBAAmB,OAAO,CAAA,CAAA;AAC/C,EAAY,WAAA,CAAA,QAAA,CAAS,oBAAoB,QAAQ,CAAA,CAAA;AACjD,EAAY,WAAA,CAAA,QAAA,CAAS,6BAA6B,eAAe,CAAA,CAAA;AACjE,EAAY,WAAA,CAAA,QAAA,CAAS,gBAAgB,IAAI,CAAA,CAAA;AACzC,EAAY,WAAA,CAAA,QAAA,CAAS,iBAAiB,KAAK,CAAA,CAAA;AAC3C,EAAY,WAAA,CAAA,QAAA,CAAS,wBAAwBA,sBAAY,CAAA,CAAA;AACzD,EAAY,WAAA,CAAA,QAAA,CAAS,wBAAwB,WAAW,CAAA,CAAA;AACxD,EAAY,WAAA,CAAA,QAAA,CAAS,iBAAiB,KAAK,CAAA,CAAA;AAC3C,EAAY,WAAA,CAAA,QAAA,CAAS,iBAAiB,KAAK,CAAA,CAAA;AAC3C,EAAY,WAAA,CAAA,QAAA,CAAS,kBAAkB,MAAM,CAAA,CAAA;AAC7C,EAAY,WAAA,CAAA,QAAA,CAAS,wBAAwB,YAAY,CAAA,CAAA;AACzD,EAAY,WAAA,CAAA,QAAA,CAAS,iBAAiB,KAAK,CAAA,CAAA;AAE7C;;;;"}
|
|
@@ -17,7 +17,8 @@ class Carousel extends Controller {
|
|
|
17
17
|
static targets = ["track", "prev", "next", "dot"];
|
|
18
18
|
static values = {
|
|
19
19
|
wizard: { type: Boolean, default: false },
|
|
20
|
-
autoplay: { type: Number, default: 0 }
|
|
20
|
+
autoplay: { type: Number, default: 0 },
|
|
21
|
+
loop: { type: Boolean, default: false }
|
|
21
22
|
};
|
|
22
23
|
observer = null;
|
|
23
24
|
_currentIndex = 0;
|
|
@@ -53,10 +54,21 @@ class Carousel extends Controller {
|
|
|
53
54
|
this.stopAutoplay();
|
|
54
55
|
}
|
|
55
56
|
next() {
|
|
56
|
-
this.
|
|
57
|
+
const atEnd = this._currentIndex >= this.slides.length - 1;
|
|
58
|
+
if (this.loopValue && atEnd) {
|
|
59
|
+
this.slides[0]?.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "nearest" });
|
|
60
|
+
} else {
|
|
61
|
+
this.trackTarget.scrollBy({ left: this.trackTarget.clientWidth, behavior: "smooth" });
|
|
62
|
+
}
|
|
57
63
|
}
|
|
58
64
|
previous() {
|
|
59
|
-
|
|
65
|
+
const atStart = this._currentIndex <= 0;
|
|
66
|
+
if (this.loopValue && atStart) {
|
|
67
|
+
const last = this.slides[this.slides.length - 1];
|
|
68
|
+
last?.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "nearest" });
|
|
69
|
+
} else {
|
|
70
|
+
this.trackTarget.scrollBy({ left: -this.trackTarget.clientWidth, behavior: "smooth" });
|
|
71
|
+
}
|
|
60
72
|
}
|
|
61
73
|
goto(event) {
|
|
62
74
|
const button = event.currentTarget;
|
|
@@ -223,11 +235,11 @@ class Carousel extends Controller {
|
|
|
223
235
|
const atStart = track.scrollLeft <= 0;
|
|
224
236
|
const atEnd = track.scrollLeft >= track.scrollWidth - track.clientWidth - 1;
|
|
225
237
|
if (this.hasPrevTarget)
|
|
226
|
-
this.prevTarget.classList.toggle("hide", atStart);
|
|
238
|
+
this.prevTarget.classList.toggle("hide", atStart && !this.loopValue);
|
|
227
239
|
if (this.hasNextTarget) {
|
|
228
240
|
const atLastSlide = this._currentIndex >= this.slides.length - 1;
|
|
229
241
|
const frontierReached = this.wizardValue && this._currentIndex >= this._maxUnlocked;
|
|
230
|
-
this.nextTarget.classList.toggle("hide", atEnd || atLastSlide || frontierReached);
|
|
242
|
+
this.nextTarget.classList.toggle("hide", (atEnd || atLastSlide || frontierReached) && !this.loopValue);
|
|
231
243
|
}
|
|
232
244
|
if (this.wizardValue && this.hasDotTarget) {
|
|
233
245
|
this.dotTargets.forEach((dot, i) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"carousel_component_controller.js","sources":["../../../../../../../app/components/midwest/carousel_component/carousel_component_controller.ts"],"sourcesContent":["import { Controller } from '@hotwired/stimulus'\n\n// Input types where pressing Enter in a form does NOT normally submit it.\n// For all other input types, Enter submits — so wizard mode intercepts it.\nconst NON_SUBMITTING_INPUT_TYPES = new Set([\n 'button', 'checkbox', 'color', 'file', 'hidden', 'image', 'radio', 'range', 'reset', 'submit'\n])\n\n// Tags that should receive arrow key events without carousel interception.\nconst FORM_ELEMENT_TAGS = new Set(['INPUT', 'TEXTAREA', 'SELECT'])\n\nexport default class Carousel extends Controller<HTMLElement> {\n static targets = ['track', 'prev', 'next', 'dot']\n static values = {\n wizard: { type: Boolean, default: false },\n autoplay: { type: Number, default: 0 }\n }\n\n declare readonly trackTarget: HTMLElement\n declare readonly prevTarget: HTMLButtonElement\n declare readonly nextTarget: HTMLButtonElement\n declare readonly dotTargets: HTMLButtonElement[]\n declare readonly hasPrevTarget: boolean\n declare readonly hasNextTarget: boolean\n declare readonly hasDotTarget: boolean\n declare wizardValue: boolean\n declare autoplayValue: number\n\n private observer: IntersectionObserver | null = null\n private _currentIndex = 0\n private _maxUnlocked = 0\n private _lastTouchX = 0\n private _autoplayTimer: ReturnType<typeof setInterval> | null = null\n\n connect () {\n this.setupObserver()\n this.updateNavigation()\n this.element.addEventListener('keydown', this.handleArrowKeys)\n if (this.wizardValue) {\n this.element.addEventListener('keydown', this.handleKeydown)\n this.trackTarget.addEventListener('wheel', this.handleWheel, { passive: false })\n this.trackTarget.addEventListener('touchstart', this.handleTouchStart, { passive: true })\n this.trackTarget.addEventListener('touchmove', this.handleTouchMove, { passive: false })\n this.updateSlideAccessibility()\n }\n if (this.autoplayValue > 0) {\n this.startAutoplay()\n this.element.addEventListener('mouseenter', this.pauseAutoplay)\n this.element.addEventListener('mouseleave', this.resumeAutoplay)\n }\n }\n\n disconnect () {\n this.observer?.disconnect()\n this.element.removeEventListener('keydown', this.handleArrowKeys)\n this.element.removeEventListener('keydown', this.handleKeydown)\n this.element.removeEventListener('mouseenter', this.pauseAutoplay)\n this.element.removeEventListener('mouseleave', this.resumeAutoplay)\n this.trackTarget.removeEventListener('wheel', this.handleWheel)\n this.trackTarget.removeEventListener('touchstart', this.handleTouchStart)\n this.trackTarget.removeEventListener('touchmove', this.handleTouchMove)\n this.stopAutoplay()\n }\n\n next () {\n this.trackTarget.scrollBy({ left: this.trackTarget.clientWidth, behavior: 'smooth' })\n }\n\n previous () {\n this.trackTarget.scrollBy({ left: -this.trackTarget.clientWidth, behavior: 'smooth' })\n }\n\n goto (event: Event) {\n const button = event.currentTarget as HTMLButtonElement\n const index = parseInt(button.dataset.index ?? '0', 10)\n const slide = this.slides[index]\n slide?.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' })\n }\n\n // Unlocks the next slide and advances to it. Intended for wizard mode: wire\n // a \"Continue\" button inside a slide to this action so users progress\n // linearly through steps before the carousel nav becomes available.\n //\n // <button type=\"button\" data-action=\"click->midwest-carousel#unlockNext\">\n // Continue\n // </button>\n unlockNext () {\n const nextIndex = this._currentIndex + 1\n if (nextIndex > this._maxUnlocked) {\n this._maxUnlocked = nextIndex\n this.updateSlideAccessibility()\n }\n this.next()\n }\n\n scrolled () {\n if (this.wizardValue) this.clampToFrontier()\n this.updateCurrentIndex()\n this.updateNavigation()\n }\n\n // Arrow function so `this` is stable for add/removeEventListener pairing.\n // Handles ArrowLeft/ArrowRight for keyboard slide navigation. Skips form\n // elements so arrow keys continue to work normally inside inputs and selects.\n private handleArrowKeys = (event: KeyboardEvent) => {\n if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') return\n\n const target = event.target as HTMLElement\n if (FORM_ELEMENT_TAGS.has(target.tagName) || target.isContentEditable) return\n\n event.preventDefault()\n if (event.key === 'ArrowLeft') {\n this.previous()\n } else {\n this.next()\n }\n }\n\n // Arrow function so `this` is stable for add/removeEventListener pairing.\n private handleKeydown = (event: KeyboardEvent) => {\n if (event.key !== 'Enter') return\n\n const target = event.target as HTMLElement\n // Only intercept Enter on text-like inputs; textarea newlines and button\n // activations should pass through untouched.\n if (target.tagName === 'TEXTAREA' || target.tagName !== 'INPUT') return\n if (NON_SUBMITTING_INPUT_TYPES.has((target as HTMLInputElement).type)) return\n\n // On the final step let Enter submit the form normally.\n if (this._currentIndex >= this.slides.length - 1) return\n\n event.preventDefault()\n this.unlockNext()\n }\n\n // Blocks wheel-based horizontal scrolling past the wizard frontier.\n private handleWheel = (event: WheelEvent) => {\n const maxLeft = this._maxUnlocked * this.trackTarget.clientWidth\n if (event.deltaX > 0 && this.trackTarget.scrollLeft >= maxLeft - 2) {\n event.preventDefault()\n }\n }\n\n private handleTouchStart = (event: TouchEvent) => {\n this._lastTouchX = event.touches[0].clientX\n }\n\n // Blocks touch-swipe scrolling past the wizard frontier. Tracks incremental\n // delta per move event so direction is accurate mid-gesture.\n private handleTouchMove = (event: TouchEvent) => {\n const currentX = event.touches[0].clientX\n const delta = currentX - this._lastTouchX // positive = swiping right (going back)\n this._lastTouchX = currentX\n\n const maxLeft = this._maxUnlocked * this.trackTarget.clientWidth\n if (delta < 0 && this.trackTarget.scrollLeft >= maxLeft - 2) {\n event.preventDefault()\n }\n }\n\n private pauseAutoplay = () => {\n this.stopAutoplay()\n }\n\n private resumeAutoplay = () => {\n this.startAutoplay()\n }\n\n private startAutoplay () {\n if (this.autoplayValue <= 0 || this._autoplayTimer !== null) return\n this._autoplayTimer = setInterval(() => { this.autoplayAdvance() }, this.autoplayValue)\n }\n\n private stopAutoplay () {\n if (this._autoplayTimer === null) return\n clearInterval(this._autoplayTimer)\n this._autoplayTimer = null\n }\n\n private autoplayAdvance () {\n const atEnd = this._currentIndex >= this.slides.length - 1\n if (atEnd) {\n const firstSlide = this.slides[0]\n firstSlide?.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' })\n } else {\n this.next()\n }\n }\n\n private get slides (): HTMLElement[] {\n return Array.from(this.trackTarget.children) as HTMLElement[]\n }\n\n // Snaps scroll position back to the frontier if the user somehow scrolled\n // past it (e.g. momentum after a partially-blocked gesture).\n private clampToFrontier () {\n const maxLeft = this._maxUnlocked * this.trackTarget.clientWidth\n if (this.trackTarget.scrollLeft > maxLeft + 2) {\n this.trackTarget.scrollLeft = maxLeft\n }\n }\n\n // Applies/removes `inert` on each slide based on the wizard frontier,\n // removing locked slides from the tab order and accessibility tree.\n private updateSlideAccessibility () {\n this.slides.forEach((slide, i) => {\n if (i > this._maxUnlocked) {\n slide.setAttribute('inert', '')\n } else {\n slide.removeAttribute('inert')\n }\n })\n }\n\n private updateCurrentIndex () {\n const track = this.trackTarget\n if (track.clientWidth > 0) {\n this._currentIndex = Math.round(track.scrollLeft / track.clientWidth)\n }\n }\n\n private setupObserver () {\n if (!this.hasDotTarget) return\n if (!('IntersectionObserver' in window)) return\n\n this.observer = new IntersectionObserver(\n (entries) => {\n entries.forEach(entry => {\n const index = this.slides.indexOf(entry.target as HTMLElement)\n if (index >= 0) {\n const dot = this.dotTargets[index]\n if (!dot) return\n dot.classList.toggle('active', entry.isIntersecting)\n if (entry.isIntersecting) {\n dot.setAttribute('aria-current', 'true')\n } else {\n dot.removeAttribute('aria-current')\n }\n }\n })\n },\n { root: this.trackTarget, threshold: 0.5 }\n )\n\n this.slides.forEach(slide => this.observer?.observe(slide))\n }\n\n private updateNavigation () {\n const track = this.trackTarget\n const atStart = track.scrollLeft <= 0\n const atEnd = track.scrollLeft >= track.scrollWidth - track.clientWidth - 1\n\n if (this.hasPrevTarget) this.prevTarget.classList.toggle('hide', atStart)\n\n if (this.hasNextTarget) {\n const atLastSlide = this._currentIndex >= this.slides.length - 1\n const frontierReached = this.wizardValue && this._currentIndex >= this._maxUnlocked\n this.nextTarget.classList.toggle('hide', atEnd || atLastSlide || frontierReached)\n }\n\n if (this.wizardValue && this.hasDotTarget) {\n this.dotTargets.forEach((dot, i) => {\n const locked = i > this._maxUnlocked\n dot.disabled = locked\n dot.classList.toggle('locked', locked)\n })\n }\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"carousel_component_controller.js","sources":["../../../../../../../app/components/midwest/carousel_component/carousel_component_controller.ts"],"sourcesContent":["import { Controller } from '@hotwired/stimulus'\n\n// Input types where pressing Enter in a form does NOT normally submit it.\n// For all other input types, Enter submits — so wizard mode intercepts it.\nconst NON_SUBMITTING_INPUT_TYPES = new Set([\n 'button', 'checkbox', 'color', 'file', 'hidden', 'image', 'radio', 'range', 'reset', 'submit'\n])\n\n// Tags that should receive arrow key events without carousel interception.\nconst FORM_ELEMENT_TAGS = new Set(['INPUT', 'TEXTAREA', 'SELECT'])\n\nexport default class Carousel extends Controller<HTMLElement> {\n static targets = ['track', 'prev', 'next', 'dot']\n static values = {\n wizard: { type: Boolean, default: false },\n autoplay: { type: Number, default: 0 },\n loop: { type: Boolean, default: false }\n }\n\n declare readonly trackTarget: HTMLElement\n declare readonly prevTarget: HTMLButtonElement\n declare readonly nextTarget: HTMLButtonElement\n declare readonly dotTargets: HTMLButtonElement[]\n declare readonly hasPrevTarget: boolean\n declare readonly hasNextTarget: boolean\n declare readonly hasDotTarget: boolean\n declare wizardValue: boolean\n declare autoplayValue: number\n declare loopValue: boolean\n\n private observer: IntersectionObserver | null = null\n private _currentIndex = 0\n private _maxUnlocked = 0\n private _lastTouchX = 0\n private _autoplayTimer: ReturnType<typeof setInterval> | null = null\n\n connect () {\n this.setupObserver()\n this.updateNavigation()\n this.element.addEventListener('keydown', this.handleArrowKeys)\n if (this.wizardValue) {\n this.element.addEventListener('keydown', this.handleKeydown)\n this.trackTarget.addEventListener('wheel', this.handleWheel, { passive: false })\n this.trackTarget.addEventListener('touchstart', this.handleTouchStart, { passive: true })\n this.trackTarget.addEventListener('touchmove', this.handleTouchMove, { passive: false })\n this.updateSlideAccessibility()\n }\n if (this.autoplayValue > 0) {\n this.startAutoplay()\n this.element.addEventListener('mouseenter', this.pauseAutoplay)\n this.element.addEventListener('mouseleave', this.resumeAutoplay)\n }\n }\n\n disconnect () {\n this.observer?.disconnect()\n this.element.removeEventListener('keydown', this.handleArrowKeys)\n this.element.removeEventListener('keydown', this.handleKeydown)\n this.element.removeEventListener('mouseenter', this.pauseAutoplay)\n this.element.removeEventListener('mouseleave', this.resumeAutoplay)\n this.trackTarget.removeEventListener('wheel', this.handleWheel)\n this.trackTarget.removeEventListener('touchstart', this.handleTouchStart)\n this.trackTarget.removeEventListener('touchmove', this.handleTouchMove)\n this.stopAutoplay()\n }\n\n next () {\n const atEnd = this._currentIndex >= this.slides.length - 1\n if (this.loopValue && atEnd) {\n this.slides[0]?.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' })\n } else {\n this.trackTarget.scrollBy({ left: this.trackTarget.clientWidth, behavior: 'smooth' })\n }\n }\n\n previous () {\n const atStart = this._currentIndex <= 0\n if (this.loopValue && atStart) {\n const last = this.slides[this.slides.length - 1]\n last?.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' })\n } else {\n this.trackTarget.scrollBy({ left: -this.trackTarget.clientWidth, behavior: 'smooth' })\n }\n }\n\n goto (event: Event) {\n const button = event.currentTarget as HTMLButtonElement\n const index = parseInt(button.dataset.index ?? '0', 10)\n const slide = this.slides[index]\n slide?.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' })\n }\n\n // Unlocks the next slide and advances to it. Intended for wizard mode: wire\n // a \"Continue\" button inside a slide to this action so users progress\n // linearly through steps before the carousel nav becomes available.\n //\n // <button type=\"button\" data-action=\"click->midwest-carousel#unlockNext\">\n // Continue\n // </button>\n unlockNext () {\n const nextIndex = this._currentIndex + 1\n if (nextIndex > this._maxUnlocked) {\n this._maxUnlocked = nextIndex\n this.updateSlideAccessibility()\n }\n this.next()\n }\n\n scrolled () {\n if (this.wizardValue) this.clampToFrontier()\n this.updateCurrentIndex()\n this.updateNavigation()\n }\n\n // Arrow function so `this` is stable for add/removeEventListener pairing.\n // Handles ArrowLeft/ArrowRight for keyboard slide navigation. Skips form\n // elements so arrow keys continue to work normally inside inputs and selects.\n private handleArrowKeys = (event: KeyboardEvent) => {\n if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') return\n\n const target = event.target as HTMLElement\n if (FORM_ELEMENT_TAGS.has(target.tagName) || target.isContentEditable) return\n\n event.preventDefault()\n if (event.key === 'ArrowLeft') {\n this.previous()\n } else {\n this.next()\n }\n }\n\n // Arrow function so `this` is stable for add/removeEventListener pairing.\n private handleKeydown = (event: KeyboardEvent) => {\n if (event.key !== 'Enter') return\n\n const target = event.target as HTMLElement\n // Only intercept Enter on text-like inputs; textarea newlines and button\n // activations should pass through untouched.\n if (target.tagName === 'TEXTAREA' || target.tagName !== 'INPUT') return\n if (NON_SUBMITTING_INPUT_TYPES.has((target as HTMLInputElement).type)) return\n\n // On the final step let Enter submit the form normally.\n if (this._currentIndex >= this.slides.length - 1) return\n\n event.preventDefault()\n this.unlockNext()\n }\n\n // Blocks wheel-based horizontal scrolling past the wizard frontier.\n private handleWheel = (event: WheelEvent) => {\n const maxLeft = this._maxUnlocked * this.trackTarget.clientWidth\n if (event.deltaX > 0 && this.trackTarget.scrollLeft >= maxLeft - 2) {\n event.preventDefault()\n }\n }\n\n private handleTouchStart = (event: TouchEvent) => {\n this._lastTouchX = event.touches[0].clientX\n }\n\n // Blocks touch-swipe scrolling past the wizard frontier. Tracks incremental\n // delta per move event so direction is accurate mid-gesture.\n private handleTouchMove = (event: TouchEvent) => {\n const currentX = event.touches[0].clientX\n const delta = currentX - this._lastTouchX // positive = swiping right (going back)\n this._lastTouchX = currentX\n\n const maxLeft = this._maxUnlocked * this.trackTarget.clientWidth\n if (delta < 0 && this.trackTarget.scrollLeft >= maxLeft - 2) {\n event.preventDefault()\n }\n }\n\n private pauseAutoplay = () => {\n this.stopAutoplay()\n }\n\n private resumeAutoplay = () => {\n this.startAutoplay()\n }\n\n private startAutoplay () {\n if (this.autoplayValue <= 0 || this._autoplayTimer !== null) return\n this._autoplayTimer = setInterval(() => { this.autoplayAdvance() }, this.autoplayValue)\n }\n\n private stopAutoplay () {\n if (this._autoplayTimer === null) return\n clearInterval(this._autoplayTimer)\n this._autoplayTimer = null\n }\n\n private autoplayAdvance () {\n const atEnd = this._currentIndex >= this.slides.length - 1\n if (atEnd) {\n const firstSlide = this.slides[0]\n firstSlide?.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' })\n } else {\n this.next()\n }\n }\n\n private get slides (): HTMLElement[] {\n return Array.from(this.trackTarget.children) as HTMLElement[]\n }\n\n // Snaps scroll position back to the frontier if the user somehow scrolled\n // past it (e.g. momentum after a partially-blocked gesture).\n private clampToFrontier () {\n const maxLeft = this._maxUnlocked * this.trackTarget.clientWidth\n if (this.trackTarget.scrollLeft > maxLeft + 2) {\n this.trackTarget.scrollLeft = maxLeft\n }\n }\n\n // Applies/removes `inert` on each slide based on the wizard frontier,\n // removing locked slides from the tab order and accessibility tree.\n private updateSlideAccessibility () {\n this.slides.forEach((slide, i) => {\n if (i > this._maxUnlocked) {\n slide.setAttribute('inert', '')\n } else {\n slide.removeAttribute('inert')\n }\n })\n }\n\n private updateCurrentIndex () {\n const track = this.trackTarget\n if (track.clientWidth > 0) {\n this._currentIndex = Math.round(track.scrollLeft / track.clientWidth)\n }\n }\n\n private setupObserver () {\n if (!this.hasDotTarget) return\n if (!('IntersectionObserver' in window)) return\n\n this.observer = new IntersectionObserver(\n (entries) => {\n entries.forEach(entry => {\n const index = this.slides.indexOf(entry.target as HTMLElement)\n if (index >= 0) {\n const dot = this.dotTargets[index]\n if (!dot) return\n dot.classList.toggle('active', entry.isIntersecting)\n if (entry.isIntersecting) {\n dot.setAttribute('aria-current', 'true')\n } else {\n dot.removeAttribute('aria-current')\n }\n }\n })\n },\n { root: this.trackTarget, threshold: 0.5 }\n )\n\n this.slides.forEach(slide => this.observer?.observe(slide))\n }\n\n private updateNavigation () {\n const track = this.trackTarget\n const atStart = track.scrollLeft <= 0\n const atEnd = track.scrollLeft >= track.scrollWidth - track.clientWidth - 1\n\n if (this.hasPrevTarget) this.prevTarget.classList.toggle('hide', atStart && !this.loopValue)\n\n if (this.hasNextTarget) {\n const atLastSlide = this._currentIndex >= this.slides.length - 1\n const frontierReached = this.wizardValue && this._currentIndex >= this._maxUnlocked\n this.nextTarget.classList.toggle('hide', (atEnd || atLastSlide || frontierReached) && !this.loopValue)\n }\n\n if (this.wizardValue && this.hasDotTarget) {\n this.dotTargets.forEach((dot, i) => {\n const locked = i > this._maxUnlocked\n dot.disabled = locked\n dot.classList.toggle('locked', locked)\n })\n }\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,22 +1,75 @@
|
|
|
1
1
|
import { Controller } from '@hotwired/stimulus';
|
|
2
2
|
|
|
3
3
|
class Autocomplete extends Controller {
|
|
4
|
-
static targets = ["input", "dropdown", "list", "tokens", "
|
|
4
|
+
static targets = ["input", "dropdown", "list", "tokens", "multipleTokenTemplate", "singleTokenTemplate", "status", "turboList", "loader"];
|
|
5
5
|
static values = {
|
|
6
6
|
options: { type: Array, default: [] },
|
|
7
7
|
url: { type: String, default: "" },
|
|
8
|
-
multiple: { type: Boolean, default: true }
|
|
8
|
+
multiple: { type: Boolean, default: true },
|
|
9
|
+
turboFrameUrl: { type: String, default: "" }
|
|
9
10
|
};
|
|
10
|
-
selectedValues = /* @__PURE__ */ new Map();
|
|
11
11
|
highlightedIndex = -1;
|
|
12
12
|
fetchAbort = null;
|
|
13
|
+
searchTimer;
|
|
14
|
+
// ── Named handlers ─────────────────────────────────────────────────────────
|
|
15
|
+
// Arrow-function class properties preserve `this` and produce a stable
|
|
16
|
+
// reference so addEventListener/removeEventListener pairs always match.
|
|
17
|
+
onFrameLoad = () => {
|
|
18
|
+
this.syncSelectedState();
|
|
19
|
+
this.loading = false;
|
|
20
|
+
this.showDropdown();
|
|
21
|
+
};
|
|
22
|
+
onToggle = (e) => {
|
|
23
|
+
const opened = e.newState === "open";
|
|
24
|
+
this.inputTarget.setAttribute("aria-expanded", String(opened));
|
|
25
|
+
if (!opened) {
|
|
26
|
+
this.inputTarget.removeAttribute("aria-activedescendant");
|
|
27
|
+
this.highlightedIndex = -1;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
onFocusOut = (e) => {
|
|
31
|
+
if (!this.element.contains(e.relatedTarget)) {
|
|
32
|
+
this.hideDropdown();
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
onListMouseDown = (e) => {
|
|
36
|
+
const option = e.target.closest(".midwest-autocomplete-option");
|
|
37
|
+
if (option) {
|
|
38
|
+
e.preventDefault();
|
|
39
|
+
this.pickItem(option);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
// ── Lifecycle ───────────────────────────────────────────────────────────────
|
|
13
43
|
connect() {
|
|
14
|
-
this.
|
|
44
|
+
this.element.addEventListener("focusout", this.onFocusOut);
|
|
15
45
|
}
|
|
16
46
|
disconnect() {
|
|
17
47
|
this.fetchAbort?.abort();
|
|
48
|
+
clearTimeout(this.searchTimer);
|
|
49
|
+
this.element.removeEventListener("focusout", this.onFocusOut);
|
|
18
50
|
}
|
|
19
|
-
//
|
|
51
|
+
// Target callbacks — Stimulus calls these automatically when targets connect
|
|
52
|
+
// or disconnect, including after the initial connect(). This replaces manual
|
|
53
|
+
// hasTurboListTarget guards and addEventListener calls inside connect().
|
|
54
|
+
dropdownTargetConnected(el) {
|
|
55
|
+
el.addEventListener("toggle", this.onToggle);
|
|
56
|
+
}
|
|
57
|
+
dropdownTargetDisconnected(el) {
|
|
58
|
+
el.removeEventListener("toggle", this.onToggle);
|
|
59
|
+
}
|
|
60
|
+
listTargetConnected(el) {
|
|
61
|
+
el.addEventListener("mousedown", this.onListMouseDown);
|
|
62
|
+
}
|
|
63
|
+
listTargetDisconnected(el) {
|
|
64
|
+
el.removeEventListener("mousedown", this.onListMouseDown);
|
|
65
|
+
}
|
|
66
|
+
turboListTargetConnected(el) {
|
|
67
|
+
el.addEventListener("turbo:frame-load", this.onFrameLoad);
|
|
68
|
+
}
|
|
69
|
+
turboListTargetDisconnected(el) {
|
|
70
|
+
el.removeEventListener("turbo:frame-load", this.onFrameLoad);
|
|
71
|
+
}
|
|
72
|
+
// ── Public actions ──────────────────────────────────────────────────────────
|
|
20
73
|
focusInput() {
|
|
21
74
|
this.inputTarget.focus();
|
|
22
75
|
this.open();
|
|
@@ -24,21 +77,20 @@ class Autocomplete extends Controller {
|
|
|
24
77
|
open() {
|
|
25
78
|
if (this.dropdownTarget.matches(":popover-open"))
|
|
26
79
|
return;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
this.fetch(query);
|
|
30
|
-
} else {
|
|
31
|
-
this.renderOptions(this.filteredOptions(query));
|
|
32
|
-
}
|
|
80
|
+
clearTimeout(this.searchTimer);
|
|
81
|
+
this.resolve(this.inputTarget.value.trim());
|
|
33
82
|
}
|
|
34
|
-
|
|
83
|
+
search() {
|
|
35
84
|
const query = this.inputTarget.value.trim();
|
|
36
|
-
if (this.urlValue) {
|
|
37
|
-
await this.fetch(query);
|
|
38
|
-
} else {
|
|
85
|
+
if (!this.hasTurboListTarget && !this.urlValue) {
|
|
39
86
|
this.renderOptions(this.filteredOptions(query));
|
|
87
|
+
return;
|
|
40
88
|
}
|
|
41
|
-
this.
|
|
89
|
+
clearTimeout(this.searchTimer);
|
|
90
|
+
this.searchTimer = window.setTimeout(() => {
|
|
91
|
+
this.searchTimer = void 0;
|
|
92
|
+
this.resolve(query);
|
|
93
|
+
}, 200);
|
|
42
94
|
}
|
|
43
95
|
navigate(event) {
|
|
44
96
|
switch (event.key) {
|
|
@@ -53,6 +105,8 @@ class Autocomplete extends Controller {
|
|
|
53
105
|
}
|
|
54
106
|
case "ArrowUp": {
|
|
55
107
|
event.preventDefault();
|
|
108
|
+
if (!this.dropdownTarget.matches(":popover-open"))
|
|
109
|
+
break;
|
|
56
110
|
const items = this.visibleOptions();
|
|
57
111
|
this.highlightedIndex = Math.max(this.highlightedIndex - 1, 0);
|
|
58
112
|
this.syncHighlight(items);
|
|
@@ -60,6 +114,8 @@ class Autocomplete extends Controller {
|
|
|
60
114
|
}
|
|
61
115
|
case "Enter": {
|
|
62
116
|
event.preventDefault();
|
|
117
|
+
if (!this.dropdownTarget.matches(":popover-open"))
|
|
118
|
+
break;
|
|
63
119
|
const items = this.visibleOptions();
|
|
64
120
|
if (this.highlightedIndex >= 0 && items[this.highlightedIndex]) {
|
|
65
121
|
this.pickItem(items[this.highlightedIndex]);
|
|
@@ -71,19 +127,14 @@ class Autocomplete extends Controller {
|
|
|
71
127
|
break;
|
|
72
128
|
case "Backspace":
|
|
73
129
|
if (this.inputTarget.value === "" && this.multipleValue) {
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
130
|
+
const tokens = this.tokensTarget.querySelectorAll("[data-value]");
|
|
131
|
+
const last = tokens[tokens.length - 1];
|
|
132
|
+
if (last?.dataset.value)
|
|
133
|
+
this.deselect(last.dataset.value);
|
|
77
134
|
}
|
|
78
135
|
break;
|
|
79
136
|
}
|
|
80
137
|
}
|
|
81
|
-
visibleOptions() {
|
|
82
|
-
return this.listTarget.querySelectorAll('.midwest-autocomplete-option:not([aria-selected="true"])');
|
|
83
|
-
}
|
|
84
|
-
handleBlur() {
|
|
85
|
-
setTimeout(() => this.hideDropdown(), 150);
|
|
86
|
-
}
|
|
87
138
|
removeToken(event) {
|
|
88
139
|
event.stopPropagation();
|
|
89
140
|
const btn = event.currentTarget;
|
|
@@ -91,16 +142,31 @@ class Autocomplete extends Controller {
|
|
|
91
142
|
if (value)
|
|
92
143
|
this.deselect(value);
|
|
93
144
|
}
|
|
94
|
-
//
|
|
145
|
+
// Public action used by turbo-frame option items via data-action.
|
|
146
|
+
// Each server-rendered <li> should have:
|
|
147
|
+
// data-action="mousedown->midwest-autocomplete#selectOption"
|
|
148
|
+
selectOption(event) {
|
|
149
|
+
event.preventDefault();
|
|
150
|
+
this.pickItem(event.currentTarget);
|
|
151
|
+
}
|
|
152
|
+
// ── Private ─────────────────────────────────────────────────────────────────
|
|
95
153
|
get optionIdPrefix() {
|
|
96
154
|
return `ac-opt-${(this.element.dataset.name ?? "field").replace(/\W/g, "-")}`;
|
|
97
155
|
}
|
|
98
|
-
|
|
99
|
-
this.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
156
|
+
isSelected(value) {
|
|
157
|
+
return !!this.tokensTarget.querySelector(`[data-value="${CSS.escape(value)}"]`);
|
|
158
|
+
}
|
|
159
|
+
// Routes to the correct data source. Shared by open() and the debounced search path.
|
|
160
|
+
resolve(query) {
|
|
161
|
+
if (this.hasTurboListTarget) {
|
|
162
|
+
this.navigateFrame(query);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (this.urlValue) {
|
|
166
|
+
this.fetch(query);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
this.renderOptions(this.filteredOptions(query));
|
|
104
170
|
}
|
|
105
171
|
filteredOptions(query) {
|
|
106
172
|
const q = query.toLowerCase();
|
|
@@ -108,10 +174,36 @@ class Autocomplete extends Controller {
|
|
|
108
174
|
(o) => q === "" || o.label.toLowerCase().includes(q) || (o.support?.toLowerCase().includes(q) ?? false)
|
|
109
175
|
);
|
|
110
176
|
}
|
|
177
|
+
navigateFrame(query) {
|
|
178
|
+
const url = new URL(this.turboFrameUrlValue, window.location.href);
|
|
179
|
+
if (query)
|
|
180
|
+
url.searchParams.set("q", query);
|
|
181
|
+
this.tokensTarget.querySelectorAll("input[type=hidden]").forEach((input) => {
|
|
182
|
+
url.searchParams.append("selected[]", input.value);
|
|
183
|
+
});
|
|
184
|
+
this.loading = true;
|
|
185
|
+
this.turboListTarget.setAttribute("src", url.toString());
|
|
186
|
+
this.showDropdown();
|
|
187
|
+
}
|
|
188
|
+
// After a turbo frame loads, mark already-selected items and update the live region.
|
|
189
|
+
// The empty state element is always present in the frame and CSS shows/hides it
|
|
190
|
+
// based on whether any non-selected options exist.
|
|
191
|
+
syncSelectedState() {
|
|
192
|
+
const options = this.turboListTarget.querySelectorAll(".midwest-autocomplete-option");
|
|
193
|
+
options.forEach((option) => {
|
|
194
|
+
const val = option.dataset.value ?? "";
|
|
195
|
+
option.setAttribute("aria-selected", this.isSelected(val) ? "true" : "false");
|
|
196
|
+
});
|
|
197
|
+
const visibleCount = this.visibleOptions().length;
|
|
198
|
+
this.announce(visibleCount > 0 ? `${visibleCount} option${visibleCount === 1 ? "" : "s"} available` : "No more options");
|
|
199
|
+
}
|
|
111
200
|
async fetch(query) {
|
|
112
201
|
this.fetchAbort?.abort();
|
|
113
202
|
this.fetchAbort = new AbortController();
|
|
114
|
-
this.
|
|
203
|
+
this.listTarget.replaceChildren();
|
|
204
|
+
this.loading = true;
|
|
205
|
+
this.announce("Loading");
|
|
206
|
+
this.showDropdown();
|
|
115
207
|
try {
|
|
116
208
|
const url = new URL(this.urlValue, window.location.href);
|
|
117
209
|
if (query)
|
|
@@ -129,11 +221,6 @@ class Autocomplete extends Controller {
|
|
|
129
221
|
}
|
|
130
222
|
}
|
|
131
223
|
select(option) {
|
|
132
|
-
if (!this.multipleValue) {
|
|
133
|
-
this.selectedValues.forEach((_, val) => this.removeTokenElement(val));
|
|
134
|
-
this.selectedValues.clear();
|
|
135
|
-
}
|
|
136
|
-
this.selectedValues.set(option.value, option);
|
|
137
224
|
this.appendToken(option);
|
|
138
225
|
this.hideDropdown();
|
|
139
226
|
this.inputTarget.value = "";
|
|
@@ -142,102 +229,106 @@ class Autocomplete extends Controller {
|
|
|
142
229
|
this.dispatch("change", { detail: { value: option.value, label: option.label } });
|
|
143
230
|
}
|
|
144
231
|
deselect(value) {
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
if (
|
|
149
|
-
this.announce(`${
|
|
232
|
+
const token = this.tokensTarget.querySelector(`[data-value="${CSS.escape(value)}"]`);
|
|
233
|
+
const label = token?.querySelector(".value")?.textContent?.trim();
|
|
234
|
+
token?.remove();
|
|
235
|
+
if (label)
|
|
236
|
+
this.announce(`${label} removed`);
|
|
150
237
|
this.dispatch("change", { detail: { value: null } });
|
|
151
238
|
}
|
|
152
239
|
appendToken(option) {
|
|
153
|
-
|
|
240
|
+
if (!this.multipleValue) {
|
|
241
|
+
this.tokensTarget.querySelectorAll("[data-value]").forEach((el) => el.remove());
|
|
242
|
+
}
|
|
243
|
+
const template = this.multipleValue ? this.multipleTokenTemplateTarget : this.singleTokenTemplateTarget;
|
|
244
|
+
const fragment = template.content.cloneNode(true);
|
|
245
|
+
const badge = fragment.querySelector(".midwest-badge");
|
|
246
|
+
if (!badge)
|
|
247
|
+
return;
|
|
248
|
+
badge.dataset.value = option.value;
|
|
249
|
+
const valueEl = badge.querySelector(".value");
|
|
250
|
+
if (valueEl)
|
|
251
|
+
valueEl.textContent = option.label;
|
|
154
252
|
if (this.multipleValue) {
|
|
155
|
-
const fragment = this.multipleTokenTemplateTarget.content.cloneNode(true);
|
|
156
|
-
const badge = fragment.querySelector(".midwest-badge");
|
|
157
|
-
badge.dataset.value = option.value;
|
|
158
|
-
badge.querySelector(".value").textContent = option.label;
|
|
159
253
|
const removeBtn = badge.querySelector(".remove");
|
|
160
|
-
removeBtn
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
this.
|
|
168
|
-
const fragment = this.singleTokenTemplateTarget.content.cloneNode(true);
|
|
169
|
-
const badge = fragment.querySelector(".midwest-badge");
|
|
170
|
-
badge.dataset.value = option.value;
|
|
171
|
-
badge.querySelector(".value").textContent = option.label;
|
|
172
|
-
const input = badge.querySelector("input[type=hidden]");
|
|
173
|
-
input.name = name;
|
|
254
|
+
if (removeBtn) {
|
|
255
|
+
removeBtn.dataset.value = option.value;
|
|
256
|
+
removeBtn.setAttribute("aria-label", `Remove ${option.label}`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
const input = badge.querySelector("input[type=hidden]");
|
|
260
|
+
if (input) {
|
|
261
|
+
input.name = this.element.dataset.name ?? "";
|
|
174
262
|
input.value = option.value;
|
|
175
|
-
this.tokensTarget.appendChild(fragment);
|
|
176
263
|
}
|
|
177
|
-
|
|
178
|
-
removeTokenElement(value) {
|
|
179
|
-
const token = this.tokensTarget.querySelector(`[data-value="${CSS.escape(value)}"]`);
|
|
180
|
-
token?.remove();
|
|
264
|
+
this.tokensTarget.appendChild(fragment);
|
|
181
265
|
}
|
|
182
266
|
renderOptions(options) {
|
|
183
|
-
this.listTarget.innerHTML = "";
|
|
184
267
|
this.highlightedIndex = -1;
|
|
185
268
|
if (options.length === 0) {
|
|
186
269
|
this.renderEmpty();
|
|
187
270
|
return;
|
|
188
271
|
}
|
|
272
|
+
this.listTarget.replaceChildren();
|
|
189
273
|
options.forEach((option, index) => {
|
|
190
274
|
const li = document.createElement("li");
|
|
191
275
|
li.id = `${this.optionIdPrefix}-${index}`;
|
|
192
276
|
li.className = "midwest-autocomplete-option";
|
|
193
277
|
li.setAttribute("role", "option");
|
|
194
|
-
li.setAttribute("aria-selected", this.
|
|
278
|
+
li.setAttribute("aria-selected", this.isSelected(option.value) ? "true" : "false");
|
|
195
279
|
li.dataset.value = option.value;
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
280
|
+
const label = document.createElement("span");
|
|
281
|
+
label.className = "midwest-autocomplete-option-label";
|
|
282
|
+
label.textContent = option.label;
|
|
283
|
+
li.appendChild(label);
|
|
284
|
+
if (option.support) {
|
|
285
|
+
const support = document.createElement("span");
|
|
286
|
+
support.className = "midwest-autocomplete-option-support";
|
|
287
|
+
support.textContent = option.support;
|
|
288
|
+
li.appendChild(support);
|
|
289
|
+
}
|
|
204
290
|
this.listTarget.appendChild(li);
|
|
205
291
|
});
|
|
206
|
-
const availableCount = options.filter((o) => !this.
|
|
292
|
+
const availableCount = options.filter((o) => !this.isSelected(o.value)).length;
|
|
207
293
|
if (availableCount === 0) {
|
|
208
294
|
this.renderEmpty("No more options");
|
|
209
295
|
return;
|
|
210
296
|
}
|
|
297
|
+
this.loading = false;
|
|
211
298
|
this.announce(`${availableCount} option${availableCount === 1 ? "" : "s"} available`);
|
|
212
299
|
this.showDropdown();
|
|
213
300
|
}
|
|
214
301
|
renderEmpty(message = "No results") {
|
|
215
|
-
|
|
302
|
+
const li = document.createElement("li");
|
|
303
|
+
li.className = "midwest-autocomplete-empty";
|
|
304
|
+
li.setAttribute("aria-hidden", "true");
|
|
305
|
+
li.textContent = message;
|
|
306
|
+
this.listTarget.replaceChildren(li);
|
|
307
|
+
this.loading = false;
|
|
216
308
|
this.announce(message);
|
|
217
309
|
this.showDropdown();
|
|
218
310
|
}
|
|
219
|
-
|
|
220
|
-
this.
|
|
221
|
-
|
|
222
|
-
this.showDropdown();
|
|
311
|
+
set loading(on) {
|
|
312
|
+
if (this.hasLoaderTarget)
|
|
313
|
+
this.loaderTarget.hidden = !on;
|
|
223
314
|
}
|
|
224
315
|
showDropdown() {
|
|
225
316
|
if (!this.dropdownTarget.matches(":popover-open"))
|
|
226
317
|
this.dropdownTarget.showPopover();
|
|
227
|
-
this.inputTarget.setAttribute("aria-expanded", "true");
|
|
228
318
|
}
|
|
229
319
|
hideDropdown() {
|
|
230
320
|
if (this.dropdownTarget.matches(":popover-open"))
|
|
231
321
|
this.dropdownTarget.hidePopover();
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
this.
|
|
322
|
+
}
|
|
323
|
+
visibleOptions() {
|
|
324
|
+
const container = this.hasTurboListTarget ? this.turboListTarget : this.listTarget;
|
|
325
|
+
return container.querySelectorAll('.midwest-autocomplete-option:not([aria-selected="true"])');
|
|
235
326
|
}
|
|
236
327
|
syncHighlight(items) {
|
|
237
328
|
items.forEach((item, i) => item.classList.toggle("is-highlighted", i === this.highlightedIndex));
|
|
238
329
|
const active = items[this.highlightedIndex];
|
|
239
330
|
active?.scrollIntoView({ block: "nearest" });
|
|
240
|
-
if (active) {
|
|
331
|
+
if (active?.id) {
|
|
241
332
|
this.inputTarget.setAttribute("aria-activedescendant", active.id);
|
|
242
333
|
} else {
|
|
243
334
|
this.inputTarget.removeAttribute("aria-activedescendant");
|
|
@@ -245,7 +336,7 @@ class Autocomplete extends Controller {
|
|
|
245
336
|
}
|
|
246
337
|
pickItem(item) {
|
|
247
338
|
const value = item.dataset.value ?? "";
|
|
248
|
-
const label = item.querySelector(".midwest-autocomplete-option-label")?.textContent?.trim()
|
|
339
|
+
const label = item.dataset.label?.trim() || item.querySelector(".midwest-autocomplete-option-label")?.textContent?.trim() || value;
|
|
249
340
|
const support = item.querySelector(".midwest-autocomplete-option-support")?.textContent?.trim();
|
|
250
341
|
this.select({ value, label, support });
|
|
251
342
|
}
|
|
@@ -253,9 +344,6 @@ class Autocomplete extends Controller {
|
|
|
253
344
|
if (this.hasStatusTarget)
|
|
254
345
|
this.statusTarget.textContent = message;
|
|
255
346
|
}
|
|
256
|
-
escapeHtml(str) {
|
|
257
|
-
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
258
|
-
}
|
|
259
347
|
}
|
|
260
348
|
|
|
261
349
|
export { Autocomplete as default };
|