UNPKG

@m3e/radio-group

Version:
502 lines (485 loc) 30 kB
/** * @license MIT * Copyright (c) 2025 matraic * See LICENSE file in the project root for full license text. */ import { LitElement, html, css } from 'lit'; import { Labelled, Dirty, Touched, Checked, KeyboardClick, Focusable, FormAssociated, Disabled, AttachInternals, Role, HoverController, PressedController, formValue, DesignToken, RequiredConstraintValidation, Required, ConstraintValidation, updateLabels } from '@m3e/core'; import { selectionManager, SelectionManager } from '@m3e/core/a11y'; /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */ function __decorate(decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; } function __classPrivateFieldGet(receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); } typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; /** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ const t$1=t=>(e,o)=>{ void 0!==o?o.addInitializer((()=>{customElements.define(t,e);})):customElements.define(t,e);}; /** * @license * Copyright 2019 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ const t=globalThis,e$3=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,s=Symbol(),o$2=new WeakMap;let n$2 = class n{constructor(t,e,o){if(this._$cssResult$=true,o!==s)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e;}get styleSheet(){let t=this.o;const s=this.t;if(e$3&&void 0===t){const e=void 0!==s&&1===s.length;e&&(t=o$2.get(s)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),e&&o$2.set(s,t));}return t}toString(){return this.cssText}};const r$2=t=>new n$2("string"==typeof t?t:t+"",void 0,s),S=(s,o)=>{if(e$3)s.adoptedStyleSheets=o.map((t=>t instanceof CSSStyleSheet?t:t.styleSheet));else for(const e of o){const o=document.createElement("style"),n=t.litNonce;void 0!==n&&o.setAttribute("nonce",n),o.textContent=e.cssText,s.appendChild(o);}},c$1=e$3?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const s of t.cssRules)e+=s.cssText;return r$2(e)})(t):t; /** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause */const{is:i,defineProperty:e$2,getOwnPropertyDescriptor:h,getOwnPropertyNames:r$1,getOwnPropertySymbols:o$1,getPrototypeOf:n$1}=Object,a=globalThis,c=a.trustedTypes,l=c?c.emptyScript:"",p=a.reactiveElementPolyfillSupport,d=(t,s)=>t,u={toAttribute(t,s){switch(s){case Boolean:t=t?l:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t);}return t},fromAttribute(t,s){let i=t;switch(s){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t);}catch(t){i=null;}}return i}},f=(t,s)=>!i(t,s),b={attribute:true,type:String,converter:u,reflect:false,useDefault:false,hasChanged:f};Symbol.metadata??=Symbol("metadata"),a.litPropertyMetadata??=new WeakMap;class y extends HTMLElement{static addInitializer(t){this._$Ei(),(this.l??=[]).push(t);}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(t,s=b){if(s.state&&(s.attribute=false),this._$Ei(),this.prototype.hasOwnProperty(t)&&((s=Object.create(s)).wrapped=true),this.elementProperties.set(t,s),!s.noAccessor){const i=Symbol(),h=this.getPropertyDescriptor(t,i,s);void 0!==h&&e$2(this.prototype,t,h);}}static getPropertyDescriptor(t,s,i){const{get:e,set:r}=h(this.prototype,t)??{get(){return this[s]},set(t){this[s]=t;}};return {get:e,set(s){const h=e?.call(this);r?.call(this,s),this.requestUpdate(t,h,i);},configurable:true,enumerable:true}}static getPropertyOptions(t){return this.elementProperties.get(t)??b}static _$Ei(){if(this.hasOwnProperty(d("elementProperties")))return;const t=n$1(this);t.finalize(),void 0!==t.l&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties);}static finalize(){if(this.hasOwnProperty(d("finalized")))return;if(this.finalized=true,this._$Ei(),this.hasOwnProperty(d("properties"))){const t=this.properties,s=[...r$1(t),...o$1(t)];for(const i of s)this.createProperty(i,t[i]);}const t=this[Symbol.metadata];if(null!==t){const s=litPropertyMetadata.get(t);if(void 0!==s)for(const[t,i]of s)this.elementProperties.set(t,i);}this._$Eh=new Map;for(const[t,s]of this.elementProperties){const i=this._$Eu(t,s);void 0!==i&&this._$Eh.set(i,t);}this.elementStyles=this.finalizeStyles(this.styles);}static finalizeStyles(s){const i=[];if(Array.isArray(s)){const e=new Set(s.flat(1/0).reverse());for(const s of e)i.unshift(c$1(s));}else void 0!==s&&i.push(c$1(s));return i}static _$Eu(t,s){const i=s.attribute;return false===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=false,this.hasUpdated=false,this._$Em=null,this._$Ev();}_$Ev(){this._$ES=new Promise((t=>this.enableUpdating=t)),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach((t=>t(this)));}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.();}removeController(t){this._$EO?.delete(t);}_$E_(){const t=new Map,s=this.constructor.elementProperties;for(const i of s.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t);}createRenderRoot(){const t=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return S(t,this.constructor.elementStyles),t}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(true),this._$EO?.forEach((t=>t.hostConnected?.()));}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach((t=>t.hostDisconnected?.()));}attributeChangedCallback(t,s,i){this._$AK(t,i);}_$ET(t,s){const i=this.constructor.elementProperties.get(t),e=this.constructor._$Eu(t,i);if(void 0!==e&&true===i.reflect){const h=(void 0!==i.converter?.toAttribute?i.converter:u).toAttribute(s,i.type);this._$Em=t,null==h?this.removeAttribute(e):this.setAttribute(e,h),this._$Em=null;}}_$AK(t,s){const i=this.constructor,e=i._$Eh.get(t);if(void 0!==e&&this._$Em!==e){const t=i.getPropertyOptions(e),h="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:u;this._$Em=e;const r=h.fromAttribute(s,t.type);this[e]=r??this._$Ej?.get(e)??r,this._$Em=null;}}requestUpdate(t,s,i){if(void 0!==t){const e=this.constructor,h=this[t];if(i??=e.getPropertyOptions(t),!((i.hasChanged??f)(h,s)||i.useDefault&&i.reflect&&h===this._$Ej?.get(t)&&!this.hasAttribute(e._$Eu(t,i))))return;this.C(t,s,i);} false===this.isUpdatePending&&(this._$ES=this._$EP());}C(t,s,{useDefault:i,reflect:e,wrapped:h},r){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,r??s??this[t]),true!==h||void 0!==r)||(this._$AL.has(t)||(this.hasUpdated||i||(s=void 0),this._$AL.set(t,s)),true===e&&this._$Em!==t&&(this._$Eq??=new Set).add(t));}async _$EP(){this.isUpdatePending=true;try{await this._$ES;}catch(t){Promise.reject(t);}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[t,s]of this._$Ep)this[t]=s;this._$Ep=void 0;}const t=this.constructor.elementProperties;if(t.size>0)for(const[s,i]of t){const{wrapped:t}=i,e=this[s];true!==t||this._$AL.has(s)||void 0===e||this.C(s,void 0,i,e);}}let t=false;const s=this._$AL;try{t=this.shouldUpdate(s),t?(this.willUpdate(s),this._$EO?.forEach((t=>t.hostUpdate?.())),this.update(s)):this._$EM();}catch(s){throw t=false,this._$EM(),s}t&&this._$AE(s);}willUpdate(t){}_$AE(t){this._$EO?.forEach((t=>t.hostUpdated?.())),this.hasUpdated||(this.hasUpdated=true,this.firstUpdated(t)),this.updated(t);}_$EM(){this._$AL=new Map,this.isUpdatePending=false;}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return true}update(t){this._$Eq&&=this._$Eq.forEach((t=>this._$ET(t,this[t]))),this._$EM();}updated(t){}firstUpdated(t){}}y.elementStyles=[],y.shadowRootOptions={mode:"open"},y[d("elementProperties")]=new Map,y[d("finalized")]=new Map,p?.({ReactiveElement:y}),(a.reactiveElementVersions??=[]).push("2.1.1"); /** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause */const o={attribute:true,type:String,converter:u,reflect:false,hasChanged:f},r=(t=o,e,r)=>{const{kind:n,metadata:i}=r;let s=globalThis.litPropertyMetadata.get(i);if(void 0===s&&globalThis.litPropertyMetadata.set(i,s=new Map),"setter"===n&&((t=Object.create(t)).wrapped=true),s.set(r.name,t),"accessor"===n){const{name:o}=r;return {set(r){const n=e.get.call(this);e.set.call(this,r),this.requestUpdate(o,n,t);},init(e){return void 0!==e&&this.C(o,void 0,t,e),e}}}if("setter"===n){const{name:o}=r;return function(r){const n=this[o];e.call(this,r),this.requestUpdate(o,n,t);}}throw Error("Unsupported decorator location: "+n)};function n(t){return (e,o)=>"object"==typeof o?r(t,e,o):((t,e,o)=>{const r=e.hasOwnProperty(o);return e.constructor.createProperty(o,t),r?Object.getOwnPropertyDescriptor(e,o):void 0})(t,e,o)} /** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ const e$1=(e,t,c)=>(c.configurable=true,c.enumerable=true,Reflect.decorate&&"object"!=typeof t&&Object.defineProperty(e,t,c),c); /** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause */function e(e,r){return (n,s,i)=>{const o=t=>t.renderRoot?.querySelector(e)??null;return e$1(n,s,{get(){return o(this)}})}} var _M3eRadioElement_instances, _M3eRadioElement_clickHandler, _M3eRadioElement_hoverController, _M3eRadioElement_pressedController, _M3eRadioElement_renderIcon, _M3eRadioElement_handleClick, _M3eRadioElement_notifySelectionChange; /** * A radio button that allows a user to select one option from a set of options. * * @description * The `m3e-radio` component represents a radio button that enables users to select an options from a set. * It supports selection from mutually exclusive options, emitting `input` and `change` events when its state updates. * The component reflects its state through customizable CSS properties for hover, focus, ripple, and icon styling— * adapting dynamically based on whether it is selected, unselected, or disabled. * * Attributes like `checked`, `disabled`, and `value` control its behavior and accessibility, while its visual * presentation can be tuned via design tokens such as `--m3e-radio-container-size` and `--m3e-radio-icon-size`. * * @example * The following example illustrates using `m3e-radio-group` and `m3e-radio` to present a group of options. * ```html * <label for="rdg1">Radio group</label> * <br /> * <m3e-radio-group id="rdg1"> * <label><m3e-radio value="1"></m3e-radio> Value 1</label> * <label><m3e-radio value="2"></m3e-radio> Value 2</label> * <label><m3e-radio value="3"></m3e-radio> Value 3</label> * <label><m3e-radio value="4"></m3e-radio> Value 4</label> * </m3e-radio-group> * ``` * * @tag m3e-radio * * @attr checked - Whether the element is checked. * @attr disabled - Whether the element is disabled. * @attr name - The name that identifies the element when submitting the associated form. * @attr required - Whether the element is required. * @attr value - A string representing the value of the radio. * * @fires input - Emitted when the checked state changes. * @fires change - Emitted when the checked state changes. * @fires click - Emitted when the element is clicked. * * @cssprop --m3e-radio-container-size - Base size of the radio button container. * @cssprop --m3e-radio-icon-size - Size of the radio icon inside the wrapper. * @cssprop --m3e-radio-unselected-hover-color - Hover state layer color when radio is not selected. * @cssprop --m3e-radio-unselected-focus-color - Focus state layer color when radio is not selected. * @cssprop --m3e-radio-unselected-ripple-color - Ripple color when radio is not selected. * @cssprop --m3e-radio-unselected-icon-color - Icon color when radio is not selected. * @cssprop --m3e-radio-selected-hover-color - Hover state layer color when radio is selected. * @cssprop --m3e-radio-selected-focus-color - Focus state layer color when radio is selected. * @cssprop --m3e-radio-selected-ripple-color - Ripple color when radio is selected. * @cssprop --m3e-radio-selected-icon-color - Icon color when radio is selected. * @cssprop --m3e-radio-disabled-icon-color - Icon color when radio is disabled. * @cssprop --m3e-radio-error-hover-color - Fallback hover color used when the radio is invalid and touched. * @cssprop --m3e-radio-error-focus-color - Fallback focus color used when the radio is invalid and touched. * @cssprop --m3e-radio-error-ripple-color - Fallback ripple color used when the radio is invalid and touched. * @cssprop --m3e-radio-error-icon-color - Fallback icon color used when the radio is invalid and touched. */ let M3eRadioElement = class M3eRadioElement extends Labelled(Dirty(Touched(Checked(KeyboardClick(Focusable(FormAssociated(Disabled(AttachInternals(Role(LitElement, "radio"), true))))))))) { constructor() { super(...arguments); _M3eRadioElement_instances.add(this); /** @private */ _M3eRadioElement_clickHandler.set(this, (e) => __classPrivateFieldGet(this, _M3eRadioElement_instances, "m", _M3eRadioElement_handleClick).call(this, e)); /** @private */ _M3eRadioElement_hoverController.set(this, new HoverController(this, { target: null, callback: (hovering) => { if (this.disabled) return; if (hovering) { this._stateLayer?.show("hover"); } else { this._stateLayer?.hide("hover"); } }, })); /** @private */ _M3eRadioElement_pressedController.set(this, new PressedController(this, { target: null, minPressedDuration: 150, callback: (pressed) => { if (this.disabled) return; if (pressed) { this._ripple?.show(0, 0, true); } else { this._ripple?.hide(); } }, })); /** * A string representing the value of the radio. * @default "on" */ this.value = "on"; } /** @inheritdoc @internal */ get [(_M3eRadioElement_clickHandler = new WeakMap(), _M3eRadioElement_hoverController = new WeakMap(), _M3eRadioElement_pressedController = new WeakMap(), _M3eRadioElement_instances = new WeakSet(), formValue)]() { return this.checked ? this.value : null; } /** @inheritdoc */ connectedCallback() { super.connectedCallback(); this.addEventListener("click", __classPrivateFieldGet(this, _M3eRadioElement_clickHandler, "f")); for (const label of this.labels) { __classPrivateFieldGet(this, _M3eRadioElement_hoverController, "f").observe(label); __classPrivateFieldGet(this, _M3eRadioElement_pressedController, "f").observe(label); } } /** @inheritdoc */ disconnectedCallback() { super.disconnectedCallback(); this.removeEventListener("click", __classPrivateFieldGet(this, _M3eRadioElement_clickHandler, "f")); for (const label of this.labels) { __classPrivateFieldGet(this, _M3eRadioElement_hoverController, "f").unobserve(label); __classPrivateFieldGet(this, _M3eRadioElement_pressedController, "f").unobserve(label); } } /** @inheritdoc */ update(changedProperties) { super.update(changedProperties); if (changedProperties.has("checked")) { __classPrivateFieldGet(this, _M3eRadioElement_instances, "m", _M3eRadioElement_notifySelectionChange).call(this); } } /** @inheritdoc */ firstUpdated(_changedProperties) { super.firstUpdated(_changedProperties); [this._focusRing, this._stateLayer, this._ripple].forEach((x) => x?.attach(this)); } /** @inheritdoc */ render() { return html `<div class="base"> <m3e-state-layer class="state-layer" ?disabled="${this.disabled}"></m3e-state-layer> <m3e-focus-ring class="focus-ring" ?disabled="${this.disabled}"></m3e-focus-ring> <m3e-ripple class="ripple" centered disable-enter ?disabled="${this.disabled}"></m3e-ripple> <div class="touch" aria-hidden="true"></div> <div class="wrapper" aria-hidden="true">${__classPrivateFieldGet(this, _M3eRadioElement_instances, "m", _M3eRadioElement_renderIcon).call(this)}</div> </div>`; } }; _M3eRadioElement_renderIcon = function _M3eRadioElement_renderIcon() { return html `<svg viewBox="0 0 20 20"> <mask id="cutout2"> <rect width="100%" height="100%" fill="white"></rect> <circle cx="10" cy="10" r="8" fill="black"></circle> </mask> <circle class="outer circle" cx="10" cy="10" r="10" mask="url(#cutout2)"></circle> <circle class="inner circle" cx="10" cy="10" r="5"></circle> </svg>`; }; _M3eRadioElement_handleClick = function _M3eRadioElement_handleClick(e) { if (e.defaultPrevented || this.checked || this.disabled) return; this.checked = true; if (this.dispatchEvent(new Event("input", { bubbles: true, composed: true, cancelable: true }))) { __classPrivateFieldGet(this, _M3eRadioElement_instances, "m", _M3eRadioElement_notifySelectionChange).call(this); this.dispatchEvent(new Event("change", { bubbles: true })); } else { this.checked = false; } }; _M3eRadioElement_notifySelectionChange = function _M3eRadioElement_notifySelectionChange() { const group = this.closest("m3e-radio-group"); if (group) { group[selectionManager].notifySelectionChange(this); } else if (this.name && this.checked) { // Uncheck any sibling radios of the same name which are also checked. [ ...(this.getRootNode()?.querySelectorAll(`m3e-radio[name="${this.name}"]`) ?? []), ] .filter((x) => x !== this && x.checked) .forEach((x) => (x.checked = false)); } }; /** The styles of the element. */ M3eRadioElement.styles = css ` :host { display: inline-block; outline: none; width: fit-content; height: fit-content; vertical-align: middle; } :host(:not([aria-disabled="true"])) { cursor: pointer; } .base { box-sizing: border-box; vertical-align: middle; display: inline-flex; align-items: center; justify-content: center; position: relative; border-radius: 50%; width: calc(var(--m3e-radio-container-size, 2.5rem) + ${DesignToken.density.calc(-3)}); height: calc(var(--m3e-radio-container-size, 2.5rem) + ${DesignToken.density.calc(-3)}); } .touch { position: absolute; height: 3rem; width: 3rem; margin: auto; } .wrapper { box-sizing: border-box; pointer-events: none; width: var(--m3e-radio-icon-size, 1.25rem); height: var(--m3e-radio-icon-size, 1.25rem); } .circle { fill: currentColor; } :host(:not([checked])) .circle.inner { opacity: 0; } :host(:not([checked])) .base { --m3e-state-layer-hover-color: var(--m3e-radio-unselected-hover-color, ${DesignToken.color.onSurface}); --m3e-state-layer-focus-color: var(--m3e-radio-unselected-focus-color, ${DesignToken.color.onSurface}); --m3e-ripple-color: var(--m3e-radio-unselected-ripple-color, ${DesignToken.color.onSurface}); color: var(--m3e-radio-unselected-icon-color, ${DesignToken.color.onSurfaceVariant}); } :host([checked]) .base { --m3e-state-layer-hover-color: var(--m3e-radio-selected-hover-color, ${DesignToken.color.primary}); --m3e-state-layer-focus-color: var(--m3e-radio-selected-focus-color, ${DesignToken.color.primary}); --m3e-ripple-color: var(--m3e-radio-selected-ripple-color, ${DesignToken.color.primary}); color: var(--m3e-radio-selected-icon-color, ${DesignToken.color.primary}); } :host([aria-disabled="true"]) .base { color: color-mix(in srgb, var(--m3e-radio-disabled-icon-color, ${DesignToken.color.onSurface}) 38%, transparent); } :host(.-touched.-invalid) .base { --m3e-state-layer-hover-color: var(--m3e-radio-error-hover-color, ${DesignToken.color.error}); --m3e-state-layer-focus-color: var(--m3e-radio-error-focus-color, ${DesignToken.color.error}); --m3e-ripple-color: var(--m3e-radio-error-ripple-color, ${DesignToken.color.error}); color: var(--m3e-radio-error-icon-color, ${DesignToken.color.error}); } @media (forced-colors: active) { :host(:not([checked])) .base, :host([checked]) .base { --m3e-state-layer-hover-color: var(--_radio-forced-color, CanvasText); --m3e-state-layer-focus-color: var(--_radio-forced-color, CanvasText); --m3e-ripple-color: var(--_radio-forced-color, CanvasText); color: var(--_radio-forced-color, CanvasText); } :host([aria-disabled="true"]) .base { color: GrayText; } :host(.-touched.-invalid) .base { --_radio-forced-color: Highlight; color: Highlight; } } `; __decorate([ e(".focus-ring") ], M3eRadioElement.prototype, "_focusRing", void 0); __decorate([ e(".state-layer") ], M3eRadioElement.prototype, "_stateLayer", void 0); __decorate([ e(".ripple") ], M3eRadioElement.prototype, "_ripple", void 0); __decorate([ n() ], M3eRadioElement.prototype, "value", void 0); M3eRadioElement = __decorate([ t$1("m3e-radio") ], M3eRadioElement); var _M3eRadioGroupElement_instances, _M3eRadioGroupElement_focusOutHandler, _M3eRadioGroupElement_handleSlotChange, _M3eRadioGroupElement_handleKeyDown, _M3eRadioGroupElement_handleChange, _a; /** * A container for a set of radio buttons. * * @description * The `m3e-radio-group` component is a semantic container that orchestrates a set of `m3e-radio` elements. * It provides accessible grouping, keyboard navigation, and validation logic for mutually exclusive selection. * When marked as `required`, the group enforces selection constraints and reflects validation state, while * delegating form submission to the checked radio. The group does not submit a value itself—it coordinates * behavior, focus, and feedback across its radios. * * @example * The following example illustrates using `m3e-radio-group` and `m3e-radio` to present a group of options. * ```html * <label for="rdg1">Radio group</label> * <br /> * <m3e-radio-group id="rdg1"> * <label><m3e-radio value="1"></m3e-radio> Value 1</label> * <label><m3e-radio value="2"></m3e-radio> Value 2</label> * <label><m3e-radio value="3"></m3e-radio> Value 3</label> * <label><m3e-radio value="4"></m3e-radio> Value 4</label> * </m3e-radio-group> * ``` * * @tag m3e-radio-group * * @slot - Renders the radio buttons of the group. * * @attr disabled - Whether the element is disabled. * @attr name - The name that identifies the element when submitting the associated form. * @attr required - Whether the element is required. * * @fires change - Emitted when the checked state of a radio button changes. */ let M3eRadioGroupElement = class M3eRadioGroupElement extends Labelled(RequiredConstraintValidation(Dirty(Touched(Required(ConstraintValidation(FormAssociated(Disabled(AttachInternals(Role(LitElement, "radiogroup")))))))))) { constructor() { super(...arguments); _M3eRadioGroupElement_instances.add(this); /** @private */ _M3eRadioGroupElement_focusOutHandler.set(this, () => __classPrivateFieldGet(this, _M3eRadioGroupElement_instances, "m", _M3eRadioGroupElement_handleChange).call(this)); /** @internal */ this[_a] = new SelectionManager().withWrap().onActiveItemChange(() => { this[selectionManager].activeItem?.click(); }); } /** The list of attributes corresponding to the registered properties. */ static get observedAttributes() { return [...super.observedAttributes, "aria-invalid"]; } /** The radios in the group. */ get radios() { return this[selectionManager]?.items ?? []; } /** The selected radio. */ get selected() { return this[selectionManager]?.selectedItems[0] ?? null; } /** The selected value of the radio group. */ get value() { return this.selected?.value ?? null; } /** @inheritdoc */ markAsTouched() { super.markAsTouched(); this.radios.forEach((x) => x.markAsTouched()); } /** @inheritdoc */ markAsUntouched() { super.markAsUntouched(); this.radios.forEach((x) => x.markAsUntouched()); } /** @inheritdoc */ markAsDirty() { super.markAsDirty(); this.radios.forEach((x) => x.markAsDirty()); } /** @inheritdoc */ markAsPristine() { super.markAsPristine(); this.radios.forEach((x) => x.markAsPristine()); } /** Synchronizes property values when attributes change. */ attributeChangedCallback(name, oldValue, newValue) { super.attributeChangedCallback(name, oldValue, newValue); switch (name) { case "name": this.radios.forEach((x) => (x.name = this.name)); break; case "aria-invalid": this.radios.forEach((x) => { x.classList.toggle("-invalid", newValue === "true"); x[updateLabels]?.(); }); break; } } /** @inheritdoc */ connectedCallback() { super.connectedCallback(); this.addEventListener("focusout", __classPrivateFieldGet(this, _M3eRadioGroupElement_focusOutHandler, "f")); } /** @inheritdoc */ disconnectedCallback() { super.disconnectedCallback(); this.removeEventListener("focusout", __classPrivateFieldGet(this, _M3eRadioGroupElement_focusOutHandler, "f")); } /** @inheritdoc */ update(changedProperties) { super.update(changedProperties); if (changedProperties.has("disabled")) { this.ariaDisabled = null; } if (changedProperties.has("disabled") && (changedProperties.get("disabled") !== undefined || this.disabled)) { this[selectionManager].disabled = this.disabled; } } /** @inheritdoc */ render() { return html `<slot @slotchange="${__classPrivateFieldGet(this, _M3eRadioGroupElement_instances, "m", _M3eRadioGroupElement_handleSlotChange)}" @keydown="${__classPrivateFieldGet(this, _M3eRadioGroupElement_instances, "m", _M3eRadioGroupElement_handleKeyDown)}" @change="${__classPrivateFieldGet(this, _M3eRadioGroupElement_instances, "m", _M3eRadioGroupElement_handleChange)}" ></slot>`; } }; _M3eRadioGroupElement_focusOutHandler = new WeakMap(); _M3eRadioGroupElement_instances = new WeakSet(); _a = selectionManager; _M3eRadioGroupElement_handleSlotChange = function _M3eRadioGroupElement_handleSlotChange() { const { added } = this[selectionManager].setItems([...this.querySelectorAll("m3e-radio")]); added.forEach((x) => (x.name = x.name || this.name)); }; _M3eRadioGroupElement_handleKeyDown = function _M3eRadioGroupElement_handleKeyDown(e) { this[selectionManager].onKeyDown(e); }; _M3eRadioGroupElement_handleChange = function _M3eRadioGroupElement_handleChange() { this.checkValidity(); }; /** The styles of the element. */ M3eRadioGroupElement.styles = css ` :host { display: inline; } `; M3eRadioGroupElement = __decorate([ t$1("m3e-radio-group") ], M3eRadioGroupElement); export { M3eRadioElement, M3eRadioGroupElement }; //# sourceMappingURL=index.js.map