@wpmoo/ui 0.9.0 → 1.0.0-rc.10

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.
Files changed (99) hide show
  1. package/ASSET_LICENSE.md +4 -2
  2. package/LICENSE +28 -0
  3. package/README.md +105 -100
  4. package/certification.json +25 -3
  5. package/dist/assets/css/moo-ui.css +14005 -12457
  6. package/dist/assets/css/moo-ui.min.css +10 -28
  7. package/dist/assets/css/moo.css +2606 -902
  8. package/dist/assets/css/moo.min.css +10 -29
  9. package/dist/js/chart.js +15312 -0
  10. package/dist/js/chart.min.js +27 -0
  11. package/dist/js/combobox.js +129 -85
  12. package/dist/js/context-menu.js +35 -66
  13. package/dist/js/datatable.js +390 -188
  14. package/dist/js/datepicker.js +1197 -0
  15. package/dist/js/datepicker.min.js +7 -0
  16. package/dist/js/moo-ui.js +3947 -0
  17. package/dist/js/moo-ui.min.js +7 -0
  18. package/dist/js/sheet.js +46 -0
  19. package/dist/js/sidebar.js +190 -172
  20. package/dist/js/slider.js +289 -0
  21. package/dist/js/state.js +76 -0
  22. package/dist/release-manifest.json +24 -0
  23. package/package.json +39 -9
  24. package/scss/_components.scss +70 -0
  25. package/scss/_config.scss +18 -0
  26. package/scss/_settings.scss +5 -0
  27. package/scss/components/_accordion.scss +54 -0
  28. package/scss/components/_alert.scss +82 -0
  29. package/scss/components/_avatar.scss +139 -0
  30. package/scss/components/_badge.scss +34 -0
  31. package/scss/components/_breadcrumb.scss +119 -0
  32. package/scss/components/_button.scss +200 -0
  33. package/scss/components/_button_group.scss +57 -0
  34. package/scss/components/_card.scss +114 -0
  35. package/scss/components/_checkbox.scss +78 -0
  36. package/scss/components/_close_button.scss +56 -0
  37. package/scss/components/_collapsible.scss +44 -0
  38. package/scss/components/_combobox.scss +253 -0
  39. package/scss/components/_context-menu.scss +18 -0
  40. package/scss/components/_datatable.scss +1067 -0
  41. package/scss/components/_datepicker.scss +237 -0
  42. package/scss/components/_dialog.scss +69 -0
  43. package/scss/components/_dropdown.scss +201 -0
  44. package/scss/components/_field.scss +94 -0
  45. package/scss/components/_input.scss +31 -0
  46. package/scss/components/_input_group.scss +151 -0
  47. package/scss/components/_kbd.scss +11 -0
  48. package/scss/components/_menubar.scss +17 -0
  49. package/scss/components/_navigation.scss +36 -0
  50. package/scss/components/_pagination.scss +58 -0
  51. package/scss/components/_popover.scss +26 -0
  52. package/scss/components/_progress.scss +5 -0
  53. package/scss/components/_radio_group.scss +108 -0
  54. package/scss/components/_search_trigger.scss +61 -0
  55. package/scss/components/_select.scss +8 -0
  56. package/scss/components/_separator.scss +10 -0
  57. package/scss/components/_sheet.scss +16 -0
  58. package/scss/components/_sidebar.scss +5 -0
  59. package/scss/components/_skeleton.scss +10 -0
  60. package/scss/components/_slider.scss +213 -0
  61. package/scss/components/_spinner.scss +11 -0
  62. package/scss/components/_switch.scss +30 -0
  63. package/scss/components/_table.scss +12 -0
  64. package/scss/components/_tabs.scss +116 -0
  65. package/scss/components/_textarea.scss +8 -0
  66. package/scss/components/_toast.scss +116 -0
  67. package/scss/components/_toggle_group.scss +17 -0
  68. package/scss/components/_tooltip.scss +64 -0
  69. package/scss/components/sidebar/_base.scss +86 -0
  70. package/scss/components/sidebar/_collapsed.scss +112 -0
  71. package/scss/components/sidebar/_identity.scss +71 -0
  72. package/scss/components/sidebar/_menus.scss +219 -0
  73. package/scss/foundations/_backdrop.scss +102 -0
  74. package/scss/foundations/_focus.scss +83 -0
  75. package/scss/foundations/_globals.scss +5 -0
  76. package/scss/foundations/_scope.scss +26 -0
  77. package/scss/layouts/_app.scss +75 -0
  78. package/scss/layouts/_page_grid.scss +27 -0
  79. package/scss/layouts/app/sidebar/_base.scss +84 -0
  80. package/scss/layouts/app/sidebar/_default.scss +20 -0
  81. package/scss/layouts/app/sidebar/_floating.scss +36 -0
  82. package/scss/layouts/app/sidebar/_inset.scss +32 -0
  83. package/scss/mixins/_banner.scss +9 -0
  84. package/scss/moo-core.scss +16 -0
  85. package/scss/moo-ui.scss +54 -0
  86. package/scss/settings/_bootstrap_overrides.scss +7 -0
  87. package/scss/settings/_component_variables.scss +244 -0
  88. package/scss/settings/_forms.scss +28 -0
  89. package/scss/settings/_options.scss +2 -0
  90. package/scss/settings/_palette.scss +90 -0
  91. package/scss/themes/_forms.scss +15 -0
  92. package/scss/themes/_root.scss +20 -0
  93. package/scss/themes/_standalone.scss +3 -0
  94. package/scss/themes/_theme.scss +244 -0
  95. package/scss/utilities/_background_color.scss +18 -0
  96. package/scss/utilities/_scroll_fade.scss +193 -0
  97. package/scss/utilities/_scroll_fade_primitives.scss +43 -0
  98. package/scss/_facade-settings.scss +0 -51
  99. package/scss/settings/_facade_public.scss +0 -23
