@zag-js/popper 1.21.9 → 1.22.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.
package/dist/index.d.mts CHANGED
@@ -72,8 +72,10 @@ interface PositioningOptions {
72
72
  fitViewport?: boolean | undefined;
73
73
  /**
74
74
  * The overflow boundary of the reference element
75
+ * Accepts a function returning a Boundary, a Boundary directly,
76
+ * or the shorthand string 'clipping-ancestors' which maps to Floating UI's 'clippingAncestors'.
75
77
  */
76
- boundary?: (() => Boundary) | undefined;
78
+ boundary?: (() => Boundary) | Boundary | "clipping-ancestors" | undefined;
77
79
  /**
78
80
  * Options to activate auto-update listeners
79
81
  */
package/dist/index.d.ts CHANGED
@@ -72,8 +72,10 @@ interface PositioningOptions {
72
72
  fitViewport?: boolean | undefined;
73
73
  /**
74
74
  * The overflow boundary of the reference element
75
+ * Accepts a function returning a Boundary, a Boundary directly,
76
+ * or the shorthand string 'clipping-ancestors' which maps to Floating UI's 'clippingAncestors'.
75
77
  */
76
- boundary?: (() => Boundary) | undefined;
78
+ boundary?: (() => Boundary) | Boundary | "clipping-ancestors" | undefined;
77
79
  /**
78
80
  * Options to activate auto-update listeners
79
81
  */
package/dist/index.js CHANGED
@@ -49,32 +49,46 @@ var cssVars = {
49
49
  transformOrigin: toVar("--transform-origin"),
50
50
  arrowOffset: toVar("--arrow-offset")
51
51
  };
52
- var getTransformOrigin = (arrow2) => ({
53
- top: "bottom center",
54
- "top-start": arrow2 ? `${arrow2.x}px bottom` : "left bottom",
55
- "top-end": arrow2 ? `${arrow2.x}px bottom` : "right bottom",
56
- bottom: "top center",
57
- "bottom-start": arrow2 ? `${arrow2.x}px top` : "top left",
58
- "bottom-end": arrow2 ? `${arrow2.x}px top` : "top right",
59
- left: "right center",
60
- "left-start": arrow2 ? `right ${arrow2.y}px` : "right top",
61
- "left-end": arrow2 ? `right ${arrow2.y}px` : "right bottom",
62
- right: "left center",
63
- "right-start": arrow2 ? `left ${arrow2.y}px` : "left top",
64
- "right-end": arrow2 ? `left ${arrow2.y}px` : "left bottom"
65
- });
66
- var transformOriginMiddleware = {
67
- name: "transformOrigin",
68
- fn({ placement, elements, middlewareData }) {
69
- const { arrow: arrow2 } = middlewareData;
70
- const transformOrigin = getTransformOrigin(arrow2)[placement];
71
- const { floating } = elements;
72
- floating.style.setProperty(cssVars.transformOrigin.variable, transformOrigin);
73
- return {
74
- data: { transformOrigin }
75
- };
76
- }
77
- };
52
+ var getSideAxis = (side) => side === "top" || side === "bottom" ? "y" : "x";
53
+ function createTransformOriginMiddleware(opts, arrowEl) {
54
+ return {
55
+ name: "transformOrigin",
56
+ fn(state) {
57
+ const { elements, middlewareData, placement, rects, y } = state;
58
+ const side = placement.split("-")[0];
59
+ const axis = getSideAxis(side);
60
+ const arrowX = middlewareData.arrow?.x || 0;
61
+ const arrowY = middlewareData.arrow?.y || 0;
62
+ const arrowWidth = arrowEl?.clientWidth || 0;
63
+ const arrowHeight = arrowEl?.clientHeight || 0;
64
+ const transformX = arrowX + arrowWidth / 2;
65
+ const transformY = arrowY + arrowHeight / 2;
66
+ const shiftY = Math.abs(middlewareData.shift?.y || 0);
67
+ const halfAnchorHeight = rects.reference.height / 2;
68
+ const arrowOffset = arrowHeight / 2;
69
+ const gutter = opts.offset?.mainAxis ?? opts.gutter;
70
+ const sideOffsetValue = typeof gutter === "number" ? gutter + arrowOffset : gutter ?? arrowOffset;
71
+ const isOverlappingAnchor = shiftY > sideOffsetValue;
72
+ const adjacentTransformOrigin = {
73
+ top: `${transformX}px calc(100% + ${sideOffsetValue}px)`,
74
+ bottom: `${transformX}px ${-sideOffsetValue}px`,
75
+ left: `calc(100% + ${sideOffsetValue}px) ${transformY}px`,
76
+ right: `${-sideOffsetValue}px ${transformY}px`
77
+ }[side];
78
+ const overlapTransformOrigin = `${transformX}px ${rects.reference.y + halfAnchorHeight - y}px`;
79
+ const useOverlap = Boolean(opts.overlap) && axis === "y" && isOverlappingAnchor;
80
+ elements.floating.style.setProperty(
81
+ cssVars.transformOrigin.variable,
82
+ useOverlap ? overlapTransformOrigin : adjacentTransformOrigin
83
+ );
84
+ return {
85
+ data: {
86
+ transformOrigin: useOverlap ? overlapTransformOrigin : adjacentTransformOrigin
87
+ }
88
+ };
89
+ }
90
+ };
91
+ }
78
92
  var rectMiddleware = {
79
93
  name: "rects",
80
94
  fn({ rects }) {
@@ -131,15 +145,14 @@ function roundByDpr(win, value) {
131
145
  const dpr = win.devicePixelRatio || 1;
132
146
  return Math.round(value * dpr) / dpr;
133
147
  }
134
- function getBoundaryMiddleware(opts) {
135
- return utils.runIfFn(opts.boundary);
148
+ function resolveBoundaryOption(boundary) {
149
+ if (typeof boundary === "function") return boundary();
150
+ if (boundary === "clipping-ancestors") return "clippingAncestors";
151
+ return boundary;
136
152
  }
137
- function getArrowMiddleware(arrowElement, opts) {
138
- if (!arrowElement) return;
139
- return dom.arrow({
140
- element: arrowElement,
141
- padding: opts.arrowPadding
142
- });
153
+ function getArrowMiddleware(arrowElement, doc, opts) {
154
+ const element = arrowElement || doc.createElement("div");
155
+ return dom.arrow({ element, padding: opts.arrowPadding });
143
156
  }
144
157
  function getOffsetMiddleware(arrowElement, opts) {
145
158
  if (utils.isNull(opts.offset ?? opts.gutter)) return;
@@ -159,16 +172,18 @@ function getOffsetMiddleware(arrowElement, opts) {
159
172
  }
160
173
  function getFlipMiddleware(opts) {
161
174
  if (!opts.flip) return;
175
+ const boundary = resolveBoundaryOption(opts.boundary);
162
176
  return dom.flip({
163
- boundary: getBoundaryMiddleware(opts),
177
+ ...boundary ? { boundary } : void 0,
164
178
  padding: opts.overflowPadding,
165
179
  fallbackPlacements: opts.flip === true ? void 0 : opts.flip
166
180
  });
167
181
  }
168
182
  function getShiftMiddleware(opts) {
169
183
  if (!opts.slide && !opts.overlap) return;
184
+ const boundary = resolveBoundaryOption(opts.boundary);
170
185
  return dom.shift({
171
- boundary: getBoundaryMiddleware(opts),
186
+ ...boundary ? { boundary } : void 0,
172
187
  mainAxis: opts.slide,
173
188
  crossAxis: opts.overlap,
174
189
  padding: opts.overflowPadding,
@@ -181,9 +196,11 @@ function getSizeMiddleware(opts) {
181
196
  apply({ elements, rects, availableHeight, availableWidth }) {
182
197
  const floating = elements.floating;
183
198
  const referenceWidth = Math.round(rects.reference.width);
199
+ const referenceHeight = Math.round(rects.reference.height);
184
200
  availableWidth = Math.floor(availableWidth);
185
201
  availableHeight = Math.floor(availableHeight);
186
202
  floating.style.setProperty("--reference-width", `${referenceWidth}px`);
203
+ floating.style.setProperty("--reference-height", `${referenceHeight}px`);
187
204
  floating.style.setProperty("--available-width", `${availableWidth}px`);
188
205
  floating.style.setProperty("--available-height", `${availableHeight}px`);
189
206
  }
@@ -191,7 +208,7 @@ function getSizeMiddleware(opts) {
191
208
  }
192
209
  function hideWhenDetachedMiddleware(opts) {
193
210
  if (!opts.hideWhenDetached) return;
194
- return dom.hide({ strategy: "referenceHidden", boundary: opts.boundary?.() ?? "clippingAncestors" });
211
+ return dom.hide({ strategy: "referenceHidden", boundary: resolveBoundaryOption(opts.boundary) ?? "clippingAncestors" });
195
212
  }
196
213
  function getAutoUpdateOptions(opts) {
197
214
  if (!opts) return {};
@@ -209,9 +226,12 @@ function getPlacementImpl(referenceOrVirtual, floating, opts = {}) {
209
226
  getOffsetMiddleware(arrowEl, options),
210
227
  getFlipMiddleware(options),
211
228
  getShiftMiddleware(options),
212
- getArrowMiddleware(arrowEl, options),
229
+ getArrowMiddleware(arrowEl, floating.ownerDocument, options),
213
230
  shiftArrowMiddleware(arrowEl),
214
- transformOriginMiddleware,
231
+ createTransformOriginMiddleware(
232
+ { gutter: options.gutter, offset: options.offset, overlap: options.overlap },
233
+ arrowEl
234
+ ),
215
235
  getSizeMiddleware(options),
216
236
  hideWhenDetachedMiddleware(options),
217
237
  rectMiddleware
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { autoUpdate, offset, flip, shift, limitShift, arrow, size, hide, computePosition } from '@floating-ui/dom';
2
2
  import { raf, isHTMLElement, getWindow, getComputedStyle } from '@zag-js/dom-query';
3
- import { noop, isNull, compact, runIfFn } from '@zag-js/utils';
3
+ import { noop, isNull, compact } from '@zag-js/utils';
4
4
 
5
5
  // src/get-placement.ts
6
6
  function createDOMRect(x = 0, y = 0, width = 0, height = 0) {
@@ -47,32 +47,46 @@ var cssVars = {
47
47
  transformOrigin: toVar("--transform-origin"),
48
48
  arrowOffset: toVar("--arrow-offset")
49
49
  };
50
- var getTransformOrigin = (arrow2) => ({
51
- top: "bottom center",
52
- "top-start": arrow2 ? `${arrow2.x}px bottom` : "left bottom",
53
- "top-end": arrow2 ? `${arrow2.x}px bottom` : "right bottom",
54
- bottom: "top center",
55
- "bottom-start": arrow2 ? `${arrow2.x}px top` : "top left",
56
- "bottom-end": arrow2 ? `${arrow2.x}px top` : "top right",
57
- left: "right center",
58
- "left-start": arrow2 ? `right ${arrow2.y}px` : "right top",
59
- "left-end": arrow2 ? `right ${arrow2.y}px` : "right bottom",
60
- right: "left center",
61
- "right-start": arrow2 ? `left ${arrow2.y}px` : "left top",
62
- "right-end": arrow2 ? `left ${arrow2.y}px` : "left bottom"
63
- });
64
- var transformOriginMiddleware = {
65
- name: "transformOrigin",
66
- fn({ placement, elements, middlewareData }) {
67
- const { arrow: arrow2 } = middlewareData;
68
- const transformOrigin = getTransformOrigin(arrow2)[placement];
69
- const { floating } = elements;
70
- floating.style.setProperty(cssVars.transformOrigin.variable, transformOrigin);
71
- return {
72
- data: { transformOrigin }
73
- };
74
- }
75
- };
50
+ var getSideAxis = (side) => side === "top" || side === "bottom" ? "y" : "x";
51
+ function createTransformOriginMiddleware(opts, arrowEl) {
52
+ return {
53
+ name: "transformOrigin",
54
+ fn(state) {
55
+ const { elements, middlewareData, placement, rects, y } = state;
56
+ const side = placement.split("-")[0];
57
+ const axis = getSideAxis(side);
58
+ const arrowX = middlewareData.arrow?.x || 0;
59
+ const arrowY = middlewareData.arrow?.y || 0;
60
+ const arrowWidth = arrowEl?.clientWidth || 0;
61
+ const arrowHeight = arrowEl?.clientHeight || 0;
62
+ const transformX = arrowX + arrowWidth / 2;
63
+ const transformY = arrowY + arrowHeight / 2;
64
+ const shiftY = Math.abs(middlewareData.shift?.y || 0);
65
+ const halfAnchorHeight = rects.reference.height / 2;
66
+ const arrowOffset = arrowHeight / 2;
67
+ const gutter = opts.offset?.mainAxis ?? opts.gutter;
68
+ const sideOffsetValue = typeof gutter === "number" ? gutter + arrowOffset : gutter ?? arrowOffset;
69
+ const isOverlappingAnchor = shiftY > sideOffsetValue;
70
+ const adjacentTransformOrigin = {
71
+ top: `${transformX}px calc(100% + ${sideOffsetValue}px)`,
72
+ bottom: `${transformX}px ${-sideOffsetValue}px`,
73
+ left: `calc(100% + ${sideOffsetValue}px) ${transformY}px`,
74
+ right: `${-sideOffsetValue}px ${transformY}px`
75
+ }[side];
76
+ const overlapTransformOrigin = `${transformX}px ${rects.reference.y + halfAnchorHeight - y}px`;
77
+ const useOverlap = Boolean(opts.overlap) && axis === "y" && isOverlappingAnchor;
78
+ elements.floating.style.setProperty(
79
+ cssVars.transformOrigin.variable,
80
+ useOverlap ? overlapTransformOrigin : adjacentTransformOrigin
81
+ );
82
+ return {
83
+ data: {
84
+ transformOrigin: useOverlap ? overlapTransformOrigin : adjacentTransformOrigin
85
+ }
86
+ };
87
+ }
88
+ };
89
+ }
76
90
  var rectMiddleware = {
77
91
  name: "rects",
78
92
  fn({ rects }) {
@@ -129,15 +143,14 @@ function roundByDpr(win, value) {
129
143
  const dpr = win.devicePixelRatio || 1;
130
144
  return Math.round(value * dpr) / dpr;
131
145
  }
132
- function getBoundaryMiddleware(opts) {
133
- return runIfFn(opts.boundary);
146
+ function resolveBoundaryOption(boundary) {
147
+ if (typeof boundary === "function") return boundary();
148
+ if (boundary === "clipping-ancestors") return "clippingAncestors";
149
+ return boundary;
134
150
  }
135
- function getArrowMiddleware(arrowElement, opts) {
136
- if (!arrowElement) return;
137
- return arrow({
138
- element: arrowElement,
139
- padding: opts.arrowPadding
140
- });
151
+ function getArrowMiddleware(arrowElement, doc, opts) {
152
+ const element = arrowElement || doc.createElement("div");
153
+ return arrow({ element, padding: opts.arrowPadding });
141
154
  }
142
155
  function getOffsetMiddleware(arrowElement, opts) {
143
156
  if (isNull(opts.offset ?? opts.gutter)) return;
@@ -157,16 +170,18 @@ function getOffsetMiddleware(arrowElement, opts) {
157
170
  }
158
171
  function getFlipMiddleware(opts) {
159
172
  if (!opts.flip) return;
173
+ const boundary = resolveBoundaryOption(opts.boundary);
160
174
  return flip({
161
- boundary: getBoundaryMiddleware(opts),
175
+ ...boundary ? { boundary } : void 0,
162
176
  padding: opts.overflowPadding,
163
177
  fallbackPlacements: opts.flip === true ? void 0 : opts.flip
164
178
  });
165
179
  }
166
180
  function getShiftMiddleware(opts) {
167
181
  if (!opts.slide && !opts.overlap) return;
182
+ const boundary = resolveBoundaryOption(opts.boundary);
168
183
  return shift({
169
- boundary: getBoundaryMiddleware(opts),
184
+ ...boundary ? { boundary } : void 0,
170
185
  mainAxis: opts.slide,
171
186
  crossAxis: opts.overlap,
172
187
  padding: opts.overflowPadding,
@@ -179,9 +194,11 @@ function getSizeMiddleware(opts) {
179
194
  apply({ elements, rects, availableHeight, availableWidth }) {
180
195
  const floating = elements.floating;
181
196
  const referenceWidth = Math.round(rects.reference.width);
197
+ const referenceHeight = Math.round(rects.reference.height);
182
198
  availableWidth = Math.floor(availableWidth);
183
199
  availableHeight = Math.floor(availableHeight);
184
200
  floating.style.setProperty("--reference-width", `${referenceWidth}px`);
201
+ floating.style.setProperty("--reference-height", `${referenceHeight}px`);
185
202
  floating.style.setProperty("--available-width", `${availableWidth}px`);
186
203
  floating.style.setProperty("--available-height", `${availableHeight}px`);
187
204
  }
@@ -189,7 +206,7 @@ function getSizeMiddleware(opts) {
189
206
  }
190
207
  function hideWhenDetachedMiddleware(opts) {
191
208
  if (!opts.hideWhenDetached) return;
192
- return hide({ strategy: "referenceHidden", boundary: opts.boundary?.() ?? "clippingAncestors" });
209
+ return hide({ strategy: "referenceHidden", boundary: resolveBoundaryOption(opts.boundary) ?? "clippingAncestors" });
193
210
  }
194
211
  function getAutoUpdateOptions(opts) {
195
212
  if (!opts) return {};
@@ -207,9 +224,12 @@ function getPlacementImpl(referenceOrVirtual, floating, opts = {}) {
207
224
  getOffsetMiddleware(arrowEl, options),
208
225
  getFlipMiddleware(options),
209
226
  getShiftMiddleware(options),
210
- getArrowMiddleware(arrowEl, options),
227
+ getArrowMiddleware(arrowEl, floating.ownerDocument, options),
211
228
  shiftArrowMiddleware(arrowEl),
212
- transformOriginMiddleware,
229
+ createTransformOriginMiddleware(
230
+ { gutter: options.gutter, offset: options.offset, overlap: options.overlap },
231
+ arrowEl
232
+ ),
213
233
  getSizeMiddleware(options),
214
234
  hideWhenDetachedMiddleware(options),
215
235
  rectMiddleware
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zag-js/popper",
3
- "version": "1.21.9",
3
+ "version": "1.22.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.4",
26
- "@zag-js/dom-query": "1.21.9",
27
- "@zag-js/utils": "1.21.9"
26
+ "@zag-js/dom-query": "1.22.1",
27
+ "@zag-js/utils": "1.22.1"
28
28
  },
29
29
  "devDependencies": {
30
30
  "clean-package": "2.2.0"