@pure-ds/core 0.7.41 → 0.7.43
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/dist/types/src/js/pds-core/pds-enhancers.d.ts.map +1 -1
- package/dist/types/src/js/pds-core/pds-generator.d.ts.map +1 -1
- package/package.json +1 -1
- package/public/assets/js/app.js +1 -1
- package/public/assets/js/pds-enhancers.js +1 -1
- package/public/assets/js/pds-manager.js +50 -46
- package/public/assets/pds/core/pds-enhancers.js +1 -1
- package/public/assets/pds/core/pds-manager.js +50 -46
- package/src/js/pds-core/pds-enhancers.js +89 -122
- package/src/js/pds-core/pds-generator.js +16 -12
|
@@ -61,6 +61,11 @@ function enhanceDropdown(elem) {
|
|
|
61
61
|
elem.querySelector("[data-dropdown-toggle]") ||
|
|
62
62
|
elem.querySelector("button");
|
|
63
63
|
|
|
64
|
+
const supportsPopover =
|
|
65
|
+
typeof HTMLElement !== "undefined" &&
|
|
66
|
+
"showPopover" in HTMLElement.prototype &&
|
|
67
|
+
"hidePopover" in HTMLElement.prototype;
|
|
68
|
+
|
|
64
69
|
if (trigger && !trigger.hasAttribute("type")) {
|
|
65
70
|
trigger.setAttribute("type", "button");
|
|
66
71
|
}
|
|
@@ -84,6 +89,19 @@ function enhanceDropdown(elem) {
|
|
|
84
89
|
trigger.setAttribute("aria-expanded", "false");
|
|
85
90
|
}
|
|
86
91
|
|
|
92
|
+
if (!supportsPopover) {
|
|
93
|
+
const warnKey = "__PDS_DROPDOWN_POPOVER_WARNED__";
|
|
94
|
+
if (!globalThis[warnKey]) {
|
|
95
|
+
globalThis[warnKey] = true;
|
|
96
|
+
console.warn(
|
|
97
|
+
"[PDS] nav[data-dropdown] requires the Popover API. Add a popover polyfill (recommended: @oddbird/popover-polyfill) for browsers without support.",
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
menu.setAttribute("popover", "auto");
|
|
104
|
+
|
|
87
105
|
const measureMenuSize = () => {
|
|
88
106
|
const previousStyle = menu.getAttribute("style");
|
|
89
107
|
menu.style.visibility = "hidden";
|
|
@@ -108,6 +126,24 @@ function enhanceDropdown(elem) {
|
|
|
108
126
|
return { width, height };
|
|
109
127
|
};
|
|
110
128
|
|
|
129
|
+
const isPopoverOpen = () => {
|
|
130
|
+
try {
|
|
131
|
+
return menu.matches(":popover-open");
|
|
132
|
+
} catch {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const syncClosedState = () => {
|
|
138
|
+
menu.setAttribute("aria-hidden", "true");
|
|
139
|
+
trigger?.setAttribute("aria-expanded", "false");
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const syncOpenState = () => {
|
|
143
|
+
menu.setAttribute("aria-hidden", "false");
|
|
144
|
+
trigger?.setAttribute("aria-expanded", "true");
|
|
145
|
+
};
|
|
146
|
+
|
|
111
147
|
const resolveDirection = () => {
|
|
112
148
|
const mode = (
|
|
113
149
|
elem.getAttribute("data-direction") ||
|
|
@@ -170,70 +206,24 @@ function enhanceDropdown(elem) {
|
|
|
170
206
|
};
|
|
171
207
|
|
|
172
208
|
const clearFloatingMenuPosition = () => {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
const getContainingAncestor = (node) => {
|
|
187
|
-
if (!node) return null;
|
|
188
|
-
if (node.parentElement) return node.parentElement;
|
|
189
|
-
const root = node.getRootNode?.();
|
|
190
|
-
return root instanceof ShadowRoot ? root.host : null;
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
const hasNonViewportFixedContainingBlock = () => {
|
|
194
|
-
let current = getContainingAncestor(menu);
|
|
195
|
-
while (current && current !== document.documentElement) {
|
|
196
|
-
const style = getComputedStyle(current);
|
|
197
|
-
const contain = style.contain || "";
|
|
198
|
-
const willChange = style.willChange || "";
|
|
199
|
-
const createsContainingBlock =
|
|
200
|
-
style.transform !== "none" ||
|
|
201
|
-
style.perspective !== "none" ||
|
|
202
|
-
style.filter !== "none" ||
|
|
203
|
-
style.backdropFilter !== "none" ||
|
|
204
|
-
contain.includes("paint") ||
|
|
205
|
-
contain.includes("layout") ||
|
|
206
|
-
contain.includes("strict") ||
|
|
207
|
-
contain.includes("content") ||
|
|
208
|
-
willChange.includes("transform") ||
|
|
209
|
-
willChange.includes("perspective") ||
|
|
210
|
-
willChange.includes("filter");
|
|
211
|
-
|
|
212
|
-
if (createsContainingBlock) {
|
|
213
|
-
return true;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
current = getContainingAncestor(current);
|
|
217
|
-
}
|
|
218
|
-
return false;
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
const reattachFloatingMenu = () => {
|
|
222
|
-
if (menu.getAttribute("aria-hidden") !== "false") return;
|
|
223
|
-
clearFloatingMenuPosition();
|
|
224
|
-
requestAnimationFrame(() => {
|
|
225
|
-
requestAnimationFrame(() => {
|
|
226
|
-
positionFloatingMenu();
|
|
227
|
-
});
|
|
228
|
-
});
|
|
209
|
+
[
|
|
210
|
+
"position",
|
|
211
|
+
"left",
|
|
212
|
+
"top",
|
|
213
|
+
"right",
|
|
214
|
+
"bottom",
|
|
215
|
+
"margin-top",
|
|
216
|
+
"margin-bottom",
|
|
217
|
+
"max-width",
|
|
218
|
+
"max-inline-size",
|
|
219
|
+
"max-height",
|
|
220
|
+
"overflow",
|
|
221
|
+
].forEach((prop) => menu.style.removeProperty(prop));
|
|
229
222
|
};
|
|
223
|
+
|
|
224
|
+
const positionPopoverMenu = () => {
|
|
225
|
+
if (!isPopoverOpen()) return;
|
|
230
226
|
|
|
231
|
-
const positionFloatingMenu = () => {
|
|
232
|
-
if (menu.getAttribute("aria-hidden") !== "false") return;
|
|
233
|
-
if (hasNonViewportFixedContainingBlock()) {
|
|
234
|
-
clearFloatingMenuPosition();
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
227
|
const anchorRect = (trigger || elem).getBoundingClientRect();
|
|
238
228
|
const viewport = window.visualViewport;
|
|
239
229
|
const viewportWidth =
|
|
@@ -283,19 +273,21 @@ function enhanceDropdown(elem) {
|
|
|
283
273
|
),
|
|
284
274
|
);
|
|
285
275
|
|
|
286
|
-
menu.style
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
276
|
+
Object.assign(menu.style, {
|
|
277
|
+
position: "fixed",
|
|
278
|
+
left: `${Math.round(left)}px`,
|
|
279
|
+
top: `${Math.round(top)}px`,
|
|
280
|
+
right: "auto",
|
|
281
|
+
bottom: "auto",
|
|
282
|
+
marginTop: "0",
|
|
283
|
+
marginBottom: "0",
|
|
284
|
+
});
|
|
293
285
|
};
|
|
294
286
|
|
|
295
287
|
let repositionHandler = null;
|
|
296
288
|
const bindReposition = () => {
|
|
297
289
|
if (repositionHandler) return;
|
|
298
|
-
repositionHandler = () =>
|
|
290
|
+
repositionHandler = () => positionPopoverMenu();
|
|
299
291
|
window.addEventListener("resize", repositionHandler);
|
|
300
292
|
window.addEventListener("scroll", repositionHandler, true);
|
|
301
293
|
};
|
|
@@ -312,7 +304,7 @@ function enhanceDropdown(elem) {
|
|
|
312
304
|
const bindConfigChanged = () => {
|
|
313
305
|
if (configChangedHandler || typeof document === "undefined") return;
|
|
314
306
|
configChangedHandler = () => {
|
|
315
|
-
if (
|
|
307
|
+
if (!isPopoverOpen()) return;
|
|
316
308
|
elem.dataset.dropdownDirection = resolveDirection();
|
|
317
309
|
elem.dataset.dropdownAlign = resolveAlign();
|
|
318
310
|
|
|
@@ -321,8 +313,8 @@ function enhanceDropdown(elem) {
|
|
|
321
313
|
}
|
|
322
314
|
configRepositionFrame = requestAnimationFrame(() => {
|
|
323
315
|
configRepositionFrame = null;
|
|
324
|
-
if (
|
|
325
|
-
|
|
316
|
+
if (!isPopoverOpen()) return;
|
|
317
|
+
positionPopoverMenu();
|
|
326
318
|
});
|
|
327
319
|
};
|
|
328
320
|
document.addEventListener("pds:config-changed", configChangedHandler);
|
|
@@ -338,58 +330,46 @@ function enhanceDropdown(elem) {
|
|
|
338
330
|
}
|
|
339
331
|
};
|
|
340
332
|
|
|
341
|
-
|
|
342
|
-
|
|
333
|
+
menu.addEventListener("toggle", (event) => {
|
|
334
|
+
const isOpen = event.newState === "open";
|
|
343
335
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
bindConfigChanged();
|
|
351
|
-
reattachFloatingMenu();
|
|
352
|
-
|
|
353
|
-
// Add click-outside handler when opening
|
|
354
|
-
if (!clickHandler) {
|
|
355
|
-
clickHandler = (event) => {
|
|
356
|
-
// Use composedPath() to handle Shadow DOM
|
|
357
|
-
const path = event.composedPath ? event.composedPath() : [event.target];
|
|
358
|
-
const clickedInside = path.some((node) => node === elem);
|
|
359
|
-
|
|
360
|
-
if (!clickedInside) {
|
|
361
|
-
closeMenu();
|
|
362
|
-
}
|
|
363
|
-
};
|
|
364
|
-
// Use a slight delay to avoid closing immediately if this was triggered by a click
|
|
365
|
-
setTimeout(() => {
|
|
366
|
-
document.addEventListener("click", clickHandler);
|
|
367
|
-
}, 0);
|
|
336
|
+
if (isOpen) {
|
|
337
|
+
syncOpenState();
|
|
338
|
+
positionPopoverMenu();
|
|
339
|
+
bindReposition();
|
|
340
|
+
bindConfigChanged();
|
|
341
|
+
return;
|
|
368
342
|
}
|
|
369
|
-
};
|
|
370
343
|
|
|
371
|
-
|
|
372
|
-
menu.setAttribute("aria-hidden", "true");
|
|
373
|
-
trigger?.setAttribute("aria-expanded", "false");
|
|
344
|
+
syncClosedState();
|
|
374
345
|
unbindReposition();
|
|
375
346
|
unbindConfigChanged();
|
|
376
347
|
clearFloatingMenuPosition();
|
|
348
|
+
});
|
|
377
349
|
|
|
378
|
-
|
|
379
|
-
if (
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
350
|
+
const openMenu = () => {
|
|
351
|
+
if (isPopoverOpen()) return;
|
|
352
|
+
elem.dataset.dropdownDirection = resolveDirection();
|
|
353
|
+
elem.dataset.dropdownAlign = resolveAlign();
|
|
354
|
+
menu.showPopover();
|
|
355
|
+
requestAnimationFrame(() => positionPopoverMenu());
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
const closeMenu = () => {
|
|
359
|
+
if (!isPopoverOpen()) return;
|
|
360
|
+
menu.hidePopover();
|
|
383
361
|
};
|
|
384
362
|
|
|
385
363
|
const toggleMenu = () => {
|
|
386
|
-
if (
|
|
364
|
+
if (isPopoverOpen()) {
|
|
387
365
|
closeMenu();
|
|
388
366
|
} else {
|
|
389
367
|
openMenu();
|
|
390
368
|
}
|
|
391
369
|
};
|
|
392
370
|
|
|
371
|
+
syncClosedState();
|
|
372
|
+
|
|
393
373
|
trigger?.addEventListener("click", (event) => {
|
|
394
374
|
event.preventDefault();
|
|
395
375
|
event.stopPropagation();
|
|
@@ -402,19 +382,6 @@ function enhanceDropdown(elem) {
|
|
|
402
382
|
trigger?.focus();
|
|
403
383
|
}
|
|
404
384
|
});
|
|
405
|
-
|
|
406
|
-
elem.addEventListener("focusout", (event) => {
|
|
407
|
-
// Only close if focus is explicitly moving to an element outside the dropdown
|
|
408
|
-
// Don't close if relatedTarget is null (which happens when clicking non-focusable elements inside)
|
|
409
|
-
// Use composedPath() to handle Shadow DOM properly
|
|
410
|
-
if (event.relatedTarget) {
|
|
411
|
-
const path = event.composedPath ? event.composedPath() : [event.relatedTarget];
|
|
412
|
-
const focusedInside = path.some((node) => node === elem);
|
|
413
|
-
if (!focusedInside) {
|
|
414
|
-
closeMenu();
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
});
|
|
418
385
|
}
|
|
419
386
|
|
|
420
387
|
function enhanceToggle(elem) {
|
|
@@ -3527,6 +3527,11 @@ dialog[open] {
|
|
|
3527
3527
|
animation: pds-dialog-enter var(--transition-normal) ease;
|
|
3528
3528
|
}
|
|
3529
3529
|
|
|
3530
|
+
html:has(dialog[open]:modal) {
|
|
3531
|
+
overflow: hidden;
|
|
3532
|
+
scrollbar-gutter: stable;
|
|
3533
|
+
}
|
|
3534
|
+
|
|
3530
3535
|
@keyframes pds-dialog-enter {
|
|
3531
3536
|
from {
|
|
3532
3537
|
opacity: 0;
|
|
@@ -3582,12 +3587,11 @@ dialog {
|
|
|
3582
3587
|
|
|
3583
3588
|
/*
|
|
3584
3589
|
* Overlay safety valve:
|
|
3585
|
-
* Some controls (e.g. pds-daterange panel
|
|
3590
|
+
* Some controls (e.g. pds-daterange panel) need to escape
|
|
3586
3591
|
* the dialog bounds. Scope overflow visibility to custom dialogs that contain
|
|
3587
3592
|
* those controls instead of enabling it for all dialogs.
|
|
3588
3593
|
*/
|
|
3589
|
-
dialog.dialog-custom:has(pds-daterange)
|
|
3590
|
-
dialog.dialog-custom:has([data-dropdown]) {
|
|
3594
|
+
dialog.dialog-custom:has(pds-daterange) {
|
|
3591
3595
|
overflow: visible;
|
|
3592
3596
|
}
|
|
3593
3597
|
|
|
@@ -3657,13 +3661,6 @@ dialog {
|
|
|
3657
3661
|
overflow-x: visible;
|
|
3658
3662
|
}
|
|
3659
3663
|
|
|
3660
|
-
/* Allow overlay menus (e.g. data-dropdown) to escape dialog-body clipping while open */
|
|
3661
|
-
article:has([data-dropdown] > :last-child[aria-hidden="false"]),
|
|
3662
|
-
form > article:has([data-dropdown] > :last-child[aria-hidden="false"]),
|
|
3663
|
-
.dialog-body:has([data-dropdown] > :last-child[aria-hidden="false"]) {
|
|
3664
|
-
overflow: visible;
|
|
3665
|
-
}
|
|
3666
|
-
|
|
3667
3664
|
article:has(pds-daterange),
|
|
3668
3665
|
form > article:has(pds-daterange),
|
|
3669
3666
|
.dialog-body:has(pds-daterange) {
|
|
@@ -4096,7 +4093,13 @@ nav[data-dropdown] {
|
|
|
4096
4093
|
transition-behavior: allow-discrete;
|
|
4097
4094
|
}
|
|
4098
4095
|
|
|
4099
|
-
& > :last-child[
|
|
4096
|
+
& > :last-child[popover] {
|
|
4097
|
+
inset: auto;
|
|
4098
|
+
margin: 0;
|
|
4099
|
+
}
|
|
4100
|
+
|
|
4101
|
+
& > :last-child[aria-hidden="false"],
|
|
4102
|
+
& > :last-child:popover-open {
|
|
4100
4103
|
display: inline-block;
|
|
4101
4104
|
opacity: 1;
|
|
4102
4105
|
visibility: visible;
|
|
@@ -4198,7 +4201,8 @@ nav[data-dropdown] {
|
|
|
4198
4201
|
}
|
|
4199
4202
|
|
|
4200
4203
|
@starting-style {
|
|
4201
|
-
nav[data-dropdown] > :last-child[aria-hidden="false"]
|
|
4204
|
+
nav[data-dropdown] > :last-child[aria-hidden="false"],
|
|
4205
|
+
nav[data-dropdown] > :last-child:popover-open {
|
|
4202
4206
|
opacity: 0;
|
|
4203
4207
|
}
|
|
4204
4208
|
}
|