@wpmoo/ui 1.1.0-dev.1 → 1.1.0

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/ASSET_LICENSE.md CHANGED
@@ -14,6 +14,6 @@ product, site, template, library, training dataset, or marketing asset without
14
14
  prior written permission from WPMoo.
15
15
 
16
16
  Third-party assets remain under their own licenses. See the
17
- [version-pinned third-party notices](https://github.com/wpmoo-org/ui/blob/v1.0.0/THIRD_PARTY_NOTICES.md).
17
+ [version-pinned third-party notices](https://github.com/wpmoo-org/ui/blob/v1.1.0/THIRD_PARTY_NOTICES.md).
18
18
  Those notices are repository-only and are intentionally not shipped in the npm
19
19
  package.
package/README.md CHANGED
@@ -194,11 +194,9 @@ calm.
194
194
 
195
195
  ## Status And Support
196
196
 
197
- The current development snapshot is `1.1.0-dev.1`. It adds a shared Table of
198
- Contents component and is not published to npm; use the locally packed tarball
199
- when evaluating this snapshot.
200
-
201
- The stable public-contract release is `1.0.0`. Component certification remains
197
+ The stable public-contract release is `1.1.0`. It adds a shared Table of
198
+ Contents component, description-only Alerts and compatible navigation fixes
199
+ while preserving the 1.0.0 package contract. Component certification remains
202
200
  `preview`, with its documented manual and browser/device limits. Public exports, package boundaries, browser support, and release evidence live in [Support & Evidence](https://ui.wpmoo.org/support/).
203
201
  Complete release notes are on
204
202
  [GitHub Releases](https://github.com/wpmoo-org/ui/releases).
@@ -224,4 +222,4 @@ The published `LICENSE` also retains the full MIT notices for bundled
224
222
  Chart.js and `@kurkle/color`.
225
223
  Asset terms live in [ASSET_LICENSE.md](ASSET_LICENSE.md); dependency notices
226
224
  live in the
227
- [version-pinned third-party notices](https://github.com/wpmoo-org/ui/blob/v1.0.0/THIRD_PARTY_NOTICES.md).
225
+ [version-pinned third-party notices](https://github.com/wpmoo-org/ui/blob/v1.1.0/THIRD_PARTY_NOTICES.md).
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": "0.1",
3
3
  "status": "preview",
4
- "coreVersion": "1.1.0-dev.1",
4
+ "coreVersion": "1.1.0",
5
5
  "bootstrap": {
6
6
  "canonicalVersion": "5.3.3",
7
7
  "targetRange": ">=5.3.0 <5.4",
@@ -28,6 +28,7 @@
28
28
  "./context-menu.js",
29
29
  "./datatable.js",
30
30
  "./slider.js",
31
+ "./toc.js",
31
32
  "./table-of-contents.js",
32
33
  "./sheet.js",
33
34
  "./chart.js",
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Moo UI v1.1.0-dev.1 (https://wpmoo.org/)
2
+ * Moo UI v1.1.0 (https://wpmoo.org/)
3
3
  * Copyright 2026 WPMoo Authors
4
4
  * Licensed under MIT (https://github.com/wpmoo-org/ui/blob/main/LICENSE)
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Moo UI v1.1.0-dev.1 (https://wpmoo.org/)
2
+ * Moo UI v1.1.0 (https://wpmoo.org/)
3
3
  * Copyright 2026 WPMoo Authors
4
4
  * Licensed under MIT (https://github.com/wpmoo-org/ui/blob/main/LICENSE)
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Moo UI v1.1.0-dev.1 (https://wpmoo.org/)
2
+ * Moo UI v1.1.0 (https://wpmoo.org/)
3
3
  * Copyright 2026 WPMoo Authors
4
4
  * Licensed under MIT (https://github.com/wpmoo-org/ui/blob/main/LICENSE)
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Moo UI v1.1.0-dev.1 (https://wpmoo.org/)
2
+ * Moo UI v1.1.0 (https://wpmoo.org/)
3
3
  * Copyright 2026 WPMoo Authors
4
4
  * Licensed under MIT (https://github.com/wpmoo-org/ui/blob/main/LICENSE)
5
5
  *
package/dist/js/chart.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Moo UI chart.js v1.1.0-dev.1 (https://wpmoo.org/)
2
+ * Moo UI chart.js v1.1.0 (https://wpmoo.org/)
3
3
  * Copyright 2026 WPMoo Authors
4
4
  * Licensed under MIT (https://github.com/wpmoo-org/ui/blob/main/LICENSE)
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Moo UI chart.min.js v1.1.0-dev.1 (https://wpmoo.org/)
2
+ * Moo UI chart.min.js v1.1.0 (https://wpmoo.org/)
3
3
  * Copyright 2026 WPMoo Authors
4
4
  * Licensed under MIT (https://github.com/wpmoo-org/ui/blob/main/LICENSE)
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Moo UI combobox.js v1.1.0-dev.1 (https://wpmoo.org/)
2
+ * Moo UI combobox.js v1.1.0 (https://wpmoo.org/)
3
3
  * Copyright 2026 WPMoo Authors
4
4
  * Licensed under MIT (https://github.com/wpmoo-org/ui/blob/main/LICENSE)
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Moo UI context-menu.js v1.1.0-dev.1 (https://wpmoo.org/)
2
+ * Moo UI context-menu.js v1.1.0 (https://wpmoo.org/)
3
3
  * Copyright 2026 WPMoo Authors
4
4
  * Licensed under MIT (https://github.com/wpmoo-org/ui/blob/main/LICENSE)
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Moo UI datatable.js v1.1.0-dev.1 (https://wpmoo.org/)
2
+ * Moo UI datatable.js v1.1.0 (https://wpmoo.org/)
3
3
  * Copyright 2026 WPMoo Authors
4
4
  * Licensed under MIT (https://github.com/wpmoo-org/ui/blob/main/LICENSE)
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Moo UI datepicker.js v1.1.0-dev.1 (https://wpmoo.org/)
2
+ * Moo UI datepicker.js v1.1.0 (https://wpmoo.org/)
3
3
  * Copyright 2026 WPMoo Authors
4
4
  * Licensed under MIT (https://github.com/wpmoo-org/ui/blob/main/LICENSE)
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Moo UI datepicker.min.js v1.1.0-dev.1 (https://wpmoo.org/)
2
+ * Moo UI datepicker.min.js v1.1.0 (https://wpmoo.org/)
3
3
  * Copyright 2026 WPMoo Authors
4
4
  * Licensed under MIT (https://github.com/wpmoo-org/ui/blob/main/LICENSE)
5
5
  */
package/dist/js/moo-ui.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Moo UI moo-ui.js v1.1.0-dev.1 (https://wpmoo.org/)
2
+ * Moo UI moo-ui.js v1.1.0 (https://wpmoo.org/)
3
3
  * Copyright 2026 WPMoo Authors
4
4
  * Licensed under MIT (https://github.com/wpmoo-org/ui/blob/main/LICENSE)
5
5
  */
@@ -3876,7 +3876,7 @@ var MooSlider = class _MooSlider {
3876
3876
  }
3877
3877
  };
3878
3878
 
3879
- // src/js/components/table-of-contents.js
3879
+ // src/js/components/toc.js
3880
3880
  var instances6 = /* @__PURE__ */ new WeakMap();
3881
3881
  var ownerSelector = '.moo-ui[data-bs-theme="light"], .moo-ui[data-bs-theme="dark"]';
3882
3882
  function activationOffset(options, previous = 0) {
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Moo UI moo-ui.min.js v1.1.0-dev.1 (https://wpmoo.org/)
2
+ * Moo UI moo-ui.min.js v1.1.0 (https://wpmoo.org/)
3
3
  * Copyright 2026 WPMoo Authors
4
4
  * Licensed under MIT (https://github.com/wpmoo-org/ui/blob/main/LICENSE)
5
5
  */
package/dist/js/sheet.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Moo UI sheet.js v1.1.0-dev.1 (https://wpmoo.org/)
2
+ * Moo UI sheet.js v1.1.0 (https://wpmoo.org/)
3
3
  * Copyright 2026 WPMoo Authors
4
4
  * Licensed under MIT (https://github.com/wpmoo-org/ui/blob/main/LICENSE)
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Moo UI sidebar.js v1.1.0-dev.1 (https://wpmoo.org/)
2
+ * Moo UI sidebar.js v1.1.0 (https://wpmoo.org/)
3
3
  * Copyright 2026 WPMoo Authors
4
4
  * Licensed under MIT (https://github.com/wpmoo-org/ui/blob/main/LICENSE)
5
5
  */
package/dist/js/slider.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Moo UI slider.js v1.1.0-dev.1 (https://wpmoo.org/)
2
+ * Moo UI slider.js v1.1.0 (https://wpmoo.org/)
3
3
  * Copyright 2026 WPMoo Authors
4
4
  * Licensed under MIT (https://github.com/wpmoo-org/ui/blob/main/LICENSE)
5
5
  */
package/dist/js/state.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Moo UI state.js v1.1.0-dev.1 (https://wpmoo.org/)
2
+ * Moo UI state.js v1.1.0 (https://wpmoo.org/)
3
3
  * Copyright 2026 WPMoo Authors
4
4
  * Licensed under MIT (https://github.com/wpmoo-org/ui/blob/main/LICENSE)
5
5
  */
@@ -1,195 +1,10 @@
1
1
  /*!
2
- * Moo UI table-of-contents.js v1.1.0-dev.1 (https://wpmoo.org/)
2
+ * Moo UI table-of-contents.js v1.1.0 (https://wpmoo.org/)
3
3
  * Copyright 2026 WPMoo Authors
4
4
  * Licensed under MIT (https://github.com/wpmoo-org/ui/blob/main/LICENSE)
5
5
  */
6
6
 
7
- const instances = /* @__PURE__ */ new WeakMap();
8
- const ownerSelector = '.moo-ui[data-bs-theme="light"], .moo-ui[data-bs-theme="dark"]';
9
- function activationOffset(options, previous = 0) {
10
- if (!options || typeof options !== "object" || Array.isArray(options)) {
11
- throw new TypeError("TableOfContents options must be an object.");
12
- }
13
- const value = options.activationOffset === void 0 ? previous : options.activationOffset;
14
- if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
15
- throw new TypeError("TableOfContents activationOffset must be a finite nonnegative number.");
16
- }
17
- return value;
18
- }
19
- function fragmentId(href) {
20
- if (!href?.startsWith("#") || href.length === 1) return null;
21
- try {
22
- return decodeURIComponent(href.slice(1));
23
- } catch {
24
- return null;
25
- }
26
- }
27
- class TableOfContents {
28
- static getInstance(element) {
29
- return element?.nodeType === 1 ? instances.get(element) || null : null;
30
- }
31
- static getOrCreateInstance(element, options = {}) {
32
- return TableOfContents.getInstance(element) || new TableOfContents(element, options);
33
- }
34
- constructor(element, options = {}) {
35
- if (element?.nodeType !== 1 || !element.matches("[data-toc]")) {
36
- throw new TypeError("TableOfContents requires a [data-toc] root element.");
37
- }
38
- const existing = instances.get(element);
39
- if (existing) return existing;
40
- this._element = element;
41
- this._document = element.ownerDocument;
42
- this._window = this._document.defaultView;
43
- this._offset = activationOffset(options);
44
- this._listeners = [];
45
- this._originalLinks = /* @__PURE__ */ new Map();
46
- this._originalStatus = /* @__PURE__ */ new Map();
47
- this._originalMarker = {
48
- attribute: element.getAttribute("data-toc-marker"),
49
- properties: ["--moo-toc-marker-offset", "--moo-toc-marker-height"].map((name) => ({
50
- name,
51
- value: element.style.getPropertyValue(name),
52
- priority: element.style.getPropertyPriority(name)
53
- }))
54
- };
55
- this._links = [];
56
- this._targets = [];
57
- this._frame = 0;
58
- this._disposed = false;
59
- instances.set(element, this);
60
- this.refresh();
61
- }
62
- refresh(options = {}) {
63
- if (this._disposed) return this;
64
- const offset = activationOffset(options, this._offset);
65
- this._clearListeners();
66
- this._offset = offset;
67
- const owner = this._element.closest(ownerSelector);
68
- const contentId = this._element.getAttribute("data-toc-content");
69
- const scope = contentId ? this._document.getElementById(contentId) : owner;
70
- const rootId = this._element.getAttribute("data-toc-scroll-root");
71
- this._scrollElement = rootId ? this._document.getElementById(rootId) : null;
72
- this._status = Array.from(this._element.querySelectorAll("[data-toc-current]")).find((node) => node.closest("[data-toc]") === this._element) || null;
73
- if (this._status && !this._originalStatus.has(this._status)) {
74
- this._originalStatus.set(this._status, this._status.textContent);
75
- }
76
- this._overview = this._status ? this._originalStatus.get(this._status) : "";
77
- this._links = Array.from(this._element.querySelectorAll('a[href^="#"]')).filter((link) => link.closest("[data-toc]") === this._element).map((link) => ({ link, id: fragmentId(link.getAttribute("href")) }));
78
- this._links.forEach(({ link }) => {
79
- if (!this._originalLinks.has(link)) {
80
- this._originalLinks.set(link, {
81
- active: link.classList.contains("active"),
82
- current: link.getAttribute("aria-current")
83
- });
84
- }
85
- });
86
- this._targets = [];
87
- this._activate(null);
88
- if (!owner || !scope || scope.closest(ownerSelector) !== owner || rootId && (!this._scrollElement || !this._scrollElement.contains(scope))) {
89
- return this;
90
- }
91
- const seen = /* @__PURE__ */ new Set();
92
- this._links.forEach(({ id, link }) => {
93
- const target = id ? this._document.getElementById(id) : null;
94
- if (!target || seen.has(id) || !scope.contains(target) || target.closest(ownerSelector) !== owner) return;
95
- seen.add(id);
96
- this._targets.push({ id, target, label: link.textContent.trim() });
97
- });
98
- if (!this._targets.length) return this;
99
- const queue = () => this._requestUpdate();
100
- this._listen(this._scrollElement || this._window, "scroll", queue, { passive: true });
101
- this._listen(this._window, "resize", queue);
102
- this._listen(this._window, "hashchange", queue);
103
- this._listen(this._element, "click", queue);
104
- this._update();
105
- return this;
106
- }
107
- dispose() {
108
- if (this._disposed) return;
109
- this._disposed = true;
110
- this._clearListeners();
111
- this._originalLinks.forEach(({ active, current }, link) => {
112
- link.classList.toggle("active", active);
113
- if (current === null) link.removeAttribute("aria-current");
114
- else link.setAttribute("aria-current", current);
115
- });
116
- this._originalStatus.forEach((text, node) => {
117
- node.textContent = text;
118
- });
119
- const { attribute, properties } = this._originalMarker;
120
- if (attribute === null) this._element.removeAttribute("data-toc-marker");
121
- else this._element.setAttribute("data-toc-marker", attribute);
122
- properties.forEach(({ name, value, priority }) => {
123
- if (value) this._element.style.setProperty(name, value, priority);
124
- else this._element.style.removeProperty(name);
125
- });
126
- this._originalLinks.clear();
127
- this._originalStatus.clear();
128
- this._targets = [];
129
- this._links = [];
130
- if (instances.get(this._element) === this) instances.delete(this._element);
131
- }
132
- _listen(target, type, handler, options) {
133
- target.addEventListener(type, handler, options);
134
- this._listeners.push({ target, type, handler, options });
135
- }
136
- _clearListeners() {
137
- this._listeners.forEach(({ target, type, handler, options }) => target.removeEventListener(type, handler, options));
138
- this._listeners = [];
139
- if (this._frame) this._window.cancelAnimationFrame(this._frame);
140
- this._frame = 0;
141
- }
142
- _requestUpdate() {
143
- if (this._disposed || this._frame) return;
144
- this._frame = this._window.requestAnimationFrame(() => {
145
- this._frame = 0;
146
- this._update();
147
- });
148
- }
149
- _update() {
150
- if (this._disposed) return;
151
- const targets = this._targets.filter(({ target }) => target.isConnected && target.getClientRects().length > 0);
152
- const scroller = this._scrollElement || this._document.scrollingElement;
153
- const line = (this._scrollElement ? this._scrollElement.getBoundingClientRect().top + this._scrollElement.clientTop : 0) + this._offset;
154
- let active = null;
155
- targets.forEach((entry) => {
156
- if (entry.target.getBoundingClientRect().top <= line + 1) active = entry;
157
- });
158
- const canScroll = scroller && scroller.scrollHeight > scroller.clientHeight + 1;
159
- if (!canScroll) {
160
- const hashId = fragmentId(this._window.location.hash);
161
- active = targets.find(({ id }) => id === hashId) || active;
162
- }
163
- if (targets.length && canScroll && scroller.scrollTop + scroller.clientHeight >= scroller.scrollHeight - 1) {
164
- active = targets[targets.length - 1];
165
- }
166
- this._activate(active);
167
- }
168
- _activate(active) {
169
- let activeLink = null;
170
- this._links.forEach(({ link, id }) => {
171
- const current = active !== null && id === active.id;
172
- link.classList.toggle("active", current);
173
- if (current) {
174
- link.setAttribute("aria-current", "location");
175
- activeLink || (activeLink = link);
176
- } else link.removeAttribute("aria-current");
177
- });
178
- if (this._status) this._status.textContent = active ? active.label : this._overview;
179
- this._updateMarker(activeLink);
180
- }
181
- _updateMarker(link) {
182
- const list = this._element.querySelector(":scope > .nav");
183
- const text = link?.querySelector("span");
184
- if (!list?.contains(link) || !text?.offsetHeight || !this._element.getClientRects().length) {
185
- this._element.removeAttribute("data-toc-marker");
186
- return;
187
- }
188
- this._element.style.setProperty("--moo-toc-marker-offset", `${link.offsetTop + text.offsetTop}px`);
189
- this._element.style.setProperty("--moo-toc-marker-height", `${text.offsetHeight}px`);
190
- this._element.setAttribute("data-toc-marker", "");
191
- }
192
- }
7
+ import { default as default2 } from "./toc.js";
193
8
  export {
194
- TableOfContents as default
9
+ default2 as default
195
10
  };
package/dist/js/toc.js ADDED
@@ -0,0 +1,195 @@
1
+ /*!
2
+ * Moo UI toc.js v1.1.0 (https://wpmoo.org/)
3
+ * Copyright 2026 WPMoo Authors
4
+ * Licensed under MIT (https://github.com/wpmoo-org/ui/blob/main/LICENSE)
5
+ */
6
+
7
+ const instances = /* @__PURE__ */ new WeakMap();
8
+ const ownerSelector = '.moo-ui[data-bs-theme="light"], .moo-ui[data-bs-theme="dark"]';
9
+ function activationOffset(options, previous = 0) {
10
+ if (!options || typeof options !== "object" || Array.isArray(options)) {
11
+ throw new TypeError("TableOfContents options must be an object.");
12
+ }
13
+ const value = options.activationOffset === void 0 ? previous : options.activationOffset;
14
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
15
+ throw new TypeError("TableOfContents activationOffset must be a finite nonnegative number.");
16
+ }
17
+ return value;
18
+ }
19
+ function fragmentId(href) {
20
+ if (!href?.startsWith("#") || href.length === 1) return null;
21
+ try {
22
+ return decodeURIComponent(href.slice(1));
23
+ } catch {
24
+ return null;
25
+ }
26
+ }
27
+ class TableOfContents {
28
+ static getInstance(element) {
29
+ return element?.nodeType === 1 ? instances.get(element) || null : null;
30
+ }
31
+ static getOrCreateInstance(element, options = {}) {
32
+ return TableOfContents.getInstance(element) || new TableOfContents(element, options);
33
+ }
34
+ constructor(element, options = {}) {
35
+ if (element?.nodeType !== 1 || !element.matches("[data-toc]")) {
36
+ throw new TypeError("TableOfContents requires a [data-toc] root element.");
37
+ }
38
+ const existing = instances.get(element);
39
+ if (existing) return existing;
40
+ this._element = element;
41
+ this._document = element.ownerDocument;
42
+ this._window = this._document.defaultView;
43
+ this._offset = activationOffset(options);
44
+ this._listeners = [];
45
+ this._originalLinks = /* @__PURE__ */ new Map();
46
+ this._originalStatus = /* @__PURE__ */ new Map();
47
+ this._originalMarker = {
48
+ attribute: element.getAttribute("data-toc-marker"),
49
+ properties: ["--moo-toc-marker-offset", "--moo-toc-marker-height"].map((name) => ({
50
+ name,
51
+ value: element.style.getPropertyValue(name),
52
+ priority: element.style.getPropertyPriority(name)
53
+ }))
54
+ };
55
+ this._links = [];
56
+ this._targets = [];
57
+ this._frame = 0;
58
+ this._disposed = false;
59
+ instances.set(element, this);
60
+ this.refresh();
61
+ }
62
+ refresh(options = {}) {
63
+ if (this._disposed) return this;
64
+ const offset = activationOffset(options, this._offset);
65
+ this._clearListeners();
66
+ this._offset = offset;
67
+ const owner = this._element.closest(ownerSelector);
68
+ const contentId = this._element.getAttribute("data-toc-content");
69
+ const scope = contentId ? this._document.getElementById(contentId) : owner;
70
+ const rootId = this._element.getAttribute("data-toc-scroll-root");
71
+ this._scrollElement = rootId ? this._document.getElementById(rootId) : null;
72
+ this._status = Array.from(this._element.querySelectorAll("[data-toc-current]")).find((node) => node.closest("[data-toc]") === this._element) || null;
73
+ if (this._status && !this._originalStatus.has(this._status)) {
74
+ this._originalStatus.set(this._status, this._status.textContent);
75
+ }
76
+ this._overview = this._status ? this._originalStatus.get(this._status) : "";
77
+ this._links = Array.from(this._element.querySelectorAll('a[href^="#"]')).filter((link) => link.closest("[data-toc]") === this._element).map((link) => ({ link, id: fragmentId(link.getAttribute("href")) }));
78
+ this._links.forEach(({ link }) => {
79
+ if (!this._originalLinks.has(link)) {
80
+ this._originalLinks.set(link, {
81
+ active: link.classList.contains("active"),
82
+ current: link.getAttribute("aria-current")
83
+ });
84
+ }
85
+ });
86
+ this._targets = [];
87
+ this._activate(null);
88
+ if (!owner || !scope || scope.closest(ownerSelector) !== owner || rootId && (!this._scrollElement || !this._scrollElement.contains(scope))) {
89
+ return this;
90
+ }
91
+ const seen = /* @__PURE__ */ new Set();
92
+ this._links.forEach(({ id, link }) => {
93
+ const target = id ? this._document.getElementById(id) : null;
94
+ if (!target || seen.has(id) || !scope.contains(target) || target.closest(ownerSelector) !== owner) return;
95
+ seen.add(id);
96
+ this._targets.push({ id, target, label: link.textContent.trim() });
97
+ });
98
+ if (!this._targets.length) return this;
99
+ const queue = () => this._requestUpdate();
100
+ this._listen(this._scrollElement || this._window, "scroll", queue, { passive: true });
101
+ this._listen(this._window, "resize", queue);
102
+ this._listen(this._window, "hashchange", queue);
103
+ this._listen(this._element, "click", queue);
104
+ this._update();
105
+ return this;
106
+ }
107
+ dispose() {
108
+ if (this._disposed) return;
109
+ this._disposed = true;
110
+ this._clearListeners();
111
+ this._originalLinks.forEach(({ active, current }, link) => {
112
+ link.classList.toggle("active", active);
113
+ if (current === null) link.removeAttribute("aria-current");
114
+ else link.setAttribute("aria-current", current);
115
+ });
116
+ this._originalStatus.forEach((text, node) => {
117
+ node.textContent = text;
118
+ });
119
+ const { attribute, properties } = this._originalMarker;
120
+ if (attribute === null) this._element.removeAttribute("data-toc-marker");
121
+ else this._element.setAttribute("data-toc-marker", attribute);
122
+ properties.forEach(({ name, value, priority }) => {
123
+ if (value) this._element.style.setProperty(name, value, priority);
124
+ else this._element.style.removeProperty(name);
125
+ });
126
+ this._originalLinks.clear();
127
+ this._originalStatus.clear();
128
+ this._targets = [];
129
+ this._links = [];
130
+ if (instances.get(this._element) === this) instances.delete(this._element);
131
+ }
132
+ _listen(target, type, handler, options) {
133
+ target.addEventListener(type, handler, options);
134
+ this._listeners.push({ target, type, handler, options });
135
+ }
136
+ _clearListeners() {
137
+ this._listeners.forEach(({ target, type, handler, options }) => target.removeEventListener(type, handler, options));
138
+ this._listeners = [];
139
+ if (this._frame) this._window.cancelAnimationFrame(this._frame);
140
+ this._frame = 0;
141
+ }
142
+ _requestUpdate() {
143
+ if (this._disposed || this._frame) return;
144
+ this._frame = this._window.requestAnimationFrame(() => {
145
+ this._frame = 0;
146
+ this._update();
147
+ });
148
+ }
149
+ _update() {
150
+ if (this._disposed) return;
151
+ const targets = this._targets.filter(({ target }) => target.isConnected && target.getClientRects().length > 0);
152
+ const scroller = this._scrollElement || this._document.scrollingElement;
153
+ const line = (this._scrollElement ? this._scrollElement.getBoundingClientRect().top + this._scrollElement.clientTop : 0) + this._offset;
154
+ let active = null;
155
+ targets.forEach((entry) => {
156
+ if (entry.target.getBoundingClientRect().top <= line + 1) active = entry;
157
+ });
158
+ const canScroll = scroller && scroller.scrollHeight > scroller.clientHeight + 1;
159
+ if (!canScroll) {
160
+ const hashId = fragmentId(this._window.location.hash);
161
+ active = targets.find(({ id }) => id === hashId) || active;
162
+ }
163
+ if (targets.length && canScroll && scroller.scrollTop + scroller.clientHeight >= scroller.scrollHeight - 1) {
164
+ active = targets[targets.length - 1];
165
+ }
166
+ this._activate(active);
167
+ }
168
+ _activate(active) {
169
+ let activeLink = null;
170
+ this._links.forEach(({ link, id }) => {
171
+ const current = active !== null && id === active.id;
172
+ link.classList.toggle("active", current);
173
+ if (current) {
174
+ link.setAttribute("aria-current", "location");
175
+ activeLink || (activeLink = link);
176
+ } else link.removeAttribute("aria-current");
177
+ });
178
+ if (this._status) this._status.textContent = active ? active.label : this._overview;
179
+ this._updateMarker(activeLink);
180
+ }
181
+ _updateMarker(link) {
182
+ const list = this._element.querySelector(":scope > .nav");
183
+ const text = link?.querySelector("span");
184
+ if (!list?.contains(link) || !text?.offsetHeight || !this._element.getClientRects().length) {
185
+ this._element.removeAttribute("data-toc-marker");
186
+ return;
187
+ }
188
+ this._element.style.setProperty("--moo-toc-marker-offset", `${link.offsetTop + text.offsetTop}px`);
189
+ this._element.style.setProperty("--moo-toc-marker-height", `${text.offsetHeight}px`);
190
+ this._element.setAttribute("data-toc-marker", "");
191
+ }
192
+ }
193
+ export {
194
+ TableOfContents as default
195
+ };
@@ -3,22 +3,22 @@
3
3
  {
4
4
  "export": "./moo.css",
5
5
  "path": "dist/assets/css/moo.css",
6
- "sha256": "38bb85c89e2f1f2f043119a9290cb58dd74a1572090b5fc41930c7c57a90c894"
6
+ "sha256": "c3845a6391648eb2623bb03d6fe7ad37f03c6f481d4e5c9c7e0bb535e5047ae4"
7
7
  },
8
8
  {
9
9
  "export": "./moo-ui.css",
10
10
  "path": "dist/assets/css/moo-ui.css",
11
- "sha256": "d6947a3a27a4315591cf9f6b92e1fd1d958a5fceb1523b709487d54fcbefc815"
11
+ "sha256": "39f8b612e0307052f250aa1df471f995f2008980e025beee96b8878771ef30d0"
12
12
  },
13
13
  {
14
14
  "export": "./state.js",
15
15
  "path": "dist/js/state.js",
16
- "sha256": "3560c86c8623a3d523ec763456d984e009d8a7fa5834de168b153ba9d242f1c2"
16
+ "sha256": "3c88bb64406688eb17abadd8b1f52bf096a1760a867d6f3ee9f0a317711f47bd"
17
17
  }
18
18
  ],
19
19
  "package": {
20
20
  "name": "@wpmoo/ui",
21
- "version": "1.1.0-dev.1"
21
+ "version": "1.1.0"
22
22
  },
23
23
  "schemaVersion": 1
24
24
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wpmoo/ui",
3
- "version": "1.1.0-dev.1",
3
+ "version": "1.1.0",
4
4
  "description": "Bootstrap markup. shadcn feel. A frontend-framework-free component system for server-rendered apps.",
5
5
  "license": "MIT",
6
6
  "author": "WPMoo (https://wpmoo.org)",
@@ -38,6 +38,7 @@
38
38
  "./context-menu.js": "./dist/js/context-menu.js",
39
39
  "./datatable.js": "./dist/js/datatable.js",
40
40
  "./slider.js": "./dist/js/slider.js",
41
+ "./toc.js": "./dist/js/toc.js",
41
42
  "./table-of-contents.js": "./dist/js/table-of-contents.js",
42
43
  "./sheet.js": "./dist/js/sheet.js",
43
44
  "./chart.js": "./dist/js/chart.js",
@@ -66,6 +67,7 @@
66
67
  "dist/js/context-menu.js",
67
68
  "dist/js/datatable.js",
68
69
  "dist/js/slider.js",
70
+ "dist/js/toc.js",
69
71
  "dist/js/table-of-contents.js",
70
72
  "dist/js/sheet.js",
71
73
  "dist/js/chart.js",
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Moo UI v1.1.0-dev.1 (https://wpmoo.org/)
2
+ * Moo UI v1.1.0 (https://wpmoo.org/)
3
3
  * Copyright 2026 WPMoo Authors
4
4
  * Licensed under MIT (https://github.com/wpmoo-org/ui/blob/main/LICENSE)
5
5
  *