bits-ui 1.0.0-next.44 → 1.0.0-next.46

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.
@@ -6,6 +6,7 @@
6
6
  import PopperLayer from "../../utilities/popper-layer/popper-layer.svelte";
7
7
  import { getFloatingContentCSSVars } from "../../../internal/floating-svelte/floating-utils.svelte.js";
8
8
  import PopperLayerForceMount from "../../utilities/popper-layer/popper-layer-force-mount.svelte";
9
+ import Mounted from "../../utilities/mounted.svelte";
9
10
 
10
11
  let {
11
12
  children,
@@ -112,6 +113,7 @@
112
113
  {@render children?.()}
113
114
  </div>
114
115
  {/if}
116
+ <Mounted onMountedChange={(m) => (contentState.root.contentMounted = m)} />
115
117
  {/snippet}
116
118
  </PopperLayer>
117
119
  {/if}
@@ -16,8 +16,10 @@ declare class LinkPreviewRootState {
16
16
  timeout: number | null;
17
17
  contentNode: HTMLElement | null;
18
18
  contentId: string | undefined;
19
+ contentMounted: boolean;
19
20
  triggerNode: HTMLElement | null;
20
21
  isPointerInTransit: import("svelte-toolbelt").WritableBox<boolean>;
22
+ isOpening: boolean;
21
23
  constructor(props: LinkPreviewRootStateProps);
22
24
  clearTimeout: () => void;
23
25
  handleOpen: () => void;
@@ -41,6 +43,7 @@ declare class LinkPreviewTriggerState {
41
43
  currentTarget: HTMLElement;
42
44
  }) => void;
43
45
  readonly onblur: () => void;
46
+ readonly onpointerleave: (e: PointerEvent) => void;
44
47
  };
45
48
  }
46
49
  type LinkPreviewContentStateProps = WithRefProps;
