bits-ui 1.3.13 → 1.3.14
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/bits/menubar/components/menubar-menu.svelte +3 -1
- package/dist/bits/menubar/menubar.svelte.d.ts +5 -3
- package/dist/bits/menubar/menubar.svelte.js +20 -8
- package/dist/bits/menubar/types.d.ts +4 -0
- package/package.json +1 -1
- package/dist/internal/dev/visualize-grace-area.d.ts +0 -5
- package/dist/internal/dev/visualize-grace-area.js +0 -28
- package/dist/internal/polygon.d.ts +0 -10
- package/dist/internal/polygon.js +0 -115
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
import { useMenubarMenu } from "../menubar.svelte.js";
|
|
5
5
|
import Menu from "../../menu/components/menu.svelte";
|
|
6
6
|
import { useId } from "../../../internal/use-id.js";
|
|
7
|
+
import { noop } from "../../../internal/noop.js";
|
|
7
8
|
|
|
8
|
-
let { value = useId(), ...restProps }: MenubarMenuProps = $props();
|
|
9
|
+
let { value = useId(), onOpenChange = noop, ...restProps }: MenubarMenuProps = $props();
|
|
9
10
|
|
|
10
11
|
const menuState = useMenubarMenu({
|
|
11
12
|
value: box.with(() => value),
|
|
13
|
+
onOpenChange: box.with(() => onOpenChange),
|
|
12
14
|
});
|
|
13
15
|
</script>
|
|
14
16
|
|
|
@@ -3,7 +3,7 @@ import type { InteractOutsideBehaviorType } from "../utilities/dismissible-layer
|
|
|
3
3
|
import type { ReadableBoxedValues, WritableBoxedValues } from "../../internal/box.svelte.js";
|
|
4
4
|
import { type UseRovingFocusReturn } from "../../internal/use-roving-focus.svelte.js";
|
|
5
5
|
import type { Direction } from "../../shared/index.js";
|
|
6
|
-
import type { BitsFocusEvent, BitsKeyboardEvent, BitsPointerEvent, WithRefProps } from "../../internal/types.js";
|
|
6
|
+
import type { BitsFocusEvent, BitsKeyboardEvent, BitsPointerEvent, OnChangeFn, WithRefProps } from "../../internal/types.js";
|
|
7
7
|
import { type FocusScopeContextValue } from "../utilities/focus-scope/use-focus-scope.svelte.js";
|
|
8
8
|
type MenubarRootStateProps = WithRefProps<ReadableBoxedValues<{
|
|
9
9
|
dir: Direction;
|
|
@@ -16,7 +16,7 @@ declare class MenubarRootState {
|
|
|
16
16
|
rovingFocusGroup: UseRovingFocusReturn;
|
|
17
17
|
wasOpenedByKeyboard: boolean;
|
|
18
18
|
triggerIds: string[];
|
|
19
|
-
|
|
19
|
+
valueToChangeHandler: Map<string, ReadableBox<OnChangeFn<boolean>>>;
|
|
20
20
|
constructor(opts: MenubarRootStateProps);
|
|
21
21
|
/**
|
|
22
22
|
* @param id - the id of the trigger to register
|
|
@@ -28,7 +28,8 @@ declare class MenubarRootState {
|
|
|
28
28
|
* @param contentId - the content id to associate with the value
|
|
29
29
|
* @returns - a function to de-register the menu
|
|
30
30
|
*/
|
|
31
|
-
registerMenu(value: string,
|
|
31
|
+
registerMenu(value: string, onOpenChange: ReadableBox<OnChangeFn<boolean>>): () => void;
|
|
32
|
+
updateValue(value: string): void;
|
|
32
33
|
getTriggers(): HTMLButtonElement[];
|
|
33
34
|
onMenuOpen(id: string, triggerId: string): void;
|
|
34
35
|
onMenuClose(): void;
|
|
@@ -41,6 +42,7 @@ declare class MenubarRootState {
|
|
|
41
42
|
}
|
|
42
43
|
type MenubarMenuStateProps = ReadableBoxedValues<{
|
|
43
44
|
value: string;
|
|
45
|
+
onOpenChange: OnChangeFn<boolean>;
|
|
44
46
|
}>;
|
|
45
47
|
declare class MenubarMenuState {
|
|
46
48
|
readonly opts: MenubarMenuStateProps;
|
|
@@ -13,7 +13,7 @@ class MenubarRootState {
|
|
|
13
13
|
rovingFocusGroup;
|
|
14
14
|
wasOpenedByKeyboard = $state(false);
|
|
15
15
|
triggerIds = $state([]);
|
|
16
|
-
|
|
16
|
+
valueToChangeHandler = new Map();
|
|
17
17
|
constructor(opts) {
|
|
18
18
|
this.opts = opts;
|
|
19
19
|
useRefById(opts);
|
|
@@ -43,12 +43,24 @@ class MenubarRootState {
|
|
|
43
43
|
* @param contentId - the content id to associate with the value
|
|
44
44
|
* @returns - a function to de-register the menu
|
|
45
45
|
*/
|
|
46
|
-
registerMenu(value,
|
|
47
|
-
this.
|
|
46
|
+
registerMenu(value, onOpenChange) {
|
|
47
|
+
this.valueToChangeHandler.set(value, onOpenChange);
|
|
48
48
|
return () => {
|
|
49
|
-
this.
|
|
49
|
+
this.valueToChangeHandler.delete(value);
|
|
50
50
|
};
|
|
51
51
|
}
|
|
52
|
+
updateValue(value) {
|
|
53
|
+
const currValue = this.opts.value.current;
|
|
54
|
+
const currHandler = this.valueToChangeHandler.get(currValue)?.current;
|
|
55
|
+
const nextHandler = this.valueToChangeHandler.get(value)?.current;
|
|
56
|
+
this.opts.value.current = value;
|
|
57
|
+
if (currHandler && currValue !== value) {
|
|
58
|
+
currHandler(false);
|
|
59
|
+
}
|
|
60
|
+
if (nextHandler) {
|
|
61
|
+
nextHandler(true);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
52
64
|
getTriggers() {
|
|
53
65
|
const node = this.opts.ref.current;
|
|
54
66
|
if (!node)
|
|
@@ -56,14 +68,14 @@ class MenubarRootState {
|
|
|
56
68
|
return Array.from(node.querySelectorAll(`[${MENUBAR_TRIGGER_ATTR}]`));
|
|
57
69
|
}
|
|
58
70
|
onMenuOpen(id, triggerId) {
|
|
59
|
-
this.
|
|
71
|
+
this.updateValue(id);
|
|
60
72
|
this.rovingFocusGroup.setCurrentTabStopId(triggerId);
|
|
61
73
|
}
|
|
62
74
|
onMenuClose() {
|
|
63
|
-
this.
|
|
75
|
+
this.updateValue("");
|
|
64
76
|
}
|
|
65
77
|
onMenuToggle(id) {
|
|
66
|
-
this.
|
|
78
|
+
this.updateValue(this.opts.value.current ? "" : id);
|
|
67
79
|
}
|
|
68
80
|
props = $derived.by(() => ({
|
|
69
81
|
id: this.opts.id.current,
|
|
@@ -87,7 +99,7 @@ class MenubarMenuState {
|
|
|
87
99
|
}
|
|
88
100
|
});
|
|
89
101
|
onMount(() => {
|
|
90
|
-
return this.root.registerMenu(this.opts.value.current,
|
|
102
|
+
return this.root.registerMenu(this.opts.value.current, opts.onOpenChange);
|
|
91
103
|
});
|
|
92
104
|
}
|
|
93
105
|
getTriggerNode() {
|
|
@@ -29,6 +29,10 @@ export type MenubarMenuPropsWithoutHTML = WithChildren<{
|
|
|
29
29
|
* within the menubar.
|
|
30
30
|
*/
|
|
31
31
|
value?: string;
|
|
32
|
+
/**
|
|
33
|
+
* A callback that is called when the menu is opened or closed.
|
|
34
|
+
*/
|
|
35
|
+
onOpenChange?: OnChangeFn<boolean>;
|
|
32
36
|
}>;
|
|
33
37
|
export type MenubarMenuProps = MenubarMenuPropsWithoutHTML;
|
|
34
38
|
export type MenubarTriggerPropsWithoutHTML = WithChild<{
|
package/package.json
CHANGED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
let graceAreaElement = null;
|
|
2
|
-
let svgContainer = null;
|
|
3
|
-
/**
|
|
4
|
-
* Debugging utility to visualize the grace area of a floating layer component.
|
|
5
|
-
*/
|
|
6
|
-
export function visualizeGraceArea(graceArea) {
|
|
7
|
-
if (graceAreaElement) {
|
|
8
|
-
graceAreaElement.remove();
|
|
9
|
-
}
|
|
10
|
-
if (!svgContainer) {
|
|
11
|
-
svgContainer = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
12
|
-
svgContainer.style.position = "absolute";
|
|
13
|
-
svgContainer.style.top = "0";
|
|
14
|
-
svgContainer.style.left = "0";
|
|
15
|
-
svgContainer.style.width = "100%";
|
|
16
|
-
svgContainer.style.height = "100%";
|
|
17
|
-
svgContainer.style.pointerEvents = "none";
|
|
18
|
-
document.body.appendChild(svgContainer);
|
|
19
|
-
}
|
|
20
|
-
graceAreaElement = document.createElementNS("http://www.w3.org/2000/svg", "polygon");
|
|
21
|
-
const pointsString = graceArea.map((p) => `${p.x},${p.y}`).join(" ");
|
|
22
|
-
graceAreaElement.setAttribute("points", pointsString);
|
|
23
|
-
graceAreaElement.setAttribute("fill", "rgba(255, 0, 0, 0.3)");
|
|
24
|
-
graceAreaElement.setAttribute("stroke", "red");
|
|
25
|
-
graceAreaElement.setAttribute("stroke-width", "1");
|
|
26
|
-
graceAreaElement.style.pointerEvents = "none";
|
|
27
|
-
svgContainer.appendChild(graceAreaElement);
|
|
28
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export interface Point {
|
|
2
|
-
x: number;
|
|
3
|
-
y: number;
|
|
4
|
-
}
|
|
5
|
-
export type Polygon = Array<Point>;
|
|
6
|
-
export declare function makeHullPresorted<P extends Point>(points: Readonly<Array<P>>): Array<P>;
|
|
7
|
-
export declare function POINT_COMPARATOR(a: Point, b: Point): number;
|
|
8
|
-
export declare function makeHullFromElements(els: Array<HTMLElement>): Array<Point>;
|
|
9
|
-
export declare function pointInPolygon(point: Point, polygon: Polygon): boolean;
|
|
10
|
-
export declare function isPointerInGraceArea(e: Pick<PointerEvent, "clientX" | "clientY">, area?: Polygon): boolean;
|
package/dist/internal/polygon.js
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Convex hull algorithm - Library (TypeScript)
|
|
3
|
-
*
|
|
4
|
-
* Copyright (c) 2021 Project Nayuki
|
|
5
|
-
* https://www.nayuki.io/page/convex-hull-algorithm
|
|
6
|
-
*
|
|
7
|
-
* This program is free software: you can redistribute it and/or modify
|
|
8
|
-
* it under the terms of the GNU Lesser General Public License as published by
|
|
9
|
-
* the Free Software Foundation, either version 3 of the License, or
|
|
10
|
-
* (at your option) any later version.
|
|
11
|
-
*
|
|
12
|
-
* This program is distributed in the hope that it will be useful,
|
|
13
|
-
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
-
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15
|
-
* GNU Lesser General Public License for more details.
|
|
16
|
-
*
|
|
17
|
-
* You should have received a copy of the GNU Lesser General Public License
|
|
18
|
-
* along with this program (see COPYING.txt and COPYING.LESSER.txt).
|
|
19
|
-
* If not, see <http://www.gnu.org/licenses/>.
|
|
20
|
-
*/
|
|
21
|
-
// Returns a new array of points representing the convex hull of
|
|
22
|
-
// the given set of points. The convex hull excludes collinear points.
|
|
23
|
-
// This algorithm runs in O(n log n) time.
|
|
24
|
-
function makeHull(points) {
|
|
25
|
-
const newPoints = points.slice();
|
|
26
|
-
newPoints.sort(POINT_COMPARATOR);
|
|
27
|
-
return makeHullPresorted(newPoints);
|
|
28
|
-
}
|
|
29
|
-
// Returns the convex hull, assuming that each points[i] <= points[i + 1]. Runs in O(n) time.
|
|
30
|
-
export function makeHullPresorted(points) {
|
|
31
|
-
if (points.length <= 1)
|
|
32
|
-
return points.slice();
|
|
33
|
-
// Andrew's monotone chain algorithm. Positive y coordinates correspond to "up"
|
|
34
|
-
// as per the mathematical convention, instead of "down" as per the computer
|
|
35
|
-
// graphics convention. This doesn't affect the correctness of the result.
|
|
36
|
-
const upperHull = [];
|
|
37
|
-
for (let i = 0; i < points.length; i++) {
|
|
38
|
-
const p = points[i];
|
|
39
|
-
while (upperHull.length >= 2) {
|
|
40
|
-
const q = upperHull[upperHull.length - 1];
|
|
41
|
-
const r = upperHull[upperHull.length - 2];
|
|
42
|
-
if ((q.x - r.x) * (p.y - r.y) >= (q.y - r.y) * (p.x - r.x))
|
|
43
|
-
upperHull.pop();
|
|
44
|
-
else
|
|
45
|
-
break;
|
|
46
|
-
}
|
|
47
|
-
upperHull.push(p);
|
|
48
|
-
}
|
|
49
|
-
upperHull.pop();
|
|
50
|
-
const lowerHull = [];
|
|
51
|
-
for (let i = points.length - 1; i >= 0; i--) {
|
|
52
|
-
const p = points[i];
|
|
53
|
-
while (lowerHull.length >= 2) {
|
|
54
|
-
const q = lowerHull[lowerHull.length - 1];
|
|
55
|
-
const r = lowerHull[lowerHull.length - 2];
|
|
56
|
-
if ((q.x - r.x) * (p.y - r.y) >= (q.y - r.y) * (p.x - r.x))
|
|
57
|
-
lowerHull.pop();
|
|
58
|
-
else
|
|
59
|
-
break;
|
|
60
|
-
}
|
|
61
|
-
lowerHull.push(p);
|
|
62
|
-
}
|
|
63
|
-
lowerHull.pop();
|
|
64
|
-
if (upperHull.length === 1 &&
|
|
65
|
-
lowerHull.length === 1 &&
|
|
66
|
-
upperHull[0].x === lowerHull[0].x &&
|
|
67
|
-
upperHull[0].y === lowerHull[0].y)
|
|
68
|
-
return upperHull;
|
|
69
|
-
else
|
|
70
|
-
return upperHull.concat(lowerHull);
|
|
71
|
-
}
|
|
72
|
-
export function POINT_COMPARATOR(a, b) {
|
|
73
|
-
if (a.x < b.x)
|
|
74
|
-
return -1;
|
|
75
|
-
else if (a.x > b.x)
|
|
76
|
-
return +1;
|
|
77
|
-
else if (a.y < b.y)
|
|
78
|
-
return -1;
|
|
79
|
-
else if (a.y > b.y)
|
|
80
|
-
return +1;
|
|
81
|
-
else
|
|
82
|
-
return 0;
|
|
83
|
-
}
|
|
84
|
-
function getPointsFromEl(el) {
|
|
85
|
-
const rect = el.getBoundingClientRect();
|
|
86
|
-
return [
|
|
87
|
-
{ x: rect.left, y: rect.top },
|
|
88
|
-
{ x: rect.right, y: rect.top },
|
|
89
|
-
{ x: rect.right, y: rect.bottom },
|
|
90
|
-
{ x: rect.left, y: rect.bottom },
|
|
91
|
-
];
|
|
92
|
-
}
|
|
93
|
-
export function makeHullFromElements(els) {
|
|
94
|
-
const points = els.flatMap((el) => getPointsFromEl(el));
|
|
95
|
-
return makeHull(points);
|
|
96
|
-
}
|
|
97
|
-
export function pointInPolygon(point, polygon) {
|
|
98
|
-
let inside = false;
|
|
99
|
-
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
|
|
100
|
-
const xi = polygon[i].x;
|
|
101
|
-
const yi = polygon[i].y;
|
|
102
|
-
const xj = polygon[j].x;
|
|
103
|
-
const yj = polygon[j].y;
|
|
104
|
-
const intersect = yi > point.y !== yj > point.y &&
|
|
105
|
-
point.x < ((xj - xi) * (point.y - yi)) / (yj - yi) + xi;
|
|
106
|
-
if (intersect)
|
|
107
|
-
inside = !inside;
|
|
108
|
-
}
|
|
109
|
-
return inside;
|
|
110
|
-
}
|
|
111
|
-
export function isPointerInGraceArea(e, area) {
|
|
112
|
-
if (!area)
|
|
113
|
-
return false;
|
|
114
|
-
return pointInPolygon({ x: e.clientX, y: e.clientY }, area);
|
|
115
|
-
}
|