stimulus_plumbers 0.2.7 → 0.2.8
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +38 -0
- data/README.md +57 -41
- data/app/assets/javascripts/stimulus-plumbers/stimulus-plumbers-controllers.es.js +734 -248
- data/app/assets/javascripts/stimulus-plumbers/stimulus-plumbers-controllers.umd.js +1 -1
- data/lib/stimulus_plumbers/components/combobox/autocomplete.rb +57 -0
- data/lib/stimulus_plumbers/components/combobox/date.rb +52 -0
- data/lib/stimulus_plumbers/components/combobox/dropdown.rb +41 -0
- data/lib/stimulus_plumbers/components/combobox/option.rb +27 -0
- data/lib/stimulus_plumbers/components/combobox/option_group.rb +52 -0
- data/lib/stimulus_plumbers/components/combobox/renderer.rb +78 -0
- data/lib/stimulus_plumbers/components/combobox/time.rb +103 -0
- data/lib/stimulus_plumbers/components/date_picker/navigation.rb +1 -1
- data/lib/stimulus_plumbers/components/plumber/html_options.rb +22 -3
- data/lib/stimulus_plumbers/components/time_picker/renderer.rb +38 -0
- data/lib/stimulus_plumbers/form/builder.rb +57 -12
- data/lib/stimulus_plumbers/form/field_component.rb +12 -10
- data/lib/stimulus_plumbers/form/fields/combobox.rb +41 -0
- data/lib/stimulus_plumbers/form/fields/password.rb +55 -0
- data/lib/stimulus_plumbers/form/fields/renderer.rb +1 -2
- data/lib/stimulus_plumbers/form/fields/search.rb +40 -0
- data/lib/stimulus_plumbers/form/fields/select.rb +8 -2
- data/lib/stimulus_plumbers/form/fields/text.rb +12 -4
- data/lib/stimulus_plumbers/helpers/combobox_helper.rb +74 -0
- data/lib/stimulus_plumbers/helpers.rb +2 -2
- data/lib/stimulus_plumbers/themes/{form.rb → base/form.rb} +6 -2
- data/lib/stimulus_plumbers/themes/base.rb +7 -7
- data/lib/stimulus_plumbers/themes/tailwind/form.rb +10 -6
- data/lib/stimulus_plumbers/version.rb +1 -1
- data/lib/stimulus_plumbers.rb +18 -1
- metadata +21 -10
- data/lib/stimulus_plumbers/components/date_picker/renderer.rb +0 -82
- data/lib/stimulus_plumbers/helpers/date_picker_helper.rb +0 -17
- /data/lib/stimulus_plumbers/themes/{action_list.rb → base/action_list.rb} +0 -0
- /data/lib/stimulus_plumbers/themes/{avatar.rb → base/avatar.rb} +0 -0
- /data/lib/stimulus_plumbers/themes/{button.rb → base/button.rb} +0 -0
- /data/lib/stimulus_plumbers/themes/{calendar.rb → base/calendar.rb} +0 -0
- /data/lib/stimulus_plumbers/themes/{card.rb → base/card.rb} +0 -0
- /data/lib/stimulus_plumbers/themes/{layout.rb → base/layout.rb} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require(`@hotwired/stimulus`)):typeof define==`function`&&define.amd?define([`exports`,`@hotwired/stimulus`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.StimulusPlumbersControllers={},e.Stimulus))})(this,function(e,t){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var n=[`a[href]`,`area[href]`,`button:not([disabled])`,`input:not([disabled])`,`select:not([disabled])`,`textarea:not([disabled])`,`[tabindex]:not([tabindex="-1"])`,`audio[controls]`,`video[controls]`,`[contenteditable]:not([contenteditable="false"])`].join(`,`);function r(e){return Array.from(e.querySelectorAll(n)).filter(e=>i(e))}function i(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)}function a(e){let t=r(e);return t.length>0?(t[0].focus(),!0):!1}var o=class{constructor(e,t={}){this.container=e,this.previouslyFocused=null,this.options=t,this.isActive=!1}activate(){this.isActive||(this.previouslyFocused=document.activeElement,this.isActive=!0,this.options.initialFocus?this.options.initialFocus.focus():a(this.container),this.container.addEventListener(`keydown`,this.handleKeyDown))}deactivate(){if(!this.isActive)return;this.isActive=!1,this.container.removeEventListener(`keydown`,this.handleKeyDown);let e=this.options.returnFocus||this.previouslyFocused;e&&i(e)&&e.focus()}handleKeyDown=e=>{if(e.key===`Escape`&&this.options.escapeDeactivates){e.preventDefault(),this.deactivate();return}if(e.key!==`Tab`)return;let t=r(this.container);if(t.length===0)return;let n=t[0],i=t[t.length-1];e.shiftKey&&document.activeElement===n?(e.preventDefault(),i.focus()):!e.shiftKey&&document.activeElement===i&&(e.preventDefault(),n.focus())}},s=class{constructor(){this.savedElement=null}save(){this.savedElement=document.activeElement}restore(){this.savedElement&&i(this.savedElement)&&(this.savedElement.focus(),this.savedElement=null)}};function c(e,t){return e.key===t}function l(e){return e.key===`Enter`||e.key===` `}function u(e){return[`ArrowUp`,`ArrowDown`,`ArrowLeft`,`ArrowRight`].includes(e.key)}function d(e){e.preventDefault(),e.stopPropagation()}var f=class{constructor(e,t=0){this.items=e,this.currentIndex=t,this.updateTabIndex()}handleKeyDown(e){let t;switch(e.key){case`ArrowDown`:case`ArrowRight`:e.preventDefault(),t=(this.currentIndex+1)%this.items.length;break;case`ArrowUp`:case`ArrowLeft`:e.preventDefault(),t=this.currentIndex===0?this.items.length-1:this.currentIndex-1;break;case`Home`:e.preventDefault(),t=0;break;case`End`:e.preventDefault(),t=this.items.length-1;break;default:return}this.setCurrentIndex(t)}setCurrentIndex(e){e>=0&&e<this.items.length&&(this.currentIndex=e,this.updateTabIndex(),this.items[e].focus())}updateTabIndex(){this.items.forEach((e,t)=>{e.tabIndex=t===this.currentIndex?0:-1})}updateItems(e){this.items=e,this.currentIndex=Math.min(this.currentIndex,e.length-1),this.updateTabIndex()}},p=(e,t,n)=>{let r=document.querySelector(`[data-live-region="${e}"]`);return r||(r=document.createElement(`div`),r.className=`sr-only`,r.dataset.liveRegion=e,r.setAttribute(`aria-live`,e),r.setAttribute(`aria-atomic`,t.toString()),r.setAttribute(`aria-relevant`,n),document.body.appendChild(r)),r};function m(e,t={}){let{politeness:n=`polite`,atomic:r=!0,relevant:i=`additions text`}=t,a=p(n,r,i);a.textContent=``,setTimeout(()=>{a.textContent=e},100)}var h=(e=`a11y`)=>`${e}-${Math.random().toString(36).substr(2,9)}`,g=(e,t=`element`)=>e.id||=h(t),_=(e,t,n)=>{e.setAttribute(t,n.toString())},v=(e,t)=>_(e,`aria-expanded`,t),y=(e,t)=>_(e,`aria-pressed`,t),b=(e,t)=>_(e,`aria-checked`,t);function x(e,t){_(e,`aria-disabled`,t),t?e.setAttribute(`tabindex`,`-1`):e.removeAttribute(`tabindex`)}var S={menu:`menu`,listbox:`listbox`,tree:`tree`,grid:`grid`,dialog:`dialog`},C=(e,t,n)=>{Object.entries(t).forEach(([t,r])=>{e.setAttribute(t,r),n[t]=r})},w=(e,t,n)=>n||!e.hasAttribute(t);function T({trigger:e,target:t,role:n=null,override:r=!1}){let i={trigger:{},target:{}};if(!e||!t)return i;let a={},o={};if(n&&w(t,`role`,r)&&(o.role=n),t.id&&(w(e,`aria-controls`,r)&&(a[`aria-controls`]=t.id),n===`tooltip`&&w(e,`aria-describedby`,r)&&(a[`aria-describedby`]=t.id)),n&&w(e,`aria-haspopup`,r)){let e=S[n]||`true`;e&&(a[`aria-haspopup`]=e)}return C(t,o,i.target),C(e,a,i.trigger),i}var E=(e,t)=>{t.forEach(t=>{e.hasAttribute(t)&&e.removeAttribute(t)})};function ee({trigger:e,target:t,attributes:n=null}){!e||!t||(E(e,n||[`aria-controls`,`aria-haspopup`,`aria-describedby`]),(!n||n.includes(`role`))&&E(t,[`role`]))}var D={get visibleOnly(){return!0},hiddenClass:null},O={get top(){return`bottom`},get bottom(){return`top`},get left(){return`right`},get right(){return`left`}};function k({x:e,y:t,width:n,height:r}){return{x:e,y:t,width:n,height:r,left:e,right:e+n,top:t,bottom:t+r}}function A(){return k({x:0,y:0,width:window.innerWidth||document.documentElement.clientWidth,height:window.innerHeight||document.documentElement.clientHeight})}function j(e){if(!(e instanceof HTMLElement))return!1;let t=A(),n=e.getBoundingClientRect(),r=n.top<=t.height&&n.top+n.height>0,i=n.left<=t.width&&n.left+n.width>0;return r&&i}function M(e){return e instanceof Date&&!isNaN(e)}function N(...e){if(e.length===0)throw`Missing values to parse as date`;if(e.length===1){let t=new Date(e[0]);if(e[0]&&M(t))return t}else{let t=new Date(...e);if(M(t))return t}}var te={element:null,visible:null,dispatch:!0,prefix:``},P=class{constructor(e,t={}){this.controller=e;let{element:n,visible:r,dispatch:i,prefix:a}=Object.assign({},te,t);this.element=n||e.element,this.visibleOnly=typeof r==`boolean`?r:D.visibleOnly,this.visibleCallback=typeof r==`string`?r:null,this.notify=!!i,this.prefix=typeof a==`string`&&a?a:e.identifier}get visible(){return this.element instanceof HTMLElement?this.visibleOnly?j(this.element)&&this.isVisible(this.element):!0:!1}isVisible(e){if(this.visibleCallback){let t=this.findCallback(this.visibleCallback);if(typeof t==`function`)return t(e)}return e instanceof HTMLElement?!e.hasAttribute(`hidden`):!1}dispatch(e,{target:t=null,prefix:n=null,detail:r=null}={}){if(this.notify)return this.controller.dispatch(e,{target:t||this.element,prefix:n||this.prefix,detail:r})}findCallback(e){if(typeof e!=`string`)return;let t=this,n=e.split(`.`).reduce((e,t)=>e&&e[t],t.controller);if(typeof n==`function`)return n.bind(t.controller);let r=e.split(`.`).reduce((e,t)=>e&&e[t],t);if(typeof r==`function`)return r.bind(t)}async awaitCallback(e,...t){if(typeof e==`string`&&(e=this.findCallback(e)),typeof e==`function`){let n=e(...t);return n instanceof Promise?await n:n}}},F=7,I={locales:[`default`],today:``,day:null,month:null,year:null,since:null,till:null,disabledDates:[],disabledWeekdays:[],disabledDays:[],disabledMonths:[],disabledYears:[],firstDayOfWeek:0,onNavigated:`navigated`},L=class extends P{constructor(e,t={}){super(e,t);let n=Object.assign({},I,t),{onNavigated:r,since:i,till:a,firstDayOfWeek:o}=n;this.onNavigated=r,this.since=N(i),this.till=N(a),this.firstDayOfWeek=0<=o&&o<7?o:I.firstDayOfWeek;let{disabledDates:s,disabledWeekdays:c,disabledDays:l,disabledMonths:u,disabledYears:d}=n;this.disabledDates=Array.isArray(s)?s:[],this.disabledWeekdays=Array.isArray(c)?c:[],this.disabledDays=Array.isArray(l)?l:[],this.disabledMonths=Array.isArray(u)?u:[],this.disabledYears=Array.isArray(d)?d:[];let{today:f,day:p,month:m,year:h}=n;this.now=N(f)||new Date,typeof h==`number`&&typeof m==`number`&&typeof p==`number`?this.current=N(h,m,p):this.current=this.now,this.build(),this.enhance()}build(){this.daysOfWeek=this.buildDaysOfWeek(),this.daysOfMonth=this.buildDaysOfMonth(),this.monthsOfYear=this.buildMonthsOfYear()}buildDaysOfWeek(){let e=new Intl.DateTimeFormat(this.localesValue,{weekday:`long`}),t=new Intl.DateTimeFormat(this.localesValue,{weekday:`short`}),n=new Date(`2024-10-06`),r=[];for(let i=this.firstDayOfWeek,a=i+7;i<a;i++){let a=new Date(n);a.setDate(n.getDate()+i),r.push({date:a,value:a.getDay(),long:e.format(a),short:t.format(a)})}return r}buildDaysOfMonth(){let e=this.month,t=this.year,n=[],r=e=>({current:this.month===e.getMonth()&&this.year===e.getFullYear(),date:e,value:e.getDate(),month:e.getMonth(),year:e.getFullYear(),iso:e.toISOString()}),i=new Date(t,e).getDay(),a=this.firstDayOfWeek-i;for(let i=a>0?a-7:a;i<0;i++){let a=new Date(t,e,i+1);n.push(r(a))}let o=new Date(t,e+1,0).getDate();for(let i=1;i<=o;i++){let a=new Date(t,e,i);n.push(r(a))}let s=n.length%F,c=s===0?0:F-s;for(let i=1;i<=c;i++){let a=new Date(t,e+1,i);n.push(r(a))}return n}buildMonthsOfYear(){let e=new Intl.DateTimeFormat(this.localesValue,{month:`long`}),t=new Intl.DateTimeFormat(this.localesValue,{month:`short`}),n=new Intl.DateTimeFormat(this.localesValue,{month:`numeric`}),r=[];for(let i=0;i<12;i++){let a=new Date(this.year,i);r.push({date:a,value:a.getMonth(),long:e.format(a),short:t.format(a),numeric:n.format(a)})}return r}get today(){return this.now}set today(e){if(!M(e))return;let t=this.month?this.month:e.getMonth(),n=this.year?this.year:e.getFullYear(),r=t==e.getMonth()&&n==e.getFullYear(),i=this.hasDayValue?this.day:r?e.getDate():1;this.now=new Date(n,t,i).toISOString()}get current(){return typeof this.year==`number`&&typeof this.month==`number`&&typeof this.day==`number`?N(this.year,this.month,this.day):null}set current(e){M(e)&&(this.day=e.getDate(),this.month=e.getMonth(),this.year=e.getFullYear())}navigate=async e=>{if(!M(e))return;let t=this.current,n=e.toISOString(),r=t.toISOString();this.dispatch(`navigate`,{detail:{from:r,to:n}}),this.current=e,this.build(),await this.awaitCallback(this.onNavigated,{from:r,to:n}),this.dispatch(`navigated`,{detail:{from:r,to:n}})};step=async(e,t)=>{if(t===0)return;let n=this.current;switch(e){case`year`:n.setFullYear(n.getFullYear()+t);break;case`month`:n.setMonth(n.getMonth()+t);break;case`day`:n.setDate(n.getDate()+t);break;default:return}await this.navigate(n)};isDisabled=e=>{if(!M(e))return!1;if(this.disabledDates.length){let t=e.getTime();for(let e of this.disabledDates)if(t===new Date(e).getTime())return!0}if(this.disabledWeekdays.length){let t=e.getDay(),n=this.daysOfWeek,r=n.findIndex(e=>e.value===t);if(r>=0){let e=n[r];for(let t of this.disabledWeekdays)if(e.value==t||e.short===t||e.long===t)return!0}}if(this.disabledDays.length){let t=e.getDate();for(let e of this.disabledDays)if(t==e)return!0}if(this.disabledMonths.length){let t=e.getMonth(),n=this.monthsOfYear,r=n.findIndex(e=>e.value===t);if(r>=0){let e=n[r];for(let t of this.disabledMonths)if(e.value==t||e.short===t||e.long===t)return!0}}if(this.disabledYears.length){let t=e.getFullYear();for(let e of this.disabledYears)if(t==e)return!0}return!1};isWithinRange=e=>{if(!M(e))return!1;let t=!0;return this.since&&(t&&=e>=this.since),this.till&&(t&&=e<=this.till),t};enhance(){let e=this;Object.assign(this.controller,{get calendar(){return{get today(){return e.today},get current(){return e.current},get day(){return e.day},get month(){return e.month},get year(){return e.year},get since(){return e.since},get till(){return e.till},get firstDayOfWeek(){return e.firstDayOfWeek},get disabledDates(){return e.disabledDates},get disabledWeekdays(){return e.disabledWeekdays},get disabledDays(){return e.disabledDays},get disabledMonths(){return e.disabledMonths},get disabledYears(){return e.disabledYears},get daysOfWeek(){return e.daysOfWeek},get daysOfMonth(){return e.daysOfMonth},get monthsOfYear(){return e.monthsOfYear},navigate:async t=>await e.navigate(t),step:async(t,n)=>await e.step(t,n),isDisabled:t=>e.isDisabled(t),isWithinRange:t=>e.isWithinRange(t)}}})}},R=(e,t)=>new L(e,t),z={content:null,url:``,reload:`never`,stale:3600,onLoad:`contentLoad`,onLoading:`contentLoading`,onLoaded:`contentLoaded`},B=class extends P{constructor(e,t={}){super(e,t);let n=Object.assign({},z,t),{content:r,url:i,reload:a,stale:o}=n;this.content=r,this.url=i,this.reload=typeof a==`string`?a:z.reload,this.stale=typeof o==`number`?o:z.stale;let{onLoad:s,onLoading:c,onLoaded:l}=n;this.onLoad=s,this.onLoading=c,this.onLoaded=l,this.enhance()}get reloadable(){switch(this.reload){case`never`:return!1;case`always`:return!0;default:{let e=N(this.loadedAt);return e&&new Date-e>this.stale*1e3}}}contentLoadable=({url:e})=>!!e;contentLoading=async({url:e})=>e?await this.remoteContentLoader(e):await this.contentLoader();contentLoader=async()=>``;remoteContentLoader=async e=>(await fetch(e)).text();load=async()=>{if(this.loadedAt&&!this.reloadable)return;let e=this.findCallback(this.onLoad),t=await this.awaitCallback(e||this.contentLoadable,{url:this.url});if(this.dispatch(`load`,{detail:{url:this.url}}),!t)return;let n=this.url?await this.remoteContentLoader(this.url):await this.contentLoader();this.dispatch(`loading`,{detail:{url:this.url}}),n&&(await this.awaitCallback(this.onLoaded,{url:this.url,content:n}),this.loadedAt=new Date().getTime(),this.dispatch(`loaded`,{detail:{url:this.url,content:n}}))};enhance(){let e=this;Object.assign(this.controller,{load:e.load.bind(e)})}},V=(e,t)=>new B(e,t),H={trigger:null,events:[`click`],onDismissed:`dismissed`},U=class extends P{constructor(e,t={}){super(e,t);let{trigger:n,events:r,onDismissed:i}=Object.assign({},H,t);this.onDismissed=i,this.trigger=n||this.element,this.events=r,this.enhance(),this.observe()}dismiss=async e=>{let{target:t}=e;t instanceof HTMLElement&&(this.element.contains(t)||this.visible&&(this.dispatch(`dismiss`),await this.awaitCallback(this.onDismissed,{target:this.trigger}),this.dispatch(`dismissed`)))};observe(){this.events.forEach(e=>{window.addEventListener(e,this.dismiss,!0)})}unobserve(){this.events.forEach(e=>{window.removeEventListener(e,this.dismiss,!0)})}enhance(){let e=this,t=e.controller.disconnect.bind(e.controller);Object.assign(this.controller,{disconnect:()=>{e.unobserve(),t()}})}},W=(e,t)=>new U(e,t),G={anchor:null,events:[`click`],placement:`bottom`,alignment:`start`,onFlipped:`flipped`,ariaRole:null,respectMotion:!0},K=class extends P{constructor(e,t={}){super(e,t);let{anchor:n,events:r,placement:i,alignment:a,onFlipped:o,ariaRole:s,respectMotion:c}=Object.assign({},G,t);this.anchor=n,this.events=r,this.placement=i,this.alignment=a,this.onFlipped=o,this.ariaRole=s,this.respectMotion=c,this.prefersReducedMotion=window.matchMedia(`(prefers-reduced-motion: reduce)`).matches,this.anchor&&this.element&&T({trigger:this.anchor,target:this.element,role:this.ariaRole}),this.enhance(),this.observe()}flip=async()=>{if(!this.visible)return;this.dispatch(`flip`),window.getComputedStyle(this.element).position!=`absolute`&&(this.element.style.position=`absolute`);let e=this.flippedRect(this.anchor.getBoundingClientRect(),this.element.getBoundingClientRect());this.element.style.transition=this.respectMotion&&this.prefersReducedMotion?`none`:``;for(let[t,n]of Object.entries(e))this.element.style[t]=n;await this.awaitCallback(this.onFlipped,{target:this.element,placement:e}),this.dispatch(`flipped`,{detail:{placement:e}})};flippedRect(e,t){let n=this.quadrumRect(e,A()),r=[this.placement,O[this.placement]],i={};for(;!Object.keys(i).length&&r.length>0;){let a=r.shift();if(!this.biggerRectThan(n[a],t))continue;let o=this.quadrumPlacement(e,a,t),s=this.quadrumAlignment(e,a,o);i.top=`${s.top+window.scrollY}px`,i.left=`${s.left+window.scrollX}px`}return Object.keys(i).length||(i.top=``,i.left=``),i}quadrumRect(e,t){return{left:k({x:t.x,y:t.y,width:e.x-t.x,height:t.height}),right:k({x:e.x+e.width,y:t.y,width:t.width-(e.x+e.width),height:t.height}),top:k({x:t.x,y:t.y,width:t.width,height:e.y-t.y}),bottom:k({x:t.x,y:e.y+e.height,width:t.width,height:t.height-(e.y+e.height)})}}quadrumPlacement(e,t,n){switch(t){case`top`:return k({x:n.x,y:e.y-n.height,width:n.width,height:n.height});case`bottom`:return k({x:n.x,y:e.y+e.height,width:n.width,height:n.height});case`left`:return k({x:e.x-n.width,y:n.y,width:n.width,height:n.height});case`right`:return k({x:e.x+e.width,y:n.y,width:n.width,height:n.height});default:throw`Unable place at the quadrum, ${t}`}}quadrumAlignment(e,t,n){switch(t){case`top`:case`bottom`:{let t=e.x;return this.alignment===`center`?t=e.x+e.width/2-n.width/2:this.alignment===`end`&&(t=e.x+e.width-n.width),k({x:t,y:n.y,width:n.width,height:n.height})}case`left`:case`right`:{let t=e.y;return this.alignment===`center`?t=e.y+e.height/2-n.height/2:this.alignment===`end`&&(t=e.y+e.height-n.height),k({x:n.x,y:t,width:n.width,height:n.height})}default:throw`Unable align at the quadrum, ${t}`}}biggerRectThan(e,t){return e.height>=t.height&&e.width>=t.width}observe(){this.events.forEach(e=>{window.addEventListener(e,this.flip,!0)})}unobserve(){this.events.forEach(e=>{window.removeEventListener(e,this.flip,!0)})}enhance(){let e=this,t=e.controller.disconnect.bind(e.controller);Object.assign(this.controller,{disconnect:()=>{e.unobserve(),t()},flip:e.flip.bind(e)})}},q=(e,t)=>new K(e,t),J={events:[`resize`],boundaries:[`top`,`left`,`right`],onShifted:`shifted`,respectMotion:!0},Y=class extends P{constructor(e,t={}){super(e,t);let{onShifted:n,events:r,boundaries:i,respectMotion:a}=Object.assign({},J,t);this.onShifted=n,this.events=r,this.boundaries=i,this.respectMotion=a,this.prefersReducedMotion=window.matchMedia(`(prefers-reduced-motion: reduce)`).matches,this.enhance(),this.observe()}shift=async()=>{if(!this.visible)return;this.dispatch(`shift`);let e=this.overflowRect(this.element.getBoundingClientRect(),this.elementTranslations(this.element)),t=e.left||e.right||0,n=e.top||e.bottom||0;this.element.style.transition=this.respectMotion&&this.prefersReducedMotion?`none`:``,this.element.style.transform=`translate(${t}px, ${n}px)`,await this.awaitCallback(this.onShifted,e),this.dispatch(`shifted`,{detail:e})};overflowRect(e,t){let n={},r=A(),i=k({x:e.x-t.x,y:e.y-t.y,width:e.width,height:e.height});for(let e of this.boundaries){let t=this.directionDistance(i,e,r),a=O[e];t<0?i[a]+t>=r[a]&&!n[a]&&(n[e]=t):n[e]=``}return n}directionDistance(e,t,n){switch(t){case`top`:case`left`:return e[t]-n[t];case`bottom`:case`right`:return n[t]-e[t];default:throw`Invalid direction to calcuate distance, ${t}`}}elementTranslations(e){let t=window.getComputedStyle(e),n=t.transform||t.webkitTransform||t.mozTransform;if(n===`none`||n===void 0)return{x:0,y:0};let r=n.includes(`3d`)?`3d`:`2d`,i=n.match(/matrix.*\((.+)\)/)[1].split(`, `);return r===`2d`?{x:Number(i[4]),y:Number(i[5])}:{x:0,y:0}}observe(){this.events.forEach(e=>{window.addEventListener(e,this.shift,!0)})}unobserve(){this.events.forEach(e=>{window.removeEventListener(e,this.shift,!0)})}enhance(){let e=this,t=e.controller.disconnect.bind(e.controller);Object.assign(this.controller,{disconnect:()=>{e.unobserve(),t()},shift:e.shift.bind(e)})}},X=(e,t)=>new Y(e,t),Z={visibility:`visibility`,onShown:`shown`,onHidden:`hidden`},ne=class extends P{constructor(e,t={}){let{visibility:n,onShown:r,onHidden:i,activator:a}=Object.assign({},Z,t),o=typeof n==`string`?n:Z.namespace,s=typeof t.visible==`string`?t.visible:`isVisible`;(typeof t.visible!=`boolean`||t.visible)&&(t.visible=`${o}.${s}`),super(e,t),this.visibility=o,this.visibilityResolver=s,this.onShown=r,this.onHidden=i,this.activator=a instanceof HTMLElement?a:null,this.enhance(),this.element instanceof HTMLElement&&this.activate(this.isVisible(this.element))}isVisible(e){if(!(e instanceof HTMLElement))return!1;let t=D.hiddenClass;return t?!e.classList.contains(t):!e.hasAttribute(`hidden`)}toggle(e,t){if(!(e instanceof HTMLElement))return;let n=D.hiddenClass;n?t?e.classList.remove(n):e.classList.add(n):t?e.removeAttribute(`hidden`):e.setAttribute(`hidden`,!0)}activate(e){this.activator&&this.activator.setAttribute(`aria-expanded`,e?`true`:`false`)}async show(){!(this.element instanceof HTMLElement)||this.isVisible(this.element)||(this.dispatch(`show`),this.toggle(this.element,!0),this.activate(!0),await this.awaitCallback(this.onShown,{target:this.element}),this.dispatch(`shown`))}async hide(){!(this.element instanceof HTMLElement)||!this.isVisible(this.element)||(this.dispatch(`hide`),this.toggle(this.element,!1),this.activate(!1),await this.awaitCallback(this.onHidden,{target:this.element}),this.dispatch(`hidden`))}enhance(){let e=this,t={show:e.show.bind(e),hide:e.hide.bind(e)};Object.defineProperty(t,`visible`,{get(){return e.isVisible(e.element)}}),Object.defineProperty(t,this.visibilityResolver,{value:e.isVisible.bind(e)}),Object.defineProperty(this.controller,this.visibility,{get(){return t}})}},Q=(e,t)=>new ne(e,t),re=class extends t.Controller{static targets=[`modal`,`overlay`];connect(){if(!this.hasModalTarget){console.error(`ModalController requires a modal target. Add data-modal-target="modal" to your element.`);return}this.isNativeDialog=this.modalTarget instanceof HTMLDialogElement,this.isNativeDialog?(this.modalTarget.addEventListener(`cancel`,this.close),this.modalTarget.addEventListener(`click`,this.handleBackdropClick)):(this.focusTrap=new o(this.modalTarget,{escapeDeactivates:!0}),W(this,{element:this.modalTarget}))}dismissed=()=>{this.close()};disconnect(){this.isNativeDialog&&(this.modalTarget.removeEventListener(`cancel`,this.close),this.modalTarget.removeEventListener(`click`,this.handleBackdropClick))}open(e){if(e&&e.preventDefault(),this.hasModalTarget){if(this.isNativeDialog)this.previouslyFocused=document.activeElement,this.modalTarget.showModal();else{let e=this.hasOverlayTarget?this.overlayTarget:this.modalTarget;e.hidden=!1,document.body.style.overflow=`hidden`,this.focusTrap&&this.focusTrap.activate()}m(`Modal opened`)}}close(e){if(e&&e.preventDefault(),this.hasModalTarget){if(this.isNativeDialog)this.modalTarget.close(),this.previouslyFocused&&this.previouslyFocused.isConnected&&setTimeout(()=>{this.previouslyFocused.focus()},0);else{let e=this.hasOverlayTarget?this.overlayTarget:this.modalTarget;e.hidden=!0,document.body.style.overflow=``,this.focusTrap&&this.focusTrap.deactivate()}m(`Modal closed`)}}handleBackdropClick=e=>{let t=this.modalTarget.getBoundingClientRect();(e.clientY<t.top||e.clientY>t.bottom||e.clientX<t.left||e.clientX>t.right)&&this.close()}},ie=class extends t.Controller{static targets=[`trigger`];connect(){W(this,{trigger:this.hasTriggerTarget?this.triggerTarget:null})}},ae=class extends t.Controller{static targets=[`anchor`,`reference`];static values={placement:{type:String,default:`bottom`},alignment:{type:String,default:`start`},role:{type:String,default:`tooltip`}};connect(){if(!this.hasReferenceTarget){console.error(`FlipperController requires a reference target. Add data-flipper-target="reference" to your element.`);return}if(!this.hasAnchorTarget){console.error(`FlipperController requires an anchor target. Add data-flipper-target="anchor" to your element.`);return}q(this,{element:this.referenceTarget,anchor:this.anchorTarget,placement:this.placementValue,alignment:this.alignmentValue,ariaRole:this.roleValue})}},oe=class extends t.Controller{static targets=[`content`,`template`,`loader`,`activator`];static classes=[`hidden`];static values={url:String,loadedAt:String,reload:{type:String,default:`never`},staleAfter:{type:Number,default:3600}};connect(){V(this,{element:this.hasContentTarget?this.contentTarget:null,url:this.hasUrlValue?this.urlValue:null}),this.hasContentTarget&&Q(this,{element:this.contentTarget,activator:this.hasActivatorTarget?this.activatorTarget:null}),this.hasLoaderTarget&&Q(this,{element:this.loaderTarget,visibility:`contentLoaderVisibility`})}async show(){await this.visibility.show()}async hide(){await this.visibility.hide()}async shown(){await this.load()}contentLoad(){return this.hasContentTarget&&this.contentTarget.tagName.toLowerCase()===`turbo-frame`?(this.hasUrlValue&&this.contentTarget.setAttribute(`src`,this.urlValue),!1):!0}async contentLoading(){this.hasLoaderTarget&&await this.contentLoaderVisibility.show()}async contentLoaded({content:e}){this.hasContentTarget&&this.contentTarget.replaceChildren(this.getContentNode(e)),this.hasLoaderTarget&&await this.contentLoaderVisibility.hide()}getContentNode(e){if(typeof e==`string`){let t=document.createElement(`template`);return t.innerHTML=e,document.importNode(t.content,!0)}return document.importNode(e,!0)}contentLoader(){if(this.hasTemplateTarget)return this.templateTarget instanceof HTMLTemplateElement?this.templateTarget.content:this.templateTarget.innerHTML}},$=class extends t.Controller{static targets=[`daysOfWeek`,`daysOfMonth`];static classes=[`dayOfWeek`,`dayOfMonth`];static values={locales:{type:Array,default:[`default`]},weekdayFormat:{type:String,default:`short`},dayFormat:{type:String,default:`numeric`},daysOfOtherMonth:{type:Boolean,default:!1}};initialize(){R(this)}connect(){this.draw()}navigated(){this.draw()}draw(){this.drawDaysOfWeek(),this.drawDaysOfMonth()}createDayElement(e,{selectable:t=!1,disabled:n=!1}={}){let r=document.createElement(t?`button`:`div`);return r.tabIndex=-1,e?r.textContent=e:r.setAttribute(`aria-hidden`,`true`),n&&(r instanceof HTMLButtonElement?r.disabled=!0:r.setAttribute(`aria-disabled`,`true`)),r}drawDaysOfWeek(){if(!this.hasDaysOfWeekTarget)return;let e=new Intl.DateTimeFormat(this.localesValue,{weekday:this.weekdayFormatValue}),t=[];for(let n of this.calendar.daysOfWeek){let r=this.createDayElement(e.format(n.date));r.setAttribute(`role`,`columnheader`),r.title=n.long,this.hasDayOfWeekClass&&r.classList.add(...this.dayOfWeekClasses),t.push(r)}let n=document.createElement(`div`);n.setAttribute(`role`,`row`),n.replaceChildren(...t),this.daysOfWeekTarget.replaceChildren(n)}drawDaysOfMonth(){if(!this.hasDaysOfMonthTarget)return;let e=this.calendar.today,t=new Date(e.getFullYear(),e.getMonth(),e.getDate()).getTime(),n=[];for(let e of this.calendar.daysOfMonth){let r=!e.current||this.calendar.isDisabled(e.date)||!this.calendar.isWithinRange(e.date),i=e.current||this.daysOfOtherMonthValue?e.value:``,a=this.createDayElement(i,{selectable:e.current,disabled:r});t===e.date.getTime()&&a.setAttribute(`aria-current`,`date`),this.hasDayOfMonthClass&&a.classList.add(...this.dayOfMonthClasses);let o=document.createElement(`time`);o.dateTime=e.iso,a.appendChild(o),n.push(a)}let r=[];for(let e=0;e<n.length;e+=7){let t=document.createElement(`div`);t.setAttribute(`role`,`row`);for(let r of n.slice(e,e+7))r.setAttribute(`role`,`gridcell`),t.appendChild(r);r.push(t)}this.daysOfMonthTarget.replaceChildren(...r)}},se=class extends t.Controller{select(e){if(!(e.target instanceof HTMLElement))return;e.preventDefault();let t=e.target instanceof HTMLTimeElement?e.target.parentElement:e.target;if(t.disabled||t.getAttribute(`aria-disabled`)===`true`)return;this.dispatch(`select`,{target:t});let n=e.target instanceof HTMLTimeElement?e.target:e.target.querySelector(`time`);if(!n)return console.error(`unable to locate time element within ${t}`);let r=N(n.dateTime);if(!r)return console.error(`unable to parse ${n.dateTime} found within the time element`);this.dispatch(`selected`,{target:t,detail:{epoch:r.getTime(),iso:r.toISOString()}})}},ce=class extends t.Controller{static targets=[`previous`,`next`,`day`,`month`,`year`,`input`,`display`];static outlets=[`calendar-month`];static values={locales:{type:Array,default:[`default`]},dayFormat:{type:String,default:`numeric`},monthFormat:{type:String,default:`long`},yearFormat:{type:String,default:`numeric`}};initialize(){this.previous=this.previous.bind(this),this.next=this.next.bind(this)}async calendarMonthOutletConnected(){if(this.hasInputTarget&&this.inputTarget.value){let e=N(this.inputTarget.value);e&&await this.calendarMonthOutlet.calendar.navigate(e)}this.draw()}selected(e){this.hasInputTarget&&(this.inputTarget.value=e.detail.iso),this.hasDisplayTarget&&(this.displayTarget.value=this.formatDate(new Date(e.detail.iso)))}formatDate(e){return new Intl.DateTimeFormat(this.localesValue,{day:this.dayFormatValue,month:this.monthFormatValue,year:this.yearFormatValue}).format(e)}previousTargetConnected(e){e.addEventListener(`click`,this.previous)}previousTargetDisconnected(e){e.removeEventListener(`click`,this.previous)}async previous(){await this.calendarMonthOutlet.calendar.step(`month`,-1),this.draw()}nextTargetConnected(e){e.addEventListener(`click`,this.next)}nextTargetDisconnected(e){e.removeEventListener(`click`,this.next)}async next(){await this.calendarMonthOutlet.calendar.step(`month`,1),this.draw()}draw(){this.drawDay(),this.drawMonth(),this.drawYear()}drawDay(){if(!this.hasDayTarget||!this.hasCalendarMonthOutlet)return;let{year:e,month:t,day:n}=this.calendarMonthOutlet.calendar,r=new Intl.DateTimeFormat(this.localesValue,{day:this.dayFormatValue});this.dayTarget.textContent=r.format(new Date(e,t,n))}drawMonth(){if(!this.hasMonthTarget||!this.hasCalendarMonthOutlet)return;let{year:e,month:t}=this.calendarMonthOutlet.calendar,n=new Intl.DateTimeFormat(this.localesValue,{month:this.monthFormatValue});this.monthTarget.textContent=n.format(new Date(e,t))}drawYear(){if(!this.hasYearTarget||!this.hasCalendarMonthOutlet)return;let{year:e}=this.calendarMonthOutlet.calendar,t=new Intl.DateTimeFormat(this.localesValue,{year:this.yearFormatValue});this.yearTarget.textContent=t.format(new Date(e,0))}},le=class extends t.Controller{static targets=[`content`];connect(){X(this,{element:this.hasContentTarget?this.contentTarget:null})}};e.ARIA_HASPOPUP_VALUES=S,e.CalendarMonthController=$,e.CalendarMonthObserverController=se,e.DatepickerController=ce,e.DismisserController=ie,e.FOCUSABLE_SELECTOR=n,e.FlipperController=ae,e.FocusRestoration=s,e.FocusTrap=o,e.ModalController=re,e.PannerController=le,e.PopoverController=oe,e.RovingTabIndex=f,e.announce=m,e.connectTriggerToTarget=T,e.disconnectTriggerFromTarget=ee,e.ensureId=g,e.focusFirst=a,e.generateId=h,e.getFocusableElements=r,e.isActivationKey=l,e.isArrowKey=u,e.isKey=c,e.isVisible=i,e.preventDefault=d,e.setAriaState=_,e.setChecked=b,e.setDisabled=x,e.setExpanded=v,e.setPressed=y});
|
|
1
|
+
(function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require(`@hotwired/stimulus`)):typeof define==`function`&&define.amd?define([`exports`,`@hotwired/stimulus`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.StimulusPlumbersControllers={},e.Stimulus))})(this,function(e,t){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var n=[`a[href]`,`area[href]`,`button:not([disabled])`,`input:not([disabled])`,`select:not([disabled])`,`textarea:not([disabled])`,`[tabindex]:not([tabindex="-1"])`,`audio[controls]`,`video[controls]`,`[contenteditable]:not([contenteditable="false"])`].join(`,`);function r(e){return Array.from(e.querySelectorAll(n)).filter(e=>i(e))}function i(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)}function a(e){let t=r(e);return t.length>0?(t[0].focus(),!0):!1}var o=class{constructor(e,t={}){this.container=e,this.previouslyFocused=null,this.options=t,this.isActive=!1}activate(){this.isActive||(this.previouslyFocused=document.activeElement,this.isActive=!0,this.options.initialFocus?this.options.initialFocus.focus():a(this.container),this.container.addEventListener(`keydown`,this.handleKeyDown))}deactivate(){if(!this.isActive)return;this.isActive=!1,this.container.removeEventListener(`keydown`,this.handleKeyDown);let e=this.options.returnFocus||this.previouslyFocused;e&&i(e)&&e.focus()}handleKeyDown=e=>{if(e.key===`Escape`&&this.options.escapeDeactivates){e.preventDefault(),this.deactivate();return}if(e.key!==`Tab`)return;let t=r(this.container);if(t.length===0)return;let n=t[0],i=t[t.length-1];e.shiftKey&&document.activeElement===n?(e.preventDefault(),i.focus()):!e.shiftKey&&document.activeElement===i&&(e.preventDefault(),n.focus())}},s=class{constructor(){this.savedElement=null}save(){this.savedElement=document.activeElement}restore(){this.savedElement&&i(this.savedElement)&&(this.savedElement.focus(),this.savedElement=null)}};function c(e,t){return e.key===t}function l(e){return e.key===`Enter`||e.key===` `}function u(e){return[`ArrowUp`,`ArrowDown`,`ArrowLeft`,`ArrowRight`].includes(e.key)}function d(e){e.preventDefault(),e.stopPropagation()}var f=class{constructor(e,t=0){this.items=e,this.currentIndex=t,this.updateTabIndex()}handleKeyDown(e){let t;switch(e.key){case`ArrowDown`:case`ArrowRight`:e.preventDefault(),t=(this.currentIndex+1)%this.items.length;break;case`ArrowUp`:case`ArrowLeft`:e.preventDefault(),t=this.currentIndex===0?this.items.length-1:this.currentIndex-1;break;case`Home`:e.preventDefault(),t=0;break;case`End`:e.preventDefault(),t=this.items.length-1;break;default:return}this.setCurrentIndex(t)}setCurrentIndex(e){e>=0&&e<this.items.length&&(this.currentIndex=e,this.updateTabIndex(),this.items[e].focus())}updateTabIndex(){this.items.forEach((e,t)=>{e.tabIndex=t===this.currentIndex?0:-1})}updateItems(e){this.items=e,this.currentIndex=Math.min(this.currentIndex,e.length-1),this.updateTabIndex()}},p=(e,t,n)=>{let r=document.querySelector(`[data-live-region="${e}"]`);return r||(r=document.createElement(`div`),r.className=`sr-only`,r.dataset.liveRegion=e,r.setAttribute(`aria-live`,e),r.setAttribute(`aria-atomic`,t.toString()),r.setAttribute(`aria-relevant`,n),document.body.appendChild(r)),r};function m(e,t={}){let{politeness:n=`polite`,atomic:r=!0,relevant:i=`additions text`}=t,a=p(n,r,i);a.textContent=``,setTimeout(()=>{a.textContent=e},100)}var h=(e=`a11y`)=>`${e}-${Math.random().toString(36).substr(2,9)}`,g=(e,t=`element`)=>e.id||=h(t),_=(e,t,n)=>{e.setAttribute(t,n.toString())},v=(e,t)=>_(e,`aria-expanded`,t),y=(e,t)=>_(e,`aria-pressed`,t),ee=(e,t)=>_(e,`aria-checked`,t);function te(e,t){_(e,`aria-disabled`,t),t?e.setAttribute(`tabindex`,`-1`):e.removeAttribute(`tabindex`)}var b={menu:`menu`,listbox:`listbox`,tree:`tree`,grid:`grid`,dialog:`dialog`},x=(e,t,n)=>{Object.entries(t).forEach(([t,r])=>{e.setAttribute(t,r),n[t]=r})},S=(e,t,n)=>n||!e.hasAttribute(t);function C({trigger:e,target:t,role:n=null,override:r=!1}){let i={trigger:{},target:{}};if(!e||!t)return i;let a={},o={};if(n&&S(t,`role`,r)&&(o.role=n),t.id&&(S(e,`aria-controls`,r)&&(a[`aria-controls`]=t.id),n===`tooltip`&&S(e,`aria-describedby`,r)&&(a[`aria-describedby`]=t.id)),n&&S(e,`aria-haspopup`,r)){let e=b[n]||`true`;e&&(a[`aria-haspopup`]=e)}return x(t,o,i.target),x(e,a,i.trigger),i}var w=(e,t)=>{t.forEach(t=>{e.hasAttribute(t)&&e.removeAttribute(t)})};function ne({trigger:e,target:t,attributes:n=null}){!e||!t||(w(e,n||[`aria-controls`,`aria-haspopup`,`aria-describedby`]),(!n||n.includes(`role`))&&w(t,[`role`]))}var T={get visibleOnly(){return!0},hiddenClass:null},E={get top(){return`bottom`},get bottom(){return`top`},get left(){return`right`},get right(){return`left`}};function D({x:e,y:t,width:n,height:r}){return{x:e,y:t,width:n,height:r,left:e,right:e+n,top:t,bottom:t+r}}function O(){return D({x:0,y:0,width:window.innerWidth||document.documentElement.clientWidth,height:window.innerHeight||document.documentElement.clientHeight})}function re(e){if(!(e instanceof HTMLElement))return!1;let t=O(),n=e.getBoundingClientRect(),r=n.top<=t.height&&n.top+n.height>0,i=n.left<=t.width&&n.left+n.width>0;return r&&i}function k(e){return e instanceof Date&&!isNaN(e)}function A(...e){if(e.length===0)throw`Missing values to parse as date`;if(e.length===1){let t=new Date(e[0]);if(e[0]&&k(t))return t}else{let t=new Date(...e);if(k(t))return t}}var ie={element:null,visible:null,dispatch:!0,prefix:``},j=class{constructor(e,t={}){this.controller=e;let{element:n,visible:r,dispatch:i,prefix:a}=Object.assign({},ie,t);this.element=n||e.element,this.visibleOnly=typeof r==`boolean`?r:T.visibleOnly,this.visibleCallback=typeof r==`string`?r:null,this.notify=!!i,this.prefix=typeof a==`string`&&a?a:e.identifier}get visible(){return this.element instanceof HTMLElement?this.visibleOnly?re(this.element)&&this.isVisible(this.element):!0:!1}isVisible(e){if(this.visibleCallback){let t=this.findCallback(this.visibleCallback);if(typeof t==`function`)return t(e)}return e instanceof HTMLElement?!e.hasAttribute(`hidden`):!1}dispatch(e,{target:t=null,prefix:n=null,detail:r=null}={}){if(this.notify)return this.controller.dispatch(e,{target:t||this.element,prefix:n||this.prefix,detail:r})}findCallback(e){if(typeof e!=`string`)return;let t=this,n=e.split(`.`).reduce((e,t)=>e&&e[t],t.controller);if(typeof n==`function`)return n.bind(t.controller);let r=e.split(`.`).reduce((e,t)=>e&&e[t],t);if(typeof r==`function`)return r.bind(t)}async awaitCallback(e,...t){if(typeof e==`string`&&(e=this.findCallback(e)),typeof e==`function`){let n=e(...t);return n instanceof Promise?await n:n}}},M=7,N={locales:[`default`],today:``,day:null,month:null,year:null,since:null,till:null,disabledDates:[],disabledWeekdays:[],disabledDays:[],disabledMonths:[],disabledYears:[],firstDayOfWeek:0,onNavigated:`navigated`},ae=class extends j{constructor(e,t={}){super(e,t);let n=Object.assign({},N,t),{onNavigated:r,since:i,till:a,firstDayOfWeek:o}=n;this.onNavigated=r,this.since=A(i),this.till=A(a),this.firstDayOfWeek=0<=o&&o<7?o:N.firstDayOfWeek;let{disabledDates:s,disabledWeekdays:c,disabledDays:l,disabledMonths:u,disabledYears:d}=n;this.disabledDates=Array.isArray(s)?s:[],this.disabledWeekdays=Array.isArray(c)?c:[],this.disabledDays=Array.isArray(l)?l:[],this.disabledMonths=Array.isArray(u)?u:[],this.disabledYears=Array.isArray(d)?d:[];let{today:f,day:p,month:m,year:h}=n;this.now=A(f)||new Date,typeof h==`number`&&typeof m==`number`&&typeof p==`number`?this.current=A(h,m,p):this.current=this.now,this.build(),this.enhance()}build(){this.daysOfWeek=this.buildDaysOfWeek(),this.daysOfMonth=this.buildDaysOfMonth(),this.monthsOfYear=this.buildMonthsOfYear()}buildDaysOfWeek(){let e=new Intl.DateTimeFormat(this.localesValue,{weekday:`long`}),t=new Intl.DateTimeFormat(this.localesValue,{weekday:`short`}),n=new Date(`2024-10-06`),r=[];for(let i=this.firstDayOfWeek,a=i+7;i<a;i++){let a=new Date(n);a.setDate(n.getDate()+i),r.push({date:a,value:a.getDay(),long:e.format(a),short:t.format(a)})}return r}buildDaysOfMonth(){let e=this.month,t=this.year,n=[],r=e=>({current:this.month===e.getMonth()&&this.year===e.getFullYear(),date:e,value:e.getDate(),month:e.getMonth(),year:e.getFullYear(),iso:e.toISOString()}),i=new Date(t,e).getDay(),a=this.firstDayOfWeek-i;for(let i=a>0?a-7:a;i<0;i++){let a=new Date(t,e,i+1);n.push(r(a))}let o=new Date(t,e+1,0).getDate();for(let i=1;i<=o;i++){let a=new Date(t,e,i);n.push(r(a))}let s=n.length%M,c=s===0?0:M-s;for(let i=1;i<=c;i++){let a=new Date(t,e+1,i);n.push(r(a))}return n}buildMonthsOfYear(){let e=new Intl.DateTimeFormat(this.localesValue,{month:`long`}),t=new Intl.DateTimeFormat(this.localesValue,{month:`short`}),n=new Intl.DateTimeFormat(this.localesValue,{month:`numeric`}),r=[];for(let i=0;i<12;i++){let a=new Date(this.year,i);r.push({date:a,value:a.getMonth(),long:e.format(a),short:t.format(a),numeric:n.format(a)})}return r}get today(){return this.now}set today(e){if(!k(e))return;let t=this.month?this.month:e.getMonth(),n=this.year?this.year:e.getFullYear(),r=t==e.getMonth()&&n==e.getFullYear(),i=this.hasDayValue?this.day:r?e.getDate():1;this.now=new Date(n,t,i).toISOString()}get current(){return typeof this.year==`number`&&typeof this.month==`number`&&typeof this.day==`number`?A(this.year,this.month,this.day):null}set current(e){k(e)&&(this.day=e.getDate(),this.month=e.getMonth(),this.year=e.getFullYear())}navigate=async e=>{if(!k(e))return;let t=this.current,n=e.toISOString(),r=t.toISOString();this.dispatch(`navigate`,{detail:{from:r,to:n}}),this.current=e,this.build(),await this.awaitCallback(this.onNavigated,{from:r,to:n}),this.dispatch(`navigated`,{detail:{from:r,to:n}})};step=async(e,t)=>{if(t===0)return;let n=this.current;switch(e){case`year`:n.setFullYear(n.getFullYear()+t);break;case`month`:n.setMonth(n.getMonth()+t);break;case`day`:n.setDate(n.getDate()+t);break;default:return}await this.navigate(n)};isDisabled=e=>{if(!k(e))return!1;if(this.disabledDates.length){let t=e.getTime();for(let e of this.disabledDates)if(t===new Date(e).getTime())return!0}if(this.disabledWeekdays.length){let t=e.getDay(),n=this.daysOfWeek,r=n.findIndex(e=>e.value===t);if(r>=0){let e=n[r];for(let t of this.disabledWeekdays)if(e.value==t||e.short===t||e.long===t)return!0}}if(this.disabledDays.length){let t=e.getDate();for(let e of this.disabledDays)if(t==e)return!0}if(this.disabledMonths.length){let t=e.getMonth(),n=this.monthsOfYear,r=n.findIndex(e=>e.value===t);if(r>=0){let e=n[r];for(let t of this.disabledMonths)if(e.value==t||e.short===t||e.long===t)return!0}}if(this.disabledYears.length){let t=e.getFullYear();for(let e of this.disabledYears)if(t==e)return!0}return!1};isWithinRange=e=>{if(!k(e))return!1;let t=!0;return this.since&&(t&&=e>=this.since),this.till&&(t&&=e<=this.till),t};enhance(){let e=this;Object.assign(this.controller,{get calendar(){return{get today(){return e.today},get current(){return e.current},get day(){return e.day},get month(){return e.month},get year(){return e.year},get since(){return e.since},get till(){return e.till},get firstDayOfWeek(){return e.firstDayOfWeek},get disabledDates(){return e.disabledDates},get disabledWeekdays(){return e.disabledWeekdays},get disabledDays(){return e.disabledDays},get disabledMonths(){return e.disabledMonths},get disabledYears(){return e.disabledYears},get daysOfWeek(){return e.daysOfWeek},get daysOfMonth(){return e.daysOfMonth},get monthsOfYear(){return e.monthsOfYear},navigate:async t=>await e.navigate(t),step:async(t,n)=>await e.step(t,n),isDisabled:t=>e.isDisabled(t),isWithinRange:t=>e.isWithinRange(t)}}})}},oe=(e,t)=>new ae(e,t),se=class extends j{constructor(e,t={}){super(e,t),this.debounceTimer=null,this.abortController=null}fuzzyFilter(e,t){let n=t.toLowerCase(),r=0;return e.querySelectorAll(`[role="option"]`).forEach(e=>{let t=this.fuzzyMatch(n,e.textContent.trim().toLowerCase());e.hidden=!t,t&&r++}),r}fuzzyMatch(e,t){let n=0;for(let r=0;r<t.length&&n<e.length;r++)t[r]===e[n]&&n++;return n===e.length}scheduleFetch(e,t,n){clearTimeout(this.debounceTimer),this.debounceTimer=setTimeout(()=>this.fetch(e,n),t)}async fetch(e,{url:t,field:n,onLoading:r,onLoaded:i,onError:a}){this.abortController?.abort(),this.abortController=new AbortController,r?.(!0);let o=new URL(t,window.location.href);o.searchParams.set(n,e);try{let e=await fetch(o,{signal:this.abortController.signal,headers:{Accept:`text/html`,"X-Requested-With":`XMLHttpRequest`}});if(!e.ok)throw Error(`${e.status}`);i?.(await e.text())}catch(e){e.name!==`AbortError`&&a?.(e)}finally{r?.(!1)}}cancel(){clearTimeout(this.debounceTimer),this.abortController?.abort()}},ce=(e,t)=>new se(e,t),P={content:null,url:``,reload:`never`,stale:3600,onLoad:`canLoad`,onLoading:`contentLoading`,onLoaded:`contentLoaded`},le=class extends j{constructor(e,t={}){super(e,t);let n=Object.assign({},P,t),{content:r,url:i,reload:a,stale:o}=n;this.content=r,this.url=i,this.reload=typeof a==`string`?a:P.reload,this.stale=typeof o==`number`?o:P.stale;let{onLoad:s,onLoading:c,onLoaded:l}=n;this.onLoad=s,this.onLoading=c,this.onLoaded=l,this.enhance()}get reloadable(){switch(this.reload){case`never`:return!1;case`always`:return!0;default:{let e=A(this.loadedAt);return e&&new Date-e>this.stale*1e3}}}contentLoadable=({url:e})=>!!e;contentLoading=async({url:e})=>e?await this.remoteContentLoader(e):await this.contentLoader();contentLoader=async()=>``;remoteContentLoader=async e=>(await fetch(e)).text();load=async()=>{if(this.loadedAt&&!this.reloadable)return;let e=this.findCallback(this.onLoad),t=await this.awaitCallback(e||this.contentLoadable,{url:this.url});if(this.dispatch(`load`,{detail:{url:this.url}}),!t)return;let n=this.url?await this.remoteContentLoader(this.url):await this.contentLoader();this.dispatch(`loading`,{detail:{url:this.url}}),n&&(await this.awaitCallback(this.onLoaded,{url:this.url,content:n}),this.loadedAt=new Date().getTime(),this.dispatch(`loaded`,{detail:{url:this.url,content:n}}))};enhance(){let e=this;Object.assign(this.controller,{load:e.load.bind(e)})}},ue=(e,t)=>new le(e,t),de={trigger:null,events:[`click`],onDismissed:`dismissed`},fe=class extends j{constructor(e,t={}){super(e,t);let{trigger:n,events:r,onDismissed:i}=Object.assign({},de,t);this.onDismissed=i,this.trigger=n||this.element,this.events=r,this.enhance(),this.observe()}dismiss=async e=>{let{target:t}=e;t instanceof HTMLElement&&(this.element.contains(t)||this.visible&&(this.dispatch(`dismiss`),await this.awaitCallback(this.onDismissed,{target:this.trigger}),this.dispatch(`dismissed`)))};observe(){this.events.forEach(e=>{window.addEventListener(e,this.dismiss,!0)})}unobserve(){this.events.forEach(e=>{window.removeEventListener(e,this.dismiss,!0)})}enhance(){let e=this,t=e.controller.disconnect.bind(e.controller);Object.assign(this.controller,{disconnect:()=>{e.unobserve(),t()}})}},F=(e,t)=>new fe(e,t),I={anchor:null,events:[`click`],placement:`bottom`,alignment:`start`,onFlipped:`flipped`,ariaRole:null,respectMotion:!0},L=class extends j{constructor(e,t={}){super(e,t);let{anchor:n,events:r,placement:i,alignment:a,onFlipped:o,ariaRole:s,respectMotion:c}=Object.assign({},I,t);this.anchor=n,this.events=r,this.placement=i,this.alignment=a,this.onFlipped=o,this.ariaRole=s,this.respectMotion=c,this.prefersReducedMotion=window.matchMedia(`(prefers-reduced-motion: reduce)`).matches,this.anchor&&this.element&&C({trigger:this.anchor,target:this.element,role:this.ariaRole}),this.enhance(),this.observe()}flip=async()=>{if(!this.visible)return;this.dispatch(`flip`),window.getComputedStyle(this.element).position!=`absolute`&&(this.element.style.position=`absolute`);let e=this.flippedRect(this.anchor.getBoundingClientRect(),this.element.getBoundingClientRect());this.element.style.transition=this.respectMotion&&this.prefersReducedMotion?`none`:``;for(let[t,n]of Object.entries(e))this.element.style[t]=n;await this.awaitCallback(this.onFlipped,{target:this.element,placement:e}),this.dispatch(`flipped`,{detail:{placement:e}})};flippedRect(e,t){let n=this.quadrumRect(e,O()),r=[this.placement,E[this.placement]],i={};for(;!Object.keys(i).length&&r.length>0;){let a=r.shift();if(!this.biggerRectThan(n[a],t))continue;let o=this.quadrumPlacement(e,a,t),s=this.quadrumAlignment(e,a,o);i.top=`${s.top+window.scrollY}px`,i.left=`${s.left+window.scrollX}px`}return Object.keys(i).length||(i.top=``,i.left=``),i}quadrumRect(e,t){return{left:D({x:t.x,y:t.y,width:e.x-t.x,height:t.height}),right:D({x:e.x+e.width,y:t.y,width:t.width-(e.x+e.width),height:t.height}),top:D({x:t.x,y:t.y,width:t.width,height:e.y-t.y}),bottom:D({x:t.x,y:e.y+e.height,width:t.width,height:t.height-(e.y+e.height)})}}quadrumPlacement(e,t,n){switch(t){case`top`:return D({x:n.x,y:e.y-n.height,width:n.width,height:n.height});case`bottom`:return D({x:n.x,y:e.y+e.height,width:n.width,height:n.height});case`left`:return D({x:e.x-n.width,y:n.y,width:n.width,height:n.height});case`right`:return D({x:e.x+e.width,y:n.y,width:n.width,height:n.height});default:throw`Unable place at the quadrum, ${t}`}}quadrumAlignment(e,t,n){switch(t){case`top`:case`bottom`:{let t=e.x;return this.alignment===`center`?t=e.x+e.width/2-n.width/2:this.alignment===`end`&&(t=e.x+e.width-n.width),D({x:t,y:n.y,width:n.width,height:n.height})}case`left`:case`right`:{let t=e.y;return this.alignment===`center`?t=e.y+e.height/2-n.height/2:this.alignment===`end`&&(t=e.y+e.height-n.height),D({x:n.x,y:t,width:n.width,height:n.height})}default:throw`Unable align at the quadrum, ${t}`}}biggerRectThan(e,t){return e.height>=t.height&&e.width>=t.width}observe(){this.events.forEach(e=>{window.addEventListener(e,this.flip,!0)})}unobserve(){this.events.forEach(e=>{window.removeEventListener(e,this.flip,!0)})}enhance(){let e=this,t=e.controller.disconnect.bind(e.controller);Object.assign(this.controller,{disconnect:()=>{e.unobserve(),t()},flip:e.flip.bind(e)})}},R=(e,t)=>new L(e,t),z={normalize(e){return typeof e==`string`?e:``},validate(){return!0}};function B(e){let t=0,n=!1;for(let r=e.length-1;r>=0;r--){let i=parseInt(e[r],10);n&&(i*=2,i>9&&(i-=9)),t+=i,n=!n}return t%10==0}var V=/\D/g,pe=/^\d{13,19}$/,me=/(.{4})(?=.)/g,he={normalize(e){return typeof e==`string`?e.replace(V,``):``},validate(e){return typeof e!=`string`||!pe.test(e)?!1:B(e)},format(e){return typeof e==`string`?e.replace(me,`$1 `):``}},H={1:10},U=/\D/g,ge=/^\+\d{7,15}$/,_e={normalize(e){if(typeof e!=`string`)return``;let t=e.trimStart().startsWith(`+`),n=e.replace(U,``);return t?`+${n}`:n},validate(e){if(typeof e!=`string`)return!1;if(ge.test(e))return!0;let t=e.replace(U,``);return Object.values(H).includes(t.length)},format(e){if(typeof e!=`string`)return``;let t=e.replace(U,``);for(let[e,n]of Object.entries(H)){if(t.length===n)return`(${t.slice(0,3)}) ${t.slice(3,6)}-${t.slice(6)}`;let r=n+e.length;if(t.length===r&&t.startsWith(e)){let n=t.slice(e.length);return`+${e} (${n.slice(0,3)}) ${n.slice(3,6)}-${n.slice(6)}`}}return e}},ve=/[^\d.,-]/g,ye=/^-?\d+(\.\d+)?$/,be={normalize(e){if(typeof e!=`string`)return``;let t=e.replace(ve,``);if(!t)return``;let n=t.lastIndexOf(`,`),r=t.lastIndexOf(`.`);return n>-1&&r>-1?n>r?t.replace(/\./g,``).replace(`,`,`.`):t.replace(/,/g,``):n>-1?t.slice(n+1).length<=2?t.replace(`,`,`.`):t.replace(/,/g,``):t},validate(e){return typeof e==`string`?ye.test(e):!1},format(e,t={}){if(typeof e!=`string`)return``;let n=parseFloat(e);if(isNaN(n))return e;let r=t.locale||`en-US`,i=t.currency||`USD`,a=t.fractionDigits===void 0?{}:{minimumFractionDigits:t.fractionDigits,maximumFractionDigits:t.fractionDigits};try{return new Intl.NumberFormat(r,{style:`currency`,currency:i,...a}).format(n)}catch{return e}}},W=/^\d{4}-\d{2}-\d{2}$/,xe=/^(\d{1,4})[/\-.](\d{1,2})[/\-.](\d{1,4})$/,Se=/\D/g,G={normalize(e){if(typeof e!=`string`)return``;let t=e.trim();if(W.test(t))return t;let n=t.match(xe);if(n){let[,e,t,r]=n;if(e.length===4)return`${e}-${t.padStart(2,`0`)}-${r.padStart(2,`0`)}`;if(r.length===4)return`${r}-${e.padStart(2,`0`)}-${t.padStart(2,`0`)}`}let r=t.replace(Se,``);if(r.length===8){let e=parseInt(r.slice(0,4),10);return e>=1e3&&e<=9999?`${r.slice(0,4)}-${r.slice(4,6)}-${r.slice(6,8)}`:`${r.slice(4,8)}-${r.slice(0,2)}-${r.slice(2,4)}`}return t},validate(e){if(typeof e!=`string`)return!1;let t=G.normalize(e);if(!W.test(t))return!1;let n=new Date(`${t}T00:00:00Z`);return!isNaN(n.getTime())&&n.toISOString().startsWith(t)},format(e,t={}){if(typeof e!=`string`)return``;let n=new Date(`${e}T00:00:00Z`);if(isNaN(n.getTime()))return e;let r=t.locale||`en-US`;try{return new Intl.DateTimeFormat(r,{year:t.year||`numeric`,month:t.month||`2-digit`,day:t.day||`2-digit`,timeZone:t.timeZone||`UTC`}).format(n)}catch{return e}}},Ce=/^([01]?\d|2[0-3]):([0-5]\d)$/,K={normalize(e){if(typeof e!=`string`)return``;let t=e.trim();if(Ce.test(t)){let[e,n]=t.split(`:`);return`${String(parseInt(e,10)).padStart(2,`0`)}:${n}`}let n=t.match(/^(\d{1,2}):(\d{2})\s*(AM|PM)$/i);if(n){let e=parseInt(n[1],10),t=n[2];return e=n[3].toUpperCase()===`AM`?e===12?0:e:e===12?12:e+12,e>23||parseInt(t,10)>59?``:`${String(e).padStart(2,`0`)}:${t}`}return``},validate(e){return K.normalize(e)!==``},format(e,t={}){if(typeof e!=`string`)return``;let n=e.match(/^(\d{2}):(\d{2})$/);if(!n)return e;let r=parseInt(n[1],10),i=n[2];if(t.format===`h24`)return`${String(r).padStart(2,`0`)}:${i}`;let a=r<12?`AM`:`PM`;return`${r%12||12}:${i} ${a}`}},q={PLAIN:`plain`,CREDIT_CARD:`creditCard`,PHONE:`phone`,CURRENCY:`currency`,DATE:`date`,TIME:`time`},J=new Map([[q.PLAIN,z],[q.CREDIT_CARD,he],[q.PHONE,_e],[q.CURRENCY,be],[q.DATE,G],[q.TIME,K]]),Y={type:q.PLAIN,options:{}},we=class extends j{static register(e,t){J.set(e,t)}constructor(e,t={}){super(e,t),this.type=t.type??Y.type,this.options=t.options??Y.options,this.enhance()}enhance(){let e=this,t=J.get(e.type)??J.get(q.PLAIN),n={normalize:n=>t.normalize?.(n,e.options)??(typeof n==`string`?n:``),validate:n=>t.validate?.(n,e.options)??!0,format:n=>t.format?.(n,e.options)??(typeof n==`string`?n:``),mask:n=>t.mask?.(n,e.options)??null,maskable:()=>typeof t.mask==`function`};Object.defineProperty(this.controller,`inputFormat`,{get(){return n},configurable:!0})}},X=(e,t)=>new we(e,t),Te={events:[`resize`],boundaries:[`top`,`left`,`right`],onShifted:`shifted`,respectMotion:!0},Ee=class extends j{constructor(e,t={}){super(e,t);let{onShifted:n,events:r,boundaries:i,respectMotion:a}=Object.assign({},Te,t);this.onShifted=n,this.events=r,this.boundaries=i,this.respectMotion=a,this.prefersReducedMotion=window.matchMedia(`(prefers-reduced-motion: reduce)`).matches,this.enhance(),this.observe()}shift=async()=>{if(!this.visible)return;this.dispatch(`shift`);let e=this.overflowRect(this.element.getBoundingClientRect(),this.elementTranslations(this.element)),t=e.left||e.right||0,n=e.top||e.bottom||0;this.element.style.transition=this.respectMotion&&this.prefersReducedMotion?`none`:``,this.element.style.transform=`translate(${t}px, ${n}px)`,await this.awaitCallback(this.onShifted,e),this.dispatch(`shifted`,{detail:e})};overflowRect(e,t){let n={},r=O(),i=D({x:e.x-t.x,y:e.y-t.y,width:e.width,height:e.height});for(let e of this.boundaries){let t=this.directionDistance(i,e,r),a=E[e];t<0?i[a]+t>=r[a]&&!n[a]&&(n[e]=t):n[e]=``}return n}directionDistance(e,t,n){switch(t){case`top`:case`left`:return e[t]-n[t];case`bottom`:case`right`:return n[t]-e[t];default:throw`Invalid direction to calcuate distance, ${t}`}}elementTranslations(e){let t=window.getComputedStyle(e),n=t.transform||t.webkitTransform||t.mozTransform;if(n===`none`||n===void 0)return{x:0,y:0};let r=n.includes(`3d`)?`3d`:`2d`,i=n.match(/matrix.*\((.+)\)/)[1].split(`, `);return r===`2d`?{x:Number(i[4]),y:Number(i[5])}:{x:0,y:0}}observe(){this.events.forEach(e=>{window.addEventListener(e,this.shift,!0)})}unobserve(){this.events.forEach(e=>{window.removeEventListener(e,this.shift,!0)})}enhance(){let e=this,t=e.controller.disconnect.bind(e.controller);Object.assign(this.controller,{disconnect:()=>{e.unobserve(),t()},shift:e.shift.bind(e)})}},De=(e,t)=>new Ee(e,t),Z={visibility:`visibility`,onShown:`shown`,onHidden:`hidden`},Oe=class extends j{constructor(e,t={}){let{visibility:n,onShown:r,onHidden:i,activator:a}=Object.assign({},Z,t),o=typeof n==`string`?n:Z.namespace,s=typeof t.visible==`string`?t.visible:`isVisible`;(typeof t.visible!=`boolean`||t.visible)&&(t.visible=`${o}.${s}`),super(e,t),this.visibility=o,this.visibilityResolver=s,this.onShown=r,this.onHidden=i,this.activator=a instanceof HTMLElement?a:null,this.enhance(),this.element instanceof HTMLElement&&this.activate(this.isVisible(this.element))}isVisible(e){if(!(e instanceof HTMLElement))return!1;let t=T.hiddenClass;return t?!e.classList.contains(t):!e.hasAttribute(`hidden`)}toggle(e,t){if(!(e instanceof HTMLElement))return;let n=T.hiddenClass;n?t?e.classList.remove(n):e.classList.add(n):t?e.removeAttribute(`hidden`):e.setAttribute(`hidden`,!0)}activate(e){this.activator&&this.activator.setAttribute(`aria-expanded`,e?`true`:`false`)}async show(){!(this.element instanceof HTMLElement)||this.isVisible(this.element)||(this.dispatch(`show`),this.toggle(this.element,!0),this.activate(!0),await this.awaitCallback(this.onShown,{target:this.element}),this.dispatch(`shown`))}async hide(){!(this.element instanceof HTMLElement)||!this.isVisible(this.element)||(this.dispatch(`hide`),this.toggle(this.element,!1),this.activate(!1),await this.awaitCallback(this.onHidden,{target:this.element}),this.dispatch(`hidden`))}enhance(){let e=this,t={show:e.show.bind(e),hide:e.hide.bind(e)};Object.defineProperty(t,`visible`,{get(){return e.isVisible(e.element)}}),Object.defineProperty(t,this.visibilityResolver,{value:e.isVisible.bind(e)}),Object.defineProperty(this.controller,this.visibility,{get(){return t}})}},Q=(e,t)=>new Oe(e,t),ke=class extends t.Controller{static targets=[`daysOfWeek`,`daysOfMonth`];static classes=[`dayOfWeek`,`dayOfMonth`];static values={locales:{type:Array,default:[`default`]},weekdayFormat:{type:String,default:`short`},dayFormat:{type:String,default:`numeric`},daysOfOtherMonth:{type:Boolean,default:!1}};initialize(){oe(this)}connect(){this.draw()}navigated(){this.draw()}draw(){this.drawDaysOfWeek(),this.drawDaysOfMonth()}createDayElement(e,{selectable:t=!1,disabled:n=!1}={}){let r=document.createElement(t?`button`:`div`);return r.tabIndex=-1,e?r.textContent=e:r.setAttribute(`aria-hidden`,`true`),n&&(r instanceof HTMLButtonElement?r.disabled=!0:r.setAttribute(`aria-disabled`,`true`)),r}drawDaysOfWeek(){if(!this.hasDaysOfWeekTarget)return;let e=new Intl.DateTimeFormat(this.localesValue,{weekday:this.weekdayFormatValue}),t=[];for(let n of this.calendar.daysOfWeek){let r=this.createDayElement(e.format(n.date));r.setAttribute(`role`,`columnheader`),r.title=n.long,this.hasDayOfWeekClass&&r.classList.add(...this.dayOfWeekClasses),t.push(r)}let n=document.createElement(`div`);n.setAttribute(`role`,`row`),n.replaceChildren(...t),this.daysOfWeekTarget.replaceChildren(n)}drawDaysOfMonth(){if(!this.hasDaysOfMonthTarget)return;let e=this.calendar.today,t=new Date(e.getFullYear(),e.getMonth(),e.getDate()).getTime(),n=[];for(let e of this.calendar.daysOfMonth){let r=!e.current||this.calendar.isDisabled(e.date)||!this.calendar.isWithinRange(e.date),i=e.current||this.daysOfOtherMonthValue?e.value:``,a=this.createDayElement(i,{selectable:e.current,disabled:r});t===e.date.getTime()&&a.setAttribute(`aria-current`,`date`),this.hasDayOfMonthClass&&a.classList.add(...this.dayOfMonthClasses);let o=document.createElement(`time`);o.dateTime=e.iso,a.appendChild(o),n.push(a)}let r=[];for(let e=0;e<n.length;e+=7){let t=document.createElement(`div`);t.setAttribute(`role`,`row`);for(let r of n.slice(e,e+7))r.setAttribute(`role`,`gridcell`),t.appendChild(r);r.push(t)}this.daysOfMonthTarget.replaceChildren(...r)}},Ae=class extends t.Controller{onSelect(e){if(!(e.target instanceof HTMLElement))return;e.preventDefault();let t=e.target instanceof HTMLTimeElement?e.target.parentElement:e.target;if(t.disabled||t.getAttribute(`aria-disabled`)===`true`)return;this.dispatch(`selecting`,{target:t});let n=e.target instanceof HTMLTimeElement?e.target:e.target.querySelector(`time`);if(!n)return console.error(`unable to locate time element within ${t}`);let r=A(n.dateTime);if(!r)return console.error(`unable to parse ${n.dateTime} found within the time element`);this.select(r.toISOString())}select(e){let t=A(e);t&&this.dispatch(`selected`,{detail:{epoch:t.getTime(),iso:e}})}},$=class extends t.Controller{static targets=[`source`];static values={type:{type:String,default:`text/plain`}};onPaste(e){let t=e.clipboardData?.getData(this.typeValue)??``,n=Array.from(e.clipboardData?.types??[]);e.preventDefault(),this.dispatch(`pasted`,{detail:{text:t,types:n},bubbles:!0})}async copy(e){let t=e.params?.text??(this.hasSourceTarget?this.sourceTarget.value??this.sourceTarget.textContent??``:``);try{await navigator.clipboard.writeText(t),this.dispatch(`copied`,{detail:{text:t},bubbles:!0})}catch(e){this.dispatch(`copy-failed`,{detail:{error:e},bubbles:!0})}}},je=class extends t.Controller{static targets=[`previous`,`next`,`day`,`month`,`year`];static outlets=[`calendar-month`];static values={date:String,locales:{type:Array,default:[`default`]},dayFormat:{type:String,default:`numeric`},monthFormat:{type:String,default:`long`},yearFormat:{type:String,default:`numeric`}};initialize(){this.previous=this.previous.bind(this),this.next=this.next.bind(this)}async calendarMonthOutletConnected(){if(this.dateValue){let e=A(this.dateValue);e&&await this.calendarMonthOutlet.calendar.navigate(e)}this.draw()}onSelect(e){this.dateValue=e.detail.iso,this.draw(),this.dispatch(`selected`,{detail:{value:e.detail.iso},bubbles:!0})}previousTargetConnected(e){e.addEventListener(`click`,this.previous)}previousTargetDisconnected(e){e.removeEventListener(`click`,this.previous)}async previous(){await this.calendarMonthOutlet.calendar.step(`month`,-1),this.draw()}nextTargetConnected(e){e.addEventListener(`click`,this.next)}nextTargetDisconnected(e){e.removeEventListener(`click`,this.next)}async next(){await this.calendarMonthOutlet.calendar.step(`month`,1),this.draw()}draw(){this.drawDay(),this.drawMonth(),this.drawYear()}drawDay(){if(!this.hasDayTarget||!this.hasCalendarMonthOutlet)return;let{year:e,month:t,day:n}=this.calendarMonthOutlet.calendar;this.dayTarget.textContent=new Intl.DateTimeFormat(this.localesValue,{day:this.dayFormatValue}).format(new Date(e,t,n))}drawMonth(){if(!this.hasMonthTarget||!this.hasCalendarMonthOutlet)return;let{year:e,month:t}=this.calendarMonthOutlet.calendar;this.monthTarget.textContent=new Intl.DateTimeFormat(this.localesValue,{month:this.monthFormatValue}).format(new Date(e,t))}drawYear(){if(!this.hasYearTarget||!this.hasCalendarMonthOutlet)return;let{year:e}=this.calendarMonthOutlet.calendar;this.yearTarget.textContent=new Intl.DateTimeFormat(this.localesValue,{year:this.yearFormatValue}).format(new Date(e,0))}},Me=class extends t.Controller{static targets=[`listbox`,`loading`,`empty`];static values={url:{type:String,default:``},field:{type:String,default:`q`},delay:{type:Number,default:300}};initialize(){this.comboboxDropdown=ce(this)}onSelect(e){let t=e.target.closest(`[role="option"]`);!t||t.getAttribute(`aria-disabled`)===`true`||this.select(t.dataset.value??``)}select(e){let t=this.listboxTarget.querySelectorAll(`[role="option"]`);t.forEach(e=>e.setAttribute(`aria-selected`,`false`));let n=[...t].find(t=>t.dataset.value===e);n&&n.setAttribute(`aria-selected`,`true`),this.dispatch(`selected`,{detail:{value:e},bubbles:!0})}onNavigate(e){if([`ArrowUp`,`ArrowDown`,`Enter`,` `].includes(e.key)){if(e.preventDefault(),e.key===`Enter`||e.key===` `){this.listboxTarget.querySelector(`[aria-selected="true"]`)?.click();return}this.step(e.key===`ArrowDown`?1:-1)}}step(e){let t=[...this.listboxTarget.querySelectorAll(`[role="option"]:not([aria-disabled="true"]):not([hidden])`)];if(!t.length)return;let n=this.listboxTarget.querySelector(`[aria-selected="true"]`),r=t.indexOf(n),i=e>0?t[Math.min(r+1,t.length-1)]:t[Math.max(r-1,0)];!i||i===n||(t.forEach(e=>e.setAttribute(`aria-selected`,`false`)),i.setAttribute(`aria-selected`,`true`),i.scrollIntoView({block:`nearest`}))}filter(e){if(this.urlValue)this.comboboxDropdown.scheduleFetch(e,this.delayValue,{url:this.urlValue,field:this.fieldValue,onLoading:e=>this.setLoading(e),onLoaded:e=>{this.listboxTarget.innerHTML=e,this.setEmpty(this.listboxTarget.querySelectorAll(`[role="option"]`).length===0)},onError:e=>console.error(`[combobox-dropdown] fetch failed`,e)});else{let t=this.comboboxDropdown.fuzzyFilter(this.listboxTarget,e);this.setEmpty(t===0)}}showAll(){this.listboxTarget.querySelectorAll(`[role="option"]`).forEach(e=>e.hidden=!1),this.setEmpty(!1)}setLoading(e){this.hasLoadingTarget&&(this.loadingTarget.hidden=!e)}setEmpty(e){this.hasEmptyTarget&&(this.emptyTarget.hidden=!e)}disconnect(){this.comboboxDropdown.cancel()}},Ne=class extends t.Controller{static targets=[`hour`,`minute`,`period`];connect(){this.select(this.toH24())}onSelect(e){let t=e.target.closest(`[role="option"]`);t&&(t.closest(`[role="listbox"]`).querySelectorAll(`[role="option"]`).forEach(e=>e.setAttribute(`aria-selected`,`false`)),t.setAttribute(`aria-selected`,`true`),this.select(this.toH24()))}select(e){e&&this.dispatch(`selected`,{detail:{value:e},bubbles:!0})}onNavigate(e){[`ArrowUp`,`ArrowDown`].includes(e.key)&&(e.preventDefault(),this.step(e.currentTarget,e.key===`ArrowDown`?1:-1))}step(e,t){let n=[...e.querySelectorAll(`[role="option"]`)],r=e.querySelector(`[aria-selected="true"]`),i=n.indexOf(r),a=t>0?n[Math.min(i+1,n.length-1)]:n[Math.max(i-1,0)];!a||a===r||(n.forEach(e=>e.setAttribute(`aria-selected`,`false`)),a.setAttribute(`aria-selected`,`true`),a.scrollIntoView({block:`nearest`}),this.select(this.toH24()))}toH24(){let e=this.selectedValue(this.hourTarget),t=this.selectedValue(this.minuteTarget);if(!e||!t)return null;if(!this.hasPeriodTarget)return`${e}:${t}`;let n=this.selectedValue(this.periodTarget),r=parseInt(e,10);return r=n===`AM`?r===12?0:r:r===12?12:r+12,`${String(r).padStart(2,`0`)}:${t}`}selectedValue(e){return e?.querySelector(`[aria-selected="true"]`)?.dataset.value??null}},Pe=class extends t.Controller{static targets=[`trigger`];connect(){F(this,{trigger:this.hasTriggerTarget?this.triggerTarget:null})}},Fe=class extends t.Controller{static targets=[`anchor`,`reference`];static values={placement:{type:String,default:`bottom`},alignment:{type:String,default:`start`},role:{type:String,default:`tooltip`}};connect(){if(!this.hasReferenceTarget){console.error(`FlipperController requires a reference target. Add data-flipper-target="reference" to your element.`);return}if(!this.hasAnchorTarget){console.error(`FlipperController requires an anchor target. Add data-flipper-target="anchor" to your element.`);return}R(this,{element:this.referenceTarget,anchor:this.anchorTarget,placement:this.placementValue,alignment:this.alignmentValue,ariaRole:this.roleValue})}},Ie=class extends t.Controller{static targets=[`trigger`,`popover`,`value`];static values={value:String,minLength:{type:Number,default:1}};static outlets=[`combobox-dropdown`];open(){this.hasPopoverTarget&&(this.popoverTarget.hidden=!1,this.hasTriggerTarget&&v(this.triggerTarget,!0),this.focusFirstInPopover())}close(){this.hasPopoverTarget&&(this.popoverTarget.hidden=!0,this.hasTriggerTarget&&(v(this.triggerTarget,!1),this.triggerTarget.focus()))}toggle(){this.hasPopoverTarget&&this.popoverTarget.hidden?this.open():this.close()}onSelect(e){e.detail?.value!==void 0&&(this.valueValue=e.detail.value),this.close()}onInput(e){if(e.target!==this.triggerTarget)return;let t=e.target.value.trim();if(t.length<this.minLengthValue){this.hasComboboxDropdownOutlet&&this.comboboxDropdownOutlet.showAll();return}this.hasComboboxDropdownOutlet&&this.comboboxDropdownOutlet.filter(t)}valueValueChanged(e){this.hasValueTarget&&(this.valueTarget.value=e),this.dispatch(`changed`,{detail:{value:e}})}focusFirstInPopover(){this.hasPopoverTarget&&this.popoverTarget.querySelector(`button:not([disabled]), [href], input:not([type="hidden"]):not([disabled]), [tabindex]:not([tabindex="-1"])`)?.focus()}},Le=class extends t.Controller{static targets=[`input`,`toggle`];static values={type:{type:String,default:`plain`},options:{type:Object,default:{}},revealed:{type:Boolean,default:!1}};connect(){X(this,{type:this.typeValue,options:this.optionsValue}),this.format(this.readValue()),this.drawToggle()}typeValueChanged(){this.inputFormat&&(X(this,{type:this.typeValue,options:this.optionsValue}),this.format(this.readValue()),this.drawToggle())}optionsValueChanged(){this.inputFormat&&(X(this,{type:this.typeValue,options:this.optionsValue}),this.format(this.readValue()))}revealedValueChanged(){this.inputFormat&&(this.format(this.readValue()),this.drawToggle())}onChange(e){this.format(e?.detail?.value??``)}format(e){this.inputFormat&&this.onFormatting(e)}toggle(){!this.inputFormat.maskable()&&this.typeValue!==`password`||(this.revealedValue=!this.revealedValue)}onPaste(e){let t=e.detail?.text??``;if(!this.inputFormat||!t)return;let n=this.inputFormat.normalize(t);this.inputFormat.validate(n)&&this.format(n)}drawToggle(){if(!this.hasToggleTarget)return;let e=this.inputFormat?.maskable()||this.typeValue===`password`;this.toggleTarget.hidden=!e,e&&y(this.toggleTarget,this.revealedValue)}readValue(){return this.hasInputTarget?this.inputTarget instanceof HTMLInputElement?this.inputTarget.value:this.inputTarget.textContent:``}onFormatting(e){if(!this.inputFormat)return;if(this.typeValue===`password`){this.hasInputTarget&&(this.inputTarget.type=this.revealedValue?`text`:`password`);return}let t=this.inputFormat.normalize(e),n=this.revealedValue||!this.inputFormat.maskable()?this.inputFormat.format(t):this.inputFormat.mask(t);this.hasInputTarget&&(this.inputTarget instanceof HTMLInputElement?this.inputTarget.value=n:this.inputTarget.textContent=n),this.dispatch(`formatted`,{detail:{value:n}})}},Re=class extends t.Controller{static targets=[`modal`,`overlay`];connect(){if(!this.hasModalTarget){console.error(`ModalController requires a modal target. Add data-modal-target="modal" to your element.`);return}this.isNativeDialog=this.modalTarget instanceof HTMLDialogElement,this.isNativeDialog?(this.modalTarget.addEventListener(`cancel`,this.close),this.modalTarget.addEventListener(`click`,this.onBackdropClick)):(this.focusTrap=new o(this.modalTarget,{escapeDeactivates:!0}),F(this,{element:this.modalTarget}))}dismissed=()=>{this.close()};disconnect(){this.isNativeDialog&&(this.modalTarget.removeEventListener(`cancel`,this.close),this.modalTarget.removeEventListener(`click`,this.onBackdropClick))}open(e){if(e&&e.preventDefault(),this.hasModalTarget){if(this.isNativeDialog)this.previouslyFocused=document.activeElement,this.modalTarget.showModal();else{let e=this.hasOverlayTarget?this.overlayTarget:this.modalTarget;e.hidden=!1,document.body.style.overflow=`hidden`,this.focusTrap&&this.focusTrap.activate()}m(`Modal opened`)}}close(e){if(e&&e.preventDefault(),this.hasModalTarget){if(this.isNativeDialog)this.modalTarget.close(),this.previouslyFocused&&this.previouslyFocused.isConnected&&setTimeout(()=>{this.previouslyFocused.focus()},0);else{let e=this.hasOverlayTarget?this.overlayTarget:this.modalTarget;e.hidden=!0,document.body.style.overflow=``,this.focusTrap&&this.focusTrap.deactivate()}m(`Modal closed`)}}onBackdropClick=e=>{let t=this.modalTarget.getBoundingClientRect();(e.clientY<t.top||e.clientY>t.bottom||e.clientX<t.left||e.clientX>t.right)&&this.close()}},ze=class extends t.Controller{static targets=[`content`];connect(){De(this,{element:this.hasContentTarget?this.contentTarget:null})}},Be=class extends t.Controller{static targets=[`content`,`template`,`loader`,`activator`];static classes=[`hidden`];static values={url:String,loadedAt:String,reload:{type:String,default:`never`},staleAfter:{type:Number,default:3600}};connect(){ue(this,{element:this.hasContentTarget?this.contentTarget:null,url:this.hasUrlValue?this.urlValue:null}),this.hasContentTarget&&Q(this,{element:this.contentTarget,activator:this.hasActivatorTarget?this.activatorTarget:null}),this.hasLoaderTarget&&Q(this,{element:this.loaderTarget,visibility:`contentLoaderVisibility`})}async show(){await this.visibility.show()}async hide(){await this.visibility.hide()}async shown(){await this.load()}canLoad(){return this.hasContentTarget&&this.contentTarget.tagName.toLowerCase()===`turbo-frame`?(this.hasUrlValue&&this.contentTarget.setAttribute(`src`,this.urlValue),!1):!0}async contentLoading(){this.hasLoaderTarget&&await this.contentLoaderVisibility.show()}async contentLoaded({content:e}){this.hasContentTarget&&this.contentTarget.replaceChildren(this.getContentNode(e)),this.hasLoaderTarget&&await this.contentLoaderVisibility.hide()}getContentNode(e){if(typeof e==`string`){let t=document.createElement(`template`);return t.innerHTML=e,document.importNode(t.content,!0)}return document.importNode(e,!0)}contentLoader(){if(this.hasTemplateTarget)return this.templateTarget instanceof HTMLTemplateElement?this.templateTarget.content:this.templateTarget.innerHTML}};e.ARIA_HASPOPUP_VALUES=b,e.CalendarMonthController=ke,e.CalendarMonthObserverController=Ae,e.ClipboardController=$,e.ComboboxDateController=je,e.ComboboxDropdownController=Me,e.ComboboxTimeController=Ne,e.DismisserController=Pe,e.FOCUSABLE_SELECTOR=n,e.FlipperController=Fe,e.FocusRestoration=s,e.FocusTrap=o,e.InputComboboxController=Ie,e.InputFormatController=Le,e.ModalController=Re,e.PannerController=ze,e.PopoverController=Be,e.RovingTabIndex=f,e.announce=m,e.connectTriggerToTarget=C,e.disconnectTriggerFromTarget=ne,e.ensureId=g,e.focusFirst=a,e.generateId=h,e.getFocusableElements=r,e.isActivationKey=l,e.isArrowKey=u,e.isKey=c,e.isVisible=i,e.preventDefault=d,e.setAriaState=_,e.setChecked=ee,e.setDisabled=te,e.setExpanded=v,e.setPressed=y});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Components
|
|
5
|
+
module Combobox
|
|
6
|
+
# Renders a listbox popover body for autocomplete.
|
|
7
|
+
# Re-uses combobox-dropdown for selection/filtering; input-combobox relays
|
|
8
|
+
# the trigger input event via the combobox-dropdown outlet.
|
|
9
|
+
class Autocomplete < Plumber::Base
|
|
10
|
+
include OptionGroup
|
|
11
|
+
|
|
12
|
+
DROPDOWN_CONTROLLER = "combobox-dropdown"
|
|
13
|
+
DROPDOWN_ACTION = [
|
|
14
|
+
"click->combobox-dropdown#select",
|
|
15
|
+
"keydown->combobox-dropdown#navigate",
|
|
16
|
+
"combobox-dropdown:selected->input-combobox#onSelected"
|
|
17
|
+
].join(" ").freeze
|
|
18
|
+
|
|
19
|
+
def self.default_opts
|
|
20
|
+
{
|
|
21
|
+
popover: {
|
|
22
|
+
tag: :div,
|
|
23
|
+
haspopup: "listbox",
|
|
24
|
+
data: { controller: DROPDOWN_CONTROLLER, action: DROPDOWN_ACTION }
|
|
25
|
+
},
|
|
26
|
+
trigger: { aria_autocomplete: "list", readonly: false }
|
|
27
|
+
}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def render(options: [], value: nil, label: nil, **_kwargs)
|
|
31
|
+
listbox_attrs = { role: "listbox", data: { "#{DROPDOWN_CONTROLLER}_target": "listbox" } }
|
|
32
|
+
listbox_attrs[:aria] = { label: label } if label
|
|
33
|
+
|
|
34
|
+
listbox = template.content_tag(:ul, **listbox_attrs) do
|
|
35
|
+
render_items(options, value: value)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
loading = template.content_tag(
|
|
39
|
+
:div,
|
|
40
|
+
hidden: "",
|
|
41
|
+
aria: { live: "polite" },
|
|
42
|
+
data: { "#{DROPDOWN_CONTROLLER}_target": "loading" }
|
|
43
|
+
) { "" }
|
|
44
|
+
|
|
45
|
+
empty = template.content_tag(
|
|
46
|
+
:div,
|
|
47
|
+
hidden: "",
|
|
48
|
+
role: "status",
|
|
49
|
+
data: { "#{DROPDOWN_CONTROLLER}_target": "empty" }
|
|
50
|
+
) { "No results" }
|
|
51
|
+
|
|
52
|
+
template.safe_join([listbox, loading, empty])
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Components
|
|
5
|
+
module Combobox
|
|
6
|
+
# Renders the date picker popover body: navigation + calendar grid.
|
|
7
|
+
# Wires ComboboxDateController; dispatches combobox-date:selected → InputComboboxController.
|
|
8
|
+
class Date < Plumber::Base
|
|
9
|
+
PICKER_CONTROLLER = "combobox-date"
|
|
10
|
+
CALENDAR_OUTLET_KEY = "#{PICKER_CONTROLLER.tr("-", "_")}_calendar_month_outlet".freeze
|
|
11
|
+
|
|
12
|
+
def self.default_opts
|
|
13
|
+
{
|
|
14
|
+
input: { data: { combobox_date_date_value: nil } },
|
|
15
|
+
popover: { label: "Picker", role: "dialog", tag: :div }
|
|
16
|
+
}
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def render(value: nil, **_kwargs)
|
|
20
|
+
calendar_id = "combobox_date_#{SecureRandom.hex(8)}_calendar"
|
|
21
|
+
|
|
22
|
+
template.content_tag(
|
|
23
|
+
:div,
|
|
24
|
+
data: {
|
|
25
|
+
controller: PICKER_CONTROLLER,
|
|
26
|
+
CALENDAR_OUTLET_KEY => "##{calendar_id}",
|
|
27
|
+
action: [
|
|
28
|
+
"calendar-month-observer:selected->#{PICKER_CONTROLLER}#onSelected",
|
|
29
|
+
"#{PICKER_CONTROLLER}:selected->input-combobox#onSelected"
|
|
30
|
+
].join(" ")
|
|
31
|
+
}.tap { |d| d["#{PICKER_CONTROLLER.tr("-", "_")}_date_value"] = value if value }
|
|
32
|
+
) do
|
|
33
|
+
template.safe_join([navigation, calendar_month(id: calendar_id)])
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def navigation
|
|
40
|
+
DatePicker::Navigation.new(template).render(
|
|
41
|
+
step: "month",
|
|
42
|
+
stimulus_controller: PICKER_CONTROLLER
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def calendar_month(**kwargs)
|
|
47
|
+
Calendar::Renderer.new(template).month(**kwargs)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Components
|
|
5
|
+
module Combobox
|
|
6
|
+
# Renders a static listbox popover body with combobox-dropdown controller.
|
|
7
|
+
# Supports flat options, descriptions, and option groups.
|
|
8
|
+
class Dropdown < Plumber::Base
|
|
9
|
+
include OptionGroup
|
|
10
|
+
|
|
11
|
+
DROPDOWN_CONTROLLER = "combobox-dropdown"
|
|
12
|
+
DROPDOWN_ACTION = [
|
|
13
|
+
"click->combobox-dropdown#select",
|
|
14
|
+
"keydown->combobox-dropdown#navigate",
|
|
15
|
+
"combobox-dropdown:selected->input-combobox#onSelected"
|
|
16
|
+
].join(" ").freeze
|
|
17
|
+
|
|
18
|
+
def self.default_opts
|
|
19
|
+
{
|
|
20
|
+
popover: {
|
|
21
|
+
tag: :div,
|
|
22
|
+
haspopup: "listbox",
|
|
23
|
+
data: { controller: DROPDOWN_CONTROLLER, action: DROPDOWN_ACTION }
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def render(options: [], value: nil, label: nil, **_kwargs)
|
|
29
|
+
listbox_attrs = { role: "listbox", data: { "#{DROPDOWN_CONTROLLER}_target": "listbox" } }
|
|
30
|
+
listbox_attrs[:aria] = { label: label } if label
|
|
31
|
+
|
|
32
|
+
listbox = template.content_tag(:ul, **listbox_attrs) do
|
|
33
|
+
render_items(options, value: value)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
template.safe_join([listbox])
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Components
|
|
5
|
+
module Combobox
|
|
6
|
+
class Option < Plumber::Base
|
|
7
|
+
def render(label:, value:, description: nil, disabled: false, selected: false)
|
|
8
|
+
aria = { selected: selected ? "true" : "false" }
|
|
9
|
+
aria[:disabled] = "true" if disabled
|
|
10
|
+
|
|
11
|
+
template.content_tag(:li, role: "option", aria: aria, data: { value: value }) do
|
|
12
|
+
if description
|
|
13
|
+
template.safe_join(
|
|
14
|
+
[
|
|
15
|
+
template.content_tag(:span, label),
|
|
16
|
+
template.content_tag(:span, description)
|
|
17
|
+
]
|
|
18
|
+
)
|
|
19
|
+
else
|
|
20
|
+
label
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Components
|
|
5
|
+
module Combobox
|
|
6
|
+
module OptionGroup
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def render_items(items, value: nil)
|
|
10
|
+
@selected_value = value.to_s
|
|
11
|
+
template.safe_join(items.map { |item| render_item(item) })
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def render_item(item)
|
|
15
|
+
case item
|
|
16
|
+
when Hash
|
|
17
|
+
item.key?(:options) ? render_group(item[:label], item[:options]) : render_option_hash(item)
|
|
18
|
+
else
|
|
19
|
+
render_option(item[0], item[1].to_s, item[2] || {})
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def render_option_hash(item)
|
|
24
|
+
render_option(item[:label], item[:value].to_s, item.except(:label, :value))
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def render_option(label, value, attrs = {})
|
|
28
|
+
Option.new(template).render(
|
|
29
|
+
label: label,
|
|
30
|
+
value: value,
|
|
31
|
+
selected: @selected_value == value,
|
|
32
|
+
disabled: attrs[:disabled] || false,
|
|
33
|
+
description: attrs[:description]
|
|
34
|
+
)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def render_group(label, options)
|
|
38
|
+
template.content_tag(:li, role: "group", aria: { label: label }) do
|
|
39
|
+
template.safe_join(
|
|
40
|
+
[
|
|
41
|
+
template.content_tag(:span, label, aria: { hidden: "true" }),
|
|
42
|
+
template.content_tag(:ul) do
|
|
43
|
+
template.safe_join(options.map { |opt| render_item(opt) })
|
|
44
|
+
end
|
|
45
|
+
]
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Components
|
|
5
|
+
module Combobox
|
|
6
|
+
class Renderer < Plumber::Base
|
|
7
|
+
STIMULUS_CONTROLLER = "input-combobox"
|
|
8
|
+
FORMAT_CONTROLLER = "input-format"
|
|
9
|
+
FORMAT_ACTION = "input-combobox:changed->input-format#format"
|
|
10
|
+
|
|
11
|
+
def render(base_id:, options: {}, **kwargs)
|
|
12
|
+
popover_id = "#{base_id}_popover"
|
|
13
|
+
initial_value = options.dig(:input, :value)
|
|
14
|
+
|
|
15
|
+
base_data = {
|
|
16
|
+
controller: "#{STIMULUS_CONTROLLER} #{FORMAT_CONTROLLER}",
|
|
17
|
+
action: FORMAT_ACTION
|
|
18
|
+
}
|
|
19
|
+
base_data[:input_combobox_value_value] = initial_value if initial_value.present?
|
|
20
|
+
|
|
21
|
+
html_options = merge_html_options({ data: base_data }, kwargs)
|
|
22
|
+
|
|
23
|
+
template.content_tag(:div, **html_options) do
|
|
24
|
+
template.safe_join(
|
|
25
|
+
[
|
|
26
|
+
trigger_input(
|
|
27
|
+
popover_id,
|
|
28
|
+
options.dig(:popover, :haspopup) || options.dig(:popover, :role) || "dialog",
|
|
29
|
+
options.fetch(:trigger, {})
|
|
30
|
+
),
|
|
31
|
+
hidden_input(options.fetch(:input, {})),
|
|
32
|
+
popover_element(popover_id, options.fetch(:popover, {}))
|
|
33
|
+
]
|
|
34
|
+
)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def trigger_input(popover_id, haspopup, opts)
|
|
41
|
+
base_data = {
|
|
42
|
+
"#{STIMULUS_CONTROLLER}_target": "trigger",
|
|
43
|
+
input_format_target: "input",
|
|
44
|
+
action: "focus->#{STIMULUS_CONTROLLER}#open keydown.esc->#{STIMULUS_CONTROLLER}#close"
|
|
45
|
+
}
|
|
46
|
+
data = merge_data_options(base_data, opts.fetch(:data, {}).symbolize_keys)
|
|
47
|
+
|
|
48
|
+
aria = { haspopup: haspopup, expanded: "false", controls: popover_id }
|
|
49
|
+
aria[:autocomplete] = opts[:aria_autocomplete] if opts[:aria_autocomplete]
|
|
50
|
+
aria[:label] = opts[:aria_label] if opts[:aria_label]
|
|
51
|
+
|
|
52
|
+
template.tag.input(
|
|
53
|
+
type: "text",
|
|
54
|
+
readonly: (opts.fetch(:readonly, true) ? true : nil),
|
|
55
|
+
role: "combobox",
|
|
56
|
+
aria: aria,
|
|
57
|
+
data: data
|
|
58
|
+
)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def hidden_input(opts)
|
|
62
|
+
data = { "#{STIMULUS_CONTROLLER}_target": "value" }.merge(opts.fetch(:data, {}))
|
|
63
|
+
template.tag.input(type: "hidden", name: opts[:name], value: opts[:value], data: data)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def popover_element(popover_id, opts)
|
|
67
|
+
base_data = { "#{STIMULUS_CONTROLLER}_target": "popover" }
|
|
68
|
+
data = merge_data_options(base_data, (opts[:data] || {}).symbolize_keys)
|
|
69
|
+
|
|
70
|
+
attrs = { id: popover_id, hidden: "", data: data }
|
|
71
|
+
attrs[:role] = opts[:role] if opts[:role]
|
|
72
|
+
attrs[:aria] = { label: opts[:label] } if opts[:label]
|
|
73
|
+
template.content_tag(opts.fetch(:tag, :div), **attrs) { opts[:content] }
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Components
|
|
5
|
+
module Combobox
|
|
6
|
+
# Renders an iOS-style drum/wheel time picker as the popover body.
|
|
7
|
+
# Wires ComboboxTimeController; dispatches combobox-time:selected → InputComboboxController.
|
|
8
|
+
class Time < Plumber::Base
|
|
9
|
+
PICKER_CONTROLLER = "combobox-time"
|
|
10
|
+
|
|
11
|
+
def self.default_opts
|
|
12
|
+
{
|
|
13
|
+
popover: { label: "Picker", role: "dialog", tag: :div }
|
|
14
|
+
}
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def render(format: :h12, step: 1, value: nil, **_kwargs)
|
|
18
|
+
@format = format
|
|
19
|
+
@step = [1, step.to_i].max
|
|
20
|
+
@time = parse_time(value)
|
|
21
|
+
|
|
22
|
+
template.content_tag(
|
|
23
|
+
:div,
|
|
24
|
+
data: {
|
|
25
|
+
controller: PICKER_CONTROLLER,
|
|
26
|
+
action: "#{PICKER_CONTROLLER}:selected->input-combobox#onSelected"
|
|
27
|
+
}
|
|
28
|
+
) do
|
|
29
|
+
template.safe_join(drums)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def drums
|
|
36
|
+
cols = [hour_drum, minute_drum]
|
|
37
|
+
cols << period_drum if @format == :h12
|
|
38
|
+
cols
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def hour_drum
|
|
42
|
+
drum.render(target: "hour", label: "Hour", items: hour_items, selected: current_hour)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def minute_drum
|
|
46
|
+
items = (0...60).step(@step).map do |m|
|
|
47
|
+
s = m.to_s.rjust(2, "0")
|
|
48
|
+
[s, s]
|
|
49
|
+
end
|
|
50
|
+
selected = @time ? snap_minute(@time.min).to_s.rjust(2, "0") : nil
|
|
51
|
+
drum.render(target: "minute", label: "Minute", items: items, selected: selected)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def period_drum
|
|
55
|
+
selected = if @time
|
|
56
|
+
@time.hour < 12 ? "AM" : "PM"
|
|
57
|
+
end
|
|
58
|
+
drum.render(target: "period", label: "Period", items: [%w[AM AM], %w[PM PM]], selected: selected)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def hour_items
|
|
62
|
+
if @format == :h12
|
|
63
|
+
(1..12).map { |h| [h.to_s, h.to_s] }
|
|
64
|
+
else
|
|
65
|
+
(0..23).map do |h|
|
|
66
|
+
s = h.to_s.rjust(2, "0")
|
|
67
|
+
[s, s]
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def current_hour
|
|
73
|
+
return nil unless @time
|
|
74
|
+
|
|
75
|
+
if @format == :h12
|
|
76
|
+
h = @time.hour % 12
|
|
77
|
+
(h.zero? ? 12 : h).to_s
|
|
78
|
+
else
|
|
79
|
+
@time.hour.to_s.rjust(2, "0")
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def snap_minute(minute)
|
|
84
|
+
return minute if @step == 1
|
|
85
|
+
|
|
86
|
+
((minute.to_f / @step).round * @step) % 60
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def drum
|
|
90
|
+
TimePicker::Renderer.new(template)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def parse_time(value)
|
|
94
|
+
return nil if value.nil? || value.to_s.strip.empty?
|
|
95
|
+
|
|
96
|
+
::Time.parse(value.to_s)
|
|
97
|
+
rescue ArgumentError
|
|
98
|
+
nil
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -4,7 +4,7 @@ module StimulusPlumbers
|
|
|
4
4
|
module Components
|
|
5
5
|
module DatePicker
|
|
6
6
|
class Navigation < Plumber::Base
|
|
7
|
-
def render(
|
|
7
|
+
def render(step:, stimulus_controller:, **kwargs)
|
|
8
8
|
html_options = merge_html_options(
|
|
9
9
|
{ classes: theme.resolve(:calendar_navigation).fetch(:classes, ""), aria: { label: "DatePicker Navigation" } },
|
|
10
10
|
kwargs
|
|
@@ -9,10 +9,29 @@ module StimulusPlumbers
|
|
|
9
9
|
extend ActiveSupport::Concern
|
|
10
10
|
|
|
11
11
|
def merge_html_options(*hashes)
|
|
12
|
-
classes
|
|
13
|
-
|
|
12
|
+
classes = hashes.flat_map { |h| [h[:class], h[:classes]] }
|
|
13
|
+
data_hashes = hashes.map { |h| h[:data] || {} }
|
|
14
|
+
rest = hashes.map { |h| h.except(:class, :classes, :data) }.reduce({}, :deep_merge)
|
|
15
|
+
|
|
14
16
|
class_value = merge_string_option(*classes).presence
|
|
15
|
-
|
|
17
|
+
merged_data = merge_data_options(*data_hashes)
|
|
18
|
+
|
|
19
|
+
result = class_value ? rest.merge(class: class_value) : rest
|
|
20
|
+
merged_data.present? ? result.merge(data: merged_data) : result
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
STIMULUS_SPACEJOIN_KEYS = %i[controller action].freeze
|
|
24
|
+
|
|
25
|
+
def merge_data_options(*hashes, spacejoin: STIMULUS_SPACEJOIN_KEYS)
|
|
26
|
+
hashes.reduce({}) do |acc, d|
|
|
27
|
+
acc.merge(d) do |key, old_val, new_val|
|
|
28
|
+
if spacejoin.include?(key.to_sym)
|
|
29
|
+
merge_string_option(old_val, new_val).presence || new_val
|
|
30
|
+
else
|
|
31
|
+
new_val
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
16
35
|
end
|
|
17
36
|
|
|
18
37
|
def merge_string_option(*parts, delimiter: " ")
|