@urbicon-ui/blocks 6.3.3 → 6.3.7

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.
@@ -240,8 +240,21 @@ export function size(options) {
240
240
  name: 'size',
241
241
  fn(state) {
242
242
  const overflow = detectOverflow(state);
243
- const availableWidth = state.rects.floating.width - Math.max(0, overflow.left) - Math.max(0, overflow.right);
244
- const availableHeight = state.rects.floating.height - Math.max(0, overflow.top) - Math.max(0, overflow.bottom);
243
+ const side = getSide(state.placement);
244
+ const vertical = side === 'top' || side === 'bottom';
245
+ // Room in the placement's MAIN axis is measured anchor → viewport edge:
246
+ // `dimension - overflow[side]` cancels the floating element's own
247
+ // dimension, so the value reflects true available room and grows back
248
+ // once space is restored. Subtracting the *clamped* overflow of both
249
+ // edges instead would only ever ratchet down — a one-way "latch" that
250
+ // keeps the panel short after the iOS keyboard closes. The CROSS axis
251
+ // keeps the "what currently fits between both edges" measure.
252
+ const availableWidth = vertical
253
+ ? state.rects.floating.width - Math.max(0, overflow.left) - Math.max(0, overflow.right)
254
+ : state.rects.floating.width - overflow[side];
255
+ const availableHeight = vertical
256
+ ? state.rects.floating.height - overflow[side]
257
+ : state.rects.floating.height - Math.max(0, overflow.top) - Math.max(0, overflow.bottom);
245
258
  options.apply({ ...state, availableWidth, availableHeight });
246
259
  const newW = state.elements.floating.offsetWidth;
247
260
  const newH = state.elements.floating.offsetHeight;
@@ -284,6 +297,21 @@ export function autoUpdate(reference, floating, callback) {
284
297
  }
285
298
  window.addEventListener('resize', callback);
286
299
  cleanups.push(() => window.removeEventListener('resize', callback));
300
+ // iOS Safari resizes and offsets the *visual* viewport when the on-screen
301
+ // keyboard opens (and during pinch-zoom / overscroll) WITHOUT firing a
302
+ // `window` 'resize' or 'scroll'. A `position: fixed` overlay would otherwise
303
+ // stay pinned to stale coordinates — visibly detaching from its anchor and
304
+ // drifting as the page settles. Tracking the visualViewport keeps anchored
305
+ // overlays glued to their reference on touch devices.
306
+ const vv = window.visualViewport;
307
+ if (vv) {
308
+ vv.addEventListener('resize', callback);
309
+ vv.addEventListener('scroll', callback, { passive: true });
310
+ cleanups.push(() => {
311
+ vv.removeEventListener('resize', callback);
312
+ vv.removeEventListener('scroll', callback);
313
+ });
314
+ }
287
315
  return () => {
288
316
  for (const fn of cleanups)
289
317
  fn();
@@ -21,6 +21,9 @@ export function useFloatingListbox(opts) {
21
21
  if (!isOpen) {
22
22
  cleanupPosition?.();
23
23
  cleanupPosition = undefined;
24
+ // Drop the keyboard-aware height clamp so the next open re-measures from
25
+ // the full design cap instead of inheriting a stale value.
26
+ floating.style.removeProperty('--blocks-overlay-available-height');
24
27
  if (usePortal && floating.matches(':popover-open')) {
25
28
  try {
26
29
  floating.hidePopover();
@@ -50,15 +53,20 @@ export function useFloatingListbox(opts) {
50
53
  offset(4),
51
54
  flip(),
52
55
  shift({ padding: 8 }),
53
- ...(syncWidth
54
- ? [
55
- floatingSize({
56
- apply({ rects }) {
57
- floating.style.width = `${rects.reference.width}px`;
58
- }
59
- })
60
- ]
61
- : [])
56
+ floatingSize({
57
+ apply({ availableHeight, rects }) {
58
+ // Cap the panel to the room actually left between the anchor and
59
+ // the (visual) viewport edge — decisive on iOS, where the
60
+ // keyboard shrinks the visualViewport. Exposed as a CSS var that
61
+ // the variant feeds into `max-h-[min(15rem,var(--…))]`, so the
62
+ // 15rem design token stays the upper bound while this tracks the
63
+ // live available height (and recovers when room is restored).
64
+ floating.style.setProperty('--blocks-overlay-available-height', `${Math.max(0, Math.round(availableHeight))}px`);
65
+ if (syncWidth) {
66
+ floating.style.width = `${rects.reference.width}px`;
67
+ }
68
+ }
69
+ })
62
70
  ]
63
71
  })
64
72
  .then(({ x, y }) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@urbicon-ui/blocks",
3
- "version": "6.3.3",
3
+ "version": "6.3.7",
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": {
@@ -80,27 +80,27 @@
80
80
  "./dist/i18n/index.d.ts"
81
81
  ],
82
82
  "peerDependencies": {
83
- "svelte": "^5.56.3",
84
- "@sveltejs/kit": "^2.65.2",
83
+ "svelte": "^5.56.4",
84
+ "@sveltejs/kit": "^2.67.0",
85
85
  "@urbicon-ui/i18n": "^6.0.0",
86
86
  "@urbicon-ui/shared-types": "^6.0.0"
87
87
  },
88
88
  "dependencies": {},
89
89
  "devDependencies": {
90
- "@sveltejs/kit": "^2.65.2",
90
+ "@sveltejs/kit": "^2.67.0",
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.3",
95
- "@urbicon-ui/shared-types": "6.3.3",
94
+ "@urbicon-ui/i18n": "6.3.7",
95
+ "@urbicon-ui/shared-types": "6.3.7",
96
96
  "prettier": "^3.8.4",
97
97
  "prettier-plugin-svelte": "^4.1.1",
98
98
  "prettier-plugin-tailwindcss": "^0.8.0",
99
- "svelte": "^5.56.3",
100
- "svelte-check": "^4.6.0",
99
+ "svelte": "^5.56.4",
100
+ "svelte-check": "^4.7.1",
101
101
  "tailwindcss": "^4.3.1",
102
102
  "typescript": "^6.0.3",
103
- "vite": "^8.0.16",
103
+ "vite": "^8.1.0",
104
104
  "vitest": "^4.1.9"
105
105
  }
106
106
  }