@wpmoo/ui 2.1.0 → 2.2.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/README.md +27 -2
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/certification.json +2 -1
- package/dist/assets/css/moo-ui.css +429 -1
- package/dist/assets/css/moo-ui.min.css +2 -2
- package/dist/assets/css/moo.css +349 -1
- package/dist/assets/css/moo.min.css +2 -2
- package/dist/js/chart.js +9 -1
- package/dist/js/chart.min.js +2 -2
- package/dist/js/combobox.js +17 -6
- package/dist/js/context-menu.js +8 -3
- package/dist/js/datatable.js +41 -19
- package/dist/js/datepicker.js +23 -9
- package/dist/js/datepicker.min.js +2 -2
- package/dist/js/floating-panel.js +1 -1
- package/dist/js/image-gallery.js +247 -0
- package/dist/js/moo-ui.js +360 -34
- package/dist/js/moo-ui.min.js +2 -2
- package/dist/js/overlay-scrollbar.js +1 -1
- package/dist/js/password-requirements.js +1 -1
- package/dist/js/sheet.js +1 -1
- package/dist/js/sidebar.js +42 -1
- package/dist/js/slider.js +1 -1
- package/dist/js/state.js +1 -1
- package/dist/js/table-of-contents.js +1 -1
- package/dist/js/toc.js +1 -1
- package/dist/release-manifest.json +4 -4
- package/package.json +3 -1
- package/scss/_components.scss +2 -0
- package/scss/components/_gallery.scss +73 -0
- package/scss/components/_item.scss +96 -0
- package/scss/foundations/_globals.scss +1 -0
- package/scss/foundations/_scope.scss +2 -0
- package/scss/mixins/_banner.scss +1 -1
- package/scss/moo-core.scss +1 -0
- package/scss/moo-ui.scss +2 -0
- package/scss/settings/_component_variables.scss +8 -0
- package/scss/utilities/_gallery.scss +25 -0
- package/scss/utilities/_motion.scss +5 -0
- package/scss/utilities/_motion_primitives.scss +10 -0
package/dist/js/moo-ui.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Moo UI moo-ui.js v2.
|
|
2
|
+
* Moo UI moo-ui.js v2.2.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
|
*/
|
|
@@ -247,11 +247,7 @@ var Combobox = class _Combobox {
|
|
|
247
247
|
_clearStaleSelection() {
|
|
248
248
|
if (this._isMultiple) {
|
|
249
249
|
this._input.value = "";
|
|
250
|
-
this.
|
|
251
|
-
option.hidden = false;
|
|
252
|
-
});
|
|
253
|
-
this._syncGroupVisibility();
|
|
254
|
-
this._empty.hidden = true;
|
|
250
|
+
this._showAllOptions();
|
|
255
251
|
return;
|
|
256
252
|
}
|
|
257
253
|
const selected = this._selectedOptions()[0] || null;
|
|
@@ -262,6 +258,14 @@ var Combobox = class _Combobox {
|
|
|
262
258
|
this._clearSelection();
|
|
263
259
|
this._input.value = "";
|
|
264
260
|
}
|
|
261
|
+
_showAllOptions() {
|
|
262
|
+
this._options.forEach((option) => {
|
|
263
|
+
option.hidden = false;
|
|
264
|
+
});
|
|
265
|
+
this._syncGroupVisibility();
|
|
266
|
+
this._empty.hidden = true;
|
|
267
|
+
this._liveRegion.textContent = "";
|
|
268
|
+
}
|
|
265
269
|
_activeOption() {
|
|
266
270
|
const activeId = this._input.getAttribute("aria-activedescendant");
|
|
267
271
|
if (!activeId) {
|
|
@@ -348,6 +352,7 @@ var Combobox = class _Combobox {
|
|
|
348
352
|
if (this._hidden) {
|
|
349
353
|
this._hidden.value = option.dataset.value || "";
|
|
350
354
|
}
|
|
355
|
+
this._showAllOptions();
|
|
351
356
|
this._toggleClear(true);
|
|
352
357
|
this._setActiveOption(option);
|
|
353
358
|
this._emitChange();
|
|
@@ -440,6 +445,12 @@ var Combobox = class _Combobox {
|
|
|
440
445
|
}
|
|
441
446
|
}
|
|
442
447
|
_handleKeydown(event) {
|
|
448
|
+
if (event.isComposing || event.keyCode === 229) {
|
|
449
|
+
if (event.key === "Tab") {
|
|
450
|
+
this._closeMenu();
|
|
451
|
+
}
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
443
454
|
const available = this._visibleOptions();
|
|
444
455
|
const current = available.findIndex(
|
|
445
456
|
(option) => option.id === this._input.getAttribute("aria-activedescendant")
|
|
@@ -544,12 +555,17 @@ var ContextMenu = class _ContextMenu {
|
|
|
544
555
|
this._bindEvents();
|
|
545
556
|
}
|
|
546
557
|
dispose() {
|
|
558
|
+
if (this._disposing) {
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
this._disposing = true;
|
|
547
562
|
this._listeners.forEach(({ target, type, handler, options }) => {
|
|
548
563
|
target.removeEventListener(type, handler, options);
|
|
549
564
|
});
|
|
550
565
|
this._listeners = [];
|
|
551
|
-
this._dropdown.dispose();
|
|
552
566
|
this._syncExpanded(false);
|
|
567
|
+
this._dropdown.hide();
|
|
568
|
+
this._dropdown.dispose();
|
|
553
569
|
this._invoker = null;
|
|
554
570
|
this._pointerRect = null;
|
|
555
571
|
instances2.delete(this._element);
|
|
@@ -661,7 +677,7 @@ var ContextMenu = class _ContextMenu {
|
|
|
661
677
|
event.preventDefault();
|
|
662
678
|
event.stopPropagation();
|
|
663
679
|
const group = radioButton.dataset.contextMenuRadioGroup;
|
|
664
|
-
this._menu.querySelectorAll(`[data-context-menu-radio-group="${group}"]`).forEach((button) => {
|
|
680
|
+
this._menu.querySelectorAll(`[data-context-menu-radio-group="${CSS.escape(String(group))}"]`).forEach((button) => {
|
|
665
681
|
const checked = button === radioButton;
|
|
666
682
|
button.classList.toggle("active", checked);
|
|
667
683
|
button.setAttribute("aria-pressed", String(checked));
|
|
@@ -749,9 +765,30 @@ function ownerPortalRoot(owner) {
|
|
|
749
765
|
|
|
750
766
|
// src/js/components/datatable.js
|
|
751
767
|
var instances3 = /* @__PURE__ */ new WeakMap();
|
|
768
|
+
var SORT_COLLATOR = new Intl.Collator(void 0, { numeric: true, sensitivity: "base" });
|
|
769
|
+
var PLAIN_NUMBER = /^[-+]?(?:\d+\.?\d*|\.\d+)$/;
|
|
770
|
+
function sortNumber(value) {
|
|
771
|
+
return PLAIN_NUMBER.test(value) ? Number(value) : null;
|
|
772
|
+
}
|
|
773
|
+
function compareSortValues(a, b) {
|
|
774
|
+
const aNumber = sortNumber(a);
|
|
775
|
+
const bNumber = sortNumber(b);
|
|
776
|
+
if (aNumber !== null && bNumber !== null) {
|
|
777
|
+
return Math.sign(aNumber - bNumber);
|
|
778
|
+
}
|
|
779
|
+
if (aNumber !== null || bNumber !== null) {
|
|
780
|
+
return aNumber !== null ? -1 : 1;
|
|
781
|
+
}
|
|
782
|
+
return SORT_COLLATOR.compare(a, b);
|
|
783
|
+
}
|
|
752
784
|
function normalize2(value) {
|
|
753
785
|
return (value ?? "").toString().trim().toLowerCase();
|
|
754
786
|
}
|
|
787
|
+
var ESCAPE_OWNER_SELECTOR = ".modal, .offcanvas, .dropdown-menu, .popover, [role='dialog'], [role='menu'], [role='listbox']";
|
|
788
|
+
function escapeBelongsElsewhere(event) {
|
|
789
|
+
const target = event.target;
|
|
790
|
+
return typeof target?.closest === "function" && (Boolean(target.closest(ESCAPE_OWNER_SELECTOR)) || target.getAttribute("aria-expanded") === "true");
|
|
791
|
+
}
|
|
755
792
|
var DataTable = class _DataTable {
|
|
756
793
|
static getInstance(element) {
|
|
757
794
|
return element?.nodeType === 1 ? instances3.get(element) || null : null;
|
|
@@ -1201,7 +1238,7 @@ var DataTable = class _DataTable {
|
|
|
1201
1238
|
);
|
|
1202
1239
|
}
|
|
1203
1240
|
_sortValue(row, key) {
|
|
1204
|
-
const cell = row.element.querySelector(`:scope > [data-datatable-column="${key}"]`);
|
|
1241
|
+
const cell = row.element.querySelector(`:scope > [data-datatable-column="${CSS.escape(String(key))}"]`);
|
|
1205
1242
|
if (!cell) {
|
|
1206
1243
|
return "";
|
|
1207
1244
|
}
|
|
@@ -1228,10 +1265,8 @@ var DataTable = class _DataTable {
|
|
|
1228
1265
|
filtered.sort((a, b) => {
|
|
1229
1266
|
const aValue = this._sortValue(a, this._sortKey);
|
|
1230
1267
|
const bValue = this._sortValue(b, this._sortKey);
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
}
|
|
1234
|
-
return aValue > bValue ? direction : -direction;
|
|
1268
|
+
const order = compareSortValues(aValue, bValue);
|
|
1269
|
+
return order ? order * direction : a.index - b.index;
|
|
1235
1270
|
});
|
|
1236
1271
|
} else {
|
|
1237
1272
|
filtered.sort((a, b) => a.index - b.index);
|
|
@@ -1298,7 +1333,7 @@ var DataTable = class _DataTable {
|
|
|
1298
1333
|
summary.appendChild(this._createFacetBadge(`${selected.size} selected`));
|
|
1299
1334
|
} else {
|
|
1300
1335
|
selected.forEach((value) => {
|
|
1301
|
-
const option = facetRoot.querySelector(`[data-datatable-facet-option="${value}"]`);
|
|
1336
|
+
const option = facetRoot.querySelector(`[data-datatable-facet-option="${CSS.escape(String(value))}"]`);
|
|
1302
1337
|
const label = option?.querySelector("[data-datatable-facet-option-label]")?.textContent ?? value;
|
|
1303
1338
|
summary.appendChild(this._createFacetBadge(label));
|
|
1304
1339
|
});
|
|
@@ -1527,17 +1562,17 @@ var DataTable = class _DataTable {
|
|
|
1527
1562
|
return withEllipsis;
|
|
1528
1563
|
}
|
|
1529
1564
|
_isColumnFixed(key) {
|
|
1530
|
-
return this._element.querySelector(`th[data-datatable-column="${key}"]`)?.hasAttribute("data-datatable-column-fixed") || false;
|
|
1565
|
+
return this._element.querySelector(`th[data-datatable-column="${CSS.escape(String(key))}"]`)?.hasAttribute("data-datatable-column-fixed") || false;
|
|
1531
1566
|
}
|
|
1532
1567
|
_setColumnVisible(key, visible, { syncViewToggle = true } = {}) {
|
|
1533
1568
|
if (!visible && this._isColumnFixed(key)) {
|
|
1534
1569
|
return;
|
|
1535
1570
|
}
|
|
1536
|
-
this._element.querySelectorAll(`[data-datatable-column="${key}"], [data-datatable-detail-column="${key}"]`).forEach((cell) => {
|
|
1571
|
+
this._element.querySelectorAll(`[data-datatable-column="${CSS.escape(String(key))}"], [data-datatable-detail-column="${CSS.escape(String(key))}"]`).forEach((cell) => {
|
|
1537
1572
|
cell.classList.toggle("datatable-col-hidden", !visible);
|
|
1538
1573
|
});
|
|
1539
1574
|
if (syncViewToggle) {
|
|
1540
|
-
this._element.querySelectorAll(`[data-datatable-column-toggle="${key}"]`).forEach((toggle) => {
|
|
1575
|
+
this._element.querySelectorAll(`[data-datatable-column-toggle="${CSS.escape(String(key))}"]`).forEach((toggle) => {
|
|
1541
1576
|
toggle.classList.toggle("active", visible);
|
|
1542
1577
|
toggle.setAttribute("aria-pressed", String(visible));
|
|
1543
1578
|
});
|
|
@@ -1674,8 +1709,8 @@ var DataTable = class _DataTable {
|
|
|
1674
1709
|
if (group) {
|
|
1675
1710
|
const key = group.dataset.datatableFilterGroup;
|
|
1676
1711
|
this._showFilterPickerPanel(key);
|
|
1677
|
-
const search = this._element.querySelector(`[data-datatable-filter-option-search="${key}"]`);
|
|
1678
|
-
const option = this._element.querySelector(`[data-datatable-filter-option-key="${key}"]`);
|
|
1712
|
+
const search = this._element.querySelector(`[data-datatable-filter-option-search="${CSS.escape(String(key))}"]`);
|
|
1713
|
+
const option = this._element.querySelector(`[data-datatable-filter-option-key="${CSS.escape(String(key))}"]`);
|
|
1679
1714
|
(search || option)?.focus({ preventScroll: true });
|
|
1680
1715
|
return;
|
|
1681
1716
|
}
|
|
@@ -1690,7 +1725,7 @@ var DataTable = class _DataTable {
|
|
|
1690
1725
|
}
|
|
1691
1726
|
const key = search.dataset.datatableFilterOptionSearch;
|
|
1692
1727
|
const query = normalize2(search.value);
|
|
1693
|
-
this._element.querySelectorAll(`[data-datatable-filter-option-key="${key}"]`).forEach((option) => {
|
|
1728
|
+
this._element.querySelectorAll(`[data-datatable-filter-option-key="${CSS.escape(String(key))}"]`).forEach((option) => {
|
|
1694
1729
|
const label = normalize2(option.querySelector("[data-datatable-filter-option-label]")?.textContent || option.textContent);
|
|
1695
1730
|
option.closest("li")?.classList.toggle("datatable-filter-option-search-hidden", query.length > 0 && !label.includes(query));
|
|
1696
1731
|
});
|
|
@@ -1707,7 +1742,7 @@ var DataTable = class _DataTable {
|
|
|
1707
1742
|
}
|
|
1708
1743
|
const key = search.dataset.datatableFilterOptionSearch;
|
|
1709
1744
|
const options = Array.from(
|
|
1710
|
-
this._element.querySelectorAll(`[data-datatable-filter-option-key="${key}"]`)
|
|
1745
|
+
this._element.querySelectorAll(`[data-datatable-filter-option-key="${CSS.escape(String(key))}"]`)
|
|
1711
1746
|
);
|
|
1712
1747
|
const firstVisible = options.find(
|
|
1713
1748
|
(option) => !option.closest("li")?.classList.contains("datatable-filter-option-search-hidden")
|
|
@@ -1832,7 +1867,7 @@ var DataTable = class _DataTable {
|
|
|
1832
1867
|
}
|
|
1833
1868
|
row.element.setAttribute(`data-datatable-facet-${key}`, value);
|
|
1834
1869
|
row.facets[key] = value;
|
|
1835
|
-
const cell = row.element.querySelector(`[data-datatable-column="${key}"]`);
|
|
1870
|
+
const cell = row.element.querySelector(`[data-datatable-column="${CSS.escape(String(key))}"]`);
|
|
1836
1871
|
if (!cell) {
|
|
1837
1872
|
return;
|
|
1838
1873
|
}
|
|
@@ -1846,7 +1881,7 @@ var DataTable = class _DataTable {
|
|
|
1846
1881
|
if (currentIcon && icon) {
|
|
1847
1882
|
currentIcon.replaceWith(icon.cloneNode(true));
|
|
1848
1883
|
}
|
|
1849
|
-
const cardValue = row.cardElement?.querySelector(`[data-datatable-detail-column="${key}"] .datatable-card-value`);
|
|
1884
|
+
const cardValue = row.cardElement?.querySelector(`[data-datatable-detail-column="${CSS.escape(String(key))}"] .datatable-card-value`);
|
|
1850
1885
|
if (cardValue) {
|
|
1851
1886
|
cardValue.innerHTML = cell.innerHTML;
|
|
1852
1887
|
}
|
|
@@ -1940,11 +1975,11 @@ var DataTable = class _DataTable {
|
|
|
1940
1975
|
}
|
|
1941
1976
|
});
|
|
1942
1977
|
this._listen(this._document, "click", (event) => this._handleSortAction(event));
|
|
1943
|
-
this._listen(this.
|
|
1944
|
-
if (event.key === "Escape" && this._selectedIds.size > 0) {
|
|
1978
|
+
this._listen(this._window, "keydown", (event) => {
|
|
1979
|
+
if (event.key === "Escape" && this._selectedIds.size > 0 && !escapeBelongsElsewhere(event)) {
|
|
1945
1980
|
this._clearSelection();
|
|
1946
1981
|
}
|
|
1947
|
-
});
|
|
1982
|
+
}, { capture: true });
|
|
1948
1983
|
this._listen(this._element, "show.bs.dropdown", (event) => {
|
|
1949
1984
|
this._reparentRowActionMenu(event.target);
|
|
1950
1985
|
this._reparentSortMenu(event.target);
|
|
@@ -2141,6 +2176,15 @@ function dispatch(element, window2, name, detail = {}, cancelable = false) {
|
|
|
2141
2176
|
})
|
|
2142
2177
|
);
|
|
2143
2178
|
}
|
|
2179
|
+
function formDefaultValue(input) {
|
|
2180
|
+
if (!input) {
|
|
2181
|
+
return "";
|
|
2182
|
+
}
|
|
2183
|
+
if (!("datepickerFormDefault" in input.dataset)) {
|
|
2184
|
+
input.dataset.datepickerFormDefault = input.getAttribute("value") || "";
|
|
2185
|
+
}
|
|
2186
|
+
return input.dataset.datepickerFormDefault;
|
|
2187
|
+
}
|
|
2144
2188
|
function setHiddenValue(input, value) {
|
|
2145
2189
|
if (!input) return;
|
|
2146
2190
|
input.value = value || "";
|
|
@@ -2544,6 +2588,7 @@ var MooCalendar = class _MooCalendar {
|
|
|
2544
2588
|
ArrowUp: -7
|
|
2545
2589
|
};
|
|
2546
2590
|
let nextDate = null;
|
|
2591
|
+
let stepTowardActive = false;
|
|
2547
2592
|
if (event.key in keyMap) {
|
|
2548
2593
|
nextDate = addDays(this._activeDate, keyMap[event.key]);
|
|
2549
2594
|
} else if (event.key === "Home") {
|
|
@@ -2554,10 +2599,13 @@ var MooCalendar = class _MooCalendar {
|
|
|
2554
2599
|
const weekStart = firstDayOfWeek(this._config.locale);
|
|
2555
2600
|
const weekdayOffset = (this._activeDate.getDay() - weekStart + 7) % 7;
|
|
2556
2601
|
nextDate = addDays(this._activeDate, 6 - weekdayOffset);
|
|
2557
|
-
} else if (event.key === "PageUp") {
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
nextDate =
|
|
2602
|
+
} else if (event.key === "PageUp" || event.key === "PageDown") {
|
|
2603
|
+
const target = addMonths(this._activeDate, event.key === "PageUp" ? -1 : 1);
|
|
2604
|
+
const { minDate, maxDate } = this._config;
|
|
2605
|
+
nextDate = limitToRange(target, minDate, maxDate);
|
|
2606
|
+
stepTowardActive = [minDate, maxDate].some(
|
|
2607
|
+
(edge) => edge && compareDates(nextDate, edge) === 0
|
|
2608
|
+
);
|
|
2561
2609
|
} else if (event.key === "Enter" || event.key === " " || event.key === "Spacebar") {
|
|
2562
2610
|
event.preventDefault();
|
|
2563
2611
|
this._selectDate(this._activeDate, true);
|
|
@@ -2566,7 +2614,8 @@ var MooCalendar = class _MooCalendar {
|
|
|
2566
2614
|
return;
|
|
2567
2615
|
}
|
|
2568
2616
|
event.preventDefault();
|
|
2569
|
-
|
|
2617
|
+
const backward = compareDates(nextDate, this._activeDate) < 0;
|
|
2618
|
+
this._setActiveDate(nextDate, true, backward !== stepTowardActive ? -1 : 1);
|
|
2570
2619
|
}
|
|
2571
2620
|
_setActiveDate(date, focus = false, step = 0) {
|
|
2572
2621
|
const nextDate = step === 0 ? this._nearestEnabledDate(date) : this._nextEnabledDate(date, step);
|
|
@@ -2832,7 +2881,7 @@ var MooDatepicker = class _MooDatepicker {
|
|
|
2832
2881
|
value: config.value || this._input.value || element.dataset.datepickerValue
|
|
2833
2882
|
});
|
|
2834
2883
|
this._placeholder = this._config.placeholder;
|
|
2835
|
-
this._defaultValue = this._input
|
|
2884
|
+
this._defaultValue = formDefaultValue(this._input);
|
|
2836
2885
|
this.calendar = MooCalendar.getOrCreateInstance(this._calendarRoot, {
|
|
2837
2886
|
...this._config,
|
|
2838
2887
|
mode: "single",
|
|
@@ -2997,8 +3046,8 @@ var MooDateRangePicker = class _MooDateRangePicker {
|
|
|
2997
3046
|
endValue: config.endValue || this._endInput.value || element.dataset.datepickerEndValue
|
|
2998
3047
|
});
|
|
2999
3048
|
this._placeholder = this._config.placeholder;
|
|
3000
|
-
this._defaultStartValue = this._startInput
|
|
3001
|
-
this._defaultEndValue = this._endInput
|
|
3049
|
+
this._defaultStartValue = formDefaultValue(this._startInput);
|
|
3050
|
+
this._defaultEndValue = formDefaultValue(this._endInput);
|
|
3002
3051
|
this.calendar = MooCalendar.getOrCreateInstance(this._calendarRoot, this._config);
|
|
3003
3052
|
rangePickerInstances.set(element, this);
|
|
3004
3053
|
this._syncFromCalendar();
|
|
@@ -3143,6 +3192,13 @@ var MooDateRangePicker = class _MooDateRangePicker {
|
|
|
3143
3192
|
|
|
3144
3193
|
// src/js/components/sidebar.js
|
|
3145
3194
|
var instances4 = /* @__PURE__ */ new WeakMap();
|
|
3195
|
+
var FLYOUT_FOCUSABLE_SELECTOR = "a[href], button, input, select, textarea, [tabindex]";
|
|
3196
|
+
function flyoutFocusables(flyout) {
|
|
3197
|
+
const view = flyout.ownerDocument.defaultView;
|
|
3198
|
+
return [...flyout.querySelectorAll(FLYOUT_FOCUSABLE_SELECTOR)].filter(
|
|
3199
|
+
(element) => !element.matches(":disabled") && element.tabIndex >= 0 && !element.closest("[hidden], [inert]") && element.getClientRects().length > 0 && view.getComputedStyle(element).visibility === "visible"
|
|
3200
|
+
);
|
|
3201
|
+
}
|
|
3146
3202
|
var Sidebar = class _Sidebar {
|
|
3147
3203
|
static getInstance(element) {
|
|
3148
3204
|
return element?.nodeType === 1 ? instances4.get(element) || null : null;
|
|
@@ -3570,7 +3626,37 @@ var Sidebar = class _Sidebar {
|
|
|
3570
3626
|
this._closeFlyouts();
|
|
3571
3627
|
} else {
|
|
3572
3628
|
this._openFlyout(item);
|
|
3629
|
+
if (event.detail === 0) {
|
|
3630
|
+
if (this._flyout) {
|
|
3631
|
+
flyoutFocusables(this._flyout)[0]?.focus();
|
|
3632
|
+
}
|
|
3633
|
+
}
|
|
3634
|
+
}
|
|
3635
|
+
}
|
|
3636
|
+
// The flyout lives in a portal, away from its trigger, so Escape and Tab
|
|
3637
|
+
// out of it return to the trigger instead of wandering. Tab between its
|
|
3638
|
+
// items stays native; a key a flyout control already handled is left alone.
|
|
3639
|
+
_handleFlyoutKeydown(event) {
|
|
3640
|
+
const owner = this._flyoutOwner;
|
|
3641
|
+
if (event.defaultPrevented || !this._flyout || !owner || !(event.key === "Escape" || event.key === "Tab")) {
|
|
3642
|
+
return false;
|
|
3643
|
+
}
|
|
3644
|
+
const inFlyout = this._flyout.contains(event.target);
|
|
3645
|
+
if (!inFlyout && !(event.key === "Escape" && owner.contains(event.target))) {
|
|
3646
|
+
return false;
|
|
3573
3647
|
}
|
|
3648
|
+
if (event.key === "Tab") {
|
|
3649
|
+
const items = flyoutFocusables(this._flyout);
|
|
3650
|
+
const edge = event.shiftKey ? items[0] : items[items.length - 1];
|
|
3651
|
+
if (edge && event.target !== edge) {
|
|
3652
|
+
return false;
|
|
3653
|
+
}
|
|
3654
|
+
}
|
|
3655
|
+
event.preventDefault();
|
|
3656
|
+
const trigger = owner.querySelector(":scope > .sidebar-menu-sub-trigger");
|
|
3657
|
+
this._closeFlyouts();
|
|
3658
|
+
trigger?.focus();
|
|
3659
|
+
return true;
|
|
3574
3660
|
}
|
|
3575
3661
|
_handleOutsideFlyoutClick(event) {
|
|
3576
3662
|
if (!this._flyout) {
|
|
@@ -3583,6 +3669,9 @@ var Sidebar = class _Sidebar {
|
|
|
3583
3669
|
}
|
|
3584
3670
|
}
|
|
3585
3671
|
_handleShortcut(event) {
|
|
3672
|
+
if (this._handleFlyoutKeydown(event)) {
|
|
3673
|
+
return;
|
|
3674
|
+
}
|
|
3586
3675
|
const target = event.target;
|
|
3587
3676
|
const isEditable = target instanceof this._window.Element && (target.matches("input, textarea, select") || target.isContentEditable);
|
|
3588
3677
|
if (event.defaultPrevented || event.isComposing || isEditable || !this._config.keyboard || !(event.metaKey || event.ctrlKey) || event.key.toLowerCase() !== "b" || !this._isDesktop()) {
|
|
@@ -4508,6 +4597,241 @@ var FloatingPanel = class _FloatingPanel {
|
|
|
4508
4597
|
}
|
|
4509
4598
|
};
|
|
4510
4599
|
|
|
4600
|
+
// src/js/components/image-gallery.js
|
|
4601
|
+
var instances9 = /* @__PURE__ */ new WeakMap();
|
|
4602
|
+
var SLIDESHOW_INTERVAL = 5e3;
|
|
4603
|
+
var RESIZE_DURATION = 250;
|
|
4604
|
+
var ImageGallery = class _ImageGallery {
|
|
4605
|
+
static getInstance(element) {
|
|
4606
|
+
return element?.nodeType === 1 ? instances9.get(element) || null : null;
|
|
4607
|
+
}
|
|
4608
|
+
static getOrCreateInstance(element) {
|
|
4609
|
+
return _ImageGallery.getInstance(element) || new _ImageGallery(element);
|
|
4610
|
+
}
|
|
4611
|
+
constructor(element) {
|
|
4612
|
+
if (element?.nodeType !== 1 || !element.matches("[data-image-gallery]")) {
|
|
4613
|
+
throw new TypeError("ImageGallery requires a [data-image-gallery] root element.");
|
|
4614
|
+
}
|
|
4615
|
+
const existing = instances9.get(element);
|
|
4616
|
+
if (existing) return existing;
|
|
4617
|
+
this._element = element;
|
|
4618
|
+
this._window = element.ownerDocument.defaultView;
|
|
4619
|
+
this._dialog = element.querySelector(".modal");
|
|
4620
|
+
this._carousel = this._dialog?.querySelector(".carousel");
|
|
4621
|
+
if (!this._dialog || !this._carousel) {
|
|
4622
|
+
throw new TypeError("ImageGallery requires a lightbox Dialog with a Carousel.");
|
|
4623
|
+
}
|
|
4624
|
+
this._slides = Array.from(this._carousel.querySelectorAll(".carousel-item"));
|
|
4625
|
+
this._counter = this._dialog.querySelector("[data-gallery-counter]");
|
|
4626
|
+
this._caption = this._dialog.querySelector("[data-gallery-caption]");
|
|
4627
|
+
this._credit = this._dialog.querySelector("[data-gallery-credit]");
|
|
4628
|
+
this._stage = this._dialog.querySelector("[data-gallery-stage]");
|
|
4629
|
+
this._frame = this._dialog.querySelector(".modal-dialog");
|
|
4630
|
+
this._progress = this._dialog.querySelector(".progress");
|
|
4631
|
+
this._progressBar = this._progress?.querySelector(".progress-bar");
|
|
4632
|
+
this._progress?.setAttribute("aria-hidden", "true");
|
|
4633
|
+
const controls = (suffix) => this._dialog.id ? element.ownerDocument.getElementById(`${this._dialog.id}-${suffix}`) : null;
|
|
4634
|
+
this._enterButton = controls("fullscreen");
|
|
4635
|
+
this._exitButton = controls("windowed");
|
|
4636
|
+
this._playButton = controls("play");
|
|
4637
|
+
this._pauseButton = controls("pause");
|
|
4638
|
+
this._listeners = [];
|
|
4639
|
+
this._listen(this._dialog, "show.bs.modal", (event) => this._onShow(event));
|
|
4640
|
+
this._listen(this._window, "resize", () => this._layoutStage());
|
|
4641
|
+
this._listen(this._dialog, "shown.bs.modal", () => this._carouselInstance());
|
|
4642
|
+
this._listen(this._carousel, "slide.bs.carousel", (event) => this.show(event.to, false));
|
|
4643
|
+
this._listen(this._carousel, "slid.bs.carousel", () => this._restartTimer());
|
|
4644
|
+
this._listen(this._dialog, "keydown", (event) => this._onKeydown(event));
|
|
4645
|
+
this._listen(this._dialog, "hidden.bs.modal", () => {
|
|
4646
|
+
this.play(false);
|
|
4647
|
+
this.fullscreen(false);
|
|
4648
|
+
});
|
|
4649
|
+
const actions = [
|
|
4650
|
+
[controls("previous"), () => this._step(false)],
|
|
4651
|
+
[controls("next"), () => this._step(true)],
|
|
4652
|
+
[this._playButton, () => this.play(true, true)],
|
|
4653
|
+
[this._pauseButton, () => this.play(false, true)],
|
|
4654
|
+
[this._enterButton, () => this.fullscreen(true, true)],
|
|
4655
|
+
[this._exitButton, () => this.fullscreen(false, true)]
|
|
4656
|
+
];
|
|
4657
|
+
for (const [button, action] of actions) {
|
|
4658
|
+
if (button) this._listen(button, "click", action);
|
|
4659
|
+
}
|
|
4660
|
+
instances9.set(element, this);
|
|
4661
|
+
}
|
|
4662
|
+
get index() {
|
|
4663
|
+
const active = this._slides.findIndex((slide) => slide.classList.contains("active"));
|
|
4664
|
+
return Math.max(active, 0);
|
|
4665
|
+
}
|
|
4666
|
+
/** Marks photo `index` current, loads it and its neighbours. */
|
|
4667
|
+
show(index, activate = true) {
|
|
4668
|
+
const count = this._slides.length;
|
|
4669
|
+
if (!count) return this;
|
|
4670
|
+
const current = (Number(index) % count + count) % count;
|
|
4671
|
+
if (activate) {
|
|
4672
|
+
this._slides.forEach((slide2, position) => {
|
|
4673
|
+
slide2.classList.toggle("active", position === current);
|
|
4674
|
+
});
|
|
4675
|
+
}
|
|
4676
|
+
for (const offset of [0, 1, -1]) {
|
|
4677
|
+
this._load(this._slides[(current + offset + count) % count]);
|
|
4678
|
+
}
|
|
4679
|
+
const slide = this._slides[current];
|
|
4680
|
+
if (this._caption) {
|
|
4681
|
+
this._caption.textContent = slide.getAttribute("data-gallery-slide-title") || slide.querySelector("img")?.alt || "";
|
|
4682
|
+
}
|
|
4683
|
+
if (this._credit) {
|
|
4684
|
+
const credit = slide.getAttribute("data-gallery-slide-credit") || "";
|
|
4685
|
+
this._credit.textContent = credit;
|
|
4686
|
+
this._credit.classList.toggle("d-none", !credit);
|
|
4687
|
+
}
|
|
4688
|
+
if (this._counter) {
|
|
4689
|
+
const template = this._counter.getAttribute("data-gallery-counter") || "{current} / {total}";
|
|
4690
|
+
this._counter.textContent = template.replaceAll("{current}", String(current + 1)).replaceAll("{total}", String(count));
|
|
4691
|
+
}
|
|
4692
|
+
return this;
|
|
4693
|
+
}
|
|
4694
|
+
/** Starts or stops the slideshow. */
|
|
4695
|
+
play(on = true, moveFocus = false) {
|
|
4696
|
+
this._playing = Boolean(on) && Boolean(this._carouselInstance());
|
|
4697
|
+
this._progress?.classList.toggle("d-none", !this._playing);
|
|
4698
|
+
this._restartTimer();
|
|
4699
|
+
this._playButton?.classList.toggle("d-none", this._playing);
|
|
4700
|
+
this._pauseButton?.classList.toggle("d-none", !this._playing);
|
|
4701
|
+
this._counter?.setAttribute("aria-live", this._playing ? "off" : "polite");
|
|
4702
|
+
if (moveFocus) (this._playing ? this._pauseButton : this._playButton)?.focus();
|
|
4703
|
+
return this;
|
|
4704
|
+
}
|
|
4705
|
+
get isFullscreen() {
|
|
4706
|
+
return Boolean(this._frame?.classList.contains("modal-fullscreen"));
|
|
4707
|
+
}
|
|
4708
|
+
/** Switches between the large dialog and full screen. */
|
|
4709
|
+
fullscreen(on = true, moveFocus = false) {
|
|
4710
|
+
if (!this._frame) return this;
|
|
4711
|
+
const value = Boolean(on);
|
|
4712
|
+
this._resize(() => {
|
|
4713
|
+
this._frame.classList.toggle("modal-fullscreen", value);
|
|
4714
|
+
this._layoutStage();
|
|
4715
|
+
});
|
|
4716
|
+
if (moveFocus) (value ? this._exitButton : this._enterButton)?.focus();
|
|
4717
|
+
return this;
|
|
4718
|
+
}
|
|
4719
|
+
dispose() {
|
|
4720
|
+
this._stopTimer();
|
|
4721
|
+
for (const [target, type, listener] of this._listeners) {
|
|
4722
|
+
target.removeEventListener(type, listener);
|
|
4723
|
+
}
|
|
4724
|
+
this._listeners = [];
|
|
4725
|
+
instances9.delete(this._element);
|
|
4726
|
+
}
|
|
4727
|
+
_listen(target, type, listener) {
|
|
4728
|
+
target.addEventListener(type, listener);
|
|
4729
|
+
this._listeners.push([target, type, listener]);
|
|
4730
|
+
}
|
|
4731
|
+
// The dialog fills the screen after the full-screen button, or below the
|
|
4732
|
+
// breakpoint of Bootstrap's .modal-fullscreen-{breakpoint}-down.
|
|
4733
|
+
_fillsScreen() {
|
|
4734
|
+
if (this.isFullscreen) return true;
|
|
4735
|
+
const breakpoint = this._frame?.className.match(/\bmodal-fullscreen-(\w+)-down\b/)?.[1];
|
|
4736
|
+
if (!breakpoint) return false;
|
|
4737
|
+
const width = Number.parseFloat(this._window.getComputedStyle(this._element.ownerDocument.documentElement).getPropertyValue(`--bs-breakpoint-${breakpoint}`));
|
|
4738
|
+
return Number.isFinite(width) && this._window.matchMedia(`(max-width: ${width - 0.02}px)`).matches;
|
|
4739
|
+
}
|
|
4740
|
+
// Runs one photo's time on the Progress bar, then shows the next photo.
|
|
4741
|
+
_restartTimer() {
|
|
4742
|
+
this._stopTimer();
|
|
4743
|
+
if (!this._playing) return;
|
|
4744
|
+
const advance = () => {
|
|
4745
|
+
if (this._playing) this._step(true);
|
|
4746
|
+
};
|
|
4747
|
+
if (typeof this._progressBar?.animate === "function") {
|
|
4748
|
+
const timer = this._progressBar.animate(
|
|
4749
|
+
[{ width: "0%" }, { width: "100%" }],
|
|
4750
|
+
{ duration: SLIDESHOW_INTERVAL, easing: "linear", fill: "forwards" }
|
|
4751
|
+
);
|
|
4752
|
+
timer.addEventListener("finish", () => {
|
|
4753
|
+
if (this._timer === timer) advance();
|
|
4754
|
+
});
|
|
4755
|
+
this._timer = timer;
|
|
4756
|
+
} else {
|
|
4757
|
+
this._timeout = this._window.setTimeout(advance, SLIDESHOW_INTERVAL);
|
|
4758
|
+
}
|
|
4759
|
+
}
|
|
4760
|
+
_stopTimer() {
|
|
4761
|
+
this._timer?.cancel();
|
|
4762
|
+
this._timer = null;
|
|
4763
|
+
this._window.clearTimeout(this._timeout);
|
|
4764
|
+
}
|
|
4765
|
+
// Animates the dialog from its old box to the new one: the content keeps
|
|
4766
|
+
// its final layout and starts scaled to where it was (first, last, invert,
|
|
4767
|
+
// play). Without motion the change applies at once.
|
|
4768
|
+
_resize(change) {
|
|
4769
|
+
const content = this._frame.querySelector(".modal-content");
|
|
4770
|
+
const reduced = this._window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
|
|
4771
|
+
if (!content || typeof content.animate !== "function" || reduced || !this._dialog.classList.contains("show")) {
|
|
4772
|
+
change();
|
|
4773
|
+
return;
|
|
4774
|
+
}
|
|
4775
|
+
this._resizing?.cancel();
|
|
4776
|
+
const first = content.getBoundingClientRect();
|
|
4777
|
+
change();
|
|
4778
|
+
const last = content.getBoundingClientRect();
|
|
4779
|
+
if (!first.width || !first.height || !last.width || !last.height) return;
|
|
4780
|
+
this._resizing = content.animate([
|
|
4781
|
+
{
|
|
4782
|
+
transformOrigin: "0 0",
|
|
4783
|
+
transform: `translate(${first.left - last.left}px, ${first.top - last.top}px) scale(${first.width / last.width}, ${first.height / last.height})`
|
|
4784
|
+
},
|
|
4785
|
+
{ transformOrigin: "0 0", transform: "none" }
|
|
4786
|
+
], { duration: RESIZE_DURATION, easing: "cubic-bezier(0.2, 0, 0, 1)" });
|
|
4787
|
+
}
|
|
4788
|
+
_layoutStage() {
|
|
4789
|
+
const fill = this._fillsScreen();
|
|
4790
|
+
this._stage?.classList.toggle("gallery-stage", !fill);
|
|
4791
|
+
this._enterButton?.classList.toggle("d-none", fill);
|
|
4792
|
+
this._exitButton?.classList.toggle("d-none", !this.isFullscreen);
|
|
4793
|
+
}
|
|
4794
|
+
_onShow(event) {
|
|
4795
|
+
this._layoutStage();
|
|
4796
|
+
const trigger = event.relatedTarget;
|
|
4797
|
+
const value = trigger?.closest?.("[data-gallery-index]")?.getAttribute("data-gallery-index");
|
|
4798
|
+
this.show(value === null || value === void 0 ? 0 : Number.parseInt(value, 10) || 0);
|
|
4799
|
+
}
|
|
4800
|
+
_onKeydown(event) {
|
|
4801
|
+
if (!["ArrowLeft", "ArrowRight"].includes(event.key)) return;
|
|
4802
|
+
if (this._carousel.contains(event.target)) return;
|
|
4803
|
+
event.preventDefault();
|
|
4804
|
+
const rtl = this._element.ownerDocument.documentElement.dir === "rtl";
|
|
4805
|
+
this._step(event.key === "ArrowRight" !== rtl);
|
|
4806
|
+
}
|
|
4807
|
+
_step(forward) {
|
|
4808
|
+
const carousel = this._carouselInstance();
|
|
4809
|
+
if (!carousel) return;
|
|
4810
|
+
if (forward) carousel.next();
|
|
4811
|
+
else carousel.prev();
|
|
4812
|
+
}
|
|
4813
|
+
_carouselInstance(create = true) {
|
|
4814
|
+
const Carousel = this._window.bootstrap?.Carousel;
|
|
4815
|
+
if (!Carousel) return null;
|
|
4816
|
+
if (!create) return Carousel.getInstance(this._carousel);
|
|
4817
|
+
return Carousel.getOrCreateInstance(this._carousel, {
|
|
4818
|
+
interval: false,
|
|
4819
|
+
pause: false,
|
|
4820
|
+
ride: false,
|
|
4821
|
+
keyboard: true,
|
|
4822
|
+
touch: true,
|
|
4823
|
+
wrap: true
|
|
4824
|
+
});
|
|
4825
|
+
}
|
|
4826
|
+
_load(slide) {
|
|
4827
|
+
const image = slide?.querySelector("img[data-gallery-src]");
|
|
4828
|
+
if (!image || image.hasAttribute("src")) return;
|
|
4829
|
+
const srcset = image.getAttribute("data-gallery-srcset");
|
|
4830
|
+
if (srcset) image.setAttribute("srcset", srcset);
|
|
4831
|
+
image.setAttribute("src", image.getAttribute("data-gallery-src"));
|
|
4832
|
+
}
|
|
4833
|
+
};
|
|
4834
|
+
|
|
4511
4835
|
// src/js/moo-ui.js
|
|
4512
4836
|
import PasswordRequirements from "./password-requirements.js";
|
|
4513
4837
|
async function loadChart() {
|
|
@@ -4517,6 +4841,7 @@ async function loadChart() {
|
|
|
4517
4841
|
var MooUI = {
|
|
4518
4842
|
PasswordRequirements,
|
|
4519
4843
|
FloatingPanel,
|
|
4844
|
+
ImageGallery,
|
|
4520
4845
|
loadChart,
|
|
4521
4846
|
Combobox,
|
|
4522
4847
|
ContextMenu,
|
|
@@ -4537,6 +4862,7 @@ export {
|
|
|
4537
4862
|
DataTable,
|
|
4538
4863
|
MooDatepicker as Datepicker,
|
|
4539
4864
|
FloatingPanel,
|
|
4865
|
+
ImageGallery,
|
|
4540
4866
|
MooCalendar,
|
|
4541
4867
|
MooDateRangePicker,
|
|
4542
4868
|
OverlayScrollbar,
|