@wpmoo/ui 1.0.0-rc.2 → 1.0.0-rc.4
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 +3 -1
- package/README.md +50 -107
- package/certification.json +20 -3
- package/dist/assets/css/moo-ui.css +8254 -7629
- package/dist/assets/css/moo-ui.min.css +3 -3
- package/dist/assets/css/moo.css +595 -71
- package/dist/assets/css/moo.min.css +3 -3
- package/dist/js/chart.js +15258 -0
- package/dist/js/chart.min.js +27 -0
- package/dist/js/combobox.js +98 -40
- package/dist/js/context-menu.js +5 -0
- package/dist/js/datatable.js +5 -0
- package/dist/js/datepicker.js +1154 -0
- package/dist/js/datepicker.min.js +6 -0
- package/dist/js/moo-ui.js +18950 -0
- package/dist/js/moo-ui.min.js +27 -0
- package/dist/js/sidebar.js +67 -25
- package/dist/js/slider.js +324 -0
- package/package.json +24 -4
- package/scss/_components.scss +73 -0
- package/scss/_config.scss +24 -0
- package/scss/_settings.scss +8 -0
- package/scss/components/_accordion.scss +59 -0
- package/scss/components/_alert.scss +94 -0
- package/scss/components/_avatar.scss +154 -0
- package/scss/components/_badge.scss +33 -0
- package/scss/components/_breadcrumb.scss +130 -0
- package/scss/components/_button.scss +228 -0
- package/scss/components/_button_group.scss +51 -0
- package/scss/components/_card.scss +142 -0
- package/scss/components/_checkbox.scss +115 -0
- package/scss/components/_close_button.scss +59 -0
- package/scss/components/_collapsible.scss +43 -0
- package/scss/components/_combobox.scss +258 -0
- package/scss/components/_context-menu.scss +28 -0
- package/scss/components/_datatable.scss +1082 -0
- package/scss/components/_datepicker.scss +236 -0
- package/scss/components/_dialog.scss +66 -0
- package/scss/components/_dropdown.scss +202 -0
- package/scss/components/_field.scss +125 -0
- package/scss/components/_input.scss +35 -0
- package/scss/components/_input_group.scss +201 -0
- package/scss/components/_kbd.scss +10 -0
- package/scss/components/_menubar.scss +16 -0
- package/scss/components/_navigation.scss +41 -0
- package/scss/components/_pagination.scss +70 -0
- package/scss/components/_popover.scss +47 -0
- package/scss/components/_progress.scss +7 -0
- package/scss/components/_radio_group.scss +9 -0
- package/scss/components/_select.scss +7 -0
- package/scss/components/_separator.scss +15 -0
- package/scss/components/_sheet.scss +32 -0
- package/scss/components/_sidebar.scss +7 -0
- package/scss/components/_skeleton.scss +17 -0
- package/scss/components/_slider.scss +214 -0
- package/scss/components/_spinner.scss +17 -0
- package/scss/components/_switch.scss +25 -0
- package/scss/components/_table.scss +7 -0
- package/scss/components/_tabs.scss +139 -0
- package/scss/components/_textarea.scss +7 -0
- package/scss/components/_toast.scss +119 -0
- package/scss/components/_toggle_group.scss +19 -0
- package/scss/components/_tooltip.scss +83 -0
- package/scss/components/sidebar/_collapsed.scss +187 -0
- package/scss/components/sidebar/_identity.scss +72 -0
- package/scss/components/sidebar/_inset.scss +59 -0
- package/scss/components/sidebar/_layout.scss +176 -0
- package/scss/components/sidebar/_menus.scss +236 -0
- package/scss/foundations/_core_global_primitives.scss +5 -0
- package/scss/foundations/_core_state_layer.scss +25 -0
- package/scss/foundations/_focus.scss +139 -0
- package/scss/foundations/_overlay_backdrop.scss +19 -0
- package/scss/moo-core.scss +47 -0
- package/scss/moo-ui.scss +50 -0
- package/scss/settings/_bootstrap_overrides.scss +11 -0
- package/scss/settings/_component_variables.scss +479 -0
- package/scss/settings/_forms.scss +65 -0
- package/scss/settings/_palette.scss +132 -0
- package/scss/themes/_scoped_core.scss +239 -0
- package/scss/themes/_standalone_root.scss +117 -0
- package/scss/utilities/_scroll_fade.scss +193 -0
- package/scss/utilities/_scroll_fade_primitives.scss +43 -0
- package/scss/_facade-settings.scss +0 -51
- package/scss/settings/_facade_public.scss +0 -23
package/dist/js/sidebar.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Moo UI sidebar.js v1.0.0-rc.4 (https://ui.wpmoo.org/)
|
|
3
|
+
* Copyright 2026 WPMoo (https://wpmoo.org)
|
|
4
|
+
* Licensed under MIT (https://github.com/wpmoo-org/ui/blob/main/LICENSE)
|
|
5
|
+
*/
|
|
1
6
|
const instances = new WeakMap();
|
|
2
7
|
|
|
3
8
|
export default class Sidebar {
|
|
@@ -39,7 +44,8 @@ export default class Sidebar {
|
|
|
39
44
|
instances.set(element, this);
|
|
40
45
|
this._bindEvents();
|
|
41
46
|
this._restoreState();
|
|
42
|
-
this.
|
|
47
|
+
this._scrollActiveItemIntoView();
|
|
48
|
+
this._element.setAttribute("data-sidebar-ready", "");
|
|
43
49
|
this._observeDirection();
|
|
44
50
|
}
|
|
45
51
|
|
|
@@ -51,15 +57,15 @@ export default class Sidebar {
|
|
|
51
57
|
this._directionObserver?.disconnect();
|
|
52
58
|
this._closeFlyouts();
|
|
53
59
|
this._element
|
|
54
|
-
.querySelectorAll("[data-
|
|
60
|
+
.querySelectorAll("[data-sidebar-dropdown-positioned]")
|
|
55
61
|
.forEach((item) => this._clearDropdownPosition(item));
|
|
56
62
|
this._element
|
|
57
|
-
.querySelectorAll("[data-
|
|
63
|
+
.querySelectorAll("[data-sidebar-tooltip]")
|
|
58
64
|
.forEach((control) => this._disposeTooltip(control));
|
|
59
65
|
this._offcanvas?.dispose();
|
|
60
66
|
this._offcanvas = null;
|
|
61
67
|
this._offcanvasTrigger = null;
|
|
62
|
-
this._element.removeAttribute("data-
|
|
68
|
+
this._element.removeAttribute("data-sidebar-ready");
|
|
63
69
|
instances.delete(this._element);
|
|
64
70
|
}
|
|
65
71
|
|
|
@@ -79,7 +85,7 @@ export default class Sidebar {
|
|
|
79
85
|
}
|
|
80
86
|
|
|
81
87
|
_isCollapsed() {
|
|
82
|
-
return this._isDesktop() && this._element.dataset.
|
|
88
|
+
return this._isDesktop() && this._element.dataset.sidebarState === "collapsed";
|
|
83
89
|
}
|
|
84
90
|
|
|
85
91
|
_trigger(name, detail = {}) {
|
|
@@ -108,7 +114,7 @@ export default class Sidebar {
|
|
|
108
114
|
}
|
|
109
115
|
|
|
110
116
|
_restoreState() {
|
|
111
|
-
const key = this._element.dataset.
|
|
117
|
+
const key = this._element.dataset.sidebarKey;
|
|
112
118
|
let stored = null;
|
|
113
119
|
if (key) {
|
|
114
120
|
try {
|
|
@@ -120,19 +126,55 @@ export default class Sidebar {
|
|
|
120
126
|
const initial =
|
|
121
127
|
stored === "collapsed" || stored === "expanded"
|
|
122
128
|
? stored
|
|
123
|
-
: this._element.dataset.
|
|
129
|
+
: this._element.dataset.sidebarState === "collapsed"
|
|
124
130
|
? "collapsed"
|
|
125
131
|
: "expanded";
|
|
126
132
|
this._setState(initial, false, false);
|
|
127
133
|
}
|
|
128
134
|
|
|
135
|
+
_scrollActiveItemIntoView() {
|
|
136
|
+
const activeRoute = this._element.querySelector(
|
|
137
|
+
'[data-slot="sidebar-content"] a[data-slot="sidebar-menu-button"][aria-current="page"], ' +
|
|
138
|
+
'[data-slot="sidebar-content"] a.sidebar-menu-button[aria-current="page"], ' +
|
|
139
|
+
'[data-slot="sidebar-content"] a.sidebar-menu-button.active'
|
|
140
|
+
);
|
|
141
|
+
const active = activeRoute || this._element.querySelector(
|
|
142
|
+
'[data-slot="sidebar-content"] [data-slot="sidebar-menu-button"][aria-current="page"], ' +
|
|
143
|
+
'[data-slot="sidebar-content"] .sidebar-menu-button[aria-current="page"], ' +
|
|
144
|
+
'[data-slot="sidebar-content"] .sidebar-menu-button.active'
|
|
145
|
+
);
|
|
146
|
+
const content = active?.closest?.('[data-slot="sidebar-content"]');
|
|
147
|
+
if (!active || !content || content.clientHeight <= 0) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const maxScrollTop = content.scrollHeight - content.clientHeight;
|
|
151
|
+
if (maxScrollTop <= 0) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const contentRect = content.getBoundingClientRect();
|
|
156
|
+
const activeRect = active.getBoundingClientRect();
|
|
157
|
+
if (contentRect.height <= 0 || activeRect.height <= 0) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const targetScrollTop =
|
|
162
|
+
content.scrollTop +
|
|
163
|
+
activeRect.top -
|
|
164
|
+
contentRect.top -
|
|
165
|
+
(content.clientHeight - activeRect.height) / 2;
|
|
166
|
+
content.scrollTop = Math.round(
|
|
167
|
+
Math.min(Math.max(targetScrollTop, 0), maxScrollTop)
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
129
171
|
_setState(state, persist = true, emit = true) {
|
|
130
172
|
const next = state === "collapsed" ? "collapsed" : "expanded";
|
|
131
|
-
const previous = this._element.dataset.
|
|
132
|
-
this._element.dataset.
|
|
133
|
-
const key = this._element.dataset.
|
|
173
|
+
const previous = this._element.dataset.sidebarState;
|
|
174
|
+
this._element.dataset.sidebarState = next;
|
|
175
|
+
const key = this._element.dataset.sidebarKey;
|
|
134
176
|
if (key === "catalog-shell") {
|
|
135
|
-
this._root.dataset.
|
|
177
|
+
this._root.dataset.sidebarCatalogState = next;
|
|
136
178
|
}
|
|
137
179
|
if (persist && key) {
|
|
138
180
|
try {
|
|
@@ -155,16 +197,16 @@ export default class Sidebar {
|
|
|
155
197
|
|
|
156
198
|
_toggle() {
|
|
157
199
|
this._setState(
|
|
158
|
-
this._element.dataset.
|
|
200
|
+
this._element.dataset.sidebarState === "collapsed" ? "expanded" : "collapsed"
|
|
159
201
|
);
|
|
160
202
|
}
|
|
161
203
|
|
|
162
204
|
_syncControls() {
|
|
163
205
|
const expanded = this._isDesktop()
|
|
164
|
-
? this._element.dataset.
|
|
206
|
+
? this._element.dataset.sidebarState === "expanded"
|
|
165
207
|
: this._sidebar?.classList.contains("show") || false;
|
|
166
208
|
this._element
|
|
167
|
-
.querySelectorAll("[data-
|
|
209
|
+
.querySelectorAll("[data-sidebar-trigger], [data-sidebar-rail]")
|
|
168
210
|
.forEach((control) => control.setAttribute("aria-expanded", String(expanded)));
|
|
169
211
|
}
|
|
170
212
|
|
|
@@ -186,7 +228,7 @@ export default class Sidebar {
|
|
|
186
228
|
if (!item) {
|
|
187
229
|
return;
|
|
188
230
|
}
|
|
189
|
-
delete item.dataset.
|
|
231
|
+
delete item.dataset.sidebarDropdownPositioned;
|
|
190
232
|
item.style.removeProperty("--moo-sidebar-dropdown-block-start");
|
|
191
233
|
item.style.removeProperty("--moo-sidebar-dropdown-inline-start");
|
|
192
234
|
}
|
|
@@ -237,7 +279,7 @@ export default class Sidebar {
|
|
|
237
279
|
"--moo-sidebar-dropdown-inline-start",
|
|
238
280
|
`${Math.round(inlineStart)}px`
|
|
239
281
|
);
|
|
240
|
-
item.dataset.
|
|
282
|
+
item.dataset.sidebarDropdownPositioned = "";
|
|
241
283
|
}
|
|
242
284
|
|
|
243
285
|
_removeFlyoutPortal() {
|
|
@@ -281,7 +323,7 @@ export default class Sidebar {
|
|
|
281
323
|
flyout.removeAttribute("id");
|
|
282
324
|
flyout.classList.remove("collapse", "show", "collapsing");
|
|
283
325
|
flyout.classList.add("sidebar-menu-flyout");
|
|
284
|
-
flyout.dataset.
|
|
326
|
+
flyout.dataset.sidebarFlyout = "";
|
|
285
327
|
flyout.removeAttribute("style");
|
|
286
328
|
flyout.style.setProperty("--moo-sidebar-flyout-block-start", `${Math.round(rect.top)}px`);
|
|
287
329
|
flyout.style.setProperty(
|
|
@@ -328,7 +370,7 @@ export default class Sidebar {
|
|
|
328
370
|
}
|
|
329
371
|
const collapsed = this._isCollapsed();
|
|
330
372
|
const placement = this._root.dir === "rtl" ? "left" : "right";
|
|
331
|
-
this._element.querySelectorAll("[data-
|
|
373
|
+
this._element.querySelectorAll("[data-sidebar-tooltip]").forEach((control) => {
|
|
332
374
|
this._disposeTooltip(control);
|
|
333
375
|
if (
|
|
334
376
|
!collapsed ||
|
|
@@ -343,7 +385,7 @@ export default class Sidebar {
|
|
|
343
385
|
// state-driven Sidebar tooltip instead.
|
|
344
386
|
const anchor = this._tooltipAnchor(control);
|
|
345
387
|
new Tooltip(anchor, {
|
|
346
|
-
title: control.getAttribute("data-
|
|
388
|
+
title: control.getAttribute("data-sidebar-tooltip"),
|
|
347
389
|
placement,
|
|
348
390
|
container: "body",
|
|
349
391
|
trigger: "hover focus",
|
|
@@ -386,7 +428,7 @@ export default class Sidebar {
|
|
|
386
428
|
this._listen(this._window, "resize", () => {
|
|
387
429
|
this._closeFlyouts();
|
|
388
430
|
this._element
|
|
389
|
-
.querySelectorAll("[data-
|
|
431
|
+
.querySelectorAll("[data-sidebar-dropdown-positioned]")
|
|
390
432
|
.forEach((item) => this._clearDropdownPosition(item));
|
|
391
433
|
this._syncControls();
|
|
392
434
|
this._syncTooltips();
|
|
@@ -397,18 +439,18 @@ export default class Sidebar {
|
|
|
397
439
|
if (!(target instanceof this._window.Element) || !this._element.contains(target)) {
|
|
398
440
|
return null;
|
|
399
441
|
}
|
|
400
|
-
if (target.matches('[data-bs-toggle="dropdown"][data-
|
|
442
|
+
if (target.matches('[data-bs-toggle="dropdown"][data-sidebar-tooltip]')) {
|
|
401
443
|
return target;
|
|
402
444
|
}
|
|
403
445
|
return target.querySelector?.(
|
|
404
|
-
'[data-bs-toggle="dropdown"][data-
|
|
446
|
+
'[data-bs-toggle="dropdown"][data-sidebar-tooltip]'
|
|
405
447
|
) || null;
|
|
406
448
|
}
|
|
407
449
|
|
|
408
450
|
_handleControlClick(event) {
|
|
409
451
|
const target = event.target;
|
|
410
452
|
const control = target instanceof this._window.Element
|
|
411
|
-
? target.closest("[data-
|
|
453
|
+
? target.closest("[data-sidebar-trigger], [data-sidebar-rail]")
|
|
412
454
|
: null;
|
|
413
455
|
if (!control || !this._element.contains(control)) {
|
|
414
456
|
return;
|
|
@@ -419,7 +461,7 @@ export default class Sidebar {
|
|
|
419
461
|
this._toggle();
|
|
420
462
|
return;
|
|
421
463
|
}
|
|
422
|
-
if (!control.matches("[data-
|
|
464
|
+
if (!control.matches("[data-sidebar-trigger]")) {
|
|
423
465
|
return;
|
|
424
466
|
}
|
|
425
467
|
const sidebar = this._document.getElementById(control.getAttribute("aria-controls"));
|
|
@@ -478,7 +520,7 @@ export default class Sidebar {
|
|
|
478
520
|
}
|
|
479
521
|
const preferred =
|
|
480
522
|
this._document.querySelector(
|
|
481
|
-
'[data-slot="sidebar-wrapper"][data-
|
|
523
|
+
'[data-slot="sidebar-wrapper"][data-sidebar-key]'
|
|
482
524
|
) || this._document.querySelector('[data-slot="sidebar-wrapper"]');
|
|
483
525
|
if (preferred !== this._element) {
|
|
484
526
|
return;
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Moo UI slider.js v1.0.0-rc.4 (https://ui.wpmoo.org/)
|
|
3
|
+
* Copyright 2026 WPMoo (https://wpmoo.org)
|
|
4
|
+
* Licensed under MIT (https://github.com/wpmoo-org/ui/blob/main/LICENSE)
|
|
5
|
+
*/
|
|
6
|
+
const instances = new WeakMap();
|
|
7
|
+
|
|
8
|
+
function asNumber(value, fallback) {
|
|
9
|
+
const number = Number(value);
|
|
10
|
+
return Number.isFinite(number) ? number : fallback;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function inputMinimum(input) {
|
|
14
|
+
return asNumber(input.min, 0);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function inputMaximum(input) {
|
|
18
|
+
return asNumber(input.max, 100);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function inputStep(input) {
|
|
22
|
+
if (!input.step || input.step === "any") {
|
|
23
|
+
return 1;
|
|
24
|
+
}
|
|
25
|
+
const step = Number(input.step);
|
|
26
|
+
return Number.isFinite(step) && step > 0 ? step : 1;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function clamp(value, min, max) {
|
|
30
|
+
return Math.min(Math.max(value, min), max);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function decimalPlaces(value) {
|
|
34
|
+
const text = String(value);
|
|
35
|
+
if (!text.includes(".")) {
|
|
36
|
+
return 0;
|
|
37
|
+
}
|
|
38
|
+
return text.split(".").at(-1).length;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function snapToStep(value, input) {
|
|
42
|
+
const min = inputMinimum(input);
|
|
43
|
+
const max = inputMaximum(input);
|
|
44
|
+
if (input.step === "any") {
|
|
45
|
+
return Number(clamp(value, min, max).toFixed(4));
|
|
46
|
+
}
|
|
47
|
+
const step = inputStep(input);
|
|
48
|
+
const snapped = min + Math.round((value - min) / step) * step;
|
|
49
|
+
const precision = Math.max(decimalPlaces(step), decimalPlaces(min));
|
|
50
|
+
return clamp(Number(snapped.toFixed(precision)), min, max);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function valuePercent(input) {
|
|
54
|
+
const min = inputMinimum(input);
|
|
55
|
+
const max = inputMaximum(input);
|
|
56
|
+
if (max <= min) {
|
|
57
|
+
return 0;
|
|
58
|
+
}
|
|
59
|
+
return ((asNumber(input.value, min) - min) / (max - min)) * 100;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function displayValue(input) {
|
|
63
|
+
return input.value || String(inputMinimum(input));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export default class MooSlider {
|
|
67
|
+
static getInstance(element) {
|
|
68
|
+
return element?.nodeType === 1 ? instances.get(element) || null : null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
static getOrCreateInstance(element, config = {}) {
|
|
72
|
+
return MooSlider.getInstance(element) || new MooSlider(element, config);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
constructor(element, config = {}) {
|
|
76
|
+
if (element?.nodeType !== 1 || !element.matches("[data-slider]")) {
|
|
77
|
+
throw new TypeError("MooSlider requires a [data-slider] root element.");
|
|
78
|
+
}
|
|
79
|
+
const existing = instances.get(element);
|
|
80
|
+
if (existing) {
|
|
81
|
+
return existing;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
this._element = element;
|
|
85
|
+
this._document = element.ownerDocument;
|
|
86
|
+
this._window = this._document.defaultView;
|
|
87
|
+
this._config = { ...config };
|
|
88
|
+
this._listeners = [];
|
|
89
|
+
this._dragListeners = [];
|
|
90
|
+
this._draggingInput = null;
|
|
91
|
+
this._disposed = false;
|
|
92
|
+
this._inputs = Array.from(
|
|
93
|
+
element.querySelectorAll(':scope input[type="range"][data-slider-input]')
|
|
94
|
+
);
|
|
95
|
+
this._track = element.querySelector("[data-slider-track]");
|
|
96
|
+
this._output = element.querySelector("[data-slider-output]");
|
|
97
|
+
|
|
98
|
+
if (this._inputs.length < 1 || this._inputs.length > 2) {
|
|
99
|
+
throw new TypeError("MooSlider requires one or two native range inputs.");
|
|
100
|
+
}
|
|
101
|
+
if (!this._track) {
|
|
102
|
+
throw new TypeError("MooSlider requires a [data-slider-track] element.");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
instances.set(element, this);
|
|
106
|
+
this._bindEvents();
|
|
107
|
+
this._sync();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
dispose() {
|
|
111
|
+
if (this._disposed) return;
|
|
112
|
+
this._disposed = true;
|
|
113
|
+
this._clearDragListeners();
|
|
114
|
+
this._clearPointerFocus();
|
|
115
|
+
this._listeners.forEach(({ target, type, handler, options }) => {
|
|
116
|
+
target.removeEventListener(type, handler, options);
|
|
117
|
+
});
|
|
118
|
+
this._listeners = [];
|
|
119
|
+
if (instances.get(this._element) === this) {
|
|
120
|
+
instances.delete(this._element);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
_listen(target, type, handler, options) {
|
|
125
|
+
if (!target) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
target.addEventListener(type, handler, options);
|
|
129
|
+
this._listeners.push({ target, type, handler, options });
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
_listenDuringDrag(target, type, handler, options) {
|
|
133
|
+
if (!target) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
target.addEventListener(type, handler, options);
|
|
137
|
+
this._dragListeners.push({ target, type, handler, options });
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
_clearDragListeners() {
|
|
141
|
+
this._dragListeners.forEach(({ target, type, handler, options }) => {
|
|
142
|
+
target.removeEventListener(type, handler, options);
|
|
143
|
+
});
|
|
144
|
+
this._dragListeners = [];
|
|
145
|
+
this._draggingInput = null;
|
|
146
|
+
this._element.removeAttribute("data-slider-dragging");
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
_bindEvents() {
|
|
150
|
+
this._inputs.forEach((input) => {
|
|
151
|
+
this._listen(input, "input", () => this._sync(input));
|
|
152
|
+
this._listen(input, "change", () => {
|
|
153
|
+
this._sync(input);
|
|
154
|
+
this._trigger("change");
|
|
155
|
+
});
|
|
156
|
+
this._listen(input, "keydown", () => this._clearPointerFocus());
|
|
157
|
+
this._listen(input, "focusout", () => this._clearPointerFocus());
|
|
158
|
+
});
|
|
159
|
+
const forms = new Set(
|
|
160
|
+
this._inputs
|
|
161
|
+
.map((input) => input.form || input.closest?.("form"))
|
|
162
|
+
.filter(Boolean),
|
|
163
|
+
);
|
|
164
|
+
forms.forEach((form) => {
|
|
165
|
+
this._listen(form, "reset", () => {
|
|
166
|
+
this._window.setTimeout(() => this._sync(), 0);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
this._listen(this._track, "pointerdown", (event) => this._handleTrackPointer(event));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
_trigger(name) {
|
|
173
|
+
this._element.dispatchEvent(
|
|
174
|
+
new this._window.CustomEvent(`${name}.moo.slider`, {
|
|
175
|
+
bubbles: true,
|
|
176
|
+
})
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
_isRange() {
|
|
181
|
+
return this._inputs.length === 2;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
_sync(changedInput = null) {
|
|
185
|
+
if (this._isRange()) {
|
|
186
|
+
const [startInput, endInput] = this._inputs;
|
|
187
|
+
const start = asNumber(startInput.value, inputMinimum(startInput));
|
|
188
|
+
const end = asNumber(endInput.value, inputMaximum(endInput));
|
|
189
|
+
if (start > end) {
|
|
190
|
+
if (changedInput === endInput) {
|
|
191
|
+
endInput.value = startInput.value;
|
|
192
|
+
} else {
|
|
193
|
+
startInput.value = endInput.value;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
this._writeFill(valuePercent(startInput), valuePercent(endInput));
|
|
197
|
+
this._writeOutput(`${displayValue(startInput)} - ${displayValue(endInput)}`);
|
|
198
|
+
this._syncAria(startInput);
|
|
199
|
+
this._syncAria(endInput);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const input = this._inputs[0];
|
|
204
|
+
this._writeFill(0, valuePercent(input));
|
|
205
|
+
this._writeOutput(displayValue(input));
|
|
206
|
+
this._syncAria(input);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
_syncAria(input) {
|
|
210
|
+
input.setAttribute("aria-valuemin", String(inputMinimum(input)));
|
|
211
|
+
input.setAttribute("aria-valuemax", String(inputMaximum(input)));
|
|
212
|
+
input.setAttribute("aria-valuenow", displayValue(input));
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
_writeFill(start, end) {
|
|
216
|
+
const clampedStart = clamp(start, 0, 100);
|
|
217
|
+
const clampedEnd = clamp(end, 0, 100);
|
|
218
|
+
this._element.style.setProperty("--moo-slider-start", `${Math.min(clampedStart, clampedEnd)}%`);
|
|
219
|
+
this._element.style.setProperty("--moo-slider-end", `${Math.max(clampedStart, clampedEnd)}%`);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
_writeOutput(value) {
|
|
223
|
+
if (this._output) {
|
|
224
|
+
this._output.textContent = value;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
_handleTrackPointer(event) {
|
|
229
|
+
if (event.button !== undefined && event.button !== 0) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
const enabledInputs = this._inputs.filter((input) => !input.disabled);
|
|
233
|
+
if (!enabledInputs.length) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
event.preventDefault?.();
|
|
237
|
+
const percent = this._pointerPercent(event);
|
|
238
|
+
const target = this._nearestInput(percent * 100, enabledInputs);
|
|
239
|
+
this._setPointerFocus();
|
|
240
|
+
this._startDrag(target, event);
|
|
241
|
+
this._setInputFromPointer(target, event);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
_setPointerFocus() {
|
|
245
|
+
this._element.setAttribute("data-slider-pointer-focus", "true");
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
_clearPointerFocus() {
|
|
249
|
+
this._element.removeAttribute("data-slider-pointer-focus");
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
_startDrag(input, event) {
|
|
253
|
+
this._clearDragListeners();
|
|
254
|
+
this._draggingInput = input;
|
|
255
|
+
this._element.setAttribute("data-slider-dragging", "true");
|
|
256
|
+
this._track.setPointerCapture?.(event.pointerId);
|
|
257
|
+
|
|
258
|
+
const pointerId = event.pointerId;
|
|
259
|
+
const samePointer = (nextEvent) => pointerId === undefined || nextEvent.pointerId === pointerId;
|
|
260
|
+
const onMove = (moveEvent) => {
|
|
261
|
+
if (!samePointer(moveEvent) || this._draggingInput !== input) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
moveEvent.preventDefault?.();
|
|
265
|
+
this._setInputFromPointer(input, moveEvent);
|
|
266
|
+
};
|
|
267
|
+
const onEnd = (endEvent) => {
|
|
268
|
+
if (!samePointer(endEvent) || this._draggingInput !== input) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
endEvent.preventDefault?.();
|
|
272
|
+
this._setInputFromPointer(input, endEvent, false);
|
|
273
|
+
input.dispatchEvent(new this._window.Event("change", { bubbles: true }));
|
|
274
|
+
this._track.releasePointerCapture?.(pointerId);
|
|
275
|
+
this._clearDragListeners();
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
this._listenDuringDrag(this._document, "pointermove", onMove);
|
|
279
|
+
this._listenDuringDrag(this._document, "pointerup", onEnd);
|
|
280
|
+
this._listenDuringDrag(this._document, "pointercancel", onEnd);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
_setInputFromPointer(input, event, emitInput = true) {
|
|
284
|
+
const percent = this._pointerPercent(event);
|
|
285
|
+
const min = inputMinimum(input);
|
|
286
|
+
const max = inputMaximum(input);
|
|
287
|
+
input.value = String(snapToStep(min + percent * (max - min), input));
|
|
288
|
+
input.focus({ preventScroll: true });
|
|
289
|
+
this._sync(input);
|
|
290
|
+
if (emitInput) {
|
|
291
|
+
input.dispatchEvent(new this._window.Event("input", { bubbles: true }));
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
_pointerPercent(event) {
|
|
296
|
+
const rect = this._track.getBoundingClientRect();
|
|
297
|
+
const orientation = this._element.dataset.sliderOrientation || "horizontal";
|
|
298
|
+
if (orientation === "vertical") {
|
|
299
|
+
return clamp((rect.bottom - event.clientY) / (rect.height || 1), 0, 1);
|
|
300
|
+
}
|
|
301
|
+
return this._horizontalPointerPercent(event, rect);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
_horizontalPointerPercent(event, rect) {
|
|
305
|
+
const direction = this._window.getComputedStyle?.(this._element).direction;
|
|
306
|
+
const offset = direction === "rtl" ? rect.right - event.clientX : event.clientX - rect.left;
|
|
307
|
+
return clamp(offset / (rect.width || 1), 0, 1);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
_nearestInput(percent, inputs) {
|
|
311
|
+
if (inputs.length === 1) {
|
|
312
|
+
return inputs[0];
|
|
313
|
+
}
|
|
314
|
+
const [startInput, endInput] = inputs;
|
|
315
|
+
const startPercent = valuePercent(startInput);
|
|
316
|
+
const endPercent = valuePercent(endInput);
|
|
317
|
+
const startDistance = Math.abs(startPercent - percent);
|
|
318
|
+
const endDistance = Math.abs(endPercent - percent);
|
|
319
|
+
if (startDistance === endDistance) {
|
|
320
|
+
return percent >= startPercent ? endInput : startInput;
|
|
321
|
+
}
|
|
322
|
+
return endDistance < startDistance ? endInput : startInput;
|
|
323
|
+
}
|
|
324
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wpmoo/ui",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.4",
|
|
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)",
|
|
@@ -27,11 +27,22 @@
|
|
|
27
27
|
"./moo-ui.min.css": "./dist/assets/css/moo-ui.min.css",
|
|
28
28
|
"./moo.css": "./dist/assets/css/moo.css",
|
|
29
29
|
"./moo.min.css": "./dist/assets/css/moo.min.css",
|
|
30
|
+
"./moo-ui.js": "./dist/js/moo-ui.js",
|
|
31
|
+
"./moo-ui.min.js": "./dist/js/moo-ui.min.js",
|
|
30
32
|
"./combobox.js": "./dist/js/combobox.js",
|
|
31
33
|
"./sidebar.js": "./dist/js/sidebar.js",
|
|
32
34
|
"./context-menu.js": "./dist/js/context-menu.js",
|
|
33
35
|
"./datatable.js": "./dist/js/datatable.js",
|
|
34
|
-
"./
|
|
36
|
+
"./slider.js": "./dist/js/slider.js",
|
|
37
|
+
"./chart.js": "./dist/js/chart.js",
|
|
38
|
+
"./chart.min.js": "./dist/js/chart.min.js",
|
|
39
|
+
"./datepicker.js": "./dist/js/datepicker.js",
|
|
40
|
+
"./datepicker.min.js": "./dist/js/datepicker.min.js",
|
|
41
|
+
"./scss/config": "./scss/_config.scss",
|
|
42
|
+
"./scss/moo-ui": "./scss/moo-ui.scss",
|
|
43
|
+
"./scss/moo-core": "./scss/moo-core.scss",
|
|
44
|
+
"./scss/components": "./scss/_components.scss",
|
|
45
|
+
"./scss/settings": "./scss/_settings.scss",
|
|
35
46
|
"./certification.json": "./certification.json",
|
|
36
47
|
"./package.json": "./package.json"
|
|
37
48
|
},
|
|
@@ -40,12 +51,19 @@
|
|
|
40
51
|
"dist/assets/css/moo-ui.min.css",
|
|
41
52
|
"dist/assets/css/moo.css",
|
|
42
53
|
"dist/assets/css/moo.min.css",
|
|
54
|
+
"dist/js/moo-ui.js",
|
|
55
|
+
"dist/js/moo-ui.min.js",
|
|
43
56
|
"dist/js/combobox.js",
|
|
44
57
|
"dist/js/sidebar.js",
|
|
45
58
|
"dist/js/context-menu.js",
|
|
46
59
|
"dist/js/datatable.js",
|
|
47
|
-
"
|
|
48
|
-
"
|
|
60
|
+
"dist/js/slider.js",
|
|
61
|
+
"dist/js/chart.js",
|
|
62
|
+
"dist/js/chart.min.js",
|
|
63
|
+
"dist/js/datepicker.js",
|
|
64
|
+
"dist/js/datepicker.min.js",
|
|
65
|
+
"scss/*.scss",
|
|
66
|
+
"scss/**/*.scss",
|
|
49
67
|
"certification.json",
|
|
50
68
|
"README.md",
|
|
51
69
|
"LICENSE",
|
|
@@ -75,6 +93,8 @@
|
|
|
75
93
|
},
|
|
76
94
|
"devDependencies": {
|
|
77
95
|
"axe-core": "4.13.0",
|
|
96
|
+
"chart.js": "4.5.1",
|
|
97
|
+
"esbuild": "0.28.1",
|
|
78
98
|
"wrangler": "^4.120.0"
|
|
79
99
|
}
|
|
80
100
|
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Bootstrap components
|
|
2
|
+
@import "bootstrap/scss/tables";
|
|
3
|
+
@import "bootstrap/scss/forms/labels";
|
|
4
|
+
@import "bootstrap/scss/forms/form-text";
|
|
5
|
+
@import "bootstrap/scss/forms/form-control";
|
|
6
|
+
@import "bootstrap/scss/forms/form-select";
|
|
7
|
+
@import "bootstrap/scss/forms/form-check";
|
|
8
|
+
@import "bootstrap/scss/forms/input-group";
|
|
9
|
+
@import "bootstrap/scss/forms/validation";
|
|
10
|
+
@import "bootstrap/scss/buttons";
|
|
11
|
+
@import "bootstrap/scss/dropdown";
|
|
12
|
+
@import "bootstrap/scss/button-group";
|
|
13
|
+
@import "bootstrap/scss/nav";
|
|
14
|
+
@import "bootstrap/scss/card";
|
|
15
|
+
@import "bootstrap/scss/accordion";
|
|
16
|
+
@import "bootstrap/scss/breadcrumb";
|
|
17
|
+
@import "bootstrap/scss/pagination";
|
|
18
|
+
@import "bootstrap/scss/badge";
|
|
19
|
+
@import "bootstrap/scss/alert";
|
|
20
|
+
@import "bootstrap/scss/close";
|
|
21
|
+
@import "bootstrap/scss/toasts";
|
|
22
|
+
@import "bootstrap/scss/modal";
|
|
23
|
+
@import "bootstrap/scss/tooltip";
|
|
24
|
+
@import "bootstrap/scss/popover";
|
|
25
|
+
@import "bootstrap/scss/offcanvas";
|
|
26
|
+
@import "bootstrap/scss/helpers/color-bg";
|
|
27
|
+
|
|
28
|
+
// Moo component support
|
|
29
|
+
@import "foundations/focus";
|
|
30
|
+
@import "utilities/scroll_fade";
|
|
31
|
+
|
|
32
|
+
// Moo components
|
|
33
|
+
@import "components/kbd";
|
|
34
|
+
@import "components/input";
|
|
35
|
+
@import "components/textarea";
|
|
36
|
+
@import "components/select";
|
|
37
|
+
@import "components/input_group";
|
|
38
|
+
@import "components/button";
|
|
39
|
+
@import "components/close_button";
|
|
40
|
+
@import "components/button_group";
|
|
41
|
+
@import "components/toggle_group";
|
|
42
|
+
@import "components/card";
|
|
43
|
+
@import "components/table";
|
|
44
|
+
@import "components/dropdown";
|
|
45
|
+
@import "components/menubar";
|
|
46
|
+
@import "components/avatar";
|
|
47
|
+
@import "components/navigation";
|
|
48
|
+
@import "components/skeleton";
|
|
49
|
+
@import "components/sidebar";
|
|
50
|
+
@import "components/alert";
|
|
51
|
+
@import "components/breadcrumb";
|
|
52
|
+
@import "components/pagination";
|
|
53
|
+
@import "components/badge";
|
|
54
|
+
@import "components/progress";
|
|
55
|
+
@import "components/slider";
|
|
56
|
+
@import "components/separator";
|
|
57
|
+
@import "components/checkbox";
|
|
58
|
+
@import "components/radio_group";
|
|
59
|
+
@import "components/switch";
|
|
60
|
+
@import "components/spinner";
|
|
61
|
+
@import "components/accordion";
|
|
62
|
+
@import "components/tabs";
|
|
63
|
+
@import "components/collapsible";
|
|
64
|
+
@import "components/tooltip";
|
|
65
|
+
@import "components/popover";
|
|
66
|
+
@import "components/dialog";
|
|
67
|
+
@import "components/sheet";
|
|
68
|
+
@import "components/toast";
|
|
69
|
+
@import "components/field";
|
|
70
|
+
@import "components/datepicker";
|
|
71
|
+
@import "components/combobox";
|
|
72
|
+
@import "components/context-menu";
|
|
73
|
+
@import "components/datatable";
|