bits-ui 1.3.12 → 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/context-menu/components/context-menu-content.svelte +3 -2
- package/dist/bits/menu/components/menu-sub-content-static.svelte +3 -2
- package/dist/bits/menu/components/menu-sub-content.svelte +3 -2
- 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
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
// the default menu behavior of handling outside interactions on the trigger
|
|
24
24
|
onEscapeKeydown = noop,
|
|
25
25
|
forceMount = false,
|
|
26
|
+
trapFocus = true,
|
|
26
27
|
...restProps
|
|
27
28
|
}: ContextMenuContentProps = $props();
|
|
28
29
|
|
|
@@ -76,7 +77,7 @@
|
|
|
76
77
|
onEscapeKeydown={handleEscapeKeydown}
|
|
77
78
|
{onOpenAutoFocus}
|
|
78
79
|
{isValidEvent}
|
|
79
|
-
trapFocus
|
|
80
|
+
{trapFocus}
|
|
80
81
|
{loop}
|
|
81
82
|
{id}
|
|
82
83
|
>
|
|
@@ -109,7 +110,7 @@
|
|
|
109
110
|
onEscapeKeydown={handleEscapeKeydown}
|
|
110
111
|
{onOpenAutoFocus}
|
|
111
112
|
{isValidEvent}
|
|
112
|
-
trapFocus
|
|
113
|
+
{trapFocus}
|
|
113
114
|
{loop}
|
|
114
115
|
{id}
|
|
115
116
|
>
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
onOpenAutoFocus: onOpenAutoFocusProp = noop,
|
|
26
26
|
onCloseAutoFocus: onCloseAutoFocusProp = noop,
|
|
27
27
|
onFocusOutside = noop,
|
|
28
|
+
trapFocus = false,
|
|
28
29
|
...restProps
|
|
29
30
|
}: MenuSubContentStaticProps = $props();
|
|
30
31
|
|
|
@@ -115,7 +116,7 @@
|
|
|
115
116
|
onFocusOutside={handleOnFocusOutside}
|
|
116
117
|
preventScroll={false}
|
|
117
118
|
{loop}
|
|
118
|
-
trapFocus
|
|
119
|
+
{trapFocus}
|
|
119
120
|
isStatic
|
|
120
121
|
>
|
|
121
122
|
{#snippet popper({ props })}
|
|
@@ -145,7 +146,7 @@
|
|
|
145
146
|
onFocusOutside={handleOnFocusOutside}
|
|
146
147
|
preventScroll={false}
|
|
147
148
|
{loop}
|
|
148
|
-
trapFocus
|
|
149
|
+
{trapFocus}
|
|
149
150
|
isStatic
|
|
150
151
|
>
|
|
151
152
|
{#snippet popper({ props })}
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
onCloseAutoFocus: onCloseAutoFocusProp = noop,
|
|
27
27
|
onFocusOutside = noop,
|
|
28
28
|
side = "right",
|
|
29
|
+
trapFocus = false,
|
|
29
30
|
...restProps
|
|
30
31
|
}: MenuSubContentProps = $props();
|
|
31
32
|
|
|
@@ -117,7 +118,7 @@
|
|
|
117
118
|
onFocusOutside={handleOnFocusOutside}
|
|
118
119
|
preventScroll={false}
|
|
119
120
|
{loop}
|
|
120
|
-
trapFocus
|
|
121
|
+
{trapFocus}
|
|
121
122
|
>
|
|
122
123
|
{#snippet popper({ props, wrapperProps })}
|
|
123
124
|
{@const finalProps = mergeProps(props, mergedProps, {
|
|
@@ -152,7 +153,7 @@
|
|
|
152
153
|
onFocusOutside={handleOnFocusOutside}
|
|
153
154
|
preventScroll={false}
|
|
154
155
|
{loop}
|
|
155
|
-
trapFocus
|
|
156
|
+
{trapFocus}
|
|
156
157
|
>
|
|
157
158
|
{#snippet popper({ props, wrapperProps })}
|
|
158
159
|
{@const finalProps = mergeProps(props, mergedProps, {
|
|
@@ -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
|
-
}
|