@zag-js/popper 0.0.0-20220802150625

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Chakra UI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,19 @@
1
+ # @zag-js/popper
2
+
3
+ Dynamic positioning logic for ui machines
4
+
5
+ ## Installation
6
+
7
+ ```sh
8
+ yarn add @zag-js/popper
9
+ # or
10
+ npm i @zag-js/popper
11
+ ```
12
+
13
+ ## Contribution
14
+
15
+ Yes please! See the [contributing guidelines](https://github.com/chakra-ui/zag/blob/main/CONTRIBUTING.md) for details.
16
+
17
+ ## Licence
18
+
19
+ This project is licensed under the terms of the [MIT license](https://github.com/chakra-ui/zag/blob/main/LICENSE).
@@ -0,0 +1,109 @@
1
+ import { Placement, Boundary, ComputePositionReturn, ComputePositionConfig, VirtualElement } from '@floating-ui/dom';
2
+ export { Placement } from '@floating-ui/dom';
3
+
4
+ declare type AutoUpdateOptions = {
5
+ ancestorScroll?: boolean;
6
+ ancestorResize?: boolean;
7
+ referenceResize?: boolean;
8
+ };
9
+
10
+ declare type PositioningOptions = {
11
+ /**
12
+ * The strategy to use for positioning
13
+ */
14
+ strategy?: "absolute" | "fixed";
15
+ /**
16
+ * The initial placement of the floating element
17
+ */
18
+ placement?: Placement;
19
+ /**
20
+ * The offset of the floating element
21
+ */
22
+ offset?: {
23
+ mainAxis?: number;
24
+ crossAxis?: number;
25
+ };
26
+ /**
27
+ * The main axis offset or gap between the reference and floating elements
28
+ */
29
+ gutter?: number;
30
+ /**
31
+ * The virtual padding around the viewport edges to check for overflow
32
+ */
33
+ overflowPadding?: number;
34
+ /**
35
+ * Whether to flip the placement
36
+ */
37
+ flip?: boolean;
38
+ /**
39
+ * Whether the floating element can overlap the reference element
40
+ * @default false
41
+ */
42
+ overlap?: boolean;
43
+ /**
44
+ * Whether to make the floating element same width as the reference element
45
+ */
46
+ sameWidth?: boolean;
47
+ /**
48
+ * Whether the popover should fit the viewport.
49
+ */
50
+ fitViewport?: boolean;
51
+ /**
52
+ * The overflow boundary of the reference element
53
+ */
54
+ boundary?: Boundary;
55
+ /**
56
+ * Options to activate auto-update listeners
57
+ */
58
+ listeners?: boolean | AutoUpdateOptions;
59
+ /**
60
+ * Function called when the placement is computed
61
+ */
62
+ onComplete?(data: ComputePositionReturn & {
63
+ compute: (config?: Omit<ComputePositionConfig, "platform">) => void;
64
+ }): void;
65
+ /**
66
+ * Function called on cleanup of all listeners
67
+ */
68
+ onCleanup?: VoidFunction;
69
+ };
70
+ declare type BasePlacement = "top" | "right" | "bottom" | "left";
71
+
72
+ declare function getPlacement(reference: HTMLElement | VirtualElement | null, floating: HTMLElement | null, opts?: PositioningOptions): (() => void) | undefined;
73
+ declare function getBasePlacement(placement: Placement): BasePlacement;
74
+
75
+ declare type Options = {
76
+ measured: boolean;
77
+ strategy?: "absolute" | "fixed";
78
+ placement?: Placement;
79
+ };
80
+ declare function getPlacementStyles(options: Options): {
81
+ arrow: {
82
+ readonly [x: string]: string | 0 | undefined;
83
+ readonly position: "absolute";
84
+ readonly width: string;
85
+ readonly height: string;
86
+ readonly opacity: 0 | undefined;
87
+ };
88
+ innerArrow: {
89
+ readonly transform: any;
90
+ readonly background: string;
91
+ readonly top: "0";
92
+ readonly left: "0";
93
+ readonly width: "100%";
94
+ readonly height: "100%";
95
+ readonly position: "absolute";
96
+ readonly zIndex: "inherit";
97
+ };
98
+ floating: {
99
+ position: "absolute" | "fixed";
100
+ readonly top?: 0 | undefined;
101
+ readonly left?: 0 | undefined;
102
+ readonly opacity?: 0 | undefined;
103
+ readonly transform?: "translate3d(0, -200%, 0)" | undefined;
104
+ readonly pointerEvents?: "none" | undefined;
105
+ readonly minWidth: "max-content";
106
+ };
107
+ };
108
+
109
+ export { PositioningOptions, getBasePlacement, getPlacement, getPlacementStyles };
package/dist/index.js ADDED
@@ -0,0 +1,343 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ getBasePlacement: () => getBasePlacement,
24
+ getPlacement: () => getPlacement,
25
+ getPlacementStyles: () => getPlacementStyles
26
+ });
27
+ module.exports = __toCommonJS(src_exports);
28
+
29
+ // src/get-placement.ts
30
+ var import_dom2 = require("@floating-ui/dom");
31
+
32
+ // ../core/dist/index.mjs
33
+ var callAll = (...fns) => (...a) => {
34
+ fns.forEach(function(fn) {
35
+ fn == null ? void 0 : fn(...a);
36
+ });
37
+ };
38
+ var isBoolean = (v) => v === true || v === false;
39
+
40
+ // src/auto-update.ts
41
+ var import_dom = require("@floating-ui/dom");
42
+
43
+ // ../dom/dist/index.mjs
44
+ var runIfFn = (v, ...a) => {
45
+ const res = typeof v === "function" ? v(...a) : v;
46
+ return res ?? void 0;
47
+ };
48
+ var hasProp = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
49
+ function isHTMLElement(v) {
50
+ return typeof v === "object" && (v == null ? void 0 : v.nodeType) === Node.ELEMENT_NODE && typeof (v == null ? void 0 : v.nodeName) === "string";
51
+ }
52
+ var isRef = (v) => hasProp(v, "current");
53
+ function addDomEvent(target, eventName, handler, options) {
54
+ const node = isRef(target) ? target.current : runIfFn(target);
55
+ node == null ? void 0 : node.addEventListener(eventName, handler, options);
56
+ return () => {
57
+ node == null ? void 0 : node.removeEventListener(eventName, handler, options);
58
+ };
59
+ }
60
+ function getObservedElements() {
61
+ ;
62
+ globalThis.__rectObserverMap__ = globalThis.__rectObserverMap__ || /* @__PURE__ */ new Map();
63
+ return globalThis.__rectObserverMap__;
64
+ }
65
+ function observeElementRect(el, fn) {
66
+ const observedElements = getObservedElements();
67
+ const data = observedElements.get(el);
68
+ if (!data) {
69
+ observedElements.set(el, { rect: {}, callbacks: [fn] });
70
+ if (observedElements.size === 1) {
71
+ rafId = requestAnimationFrame(runLoop);
72
+ }
73
+ } else {
74
+ data.callbacks.push(fn);
75
+ fn(el.getBoundingClientRect());
76
+ }
77
+ return function unobserve() {
78
+ const data2 = observedElements.get(el);
79
+ if (!data2)
80
+ return;
81
+ const index = data2.callbacks.indexOf(fn);
82
+ if (index > -1) {
83
+ data2.callbacks.splice(index, 1);
84
+ }
85
+ if (data2.callbacks.length === 0) {
86
+ observedElements.delete(el);
87
+ if (observedElements.size === 0) {
88
+ cancelAnimationFrame(rafId);
89
+ }
90
+ }
91
+ };
92
+ }
93
+ var rafId;
94
+ function runLoop() {
95
+ const observedElements = getObservedElements();
96
+ const changedRectsData = [];
97
+ observedElements.forEach((data, element) => {
98
+ const newRect = element.getBoundingClientRect();
99
+ if (!isEqual(data.rect, newRect)) {
100
+ data.rect = newRect;
101
+ changedRectsData.push(data);
102
+ }
103
+ });
104
+ changedRectsData.forEach((data) => {
105
+ data.callbacks.forEach((callback) => callback(data.rect));
106
+ });
107
+ rafId = requestAnimationFrame(runLoop);
108
+ }
109
+ function isEqual(rect1, rect2) {
110
+ return rect1.width === rect2.width && rect1.height === rect2.height && rect1.top === rect2.top && rect1.right === rect2.right && rect1.bottom === rect2.bottom && rect1.left === rect2.left;
111
+ }
112
+
113
+ // src/auto-update.ts
114
+ function resolveOptions(option) {
115
+ const bool = isBoolean(option);
116
+ return {
117
+ ancestorResize: bool ? option : option.ancestorResize ?? true,
118
+ ancestorScroll: bool ? option : option.ancestorScroll ?? true,
119
+ referenceResize: bool ? option : option.referenceResize ?? true
120
+ };
121
+ }
122
+ function autoUpdate(reference, floating, update, options = false) {
123
+ const { ancestorScroll, ancestorResize, referenceResize } = resolveOptions(options);
124
+ const useAncestors = ancestorScroll || ancestorResize;
125
+ const ancestors = [];
126
+ if (useAncestors && isHTMLElement(reference)) {
127
+ ancestors.push(...(0, import_dom.getOverflowAncestors)(reference));
128
+ }
129
+ function addResizeListeners() {
130
+ let cleanups = [observeElementRect(floating, update)];
131
+ if (referenceResize && isHTMLElement(reference)) {
132
+ cleanups.push(observeElementRect(reference, update));
133
+ }
134
+ cleanups.push(callAll(...ancestors.map((el) => addDomEvent(el, "resize", update))));
135
+ return () => cleanups.forEach((fn) => fn());
136
+ }
137
+ function addScrollListeners() {
138
+ return callAll(...ancestors.map((el) => addDomEvent(el, "scroll", update, { passive: true })));
139
+ }
140
+ return callAll(addResizeListeners(), addScrollListeners());
141
+ }
142
+
143
+ // src/middleware.ts
144
+ var toVar = (value) => ({ variable: value, reference: `var(${value})` });
145
+ var cssVars = {
146
+ arrowSize: toVar("--arrow-size"),
147
+ arrowSizeHalf: toVar("--arrow-size-half"),
148
+ arrowBg: toVar("--arrow-background"),
149
+ transformOrigin: toVar("--transform-origin"),
150
+ arrowOffset: toVar("--arrow-offset")
151
+ };
152
+ var getTransformOrigin = (arrow2) => ({
153
+ top: "bottom center",
154
+ "top-start": arrow2 ? `${arrow2.x}px bottom` : "left bottom",
155
+ "top-end": arrow2 ? `${arrow2.x}px bottom` : "right bottom",
156
+ bottom: "top center",
157
+ "bottom-start": arrow2 ? `${arrow2.x}px top` : "top left",
158
+ "bottom-end": arrow2 ? `${arrow2.x}px top` : "top right",
159
+ left: "right center",
160
+ "left-start": arrow2 ? `right ${arrow2.y}px` : "right top",
161
+ "left-end": arrow2 ? `right ${arrow2.y}px` : "right bottom",
162
+ right: "left center",
163
+ "right-start": arrow2 ? `left ${arrow2.y}px` : "left top",
164
+ "right-end": arrow2 ? `left ${arrow2.y}px` : "left bottom"
165
+ });
166
+ var transformOrigin = {
167
+ name: "transformOrigin",
168
+ fn({ placement, elements, middlewareData }) {
169
+ const { arrow: arrow2 } = middlewareData;
170
+ const transformOrigin2 = getTransformOrigin(arrow2)[placement];
171
+ const { floating } = elements;
172
+ floating.style.setProperty(cssVars.transformOrigin.variable, transformOrigin2);
173
+ return {
174
+ data: { transformOrigin: transformOrigin2 }
175
+ };
176
+ }
177
+ };
178
+ var shiftArrow = (opts) => ({
179
+ name: "shiftArrow",
180
+ fn({ placement, middlewareData }) {
181
+ const { element: arrow2 } = opts;
182
+ if (middlewareData.arrow) {
183
+ const { x, y } = middlewareData.arrow;
184
+ const dir = placement.split("-")[0];
185
+ Object.assign(arrow2.style, {
186
+ left: x != null ? `${x}px` : "",
187
+ top: y != null ? `${y}px` : "",
188
+ [dir]: `calc(100% + ${cssVars.arrowOffset.reference})`
189
+ });
190
+ }
191
+ return {};
192
+ }
193
+ });
194
+
195
+ // src/get-placement.ts
196
+ var defaultOptions = {
197
+ strategy: "absolute",
198
+ placement: "bottom",
199
+ listeners: true,
200
+ gutter: 8,
201
+ flip: true,
202
+ sameWidth: false,
203
+ overflowPadding: 8
204
+ };
205
+ function getPlacement(reference, floating, opts = {}) {
206
+ if (!floating || !reference)
207
+ return;
208
+ const options = Object.assign({}, defaultOptions, opts);
209
+ const arrowEl = floating.querySelector("[data-part=arrow]");
210
+ const middleware = [];
211
+ if (options.flip) {
212
+ middleware.push(
213
+ (0, import_dom2.flip)({
214
+ boundary: options.boundary,
215
+ padding: options.overflowPadding
216
+ })
217
+ );
218
+ }
219
+ if (options.gutter || options.offset) {
220
+ const arrowOffset = arrowEl ? arrowEl.offsetHeight / 2 : 0;
221
+ const data = options.gutter ? { mainAxis: options.gutter } : options.offset;
222
+ if ((data == null ? void 0 : data.mainAxis) != null)
223
+ data.mainAxis += arrowOffset;
224
+ middleware.push((0, import_dom2.offset)(data));
225
+ }
226
+ middleware.push(
227
+ (0, import_dom2.shift)({
228
+ boundary: options.boundary,
229
+ crossAxis: options.overlap,
230
+ padding: options.overflowPadding
231
+ })
232
+ );
233
+ if (arrowEl) {
234
+ middleware.push(
235
+ (0, import_dom2.arrow)({ element: arrowEl, padding: 8 }),
236
+ shiftArrow({ element: arrowEl })
237
+ );
238
+ }
239
+ middleware.push(transformOrigin);
240
+ middleware.push(
241
+ (0, import_dom2.size)({
242
+ padding: options.overflowPadding,
243
+ apply({ rects, availableHeight, availableWidth }) {
244
+ const referenceWidth = Math.round(rects.reference.width);
245
+ floating.style.setProperty("--reference-width", `${referenceWidth}px`);
246
+ floating.style.setProperty("--available-width", `${availableWidth}px`);
247
+ floating.style.setProperty("--available-height", `${availableHeight}px`);
248
+ if (options.sameWidth) {
249
+ Object.assign(floating.style, {
250
+ width: `${referenceWidth}px`,
251
+ minWidth: "unset"
252
+ });
253
+ }
254
+ if (options.fitViewport) {
255
+ Object.assign(floating.style, {
256
+ maxWidth: `${availableWidth}px`,
257
+ maxHeight: `${availableHeight}px`
258
+ });
259
+ }
260
+ }
261
+ })
262
+ );
263
+ function compute(config = {}) {
264
+ if (!reference || !floating)
265
+ return;
266
+ const { placement, strategy } = options;
267
+ (0, import_dom2.computePosition)(reference, floating, {
268
+ placement,
269
+ middleware,
270
+ strategy,
271
+ ...config
272
+ }).then((data) => {
273
+ var _a;
274
+ const x = Math.round(data.x);
275
+ const y = Math.round(data.y);
276
+ Object.assign(floating.style, {
277
+ position: data.strategy,
278
+ top: "0",
279
+ left: "0",
280
+ transform: `translate3d(${x}px, ${y}px, 0)`
281
+ });
282
+ (_a = options.onComplete) == null ? void 0 : _a.call(options, { ...data, compute });
283
+ });
284
+ }
285
+ compute();
286
+ return callAll(
287
+ options.listeners ? autoUpdate(reference, floating, compute, options.listeners) : void 0,
288
+ options.onCleanup
289
+ );
290
+ }
291
+ function getBasePlacement(placement) {
292
+ return placement.split("-")[0];
293
+ }
294
+
295
+ // src/get-styles.ts
296
+ var UNMEASURED_FLOATING_STYLE = {
297
+ position: "fixed",
298
+ top: 0,
299
+ left: 0,
300
+ opacity: 0,
301
+ transform: "translate3d(0, -200%, 0)",
302
+ pointerEvents: "none"
303
+ };
304
+ var ARROW_FLOATING_STYLE = {
305
+ bottom: "rotate(45deg)",
306
+ left: "rotate(135deg)",
307
+ top: "rotate(225deg)",
308
+ right: "rotate(315deg)"
309
+ };
310
+ function getPlacementStyles(options) {
311
+ const { measured, strategy = "absolute", placement = "bottom" } = options;
312
+ return {
313
+ arrow: {
314
+ position: "absolute",
315
+ width: cssVars.arrowSize.reference,
316
+ height: cssVars.arrowSize.reference,
317
+ [cssVars.arrowSizeHalf.variable]: `calc(${cssVars.arrowSize.reference} / 2)`,
318
+ [cssVars.arrowOffset.variable]: `calc(${cssVars.arrowSizeHalf.reference} * -1)`,
319
+ opacity: !measured ? 0 : void 0
320
+ },
321
+ innerArrow: {
322
+ transform: ARROW_FLOATING_STYLE[placement.split("-")[0]],
323
+ background: cssVars.arrowBg.reference,
324
+ top: "0",
325
+ left: "0",
326
+ width: "100%",
327
+ height: "100%",
328
+ position: "absolute",
329
+ zIndex: "inherit"
330
+ },
331
+ floating: {
332
+ position: strategy,
333
+ minWidth: "max-content",
334
+ ...!measured && UNMEASURED_FLOATING_STYLE
335
+ }
336
+ };
337
+ }
338
+ // Annotate the CommonJS export names for ESM import in node:
339
+ 0 && (module.exports = {
340
+ getBasePlacement,
341
+ getPlacement,
342
+ getPlacementStyles
343
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,314 @@
1
+ // src/get-placement.ts
2
+ import { arrow, computePosition, flip, offset, shift, size } from "@floating-ui/dom";
3
+
4
+ // ../core/dist/index.mjs
5
+ var callAll = (...fns) => (...a) => {
6
+ fns.forEach(function(fn) {
7
+ fn == null ? void 0 : fn(...a);
8
+ });
9
+ };
10
+ var isBoolean = (v) => v === true || v === false;
11
+
12
+ // src/auto-update.ts
13
+ import { getOverflowAncestors } from "@floating-ui/dom";
14
+
15
+ // ../dom/dist/index.mjs
16
+ var runIfFn = (v, ...a) => {
17
+ const res = typeof v === "function" ? v(...a) : v;
18
+ return res ?? void 0;
19
+ };
20
+ var hasProp = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
21
+ function isHTMLElement(v) {
22
+ return typeof v === "object" && (v == null ? void 0 : v.nodeType) === Node.ELEMENT_NODE && typeof (v == null ? void 0 : v.nodeName) === "string";
23
+ }
24
+ var isRef = (v) => hasProp(v, "current");
25
+ function addDomEvent(target, eventName, handler, options) {
26
+ const node = isRef(target) ? target.current : runIfFn(target);
27
+ node == null ? void 0 : node.addEventListener(eventName, handler, options);
28
+ return () => {
29
+ node == null ? void 0 : node.removeEventListener(eventName, handler, options);
30
+ };
31
+ }
32
+ function getObservedElements() {
33
+ ;
34
+ globalThis.__rectObserverMap__ = globalThis.__rectObserverMap__ || /* @__PURE__ */ new Map();
35
+ return globalThis.__rectObserverMap__;
36
+ }
37
+ function observeElementRect(el, fn) {
38
+ const observedElements = getObservedElements();
39
+ const data = observedElements.get(el);
40
+ if (!data) {
41
+ observedElements.set(el, { rect: {}, callbacks: [fn] });
42
+ if (observedElements.size === 1) {
43
+ rafId = requestAnimationFrame(runLoop);
44
+ }
45
+ } else {
46
+ data.callbacks.push(fn);
47
+ fn(el.getBoundingClientRect());
48
+ }
49
+ return function unobserve() {
50
+ const data2 = observedElements.get(el);
51
+ if (!data2)
52
+ return;
53
+ const index = data2.callbacks.indexOf(fn);
54
+ if (index > -1) {
55
+ data2.callbacks.splice(index, 1);
56
+ }
57
+ if (data2.callbacks.length === 0) {
58
+ observedElements.delete(el);
59
+ if (observedElements.size === 0) {
60
+ cancelAnimationFrame(rafId);
61
+ }
62
+ }
63
+ };
64
+ }
65
+ var rafId;
66
+ function runLoop() {
67
+ const observedElements = getObservedElements();
68
+ const changedRectsData = [];
69
+ observedElements.forEach((data, element) => {
70
+ const newRect = element.getBoundingClientRect();
71
+ if (!isEqual(data.rect, newRect)) {
72
+ data.rect = newRect;
73
+ changedRectsData.push(data);
74
+ }
75
+ });
76
+ changedRectsData.forEach((data) => {
77
+ data.callbacks.forEach((callback) => callback(data.rect));
78
+ });
79
+ rafId = requestAnimationFrame(runLoop);
80
+ }
81
+ function isEqual(rect1, rect2) {
82
+ return rect1.width === rect2.width && rect1.height === rect2.height && rect1.top === rect2.top && rect1.right === rect2.right && rect1.bottom === rect2.bottom && rect1.left === rect2.left;
83
+ }
84
+
85
+ // src/auto-update.ts
86
+ function resolveOptions(option) {
87
+ const bool = isBoolean(option);
88
+ return {
89
+ ancestorResize: bool ? option : option.ancestorResize ?? true,
90
+ ancestorScroll: bool ? option : option.ancestorScroll ?? true,
91
+ referenceResize: bool ? option : option.referenceResize ?? true
92
+ };
93
+ }
94
+ function autoUpdate(reference, floating, update, options = false) {
95
+ const { ancestorScroll, ancestorResize, referenceResize } = resolveOptions(options);
96
+ const useAncestors = ancestorScroll || ancestorResize;
97
+ const ancestors = [];
98
+ if (useAncestors && isHTMLElement(reference)) {
99
+ ancestors.push(...getOverflowAncestors(reference));
100
+ }
101
+ function addResizeListeners() {
102
+ let cleanups = [observeElementRect(floating, update)];
103
+ if (referenceResize && isHTMLElement(reference)) {
104
+ cleanups.push(observeElementRect(reference, update));
105
+ }
106
+ cleanups.push(callAll(...ancestors.map((el) => addDomEvent(el, "resize", update))));
107
+ return () => cleanups.forEach((fn) => fn());
108
+ }
109
+ function addScrollListeners() {
110
+ return callAll(...ancestors.map((el) => addDomEvent(el, "scroll", update, { passive: true })));
111
+ }
112
+ return callAll(addResizeListeners(), addScrollListeners());
113
+ }
114
+
115
+ // src/middleware.ts
116
+ var toVar = (value) => ({ variable: value, reference: `var(${value})` });
117
+ var cssVars = {
118
+ arrowSize: toVar("--arrow-size"),
119
+ arrowSizeHalf: toVar("--arrow-size-half"),
120
+ arrowBg: toVar("--arrow-background"),
121
+ transformOrigin: toVar("--transform-origin"),
122
+ arrowOffset: toVar("--arrow-offset")
123
+ };
124
+ var getTransformOrigin = (arrow2) => ({
125
+ top: "bottom center",
126
+ "top-start": arrow2 ? `${arrow2.x}px bottom` : "left bottom",
127
+ "top-end": arrow2 ? `${arrow2.x}px bottom` : "right bottom",
128
+ bottom: "top center",
129
+ "bottom-start": arrow2 ? `${arrow2.x}px top` : "top left",
130
+ "bottom-end": arrow2 ? `${arrow2.x}px top` : "top right",
131
+ left: "right center",
132
+ "left-start": arrow2 ? `right ${arrow2.y}px` : "right top",
133
+ "left-end": arrow2 ? `right ${arrow2.y}px` : "right bottom",
134
+ right: "left center",
135
+ "right-start": arrow2 ? `left ${arrow2.y}px` : "left top",
136
+ "right-end": arrow2 ? `left ${arrow2.y}px` : "left bottom"
137
+ });
138
+ var transformOrigin = {
139
+ name: "transformOrigin",
140
+ fn({ placement, elements, middlewareData }) {
141
+ const { arrow: arrow2 } = middlewareData;
142
+ const transformOrigin2 = getTransformOrigin(arrow2)[placement];
143
+ const { floating } = elements;
144
+ floating.style.setProperty(cssVars.transformOrigin.variable, transformOrigin2);
145
+ return {
146
+ data: { transformOrigin: transformOrigin2 }
147
+ };
148
+ }
149
+ };
150
+ var shiftArrow = (opts) => ({
151
+ name: "shiftArrow",
152
+ fn({ placement, middlewareData }) {
153
+ const { element: arrow2 } = opts;
154
+ if (middlewareData.arrow) {
155
+ const { x, y } = middlewareData.arrow;
156
+ const dir = placement.split("-")[0];
157
+ Object.assign(arrow2.style, {
158
+ left: x != null ? `${x}px` : "",
159
+ top: y != null ? `${y}px` : "",
160
+ [dir]: `calc(100% + ${cssVars.arrowOffset.reference})`
161
+ });
162
+ }
163
+ return {};
164
+ }
165
+ });
166
+
167
+ // src/get-placement.ts
168
+ var defaultOptions = {
169
+ strategy: "absolute",
170
+ placement: "bottom",
171
+ listeners: true,
172
+ gutter: 8,
173
+ flip: true,
174
+ sameWidth: false,
175
+ overflowPadding: 8
176
+ };
177
+ function getPlacement(reference, floating, opts = {}) {
178
+ if (!floating || !reference)
179
+ return;
180
+ const options = Object.assign({}, defaultOptions, opts);
181
+ const arrowEl = floating.querySelector("[data-part=arrow]");
182
+ const middleware = [];
183
+ if (options.flip) {
184
+ middleware.push(
185
+ flip({
186
+ boundary: options.boundary,
187
+ padding: options.overflowPadding
188
+ })
189
+ );
190
+ }
191
+ if (options.gutter || options.offset) {
192
+ const arrowOffset = arrowEl ? arrowEl.offsetHeight / 2 : 0;
193
+ const data = options.gutter ? { mainAxis: options.gutter } : options.offset;
194
+ if ((data == null ? void 0 : data.mainAxis) != null)
195
+ data.mainAxis += arrowOffset;
196
+ middleware.push(offset(data));
197
+ }
198
+ middleware.push(
199
+ shift({
200
+ boundary: options.boundary,
201
+ crossAxis: options.overlap,
202
+ padding: options.overflowPadding
203
+ })
204
+ );
205
+ if (arrowEl) {
206
+ middleware.push(
207
+ arrow({ element: arrowEl, padding: 8 }),
208
+ shiftArrow({ element: arrowEl })
209
+ );
210
+ }
211
+ middleware.push(transformOrigin);
212
+ middleware.push(
213
+ size({
214
+ padding: options.overflowPadding,
215
+ apply({ rects, availableHeight, availableWidth }) {
216
+ const referenceWidth = Math.round(rects.reference.width);
217
+ floating.style.setProperty("--reference-width", `${referenceWidth}px`);
218
+ floating.style.setProperty("--available-width", `${availableWidth}px`);
219
+ floating.style.setProperty("--available-height", `${availableHeight}px`);
220
+ if (options.sameWidth) {
221
+ Object.assign(floating.style, {
222
+ width: `${referenceWidth}px`,
223
+ minWidth: "unset"
224
+ });
225
+ }
226
+ if (options.fitViewport) {
227
+ Object.assign(floating.style, {
228
+ maxWidth: `${availableWidth}px`,
229
+ maxHeight: `${availableHeight}px`
230
+ });
231
+ }
232
+ }
233
+ })
234
+ );
235
+ function compute(config = {}) {
236
+ if (!reference || !floating)
237
+ return;
238
+ const { placement, strategy } = options;
239
+ computePosition(reference, floating, {
240
+ placement,
241
+ middleware,
242
+ strategy,
243
+ ...config
244
+ }).then((data) => {
245
+ var _a;
246
+ const x = Math.round(data.x);
247
+ const y = Math.round(data.y);
248
+ Object.assign(floating.style, {
249
+ position: data.strategy,
250
+ top: "0",
251
+ left: "0",
252
+ transform: `translate3d(${x}px, ${y}px, 0)`
253
+ });
254
+ (_a = options.onComplete) == null ? void 0 : _a.call(options, { ...data, compute });
255
+ });
256
+ }
257
+ compute();
258
+ return callAll(
259
+ options.listeners ? autoUpdate(reference, floating, compute, options.listeners) : void 0,
260
+ options.onCleanup
261
+ );
262
+ }
263
+ function getBasePlacement(placement) {
264
+ return placement.split("-")[0];
265
+ }
266
+
267
+ // src/get-styles.ts
268
+ var UNMEASURED_FLOATING_STYLE = {
269
+ position: "fixed",
270
+ top: 0,
271
+ left: 0,
272
+ opacity: 0,
273
+ transform: "translate3d(0, -200%, 0)",
274
+ pointerEvents: "none"
275
+ };
276
+ var ARROW_FLOATING_STYLE = {
277
+ bottom: "rotate(45deg)",
278
+ left: "rotate(135deg)",
279
+ top: "rotate(225deg)",
280
+ right: "rotate(315deg)"
281
+ };
282
+ function getPlacementStyles(options) {
283
+ const { measured, strategy = "absolute", placement = "bottom" } = options;
284
+ return {
285
+ arrow: {
286
+ position: "absolute",
287
+ width: cssVars.arrowSize.reference,
288
+ height: cssVars.arrowSize.reference,
289
+ [cssVars.arrowSizeHalf.variable]: `calc(${cssVars.arrowSize.reference} / 2)`,
290
+ [cssVars.arrowOffset.variable]: `calc(${cssVars.arrowSizeHalf.reference} * -1)`,
291
+ opacity: !measured ? 0 : void 0
292
+ },
293
+ innerArrow: {
294
+ transform: ARROW_FLOATING_STYLE[placement.split("-")[0]],
295
+ background: cssVars.arrowBg.reference,
296
+ top: "0",
297
+ left: "0",
298
+ width: "100%",
299
+ height: "100%",
300
+ position: "absolute",
301
+ zIndex: "inherit"
302
+ },
303
+ floating: {
304
+ position: strategy,
305
+ minWidth: "max-content",
306
+ ...!measured && UNMEASURED_FLOATING_STYLE
307
+ }
308
+ };
309
+ }
310
+ export {
311
+ getBasePlacement,
312
+ getPlacement,
313
+ getPlacementStyles
314
+ };
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@zag-js/popper",
3
+ "version": "0.0.0-20220802150625",
4
+ "description": "Dynamic positioning logic for ui machines",
5
+ "keywords": [
6
+ "js",
7
+ "utils",
8
+ "popper"
9
+ ],
10
+ "author": "Segun Adebayo <sage@adebayosegun.com>",
11
+ "homepage": "https://github.com/chakra-ui/zag#readme",
12
+ "license": "MIT",
13
+ "main": "dist/index.js",
14
+ "module": "dist/index.mjs",
15
+ "types": "dist/index.d.ts",
16
+ "repository": "https://github.com/chakra-ui/zag/tree/main/packages/utilities/popper",
17
+ "sideEffects": false,
18
+ "files": [
19
+ "dist/**/*"
20
+ ],
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "bugs": {
25
+ "url": "https://github.com/chakra-ui/zag/issues"
26
+ },
27
+ "dependencies": {
28
+ "@floating-ui/dom": "1.0.0"
29
+ },
30
+ "devDependencies": {
31
+ "@zag-js/dom-utils": "0.1.8",
32
+ "@zag-js/utils": "0.1.3"
33
+ },
34
+ "scripts": {
35
+ "build-fast": "tsup src/index.ts --format=esm,cjs",
36
+ "start": "pnpm build --watch",
37
+ "build": "tsup src/index.ts --format=esm,cjs --dts",
38
+ "test": "jest --config ../../../jest.config.js --rootDir . --passWithNoTests",
39
+ "lint": "eslint src --ext .ts,.tsx",
40
+ "test-ci": "pnpm test --ci --runInBand",
41
+ "test-watch": "pnpm test --watch -u",
42
+ "typecheck": "tsc --noEmit"
43
+ }
44
+ }