@m3e/toc 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,597 @@
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, unsafeCSS, css, nothing } from 'lit';
7
+ import { Selected, Disabled, AttachInternals, Role, DesignToken, getTextContent, guid, HtmlFor, IntersectionController, ScrollController, MutationController, debounce, hasAssignedNodes } from '@m3e/core';
8
+ import { 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
+ function __classPrivateFieldSet(receiver, state, value, kind, f) {
41
+ if (kind === "m") throw new TypeError("Private method is not writable");
42
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
43
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
44
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
45
+ }
46
+
47
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
48
+ var e = new Error(message);
49
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
50
+ };
51
+
52
+ /**
53
+ * @license
54
+ * Copyright 2017 Google LLC
55
+ * SPDX-License-Identifier: BSD-3-Clause
56
+ */
57
+ const t$1=t=>(e,o)=>{ void 0!==o?o.addInitializer((()=>{customElements.define(t,e);})):customElements.define(t,e);};
58
+
59
+ /**
60
+ * @license
61
+ * Copyright 2019 Google LLC
62
+ * SPDX-License-Identifier: BSD-3-Clause
63
+ */
64
+ 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;
65
+
66
+ /**
67
+ * @license
68
+ * Copyright 2017 Google LLC
69
+ * SPDX-License-Identifier: BSD-3-Clause
70
+ */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");
71
+
72
+ /**
73
+ * @license
74
+ * Copyright 2017 Google LLC
75
+ * SPDX-License-Identifier: BSD-3-Clause
76
+ */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)}
77
+
78
+ /**
79
+ * @license
80
+ * Copyright 2017 Google LLC
81
+ * SPDX-License-Identifier: BSD-3-Clause
82
+ */function r(r){return n({...r,state:true,attribute:false})}
83
+
84
+ /**
85
+ * @license
86
+ * Copyright 2017 Google LLC
87
+ * SPDX-License-Identifier: BSD-3-Clause
88
+ */
89
+ const e$1=(e,t,c)=>(c.configurable=true,c.enumerable=true,Reflect.decorate&&"object"!=typeof t&&Object.defineProperty(e,t,c),c);
90
+
91
+ /**
92
+ * @license
93
+ * Copyright 2017 Google LLC
94
+ * SPDX-License-Identifier: BSD-3-Clause
95
+ */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)}})}}
96
+
97
+ /**
98
+ * An item in a table of contents.
99
+ * @tag m3e-toc-item
100
+ *
101
+ * @slot - Renders the label of the item.
102
+ *
103
+ * @attr disabled - A value indicating whether the element is disabled.
104
+ *
105
+ * @cssprop --m3e-toc-item-shape - Border radius of the TOC item.
106
+ * @cssprop --m3e-toc-item-padding-block - Block padding for the TOC item.
107
+ * @cssprop --m3e-toc-item-padding - Inline padding for the TOC item.
108
+ * @cssprop --m3e-toc-item-inset - Indentation per level for the TOC item.
109
+ * @cssprop --m3e-toc-active-indicator-animation-duration - Animation duration for the active indicator.
110
+ * @cssprop --m3e-toc-item-font-size - Font size for unselected items.
111
+ * @cssprop --m3e-toc-item-font-weight - Font weight for unselected items.
112
+ * @cssprop --m3e-toc-item-line-height - Line height for unselected items.
113
+ * @cssprop --m3e-toc-item-tracking - Letter spacing for unselected items.
114
+ * @cssprop --m3e-toc-item-color - Text color for unselected items.
115
+ * @cssprop --m3e-toc-item-selected-font-size - Font size for selected items.
116
+ * @cssprop --m3e-toc-item-selected-font-weight - Font weight for selected items.
117
+ * @cssprop --m3e-toc-item-selected-line-height - Line height for selected items.
118
+ * @cssprop --m3e-toc-item-selected-tracking - Letter spacing for selected items.
119
+ * @cssprop --m3e-toc-item-selected-color - Text color for selected items.
120
+ */
121
+ let M3eTocItemElement = class M3eTocItemElement extends Selected(Disabled(AttachInternals(Role(LitElement, "link")))) {
122
+ /** @internal */
123
+ update(changedProperties) {
124
+ super.update(changedProperties);
125
+ if (changedProperties.has("selected")) {
126
+ this.ariaSelected = null;
127
+ this.ariaCurrent = this.selected ? "true" : null;
128
+ }
129
+ if (changedProperties.has("node")) {
130
+ if (this.node) {
131
+ this._base?.style.setProperty("--_level", `${this.node.level - 1}`);
132
+ }
133
+ else {
134
+ this._base?.style.removeProperty("--_level");
135
+ }
136
+ }
137
+ }
138
+ /** @inheritdoc */
139
+ firstUpdated(_changedProperties) {
140
+ super.firstUpdated(_changedProperties);
141
+ this._stateLayer?.attach(this);
142
+ }
143
+ /** @inheritdoc */
144
+ render() {
145
+ return html `<m3e-state-layer class="state-layer"></m3e-state-layer>
146
+ <div class="base"><slot></slot></div>`;
147
+ }
148
+ };
149
+ /** The styles of the element. */
150
+ M3eTocItemElement.styles = css `
151
+ :host {
152
+ display: inline-block;
153
+ position: relative;
154
+ user-select: none;
155
+ border-radius: var(--m3e-toc-item-shape, ${DesignToken.shape.corner.largeIncreased});
156
+ padding-block: var(--m3e-toc-item-padding-block, 0.5rem);
157
+ }
158
+ :host(:not(:disabled)) {
159
+ cursor: pointer;
160
+ }
161
+ .base {
162
+ padding-inline-start: calc(
163
+ var(--m3e-toc-item-padding, 1rem) + calc(var(--m3e-toc-item-inset, 0.75rem) * var(--_level, 0))
164
+ );
165
+ padding-inline-end: var(--m3e-toc-item-padding, 1rem);
166
+ transition: ${unsafeCSS(`color var(--m3e-toc-active-indicator-animation-duration, ${DesignToken.motion.duration.long1}) ${DesignToken.motion.easing.standard}`)};
167
+ }
168
+ :host(:not([selected])) {
169
+ font-size: var(--m3e-toc-item-font-size, ${DesignToken.typescale.standard.body.large.fontSize});
170
+ font-weight: var(--m3e-toc-item-font-weight, ${DesignToken.typescale.standard.body.large.fontWeight});
171
+ line-height: var(--m3e-toc-item-line-height, ${DesignToken.typescale.standard.body.large.lineHeight});
172
+ letter-spacing: var(--m3e-toc-item-tracking, ${DesignToken.typescale.standard.body.large.tracking});
173
+ color: var(--m3e-toc-item-color, ${DesignToken.color.onSurfaceVariant});
174
+ }
175
+ :host([selected]) {
176
+ font-size: var(--m3e-toc-item-selected-font-size, ${DesignToken.typescale.emphasized.body.large.fontSize});
177
+ font-weight: var(--m3e-toc-item-selected-font-weight, ${DesignToken.typescale.emphasized.body.large.fontWeight});
178
+ line-height: var(--m3e-toc-item-selected-line-height, ${DesignToken.typescale.emphasized.body.large.lineHeight});
179
+ letter-spacing: var(--m3e-toc-item-selected-tracking, ${DesignToken.typescale.emphasized.body.large.tracking});
180
+ color: var(--m3e-toc-item-selected-color, ${DesignToken.color.onSecondaryContainer});
181
+ }
182
+ .base {
183
+ justify-content: unset;
184
+ }
185
+ .state-layer {
186
+ --m3e-state-layer-focus-opacity: 0;
187
+ }
188
+ @media (prefers-reduced-motion) {
189
+ .base {
190
+ transition: none;
191
+ }
192
+ }
193
+ `;
194
+ __decorate([
195
+ e(".base")
196
+ ], M3eTocItemElement.prototype, "_base", void 0);
197
+ __decorate([
198
+ e(".state-layer")
199
+ ], M3eTocItemElement.prototype, "_stateLayer", void 0);
200
+ __decorate([
201
+ r()
202
+ ], M3eTocItemElement.prototype, "node", void 0);
203
+ M3eTocItemElement = __decorate([
204
+ t$1("m3e-toc-item")
205
+ ], M3eTocItemElement);
206
+
207
+ var _a, _TocGenerator_getHeaderLevel;
208
+ /** Provides functionality used to generate a table of contents used for in-page navigation. */
209
+ class TocGenerator {
210
+ /**
211
+ * Generates nodes from which to construct a table of contents for in-page navigation.
212
+ * @param {HTMLElement} element The element for which to generate a table of contents.
213
+ * @param {number} [maxDepth=6] The maximum depth of the table of contents.
214
+ * @returns {Array<TocNode>} The top-level nodes of the table of contents.
215
+ */
216
+ static generate(element, maxDepth = 6) {
217
+ const maxLevel = 6;
218
+ let topLevel = maxLevel;
219
+ const nodes = new Array();
220
+ element
221
+ .querySelectorAll("h1:not([m3e-toc-ignore]),h2:not([m3e-toc-ignore]),h3:not([m3e-toc-ignore]),h4:not([m3e-toc-ignore]),h5:not([m3e-toc-ignore]),h6:not([m3e-toc-ignore]),m3e-heading[level]:not([m3e-toc-ignore])")
222
+ .forEach((element) => {
223
+ const level = __classPrivateFieldGet(_a, _a, "m", _TocGenerator_getHeaderLevel).call(_a, element);
224
+ topLevel = Math.min(level, topLevel);
225
+ nodes.push({
226
+ id: element.id || guid(),
227
+ element,
228
+ level,
229
+ label: getTextContent(element, true),
230
+ nodes: new Array(),
231
+ });
232
+ });
233
+ for (let level = topLevel + maxDepth - 1; level > topLevel; level--) {
234
+ for (let i = 0; i < nodes.length; i++) {
235
+ const node = nodes[i];
236
+ if (node.level === level) {
237
+ for (let j = i; j >= 0; j--) {
238
+ const prev = nodes[j];
239
+ if (prev.level < level) {
240
+ prev.nodes.push(node);
241
+ break;
242
+ }
243
+ }
244
+ }
245
+ }
246
+ }
247
+ nodes.forEach((x) => (x.level -= topLevel - 1));
248
+ return nodes.filter((x) => x.level === 1);
249
+ }
250
+ }
251
+ _a = TocGenerator, _TocGenerator_getHeaderLevel = function _TocGenerator_getHeaderLevel(element) {
252
+ return element.tagName.startsWith("H")
253
+ ? parseInt(element.tagName.substring(1))
254
+ : parseInt(element.getAttribute("level") ?? "0");
255
+ };
256
+
257
+ var _M3eTocElement_instances, _M3eTocElement_ignoreScroll, _M3eTocElement_selectionManager, _M3eTocElement_intersectionController, _M3eTocElement_scrollController, _M3eTocElement_mutationController, _M3eTocElement_renderNode, _M3eTocElement_handleOverlineSlotChange, _M3eTocElement_handleTitleSlotChange, _M3eTocElement_handleClick, _M3eTocElement_generateToc;
258
+ /**
259
+ * @summary
260
+ * A table of contents that provides in-page scroll navigation.
261
+ *
262
+ * @description
263
+ * The `m3e-toc` component generates a hierarchical table of contents for in-page navigation.
264
+ * It automatically detects headings or sections in a target element, builds a navigable list,
265
+ * and highlights the active section as the user scrolls. The component supports custom header
266
+ * slots, depth limiting, smooth scrolling, and extensive theming via CSS custom properties.
267
+ *
268
+ * To exclude a heading from the generated table of contents, add the `m3e-toc-ignore` attribute
269
+ * to that heading element.
270
+ *
271
+ * @example
272
+ * ```html
273
+ * <m3e-toc for="content" max-depth="3">
274
+ * <span slot="overline">Contents</span>
275
+ * <span slot="title">Documentation</span>
276
+ * </m3e-toc>
277
+ * <div id="content">
278
+ * <h2>Introduction</h2>
279
+ * <h2>Getting Started</h2>
280
+ * <h3>Installation</h3>
281
+ * <h3>Usage</h3>
282
+ * <h2>API Reference</h2>
283
+ * </div>
284
+ * ```
285
+ *
286
+ * @tag m3e-toc
287
+ *
288
+ * @slot - Renders content between the header and items.
289
+ * @slot overline - Renders the overline of the table of contents.
290
+ * @slot title - Renders the title of the table of contents.
291
+ *
292
+ * @attr for - The query selector used to specify the element related to this element.
293
+ * @attr max-depth - The maximum depth of the table of contents.
294
+ *
295
+ * @cssprop --m3e-toc-width - Width of the table of contents.
296
+ * @cssprop --m3e-toc-item-shape - Border radius of TOC items and active indicator.
297
+ * @cssprop --m3e-toc-active-indicator-color - Border color of the active indicator.
298
+ * @cssprop --m3e-toc-active-indicator-animation-duration - Animation duration for the active indicator.
299
+ * @cssprop --m3e-toc-item-padding - Inline padding for TOC items and header.
300
+ * @cssprop --m3e-toc-header-space - Block space below and between header elements.
301
+ * @cssprop --m3e-toc-overline-font-size - Font size for the overline slot.
302
+ * @cssprop --m3e-toc-overline-font-weight - Font weight for the overline slot.
303
+ * @cssprop --m3e-toc-overline-line-height - Line height for the overline slot.
304
+ * @cssprop --m3e-toc-overline-tracking - Letter spacing for the overline slot.
305
+ * @cssprop --m3e-toc-overline-color - Text color for the overline slot.
306
+ * @cssprop --m3e-toc-title-font-size - Font size for the title slot.
307
+ * @cssprop --m3e-toc-title-font-weight - Font weight for the title slot.
308
+ * @cssprop --m3e-toc-title-line-height - Line height for the title slot.
309
+ * @cssprop --m3e-toc-title-tracking - Letter spacing for the title slot.
310
+ * @cssprop --m3e-toc-title-color - Text color for the title slot.
311
+ */
312
+ let M3eTocElement = class M3eTocElement extends HtmlFor(AttachInternals(Role(LitElement, "navigation"))) {
313
+ constructor() {
314
+ super(...arguments);
315
+ _M3eTocElement_instances.add(this);
316
+ /** @private */ this._toc = [];
317
+ /** @private */ _M3eTocElement_ignoreScroll.set(this, false);
318
+ /** @private */ _M3eTocElement_selectionManager.set(this, new SelectionManager()
319
+ .withHomeAndEnd()
320
+ .withVerticalOrientation()
321
+ .disableRovingTabIndex()
322
+ .onSelectedItemsChange(() => {
323
+ if (this._activeIndicator) {
324
+ const item = __classPrivateFieldGet(this, _M3eTocElement_selectionManager, "f").selectedItems[0];
325
+ if (!item) {
326
+ this.classList.toggle("-no-animate", true);
327
+ this._activeIndicator.style.top = `0px`;
328
+ this._activeIndicator.style.height = `0px`;
329
+ this._activeIndicator.style.visibility = "hidden";
330
+ }
331
+ else {
332
+ this._activeIndicator.style.top = `${item.offsetTop}px`;
333
+ this._activeIndicator.style.height = `${item.clientHeight}px`;
334
+ this._activeIndicator.style.visibility = "";
335
+ if (this.classList.contains("-no-animate")) {
336
+ setTimeout(() => this.classList.toggle("-no-animate", false), 40);
337
+ }
338
+ }
339
+ }
340
+ }));
341
+ /** @private */
342
+ _M3eTocElement_intersectionController.set(this, new IntersectionController(this, {
343
+ target: null,
344
+ callback: (entries) => {
345
+ if (!this.control || __classPrivateFieldGet(this, _M3eTocElement_ignoreScroll, "f"))
346
+ return;
347
+ const targetOffset = this.control.scrollTop;
348
+ let closestElement = null;
349
+ let closestDistance = Number.POSITIVE_INFINITY;
350
+ entries
351
+ .filter((x) => x.isIntersecting)
352
+ .map((x) => x.target)
353
+ .forEach((item) => {
354
+ const offsetTop = item.offsetTop;
355
+ const distance = Math.abs(offsetTop - targetOffset);
356
+ if (distance < closestDistance) {
357
+ closestDistance = distance;
358
+ closestElement = item;
359
+ }
360
+ });
361
+ if (closestElement) {
362
+ const item = __classPrivateFieldGet(this, _M3eTocElement_selectionManager, "f").items.find((x) => x.node?.element === closestElement);
363
+ if (item) {
364
+ __classPrivateFieldGet(this, _M3eTocElement_selectionManager, "f").select(item);
365
+ }
366
+ }
367
+ },
368
+ }));
369
+ /** @private */
370
+ _M3eTocElement_scrollController.set(this, new ScrollController(this, {
371
+ target: null,
372
+ callback: () => (__classPrivateFieldSet(this, _M3eTocElement_ignoreScroll, false, "f")),
373
+ debounce: true,
374
+ }));
375
+ /** @private */
376
+ _M3eTocElement_mutationController.set(this, new MutationController(this, {
377
+ target: null,
378
+ config: {
379
+ childList: true,
380
+ subtree: true,
381
+ },
382
+ callback: () => this._updateToc(),
383
+ }));
384
+ /**
385
+ * The maximum depth of the table of contents.
386
+ * @default 2
387
+ */
388
+ this.maxDepth = 2;
389
+ }
390
+ /** @inheritdoc */
391
+ attach(control) {
392
+ super.attach(control);
393
+ __classPrivateFieldGet(this, _M3eTocElement_mutationController, "f").observe(control);
394
+ __classPrivateFieldGet(this, _M3eTocElement_scrollController, "f").observe(control);
395
+ __classPrivateFieldGet(this, _M3eTocElement_instances, "m", _M3eTocElement_generateToc).call(this);
396
+ }
397
+ /** @inheritdoc */
398
+ detach() {
399
+ if (this.control) {
400
+ __classPrivateFieldGet(this, _M3eTocElement_mutationController, "f").unobserve(this.control);
401
+ __classPrivateFieldGet(this, _M3eTocElement_scrollController, "f").unobserve(this.control);
402
+ }
403
+ super.detach();
404
+ __classPrivateFieldGet(this, _M3eTocElement_instances, "m", _M3eTocElement_generateToc).call(this);
405
+ }
406
+ /** @inheritdoc */
407
+ update(changedProperties) {
408
+ super.update(changedProperties);
409
+ if (changedProperties.has("maxDepth")) {
410
+ __classPrivateFieldGet(this, _M3eTocElement_instances, "m", _M3eTocElement_generateToc).call(this);
411
+ }
412
+ }
413
+ /** @inheritdoc */
414
+ updated(_changedProperties) {
415
+ super.updated(_changedProperties);
416
+ if (_changedProperties.has("_toc")) {
417
+ const { added, removed } = __classPrivateFieldGet(this, _M3eTocElement_selectionManager, "f").setItems([
418
+ ...(this.shadowRoot?.querySelectorAll("m3e-toc-item") ?? []),
419
+ ]);
420
+ if (!__classPrivateFieldGet(this, _M3eTocElement_selectionManager, "f").activeItem) {
421
+ this.classList.toggle("-no-animate", true);
422
+ __classPrivateFieldGet(this, _M3eTocElement_selectionManager, "f").updateActiveItem(added.find((x) => !x.disabled));
423
+ }
424
+ for (const item of added) {
425
+ if (item.node) {
426
+ __classPrivateFieldGet(this, _M3eTocElement_intersectionController, "f").observe(item.node.element);
427
+ }
428
+ }
429
+ for (const item of removed) {
430
+ if (item.node) {
431
+ __classPrivateFieldGet(this, _M3eTocElement_intersectionController, "f").unobserve(item.node.element);
432
+ }
433
+ }
434
+ }
435
+ }
436
+ /** @inheritdoc */
437
+ render() {
438
+ return html `<div class="header">
439
+ <div class="overline">
440
+ <slot name="overline" @slotchange="${__classPrivateFieldGet(this, _M3eTocElement_instances, "m", _M3eTocElement_handleOverlineSlotChange)}"></slot>
441
+ </div>
442
+ <div class="title">
443
+ <slot name="title" @slotchange="${__classPrivateFieldGet(this, _M3eTocElement_instances, "m", _M3eTocElement_handleTitleSlotChange)}"></slot>
444
+ </div>
445
+ </div>
446
+ <slot></slot>
447
+ <ul class="list">
448
+ ${this._toc.map((x) => __classPrivateFieldGet(this, _M3eTocElement_instances, "m", _M3eTocElement_renderNode).call(this, x))}
449
+ </ul>
450
+ <div class="active-indicator" aria-hidden="true"></div>`;
451
+ }
452
+ /** @private */
453
+ _updateToc() {
454
+ __classPrivateFieldGet(this, _M3eTocElement_instances, "m", _M3eTocElement_generateToc).call(this);
455
+ }
456
+ };
457
+ _M3eTocElement_ignoreScroll = new WeakMap();
458
+ _M3eTocElement_selectionManager = new WeakMap();
459
+ _M3eTocElement_intersectionController = new WeakMap();
460
+ _M3eTocElement_scrollController = new WeakMap();
461
+ _M3eTocElement_mutationController = new WeakMap();
462
+ _M3eTocElement_instances = new WeakSet();
463
+ _M3eTocElement_renderNode = function _M3eTocElement_renderNode(node) {
464
+ return html `<li>
465
+ <m3e-toc-item tabindex="-1" .node="${node}" @click="${__classPrivateFieldGet(this, _M3eTocElement_instances, "m", _M3eTocElement_handleClick)}">${node.label}</m3e-toc-item>
466
+ ${node.nodes.length == 0
467
+ ? nothing
468
+ : html `<ul>
469
+ ${node.nodes.map((x) => __classPrivateFieldGet(this, _M3eTocElement_instances, "m", _M3eTocElement_renderNode).call(this, x))}
470
+ </ul>`}
471
+ </li>`;
472
+ };
473
+ _M3eTocElement_handleOverlineSlotChange = function _M3eTocElement_handleOverlineSlotChange(e) {
474
+ this.classList.toggle("-with-overline", hasAssignedNodes(e.target));
475
+ };
476
+ _M3eTocElement_handleTitleSlotChange = function _M3eTocElement_handleTitleSlotChange(e) {
477
+ this.classList.toggle("-with-title", hasAssignedNodes(e.target));
478
+ };
479
+ _M3eTocElement_handleClick = function _M3eTocElement_handleClick(e) {
480
+ if (e.target instanceof M3eTocItemElement && !e.target.disabled && e.target.node?.element) {
481
+ __classPrivateFieldSet(this, _M3eTocElement_ignoreScroll, true, "f");
482
+ e.target.node.element.scrollIntoView({ block: "start", inline: "start", behavior: "smooth" });
483
+ __classPrivateFieldGet(this, _M3eTocElement_selectionManager, "f").updateActiveItem(e.target);
484
+ __classPrivateFieldGet(this, _M3eTocElement_selectionManager, "f").select(e.target);
485
+ }
486
+ };
487
+ _M3eTocElement_generateToc = function _M3eTocElement_generateToc() {
488
+ this._toc = this.control ? TocGenerator.generate(this.control, Math.max(1, Math.min(this.maxDepth, 6))) : [];
489
+ this.requestUpdate();
490
+ };
491
+ /** The styles of the element. */
492
+ M3eTocElement.styles = css `
493
+ :host {
494
+ display: inline-block;
495
+ position: relative;
496
+ overflow-y: auto;
497
+ scrollbar-width: ${DesignToken.scrollbar.thinWidth};
498
+ scrollbar-color: ${DesignToken.scrollbar.color};
499
+ width: var(--m3e-toc-width, 9.75rem);
500
+ }
501
+ ul {
502
+ list-style: none;
503
+ padding-inline-start: unset;
504
+ margin-block-start: unset;
505
+ margin-block-end: unset;
506
+ }
507
+ ul,
508
+ li {
509
+ display: flex;
510
+ flex-direction: column;
511
+ align-items: stretch;
512
+ }
513
+ m3e-toc-item {
514
+ flex: none;
515
+ }
516
+ .active-indicator {
517
+ position: absolute;
518
+ pointer-events: none;
519
+ box-sizing: border-box;
520
+ left: 0;
521
+ right: 0;
522
+
523
+ border-radius: var(--m3e-toc-item-shape, ${DesignToken.shape.corner.largeIncreased});
524
+ border: 1px solid var(--m3e-toc-active-indicator-color, ${DesignToken.color.outline});
525
+ transition: ${unsafeCSS(`visibility var(--m3e-toc-active-indicator-animation-duration, ${DesignToken.motion.duration.long1})
526
+ ${DesignToken.motion.easing.standard},
527
+ height var(--m3e-toc-active-indicator-animation-duration, ${DesignToken.motion.duration.long1})
528
+ ${DesignToken.motion.easing.standard},
529
+ top var(--m3e-toc-active-indicator-animation-duration, ${DesignToken.motion.duration.long1})
530
+ ${DesignToken.motion.easing.standard}`)};
531
+ }
532
+ .header {
533
+ display: flex;
534
+ flex-direction: column;
535
+ align-items: stretch;
536
+ padding-inline-start: var(--m3e-toc-item-padding, 1rem);
537
+ padding-block-end: var(--m3e-toc-header-space, 0.5rem);
538
+ row-gap: var(--m3e-toc-header-space, 0.5rem);
539
+ }
540
+ .overline {
541
+ white-space: nowrap;
542
+ overflow: hidden;
543
+ text-overflow: ellipsis;
544
+ }
545
+ .title {
546
+ display: -webkit-box;
547
+ -webkit-line-clamp: 2;
548
+ -webkit-box-orient: vertical;
549
+ overflow: hidden;
550
+ line-clamp: 2;
551
+ }
552
+ :host(:not(.-with-overline)) .overline,
553
+ :host(:not(.-with-title)) .title,
554
+ :host(:not(.-with-overline):not(.-with-title)) .header {
555
+ display: none;
556
+ }
557
+ ::slotted([slot="overline"]) {
558
+ font-size: var(--m3e-toc-overline-font-size, ${DesignToken.typescale.standard.label.small.fontSize});
559
+ font-weight: var(--m3e-toc-overline-font-weight, ${DesignToken.typescale.standard.label.small.fontWeight});
560
+ line-height: var(--m3e-toc-overline-line-height, ${DesignToken.typescale.standard.label.small.lineHeight});
561
+ letter-spacing: var(--m3e-toc-overline-tracking, ${DesignToken.typescale.standard.label.small.tracking});
562
+ color: var(--m3e-toc-overline-color, ${DesignToken.color.onSurfaceVariant});
563
+ }
564
+ ::slotted([slot="title"]) {
565
+ font-size: var(--m3e-toc-title-font-size, ${DesignToken.typescale.standard.headline.small.fontSize});
566
+ font-weight: var(--m3e-toc-title-font-weight, ${DesignToken.typescale.standard.headline.small.fontWeight});
567
+ line-height: var(--m3e-toc-title-line-height, ${DesignToken.typescale.standard.headline.small.lineHeight});
568
+ letter-spacing: var(--m3e-toc-title-tracking, ${DesignToken.typescale.standard.headline.small.tracking});
569
+ color: var(--m3e-toc-title-color, ${DesignToken.color.onSurface});
570
+ }
571
+ :host(.-no-animate) .active-indicator {
572
+ transition: none;
573
+ }
574
+ @media (prefers-reduced-motion) {
575
+ .active-indicator {
576
+ transition: none;
577
+ }
578
+ }
579
+ `;
580
+ __decorate([
581
+ r()
582
+ ], M3eTocElement.prototype, "_toc", void 0);
583
+ __decorate([
584
+ e(".active-indicator")
585
+ ], M3eTocElement.prototype, "_activeIndicator", void 0);
586
+ __decorate([
587
+ n({ attribute: "max-depth", type: Number })
588
+ ], M3eTocElement.prototype, "maxDepth", void 0);
589
+ __decorate([
590
+ debounce(40)
591
+ ], M3eTocElement.prototype, "_updateToc", null);
592
+ M3eTocElement = __decorate([
593
+ t$1("m3e-toc")
594
+ ], M3eTocElement);
595
+
596
+ export { M3eTocElement, M3eTocItemElement, TocGenerator };
597
+ //# sourceMappingURL=index.js.map