@zag-js/popper 1.33.1 → 1.34.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/index.d.mts CHANGED
@@ -70,6 +70,15 @@ interface PositioningOptions {
70
70
  * Whether the popover should fit the viewport.
71
71
  */
72
72
  fitViewport?: boolean | undefined;
73
+ /**
74
+ * Whether to use the size middleware from Floating UI.
75
+ * It computes and sets CSS variables (`--reference-width`, `--reference-height`, `--available-width`, `--available-height`) used by `sameWidth` and `fitViewport`.
76
+ *
77
+ * Disabling it improves scroll performance with heavy content by avoiding layout thrashing on each update.
78
+ * Only applies when both `sameWidth` and `fitViewport` are false — the middleware is always used when either is enabled.
79
+ * @default true
80
+ */
81
+ sizeMiddleware?: boolean | undefined;
73
82
  /**
74
83
  * The overflow boundary of the reference element
75
84
  * Accepts a function returning a Boundary, a Boundary directly,
package/dist/index.d.ts CHANGED
@@ -70,6 +70,15 @@ interface PositioningOptions {
70
70
  * Whether the popover should fit the viewport.
71
71
  */
72
72
  fitViewport?: boolean | undefined;
73
+ /**
74
+ * Whether to use the size middleware from Floating UI.
75
+ * It computes and sets CSS variables (`--reference-width`, `--reference-height`, `--available-width`, `--available-height`) used by `sameWidth` and `fitViewport`.
76
+ *
77
+ * Disabling it improves scroll performance with heavy content by avoiding layout thrashing on each update.
78
+ * Only applies when both `sameWidth` and `fitViewport` are false — the middleware is always used when either is enabled.
79
+ * @default true
80
+ */
81
+ sizeMiddleware?: boolean | undefined;
73
82
  /**
74
83
  * The overflow boundary of the reference element
75
84
  * Accepts a function returning a Boundary, a Boundary directly,
package/dist/index.js CHANGED
@@ -145,6 +145,9 @@ function roundByDpr(win, value) {
145
145
  const dpr = win.devicePixelRatio || 1;
146
146
  return Math.round(value * dpr) / dpr;
147
147
  }
148
+ function isApproximatelyEqual(a, b) {
149
+ return a != null && Math.abs(a - b) < 0.5;
150
+ }
148
151
  function resolveBoundaryOption(boundary) {
149
152
  if (typeof boundary === "function") return boundary();
150
153
  if (boundary === "clipping-ancestors") return "clippingAncestors";
@@ -191,6 +194,11 @@ function getShiftMiddleware(opts) {
191
194
  });
192
195
  }
193
196
  function getSizeMiddleware(opts) {
197
+ if (opts.sizeMiddleware === false && !opts.sameWidth && !opts.fitViewport) return;
198
+ let lastReferenceWidth;
199
+ let lastReferenceHeight;
200
+ let lastAvailableWidth;
201
+ let lastAvailableHeight;
194
202
  return dom.size({
195
203
  padding: opts.overflowPadding,
196
204
  apply({ elements, rects, availableHeight, availableWidth }) {
@@ -199,10 +207,22 @@ function getSizeMiddleware(opts) {
199
207
  const referenceHeight = Math.round(rects.reference.height);
200
208
  availableWidth = Math.floor(availableWidth);
201
209
  availableHeight = Math.floor(availableHeight);
202
- floating.style.setProperty("--reference-width", `${referenceWidth}px`);
203
- floating.style.setProperty("--reference-height", `${referenceHeight}px`);
204
- floating.style.setProperty("--available-width", `${availableWidth}px`);
205
- floating.style.setProperty("--available-height", `${availableHeight}px`);
210
+ if (!isApproximatelyEqual(lastReferenceWidth, referenceWidth)) {
211
+ floating.style.setProperty("--reference-width", `${referenceWidth}px`);
212
+ lastReferenceWidth = referenceWidth;
213
+ }
214
+ if (!isApproximatelyEqual(lastReferenceHeight, referenceHeight)) {
215
+ floating.style.setProperty("--reference-height", `${referenceHeight}px`);
216
+ lastReferenceHeight = referenceHeight;
217
+ }
218
+ if (!isApproximatelyEqual(lastAvailableWidth, availableWidth)) {
219
+ floating.style.setProperty("--available-width", `${availableWidth}px`);
220
+ lastAvailableWidth = availableWidth;
221
+ }
222
+ if (!isApproximatelyEqual(lastAvailableHeight, availableHeight)) {
223
+ floating.style.setProperty("--available-height", `${availableHeight}px`);
224
+ lastAvailableHeight = availableHeight;
225
+ }
206
226
  }
207
227
  });
208
228
  }
@@ -238,6 +258,9 @@ function getPlacementImpl(referenceOrVirtual, floating, opts = {}) {
238
258
  rectMiddleware
239
259
  ];
240
260
  const { placement, strategy, onComplete, onPositioned } = options;
261
+ let lastX;
262
+ let lastY;
263
+ let zIndexComputed = false;
241
264
  const updatePosition = async () => {
242
265
  if (!reference || !floating) return;
243
266
  const pos = await dom.computePosition(reference, floating, {
@@ -246,12 +269,18 @@ function getPlacementImpl(referenceOrVirtual, floating, opts = {}) {
246
269
  strategy
247
270
  });
248
271
  onComplete?.(pos);
249
- onPositioned?.({ placed: true });
250
272
  const win = domQuery.getWindow(floating);
251
273
  const x = roundByDpr(win, pos.x);
252
274
  const y = roundByDpr(win, pos.y);
253
- floating.style.setProperty("--x", `${x}px`);
254
- floating.style.setProperty("--y", `${y}px`);
275
+ floating.style.transform = `translate3d(${x}px, ${y}px, 0)`;
276
+ if (!isApproximatelyEqual(lastX, x)) {
277
+ floating.style.setProperty("--x", `${x}px`);
278
+ lastX = x;
279
+ }
280
+ if (!isApproximatelyEqual(lastY, y)) {
281
+ floating.style.setProperty("--y", `${y}px`);
282
+ lastY = y;
283
+ }
255
284
  if (options.hideWhenDetached) {
256
285
  const isHidden = pos.middlewareData.hide?.referenceHidden;
257
286
  if (isHidden) {
@@ -262,10 +291,12 @@ function getPlacementImpl(referenceOrVirtual, floating, opts = {}) {
262
291
  floating.style.removeProperty("pointer-events");
263
292
  }
264
293
  }
265
- const contentEl = floating.firstElementChild;
266
- if (contentEl) {
267
- const styles = domQuery.getComputedStyle(contentEl);
268
- floating.style.setProperty("--z-index", styles.zIndex);
294
+ if (!zIndexComputed) {
295
+ const contentEl = floating.firstElementChild;
296
+ if (contentEl) {
297
+ floating.style.setProperty("--z-index", domQuery.getComputedStyle(contentEl).zIndex);
298
+ zIndexComputed = true;
299
+ }
269
300
  }
270
301
  };
271
302
  const update = async () => {
package/dist/index.mjs CHANGED
@@ -143,6 +143,9 @@ function roundByDpr(win, value) {
143
143
  const dpr = win.devicePixelRatio || 1;
144
144
  return Math.round(value * dpr) / dpr;
145
145
  }
146
+ function isApproximatelyEqual(a, b) {
147
+ return a != null && Math.abs(a - b) < 0.5;
148
+ }
146
149
  function resolveBoundaryOption(boundary) {
147
150
  if (typeof boundary === "function") return boundary();
148
151
  if (boundary === "clipping-ancestors") return "clippingAncestors";
@@ -189,6 +192,11 @@ function getShiftMiddleware(opts) {
189
192
  });
190
193
  }
191
194
  function getSizeMiddleware(opts) {
195
+ if (opts.sizeMiddleware === false && !opts.sameWidth && !opts.fitViewport) return;
196
+ let lastReferenceWidth;
197
+ let lastReferenceHeight;
198
+ let lastAvailableWidth;
199
+ let lastAvailableHeight;
192
200
  return size({
193
201
  padding: opts.overflowPadding,
194
202
  apply({ elements, rects, availableHeight, availableWidth }) {
@@ -197,10 +205,22 @@ function getSizeMiddleware(opts) {
197
205
  const referenceHeight = Math.round(rects.reference.height);
198
206
  availableWidth = Math.floor(availableWidth);
199
207
  availableHeight = Math.floor(availableHeight);
200
- floating.style.setProperty("--reference-width", `${referenceWidth}px`);
201
- floating.style.setProperty("--reference-height", `${referenceHeight}px`);
202
- floating.style.setProperty("--available-width", `${availableWidth}px`);
203
- floating.style.setProperty("--available-height", `${availableHeight}px`);
208
+ if (!isApproximatelyEqual(lastReferenceWidth, referenceWidth)) {
209
+ floating.style.setProperty("--reference-width", `${referenceWidth}px`);
210
+ lastReferenceWidth = referenceWidth;
211
+ }
212
+ if (!isApproximatelyEqual(lastReferenceHeight, referenceHeight)) {
213
+ floating.style.setProperty("--reference-height", `${referenceHeight}px`);
214
+ lastReferenceHeight = referenceHeight;
215
+ }
216
+ if (!isApproximatelyEqual(lastAvailableWidth, availableWidth)) {
217
+ floating.style.setProperty("--available-width", `${availableWidth}px`);
218
+ lastAvailableWidth = availableWidth;
219
+ }
220
+ if (!isApproximatelyEqual(lastAvailableHeight, availableHeight)) {
221
+ floating.style.setProperty("--available-height", `${availableHeight}px`);
222
+ lastAvailableHeight = availableHeight;
223
+ }
204
224
  }
205
225
  });
206
226
  }
@@ -236,6 +256,9 @@ function getPlacementImpl(referenceOrVirtual, floating, opts = {}) {
236
256
  rectMiddleware
237
257
  ];
238
258
  const { placement, strategy, onComplete, onPositioned } = options;
259
+ let lastX;
260
+ let lastY;
261
+ let zIndexComputed = false;
239
262
  const updatePosition = async () => {
240
263
  if (!reference || !floating) return;
241
264
  const pos = await computePosition(reference, floating, {
@@ -244,12 +267,18 @@ function getPlacementImpl(referenceOrVirtual, floating, opts = {}) {
244
267
  strategy
245
268
  });
246
269
  onComplete?.(pos);
247
- onPositioned?.({ placed: true });
248
270
  const win = getWindow(floating);
249
271
  const x = roundByDpr(win, pos.x);
250
272
  const y = roundByDpr(win, pos.y);
251
- floating.style.setProperty("--x", `${x}px`);
252
- floating.style.setProperty("--y", `${y}px`);
273
+ floating.style.transform = `translate3d(${x}px, ${y}px, 0)`;
274
+ if (!isApproximatelyEqual(lastX, x)) {
275
+ floating.style.setProperty("--x", `${x}px`);
276
+ lastX = x;
277
+ }
278
+ if (!isApproximatelyEqual(lastY, y)) {
279
+ floating.style.setProperty("--y", `${y}px`);
280
+ lastY = y;
281
+ }
253
282
  if (options.hideWhenDetached) {
254
283
  const isHidden = pos.middlewareData.hide?.referenceHidden;
255
284
  if (isHidden) {
@@ -260,10 +289,12 @@ function getPlacementImpl(referenceOrVirtual, floating, opts = {}) {
260
289
  floating.style.removeProperty("pointer-events");
261
290
  }
262
291
  }
263
- const contentEl = floating.firstElementChild;
264
- if (contentEl) {
265
- const styles = getComputedStyle(contentEl);
266
- floating.style.setProperty("--z-index", styles.zIndex);
292
+ if (!zIndexComputed) {
293
+ const contentEl = floating.firstElementChild;
294
+ if (contentEl) {
295
+ floating.style.setProperty("--z-index", getComputedStyle(contentEl).zIndex);
296
+ zIndexComputed = true;
297
+ }
267
298
  }
268
299
  };
269
300
  const update = async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zag-js/popper",
3
- "version": "1.33.1",
3
+ "version": "1.34.0",
4
4
  "description": "Dynamic positioning logic for ui machines",
5
5
  "keywords": [
6
6
  "js",
@@ -23,8 +23,8 @@
23
23
  },
24
24
  "dependencies": {
25
25
  "@floating-ui/dom": "^1.7.5",
26
- "@zag-js/dom-query": "1.33.1",
27
- "@zag-js/utils": "1.33.1"
26
+ "@zag-js/dom-query": "1.34.0",
27
+ "@zag-js/utils": "1.34.0"
28
28
  },
29
29
  "devDependencies": {
30
30
  "clean-package": "2.2.0"