@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 +1 -1
- package/README.md +4 -6
- package/certification.json +2 -1
- package/dist/assets/css/moo-ui.css +1 -1
- package/dist/assets/css/moo-ui.min.css +1 -1
- package/dist/assets/css/moo.css +1 -1
- package/dist/assets/css/moo.min.css +1 -1
- package/dist/js/chart.js +1 -1
- package/dist/js/chart.min.js +1 -1
- package/dist/js/combobox.js +1 -1
- package/dist/js/context-menu.js +1 -1
- package/dist/js/datatable.js +1 -1
- package/dist/js/datepicker.js +1 -1
- package/dist/js/datepicker.min.js +1 -1
- package/dist/js/moo-ui.js +2 -2
- package/dist/js/moo-ui.min.js +1 -1
- package/dist/js/sheet.js +1 -1
- package/dist/js/sidebar.js +1 -1
- package/dist/js/slider.js +1 -1
- package/dist/js/state.js +1 -1
- package/dist/js/table-of-contents.js +3 -188
- package/dist/js/toc.js +195 -0
- package/dist/release-manifest.json +4 -4
- package/package.json +3 -1
- package/scss/mixins/_banner.scss +1 -1
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.
|
|
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
|
|
198
|
-
Contents component
|
|
199
|
-
|
|
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.
|
|
225
|
+
[version-pinned third-party notices](https://github.com/wpmoo-org/ui/blob/v1.1.0/THIRD_PARTY_NOTICES.md).
|
package/certification.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "0.1",
|
|
3
3
|
"status": "preview",
|
|
4
|
-
"coreVersion": "1.1.0
|
|
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",
|
package/dist/assets/css/moo.css
CHANGED
package/dist/js/chart.js
CHANGED
package/dist/js/chart.min.js
CHANGED
package/dist/js/combobox.js
CHANGED
package/dist/js/context-menu.js
CHANGED
package/dist/js/datatable.js
CHANGED
package/dist/js/datepicker.js
CHANGED
package/dist/js/moo-ui.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Moo UI moo-ui.js v1.1.0
|
|
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/
|
|
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) {
|
package/dist/js/moo-ui.min.js
CHANGED
package/dist/js/sheet.js
CHANGED
package/dist/js/sidebar.js
CHANGED
package/dist/js/slider.js
CHANGED
package/dist/js/state.js
CHANGED
|
@@ -1,195 +1,10 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Moo UI table-of-contents.js v1.1.0
|
|
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
|
-
|
|
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
|
-
|
|
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": "
|
|
6
|
+
"sha256": "c3845a6391648eb2623bb03d6fe7ad37f03c6f481d4e5c9c7e0bb535e5047ae4"
|
|
7
7
|
},
|
|
8
8
|
{
|
|
9
9
|
"export": "./moo-ui.css",
|
|
10
10
|
"path": "dist/assets/css/moo-ui.css",
|
|
11
|
-
"sha256": "
|
|
11
|
+
"sha256": "39f8b612e0307052f250aa1df471f995f2008980e025beee96b8878771ef30d0"
|
|
12
12
|
},
|
|
13
13
|
{
|
|
14
14
|
"export": "./state.js",
|
|
15
15
|
"path": "dist/js/state.js",
|
|
16
|
-
"sha256": "
|
|
16
|
+
"sha256": "3c88bb64406688eb17abadd8b1f52bf096a1760a867d6f3ee9f0a317711f47bd"
|
|
17
17
|
}
|
|
18
18
|
],
|
|
19
19
|
"package": {
|
|
20
20
|
"name": "@wpmoo/ui",
|
|
21
|
-
"version": "1.1.0
|
|
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
|
|
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",
|
package/scss/mixins/_banner.scss
CHANGED