bits-ui 1.0.0-next.56 → 1.0.0-next.58

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.
@@ -18,7 +18,7 @@ declare class AvatarRootState {
18
18
  delayMs: AvatarRootStateProps["delayMs"];
19
19
  loadingStatus: AvatarRootStateProps["loadingStatus"];
20
20
  constructor(props: AvatarRootStateProps);
21
- loadImage(src: string, crossorigin?: CrossOrigin, referrerPolicy?: ReferrerPolicy): () => void;
21
+ loadImage: (src: string, crossorigin?: CrossOrigin, referrerPolicy?: ReferrerPolicy) => () => void;
22
22
  props: {
23
23
  readonly id: string;
24
24
  readonly "data-avatar-root": "";
@@ -19,7 +19,7 @@ class AvatarRootState {
19
19
  ref: this.#ref,
20
20
  });
21
21
  }
22
- loadImage(src, crossorigin, referrerPolicy) {
22
+ loadImage = (src, crossorigin, referrerPolicy) => {
23
23
  let imageTimerId;
24
24
  const image = new Image();
25
25
  image.src = src;
@@ -37,9 +37,9 @@ class AvatarRootState {
37
37
  this.loadingStatus.current = "error";
38
38
  };
39
39
  return () => {
40
- clearTimeout(imageTimerId);
40
+ window.clearTimeout(imageTimerId);
41
41
  };
42
- }
42
+ };
43
43
  props = $derived.by(() => ({
44
44
  id: this.#id.current,
45
45
  [AVATAR_ROOT_ATTR]: "",
@@ -65,8 +65,10 @@ class AvatarImageState {
65
65
  ref: this.#ref,
66
66
  });
67
67
  $effect.pre(() => {
68
- if (!this.#src.current)
68
+ if (!this.#src.current) {
69
+ this.#root.loadingStatus.current = "error";
69
70
  return;
71
+ }
70
72
  // dependency on crossorigin
71
73
  this.#crossOrigin.current;
72
74
  untrack(() => this.#root.loadImage(this.#src.current ?? "", this.#crossOrigin.current, this.#referrerPolicy.current));
@@ -1,3 +1,4 @@
1
+ import type { HTMLButtonAttributes } from "svelte/elements";
1
2
  import type { ReadableBoxedValues, WritableBoxedValues } from "../../internal/box.svelte.js";
2
3
  import type { WithRefProps } from "../../internal/types.js";
3
4
  type CheckboxRootStateProps = WithRefProps<ReadableBoxedValues<{
@@ -5,6 +6,7 @@ type CheckboxRootStateProps = WithRefProps<ReadableBoxedValues<{
5
6
  required: boolean;
6
7
  name: string | undefined;
7
8
  value: string | undefined;
9
+ type: HTMLButtonAttributes["type"];
8
10
  }> & WritableBoxedValues<{
9
11
  checked: boolean;
10
12
  indeterminate: boolean;
@@ -21,7 +23,7 @@ declare class CheckboxRootState {
21
23
  props: {
22
24
  readonly id: string;
23
25
  readonly role: "checkbox";
24
- readonly type: "button";
26
+ readonly type: "button" | "reset" | "submit" | null | undefined;
25
27
  readonly disabled: boolean;
26
28
  readonly "aria-checked": "true" | "false" | "mixed";
27
29
  readonly "aria-required": "true" | "false";
@@ -6,6 +6,7 @@ const CHECKBOX_ROOT_ATTR = "data-checkbox-root";
6
6
  class CheckboxRootState {
7
7
  #id;
8
8
  #ref;
9
+ #type;
9
10
  checked;
10
11
  disabled;
11
12
  required;
@@ -21,6 +22,7 @@ class CheckboxRootState {
21
22
  this.#ref = props.ref;
22
23
  this.#id = props.id;
23
24
  this.indeterminate = props.indeterminate;
25
+ this.#type = props.type;
24
26
  useRefById({
25
27
  id: this.#id,
26
28
  ref: this.#ref,
@@ -53,7 +55,7 @@ class CheckboxRootState {
53
55
  props = $derived.by(() => ({
54
56
  id: this.#id.current,
55
57
  role: "checkbox",
56
- type: "button",
58
+ type: this.#type.current,
57
59
  disabled: this.disabled.current,
58
60
  "aria-checked": getAriaChecked(this.checked.current, this.indeterminate.current),
59
61
  "aria-required": getAriaRequired(this.required.current),
@@ -20,6 +20,7 @@
20
20
  controlledIndeterminate = false,
21
21
  onIndeterminateChange,
22
22
  child,
23
+ type = "button",
23
24
  ...restProps
24
25
  }: CheckboxRootProps = $props();
25
26
 
@@ -55,6 +56,7 @@
55
56
  }
56
57
  }
57
58
  ),
59
+ type: box.with(() => type),
58
60
  });
59
61
 
60
62
  const mergedProps = $derived(mergeProps({ ...restProps }, rootState.props));
@@ -1,3 +1,4 @@
1
+ import type { KeyboardEventHandler, MouseEventHandler, PointerEventHandler } from "svelte/elements";
1
2
  import type { ReadableBoxedValues, WritableBoxedValues } from "../../internal/box.svelte.js";
2
3
  import type { WithRefProps } from "../../internal/types.js";
3
4
  type DialogVariant = "alert-dialog" | "dialog";
@@ -47,9 +48,9 @@ declare class DialogTriggerState {
47
48
  readonly "aria-haspopup": "dialog";
48
49
  readonly "aria-expanded": "true" | "false";
49
50
  readonly "aria-controls": string | undefined;
50
- readonly onpointerdown: (e: PointerEvent) => void;
51
- readonly onkeydown: (e: KeyboardEvent) => void;
52
- readonly onpointerup: (e: PointerEvent) => void;
51
+ readonly onpointerdown: PointerEventHandler<HTMLButtonElement>;
52
+ readonly onkeydown: KeyboardEventHandler<HTMLButtonElement>;
53
+ readonly onclick: MouseEventHandler<HTMLButtonElement>;
53
54
  };
54
55
  }
55
56
  type DialogCloseStateProps = WithRefProps & ReadableBoxedValues<{
@@ -62,9 +63,9 @@ declare class DialogCloseState {
62
63
  props: {
63
64
  readonly "data-state": "open" | "closed";
64
65
  readonly id: string;
65
- readonly onpointerdown: (e: PointerEvent) => void;
66
- readonly onpointerup: (e: PointerEvent) => void;
67
- readonly onkeydown: (e: KeyboardEvent) => void;
66
+ readonly onpointerdown: PointerEventHandler<HTMLButtonElement>;
67
+ readonly onclick: MouseEventHandler<HTMLButtonElement>;
68
+ readonly onkeydown: KeyboardEventHandler<HTMLButtonElement>;
68
69
  };
69
70
  }
70
71
  type DialogActionStateProps = WithRefProps;
@@ -142,9 +143,9 @@ declare class AlertDialogCancelState {
142
143
  props: {
143
144
  readonly "data-state": "open" | "closed";
144
145
  readonly id: string;
145
- readonly onpointerdown: (e: PointerEvent) => void;
146
- readonly onpointerup: (e: PointerEvent) => void;
147
- readonly onkeydown: (e: KeyboardEvent) => void;
146
+ readonly onpointerdown: PointerEventHandler<HTMLButtonElement>;
147
+ readonly onclick: MouseEventHandler<HTMLButtonElement>;
148
+ readonly onkeydown: KeyboardEventHandler<HTMLButtonElement>;
148
149
  };
149
150
  }
150
151
  export declare function useDialogRoot(props: DialogRootStateProps): DialogRootState;
@@ -64,6 +64,13 @@ class DialogTriggerState {
64
64
  },
65
65
  });
66
66
  }
67
+ #onclick = (e) => {
68
+ if (this.#disabled.current)
69
+ return;
70
+ if (e.button > 0)
71
+ return;
72
+ this.#root.handleOpen();
73
+ };
67
74
  #onpointerdown = (e) => {
68
75
  if (this.#disabled.current)
69
76
  return;
@@ -76,14 +83,6 @@ class DialogTriggerState {
76
83
  e.preventDefault();
77
84
  this.#root.handleOpen();
78
85
  };
79
- #onpointerup = (e) => {
80
- if (this.#disabled.current)
81
- return;
82
- if (e.pointerType === "touch") {
83
- e.preventDefault();
84
- this.#root.handleOpen();
85
- }
86
- };
87
86
  #onkeydown = (e) => {
88
87
  if (this.#disabled.current)
89
88
  return;
@@ -100,7 +99,7 @@ class DialogTriggerState {
100
99
  [this.#root.attrs.trigger]: "",
101
100
  onpointerdown: this.#onpointerdown,
102
101
  onkeydown: this.#onkeydown,
103
- onpointerup: this.#onpointerup,
102
+ onclick: this.#onclick,
104
103
  ...this.#root.sharedProps,
105
104
  }));
106
105
  }
@@ -123,22 +122,24 @@ class DialogCloseState {
123
122
  deps: () => this.#root.open.current,
124
123
  });
125
124
  }
126
- #onpointerdown = (e) => {
125
+ #onclick = (e) => {
127
126
  if (this.#disabled.current)
128
127
  return;
129
- if (e.pointerType === "touch")
130
- return e.preventDefault();
131
128
  if (e.button > 0)
132
129
  return;
133
130
  this.#root.handleClose();
134
131
  };
135
- #onpointerup = (e) => {
132
+ #onpointerdown = (e) => {
136
133
  if (this.#disabled.current)
137
134
  return;
138
- if (e.pointerType === "touch") {
139
- e.preventDefault();
140
- this.#root.handleClose();
141
- }
135
+ if (e.pointerType === "touch")
136
+ return e.preventDefault();
137
+ if (e.button > 0)
138
+ return;
139
+ // by default, it will attempt to focus this trigger on pointerdown
140
+ // since this also opens the dialog we want to prevent that behavior
141
+ e.preventDefault();
142
+ this.#root.handleClose();
142
143
  };
143
144
  #onkeydown = (e) => {
144
145
  if (this.#disabled.current)
@@ -152,7 +153,7 @@ class DialogCloseState {
152
153
  id: this.#id.current,
153
154
  [this.#attr]: "",
154
155
  onpointerdown: this.#onpointerdown,
155
- onpointerup: this.#onpointerup,
156
+ onclick: this.#onclick,
156
157
  onkeydown: this.#onkeydown,
157
158
  ...this.#root.sharedProps,
158
159
  }));
@@ -303,6 +304,13 @@ class AlertDialogCancelState {
303
304
  },
304
305
  });
305
306
  }
307
+ #onclick = (e) => {
308
+ if (this.#disabled.current)
309
+ return;
310
+ if (e.button > 0)
311
+ return;
312
+ this.#root.handleClose();
313
+ };
306
314
  #onpointerdown = (e) => {
307
315
  if (this.#disabled.current)
308
316
  return;
@@ -310,6 +318,9 @@ class AlertDialogCancelState {
310
318
  return e.preventDefault();
311
319
  if (e.button > 0)
312
320
  return;
321
+ // by default, it will attempt to focus this trigger on pointerdown
322
+ // since this also opens the dialog we want to prevent that behavior
323
+ e.preventDefault();
313
324
  this.#root.handleClose();
314
325
  };
315
326
  #onkeydown = (e) => {
@@ -320,19 +331,11 @@ class AlertDialogCancelState {
320
331
  this.#root.handleClose();
321
332
  }
322
333
  };
323
- #onpointerup = (e) => {
324
- if (this.#disabled.current)
325
- return;
326
- if (e.pointerType === "touch") {
327
- e.preventDefault();
328
- this.#root.handleClose();
329
- }
330
- };
331
334
  props = $derived.by(() => ({
332
335
  id: this.#id.current,
333
336
  [this.#root.attrs.cancel]: "",
334
337
  onpointerdown: this.#onpointerdown,
335
- onpointerup: this.#onpointerup,
338
+ onclick: this.#onclick,
336
339
  onkeydown: this.#onkeydown,
337
340
  ...this.#root.sharedProps,
338
341
  }));
@@ -73,15 +73,15 @@ export function useFocusScope({ id, loop, enabled, onOpenAutoFocus, onCloseAutoF
73
73
  focus(container);
74
74
  }
75
75
  }
76
- const unsubEvents = executeCallbacks(addEventListener(document, "focusin", handleFocusIn), addEventListener(document, "focusout", handleFocusOut));
77
- const mutationObserver = new MutationObserver(handleMutations);
78
- if (container) {
76
+ return untrack(() => {
77
+ const unsubEvents = executeCallbacks(addEventListener(document, "focusin", handleFocusIn), addEventListener(document, "focusout", handleFocusOut));
78
+ const mutationObserver = new MutationObserver(handleMutations);
79
79
  mutationObserver.observe(container, { childList: true, subtree: true });
80
- }
81
- return () => {
82
- unsubEvents();
83
- mutationObserver.disconnect();
84
- };
80
+ return () => {
81
+ unsubEvents();
82
+ mutationObserver.disconnect();
83
+ };
84
+ });
85
85
  });
86
86
  $effect(() => {
87
87
  if (forceMount.current)
@@ -100,8 +100,8 @@ export function useFocusScope({ id, loop, enabled, onOpenAutoFocus, onCloseAutoF
100
100
  $effect(() => {
101
101
  if (!forceMount.current)
102
102
  return;
103
- let container = ref.current;
104
103
  enabled.current;
104
+ const container = ref.current;
105
105
  const previouslyFocusedElement = document.activeElement;
106
106
  untrack(() => {
107
107
  handleMount(container, previouslyFocusedElement);
@@ -113,9 +113,8 @@ export function useFocusScope({ id, loop, enabled, onOpenAutoFocus, onCloseAutoF
113
113
  };
114
114
  });
115
115
  function handleMount(container, prevFocusedElement) {
116
- if (!container) {
116
+ if (!container)
117
117
  container = document.getElementById(id.current);
118
- }
119
118
  if (!container)
120
119
  return;
121
120
  focusScopeStack.add(focusScope);
@@ -2,5 +2,5 @@ export function shouldTrapFocus({ forceMount, present, trapFocus, open, }) {
2
2
  if (forceMount) {
3
3
  return open && trapFocus;
4
4
  }
5
- return present && trapFocus;
5
+ return present && trapFocus && open;
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bits-ui",
3
- "version": "1.0.0-next.56",
3
+ "version": "1.0.0-next.58",
4
4
  "license": "MIT",
5
5
  "repository": "github:huntabyte/bits-ui",
6
6
  "funding": "https://github.com/sponsors/huntabyte",