UNPKG

web-ui-pack

Version:
279 lines (278 loc) 9.2 kB
import { WUPcssHidden } from "../styles"; import WUPBaseControl from "./baseControl"; const tagName = "wup-radio"; export default class WUPRadioControl extends WUPBaseControl { #ctr = this.constructor; static $ariaReadonly = __wupln("readonly", "aria"); static get $styleRoot() { return `:root { --ctrl-radio-item-r: 14px; --ctrl-radio-item-r-on: 8px; --ctrl-radio-item-bg: none; --ctrl-radio-item-on: var(--ctrl-focus); --ctrl-radio-item-border: #0003; --ctrl-radio-item-border-w: 2px; --ctrl-radio-gap: 7px; } [wupdark] { --ctrl-radio-item-border: var(--ctrl-label); }`; } static get $style() { return `${super.$style} :host { position: relative; padding: var(--ctrl-padding); } :host fieldset { border: none; padding: 0; margin: calc(var(--ctrl-radio-gap) * -0.5) calc(var(--ctrl-radio-gap) * -1); display: flex; flex-wrap: wrap; } :host legend { display: block; position: absolute; top: 0.2em; transform-origin: top left; transform: scale(0.9); margin: 0 var(--ctrl-radio-gap); padding: 0; box-sizing: border-box; max-width: 100%; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; font-weight: normal; text-decoration: none; } :host strong { font: inherit; } :host label { padding: var(--ctrl-radio-gap); gap: var(--ctrl-radio-gap); } :host[w-reverse] label { flex-direction: row-reverse; } :host input {${WUPcssHidden}} :host[readonly], :host[readonly] legend, :host[readonly] label { cursor: default; } :host [icon] { display: inline-flex; align-items: center; justify-content: center; width: var(--ctrl-radio-item-r); height: var(--ctrl-radio-item-r); box-sizing: border-box; background: var(--ctrl-radio-item-bg); box-shadow: 0 0 1px var(--ctrl-radio-item-border-w) var(--ctrl-radio-item-border); border-radius: 50%; } :host [icon]:after { content: ""; display: inline-block; width: var(--ctrl-radio-item-r-on); height: var(--ctrl-radio-item-r-on); border-radius: 50%; transition: background-color var(--anim); } :host input:checked + [icon]:after { background: var(--ctrl-radio-item-on); } @media not all and (prefers-reduced-motion) { :host label { transition: color var(--anim); } :host [icon] { transition: background-color var(--anim); } } :host label:focus-within { color: var(--ctrl-selected); } :host label:focus-within [icon] { --ctrl-radio-item-border: var(--ctrl-selected); } @media (hover: hover) and (pointer: fine) { :host label:hover { color: var(--ctrl-selected); } :host label:hover [icon] { --ctrl-radio-item-border: var(--ctrl-selected); } }`; } static $defaults = { ...WUPBaseControl.$defaults, validationRules: { ...WUPBaseControl.$defaults.validationRules }, items: [], reverse: false, }; static cloneDefaults() { const d = super.cloneDefaults(); d.items = []; return d; } parse(attrValue) { const a = this.getItems(); if (!a?.length) { return undefined; } const r = attrValue === "" ? a.find((o) => o.value == null) : a.find((o) => o.value && `${o.value}` === attrValue); return r?.value; } gotReady() { super.gotReady(); this.appendEvent(this, "input", this.gotInput); } gotInput(e) { this.setValue(this.getItems()[e.target._index].value, 3); } #cachedItems; getItems() { if (!this.#cachedItems) { const { items } = this._opts; this.#cachedItems = typeof items === "function" ? items() : items; } return this.#cachedItems; } $refFieldset = document.createElement("fieldset"); $refItems = []; renderControl() { this.$refFieldset.appendChild(document.createElement("legend")).appendChild(this.$refTitle); this.appendChild(this.$refFieldset); } renderItems(parent) { this.$refItems.forEach((r) => r.parentElement.remove()); this.$refItems.length = 0; const arr = this.getItems(); if (!arr?.length) { return; } const nm = this.#ctr.$uniqueId + (Date.now() % 1000); arr.forEach((item, i) => { const lbl = document.createElement("label"); const s = item.text; if (typeof s === "function") { s(item.value, lbl, i, this); } else { lbl.appendChild(document.createTextNode(item.text)); } const inp = lbl.appendChild(document.createElement("input")); lbl.appendChild(document.createElement("span")).setAttribute("icon", ""); this.$refItems.push(inp); inp.id = this.#ctr.$uniqueId; inp._index = i; lbl.setAttribute("for", inp.id); inp.type = "radio"; inp.name = nm; parent.appendChild(lbl); if (item.onClick) { lbl.onclick = (e) => item.onClick?.call(lbl, e, item); } }); this.$refItems[0].tabIndex = 0; setTimeout(() => this.checkInput(this.$value)); } checkInput(v) { this.$refInput.checked = false; this.$refLabel.removeAttribute("checked"); if (v === undefined) { this.$refInput = this.$refItems[0] || this.$refInput; } else { const item = this.$refItems[this.getItems().findIndex((a) => this.#ctr.$isEqual(a.value, v, this))]; if (!item) { console.error(`${this.tagName}${this._opts.name ? `[${this._opts.name}]` : ""}. Not found in items`, { items: this._opts.items, value: v, }); } else { item.checked = true; this.$refInput = item; } } this.setupInput(); this.$refLabel = this.$refInput.parentElement; this.$refInput.checked && this.$refLabel.setAttribute("checked", ""); } setValue(v, reason) { if (this.$isReady) { reason === 1 ? setTimeout(() => this.checkInput(v)) : this.checkInput(v); } return super.setValue(v, reason); } gotChanges(propsChanged) { this._opts.items ??= []; const isNeedRenderItems = !propsChanged || propsChanged.includes("items"); if (isNeedRenderItems) { this.#cachedItems = undefined; this.renderItems(this.$refFieldset); } super.gotChanges(propsChanged); this.setAttr("w-reverse", this._opts.reverse, true); const req = this.validations?.required; this.setAttr.call(this.$refFieldset, "aria-required", !!req); } gotFormChanges(propsChanged) { super.gotFormChanges(propsChanged); this.$refInput.disabled = false; this.$refFieldset.disabled = this.$isDisabled; this.$ariaDetails(this.$isReadOnly ? this.#ctr.$ariaReadonly : null); } setupInputReadonly() { const r = this.$isReadOnly; this.$refItems.forEach((a) => (a.readOnly = r)); this.$refFieldset.onclick = r ? (e) => e.preventDefault() : null; } gotFocus(e) { if (e.target !== this.$refInput) { this.$refInput.focus(); } return super.gotFocus(e); } focus() { this.$refInput.focus(); return document.activeElement === this.$refInput || super.focus(); } goShowError(err, target) { super.goShowError(err, this.$refFieldset); } valueToStrCompare(a) { const at = typeof a.text === "function" ? a.value?.toString() : a.text; return at?.replace(/\s/g, "") ?? null; } valueFromStorage(str) { if (str === "null") { return null; } const s = str.toLowerCase(); const items = this.getItems(); const item = items.find((a) => this.valueToStrCompare(a)?.toLowerCase() === s); if (item === undefined) { this.throwError("Not found in items (search by item.value.toString() & item.text)", { items, searchText: str, }); return undefined; } return item.value; } valueToStorage(v) { if (v == null) { return "null"; } const items = this.getItems(); const item = items.find((o) => this.#ctr.$isEqual(o.value, v, this)) || { value: v, text: v?.toString() }; return this.valueToStrCompare(item); } } customElements.define(tagName, WUPRadioControl);