@urbicon-ui/blocks 6.3.13 → 6.3.14
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/primitives/Combobox/Combobox.svelte +15 -4
- package/dist/primitives/Popover/Popover.svelte +20 -12
- package/dist/primitives/Select/Select.svelte +11 -4
- package/dist/utils/floating.js +17 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/use-floating-panel.svelte.d.ts +24 -12
- package/dist/utils/use-floating-panel.svelte.js +25 -15
- package/package.json +3 -3
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import CloseIconDefault from '../../icons/CloseIcon.svelte';
|
|
8
8
|
import ChevronDownIconDefault from '../../icons/ChevronDownIcon.svelte';
|
|
9
9
|
import CheckIconDefault from '../../icons/CheckIcon.svelte';
|
|
10
|
-
import { useFormField, getTierContext, useFloatingPanel,
|
|
10
|
+
import { useFormField, getTierContext, useFloatingPanel, floatingPanelHidden } from '../../utils';
|
|
11
11
|
import type { ComboboxProps, ComboboxOption } from './index';
|
|
12
12
|
|
|
13
13
|
const bt = useBlocksI18n();
|
|
@@ -373,8 +373,15 @@
|
|
|
373
373
|
open/close cycles. In top-layer mode the `popover="manual"` UA-rule
|
|
374
374
|
`[popover]:not(:popover-open)` hides it until `showPopover()` runs; inside a
|
|
375
375
|
modal dialog (`panel.topLayer === false`) the popover attribute is dropped
|
|
376
|
-
and `
|
|
377
|
-
|
|
376
|
+
and `display` drives visibility instead, so the listbox renders in the
|
|
377
|
+
dialog's own top-layer subtree (Codeberg #23).
|
|
378
|
+
|
|
379
|
+
The positioning frame is applied with per-property `style:` directives, NOT
|
|
380
|
+
a single `style={…}` string: a dynamic `style={…}` compiles to
|
|
381
|
+
`setAttribute('style')`, replacing the whole attribute and wiping the
|
|
382
|
+
`left`/`top` Floating UI writes imperatively (the iOS `inset: auto` clobber
|
|
383
|
+
behind Codeberg #23). `style:` directives and Floating UI's writes are both
|
|
384
|
+
per-property and never overwrite one another.
|
|
378
385
|
-->
|
|
379
386
|
<div
|
|
380
387
|
bind:this={listboxEl}
|
|
@@ -385,7 +392,11 @@
|
|
|
385
392
|
class={unstyled
|
|
386
393
|
? (slotClasses?.listbox ?? '')
|
|
387
394
|
: styles.listbox({ class: slotClasses?.listbox })}
|
|
388
|
-
style={
|
|
395
|
+
style:position={panel.strategy}
|
|
396
|
+
style:inset="auto"
|
|
397
|
+
style:margin="0"
|
|
398
|
+
style:overflow-y="auto"
|
|
399
|
+
style:display={floatingPanelHidden(panel, open) ? 'none' : null}
|
|
389
400
|
>
|
|
390
401
|
{#if open}
|
|
391
402
|
{#if filtered.length === 0}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { untrack } from 'svelte';
|
|
3
3
|
import { getBlocksConfig, resolveSlotClasses } from '../../provider';
|
|
4
|
-
import { useFloatingPanel,
|
|
4
|
+
import { useFloatingPanel, floatingPanelHidden } from '../../utils';
|
|
5
5
|
import { popoverVariants } from './popover.variants';
|
|
6
6
|
import type { PopoverProps } from './index';
|
|
7
7
|
|
|
@@ -308,17 +308,21 @@
|
|
|
308
308
|
`{...restProps}` so a consumer-supplied `popover="manual"` or override
|
|
309
309
|
of `role`/`id` cannot silently break the show/hide flow or ARIA pairing.
|
|
310
310
|
|
|
311
|
-
The
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
red"` still works;
|
|
315
|
-
cannot
|
|
316
|
-
|
|
317
|
-
|
|
311
|
+
The consumer `style` prop is the static `style` attribute; the load-bearing
|
|
312
|
+
positioning frame follows as per-property `style:` directives, which the
|
|
313
|
+
browser applies on top of (and so win over) the consumer string. A consumer
|
|
314
|
+
passing `style="background: red"` still works; `style="position: absolute"`
|
|
315
|
+
is overridden by `style:position` and cannot break Floating UI's coordinates.
|
|
316
|
+
Keep the prop static — a reactive `style` recompiles to `setAttribute('style')`
|
|
317
|
+
and would momentarily wipe the directive + Floating UI writes.
|
|
318
|
+
|
|
318
319
|
Non-top-layer render (`panel.topLayer === false`): the `popover` attribute is
|
|
319
|
-
suppressed so the browser does not promote the element to the top layer;
|
|
320
|
-
`
|
|
321
|
-
`left` / `top`.
|
|
320
|
+
suppressed so the browser does not promote the element to the top layer; the
|
|
321
|
+
`style:` directives drive `position` + `display` while Floating UI drives
|
|
322
|
+
`left` / `top`. Per-property `style:` directives are used (never a single
|
|
323
|
+
dynamic `style={…}` string) so Svelte's `setAttribute('style')` can't wipe the
|
|
324
|
+
imperative `left`/`top` writes — the iOS `inset: auto` clobber (Codeberg #23).
|
|
325
|
+
Two triggers:
|
|
322
326
|
• `usePortal=false` (e.g. Menu / Select inside another popover) →
|
|
323
327
|
`position: absolute`, avoiding nested-top-layer focus & z-index quirks.
|
|
324
328
|
• nested in an open modal `<dialog>` → `position: fixed`, so the panel paints
|
|
@@ -331,7 +335,11 @@
|
|
|
331
335
|
class={popoverClasses}
|
|
332
336
|
{...restProps}
|
|
333
337
|
popover={panel.topLayer ? popoverMode : null}
|
|
334
|
-
style={
|
|
338
|
+
style={style || null}
|
|
339
|
+
style:position={panel.strategy}
|
|
340
|
+
style:inset="auto"
|
|
341
|
+
style:margin="0"
|
|
342
|
+
style:display={floatingPanelHidden(panel, open) ? 'none' : null}
|
|
335
343
|
{role}
|
|
336
344
|
aria-modal={ariaModal || undefined}
|
|
337
345
|
{id}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts" generics="T extends string | number | boolean = string">
|
|
2
2
|
import { useBlocksI18n, mintRegistry } from '../..';
|
|
3
|
-
import { useFormField, getTierContext, useFloatingPanel,
|
|
3
|
+
import { useFormField, getTierContext, useFloatingPanel, floatingPanelHidden } from '../../utils';
|
|
4
4
|
import { getBlocksConfig, resolveSlotClasses } from '../../provider';
|
|
5
5
|
import { resolveIcon } from '../../icons';
|
|
6
6
|
import ChevronDownIconDefault from '../../icons/ChevronDownIcon.svelte';
|
|
@@ -509,8 +509,11 @@
|
|
|
509
509
|
open/close cycles. Top-layer mode uses `popover="manual"` + the UA
|
|
510
510
|
`[popover]:not(:popover-open)` display rule until `showPopover()` runs; the
|
|
511
511
|
in-place modes (nested in a modal dialog → Codeberg #23, or the explicit
|
|
512
|
-
`usePortal=false`) drop the popover attribute and
|
|
513
|
-
|
|
512
|
+
`usePortal=false`) drop the popover attribute and drive visibility via
|
|
513
|
+
`display`. The positioning frame uses per-property `style:` directives, not
|
|
514
|
+
a `style={…}` string, so Svelte's `setAttribute('style')` can never wipe the
|
|
515
|
+
`left`/`top` Floating UI writes imperatively (iOS `inset: auto` clobber,
|
|
516
|
+
Codeberg #23).
|
|
514
517
|
|
|
515
518
|
`tabindex={-1}` keeps the listbox programmatically focusable without
|
|
516
519
|
adding it to the tab order — the ARIA Listbox / `aria-activedescendant`
|
|
@@ -530,7 +533,11 @@
|
|
|
530
533
|
class={unstyled
|
|
531
534
|
? (slotClasses?.listbox ?? '')
|
|
532
535
|
: styles.listbox({ class: slotClasses?.listbox })}
|
|
533
|
-
style={
|
|
536
|
+
style:position={panel.strategy}
|
|
537
|
+
style:inset="auto"
|
|
538
|
+
style:margin="0"
|
|
539
|
+
style:overflow-y="auto"
|
|
540
|
+
style:display={floatingPanelHidden(panel, open) ? 'none' : null}
|
|
534
541
|
aria-labelledby={labelId}
|
|
535
542
|
>
|
|
536
543
|
{#if open}
|
package/dist/utils/floating.js
CHANGED
|
@@ -71,6 +71,22 @@ function fixedOriginOffset(floating) {
|
|
|
71
71
|
floating.style.top = prevTop;
|
|
72
72
|
return { x: origin.left, y: origin.top };
|
|
73
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* `true` when `floating` is a popover currently promoted to the browser top
|
|
76
|
+
* layer. Guarded: `:popover-open` predates the Popover API, so `matches()`
|
|
77
|
+
* throws `SyntaxError` on engines that lack it (Safari < 17) — there, no element
|
|
78
|
+
* can be in the top layer, so `false` is the correct answer (and never throwing
|
|
79
|
+
* keeps `computePosition` from aborting mid-flight, which would leave the panel
|
|
80
|
+
* unpositioned — Codeberg #23).
|
|
81
|
+
*/
|
|
82
|
+
function isTopLayerPopover(floating) {
|
|
83
|
+
try {
|
|
84
|
+
return floating.matches(':popover-open');
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
74
90
|
function viewportToLocal(floating, x, y, strategy) {
|
|
75
91
|
if (strategy === 'fixed') {
|
|
76
92
|
// A top-layer popover is positioned against the viewport regardless of any
|
|
@@ -78,7 +94,7 @@ function viewportToLocal(floating, x, y, strategy) {
|
|
|
78
94
|
// fixed panel (the in-dialog mode) is positioned against its containing
|
|
79
95
|
// block, which can be a transformed ancestor on WebKit — compensate for its
|
|
80
96
|
// measured viewport offset so the same coordinates land correctly there too.
|
|
81
|
-
if (floating
|
|
97
|
+
if (isTopLayerPopover(floating))
|
|
82
98
|
return { x, y };
|
|
83
99
|
const origin = fixedOriginOffset(floating);
|
|
84
100
|
return { x: x - origin.x, y: y - origin.y };
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -11,6 +11,6 @@ export { type EasingFn, getOverlayMotion, OVERLAY_MOTION_DEFAULTS, type OverlayM
|
|
|
11
11
|
export * from './persistent-state.svelte.js';
|
|
12
12
|
export { getTierContext, type InteractiveTier, setTierContext, type TierContext } from './tier-context.js';
|
|
13
13
|
export * from './types.js';
|
|
14
|
-
export { type FloatingPanelOptions, type FloatingPanelState,
|
|
14
|
+
export { type FloatingPanelOptions, type FloatingPanelState, floatingPanelHidden, useFloatingPanel } from './use-floating-panel.svelte.js';
|
|
15
15
|
export { computeFormFieldAria, type UseFormFieldInputs, type UseFormFieldReturn, useFormField } from './use-form-field.svelte.js';
|
|
16
16
|
export { cx, type TVProps, tv, type VariantProps } from './variants.js';
|
package/dist/utils/index.js
CHANGED
|
@@ -11,6 +11,6 @@ export { getOverlayMotion, OVERLAY_MOTION_DEFAULTS } from './overlay-tokens.js';
|
|
|
11
11
|
export * from './persistent-state.svelte.js';
|
|
12
12
|
export { getTierContext, setTierContext } from './tier-context.js';
|
|
13
13
|
export * from './types.js';
|
|
14
|
-
export {
|
|
14
|
+
export { floatingPanelHidden, useFloatingPanel } from './use-floating-panel.svelte.js';
|
|
15
15
|
export { computeFormFieldAria, useFormField } from './use-form-field.svelte.js';
|
|
16
16
|
export { cx, tv } from './variants.js';
|
|
@@ -64,8 +64,8 @@ export interface FloatingPanelState {
|
|
|
64
64
|
* returns `topLayer: false`) when the anchor sits inside an open modal
|
|
65
65
|
* `<dialog>`, where a second top-layer element is invisible on iOS/WebKit
|
|
66
66
|
* (Codeberg #23) — the panel then renders `position: fixed` in place. Callers
|
|
67
|
-
* mirror the returned `topLayer`/`strategy` in their markup via
|
|
68
|
-
* {@link
|
|
67
|
+
* mirror the returned `topLayer`/`strategy` in their markup via per-property
|
|
68
|
+
* `style:` directives and {@link floatingPanelHidden}.
|
|
69
69
|
*
|
|
70
70
|
* The Floating-UI `size` middleware feeds the room actually left between the
|
|
71
71
|
* anchor and the (visual) viewport edge into `--blocks-overlay-available-height`.
|
|
@@ -75,15 +75,27 @@ export interface FloatingPanelState {
|
|
|
75
75
|
*/
|
|
76
76
|
export declare function useFloatingPanel(opts: FloatingPanelOptions): FloatingPanelState;
|
|
77
77
|
/**
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
* `popover` attribute is present, which the in-place modes omit).
|
|
78
|
+
* Whether a panel driven by {@link useFloatingPanel} must be hidden via
|
|
79
|
+
* `display: none` while closed. Top-layer panels carry the `popover` attribute
|
|
80
|
+
* and lean on the UA `[popover]:not(:popover-open){display:none}` rule, so they
|
|
81
|
+
* report `false`; the in-place modes (in-dialog `fixed`, or the explicit
|
|
82
|
+
* `portal=false` inline mode) have no such rule and are hidden by the caller.
|
|
84
83
|
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
84
|
+
* Callers apply the positioning frame with `style:` DIRECTIVES, never a single
|
|
85
|
+
* dynamic `style={…}` string — `style={…}` compiles to `setAttribute('style')`,
|
|
86
|
+
* which replaces the whole attribute and would wipe the `left`/`top` Floating UI
|
|
87
|
+
* writes imperatively (the iOS `inset: auto` clobber behind Codeberg #23).
|
|
88
|
+
* Per-property `style:` directives and Floating UI's `style.left/top` writes
|
|
89
|
+
* coexist without ever overwriting each other:
|
|
90
|
+
*
|
|
91
|
+
* ```svelte
|
|
92
|
+
* <div
|
|
93
|
+
* popover={panel.topLayer ? 'manual' : null}
|
|
94
|
+
* style:position={panel.strategy}
|
|
95
|
+
* style:inset="auto"
|
|
96
|
+
* style:margin="0"
|
|
97
|
+
* style:display={floatingPanelHidden(panel, open) ? 'none' : null}
|
|
98
|
+
* >
|
|
99
|
+
* ```
|
|
88
100
|
*/
|
|
89
|
-
export declare function
|
|
101
|
+
export declare function floatingPanelHidden(panel: FloatingPanelState, open: boolean): boolean;
|
|
@@ -16,8 +16,8 @@ import { isAnchoredInModalDialog } from './overlay.js';
|
|
|
16
16
|
* returns `topLayer: false`) when the anchor sits inside an open modal
|
|
17
17
|
* `<dialog>`, where a second top-layer element is invisible on iOS/WebKit
|
|
18
18
|
* (Codeberg #23) — the panel then renders `position: fixed` in place. Callers
|
|
19
|
-
* mirror the returned `topLayer`/`strategy` in their markup via
|
|
20
|
-
* {@link
|
|
19
|
+
* mirror the returned `topLayer`/`strategy` in their markup via per-property
|
|
20
|
+
* `style:` directives and {@link floatingPanelHidden}.
|
|
21
21
|
*
|
|
22
22
|
* The Floating-UI `size` middleware feeds the room actually left between the
|
|
23
23
|
* anchor and the (visual) viewport edge into `--blocks-overlay-available-height`.
|
|
@@ -151,19 +151,29 @@ export function useFloatingPanel(opts) {
|
|
|
151
151
|
};
|
|
152
152
|
}
|
|
153
153
|
/**
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
* `popover` attribute is present, which the in-place modes omit).
|
|
154
|
+
* Whether a panel driven by {@link useFloatingPanel} must be hidden via
|
|
155
|
+
* `display: none` while closed. Top-layer panels carry the `popover` attribute
|
|
156
|
+
* and lean on the UA `[popover]:not(:popover-open){display:none}` rule, so they
|
|
157
|
+
* report `false`; the in-place modes (in-dialog `fixed`, or the explicit
|
|
158
|
+
* `portal=false` inline mode) have no such rule and are hidden by the caller.
|
|
160
159
|
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
160
|
+
* Callers apply the positioning frame with `style:` DIRECTIVES, never a single
|
|
161
|
+
* dynamic `style={…}` string — `style={…}` compiles to `setAttribute('style')`,
|
|
162
|
+
* which replaces the whole attribute and would wipe the `left`/`top` Floating UI
|
|
163
|
+
* writes imperatively (the iOS `inset: auto` clobber behind Codeberg #23).
|
|
164
|
+
* Per-property `style:` directives and Floating UI's `style.left/top` writes
|
|
165
|
+
* coexist without ever overwriting each other:
|
|
166
|
+
*
|
|
167
|
+
* ```svelte
|
|
168
|
+
* <div
|
|
169
|
+
* popover={panel.topLayer ? 'manual' : null}
|
|
170
|
+
* style:position={panel.strategy}
|
|
171
|
+
* style:inset="auto"
|
|
172
|
+
* style:margin="0"
|
|
173
|
+
* style:display={floatingPanelHidden(panel, open) ? 'none' : null}
|
|
174
|
+
* >
|
|
175
|
+
* ```
|
|
164
176
|
*/
|
|
165
|
-
export function
|
|
166
|
-
|
|
167
|
-
const hidden = !panel.topLayer && !open ? ' display: none;' : '';
|
|
168
|
-
return `${extra}${positioning}${hidden}`;
|
|
177
|
+
export function floatingPanelHidden(panel, open) {
|
|
178
|
+
return !panel.topLayer && !open;
|
|
169
179
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@urbicon-ui/blocks",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.14",
|
|
4
4
|
"description": "Svelte 5 UI component library with Tailwind CSS 4, OKLCH design tokens and zero runtime dependencies",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -91,8 +91,8 @@
|
|
|
91
91
|
"@sveltejs/package": "^2.5.8",
|
|
92
92
|
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
|
93
93
|
"@tailwindcss/vite": "^4.3.1",
|
|
94
|
-
"@urbicon-ui/i18n": "6.3.
|
|
95
|
-
"@urbicon-ui/shared-types": "6.3.
|
|
94
|
+
"@urbicon-ui/i18n": "6.3.14",
|
|
95
|
+
"@urbicon-ui/shared-types": "6.3.14",
|
|
96
96
|
"prettier": "^3.8.4",
|
|
97
97
|
"prettier-plugin-svelte": "^4.1.1",
|
|
98
98
|
"prettier-plugin-tailwindcss": "^0.8.0",
|