UNPKG

vicowa-web-components

Version:
297 lines (274 loc) 9.15 kB
import { VicowaInputBaseClass } from '../vicowa-input-base/vicowa-input-base.js'; import '../vicowa-string/vicowa-string.js'; import { createQuickAccess } from '/third_party/web-component-base-class/src/tools.js'; /** * Class to represent the vicowa-single-selection custom element * @extends VicowaInputBaseClass */ class VicowaSingleSelection extends VicowaInputBaseClass { constructor() { super(); } static get properties() { return Object.assign({}, super.properties, { options: { type: Array, value: [], observer: (control) => control.#optionsChanged(), }, type: { type: String, value: 'radio', reflectToAttribute: true, observer: (control) => control.#optionsChanged(), }, opened: { type: Boolean, value: false, reflectToAttribute: true, }, hideLabel: { type: Boolean, value: false, reflectToAttribute: true, }, }); } updateTranslation() { super.updateTranslation(); } attached() { this.addAutoEventListener(this.$.dropdownControl, 'click', (event) => { this.opened = !this.opened; if (this.opened) { this.$.selectOptionContainer.style.width = `${this.$.dropdownControl.offsetWidth}px`; } event.preventDefault(); event.cancelBubble = true; const handleOutsideClick = () => { this.opened = false; this.removeAutoEventListener(window, 'click', handleOutsideClick); }; this.addAutoEventListener(window, 'click', handleOutsideClick); }); } _handleValueChange() { if (this.type) { switch (this.type) { case 'radio': this.$$$('[name="option-container"] input').forEach((radioOption) => { radioOption.checked = this.value === radioOption.id; if (this.value === radioOption.id) { radioOption.parentNode.setAttribute('checked', ''); } else { radioOption.parentNode.removeAttribute('checked'); } }); break; case 'select': this.$$$('[name="option-container"]').forEach((optionContainer) => { if (this.value === optionContainer.id) { optionContainer.setAttribute('checked', ''); } else { optionContainer.removeAttribute('checked'); } }); break; default: throw new Error('Invalid type specified'); } } } #handleSelectionChange(element) { this.value = element.id; } #optionsChanged() { this.$.radioOptionContainer.innerHTML = ''; this.$.selectOptionContainer.innerHTML = ''; if (this.type) { switch (this.type) { case 'radio': (this.options || []).forEach((option) => { const item = document.importNode(this.$.radioOption.content, true); const itemAccess = createQuickAccess(item, 'name'); itemAccess.optionLabelString.setAttribute('string', option.displayText || ''); itemAccess.optionLabel.setAttribute('for', option.value); itemAccess.vicowaSelectionOption.id = option.value; if (this.value === option.value) { itemAccess.optionContainer.setAttribute('checked', ''); } else { itemAccess.optionContainer.removeAttribute('checked'); } itemAccess.vicowaSelectionOption.checked = this.value === option.value; itemAccess.vicowaSelectionOption.addEventListener('change', () => { this.#handleSelectionChange(itemAccess.vicowaSelectionOption); this.$$$('[name="option-container"] input').forEach((radioOption) => { radioOption.checked = this.value === radioOption.id; if (this.value === radioOption.id) { radioOption.parentNode.setAttribute('checked', ''); } else { radioOption.parentNode.removeAttribute('checked'); } }); }); const childElement = (option.childElementName) ? document.createElement(option.childElementName) : (option.childElement || null); if (childElement) { itemAccess.childContainer.appendChild(childElement); } this.$.radioOptionContainer.appendChild(item); if (option.childElementName && childElement) { childElement.onAttached = () => { childElement.option = option; }; } }); break; case 'select': (this.options || []).forEach((option) => { const item = document.importNode(this.$.selectOption.content, true); const itemAccess = createQuickAccess(item, 'name'); itemAccess.optionLabelString.setAttribute('string', option.displayText || ''); itemAccess.optionContainer.id = option.value; if (this.value === option.value) { itemAccess.optionContainer.setAttribute('checked', ''); } else { itemAccess.optionContainer.removeAttribute('checked'); } itemAccess.optionContainer.addEventListener('click', () => { this.#handleSelectionChange(itemAccess.optionContainer); this.$$$('[name="option-container"]').forEach((optionContainer) => { if (this.value === optionContainer.id) { optionContainer.setAttribute('checked', ''); } else { optionContainer.removeAttribute('checked'); } }); }); const childElement = (option.childElementName) ? document.createElement(option.childElementName) : (option.childElement || null); if (childElement) { itemAccess.childContainer.appendChild(childElement); } this.$.selectOptionContainer.appendChild(item); if (option.childElementName && childElement) { childElement.onAttached = () => { childElement.option = option; }; } }); break; default: throw new Error('Invalid type specified'); } } } static get template() { return ` <style> :host { position: relative; display: block; box-sizing: border-box; width: var(--vicowa-single-selection-width, 150px); } :host([type="select"]) { } :host(:not([type="select"])) #dropdown-control { display: none; } #control-container { position: relative; display: flex; flex-direction: row; } #arrow { position: absolute; top: 7px; right: 5px; border-top: 8px solid var(--vicowa-single-selection-arrow-color, black); border-bottom: 8px solid transparent; border-left: 6px solid transparent; border-right: 6px solid transparent; } :host([opened]) #arrow { z-index: 1000; } label { flex: 0 0 auto; } #label { margin-right: 1em; } #input { position: relative; flex: 1 1 auto; overflow: hidden; } #dropdown-control { position: absolute; left: 0; top: 0; right: 0; bottom: 0; } #select-option-container { display: block; position: relative; overflow-x: hidden; overflow-y: auto; border: var(--vicowa-single-selection-border, 1px solid #bbb); background: var(--vicowa-single-selection-background, white); } .option-container { padding: 2px 5px; display: none; width: 100%; height: var(--vicowa-single-selection-height, auto); } :host([opened]) .option-container, :host(:not([type="select"])) .option-container, .option-container[checked] { display: block; cursor: pointer; } :host([opened]) .option-container[checked], :host([opened]) .option-container:hover { background: var(--vicowa-single-selection-selected-background, blue); color: var(--vicowa-single-selection-selected-color, white); --vicowa-icon-fill-color: var(--vicowa-single-selection-selected-color, white); } :host([opened]) #select-option-container { box-sizing: border-box; position: fixed; z-index: 1000; } :host([static][type="select"]) #arrow, :host([static]) .option-container:not([checked]), :host([static]) .option-container input { display: none; } :host([static]) #select-option-container, :host([static]) #radio-option-container { width: 100%; border: transparent; overflow: hidden; text-overflow: ellipsis; } :host([static]) label[name="option-label"] { max-width: 100%; overflow: hidden; text-overflow: ellipsis; display: inline-block; } :host([hide-label]) #label { display: none; } </style> <div id="control-container"> <label for="input"><vicowa-string id="label"></vicowa-string></label> <div id="input"> <div id="dropdown-control"> <div id="select-option-container"> </div> <div id="arrow"></div> </div> <div id="radio-option-container"></div> </div> </div> <vicowa-string id="error"></vicowa-string> <template id="radio-option"> <div name="option-container" class="option-container"> <input name="vicowa-selection-option" type="radio"> <label name="option-label"><vicowa-string name="option-label-string" string=""></vicowa-string></label> <div name="child-container"></div> </div> </template> <template id="select-option"> <div name="option-container" class="option-container"> <vicowa-string name="option-label-string" string=""></vicowa-string> <div name="child-container"></div> </div> </template> `; } } window.customElements.define('vicowa-single-selection', VicowaSingleSelection);