bits-ui 1.0.0-next.47 → 1.0.0-next.49

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,7 +6,8 @@ type CheckboxRootStateProps = WithRefProps<ReadableBoxedValues<{
6
6
  name: string | undefined;
7
7
  value: string | undefined;
8
8
  }> & WritableBoxedValues<{
9
- checked: boolean | "indeterminate";
9
+ checked: boolean;
10
+ indeterminate: boolean;
10
11
  }>>;
11
12
  declare class CheckboxRootState {
12
13
  #private;
@@ -15,6 +16,7 @@ declare class CheckboxRootState {
15
16
  required: CheckboxRootStateProps["required"];
16
17
  name: CheckboxRootStateProps["name"];
17
18
  value: CheckboxRootStateProps["value"];
19
+ indeterminate: CheckboxRootStateProps["indeterminate"];
18
20
  constructor(props: CheckboxRootStateProps);
19
21
  props: {
20
22
  readonly id: string;
@@ -11,6 +11,7 @@ class CheckboxRootState {
11
11
  required;
12
12
  name;
13
13
  value;
14
+ indeterminate;
14
15
  constructor(props) {
15
16
  this.checked = props.checked;
16
17
  this.disabled = props.disabled;
@@ -19,6 +20,7 @@ class CheckboxRootState {
19
20
  this.value = props.value;
20
21
  this.#ref = props.ref;
21
22
  this.#id = props.id;
23
+ this.indeterminate = props.indeterminate;
22
24
  useRefById({
23
25
  id: this.#id,
24
26
  ref: this.#ref,
@@ -35,7 +37,8 @@ class CheckboxRootState {
35
37
  }
36
38
  };
37
39
  #toggle = () => {
38
- if (this.checked.current === "indeterminate") {
40
+ if (this.indeterminate.current) {
41
+ this.indeterminate.current = false;
39
42
  this.checked.current = true;
40
43
  }
41
44
  else {
@@ -52,10 +55,10 @@ class CheckboxRootState {
52
55
  role: "checkbox",
53
56
  type: "button",
54
57
  disabled: this.disabled.current,
55
- "aria-checked": getAriaChecked(this.checked.current),
58
+ "aria-checked": getAriaChecked(this.checked.current, this.indeterminate.current),
56
59
  "aria-required": getAriaRequired(this.required.current),
57
60
  "data-disabled": getDataDisabled(this.disabled.current),
58
- "data-state": getCheckboxDataState(this.checked.current),
61
+ "data-state": getCheckboxDataState(this.checked.current, this.indeterminate.current),
59
62
  [CHECKBOX_ROOT_ATTR]: "",
60
63
  //
61
64
  onclick: this.#onclick,
@@ -85,8 +88,8 @@ class CheckboxInputState {
85
88
  //
86
89
  // HELPERS
87
90
  //
88
- function getCheckboxDataState(checked) {
89
- if (checked === "indeterminate") {
91
+ function getCheckboxDataState(checked, indeterminate) {
92
+ if (indeterminate) {
90
93
  return "indeterminate";
91
94
  }
92
95
  return checked ? "checked" : "unchecked";
@@ -16,6 +16,9 @@
16
16
  value = "on",
17
17
  id = useId(),
18
18
  controlledChecked = false,
19
+ indeterminate = $bindable(false),
20
+ controlledIndeterminate = false,
21
+ onIndeterminateChange,
19
22
  child,
20
23
  ...restProps
21
24
  }: CheckboxRootProps = $props();
@@ -41,17 +44,33 @@
41
44
  () => ref,
42
45
  (v) => (ref = v)
43
46
  ),
47
+ indeterminate: box.with(
48
+ () => indeterminate,
49
+ (v) => {
50
+ if (controlledIndeterminate) {
51
+ onIndeterminateChange?.(v);
52
+ } else {
53
+ indeterminate = v;
54
+ onIndeterminateChange?.(v);
55
+ }
56
+ }
57
+ ),
44
58
  });
45
59
 
46
60
  const mergedProps = $derived(mergeProps({ ...restProps }, rootState.props));
47
61
  </script>
48
62
 
49
63
  {#if child}
50
- {@render child({ props: mergedProps, checked: rootState.checked.current })}
64
+ {@render child({
65
+ props: mergedProps,
66
+ checked: rootState.checked.current,
67
+ indeterminate: rootState.indeterminate.current,
68
+ })}
51
69
  {:else}
52
70
  <button {...mergedProps}>
53
71
  {@render children?.({
54
72
  checked: rootState.checked.current,
73
+ indeterminate: rootState.indeterminate.current,
55
74
  })}
56
75
  </button>
57
76
  {/if}
@@ -14,6 +14,6 @@ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> =
14
14
  }
15
15
  declare const Checkbox: $$__sveltets_2_IsomorphicComponent<CheckboxRootProps, {
16
16
  [evt: string]: CustomEvent<any>;
17
- }, {}, {}, "checked" | "ref">;
17
+ }, {}, {}, "checked" | "indeterminate" | "ref">;
18
18
  type Checkbox = InstanceType<typeof Checkbox>;
19
19
  export default Checkbox;
@@ -1,7 +1,8 @@
1
1
  import type { OnChangeFn, WithChild, Without } from "../../internal/types.js";
2
2
  import type { BitsPrimitiveButtonAttributes } from "../../shared/attributes.js";
3
3
  export type CheckboxRootSnippetProps = {
4
- checked: boolean | "indeterminate";
4
+ checked: boolean;
5
+ indeterminate: boolean;
5
6
  };
6
7
  export type CheckboxRootPropsWithoutHTML = WithChild<{
7
8
  /**
@@ -33,15 +34,14 @@ export type CheckboxRootPropsWithoutHTML = WithChild<{
33
34
  * The checked state of the checkbox. It can be one of:
34
35
  * - `true` for checked
35
36
  * - `false` for unchecked
36
- * - `"indeterminate"` for indeterminate
37
37
  *
38
38
  * @defaultValue false
39
39
  */
40
- checked?: boolean | "indeterminate" | undefined;
40
+ checked?: boolean;
41
41
  /**
42
42
  * A callback function called when the checked state changes.
43
43
  */
44
- onCheckedChange?: OnChangeFn<boolean | "indeterminate">;
44
+ onCheckedChange?: OnChangeFn<boolean>;
45
45
  /**
46
46
  * Whether or not the checkbox is controlled or not. If `true`, the checkbox will not update
47
47
  * the checked state internally, instead it will call `onCheckedChange` when it would have
@@ -51,5 +51,24 @@ export type CheckboxRootPropsWithoutHTML = WithChild<{
51
51
  * @defaultValue false
52
52
  */
53
53
  controlledChecked?: boolean;
54
+ /**
55
+ * Whether the checkbox is in an indeterminate state or not.
56
+ *
57
+ * @defaultValue false
58
+ */
59
+ indeterminate?: boolean;
60
+ /**
61
+ * A callback function called when the indeterminate state changes.
62
+ */
63
+ onIndeterminateChange?: OnChangeFn<boolean>;
64
+ /**
65
+ * Whether the indeterminate state is controlled or not. If `true`, the checkbox will
66
+ * not update the indeterminate state internally, instead it will call
67
+ * `onIndeterminateChange` when it would have otherwise, and it is up to you to update
68
+ * the `indeterminate` prop that is passed to the component.
69
+ *
70
+ * @defaultValue false
71
+ */
72
+ controlledIndeterminate?: boolean;
54
73
  }, CheckboxRootSnippetProps>;
55
74
  export type CheckboxRootProps = CheckboxRootPropsWithoutHTML & Without<BitsPrimitiveButtonAttributes, CheckboxRootPropsWithoutHTML>;
@@ -16,6 +16,9 @@
16
16
  onSelect = noop,
17
17
  controlledChecked = false,
18
18
  closeOnSelect = true,
19
+ indeterminate = $bindable(false),
20
+ controlledIndeterminate = false,
21
+ onIndeterminateChange = noop,
19
22
  ...restProps
20
23
  }: MenuCheckboxItemProps = $props();
21
24
 
@@ -39,6 +42,17 @@
39
42
  (v) => (ref = v)
40
43
  ),
41
44
  closeOnSelect: box.with(() => closeOnSelect),
45
+ indeterminate: box.with(
46
+ () => indeterminate,
47
+ (v) => {
48
+ if (controlledIndeterminate) {
49
+ onIndeterminateChange(v);
50
+ } else {
51
+ indeterminate = v;
52
+ onIndeterminateChange(v);
53
+ }
54
+ }
55
+ ),
42
56
  });
43
57
 
44
58
  function handleSelect(e: Event) {
@@ -51,9 +65,9 @@
51
65
  </script>
52
66
 
53
67
  {#if child}
54
- {@render child({ props: mergedProps, checked })}
68
+ {@render child({ props: mergedProps, ...checkboxItemState.snippetProps })}
55
69
  {:else}
56
70
  <div {...mergedProps}>
57
- {@render children?.({ checked })}
71
+ {@render children?.(checkboxItemState.snippetProps)}
58
72
  </div>
59
73
  {/if}
@@ -14,6 +14,6 @@ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> =
14
14
  }
15
15
  declare const MenuCheckboxItem: $$__sveltets_2_IsomorphicComponent<MenuCheckboxItemProps, {
16
16
  [evt: string]: CustomEvent<any>;
17
- }, {}, {}, "checked" | "ref">;
17
+ }, {}, {}, "checked" | "indeterminate" | "ref">;
18
18
  type MenuCheckboxItem = InstanceType<typeof MenuCheckboxItem>;
19
19
  export default MenuCheckboxItem;
@@ -167,12 +167,17 @@ declare class MenuSubTriggerState {
167
167
  };
168
168
  }
169
169
  type MenuCheckboxItemStateProps = WritableBoxedValues<{
170
- checked: boolean | "indeterminate";
170
+ checked: boolean;
171
+ indeterminate: boolean;
171
172
  }>;
172
173
  declare class MenuCheckboxItemState {
173
174
  #private;
174
175
  constructor(props: MenuCheckboxItemStateProps, item: MenuItemState);
175
176
  toggleChecked: () => void;
177
+ snippetProps: {
178
+ checked: boolean;
179
+ indeterminate: boolean;
180
+ };
176
181
  props: {
177
182
  readonly [x: string]: string | -1 | ((e: PointerEvent) => void) | undefined;
178
183
  readonly role: "menuitemcheckbox";
@@ -511,25 +511,29 @@ class MenuSubTriggerState {
511
511
  class MenuCheckboxItemState {
512
512
  #item;
513
513
  #checked;
514
+ #indeterminate;
514
515
  constructor(props, item) {
515
516
  this.#item = item;
516
517
  this.#checked = props.checked;
518
+ this.#indeterminate = props.indeterminate;
517
519
  }
518
520
  toggleChecked = () => {
519
- if (this.#checked.current === true) {
520
- this.#checked.current = false;
521
- }
522
- else if (this.#checked.current === false) {
521
+ if (this.#indeterminate.current) {
522
+ this.#indeterminate.current = false;
523
523
  this.#checked.current = true;
524
524
  }
525
- else if (this.#checked.current === "indeterminate") {
526
- this.#checked.current = true;
525
+ else {
526
+ this.#checked.current = !this.#checked.current;
527
527
  }
528
528
  };
529
+ snippetProps = $derived.by(() => ({
530
+ checked: this.#checked.current,
531
+ indeterminate: this.#indeterminate.current,
532
+ }));
529
533
  props = $derived.by(() => ({
530
534
  ...this.#item.props,
531
535
  role: "menuitemcheckbox",
532
- "aria-checked": getAriaChecked(this.#checked.current),
536
+ "aria-checked": getAriaChecked(this.#checked.current, this.#indeterminate.current),
533
537
  "data-state": getCheckedState(this.#checked.current),
534
538
  [this.#item.root.getAttr("checkbox-item")]: "",
535
539
  }));
@@ -660,7 +664,7 @@ class MenuRadioItemState {
660
664
  [this.#group.root.getAttr("radio-item")]: "",
661
665
  ...this.#item.props,
662
666
  role: "menuitemradio",
663
- "aria-checked": getAriaChecked(this.isChecked),
667
+ "aria-checked": getAriaChecked(this.isChecked, false),
664
668
  "data-state": getCheckedState(this.isChecked),
665
669
  }));
666
670
  }
@@ -80,19 +80,22 @@ export type MenuItemPropsWithoutHTML<U extends Record<PropertyKey, unknown> = {
80
80
  }, U>;
81
81
  export type MenuItemProps = MenuItemPropsWithoutHTML & Without<BitsPrimitiveDivAttributes, MenuItemPropsWithoutHTML>;
82
82
  export type MenuCheckboxItemSnippetProps = {
83
- checked: boolean | "indeterminate";
83
+ checked: boolean;
84
+ indeterminate: boolean;
84
85
  };
85
86
  export type MenuCheckboxItemPropsWithoutHTML = MenuItemPropsWithoutHTML<MenuCheckboxItemSnippetProps> & {
86
87
  /**
87
- * The checked state of the checkbox item.
88
+ * The checked state of the checkbox. It can be one of:
89
+ * - `true` for checked
90
+ * - `false` for unchecked
88
91
  *
89
- * Supports two-way binding with `bind:checked`.
92
+ * @defaultValue false
90
93
  */
91
- checked?: boolean | "indeterminate";
94
+ checked?: boolean;
92
95
  /**
93
96
  * A callback that is fired when the checked state changes.
94
97
  */
95
- onCheckedChange?: OnChangeFn<boolean | "indeterminate">;
98
+ onCheckedChange?: OnChangeFn<boolean>;
96
99
  /**
97
100
  * Whether or not the checked state is controlled or not. If `true`, the component will not
98
101
  * update the checked state internally, instead it will call `onCheckedChange` when it
@@ -102,6 +105,25 @@ export type MenuCheckboxItemPropsWithoutHTML = MenuItemPropsWithoutHTML<MenuChec
102
105
  * @defaultValue false
103
106
  */
104
107
  controlledChecked?: boolean;
108
+ /**
109
+ * Whether the checkbox is in an indeterminate state or not.
110
+ *
111
+ * @defaultValue false
112
+ */
113
+ indeterminate?: boolean;
114
+ /**
115
+ * A callback function called when the indeterminate state changes.
116
+ */
117
+ onIndeterminateChange?: OnChangeFn<boolean>;
118
+ /**
119
+ * Whether the indeterminate state is controlled or not. If `true`, the checkbox will
120
+ * not update the indeterminate state internally, instead it will call
121
+ * `onIndeterminateChange` when it would have otherwise, and it is up to you to update
122
+ * the `indeterminate` prop that is passed to the component.
123
+ *
124
+ * @defaultValue false
125
+ */
126
+ controlledIndeterminate?: boolean;
105
127
  /**
106
128
  * Whether or not the menu item should close when selected.
107
129
  *
@@ -102,7 +102,7 @@ class RadioGroupItemState {
102
102
  "data-orientation": this.#root.orientation.current,
103
103
  "data-disabled": getDataDisabled(this.#isDisabled),
104
104
  "data-state": this.#isChecked ? "checked" : "unchecked",
105
- "aria-checked": getAriaChecked(this.#isChecked),
105
+ "aria-checked": getAriaChecked(this.#isChecked, false),
106
106
  [RADIO_GROUP_ITEM_ATTR]: "",
107
107
  type: "button",
108
108
  role: "radio",
@@ -49,7 +49,7 @@ class SwitchRootState {
49
49
  id: this.#id.current,
50
50
  role: "switch",
51
51
  disabled: getDisabled(this.disabled.current),
52
- "aria-checked": getAriaChecked(this.checked.current),
52
+ "aria-checked": getAriaChecked(this.checked.current, false),
53
53
  "aria-required": getAriaRequired(this.required.current),
54
54
  [ROOT_ATTR]: "",
55
55
  //
@@ -131,7 +131,7 @@ class ToggleGroupItemState {
131
131
  };
132
132
  isPressed = $derived.by(() => this.#root.includesItem(this.#value.current));
133
133
  #ariaChecked = $derived.by(() => {
134
- return this.#root.isMulti ? undefined : getAriaChecked(this.isPressed);
134
+ return this.#root.isMulti ? undefined : getAriaChecked(this.isPressed, false);
135
135
  });
136
136
  #ariaPressed = $derived.by(() => {
137
137
  return this.#root.isMulti ? getAriaPressed(this.isPressed) : undefined;
@@ -150,7 +150,7 @@ class ToolbarGroupItemState {
150
150
  };
151
151
  isPressed = $derived.by(() => this.#group.includesItem(this.#value.current));
152
152
  #ariaChecked = $derived.by(() => {
153
- return this.#group.isMulti ? undefined : getAriaChecked(this.isPressed);
153
+ return this.#group.isMulti ? undefined : getAriaChecked(this.isPressed, false);
154
154
  });
155
155
  #ariaPressed = $derived.by(() => {
156
156
  return this.#group.isMulti ? getAriaPressed(this.isPressed) : undefined;
@@ -6,7 +6,7 @@ export declare function getAriaExpanded(condition: boolean): "true" | "false";
6
6
  export declare function getDataDisabled(condition: boolean): "" | undefined;
7
7
  export declare function getAriaRequired(condition: boolean): "true" | "false";
8
8
  export declare function getAriaSelected(condition: boolean): "true" | "false";
9
- export declare function getAriaChecked(condition: boolean | "indeterminate"): "true" | "false" | "mixed";
9
+ export declare function getAriaChecked(checked: boolean, indeterminate: boolean): "true" | "false" | "mixed";
10
10
  export declare function getAriaOrientation(orientation: "horizontal" | "vertical"): "horizontal" | "vertical";
11
11
  export declare function getAriaHidden(condition: boolean): "true" | undefined;
12
12
  export declare function getAriaInvalid(condition: boolean): "true" | undefined;
@@ -22,11 +22,11 @@ export function getAriaRequired(condition) {
22
22
  export function getAriaSelected(condition) {
23
23
  return condition ? "true" : "false";
24
24
  }
25
- export function getAriaChecked(condition) {
26
- if (condition === "indeterminate") {
25
+ export function getAriaChecked(checked, indeterminate) {
26
+ if (indeterminate) {
27
27
  return "mixed";
28
28
  }
29
- return condition ? "true" : "false";
29
+ return checked ? "true" : "false";
30
30
  }
31
31
  export function getAriaOrientation(orientation) {
32
32
  return orientation;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bits-ui",
3
- "version": "1.0.0-next.47",
3
+ "version": "1.0.0-next.49",
4
4
  "license": "MIT",
5
5
  "repository": "github:huntabyte/bits-ui",
6
6
  "funding": "https://github.com/sponsors/huntabyte",