@mlola-ui/behavior 1.1.10 → 1.1.11
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/package.json +1 -1
- package/src/index.js +17 -0
- package/src/interaction.js +12 -0
- package/src/logic.d.ts +9 -0
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
clampToStep,
|
|
22
22
|
focusTrapIndex,
|
|
23
23
|
percentOf,
|
|
24
|
+
fitMenu,
|
|
24
25
|
fitTooltip,
|
|
25
26
|
isSidewaysDrag,
|
|
26
27
|
placeFloating,
|
|
@@ -156,11 +157,24 @@ const behaviors = {
|
|
|
156
157
|
(item) => !item.disabled && item.getAttribute("aria-disabled") !== "true",
|
|
157
158
|
);
|
|
158
159
|
|
|
160
|
+
// CSS hangs the menu under the trigger; this keeps it on screen.
|
|
161
|
+
const fit = () => {
|
|
162
|
+
const rect = root.getBoundingClientRect();
|
|
163
|
+
const { side, shift } = fitMenu(
|
|
164
|
+
{ x: rect.left, y: rect.top, width: rect.width, height: rect.height },
|
|
165
|
+
{ width: menu.offsetWidth, height: menu.offsetHeight },
|
|
166
|
+
{ width: window.innerWidth, height: window.innerHeight },
|
|
167
|
+
menu.dataset.align === "end" ? "end" : "start",
|
|
168
|
+
);
|
|
169
|
+
menu.style.setProperty("--ml-dropdown-shift", `${shift}px`);
|
|
170
|
+
menu.dataset.side = side;
|
|
171
|
+
};
|
|
159
172
|
const setOpen = (open) => {
|
|
160
173
|
root.dataset.state = open ? "open" : "closed";
|
|
161
174
|
menu.dataset.state = open ? "open" : "closed";
|
|
162
175
|
menu.hidden = !open;
|
|
163
176
|
trigger.setAttribute("aria-expanded", String(open));
|
|
177
|
+
if (open) fit();
|
|
164
178
|
if (!open) for (const item of items()) delete item.dataset.highlighted;
|
|
165
179
|
};
|
|
166
180
|
const isOpen = () => root.dataset.state === "open";
|
|
@@ -207,6 +221,9 @@ const behaviors = {
|
|
|
207
221
|
on(document, "pointerdown", (event) => {
|
|
208
222
|
if (isOpen() && !root.contains(event.target)) setOpen(false);
|
|
209
223
|
}),
|
|
224
|
+
on(window, "resize", () => {
|
|
225
|
+
if (isOpen()) fit();
|
|
226
|
+
}),
|
|
210
227
|
];
|
|
211
228
|
},
|
|
212
229
|
|
package/src/interaction.js
CHANGED
|
@@ -177,6 +177,18 @@ export function placeFloating(anchor, floating, viewport, { side = "bottom", ali
|
|
|
177
177
|
return { x: Math.round(x), y: Math.round(y), side: chosen };
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
+
/**
|
|
181
|
+
* Where a menu anchored under its trigger by CSS should sit to stay on
|
|
182
|
+
* screen: the side it opens on (above when there is no room below), and how
|
|
183
|
+
* far to slide it sideways from where CSS put it (its start edge on the
|
|
184
|
+
* anchor's start, or its end edge on the anchor's end).
|
|
185
|
+
*/
|
|
186
|
+
export function fitMenu(anchor, menu, viewport, align = "start", { offset = 6, padding = 8 } = {}) {
|
|
187
|
+
const placed = placeFloating(anchor, menu, viewport, { side: "bottom", align, offset, padding });
|
|
188
|
+
const cssX = align === "end" ? anchor.x + anchor.width - menu.width : anchor.x;
|
|
189
|
+
return { side: placed.side, shift: Math.round(placed.x - cssX) };
|
|
190
|
+
}
|
|
191
|
+
|
|
180
192
|
/**
|
|
181
193
|
* Where a tooltip anchored beside its trigger by CSS should sit to stay on
|
|
182
194
|
* screen: the side it opens on (flipped when the other has more room, or
|
package/src/logic.d.ts
CHANGED
|
@@ -136,6 +136,15 @@ export function placeFloating(
|
|
|
136
136
|
options?: { side?: Side; align?: Align; offset?: number; padding?: number },
|
|
137
137
|
): Placement;
|
|
138
138
|
|
|
139
|
+
/** A CSS-anchored menu's side (bottom, or top without room) and its sideways slide to stay on screen. */
|
|
140
|
+
export function fitMenu(
|
|
141
|
+
anchor: Rect,
|
|
142
|
+
menu: { width: number; height: number },
|
|
143
|
+
viewport: { width: number; height: number },
|
|
144
|
+
align?: "start" | "end",
|
|
145
|
+
options?: { offset?: number; padding?: number },
|
|
146
|
+
): { side: Side; shift: number };
|
|
147
|
+
|
|
139
148
|
/** A CSS-anchored tooltip's side and its slide along that edge to stay on screen. */
|
|
140
149
|
export function fitTooltip(
|
|
141
150
|
anchor: Rect,
|