@@ -18,8 +18,10 @@ class LinkPreviewRootState {
18
18
  timeout = null;
19
19
  contentNode = $state(null);
20
20
  contentId = $state(undefined);
21
+ contentMounted = $state(false);
21
22
  triggerNode = $state(null);
22
23
  isPointerInTransit = box(false);
24
+ isOpening = $state(false);
23
25
  constructor(props) {
24
26
  this.open = props.open;
25
27
  this.openDelay = props.openDelay;
@@ -67,15 +69,21 @@ class LinkPreviewRootState {
67
69
  this.clearTimeout();
68
70
  if (this.open.current)
69
71
  return;
72
+ this.isOpening = true;
70
73
  this.timeout = window.setTimeout(() => {
71
- this.open.current = true;
74
+ if (this.isOpening) {
75
+ this.open.current = true;
76
+ this.isOpening = false;
77
+ }
72
78
  }, this.openDelay.current);
73
79
  };
74
80
  immediateClose = () => {
75
81
  this.clearTimeout();
82
+ this.isOpening = false;
76
83
  this.open.current = false;
77
84
  };
78
85
  handleClose = () => {
86
+ this.isOpening = false;
79
87
  this.clearTimeout();
80
88
  if (!this.isPointerDownOnContent && !this.hasSelection) {
81
89
  this.timeout = window.setTimeout(() => {
@@ -105,6 +113,13 @@ class LinkPreviewTriggerState {
105
113
  return;
106
114
  this.#root.handleOpen();
107
115
  };
116
+ #onpointerleave = (e) => {
117
+ if (isTouch(e))
118
+ return;
119
+ if (!this.#root.contentMounted) {
120
+ this.#root.immediateClose();
121
+ }
122
+ };
108
123
  #onfocus = (e) => {
109
124
  if (!isFocusVisible(e.currentTarget))
110
125
  return;
@@ -124,6 +139,7 @@ class LinkPreviewTriggerState {
124
139
  onpointerenter: this.#onpointerenter,
125
140
  onfocus: this.#onfocus,
126
141
  onblur: this.#onblur,
142
+ onpointerleave: this.#onpointerleave,
127
143
  }));
128
144
  }
129
145
  class LinkPreviewContentState {
@@ -18,6 +18,7 @@ type SliderRootStateProps = WithRefProps<ReadableBoxedValues<{
18
18
  value: number[];
19
19
  }>>;
20
20
  declare class SliderRootState {
21
+ #private;
21
22
  id: SliderRootStateProps["id"];
22
23
  ref: SliderRootStateProps["ref"];
23
24
  value: SliderRootStateProps["value"];
@@ -84,7 +85,7 @@ declare class SliderRootState {
84
85
  readonly "data-orientation": "horizontal" | "vertical";
85
86
  readonly "data-disabled": "" | undefined;
86
87
  readonly style: {
87
- readonly touchAction: "pan-x" | "pan-y";
88
+ readonly touchAction: string | undefined;
88
89
  };
89
90
  readonly "data-slider-root": "";
90
91
  };
@@ -125,7 +125,7 @@ class SliderRootState {
125
125
  };
126
126
  };
127
127
  handlePointerMove = (e) => {
128
- if (!this.isActive)
128
+ if (!this.isActive || this.disabled.current)
129
129
  return;
130
130
  e.preventDefault();
131
131
  e.stopPropagation();
@@ -170,7 +170,7 @@ class SliderRootState {
170
170
  }
171
171
  };
172
172
  handlePointerDown = (e) => {
173
- if (e.button !== 0)
173
+ if (e.button !== 0 || this.disabled.current)
174
174
  return;
175
175
  const sliderNode = this.ref.current;
176
176
  const closestThumb = this.getClosestThumb(e);
@@ -186,6 +186,8 @@ class SliderRootState {
186
186
  this.handlePointerMove(e);
187
187
  };
188
188
  handlePointerUp = () => {
189
+ if (this.disabled.current)
190
+ return;
189
191
  if (this.isActive) {
190
192
  this.onValueCommit.current(untrack(() => this.value.current));
191
193
  }
@@ -306,12 +308,17 @@ class SliderRootState {
306
308
  ticks: this.ticksRenderArr,
307
309
  thumbs: this.thumbsRenderArr,
308
310
  }));
311
+ #touchAction = $derived.by(() => {
312
+ if (this.disabled.current)
313
+ return undefined;
314
+ return this.orientation.current === "horizontal" ? "pan-y" : "pan-x";
315
+ });
309
316
  props = $derived.by(() => ({
310
317
  id: this.id.current,
311
318
  "data-orientation": getDataOrientation(this.orientation.current),
312
319
  "data-disabled": getDataDisabled(this.disabled.current),
313
320
  style: {
314
- touchAction: this.orientation.current === "horizontal" ? "pan-y" : "pan-x",
321
+ touchAction: this.#touchAction,
315
322
  },
316
323
  [SLIDER_ROOT_ATTR]: "",
317
324
  }));
@@ -1,5 +1,5 @@
1
1
  import { type Getter } from "svelte-toolbelt";
2
- export declare function useGraceArea(triggerNode: Getter<HTMLElement | null>, contentNode: Getter<HTMLElement | null>): {
2
+ export declare function useGraceArea(getTriggerNode: Getter<HTMLElement | null>, getContentNode: Getter<HTMLElement | null>): {
3
3
  isPointerInTransit: import("svelte-toolbelt").WritableBox<boolean>;
4
4
  onPointerExit: import("./create-event-hook.svelte.js").EventHookOn<void>;
5
5
  };
@@ -3,8 +3,10 @@ import { boxAutoReset } from "./box-auto-reset.svelte.js";
3
3
  import { createEventHook } from "./create-event-hook.svelte.js";
4
4
  import { isElement, isHTMLElement } from "./is.js";
5
5
  import { addEventListener } from "./events.js";
6
- export function useGraceArea(triggerNode, contentNode) {
6
+ export function useGraceArea(getTriggerNode, getContentNode) {
7
7
  const isPointerInTransit = boxAutoReset(false, 300);
8
+ const triggerNode = $derived(getTriggerNode());
9
+ const contentNode = $derived(getContentNode());
8
10
  let pointerGraceArea = $state(null);
9
11
  const pointerExit = createEventHook();
10
12
  function handleRemoveGraceArea() {
@@ -24,17 +26,15 @@ export function useGraceArea(triggerNode, contentNode) {
24
26
  isPointerInTransit.current = true;
25
27
  }
26
28
  $effect(() => {
27
- const trigger = triggerNode();
28
- const content = contentNode();
29
- if (!trigger || !content)
29
+ if (!triggerNode || !contentNode)
30
30
  return;
31
31
  const handleTriggerLeave = (e) => {
32
- handleCreateGraceArea(e, content);
32
+ handleCreateGraceArea(e, contentNode);
33
33
  };
34
34
  const handleContentLeave = (e) => {
35
- handleCreateGraceArea(e, trigger);
35
+ handleCreateGraceArea(e, triggerNode);
36
36
  };
37
- const unsub = executeCallbacks(addEventListener(trigger, "pointerleave", handleTriggerLeave), addEventListener(content, "pointerleave", handleContentLeave));
37
+ const unsub = executeCallbacks(addEventListener(triggerNode, "pointerleave", handleTriggerLeave), addEventListener(contentNode, "pointerleave", handleContentLeave));
38
38
  return unsub;
39
39
  });
40
40
  $effect(() => {
@@ -46,10 +46,8 @@ export function useGraceArea(triggerNode, contentNode) {
46
46
  const target = e.target;
47
47
  if (!isElement(target))
48
48
  return;
49
- const trigger = triggerNode();
50
- const content = contentNode();
51
49
  const pointerPosition = { x: e.clientX, y: e.clientY };
52
- const hasEnteredTarget = trigger?.contains(target) || content?.contains(target);
50
+ const hasEnteredTarget = triggerNode?.contains(target) || contentNode?.contains(target);
53
51
  const isPointerOutsideGraceArea = !isPointInPolygon(pointerPosition, pointerGraceArea);
54
52
  if (hasEnteredTarget) {
55
53
  handleRemoveGraceArea();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bits-ui",
3
- "version": "1.0.0-next.44",
3
+ "version": "1.0.0-next.46",
4
4
  "license": "MIT",
5
5
  "repository": "github:huntabyte/bits-ui",
6
6
  "funding": "https://github.com/sponsors/huntabyte",