@zag-js/popper 1.38.2 → 1.39.1

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.
@@ -166,34 +166,88 @@ function createStyleCleanup(el, props) {
166
166
  }
167
167
  };
168
168
  }
169
- function getPlacementImpl(referenceOrVirtual, floating, opts = {}) {
170
- const anchor = opts.getAnchorElement?.() ?? referenceOrVirtual;
171
- const reference = (0, import_get_anchor.getAnchorElement)(anchor, opts.getAnchorRect);
172
- if (!floating || !reference) return;
169
+ function anchorIdentity(anchor) {
170
+ if (anchor == null) return null;
171
+ if ((0, import_dom_query.isHTMLElement)(anchor)) return anchor;
172
+ if (typeof anchor === "object" && anchor && "contextElement" in anchor && anchor.contextElement) {
173
+ return anchor.contextElement;
174
+ }
175
+ return anchor;
176
+ }
177
+ function getPlacementImpl(referenceOrVirtual, floatingOrVirtual, opts = {}) {
178
+ const resolveFloating = () => {
179
+ const raw = typeof floatingOrVirtual === "function" ? floatingOrVirtual() : floatingOrVirtual;
180
+ return raw ?? null;
181
+ };
182
+ const resolveAnchor = () => {
183
+ const raw = typeof referenceOrVirtual === "function" ? referenceOrVirtual() : referenceOrVirtual;
184
+ return opts.getAnchorElement?.() ?? raw;
185
+ };
186
+ const resolveReference = () => {
187
+ const anchor = resolveAnchor();
188
+ if (!anchor && !opts.getAnchorRect) return null;
189
+ return (0, import_get_anchor.getAnchorElement)(anchor, opts.getAnchorRect);
190
+ };
173
191
  const options = Object.assign({}, defaultOptions, opts);
174
- const arrowEl = floating.querySelector("[data-part=arrow]");
175
- const restoreFloatingStyles = options.restoreStyles ? createStyleCleanup(floating, floatingStyleProps) : void 0;
176
- const restoreArrowStyles = options.restoreStyles ? createStyleCleanup(arrowEl, arrowStyleProps) : void 0;
177
- const middleware = [
178
- getOffsetMiddleware(arrowEl, options),
179
- getFlipMiddleware(options),
180
- getShiftMiddleware(options),
181
- getArrowMiddleware(arrowEl, floating.ownerDocument, options),
182
- (0, import_middleware.shiftArrowMiddleware)(arrowEl),
183
- (0, import_middleware.createTransformOriginMiddleware)(
184
- { gutter: options.gutter, offset: options.offset, overlap: options.overlap },
185
- arrowEl
186
- ),
187
- getSizeMiddleware(options),
188
- hideWhenDetachedMiddleware(options),
189
- import_middleware.rectMiddleware
190
- ];
192
+ let middleware = [];
193
+ let cachedMiddlewareFloating = null;
194
+ let restoreFloatingStyles;
195
+ let restoreArrowStyles;
196
+ function rebuildMiddlewareForFloating(floating) {
197
+ restoreFloatingStyles?.();
198
+ restoreArrowStyles?.();
199
+ cachedMiddlewareFloating = floating;
200
+ restoreFloatingStyles = options.restoreStyles ? createStyleCleanup(floating, floatingStyleProps) : void 0;
201
+ const arrowEl = floating.querySelector("[data-part=arrow]");
202
+ restoreArrowStyles = options.restoreStyles ? createStyleCleanup(arrowEl, arrowStyleProps) : void 0;
203
+ middleware = [
204
+ getOffsetMiddleware(arrowEl, options),
205
+ getFlipMiddleware(options),
206
+ getShiftMiddleware(options),
207
+ getArrowMiddleware(arrowEl, floating.ownerDocument, options),
208
+ (0, import_middleware.shiftArrowMiddleware)(arrowEl),
209
+ (0, import_middleware.createTransformOriginMiddleware)(
210
+ { gutter: options.gutter, offset: options.offset, overlap: options.overlap },
211
+ arrowEl
212
+ ),
213
+ getSizeMiddleware(options),
214
+ hideWhenDetachedMiddleware(options),
215
+ import_middleware.rectMiddleware
216
+ ];
217
+ }
191
218
  const { placement, strategy, onComplete, onPositioned } = options;
192
219
  let lastX;
193
220
  let lastY;
194
221
  let zIndexComputed = false;
195
- const updatePosition = async () => {
222
+ let lastAnchorForObserve = void 0;
223
+ let lastFloatingForObserve = void 0;
224
+ let cancelAutoUpdate = import_utils.noop;
225
+ const autoUpdateOptions = getAutoUpdateOptions(options.listeners);
226
+ function syncAutoUpdateObservers() {
227
+ if (!options.listeners) return;
228
+ const anchor = resolveAnchor();
229
+ const reference = resolveReference();
230
+ const floating = resolveFloating();
196
231
  if (!reference || !floating) return;
232
+ const anchorChanged = anchorIdentity(anchor) !== anchorIdentity(lastAnchorForObserve);
233
+ const floatingChanged = floating !== lastFloatingForObserve;
234
+ if (anchorChanged || floatingChanged) {
235
+ cancelAutoUpdate();
236
+ lastAnchorForObserve = anchor;
237
+ lastFloatingForObserve = floating;
238
+ cancelAutoUpdate = (0, import_dom.autoUpdate)(reference, floating, runUpdate, autoUpdateOptions);
239
+ }
240
+ }
241
+ async function updatePosition() {
242
+ syncAutoUpdateObservers();
243
+ const floating = resolveFloating();
244
+ if (!floating) return;
245
+ if (floating !== cachedMiddlewareFloating) {
246
+ rebuildMiddlewareForFloating(floating);
247
+ zIndexComputed = false;
248
+ }
249
+ const reference = resolveReference();
250
+ if (!reference) return;
197
251
  const pos = await (0, import_dom.computePosition)(reference, floating, {
198
252
  placement,
199
253
  middleware,
@@ -203,7 +257,6 @@ function getPlacementImpl(referenceOrVirtual, floating, opts = {}) {
203
257
  const win = (0, import_dom_query.getWindow)(floating);
204
258
  const x = roundByDpr(win, pos.x);
205
259
  const y = roundByDpr(win, pos.y);
206
- floating.style.transform = `translate3d(${x}px, ${y}px, 0)`;
207
260
  if (!isApproximatelyEqual(lastX, x)) {
208
261
  floating.style.setProperty("--x", `${x}px`);
209
262
  lastX = x;
@@ -229,20 +282,18 @@ function getPlacementImpl(referenceOrVirtual, floating, opts = {}) {
229
282
  zIndexComputed = true;
230
283
  }
231
284
  }
232
- };
233
- const update = async () => {
285
+ }
286
+ async function runUpdate() {
234
287
  if (opts.updatePosition) {
235
- await opts.updatePosition({ updatePosition, floatingElement: floating });
288
+ await opts.updatePosition({ updatePosition, floatingElement: resolveFloating() });
236
289
  onPositioned?.({ placed: true });
237
290
  } else {
238
291
  await updatePosition();
239
292
  }
240
- };
241
- const autoUpdateOptions = getAutoUpdateOptions(options.listeners);
242
- const cancelAutoUpdate = options.listeners ? (0, import_dom.autoUpdate)(reference, floating, update, autoUpdateOptions) : import_utils.noop;
243
- update();
293
+ }
294
+ runUpdate();
244
295
  return () => {
245
- cancelAutoUpdate?.();
296
+ cancelAutoUpdate();
246
297
  restoreArrowStyles?.();
247
298
  restoreFloatingStyles?.();
248
299
  onPositioned?.({ placed: false });
@@ -254,9 +305,7 @@ function getPlacement(referenceOrFn, floatingOrFn, opts = {}) {
254
305
  const cleanups = [];
255
306
  cleanups.push(
256
307
  func(() => {
257
- const reference = typeof referenceOrFn === "function" ? referenceOrFn() : referenceOrFn;
258
- const floating = typeof floatingOrFn === "function" ? floatingOrFn() : floatingOrFn;
259
- cleanups.push(getPlacementImpl(reference, floating, options));
308
+ cleanups.push(getPlacementImpl(referenceOrFn, floatingOrFn, options));
260
309
  })
261
310
  );
262
311
  return () => {
@@ -1,6 +1,6 @@
1
1
  // src/get-placement.ts
2
2
  import { arrow, autoUpdate, computePosition, flip, hide, limitShift, offset, shift, size } from "@floating-ui/dom";
3
- import { getComputedStyle, getWindow, raf } from "@zag-js/dom-query";
3
+ import { getComputedStyle, getWindow, isHTMLElement, raf } from "@zag-js/dom-query";
4
4
  import { compact, isNull, noop } from "@zag-js/utils";
5
5
  import { getAnchorElement } from "./get-anchor.mjs";
6
6
  import { createTransformOriginMiddleware, rectMiddleware, shiftArrowMiddleware } from "./middleware.mjs";
@@ -142,34 +142,88 @@ function createStyleCleanup(el, props) {
142
142
  }
143
143
  };
144
144
  }
145
- function getPlacementImpl(referenceOrVirtual, floating, opts = {}) {
146
- const anchor = opts.getAnchorElement?.() ?? referenceOrVirtual;
147
- const reference = getAnchorElement(anchor, opts.getAnchorRect);
148
- if (!floating || !reference) return;
145
+ function anchorIdentity(anchor) {
146
+ if (anchor == null) return null;
147
+ if (isHTMLElement(anchor)) return anchor;
148
+ if (typeof anchor === "object" && anchor && "contextElement" in anchor && anchor.contextElement) {
149
+ return anchor.contextElement;
150
+ }
151
+ return anchor;
152
+ }
153
+ function getPlacementImpl(referenceOrVirtual, floatingOrVirtual, opts = {}) {
154
+ const resolveFloating = () => {
155
+ const raw = typeof floatingOrVirtual === "function" ? floatingOrVirtual() : floatingOrVirtual;
156
+ return raw ?? null;
157
+ };
158
+ const resolveAnchor = () => {
159
+ const raw = typeof referenceOrVirtual === "function" ? referenceOrVirtual() : referenceOrVirtual;
160
+ return opts.getAnchorElement?.() ?? raw;
161
+ };
162
+ const resolveReference = () => {
163
+ const anchor = resolveAnchor();
164
+ if (!anchor && !opts.getAnchorRect) return null;
165
+ return getAnchorElement(anchor, opts.getAnchorRect);
166
+ };
149
167
  const options = Object.assign({}, defaultOptions, opts);
150
- const arrowEl = floating.querySelector("[data-part=arrow]");
151
- const restoreFloatingStyles = options.restoreStyles ? createStyleCleanup(floating, floatingStyleProps) : void 0;
152
- const restoreArrowStyles = options.restoreStyles ? createStyleCleanup(arrowEl, arrowStyleProps) : void 0;
153
- const middleware = [
154
- getOffsetMiddleware(arrowEl, options),
155
- getFlipMiddleware(options),
156
- getShiftMiddleware(options),
157
- getArrowMiddleware(arrowEl, floating.ownerDocument, options),
158
- shiftArrowMiddleware(arrowEl),
159
- createTransformOriginMiddleware(
160
- { gutter: options.gutter, offset: options.offset, overlap: options.overlap },
161
- arrowEl
162
- ),
163
- getSizeMiddleware(options),
164
- hideWhenDetachedMiddleware(options),
165
- rectMiddleware
166
- ];
168
+ let middleware = [];
169
+ let cachedMiddlewareFloating = null;
170
+ let restoreFloatingStyles;
171
+ let restoreArrowStyles;
172
+ function rebuildMiddlewareForFloating(floating) {
173
+ restoreFloatingStyles?.();
174
+ restoreArrowStyles?.();
175
+ cachedMiddlewareFloating = floating;
176
+ restoreFloatingStyles = options.restoreStyles ? createStyleCleanup(floating, floatingStyleProps) : void 0;
177
+ const arrowEl = floating.querySelector("[data-part=arrow]");
178
+ restoreArrowStyles = options.restoreStyles ? createStyleCleanup(arrowEl, arrowStyleProps) : void 0;
179
+ middleware = [
180
+ getOffsetMiddleware(arrowEl, options),
181
+ getFlipMiddleware(options),
182
+ getShiftMiddleware(options),
183
+ getArrowMiddleware(arrowEl, floating.ownerDocument, options),
184
+ shiftArrowMiddleware(arrowEl),
185
+ createTransformOriginMiddleware(
186
+ { gutter: options.gutter, offset: options.offset, overlap: options.overlap },
187
+ arrowEl
188
+ ),
189
+ getSizeMiddleware(options),
190
+ hideWhenDetachedMiddleware(options),
191
+ rectMiddleware
192
+ ];
193
+ }
167
194
  const { placement, strategy, onComplete, onPositioned } = options;
168
195
  let lastX;
169
196
  let lastY;
170
197
  let zIndexComputed = false;
171
- const updatePosition = async () => {
198
+ let lastAnchorForObserve = void 0;
199
+ let lastFloatingForObserve = void 0;
200
+ let cancelAutoUpdate = noop;
201
+ const autoUpdateOptions = getAutoUpdateOptions(options.listeners);
202
+ function syncAutoUpdateObservers() {
203
+ if (!options.listeners) return;
204
+ const anchor = resolveAnchor();
205
+ const reference = resolveReference();
206
+ const floating = resolveFloating();
172
207
  if (!reference || !floating) return;
208
+ const anchorChanged = anchorIdentity(anchor) !== anchorIdentity(lastAnchorForObserve);
209
+ const floatingChanged = floating !== lastFloatingForObserve;
210
+ if (anchorChanged || floatingChanged) {
211
+ cancelAutoUpdate();
212
+ lastAnchorForObserve = anchor;
213
+ lastFloatingForObserve = floating;
214
+ cancelAutoUpdate = autoUpdate(reference, floating, runUpdate, autoUpdateOptions);
215
+ }
216
+ }
217
+ async function updatePosition() {
218
+ syncAutoUpdateObservers();
219
+ const floating = resolveFloating();
220
+ if (!floating) return;
221
+ if (floating !== cachedMiddlewareFloating) {
222
+ rebuildMiddlewareForFloating(floating);
223
+ zIndexComputed = false;
224
+ }
225
+ const reference = resolveReference();
226
+ if (!reference) return;
173
227
  const pos = await computePosition(reference, floating, {
174
228
  placement,
175
229
  middleware,
@@ -179,7 +233,6 @@ function getPlacementImpl(referenceOrVirtual, floating, opts = {}) {
179
233
  const win = getWindow(floating);
180
234
  const x = roundByDpr(win, pos.x);
181
235
  const y = roundByDpr(win, pos.y);
182
- floating.style.transform = `translate3d(${x}px, ${y}px, 0)`;
183
236
  if (!isApproximatelyEqual(lastX, x)) {
184
237
  floating.style.setProperty("--x", `${x}px`);
185
238
  lastX = x;
@@ -205,20 +258,18 @@ function getPlacementImpl(referenceOrVirtual, floating, opts = {}) {
205
258
  zIndexComputed = true;
206
259
  }
207
260
  }
208
- };
209
- const update = async () => {
261
+ }
262
+ async function runUpdate() {
210
263
  if (opts.updatePosition) {
211
- await opts.updatePosition({ updatePosition, floatingElement: floating });
264
+ await opts.updatePosition({ updatePosition, floatingElement: resolveFloating() });
212
265
  onPositioned?.({ placed: true });
213
266
  } else {
214
267
  await updatePosition();
215
268
  }
216
- };
217
- const autoUpdateOptions = getAutoUpdateOptions(options.listeners);
218
- const cancelAutoUpdate = options.listeners ? autoUpdate(reference, floating, update, autoUpdateOptions) : noop;
219
- update();
269
+ }
270
+ runUpdate();
220
271
  return () => {
221
- cancelAutoUpdate?.();
272
+ cancelAutoUpdate();
222
273
  restoreArrowStyles?.();
223
274
  restoreFloatingStyles?.();
224
275
  onPositioned?.({ placed: false });
@@ -230,9 +281,7 @@ function getPlacement(referenceOrFn, floatingOrFn, opts = {}) {
230
281
  const cleanups = [];
231
282
  cleanups.push(
232
283
  func(() => {
233
- const reference = typeof referenceOrFn === "function" ? referenceOrFn() : referenceOrFn;
234
- const floating = typeof floatingOrFn === "function" ? floatingOrFn() : floatingOrFn;
235
- cleanups.push(getPlacementImpl(reference, floating, options));
284
+ cleanups.push(getPlacementImpl(referenceOrFn, floatingOrFn, options));
236
285
  })
237
286
  );
238
287
  return () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zag-js/popper",
3
- "version": "1.38.2",
3
+ "version": "1.39.1",
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.6",
26
- "@zag-js/dom-query": "1.38.2",
27
- "@zag-js/utils": "1.38.2"
26
+ "@zag-js/dom-query": "1.39.1",
27
+ "@zag-js/utils": "1.39.1"
28
28
  },
29
29
  "devDependencies": {
30
30
  "clean-package": "2.2.0"