@@ -1,14 +1,17 @@
1
- const instances = new WeakMap();
1
+ /*!
2
+ * Moo UI context-menu.js v1.0.0-rc.10 (https://wpmoo.org/)
3
+ * Copyright 2026 WPMoo Authors
4
+ * Licensed under MIT (https://github.com/wpmoo-org/ui/blob/main/LICENSE)
5
+ */
2
6
 
7
+ const instances = /* @__PURE__ */ new WeakMap();
3
8
  function pointerRect(event) {
4
9
  const { clientX: x, clientY: y } = event;
5
10
  return { top: y, left: x, right: x, bottom: y, width: 0, height: 0, x, y };
6
11
  }
7
-
8
12
  function isDisabledTrigger(trigger) {
9
13
  return Boolean(trigger?.disabled || trigger?.getAttribute?.("aria-disabled") === "true");
10
14
  }
11
-
12
15
  function menuItemFromEvent(event) {
13
16
  const target = event.target;
14
17
  const item = target.closest?.(".dropdown-item");
@@ -17,16 +20,13 @@ function menuItemFromEvent(event) {
17
20
  }
18
21
  return target.closest?.("li")?.querySelector(":scope > .dropdown-item") || null;
19
22
  }
20
-
21
- export default class ContextMenu {
23
+ class ContextMenu {
22
24
  static getInstance(element) {
23
25
  return element?.nodeType === 1 ? instances.get(element) || null : null;
24
26
  }
25
-
26
27
  static getOrCreateInstance(element, config = {}) {
27
28
  return ContextMenu.getInstance(element) || new ContextMenu(element, config);
28
29
  }
29
-
30
30
  constructor(element, config = {}) {
31
31
  if (element?.nodeType !== 1 || !element.matches(".context-menu")) {
32
32
  throw new TypeError("ContextMenu requires a .context-menu root element.");
@@ -35,7 +35,6 @@ export default class ContextMenu {
35
35
  if (existing) {
36
36
  return existing;
37
37
  }
38
-
39
38
  this._element = element;
40
39
  this._document = element.ownerDocument;
41
40
  this._window = this._document.defaultView;
@@ -47,12 +46,10 @@ export default class ContextMenu {
47
46
  if (!this._fallback) {
48
47
  throw new TypeError("ContextMenu requires an explicit fallback trigger.");
49
48
  }
50
-
51
49
  const Dropdown = this._bootstrap("Dropdown");
52
50
  if (!Dropdown) {
53
51
  throw new TypeError("ContextMenu requires bootstrap.Dropdown (bootstrap.bundle.js).");
54
52
  }
55
-
56
53
  this._config = { ...config };
57
54
  this._listeners = [];
58
55
  this._invoker = null;
@@ -60,17 +57,15 @@ export default class ContextMenu {
60
57
  this._openMethod = null;
61
58
  this._reference = {
62
59
  getBoundingClientRect: () => this._currentRect(),
63
- contextElement: this._fallback,
60
+ contextElement: this._fallback
64
61
  };
65
62
  this._dropdown = new Dropdown(this._fallback, {
66
63
  reference: this._reference,
67
- autoClose: "outside",
64
+ autoClose: "outside"
68
65
  });
69
-
70
66
  instances.set(element, this);
71
67
  this._bindEvents();
72
68
  }
73
-
74
69
  dispose() {
75
70
  this._listeners.forEach(({ target, type, handler, options }) => {
76
71
  target.removeEventListener(type, handler, options);
@@ -82,56 +77,46 @@ export default class ContextMenu {
82
77
  this._pointerRect = null;
83
78
  instances.delete(this._element);
84
79
  }
85
-
86
80
  show(anchor = this._fallback) {
87
81
  this._open(anchor, null);
88
82
  }
89
-
90
83
  hide() {
91
84
  this._dropdown.hide();
92
85
  }
93
-
94
86
  _listen(target, type, handler, options) {
95
87
  target.addEventListener(type, handler, options);
96
88
  this._listeners.push({ target, type, handler, options });
97
89
  }
98
-
99
90
  _bootstrap(name) {
100
91
  return this._window.bootstrap?.[name] || null;
101
92
  }
102
-
103
93
  _currentRect() {
104
94
  if (this._pointerRect) {
105
95
  return this._pointerRect;
106
96
  }
107
97
  return (this._invoker || this._fallback).getBoundingClientRect();
108
98
  }
109
-
110
99
  _trigger(name, cancelable = false) {
111
100
  return this._element.dispatchEvent(
112
101
  new this._window.CustomEvent(`${name}.moo.context-menu`, {
113
102
  bubbles: true,
114
- cancelable,
103
+ cancelable
115
104
  })
116
105
  );
117
106
  }
118
-
119
107
  _triggers() {
120
108
  return Array.from(this._element.querySelectorAll(":scope > .context-menu-trigger"));
121
109
  }
122
-
123
110
  _syncExpanded(expanded) {
124
111
  this._triggers().forEach((trigger) => {
125
112
  trigger.setAttribute("aria-expanded", String(expanded));
126
113
  });
127
114
  }
128
-
129
115
  _menuItems() {
130
116
  return Array.from(this._menu.querySelectorAll(".dropdown-item")).filter(
131
117
  (item) => !isDisabledTrigger(item) && !item.classList.contains("disabled")
132
118
  );
133
119
  }
134
-
135
120
  _open(trigger, pointerEvent, openMethod) {
136
121
  if (isDisabledTrigger(trigger)) {
137
122
  return;
@@ -144,7 +129,6 @@ export default class ContextMenu {
144
129
  this._openMethod = openMethod;
145
130
  this._dropdown.show();
146
131
  }
147
-
148
132
  _handleContextMenu(event) {
149
133
  const trigger = event.target.closest(".context-menu-trigger");
150
134
  if (!trigger || !this._element.contains(trigger)) {
@@ -153,7 +137,6 @@ export default class ContextMenu {
153
137
  event.preventDefault();
154
138
  this._open(trigger, event, "pointer");
155
139
  }
156
-
157
140
  _handleKeydown(event) {
158
141
  const trigger = event.target.closest(".context-menu-trigger");
159
142
  if (!trigger || !this._element.contains(trigger)) {
@@ -167,44 +150,45 @@ export default class ContextMenu {
167
150
  event.preventDefault();
168
151
  this._open(trigger, null, "keyboard");
169
152
  }
170
-
171
153
  _handleFallbackClick() {
172
154
  this._invoker = this._fallback;
173
155
  this._pointerRect = null;
174
156
  this._openMethod = "pointer";
175
157
  }
176
-
158
+ _handleFallbackKeydown(event) {
159
+ if (event.key !== " " && event.key !== "Spacebar") {
160
+ return;
161
+ }
162
+ event.preventDefault();
163
+ event.stopPropagation();
164
+ if (this._menu.classList.contains("show")) {
165
+ this._dropdown.hide();
166
+ return;
167
+ }
168
+ this._open(this._fallback, null, "fallback-keyboard");
169
+ }
177
170
  _handleMenuClick(event) {
178
171
  const item = menuItemFromEvent(event);
179
172
  if (!item) {
180
173
  return;
181
174
  }
182
175
  const persistentItem = item.matches(".dropdown-item-check, [data-context-menu-persistent]");
183
- const disabledItem =
184
- item.matches('.dropdown-item.disabled, .dropdown-item[aria-disabled="true"], .dropdown-item:disabled') ||
185
- isDisabledTrigger(item);
176
+ const disabledItem = item.matches('.dropdown-item.disabled, .dropdown-item[aria-disabled="true"], .dropdown-item:disabled') || isDisabledTrigger(item);
186
177
  if (disabledItem) {
187
178
  event.preventDefault();
188
179
  event.stopPropagation();
189
180
  return;
190
181
  }
191
- // Radio-grouped toggle buttons reuse the dropdown_toggle_item() button
192
- // Bootstrap's own roving arrow-key focus already picks up (it only ever
193
- // matches real .dropdown-item elements); Moo adds only the missing
194
- // mutual-exclusivity Bootstrap's independent button-toggle plugin does
195
- // not provide.
196
182
  const radioButton = item.matches("[data-context-menu-radio-group]") ? item : null;
197
183
  if (radioButton) {
198
184
  event.preventDefault();
199
185
  event.stopPropagation();
200
186
  const group = radioButton.dataset.contextMenuRadioGroup;
201
- this._menu
202
- .querySelectorAll(`[data-context-menu-radio-group="${group}"]`)
203
- .forEach((button) => {
204
- const checked = button === radioButton;
205
- button.classList.toggle("active", checked);
206
- button.setAttribute("aria-pressed", String(checked));
207
- });
187
+ this._menu.querySelectorAll(`[data-context-menu-radio-group="${group}"]`).forEach((button) => {
188
+ const checked = button === radioButton;
189
+ button.classList.toggle("active", checked);
190
+ button.setAttribute("aria-pressed", String(checked));
191
+ });
208
192
  return;
209
193
  }
210
194
  if (persistentItem) {
@@ -212,11 +196,11 @@ export default class ContextMenu {
212
196
  }
213
197
  this._dropdown.hide();
214
198
  }
215
-
216
199
  _bindEvents() {
217
200
  this._listen(this._element, "contextmenu", (event) => this._handleContextMenu(event));
218
201
  this._listen(this._element, "keydown", (event) => this._handleKeydown(event));
219
202
  this._listen(this._fallback, "click", () => this._handleFallbackClick());
203
+ this._listen(this._fallback, "keydown", (event) => this._handleFallbackKeydown(event));
220
204
  this._listen(this._menu, "click", (event) => this._handleMenuClick(event));
221
205
  this._listen(this._fallback, "show.bs.dropdown", (event) => {
222
206
  if (!this._trigger("show", true)) {
@@ -225,13 +209,9 @@ export default class ContextMenu {
225
209
  });
226
210
  this._listen(this._fallback, "shown.bs.dropdown", () => {
227
211
  this._syncExpanded(true);
228
- // Bootstrap's own show() never focuses an item either -- only its
229
- // Up/Down-key handler does, since a pointer-driven open (right-click,
230
- // or a click on the fallback trigger) should let :hover carry the
231
- // highlight instead of a focus ring competing with it. A keyboard-
232
- // driven open (Shift+F10/ContextMenu key) still lands on the first
233
- // item, matching that same native arrow-key-open convention.
234
- if (this._openMethod === "keyboard") {
212
+ if (this._openMethod === "fallback-keyboard") {
213
+ this._fallback.focus();
214
+ } else if (this._openMethod === "keyboard") {
235
215
  this._menuItems()[0]?.focus();
236
216
  } else {
237
217
  this._menu.focus();
@@ -248,22 +228,8 @@ export default class ContextMenu {
248
228
  const invoker = this._invoker;
249
229
  this._pointerRect = null;
250
230
  this._invoker = null;
251
- // A native Bootstrap click on the fallback trigger bypasses _open()
252
- // entirely (Bootstrap's own capture-phase click-to-toggle handler
253
- // calls show() directly), so _handleFallbackClick's bubble-phase
254
- // "click" listener always runs too late to set _openMethod before
255
- // shown.bs.dropdown reads it. Resetting it here, back to the neutral
256
- // pointer default, is what keeps that path correct rather than
257
- // inheriting whatever a prior keyboard-triggered open last set.
258
231
  this._openMethod = null;
259
232
  this._trigger("hidden");
260
- // Bootstrap's own dataApiKeydownHandler (js/src/dropdown.js) intercepts
261
- // Escape in the document capture phase and hardcodes
262
- // `getToggleButton.focus()` (our fallback trigger) after hide().
263
- // Nothing downstream of a capture-phase stopPropagation() can observe
264
- // that keydown to redirect focus, so correct it in a microtask, which
265
- // still runs before the next paint: if focus landed on the fallback
266
- // but a different trigger invoked this menu, return focus there.
267
233
  queueMicrotask(() => {
268
234
  if (invoker && invoker !== this._fallback && this._document.activeElement === this._fallback) {
269
235
  invoker.focus();
@@ -272,3 +238,6 @@ export default class ContextMenu {
272
238
  });
273
239
  }
274
240
  }
241
+ export {
242
+ ContextMenu as default
243
+ };