@marianmeres/stuic 3.151.0 → 3.152.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/actions/dim-behind/dim-behind.fixture.svelte +54 -0
- package/dist/actions/dim-behind/dim-behind.fixture.svelte.d.ts +9 -0
- package/dist/actions/dim-behind/dim-behind.svelte.d.ts +10 -0
- package/dist/actions/dim-behind/dim-behind.svelte.js +72 -41
- package/dist/actions/popover/README.md +37 -17
- package/dist/actions/popover/popover.container.fixture.svelte +26 -0
- package/dist/actions/popover/popover.container.fixture.svelte.d.ts +7 -0
- package/dist/actions/popover/popover.svelte.d.ts +10 -0
- package/dist/actions/popover/popover.svelte.js +20 -7
- package/dist/actions/spotlight/spotlight.container.fixture.svelte +33 -0
- package/dist/actions/spotlight/spotlight.container.fixture.svelte.d.ts +7 -0
- package/dist/actions/spotlight/spotlight.svelte.d.ts +9 -0
- package/dist/actions/spotlight/spotlight.svelte.js +95 -37
- package/dist/components/DropdownMenu/DropdownMenu.svelte +14 -7
- package/dist/components/DropdownMenu/README.md +1 -0
- package/dist/components/Float/Float.svelte +21 -0
- package/dist/components/Float/README.md +1 -1
- package/dist/components/HoverExpandableWidth/HoverExpandableWidth.svelte +30 -5
- package/dist/utils/anchor-position.d.ts +12 -3
- package/dist/utils/anchor-position.js +35 -15
- package/dist/utils/containing-block.d.ts +55 -0
- package/dist/utils/containing-block.js +131 -0
- package/dist/utils/overlay-container.d.ts +16 -0
- package/dist/utils/overlay-container.js +12 -0
- package/package.json +12 -12
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers for working with the containing block (CB) of `position: fixed`
|
|
3
|
+
* elements.
|
|
4
|
+
*
|
|
5
|
+
* A fixed-positioned element resolves against the viewport UNLESS an ancestor
|
|
6
|
+
* establishes a fixed containing block — via `transform`/`translate`/`rotate`/
|
|
7
|
+
* `scale`/`perspective`/`filter`/`backdrop-filter`, a `will-change` naming any
|
|
8
|
+
* of those, or layout/paint containment (`contain: layout|paint|strict|content`,
|
|
9
|
+
* which `content-visibility: auto` also applies). Overlay code that measures
|
|
10
|
+
* "does this fixed element fit" must therefore compare against the CB rect,
|
|
11
|
+
* not `window.innerWidth/innerHeight` — the two only coincide in the (common)
|
|
12
|
+
* no-such-ancestor case.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* `will-change` values that force a fixed containing block: any property whose
|
|
16
|
+
* non-initial value would form one. (Same set floating-ui checks.)
|
|
17
|
+
*/
|
|
18
|
+
const WILL_CHANGE_RE = /transform|translate|scale|rotate|perspective|filter/;
|
|
19
|
+
/** `contain` values that apply layout and/or paint containment. */
|
|
20
|
+
const CONTAIN_RE = /paint|layout|strict|content/;
|
|
21
|
+
let _isWebKit;
|
|
22
|
+
/**
|
|
23
|
+
* WebKit (Safari) detection — the same probe floating-ui uses. Only Safari
|
|
24
|
+
* supports the `-webkit-` prefixed backdrop-filter.
|
|
25
|
+
*/
|
|
26
|
+
function isWebKit() {
|
|
27
|
+
if (_isWebKit === undefined) {
|
|
28
|
+
_isWebKit =
|
|
29
|
+
typeof CSS !== "undefined" &&
|
|
30
|
+
typeof CSS.supports === "function" &&
|
|
31
|
+
CSS.supports("-webkit-backdrop-filter", "none");
|
|
32
|
+
}
|
|
33
|
+
return _isWebKit;
|
|
34
|
+
}
|
|
35
|
+
function notNone(value) {
|
|
36
|
+
return !!value && value !== "none";
|
|
37
|
+
}
|
|
38
|
+
// Elements promoted to the TOP LAYER (modal dialogs via `showModal()`, open
|
|
39
|
+
// `[popover]`s, fullscreen elements) escape every ancestor containing block by
|
|
40
|
+
// design — their fixed descendants resolve against the viewport again. Each
|
|
41
|
+
// selector is probed separately: an engine that doesn't know one would
|
|
42
|
+
// otherwise reject the whole list.
|
|
43
|
+
const TOP_LAYER_SELECTORS = [":modal", ":popover-open", ":fullscreen"];
|
|
44
|
+
function isTopLayer(el) {
|
|
45
|
+
return TOP_LAYER_SELECTORS.some((s) => {
|
|
46
|
+
try {
|
|
47
|
+
return el.matches(s);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Does this element establish a containing block for `position: fixed`
|
|
56
|
+
* descendants?
|
|
57
|
+
*
|
|
58
|
+
* Mirrors floating-ui's battle-tested `isContainingBlock`, with the same two
|
|
59
|
+
* deliberate omissions relative to a naive reading of MDN:
|
|
60
|
+
*
|
|
61
|
+
* - `filter`/`backdrop-filter` are ignored on WebKit: Safari historically does
|
|
62
|
+
* NOT form a fixed CB from them (plain `filter` was only fixed in Safari 26).
|
|
63
|
+
* Misdetecting a CB the browser doesn't honor would break correct layouts;
|
|
64
|
+
* missing one merely preserves the pre-CB-aware behavior.
|
|
65
|
+
* - `container-type` is NOT checked: the CSSWG removed layout containment from
|
|
66
|
+
* it (2024, csswg-drafts#10544) and Chrome 129+/Firefox/Safari all shipped
|
|
67
|
+
* the change, so container queries no longer re-parent fixed descendants.
|
|
68
|
+
*/
|
|
69
|
+
export function isFixedContainingBlock(el) {
|
|
70
|
+
const s = getComputedStyle(el);
|
|
71
|
+
return (notNone(s.transform) ||
|
|
72
|
+
notNone(s.translate) ||
|
|
73
|
+
notNone(s.rotate) ||
|
|
74
|
+
notNone(s.scale) ||
|
|
75
|
+
notNone(s.perspective) ||
|
|
76
|
+
(!isWebKit() && (notNone(s.filter) || notNone(s.backdropFilter))) ||
|
|
77
|
+
s.contentVisibility === "auto" ||
|
|
78
|
+
WILL_CHANGE_RE.test(s.willChange) ||
|
|
79
|
+
CONTAIN_RE.test(s.contain));
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* The nearest ancestor of `el` that establishes a containing block for
|
|
83
|
+
* `position: fixed` descendants, or `null` when fixed descendants resolve
|
|
84
|
+
* against the viewport. The walk stops (returning `null`) at top-layer
|
|
85
|
+
* elements — a modal `<dialog>`, an open `[popover]`, a fullscreen element —
|
|
86
|
+
* since the top layer escapes every ancestor containing block by design
|
|
87
|
+
* (unless such an element is itself CB-forming, e.g. a transformed dialog).
|
|
88
|
+
*/
|
|
89
|
+
export function fixedContainingBlockAncestor(el) {
|
|
90
|
+
for (let n = el.parentElement; n; n = n.parentElement) {
|
|
91
|
+
if (isFixedContainingBlock(n))
|
|
92
|
+
return n;
|
|
93
|
+
if (isTopLayer(n))
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* The rect that `position: fixed` descendants of `el` actually resolve
|
|
100
|
+
* against, in viewport (visual) coordinates.
|
|
101
|
+
*
|
|
102
|
+
* Walks up from `el`'s parent looking for the nearest containing-block-forming
|
|
103
|
+
* ancestor (see {@link fixedContainingBlockAncestor}) and returns its padding
|
|
104
|
+
* box — per CSS, the CB is the padding box, not the border box. When no such
|
|
105
|
+
* ancestor exists (the overwhelmingly common case) it returns the viewport
|
|
106
|
+
* rect based on `window.innerWidth/innerHeight`, byte-identical to what the
|
|
107
|
+
* pre-CB-aware code measured.
|
|
108
|
+
*
|
|
109
|
+
* Known limitation: for a ROTATED CB ancestor the returned rect is the
|
|
110
|
+
* axis-aligned bounding box of the rotated element — consumers comparing
|
|
111
|
+
* rects (overflow checks, clamps) get an approximation there. Scaled
|
|
112
|
+
* ancestors are handled (border widths are converted to visual px).
|
|
113
|
+
*/
|
|
114
|
+
export function fixedContainingBlockRect(el) {
|
|
115
|
+
const n = fixedContainingBlockAncestor(el);
|
|
116
|
+
if (n) {
|
|
117
|
+
const s = getComputedStyle(n);
|
|
118
|
+
const r = n.getBoundingClientRect();
|
|
119
|
+
// computed border widths are layout px; the rect is visual (post-
|
|
120
|
+
// transform) px — convert via the ancestor's accumulated scale so the
|
|
121
|
+
// inset is correct inside scaled wrappers too
|
|
122
|
+
const sx = n.offsetWidth ? r.width / n.offsetWidth : 1;
|
|
123
|
+
const sy = n.offsetHeight ? r.height / n.offsetHeight : 1;
|
|
124
|
+
const bl = (parseFloat(s.borderLeftWidth) || 0) * sx;
|
|
125
|
+
const bt = (parseFloat(s.borderTopWidth) || 0) * sy;
|
|
126
|
+
const br = (parseFloat(s.borderRightWidth) || 0) * sx;
|
|
127
|
+
const bb = (parseFloat(s.borderBottomWidth) || 0) * sy;
|
|
128
|
+
return new DOMRectReadOnly(r.left + bl, r.top + bt, r.width - bl - br, r.height - bt - bb);
|
|
129
|
+
}
|
|
130
|
+
return new DOMRectReadOnly(0, 0, window.innerWidth, window.innerHeight);
|
|
131
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared bits for overlay actions (`popover`, `spotlight`, `dimBehind`) that
|
|
3
|
+
* can portal their DOM into a consumer-provided container instead of
|
|
4
|
+
* `document.body`.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* The `container` option shape: a concrete element, or a lazy factory (useful
|
|
8
|
+
* when the element does not exist yet at action-setup time). A factory
|
|
9
|
+
* returning `null` means "use the default".
|
|
10
|
+
*/
|
|
11
|
+
export type OverlayContainerOption = HTMLElement | (() => HTMLElement | null);
|
|
12
|
+
/**
|
|
13
|
+
* Resolve a `container` option to an element, or `null` when unset (callers
|
|
14
|
+
* then fall back to their default — typically `document.body`).
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolveContainerOption(option: OverlayContainerOption | undefined | null): HTMLElement | null;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared bits for overlay actions (`popover`, `spotlight`, `dimBehind`) that
|
|
3
|
+
* can portal their DOM into a consumer-provided container instead of
|
|
4
|
+
* `document.body`.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Resolve a `container` option to an element, or `null` when unset (callers
|
|
8
|
+
* then fall back to their default — typically `document.body`).
|
|
9
|
+
*/
|
|
10
|
+
export function resolveContainerOption(option) {
|
|
11
|
+
return (typeof option === "function" ? option() : option) ?? null;
|
|
12
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marianmeres/stuic",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.152.0",
|
|
4
4
|
"packageManager": "pnpm@11.5.0",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "vite dev",
|
|
@@ -136,15 +136,15 @@
|
|
|
136
136
|
"@codemirror/view": "^6.43.8",
|
|
137
137
|
"@eslint/js": "^9.39.5",
|
|
138
138
|
"@marianmeres/random-human-readable": "^1.10.2",
|
|
139
|
-
"@milkdown/core": "^7.22.
|
|
140
|
-
"@milkdown/ctx": "^7.22.
|
|
141
|
-
"@milkdown/plugin-history": "^7.22.
|
|
142
|
-
"@milkdown/plugin-listener": "^7.22.
|
|
143
|
-
"@milkdown/preset-commonmark": "^7.22.
|
|
144
|
-
"@milkdown/preset-gfm": "^7.22.
|
|
145
|
-
"@milkdown/prose": "^7.22.
|
|
146
|
-
"@milkdown/transformer": "^7.22.
|
|
147
|
-
"@milkdown/utils": "^7.22.
|
|
139
|
+
"@milkdown/core": "^7.22.1",
|
|
140
|
+
"@milkdown/ctx": "^7.22.1",
|
|
141
|
+
"@milkdown/plugin-history": "^7.22.1",
|
|
142
|
+
"@milkdown/plugin-listener": "^7.22.1",
|
|
143
|
+
"@milkdown/preset-commonmark": "^7.22.1",
|
|
144
|
+
"@milkdown/preset-gfm": "^7.22.1",
|
|
145
|
+
"@milkdown/prose": "^7.22.1",
|
|
146
|
+
"@milkdown/transformer": "^7.22.1",
|
|
147
|
+
"@milkdown/utils": "^7.22.1",
|
|
148
148
|
"@sveltejs/adapter-auto": "^4.0.0",
|
|
149
149
|
"@sveltejs/kit": "^2.70.2",
|
|
150
150
|
"@sveltejs/package": "^2.5.8",
|
|
@@ -162,8 +162,8 @@
|
|
|
162
162
|
"prettier": "^3.9.6",
|
|
163
163
|
"prettier-plugin-svelte": "^3.5.2",
|
|
164
164
|
"publint": "^0.3.23",
|
|
165
|
-
"svelte": "^5.56.
|
|
166
|
-
"svelte-check": "^4.7.
|
|
165
|
+
"svelte": "^5.56.9",
|
|
166
|
+
"svelte-check": "^4.7.6",
|
|
167
167
|
"tailwindcss": "^4.3.3",
|
|
168
168
|
"tsx": "^4.23.12",
|
|
169
169
|
"typescript": "^5.9.3",
|