@m3e/tabs 1.0.0-rc.1

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.
package/dist/index.js ADDED
@@ -0,0 +1,737 @@
1
+ /**
2
+ * @license MIT
3
+ * Copyright (c) 2025 matraic
4
+ * See LICENSE file in the project root for full license text.
5
+ */
6
+ import { LitElement, html, css, nothing, unsafeCSS } from 'lit';
7
+ import { Selected, HtmlFor, KeyboardClick, Focusable, Disabled, AttachInternals, Role, DesignToken, ResizeController } from '@m3e/core';
8
+ import { addAriaReferencedId, removeAriaReferencedId, selectionManager, SelectionManager } from '@m3e/core/a11y';
9
+
10
+ /******************************************************************************
11
+ Copyright (c) Microsoft Corporation.
12
+
13
+ Permission to use, copy, modify, and/or distribute this software for any
14
+ purpose with or without fee is hereby granted.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
17
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
18
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
19
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
20
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
21
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22
+ PERFORMANCE OF THIS SOFTWARE.
23
+ ***************************************************************************** */
24
+ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
25
+
26
+
27
+ function __decorate(decorators, target, key, desc) {
28
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
29
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
30
+ 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;
31
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
32
+ }
33
+
34
+ function __classPrivateFieldGet(receiver, state, kind, f) {
35
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
36
+ 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");
37
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
38
+ }
39
+
40
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
41
+ var e = new Error(message);
42
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
43
+ };
44
+
45
+ /**
46
+ * @license
47
+ * Copyright 2017 Google LLC
48
+ * SPDX-License-Identifier: BSD-3-Clause
49
+ */
50
+ const t$1=t=>(e,o)=>{ void 0!==o?o.addInitializer((()=>{customElements.define(t,e);})):customElements.define(t,e);};
51
+
52
+ /**
53
+ * @license
54
+ * Copyright 2019 Google LLC
55
+ * SPDX-License-Identifier: BSD-3-Clause
56
+ */
57
+ 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$3=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$3(e)})(t):t;
58
+
59
+ /**
60
+ * @license
61
+ * Copyright 2017 Google LLC
62
+ * SPDX-License-Identifier: BSD-3-Clause
63
+ */const{is:i,defineProperty:e$2,getOwnPropertyDescriptor:h,getOwnPropertyNames:r$2,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$2(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");
64
+
65
+ /**
66
+ * @license
67
+ * Copyright 2017 Google LLC
68
+ * SPDX-License-Identifier: BSD-3-Clause
69
+ */const o={attribute:true,type:String,converter:u,reflect:false,hasChanged:f},r$1=(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$1(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)}
70
+
71
+ /**
72
+ * @license
73
+ * Copyright 2017 Google LLC
74
+ * SPDX-License-Identifier: BSD-3-Clause
75
+ */function r(r){return n({...r,state:true,attribute:false})}
76
+
77
+ /**
78
+ * @license
79
+ * Copyright 2017 Google LLC
80
+ * SPDX-License-Identifier: BSD-3-Clause
81
+ */
82
+ const e$1=(e,t,c)=>(c.configurable=true,c.enumerable=true,Reflect.decorate&&"object"!=typeof t&&Object.defineProperty(e,t,c),c);
83
+
84
+ /**
85
+ * @license
86
+ * Copyright 2017 Google LLC
87
+ * SPDX-License-Identifier: BSD-3-Clause
88
+ */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)}})}}
89
+
90
+ var _M3eTabElement_instances, _M3eTabElement_clickHandler, _M3eTabElement_handleClick;
91
+ var M3eTabElement_1;
92
+ /**
93
+ * @summary
94
+ * An interactive element that, when activated, presents an associated tab panel.
95
+ *
96
+ * @description
97
+ * The `m3e-tab` component is an interactive control used within a tabbed interface to activate and
98
+ * reveal an associated tab panel. It supports accessible labeling, optional iconography, and selection
99
+ * state styling consistent with Material 3 guidance. Tabs may be disabled, selected, or linked to a
100
+ * specific panel via the `for` attribute, enabling declarative control and semantic clarity.
101
+ *
102
+ * @example
103
+ * The following example illustrates using the `m3e-tabs`, `m3e-tab`, and `m3e-tab-panel` components to present
104
+ * secondary tabs.
105
+ * ```html
106
+ * <m3e-tabs>
107
+ * <m3e-tab selected for="videos"><m3e-icon slot="icon" name="videocam"></m3e-icon>Video</m3e-tab>
108
+ * <m3e-tab for="photos"><m3e-icon slot="icon" name="photo"></m3e-icon>Photos</m3e-tab>
109
+ * <m3e-tab for="audio"><m3e-icon slot="icon" name="music_note"></m3e-icon>Audio</m3e-tab>
110
+ * <m3e-tab-panel id="videos">Videos</m3e-tab-panel>
111
+ * <m3e-tab-panel id="photos">Photos</m3e-tab-panel>
112
+ * <m3e-tab-panel id="audio">Audio</m3e-tab-panel>
113
+ * </m3e-tabs>
114
+ * ```
115
+ *
116
+ * @tag m3e-tab
117
+ *
118
+ * @slot - Renders the label of the tab.
119
+ * @slot icon - Renders an icon before the tab's label.
120
+ *
121
+ * @attr disabled - Whether the element is disabled.
122
+ * @attr for - The query selector used to specify the element related to this element.
123
+ * @attr selected - Whether the element is selected.
124
+ *
125
+ * @cssprop --m3e-tab-font-size - Font size for tab label.
126
+ * @cssprop --m3e-tab-font-weight - Font weight for tab label.
127
+ * @cssprop --m3e-tab-line-height - Line height for tab label.
128
+ * @cssprop --m3e-tab-tracking - Letter spacing for tab label.
129
+ * @cssprop --m3e-tab-padding-start - Padding on the inline start of the tab.
130
+ * @cssprop --m3e-tab-padding-end - Padding on the inline end of the tab.
131
+ * @cssprop --m3e-tab-focus-ring-shape - Border radius for the focus ring.
132
+ * @cssprop --m3e-tab-selected-color - Text color for selected tab.
133
+ * @cssprop --m3e-tab-selected-container-hover-color - Hover state-layer color for selected tab.
134
+ * @cssprop --m3e-tab-selected-container-focus-color - Focus state-layer color for selected tab.
135
+ * @cssprop --m3e-tab-selected-ripple-color - Ripple color for selected tab.
136
+ * @cssprop --m3e-tab-unselected-color - Text color for unselected tab.
137
+ * @cssprop --m3e-tab-unselected-container-hover-color - Hover state-layer color for unselected tab.
138
+ * @cssprop --m3e-tab-unselected-container-focus-color - Focus state-layer color for unselected tab.
139
+ * @cssprop --m3e-tab-unselected-ripple-color - Ripple color for unselected tab.
140
+ * @cssprop --m3e-tab-disabled-color - Text color for disabled tab.
141
+ * @cssprop --m3e-tab-disabled-opacity - Text opacity for disabled tab.
142
+ * @cssprop --m3e-tab-spacing - Column gap between icon and label.
143
+ * @cssprop --m3e-tab-icon-size - Font size for slotted icon.
144
+ */
145
+ let M3eTabElement = M3eTabElement_1 = class M3eTabElement extends Selected(HtmlFor(KeyboardClick(Focusable(Disabled(AttachInternals(Role(LitElement, "tab"), true)))))) {
146
+ constructor() {
147
+ super(...arguments);
148
+ _M3eTabElement_instances.add(this);
149
+ /** @private */ _M3eTabElement_clickHandler.set(this, (e) => __classPrivateFieldGet(this, _M3eTabElement_instances, "m", _M3eTabElement_handleClick).call(this, e));
150
+ }
151
+ /** @inheritdoc */
152
+ attach(control) {
153
+ super.attach(control);
154
+ control.id = control.id || `m3e-tab-panel-${M3eTabElement_1.__nextId++}`;
155
+ addAriaReferencedId(this, "aria-controls", control.id);
156
+ }
157
+ /** @inheritdoc */
158
+ detach() {
159
+ if (this.control && this.control.id) {
160
+ removeAriaReferencedId(this, "aria-controls", this.control.id);
161
+ }
162
+ super.detach();
163
+ }
164
+ /** @inheritdoc */
165
+ connectedCallback() {
166
+ super.connectedCallback();
167
+ this.addEventListener("click", __classPrivateFieldGet(this, _M3eTabElement_clickHandler, "f"));
168
+ }
169
+ /** @inheritdoc */
170
+ disconnectedCallback() {
171
+ super.disconnectedCallback();
172
+ this.removeEventListener("click", __classPrivateFieldGet(this, _M3eTabElement_clickHandler, "f"));
173
+ }
174
+ /** @inheritdoc */
175
+ firstUpdated(_changedProperties) {
176
+ super.firstUpdated(_changedProperties);
177
+ [this._focusRing, this._stateLayer, this._ripple].forEach((x) => x?.attach(this));
178
+ }
179
+ /** @inheritdoc */
180
+ update(changedProperties) {
181
+ super.update(changedProperties);
182
+ if (changedProperties.has("selected")) {
183
+ this.closest("m3e-tabs")?.[selectionManager].notifySelectionChange(this);
184
+ }
185
+ }
186
+ /** @inheritdoc */
187
+ render() {
188
+ return html `<div class="base">
189
+ <m3e-state-layer class="state-layer" ?disabled="${this.disabled}"></m3e-state-layer>
190
+ <m3e-focus-ring class="focus-ring" inward ?disabled="${this.disabled}"></m3e-focus-ring>
191
+ <m3e-ripple class="ripple" ?disabled="${this.disabled}"></m3e-ripple>
192
+ <div class="touch" aria-hidden="true"></div>
193
+ <div class="wrapper">
194
+ <slot name="icon" aria-hidden="true"></slot><span class="label"><slot></slot></span>
195
+ </div>
196
+ </div>`;
197
+ }
198
+ };
199
+ _M3eTabElement_clickHandler = new WeakMap();
200
+ _M3eTabElement_instances = new WeakSet();
201
+ _M3eTabElement_handleClick = function _M3eTabElement_handleClick(e) {
202
+ if (this.disabled) {
203
+ e.preventDefault();
204
+ e.stopImmediatePropagation();
205
+ }
206
+ if (e.defaultPrevented || this.selected)
207
+ return;
208
+ this.selected = true;
209
+ if (this.dispatchEvent(new Event("input", { bubbles: true, composed: true, cancelable: true }))) {
210
+ this.closest("m3e-tabs")?.[selectionManager].notifySelectionChange(this);
211
+ this.dispatchEvent(new Event("change", { bubbles: true }));
212
+ }
213
+ else {
214
+ this.selected = false;
215
+ }
216
+ };
217
+ /** The styles of the element. */
218
+ M3eTabElement.styles = css `
219
+ :host {
220
+ display: inline-block;
221
+ outline: none;
222
+ user-select: none;
223
+ height: calc(var(--_tab-height) + ${DesignToken.density.calc(-3)});
224
+ font-size: var(--m3e-tab-font-size, ${DesignToken.typescale.standard.title.small.fontSize});
225
+ font-weight: var(--m3e-tab-font-weight, ${DesignToken.typescale.standard.title.small.fontWeight});
226
+ line-height: var(--m3e-tab-line-height, ${DesignToken.typescale.standard.title.small.lineHeight});
227
+ letter-spacing: var(--m3e-tab-tracking, ${DesignToken.typescale.standard.title.small.tracking});
228
+ flex-grow: var(--_tab-grow);
229
+ }
230
+ :host(:not(:disabled)) {
231
+ cursor: pointer;
232
+ }
233
+ .base {
234
+ box-sizing: border-box;
235
+ vertical-align: middle;
236
+ display: inline-flex;
237
+ align-items: center;
238
+ justify-content: center;
239
+ position: relative;
240
+ width: 100%;
241
+ height: 100%;
242
+ padding-inline-start: var(--m3e-tab-padding-start, 1.5rem);
243
+ padding-inline-end: var(--m3e-tab-padding-end, 1.5rem);
244
+ }
245
+ .touch {
246
+ position: absolute;
247
+ height: 3rem;
248
+ left: 0;
249
+ right: 0;
250
+ }
251
+ .focus-ring {
252
+ border-radius: var(--m3e-tab-focus-ring-shape, ${DesignToken.shape.corner.large});
253
+ }
254
+ :host([selected]:focus-within) .focus-ring {
255
+ top: var(--_tab-focus-ring-top-offset, 0);
256
+ bottom: var(--_tab-focus-ring-bottom-offset, 0);
257
+ }
258
+ :host([selected]:not(:disabled)) .base {
259
+ color: var(--m3e-tab-selected-color, ${DesignToken.color.primary});
260
+ --m3e-state-layer-hover-color: var(--m3e-tab-selected-container-hover-color, ${DesignToken.color.primary});
261
+ --m3e-state-layer-focus-color: var(--m3e-tab-selected-container-focus-color, ${DesignToken.color.primary});
262
+ --m3e-ripple-color: var(--m3e-tab-selected-ripple-color, ${DesignToken.color.primary});
263
+ }
264
+ :host(:not([selected]):not(:disabled)) .base {
265
+ color: var(--m3e-tab-unselected-color, ${DesignToken.color.onSurface});
266
+ --m3e-state-layer-hover-color: var(--m3e-tab-unselected-container-hover-color, ${DesignToken.color.onSurface});
267
+ --m3e-state-layer-focus-color: var(--m3e-tab-unselected-container-focus-color, ${DesignToken.color.onSurface});
268
+ --m3e-ripple-color: var(--m3e-tab-unselected-ripple-color, ${DesignToken.color.onSurface});
269
+ }
270
+ :host(:disabled) .base {
271
+ color: color-mix(
272
+ in srgb,
273
+ var(--m3e-tab-disabled-color, ${DesignToken.color.onSurface}) var(--m3e-tab-disabled-opacity, 38%),
274
+ transparent
275
+ );
276
+ }
277
+ .wrapper {
278
+ display: inline-flex;
279
+ align-items: center;
280
+ white-space: nowrap;
281
+ flex-direction: var(--_tab-direction);
282
+ justify-content: center;
283
+ column-gap: var(--m3e-tab-spacing, 0.5rem);
284
+ }
285
+ ::slotted([slot="icon"]) {
286
+ width: 1em;
287
+ font-size: var(--m3e-tab-icon-size, 1.5rem) !important;
288
+ }
289
+ @media (forced-colors: active) {
290
+ :host([selected]:not(:disabled)) .base {
291
+ color: ButtonText;
292
+ }
293
+ :host(:not([selected]):not(:disabled)) .base {
294
+ color: ButtonText;
295
+ }
296
+ :host(:disabled) .base {
297
+ color: GrayText;
298
+ }
299
+ }
300
+ `;
301
+ /** @private */ M3eTabElement.__nextId = 0;
302
+ __decorate([
303
+ e(".focus-ring")
304
+ ], M3eTabElement.prototype, "_focusRing", void 0);
305
+ __decorate([
306
+ e(".state-layer")
307
+ ], M3eTabElement.prototype, "_stateLayer", void 0);
308
+ __decorate([
309
+ e(".ripple")
310
+ ], M3eTabElement.prototype, "_ripple", void 0);
311
+ __decorate([
312
+ e(".label")
313
+ ], M3eTabElement.prototype, "label", void 0);
314
+ M3eTabElement = M3eTabElement_1 = __decorate([
315
+ t$1("m3e-tab")
316
+ ], M3eTabElement);
317
+
318
+ /**
319
+ * @summary
320
+ * A panel presented for a tab.
321
+ *
322
+ * @description
323
+ * The `m3e-tab-panel` component represents the content region associated with a selected tab.
324
+ * It is conditionally rendered based on tab selection and provides a structured surface for
325
+ * displaying contextual information, media, or interactive elements. Panels are linked to their
326
+ * corresponding tabs via the `for` attribute on `m3e-tab`, enabling declarative control and
327
+ * accessible navigation consistent with Material 3 guidance.
328
+ *
329
+ * @example
330
+ * The following example illustrates using the `m3e-tabs`, `m3e-tab`, and `m3e-tab-panel` components to present
331
+ * secondary tabs.
332
+ * ```html
333
+ * <m3e-tabs>
334
+ * <m3e-tab selected for="videos"><m3e-icon slot="icon" name="videocam"></m3e-icon>Video</m3e-tab>
335
+ * <m3e-tab for="photos"><m3e-icon slot="icon" name="photo"></m3e-icon>Photos</m3e-tab>
336
+ * <m3e-tab for="audio"><m3e-icon slot="icon" name="music_note"></m3e-icon>Audio</m3e-tab>
337
+ * <m3e-tab-panel id="videos">Videos</m3e-tab-panel>
338
+ * <m3e-tab-panel id="photos">Photos</m3e-tab-panel>
339
+ * <m3e-tab-panel id="audio">Audio</m3e-tab-panel>
340
+ * </m3e-tabs>
341
+ * ```
342
+ *
343
+ * @tag m3e-tab-panel
344
+ *
345
+ * @slot - Renders the content of the panel.
346
+ */
347
+ let M3eTabPanelElement = class M3eTabPanelElement extends Role(LitElement, "tabpanel") {
348
+ /** @inheritdoc */
349
+ connectedCallback() {
350
+ super.connectedCallback();
351
+ this.slot = "panel";
352
+ }
353
+ /** @inheritdoc */
354
+ render() {
355
+ return html `<slot></slot>`;
356
+ }
357
+ };
358
+ /** The styles of the element. */
359
+ M3eTabPanelElement.styles = css `
360
+ :host {
361
+ display: block;
362
+ overflow-y: auto;
363
+ scrollbar-width: ${DesignToken.scrollbar.width};
364
+ scrollbar-color: ${DesignToken.scrollbar.color};
365
+ }
366
+ `;
367
+ M3eTabPanelElement = __decorate([
368
+ t$1("m3e-tab-panel")
369
+ ], M3eTabPanelElement);
370
+
371
+ var _M3eTabsElement_instances, _M3eTabsElement_renderHeader, _M3eTabsElement_handleSlotChange, _M3eTabsElement_handleKeyDown, _M3eTabsElement_handleChange, _M3eTabsElement_handleSelectedChange, _M3eTabsElement_updateInkBar, _a;
372
+ const MIN_PRIMARY_TAB_WIDTH = 24;
373
+ /**
374
+ * @summary
375
+ * Organizes content into separate views where only one view can be visible at a time.
376
+ *
377
+ * @description
378
+ * The `m3e-tabs` component provides a structured navigation surface for organizing content into distinct views,
379
+ * where only one view is visible at a time. It supports scrollable tab headers with optional pagination,
380
+ * accessible labeling for navigation controls, and configurable header positioning to suit various layout
381
+ * contexts. Two visual variants are available: `primary`, which emphasizes active indicators and shape styling
382
+ * for prominent navigation, and `secondary`, which offers a more subtle presentation with reduced indicator
383
+ * thickness. Stretch behavior allows tabs to expand and align rhythmically within their container, consistent
384
+ * with Material 3 guidance.
385
+ *
386
+ * @example
387
+ * The following example illustrates using the `m3e-tabs`, `m3e-tab`, and `m3e-tab-panel` components to present
388
+ * secondary tabs.
389
+ * ```html
390
+ * <m3e-tabs>
391
+ * <m3e-tab selected for="videos"><m3e-icon slot="icon" name="videocam"></m3e-icon>Video</m3e-tab>
392
+ * <m3e-tab for="photos"><m3e-icon slot="icon" name="photo"></m3e-icon>Photos</m3e-tab>
393
+ * <m3e-tab for="audio"><m3e-icon slot="icon" name="music_note"></m3e-icon>Audio</m3e-tab>
394
+ * <m3e-tab-panel id="videos">Videos</m3e-tab-panel>
395
+ * <m3e-tab-panel id="photos">Photos</m3e-tab-panel>
396
+ * <m3e-tab-panel id="audio">Audio</m3e-tab-panel>
397
+ * </m3e-tabs>
398
+ * ```
399
+ *
400
+ * @tag m3e-tabs
401
+ *
402
+ * @slot - Renders the tabs.
403
+ * @slot panel - Renders the panels of the tabs.
404
+ * @slot next-icon - Renders the icon to present for the next button used to paginate.
405
+ * @slot prev-icon - Renders the icon to present for the previous button used to paginate.
406
+ *
407
+ * @attr disable-pagination - Whether scroll buttons are disabled.
408
+ * @attr header-position - The position of the tab headers.
409
+ * @attr next-page-label - The accessible label given to the button used to move to the previous page.
410
+ * @attr previous-page-label - The accessible label given to the button used to move to the next page.
411
+ * @attr stretch - Whether tabs are stretched to fill the header.
412
+ * @attr variant - The appearance variant of the tabs.
413
+ *
414
+ * @fires change - Emitted when the selected tab changes.
415
+ *
416
+ * @cssprop --m3e-tabs-paginator-button-icon-size - Overrides the icon size for paginator buttons.
417
+ * @cssprop --m3e-tabs-active-indicator-color - Color of the active tab indicator.
418
+ * @cssprop --m3e-tabs-primary-before-active-indicator-shape - Border radius for active indicator when header is before and variant is primary.
419
+ * @cssprop --m3e-tabs-primary-after-active-indicator-shape - Border radius for active indicator when header is after and variant is primary.
420
+ * @cssprop --m3e-tabs-primary-active-indicator-inset - Inset for primary variant's active indicator.
421
+ * @cssprop --m3e-tabs-primary-active-indicator-thickness - Thickness for primary variant's active indicator.
422
+ * @cssprop --m3e-tabs-secondary-active-indicator-thickness - Thickness for secondary variant's active indicator.
423
+ */
424
+ let M3eTabsElement = class M3eTabsElement extends AttachInternals(LitElement) {
425
+ constructor() {
426
+ super();
427
+ _M3eTabsElement_instances.add(this);
428
+ /** @private */ this._selectedIndex = null;
429
+ /** @internal */
430
+ this[_a] = new SelectionManager()
431
+ .onSelectedItemsChange(() => __classPrivateFieldGet(this, _M3eTabsElement_instances, "m", _M3eTabsElement_handleSelectedChange).call(this))
432
+ .withHomeAndEnd()
433
+ .withWrap();
434
+ /**
435
+ * Whether scroll buttons are disabled.
436
+ * @default false
437
+ */
438
+ this.disablePagination = false;
439
+ /**
440
+ * The position of the tab headers.
441
+ * @default "before"
442
+ */
443
+ this.headerPosition = "before";
444
+ /**
445
+ * The appearance variant of the tabs.
446
+ * @default "secondary"
447
+ */
448
+ this.variant = "secondary";
449
+ /**
450
+ * Whether tabs are stretched to fill the header.
451
+ * @default false
452
+ */
453
+ this.stretch = false;
454
+ /**
455
+ * The accessible label given to the button used to move to the previous page.
456
+ * @default "Previous page"
457
+ */
458
+ this.previousPageLabel = "Previous page";
459
+ /**
460
+ * The accessible label given to the button used to move to the next page.
461
+ * @default "Next page"
462
+ */
463
+ this.nextPageLabel = "Next page";
464
+ new ResizeController(this, {
465
+ skipInitial: true,
466
+ callback: () => {
467
+ this.classList.toggle("-no-animate", true);
468
+ __classPrivateFieldGet(this, _M3eTabsElement_instances, "m", _M3eTabsElement_updateInkBar).call(this);
469
+ },
470
+ });
471
+ }
472
+ /** The tabs. */
473
+ get tabs() {
474
+ return this[selectionManager]?.items ?? [];
475
+ }
476
+ /** The selected tab. */
477
+ get selectedTab() {
478
+ return this._selectedIndex !== null ? this.tabs[this._selectedIndex] ?? null : null;
479
+ }
480
+ /** The zero-based index of the selected tab. */
481
+ get selectedIndex() {
482
+ return this._selectedIndex ?? -1;
483
+ }
484
+ set selectedIndex(value) {
485
+ if (value >= 0 && value < this.tabs.length) {
486
+ this.tabs[value].selected = true;
487
+ }
488
+ else {
489
+ const selectedTab = this.selectedTab;
490
+ if (selectedTab) {
491
+ selectedTab.selected = false;
492
+ }
493
+ }
494
+ }
495
+ /** @inheritdoc */
496
+ connectedCallback() {
497
+ super.connectedCallback();
498
+ this.classList.toggle("-no-animate", true);
499
+ }
500
+ /** @inheritdoc */
501
+ updated(_changedProperties) {
502
+ super.updated(_changedProperties);
503
+ if ((_changedProperties.has("variant") || _changedProperties.has("stretch")) && this._selectedIndex !== null) {
504
+ __classPrivateFieldGet(this, _M3eTabsElement_instances, "m", _M3eTabsElement_updateInkBar).call(this);
505
+ }
506
+ }
507
+ /** @inheritdoc */
508
+ render() {
509
+ let panelIndex = null;
510
+ if (this.selectedTab?.control) {
511
+ panelIndex = [...this.querySelectorAll("[slot='panel']")].indexOf(this.selectedTab.control);
512
+ if (panelIndex === -1) {
513
+ panelIndex = null;
514
+ }
515
+ }
516
+ return html ` ${this.headerPosition === "before" ? __classPrivateFieldGet(this, _M3eTabsElement_instances, "m", _M3eTabsElement_renderHeader).call(this) : nothing}
517
+ <m3e-slide class="tabs" .selectedIndex="${panelIndex}">
518
+ <slot name="panel"></slot>
519
+ </m3e-slide>
520
+ ${this.headerPosition === "after" ? __classPrivateFieldGet(this, _M3eTabsElement_instances, "m", _M3eTabsElement_renderHeader).call(this) : nothing}`;
521
+ }
522
+ };
523
+ _M3eTabsElement_instances = new WeakSet();
524
+ _a = selectionManager;
525
+ _M3eTabsElement_renderHeader = function _M3eTabsElement_renderHeader() {
526
+ return html `<m3e-slide-group
527
+ class="tablist"
528
+ threshold="8"
529
+ previous-page-label="${this.previousPageLabel}"
530
+ next-page-label="${this.nextPageLabel}"
531
+ ?disabled="${this.disablePagination}"
532
+ >
533
+ <slot name="prev-icon" slot="prev-icon">
534
+ <svg class="prev icon" viewBox="0 -960 960 960" fill="currentColor">
535
+ <path d="M640-80 240-480l400-400 71 71-329 329 329 329-71 71Z" />
536
+ </svg>
537
+ </slot>
538
+ <slot name="next-icon" slot="next-icon">
539
+ <svg class="next icon" viewBox="0 -960 960 960" fill="currentColor">
540
+ <path d="m321-80-71-71 329-329-329-329 71-71 400 400L321-80Z" />
541
+ </svg>
542
+ </slot>
543
+ <div class="header" role="tablist">
544
+ <div class="tabs">
545
+ <slot
546
+ @slotchange="${__classPrivateFieldGet(this, _M3eTabsElement_instances, "m", _M3eTabsElement_handleSlotChange)}"
547
+ @keydown="${__classPrivateFieldGet(this, _M3eTabsElement_instances, "m", _M3eTabsElement_handleKeyDown)}"
548
+ @change="${__classPrivateFieldGet(this, _M3eTabsElement_instances, "m", _M3eTabsElement_handleChange)}"
549
+ ></slot>
550
+ </div>
551
+ <div class="ink-bar" aria-hidden="true">
552
+ <div class="active-indicator"></div>
553
+ </div>
554
+ </div>
555
+ </m3e-slide-group>`;
556
+ };
557
+ _M3eTabsElement_handleSlotChange = function _M3eTabsElement_handleSlotChange() {
558
+ this[selectionManager].setItems([...this.querySelectorAll("m3e-tab")]);
559
+ };
560
+ _M3eTabsElement_handleKeyDown = function _M3eTabsElement_handleKeyDown(e) {
561
+ this[selectionManager].onKeyDown(e);
562
+ };
563
+ _M3eTabsElement_handleChange = function _M3eTabsElement_handleChange(e) {
564
+ e.stopPropagation();
565
+ this.dispatchEvent(new Event("change", { bubbles: true }));
566
+ };
567
+ _M3eTabsElement_handleSelectedChange = function _M3eTabsElement_handleSelectedChange() {
568
+ const selected = this[selectionManager].selectedItems[0];
569
+ let selectedIndex = selected ? this.tabs.indexOf(selected) : null;
570
+ if (selectedIndex === -1) {
571
+ selectedIndex = null;
572
+ }
573
+ this._selectedIndex = selectedIndex;
574
+ __classPrivateFieldGet(this, _M3eTabsElement_instances, "m", _M3eTabsElement_updateInkBar).call(this);
575
+ };
576
+ _M3eTabsElement_updateInkBar = function _M3eTabsElement_updateInkBar() {
577
+ if (!this._tablist)
578
+ return;
579
+ const selected = this[selectionManager].selectedItems[0];
580
+ let left = 0;
581
+ let width = 0;
582
+ if (selected && this._selectedIndex !== null) {
583
+ for (let i = 0; i < this._selectedIndex; i++) {
584
+ left += this.tabs[i].clientWidth;
585
+ }
586
+ width = selected.clientWidth;
587
+ if (this.variant === "primary" && selected.label) {
588
+ left += selected.label.offsetLeft;
589
+ width = selected.label.clientWidth;
590
+ if (width < MIN_PRIMARY_TAB_WIDTH) {
591
+ left -= (MIN_PRIMARY_TAB_WIDTH - width) / 2;
592
+ width = MIN_PRIMARY_TAB_WIDTH;
593
+ }
594
+ }
595
+ }
596
+ this._tablist.style.setProperty("--_tabs-active-tab-position", `${left}px`);
597
+ this._tablist.style.setProperty("--_tabs-active-tab-size", `${width}px`);
598
+ if (width > 0 && this.classList.contains("-no-animate")) {
599
+ setTimeout(() => this.classList.toggle("-no-animate", false));
600
+ }
601
+ };
602
+ /** The styles of the element. */
603
+ M3eTabsElement.styles = css `
604
+ :host {
605
+ display: flex;
606
+ flex-direction: column;
607
+ position: relative;
608
+ }
609
+ .tablist {
610
+ position: relative;
611
+ box-sizing: border-box;
612
+ flex: none;
613
+ }
614
+ ::slotted(prev-icon),
615
+ ::slotted(next-icon),
616
+ .icon {
617
+ width: 1em;
618
+ font-size: var(--m3e-tabs-paginator-button-icon-size, var(--m3e-icon-button-icon-size, 1.5rem)) !important;
619
+ }
620
+ .header {
621
+ display: flex;
622
+ flex-direction: column;
623
+ }
624
+ .tabs {
625
+ display: flex;
626
+ flex-wrap: nowrap;
627
+ align-items: center;
628
+ }
629
+ .ink-bar {
630
+ box-sizing: border-box;
631
+ height: var(--_tabs-active-indicator-thickness);
632
+ }
633
+ .active-indicator {
634
+ position: relative;
635
+ height: var(--_tabs-active-indicator-thickness);
636
+ left: calc(var(--_tabs-active-tab-position) + var(--_tabs-activate-indicator-inset, 0px));
637
+ width: calc(var(--_tabs-active-tab-size) - calc(var(--_tabs-activate-indicator-inset, 0px) * 2));
638
+ background-color: var(--m3e-tabs-active-indicator-color, ${DesignToken.color.primary});
639
+ transition: ${unsafeCSS(`left var(--m3e-slide-animation-duration, ${DesignToken.motion.duration.long2}) ${DesignToken.motion.easing.standard},
640
+ width var(--m3e-slide-animation-duration, ${DesignToken.motion.duration.long2}) ${DesignToken.motion.easing.standard}`)};
641
+ }
642
+ :host([header-position="after"]) .header {
643
+ flex-direction: column-reverse;
644
+ }
645
+ :host([header-position="before"]) .ink-bar {
646
+ margin-top: calc(0px - var(--_tabs-active-indicator-thickness));
647
+ }
648
+ :host([header-position="before"]) .tablist {
649
+ --m3e-slide-group-divider-bottom: var(--m3e-divider-thickness, 1px) solid
650
+ var(--m3e-divider-color, ${DesignToken.color.outlineVariant});
651
+ }
652
+ :host([header-position="after"]) .ink-bar {
653
+ margin-bottom: calc(0px - var(--_tabs-active-indicator-thickness));
654
+ }
655
+ :host([header-position="after"]) .tablist {
656
+ --m3e-slide-group-divider-top: var(--m3e-divider-thickness, 1px) solid
657
+ var(--m3e-divider-color, ${DesignToken.color.outlineVariant});
658
+ }
659
+ :host([header-position="before"][variant="primary"]) .active-indicator {
660
+ border-radius: var(--m3e-tabs-primary-before-active-indicator-shape, ${DesignToken.shape.corner.extraSmallTop});
661
+ }
662
+ :host([header-position="after"][variant="primary"]) .active-indicator {
663
+ border-radius: var(--m3e-tabs-primary-after-active-indicator-shape, ${DesignToken.shape.corner.extraSmallBottom});
664
+ }
665
+ .tabs {
666
+ flex: 1 1 auto;
667
+ }
668
+ :host([variant="primary"]) .tablist {
669
+ --_tabs-activate-indicator-inset: var(--m3e-tabs-primary-active-indicator-inset, 0.125rem);
670
+ --_tabs-active-indicator-thickness: var(--m3e-tabs-primary-active-indicator-thickness, 0.1875rem);
671
+ --_tab-height: 4rem;
672
+ }
673
+ :host([header-position="before"]) .tablist {
674
+ --_tab-focus-ring-bottom-offset: calc(var(--_tabs-active-indicator-thickness) + 1px);
675
+ }
676
+ :host([header-position="after"]) .tablist {
677
+ --_tab-focus-ring-top-offset: calc(var(--_tabs-active-indicator-thickness) + 2px);
678
+ }
679
+ :host([header-position="before"][variant="primary"]) .tablist {
680
+ --_tab-direction: column;
681
+ }
682
+ :host([header-position="after"][variant="primary"]) .tablist {
683
+ --_tab-direction: column-reverse;
684
+ }
685
+ :host([variant="secondary"]) .tablist {
686
+ --_tabs-active-indicator-thickness: var(--m3e-tabs-secondary-active-indicator-thickness, 0.125rem);
687
+ --_tab-height: 3rem;
688
+ }
689
+ :host([stretch]) .header {
690
+ width: 100%;
691
+ --_tab-grow: 1;
692
+ }
693
+ :host(.-no-animate) .active-indicator {
694
+ transition: none;
695
+ }
696
+ @media (prefers-reduced-motion) {
697
+ .active-indicator {
698
+ transition: none;
699
+ }
700
+ }
701
+ @media (forced-colors: active) {
702
+ .active-indicator {
703
+ background-color: ButtonText;
704
+ --m3e-divider-color: GrayText;
705
+ }
706
+ }
707
+ `;
708
+ __decorate([
709
+ e(".tablist")
710
+ ], M3eTabsElement.prototype, "_tablist", void 0);
711
+ __decorate([
712
+ r()
713
+ ], M3eTabsElement.prototype, "_selectedIndex", void 0);
714
+ __decorate([
715
+ n({ attribute: "disable-pagination", type: Boolean })
716
+ ], M3eTabsElement.prototype, "disablePagination", void 0);
717
+ __decorate([
718
+ n({ attribute: "header-position", reflect: true })
719
+ ], M3eTabsElement.prototype, "headerPosition", void 0);
720
+ __decorate([
721
+ n({ reflect: true })
722
+ ], M3eTabsElement.prototype, "variant", void 0);
723
+ __decorate([
724
+ n({ type: Boolean, reflect: true })
725
+ ], M3eTabsElement.prototype, "stretch", void 0);
726
+ __decorate([
727
+ n({ attribute: "previous-page-label" })
728
+ ], M3eTabsElement.prototype, "previousPageLabel", void 0);
729
+ __decorate([
730
+ n({ attribute: "next-page-label" })
731
+ ], M3eTabsElement.prototype, "nextPageLabel", void 0);
732
+ M3eTabsElement = __decorate([
733
+ t$1("m3e-tabs")
734
+ ], M3eTabsElement);
735
+
736
+ export { M3eTabElement, M3eTabPanelElement, M3eTabsElement };
737
+ //# sourceMappingURL=index.js.map