@marianmeres/stuic 2.55.0 → 2.57.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/dist/components/Avatar/Avatar.svelte +5 -4
- package/dist/components/Avatar/Avatar.svelte.d.ts +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.svelte +54 -2
- package/dist/components/DropdownMenu/index.css +35 -1
- package/dist/utils/breakpoint.svelte.d.ts +1 -0
- package/dist/utils/breakpoint.svelte.js +9 -0
- package/package.json +1 -1
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
/** Optional string for color hash calculation (e.g., email, user ID). Falls back to `initials` */
|
|
23
23
|
hashSource?: string;
|
|
24
24
|
/** Size preset or custom Tailwind size class */
|
|
25
|
-
size?: "sm" | "md" | "lg" | "xl" | string;
|
|
25
|
+
size?: "sm" | "md" | "lg" | "xl" | "2xl" | string;
|
|
26
26
|
/** Click handler - when provided, renders as a button */
|
|
27
27
|
onclick?: (event: MouseEvent) => void;
|
|
28
28
|
/** Background color (Tailwind class). Ignored if autoColor=true */
|
|
@@ -62,9 +62,10 @@
|
|
|
62
62
|
|
|
63
63
|
const SIZE_PRESETS: Record<string, { container: string; icon: number }> = {
|
|
64
64
|
sm: { container: "size-8 text-xs", icon: 16 },
|
|
65
|
-
md: { container: "size-10 text-
|
|
66
|
-
lg: { container: "size-
|
|
67
|
-
xl: { container: "size-
|
|
65
|
+
md: { container: "size-10 text-base", icon: 20 },
|
|
66
|
+
lg: { container: "size-12 text-lg", icon: 28 },
|
|
67
|
+
xl: { container: "size-14 text-xl", icon: 32 },
|
|
68
|
+
"2xl": { container: "size-16 text-2xl", icon: 36 },
|
|
68
69
|
};
|
|
69
70
|
|
|
70
71
|
// Extract initials from input string (email, name, or raw initials)
|
|
@@ -23,7 +23,7 @@ export interface Props {
|
|
|
23
23
|
/** Optional string for color hash calculation (e.g., email, user ID). Falls back to `initials` */
|
|
24
24
|
hashSource?: string;
|
|
25
25
|
/** Size preset or custom Tailwind size class */
|
|
26
|
-
size?: "sm" | "md" | "lg" | "xl" | string;
|
|
26
|
+
size?: "sm" | "md" | "lg" | "xl" | "2xl" | string;
|
|
27
27
|
/** Click handler - when provided, renders as a button */
|
|
28
28
|
onclick?: (event: MouseEvent) => void;
|
|
29
29
|
/** Background color (Tailwind class). Ignored if autoColor=true */
|
|
@@ -271,11 +271,12 @@
|
|
|
271
271
|
import Thc from "../Thc/Thc.svelte";
|
|
272
272
|
import "./index.css";
|
|
273
273
|
import { BodyScroll } from "../../utils/body-scroll-locker.js";
|
|
274
|
+
import { waitForTwoRepaints } from "../../utils/paint.js";
|
|
274
275
|
|
|
275
276
|
let {
|
|
276
277
|
items,
|
|
277
278
|
isOpen = $bindable(false),
|
|
278
|
-
position = "bottom-span-
|
|
279
|
+
position = "bottom-span-right",
|
|
279
280
|
offset = "0.25rem",
|
|
280
281
|
maxHeight = "300px",
|
|
281
282
|
closeOnSelect = true,
|
|
@@ -314,7 +315,14 @@
|
|
|
314
315
|
let wrapperEl: HTMLDivElement = $state()!;
|
|
315
316
|
let activeItemEl: HTMLButtonElement | undefined = $state();
|
|
316
317
|
const reducedMotion = prefersReducedMotion();
|
|
317
|
-
|
|
318
|
+
|
|
319
|
+
// Runtime overflow detection state
|
|
320
|
+
let runtimeFallback = $state(false);
|
|
321
|
+
let switchingToFallback = false; // Non-reactive flag to prevent recursion
|
|
322
|
+
|
|
323
|
+
const isSupported = $derived(
|
|
324
|
+
!forceFallback && !runtimeFallback && isAnchorPositioningSupported()
|
|
325
|
+
);
|
|
318
326
|
|
|
319
327
|
// Track expanded sections (independent toggle - multiple can be open)
|
|
320
328
|
let expandedSections = $state<Set<string | number>>(new Set());
|
|
@@ -380,6 +388,50 @@
|
|
|
380
388
|
}
|
|
381
389
|
});
|
|
382
390
|
|
|
391
|
+
// Reset runtime fallback when menu closes
|
|
392
|
+
$effect(() => {
|
|
393
|
+
if (!isOpen) {
|
|
394
|
+
// Unlock body scroll if we were in runtime fallback mode
|
|
395
|
+
// (must do this before resetting runtimeFallback, otherwise isSupported
|
|
396
|
+
// becomes true and the main body scroll effect skips the unlock)
|
|
397
|
+
if (runtimeFallback && !noScrollLock) {
|
|
398
|
+
BodyScroll.unlock();
|
|
399
|
+
}
|
|
400
|
+
runtimeFallback = false;
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
// Runtime viewport overflow detection
|
|
405
|
+
$effect(() => {
|
|
406
|
+
if (!isOpen || !dropdownEl || forceFallback || runtimeFallback) return;
|
|
407
|
+
if (!isAnchorPositioningSupported()) return;
|
|
408
|
+
if (switchingToFallback) return;
|
|
409
|
+
|
|
410
|
+
const checkOverflow = async () => {
|
|
411
|
+
await waitForTwoRepaints();
|
|
412
|
+
if (!dropdownEl || !isOpen) return;
|
|
413
|
+
|
|
414
|
+
const rect = dropdownEl.getBoundingClientRect();
|
|
415
|
+
const viewportWidth = window.innerWidth;
|
|
416
|
+
const viewportHeight = window.innerHeight;
|
|
417
|
+
|
|
418
|
+
if (
|
|
419
|
+
rect.left < 0 ||
|
|
420
|
+
rect.right > viewportWidth ||
|
|
421
|
+
rect.top < 0 ||
|
|
422
|
+
rect.bottom > viewportHeight
|
|
423
|
+
) {
|
|
424
|
+
switchingToFallback = true;
|
|
425
|
+
runtimeFallback = true;
|
|
426
|
+
requestAnimationFrame(() => {
|
|
427
|
+
switchingToFallback = false;
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
checkOverflow();
|
|
433
|
+
});
|
|
434
|
+
|
|
383
435
|
// Scroll active item into view
|
|
384
436
|
$effect(() => {
|
|
385
437
|
if (isOpen && _navItems.active?.id) {
|
|
@@ -3,10 +3,44 @@
|
|
|
3
3
|
scrollbar-width: thin;
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
+
@position-try --pop-top {
|
|
7
|
+
position-area: top; /* above, centered */
|
|
8
|
+
}
|
|
9
|
+
@position-try --pop-top-span-right {
|
|
10
|
+
position-area: top span-right; /* above, aligned to anchor's left edge */
|
|
11
|
+
}
|
|
12
|
+
@position-try --pop-top-span-left {
|
|
13
|
+
position-area: top span-left; /* above, aligned to anchor's right edge */
|
|
14
|
+
}
|
|
15
|
+
@position-try --pop-bottom {
|
|
16
|
+
position-area: bottom; /* below, centered */
|
|
17
|
+
}
|
|
18
|
+
@position-try --pop-bottom-span-right {
|
|
19
|
+
position-area: bottom span-right;
|
|
20
|
+
}
|
|
21
|
+
@position-try --pop-bottom-span-left {
|
|
22
|
+
position-area: bottom span-left;
|
|
23
|
+
}
|
|
24
|
+
@position-try --pop-left {
|
|
25
|
+
position-area: left;
|
|
26
|
+
}
|
|
27
|
+
@position-try --pop-right {
|
|
28
|
+
position-area: right;
|
|
29
|
+
}
|
|
30
|
+
|
|
6
31
|
/* CSS Anchor Positioning supported mode - flip only when viewport overflow */
|
|
7
32
|
@supports (anchor-name: --anchor) {
|
|
8
33
|
.stuic-dropdown-menu-dropdown {
|
|
9
|
-
position-try-fallbacks: flip-block, flip-inline;
|
|
34
|
+
/* position-try-fallbacks: flip-block, flip-inline; */
|
|
35
|
+
|
|
36
|
+
/* position-area is set via inline style based on position param */
|
|
37
|
+
|
|
38
|
+
/* fallbacks ensure popover stays within viewport */
|
|
39
|
+
/* order: try other bottom positions first, then top, then left/right */
|
|
40
|
+
position-try-fallbacks:
|
|
41
|
+
flip-inline, --pop-bottom-span-right, --pop-bottom-span-left, --pop-bottom,
|
|
42
|
+
flip-block, --pop-top-span-right, --pop-top-span-left, --pop-top,
|
|
43
|
+
--pop-left, --pop-right;
|
|
10
44
|
}
|
|
11
45
|
}
|
|
12
46
|
|
|
@@ -29,6 +29,8 @@ const _breakpoints = [
|
|
|
29
29
|
* ```
|
|
30
30
|
*/
|
|
31
31
|
export class Breakpoint {
|
|
32
|
+
/** Singleton */
|
|
33
|
+
static #instance;
|
|
32
34
|
#bp = $derived.by(() => {
|
|
33
35
|
const w = innerWidth.current || 0;
|
|
34
36
|
return _breakpoints.reduce((m, [k, v]) => {
|
|
@@ -37,6 +39,13 @@ export class Breakpoint {
|
|
|
37
39
|
return m;
|
|
38
40
|
}, { current: null, sm: false, md: false, lg: false, xl: false, "2xl": false });
|
|
39
41
|
});
|
|
42
|
+
static get instance() {
|
|
43
|
+
// return (Breakpoint.#instance ??= new Breakpoint();) // does not work with Svelte correctly...
|
|
44
|
+
if (!Breakpoint.#instance) {
|
|
45
|
+
Breakpoint.#instance = new Breakpoint();
|
|
46
|
+
}
|
|
47
|
+
return Breakpoint.#instance;
|
|
48
|
+
}
|
|
40
49
|
get current() {
|
|
41
50
|
return this.#bp.current;
|
|
42
51
|
}
|