bits-ui 1.0.0-next.40 → 1.0.0-next.42
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/app.d.ts +15 -0
- package/dist/bits/select/components/select-content.svelte +3 -2
- package/dist/bits/utilities/dismissible-layer/useDismissibleLayer.svelte.js +7 -5
- package/dist/bits/utilities/escape-layer/useEscapeLayer.svelte.js +4 -4
- package/dist/bits/utilities/presence-layer/usePresence.svelte.js +48 -40
- package/dist/bits/utilities/text-selection-layer/useTextSelectionLayer.svelte.js +5 -5
- package/package.json +1 -1
package/dist/app.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ReadableBox } from "svelte-toolbelt";
|
|
2
|
+
import type { DismissibleLayerState } from "./bits/utilities/dismissible-layer/useDismissibleLayer.svelte.ts";
|
|
3
|
+
import type { InteractOutsideBehaviorType } from "./bits/utilities/dismissible-layer/types.ts";
|
|
4
|
+
import type { EscapeLayerState } from "./bits/utilities/escape-layer/useEscapeLayer.svelte.ts";
|
|
5
|
+
import type { EscapeBehaviorType } from "./bits/utilities/escape-layer/types.ts";
|
|
6
|
+
import type { TextSelectionLayerState } from "./bits/utilities/text-selection-layer/useTextSelectionLayer.svelte.ts";
|
|
7
|
+
|
|
8
|
+
declare global {
|
|
9
|
+
// eslint-disable-next-line vars-on-top, no-var
|
|
10
|
+
var bitsDismissableLayers: Map<DismissibleLayerState, ReadableBox<InteractOutsideBehaviorType>>;
|
|
11
|
+
// eslint-disable-next-line vars-on-top, no-var
|
|
12
|
+
var bitsEscapeLayers: Map<EscapeLayerState, ReadableBox<EscapeBehaviorType>>;
|
|
13
|
+
// eslint-disable-next-line vars-on-top, no-var
|
|
14
|
+
var bitsTextSelectionLayers: Map<TextSelectionLayerState, ReadableBox<boolean>>;
|
|
15
|
+
}
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
onEscapeKeydown = noop,
|
|
17
17
|
children,
|
|
18
18
|
child,
|
|
19
|
+
preventScroll = false,
|
|
19
20
|
...restProps
|
|
20
21
|
}: SelectContentProps = $props();
|
|
21
22
|
|
|
@@ -56,7 +57,7 @@
|
|
|
56
57
|
onCloseAutoFocus={(e) => e.preventDefault()}
|
|
57
58
|
trapFocus={false}
|
|
58
59
|
loop={false}
|
|
59
|
-
preventScroll
|
|
60
|
+
{preventScroll}
|
|
60
61
|
onPlaced={() => (contentState.isPositioned = true)}
|
|
61
62
|
forceMount={true}
|
|
62
63
|
>
|
|
@@ -83,7 +84,7 @@
|
|
|
83
84
|
onCloseAutoFocus={(e) => e.preventDefault()}
|
|
84
85
|
trapFocus={false}
|
|
85
86
|
loop={false}
|
|
86
|
-
preventScroll
|
|
87
|
+
{preventScroll}
|
|
87
88
|
onPlaced={() => (contentState.isPositioned = true)}
|
|
88
89
|
forceMount={false}
|
|
89
90
|
>
|
|
@@ -5,7 +5,7 @@ import { debounce } from "../../../internal/debounce.js";
|
|
|
5
5
|
import { noop } from "../../../internal/noop.js";
|
|
6
6
|
import { getOwnerDocument, isOrContainsTarget } from "../../../internal/elements.js";
|
|
7
7
|
import { isElement } from "../../../internal/is.js";
|
|
8
|
-
|
|
8
|
+
globalThis.bitsDismissableLayers ??= new Map();
|
|
9
9
|
export class DismissibleLayerState {
|
|
10
10
|
#interactOutsideProp;
|
|
11
11
|
#behaviorType;
|
|
@@ -41,14 +41,16 @@ export class DismissibleLayerState {
|
|
|
41
41
|
let unsubEvents = noop;
|
|
42
42
|
const cleanup = () => {
|
|
43
43
|
this.#resetState();
|
|
44
|
-
|
|
44
|
+
globalThis.bitsDismissableLayers.delete(this);
|
|
45
45
|
this.#handleInteractOutside.destroy();
|
|
46
46
|
unsubEvents();
|
|
47
47
|
};
|
|
48
48
|
$effect(() => {
|
|
49
49
|
if (this.#enabled.current && this.currNode) {
|
|
50
50
|
afterSleep(1, () => {
|
|
51
|
-
|
|
51
|
+
if (!this.currNode)
|
|
52
|
+
return;
|
|
53
|
+
globalThis.bitsDismissableLayers.set(this, untrack(() => this.#behaviorType));
|
|
52
54
|
unsubEvents();
|
|
53
55
|
unsubEvents = this.#addEventListeners();
|
|
54
56
|
});
|
|
@@ -59,7 +61,7 @@ export class DismissibleLayerState {
|
|
|
59
61
|
});
|
|
60
62
|
onDestroyEffect(() => {
|
|
61
63
|
this.#resetState.destroy();
|
|
62
|
-
|
|
64
|
+
globalThis.bitsDismissableLayers.delete(this);
|
|
63
65
|
this.#handleInteractOutside.destroy();
|
|
64
66
|
this.#unsubClickListener();
|
|
65
67
|
unsubEvents();
|
|
@@ -178,7 +180,7 @@ function getTopMostLayer(layersArr) {
|
|
|
178
180
|
return layersArr.findLast(([_, { current: behaviorType }]) => behaviorType === "close" || behaviorType === "ignore");
|
|
179
181
|
}
|
|
180
182
|
function isResponsibleLayer(node) {
|
|
181
|
-
const layersArr = [...
|
|
183
|
+
const layersArr = [...globalThis.bitsDismissableLayers];
|
|
182
184
|
/**
|
|
183
185
|
* We first check if we can find a top layer with `close` or `ignore`.
|
|
184
186
|
* If that top layer was found and matches the provided node, then the node is
|
|
@@ -2,7 +2,7 @@ import { untrack } from "svelte";
|
|
|
2
2
|
import { addEventListener } from "../../../internal/events.js";
|
|
3
3
|
import { kbd } from "../../../internal/kbd.js";
|
|
4
4
|
import { noop } from "../../../internal/noop.js";
|
|
5
|
-
|
|
5
|
+
globalThis.bitsEscapeLayers ??= new Map();
|
|
6
6
|
export class EscapeLayerState {
|
|
7
7
|
#onEscapeProp;
|
|
8
8
|
#behaviorType;
|
|
@@ -14,12 +14,12 @@ export class EscapeLayerState {
|
|
|
14
14
|
let unsubEvents = noop;
|
|
15
15
|
$effect(() => {
|
|
16
16
|
if (this.#enabled.current) {
|
|
17
|
-
|
|
17
|
+
globalThis.bitsEscapeLayers.set(this, untrack(() => this.#behaviorType));
|
|
18
18
|
unsubEvents = this.#addEventListener();
|
|
19
19
|
}
|
|
20
20
|
return () => {
|
|
21
21
|
unsubEvents();
|
|
22
|
-
|
|
22
|
+
globalThis.bitsEscapeLayers.delete(this);
|
|
23
23
|
};
|
|
24
24
|
});
|
|
25
25
|
}
|
|
@@ -41,7 +41,7 @@ export function useEscapeLayer(props) {
|
|
|
41
41
|
return new EscapeLayerState(props);
|
|
42
42
|
}
|
|
43
43
|
function isResponsibleEscapeLayer(instance) {
|
|
44
|
-
const layersArr = [...
|
|
44
|
+
const layersArr = [...globalThis.bitsEscapeLayers];
|
|
45
45
|
/**
|
|
46
46
|
* We first check if we can find a top layer with `close` or `ignore`.
|
|
47
47
|
* If that top layer was found and matches the provided node, then the node is
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { afterTick } from "svelte-toolbelt";
|
|
2
|
+
import { Previous } from "runed";
|
|
3
|
+
import { untrack } from "svelte";
|
|
2
4
|
import { useStateMachine } from "../../../internal/use-state-machine.svelte.js";
|
|
3
|
-
import { watch } from "../../../internal/box.svelte.js";
|
|
4
5
|
export function usePresence(present, id) {
|
|
5
6
|
let styles = $state({});
|
|
6
7
|
let prevAnimationNameState = $state("none");
|
|
7
8
|
const initialState = present.current ? "mounted" : "unmounted";
|
|
8
9
|
let node = $state(null);
|
|
10
|
+
const prevPresent = new Previous(() => present.current);
|
|
9
11
|
$effect(() => {
|
|
10
12
|
if (!id.current)
|
|
11
13
|
return;
|
|
@@ -28,40 +30,43 @@ export function usePresence(present, id) {
|
|
|
28
30
|
MOUNT: "mounted",
|
|
29
31
|
},
|
|
30
32
|
});
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return;
|
|
37
|
-
const hasPresentChanged = currPresent !== prevPresent;
|
|
38
|
-
if (!hasPresentChanged)
|
|
39
|
-
return;
|
|
40
|
-
const prevAnimationName = prevAnimationNameState;
|
|
41
|
-
const currAnimationName = getAnimationName(node);
|
|
42
|
-
if (currPresent) {
|
|
43
|
-
dispatch("MOUNT");
|
|
44
|
-
}
|
|
45
|
-
else if (currAnimationName === "none" || styles.display === "none") {
|
|
46
|
-
// If there is no exit animation or the element is hidden, animations won't run
|
|
47
|
-
// so we unmount instantly
|
|
48
|
-
dispatch("UNMOUNT");
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
/**
|
|
52
|
-
* When `present` changes to `false`, we check changes to animation-name to
|
|
53
|
-
* determine whether an animation has started. We chose this approach (reading
|
|
54
|
-
* computed styles) because there is no `animationrun` event and `animationstart`
|
|
55
|
-
* fires after `animation-delay` has expired which would be too late.
|
|
56
|
-
*/
|
|
57
|
-
const isAnimating = prevAnimationName !== currAnimationName;
|
|
58
|
-
if (prevPresent && isAnimating) {
|
|
59
|
-
dispatch("ANIMATION_OUT");
|
|
33
|
+
$effect(() => {
|
|
34
|
+
const currPresent = present.current;
|
|
35
|
+
untrack(() => {
|
|
36
|
+
if (!node) {
|
|
37
|
+
node = document.getElementById(id.current);
|
|
60
38
|
}
|
|
61
|
-
|
|
39
|
+
if (!node)
|
|
40
|
+
return;
|
|
41
|
+
const hasPresentChanged = currPresent !== prevPresent.current;
|
|
42
|
+
if (!hasPresentChanged)
|
|
43
|
+
return;
|
|
44
|
+
const prevAnimationName = prevAnimationNameState;
|
|
45
|
+
const currAnimationName = getAnimationName(node);
|
|
46
|
+
if (currPresent) {
|
|
47
|
+
dispatch("MOUNT");
|
|
48
|
+
}
|
|
49
|
+
else if (currAnimationName === "none" || styles.display === "none") {
|
|
50
|
+
// If there is no exit animation or the element is hidden, animations won't run
|
|
51
|
+
// so we unmount instantly
|
|
62
52
|
dispatch("UNMOUNT");
|
|
63
53
|
}
|
|
64
|
-
|
|
54
|
+
else {
|
|
55
|
+
/**
|
|
56
|
+
* When `present` changes to `false`, we check changes to animation-name to
|
|
57
|
+
* determine whether an animation has started. We chose this approach (reading
|
|
58
|
+
* computed styles) because there is no `animationrun` event and `animationstart`
|
|
59
|
+
* fires after `animation-delay` has expired which would be too late.
|
|
60
|
+
*/
|
|
61
|
+
const isAnimating = prevAnimationName !== currAnimationName;
|
|
62
|
+
if (prevPresent && isAnimating) {
|
|
63
|
+
dispatch("ANIMATION_OUT");
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
dispatch("UNMOUNT");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
});
|
|
65
70
|
});
|
|
66
71
|
/**
|
|
67
72
|
* Triggering an ANIMATION_OUT during an ANIMATION_IN will fire an `animationcancel`
|
|
@@ -90,14 +95,17 @@ export function usePresence(present, id) {
|
|
|
90
95
|
prevAnimationNameState = getAnimationName(node);
|
|
91
96
|
}
|
|
92
97
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
$effect(() => {
|
|
99
|
+
state.current;
|
|
100
|
+
untrack(() => {
|
|
101
|
+
if (!node) {
|
|
102
|
+
node = document.getElementById(id.current);
|
|
103
|
+
}
|
|
104
|
+
if (!node)
|
|
105
|
+
return;
|
|
106
|
+
const currAnimationName = getAnimationName(node);
|
|
107
|
+
prevAnimationNameState = state.current === "mounted" ? currAnimationName : "none";
|
|
108
|
+
});
|
|
101
109
|
});
|
|
102
110
|
$effect(() => {
|
|
103
111
|
if (!node)
|
|
@@ -4,7 +4,7 @@ import { addEventListener } from "../../../internal/events.js";
|
|
|
4
4
|
import { noop } from "../../../internal/noop.js";
|
|
5
5
|
import { isHTMLElement } from "../../../internal/is.js";
|
|
6
6
|
import { isOrContainsTarget } from "../../../internal/elements.js";
|
|
7
|
-
|
|
7
|
+
globalThis.bitsTextSelectionLayers ??= new Map();
|
|
8
8
|
export class TextSelectionLayerState {
|
|
9
9
|
#id;
|
|
10
10
|
#onPointerDownProp;
|
|
@@ -25,13 +25,13 @@ export class TextSelectionLayerState {
|
|
|
25
25
|
let unsubEvents = noop;
|
|
26
26
|
$effect(() => {
|
|
27
27
|
if (this.#enabled.current) {
|
|
28
|
-
|
|
28
|
+
globalThis.bitsTextSelectionLayers.set(this, untrack(() => this.#enabled));
|
|
29
29
|
unsubEvents = this.#addEventListeners();
|
|
30
30
|
}
|
|
31
31
|
return () => {
|
|
32
32
|
unsubEvents();
|
|
33
33
|
this.#resetSelectionLock();
|
|
34
|
-
|
|
34
|
+
globalThis.bitsTextSelectionLayers.delete(this);
|
|
35
35
|
};
|
|
36
36
|
});
|
|
37
37
|
}
|
|
@@ -45,7 +45,7 @@ export class TextSelectionLayerState {
|
|
|
45
45
|
return;
|
|
46
46
|
/**
|
|
47
47
|
* We only lock user-selection overflow if layer is the top most layer and
|
|
48
|
-
* pointerdown
|
|
48
|
+
* pointerdown occurred inside the node. You are still allowed to select text
|
|
49
49
|
* outside the node provided pointerdown occurs outside the node.
|
|
50
50
|
*/
|
|
51
51
|
if (!isHighestLayer(this) || !isOrContainsTarget(node, target))
|
|
@@ -80,7 +80,7 @@ function setUserSelect(node, value) {
|
|
|
80
80
|
node.style.webkitUserSelect = value;
|
|
81
81
|
}
|
|
82
82
|
function isHighestLayer(instance) {
|
|
83
|
-
const layersArr = [...
|
|
83
|
+
const layersArr = [...globalThis.bitsTextSelectionLayers];
|
|
84
84
|
if (!layersArr.length)
|
|
85
85
|
return false;
|
|
86
86
|
const highestLayer = layersArr.at(-1);
|