14 lines
12 KiB
JavaScript
14 lines
12 KiB
JavaScript
/*
|
|
* This file is part of the TYPO3 CMS project.
|
|
*
|
|
* It is free software; you can redistribute it and/or modify it under
|
|
* the terms of the GNU General Public License, either version 2
|
|
* of the License, or any later version.
|
|
*
|
|
* For the full copyright and license information, please read the
|
|
* LICENSE.txt file that was distributed with this source code.
|
|
*
|
|
* The TYPO3 project - inspiring people to share!
|
|
*/
|
|
import{LitElement as y,css as v,html as r}from"lit";import{property as g,state as b,customElement as C}from"lit/decorators.js";import"@typo3/backend/element/icon-element.js";var o=function(u,t,e,i){var s=arguments.length,n=s<3?t:i===null?i=Object.getOwnPropertyDescriptor(t,e):i,l;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")n=Reflect.decorate(u,t,e,i);else for(var h=u.length-1;h>=0;h--)(l=u[h])&&(n=(s<3?l(n):s>3?l(t,e,n):l(t,e))||n);return s>3&&n&&Object.defineProperty(t,e,n),n};const d={INPUT:"input",NAVIGATION:"navigation"},f={NONE:"none",CURRENT:"current"};let E=0,a=class extends y{constructor(){super(...arguments),this.value="",this.icon="",this.disabled=!1,this.isSelected=!1}static{this.styles=v`*,:after,:before{box-sizing:border-box}:host{display:flex;align-items:center;width:100%;user-select:none}`}connectedCallback(){super.connectedCallback(),this.setAttribute("role","option"),this.setAttribute("aria-selected","false"),!this.value&&this.textContent&&(this.value=this.textContent.trim()),this.updateAriaLabel()}setSelected(t){this.isSelected=t}updated(t){t.has("value")&&this.updateAriaLabel()}render(){const t=this.getLabel(),e=this.value&&this.value!==t;return r`<span class=indicator part=indicator> <typo3-backend-icon identifier=${this.isSelected?"actions-check":"miscellaneous-placeholder"} size=small aria-hidden=true></typo3-backend-icon> </span> ${this.icon?r`<typo3-backend-icon identifier=${this.icon} size=small part=icon></typo3-backend-icon>`:""} <span class=content part=content>${t} ${e?r`<span class=value part=value>(${this.value})</span>`:""} </span>`}getLabel(){return this.textContent?.trim()||this.value}updateAriaLabel(){const t=this.getLabel(),e=this.value&&this.value!==t?` (${this.value})`:"";this.setAttribute("aria-label",`${t}${e}`)}};o([g({type:String})],a.prototype,"value",void 0),o([g({type:String})],a.prototype,"icon",void 0),o([g({type:Boolean})],a.prototype,"disabled",void 0),o([b()],a.prototype,"isSelected",void 0),a=o([C("typo3-backend-combobox-choice")],a);let c=class extends y{static{this.styles=v`*,:after,:before{box-sizing:border-box}:host{position:relative;display:block;width:100%}.controls{position:absolute;top:50%;inset-inline-end:0;transform:translateY(-50%);gap:.25rem;padding-inline-end:var(--typo3-form-combobox-padding-x,.75rem)}.clear-button,.controls{display:flex;align-items:center}.clear-button{justify-content:center;cursor:pointer;color:inherit;opacity:.3;transition:opacity .2s ease}.clear-button:hover{opacity:.5}.separator{width:1px;height:1.25rem;background-color:var(--typo3-input-border-color,currentColor)}.indicator{display:flex;align-items:center;justify-content:center}.listbox{position:absolute;top:100%;left:0;width:100%;z-index:1000}`}constructor(){super(),this.isOpen=!1,this.inputHasValue=!1,this.highlightedIndex=-1,this.inputIsDisabledOrReadonly=!1,this.choiceIdCounter=0,this.delayedCloseTimeout=null,this.internalId=null,this.lastAction=null,this.lastHighlightedIndex=null,this.userSelectionIntent=null,this.handleChoiceMouseEnter=t=>{const e=t.currentTarget,s=this.getChoiceElements().indexOf(e);s>=0&&this.setHighlightedIndex(s)},this.handleInputInit=()=>{this.updateInputHasValue(),this.updateSelectedState()},this.handleInputInput=()=>{this.lastAction=d.INPUT,this.highlightedIndex!==-1&&(this.lastHighlightedIndex=this.highlightedIndex,this.setHighlightedIndex(-1)),this.updateInputHasValue(),this.updateSelectedState()},this.handleInputPointerdown=t=>{t.button===0&&this.setOpen()},this.handleInputBlur=t=>{this.contains(t.relatedTarget)?this.getInput()?.focus():this.dispatchClose()},this.handleInputKeydown=t=>{const e=this.getInput();if(e?.disabled||e?.readOnly)return;const s=this.getChoiceElements().length;switch(t.key){case"ArrowDown":t.preventDefault(),t.stopPropagation(),t.altKey&&!this.isOpen?this.userSelectionIntent=f.NONE:(this.lastAction=d.NAVIGATION,this.setHighlightedIndex(this.getNextHighlightIndex(1,s))),this.setOpen();break;case"ArrowUp":t.preventDefault(),t.stopPropagation(),t.altKey&&!this.isOpen?this.userSelectionIntent=f.NONE:(this.lastAction=d.NAVIGATION,this.setHighlightedIndex(this.getNextHighlightIndex(-1,s))),this.setOpen();break;case"Enter":this.isOpen&&(this.highlightedIndex>=0&&this.lastAction===d.NAVIGATION&&(t.preventDefault(),this.selectChoiceOption(this.highlightedIndex)),this.immediateClose());break;case"Tab":this.isOpen&&(this.highlightedIndex>=0&&this.lastAction===d.NAVIGATION&&this.selectChoiceOption(this.highlightedIndex),this.immediateClose(),t.shiftKey||t.preventDefault());break;case"Escape":if(this.lastAction=d.NAVIGATION,this.isOpen)this.isOpen=!1,t.preventDefault(),t.stopPropagation();else{const n=this.getInput();n&&n.value&&!n.readOnly&&!n.disabled&&(this.clearInput(),this.isOpen=!1,t.preventDefault(),t.stopPropagation())}break;default:break}},this.handleClearClick=t=>{t.stopPropagation(),this.clearInput(),this.getInput()?.focus()},this.handleIndicatorClick=t=>{t.stopPropagation(),this.isOpen?this.immediateClose():(this.setOpen(),this.getInput()?.focus())},this.handleChoiceClick=t=>{const e=t.target,i=e.closest("typo3-backend-combobox-choice"),s=this.getInput();if(i&&!i.disabled){const l=this.getChoiceElements().indexOf(i);if(l>=0){this.selectChoiceOption(l),this.immediateClose(),s.focus();return}}s&&e!==s&&(s.focus(),this.setOpen())},this.addEventListener("click",this.handleChoiceClick)}focus(t){this.getInput()?.focus(t)}blur(){this.getInput()?.blur()}willUpdate(t){if(t.has("isOpen")&&this.isOpen&&this.userSelectionIntent!==f.NONE){const e=this.findCurrentSelectionChoice();if(e){const s=this.getChoiceElements().indexOf(e);this.setHighlightedIndex(s)}}}render(){const t=this.getInput();return t&&(t.setAttribute("aria-expanded",this.isOpen.toString()),t.setAttribute("aria-activedescendant",this.getActiveDescendantId())),r`<slot @slotchange=${this.handleSlotChange}></slot>${this.inputIsDisabledOrReadonly?"":r`<div class=controls part=controls>${this.inputHasValue?r`<span class=clear-button part=clear-button @click=${this.handleClearClick} aria-label="Clear value" tabindex=-1> <typo3-backend-icon identifier=actions-close size=small aria-hidden=true></typo3-backend-icon> </span> <span class=separator part=separator></span>`:""} <span class=indicator part=indicator tabindex=-1 @click=${this.handleIndicatorClick} style=cursor:pointer><typo3-backend-icon identifier=actions-chevron-expand size=small aria-hidden=true></typo3-backend-icon> </span></div><div class=listbox part=listbox role=listbox id=${this.getId()}-listbox ?hidden=${!this.isOpen}><slot name=choices></slot></div>`}`}firstUpdated(){this.setup()}updated(t){t.has("isOpen")&&(this.isOpen?(this.positionDropdown(),this.scrollToHighlightedOption()):(this.setHighlightedIndex(-1),this.lastAction=null,this.userSelectionIntent=null))}getId(){return this.internalId??=this.id!==""?this.id:"combobox-"+E++,this.internalId}positionDropdown(){const t=this.shadowRoot?.querySelector(".listbox");if(!t)return;const e=this.getBoundingClientRect(),i=window.innerHeight,s=window.scrollY,n=window.getComputedStyle(t),l=n.maxHeight!=="none"?parseFloat(n.maxHeight):1/0,h=Math.min(t.scrollHeight,l),I=i-(e.bottom-s),m=e.top-s,x=I<h&&m>I,p=x?m:I;requestAnimationFrame(()=>{x?(t.style.top="auto",t.style.bottom="100%",h>p?t.style.maxHeight=`${Math.floor(p)}px`:t.style.removeProperty("max-height")):(t.style.top="100%",t.style.bottom="auto",h>p?t.style.maxHeight=`${Math.floor(p)}px`:t.style.removeProperty("max-height"))})}scrollToHighlightedOption(){if(this.highlightedIndex<0)return;const e=this.getChoiceElements()[this.highlightedIndex];e&&e.scrollIntoView({block:"nearest",inline:"nearest"})}selectChoiceOption(t){const i=this.getChoiceElements()[t];if(!i||i.disabled)return;const s=i.value,n=this.getInput();n&&(n.value=s,this.dispatchInputEvents(n)),this.updateInputHasValue(),this.updateSelectedState(),this.setHighlightedIndex(t)}setHighlightedIndex(t){this.getChoiceElements().forEach((i,s)=>{i.setAttribute("aria-selected",s===t?"true":"false")}),this.highlightedIndex=t,this.scrollToHighlightedOption()}getNextHighlightIndex(t,e){if(this.highlightedIndex<0){const{lastHighlightedIndex:i}=this;if(i!==null&&i!==-1)return this.lastHighlightedIndex=null,i;const s=this.findCurrentSelectionChoice();return s?this.getChoiceElements().indexOf(s):t>0?0:e-1}return t>0?Math.min(this.highlightedIndex+1,e-1):Math.max(this.highlightedIndex-1,0)}setup(){this.setupInputAttributes(),this.setupChoiceElements(),this.updateInputHasValue(),this.updateSelectedState()}handleSlotChange(){this.setup()}setupChoiceElements(){this.querySelectorAll('typo3-backend-combobox-choice:not([slot="choices"])').forEach(e=>{e.slot="choices",e.tabIndex=-1,e.id||(e.id=`${this.getId()}-option-${++this.choiceIdCounter}`),e.addEventListener("mouseenter",this.handleChoiceMouseEnter)})}setupInputAttributes(){const t=this.getInput();t&&(t.removeEventListener("formengine:input:initialized",this.handleInputInit),t.removeEventListener("input",this.handleInputInput),t.removeEventListener("pointerdown",this.handleInputPointerdown),t.removeEventListener("blur",this.handleInputBlur),t.removeEventListener("keydown",this.handleInputKeydown),t.setAttribute("role","combobox"),t.setAttribute("aria-expanded","false"),t.setAttribute("aria-haspopup","listbox"),t.setAttribute("aria-controls",`${this.getId()}-listbox`),t.setAttribute("autocomplete","off"),t.addEventListener("formengine:input:initialized",this.handleInputInit),t.addEventListener("input",this.handleInputInput),t.addEventListener("pointerdown",this.handleInputPointerdown),t.addEventListener("blur",this.handleInputBlur),t.addEventListener("keydown",this.handleInputKeydown),this.updateInputDisabledState())}getInput(){const t=this.shadowRoot?.querySelector("slot:not([name])");return t?t.assignedElements().find(i=>i.tagName==="INPUT"):null}setOpen(){const t=this.getInput();this.updateInputDisabledState(),!(t?.disabled||t?.readOnly)&&(this.isOpen=!0,this.delayedCloseTimeout!==null&&(clearTimeout(this.delayedCloseTimeout),this.delayedCloseTimeout=null))}immediateClose(){this.isOpen=!1}dispatchClose(){this.delayedCloseTimeout===null&&(this.delayedCloseTimeout=setTimeout(()=>{this.immediateClose()},100))}getChoiceElements(){return Array.from(this.querySelectorAll("typo3-backend-combobox-choice"))}findCurrentSelectionChoice(){const t=this.getInput();return t?.value&&this.getChoiceElements().find(i=>i.value===t.value)||null}getActiveDescendantId(){const t=this.getChoiceElements();return this.highlightedIndex>=0&&t[this.highlightedIndex]?.id||""}updateSelectedState(){const t=this.getInput();this.getChoiceElements().forEach(i=>{const s=i.value===t?.value;i.setSelected(s)})}updateInputHasValue(){const t=this.getInput();this.inputHasValue=(t?.value||"").length>0}updateInputDisabledState(){const t=this.getInput();this.inputIsDisabledOrReadonly=t?.disabled||t?.readOnly||!1}dispatchInputEvents(t){t.dispatchEvent(new Event("input",{bubbles:!0})),t.dispatchEvent(new Event("change",{bubbles:!0}))}clearInput(){const t=this.getInput();t&&(t.value="",this.dispatchInputEvents(t),this.updateInputHasValue(),this.setHighlightedIndex(-1))}};o([b()],c.prototype,"isOpen",void 0),o([g({type:Boolean,reflect:!0,attribute:"data-has-value"})],c.prototype,"inputHasValue",void 0),o([b()],c.prototype,"highlightedIndex",void 0),o([b()],c.prototype,"inputIsDisabledOrReadonly",void 0),c=o([C("typo3-backend-combobox")],c);export{a as ComboboxChoiceElement,c as ComboboxElement};
|