bits-ui 2.9.6 → 2.9.8

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 CHANGED
@@ -1,3 +1,4 @@
1
+ // oxlint-disable no-var
1
2
  import type { ReadableBox } from "svelte-toolbelt";
2
3
  import type { DismissibleLayerState } from "./bits/utilities/dismissible-layer/use-dismissable-layer.svelte.ts";
3
4
  import type { InteractOutsideBehaviorType } from "./bits/utilities/dismissible-layer/types.ts";
@@ -43,7 +43,7 @@
43
43
  () => value!,
44
44
  (v) => {
45
45
  value = v;
46
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
46
+ // oxlint-disable-next-line no-explicit-any
47
47
  onValueChange(v as any);
48
48
  }
49
49
  ) as WritableBox<string> | WritableBox<string[]>,
@@ -109,7 +109,7 @@
109
109
  () => value,
110
110
  (v) => {
111
111
  value = v;
112
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
112
+ // oxlint-disable-next-line no-explicit-any
113
113
  onValueChange(v as any);
114
114
  }
115
115
  ),
@@ -1,7 +1,7 @@
1
1
  import { type ReadableBoxedValues, type WritableBoxedValues } from "svelte-toolbelt";
2
2
  import type { HTMLButtonAttributes } from "svelte/elements";
3
3
  import { Context } from "runed";
4
- import type { BitsKeyboardEvent, BitsMouseEvent, OnChangeFn, RefAttachment, WithRefOpts } from "../../internal/types.js";
4
+ import type { BitsFocusEvent, BitsKeyboardEvent, BitsMouseEvent, OnChangeFn, RefAttachment, WithRefOpts } from "../../internal/types.js";
5
5
  interface CheckboxGroupStateOpts extends WithRefOpts, ReadableBoxedValues<{
6
6
  name: string | undefined;
7
7
  disabled: boolean;
@@ -91,6 +91,7 @@ export declare class CheckboxInputState {
91
91
  readonly trueChecked: boolean;
92
92
  readonly shouldRender: boolean;
93
93
  constructor(root: CheckboxRootState);
94
+ onfocus(_: BitsFocusEvent): void;
94
95
  readonly props: {
95
96
  readonly type: "checkbox";
96
97
  readonly checked: boolean;
@@ -99,6 +100,7 @@ export declare class CheckboxInputState {
99
100
  readonly name: string | undefined;
100
101
  readonly value: string | undefined;
101
102
  readonly readonly: boolean;
103
+ readonly onfocus: (_: BitsFocusEvent) => void;
102
104
  };
103
105
  }
104
106
  export {};
@@ -3,6 +3,7 @@ import { Context, watch } from "runed";
3
3
  import { createBitsAttrs, getAriaChecked, getAriaReadonly, getAriaRequired, getDataDisabled, getDataReadonly, } from "../../internal/attrs.js";
4
4
  import { kbd } from "../../internal/kbd.js";
5
5
  import { arraysAreEqual } from "../../internal/arrays.js";
6
+ import { isHTMLElement } from "../../internal/is.js";
6
7
  const checkboxAttrs = createBitsAttrs({
7
8
  component: "checkbox",
8
9
  parts: ["root", "group", "group-label", "input"],
@@ -192,6 +193,12 @@ export class CheckboxInputState {
192
193
  shouldRender = $derived.by(() => Boolean(this.root.trueName));
193
194
  constructor(root) {
194
195
  this.root = root;
196
+ this.onfocus = this.onfocus.bind(this);
197
+ }
198
+ onfocus(_) {
199
+ if (!isHTMLElement(this.root.opts.ref.current))
200
+ return;
201
+ this.root.opts.ref.current.focus();
195
202
  }
196
203
  props = $derived.by(() => ({
197
204
  type: "checkbox",
@@ -201,6 +208,7 @@ export class CheckboxInputState {
201
208
  name: this.root.trueName,
202
209
  value: this.root.opts.value.current,
203
210
  readonly: this.root.trueReadonly,
211
+ onfocus: this.onfocus,
204
212
  }));
205
213
  }
206
214
  function getCheckboxDataState(checked, indeterminate) {
@@ -528,7 +528,6 @@ export class CommandRootState {
528
528
  this.#scheduleUpdate();
529
529
  return () => {
530
530
  const selectedItem = this.#getSelectedItem();
531
- this.allIds.delete(id);
532
531
  this.allItems.delete(id);
533
532
  this.commandState.filtered.items.delete(id);
534
533
  this.#filterItems();
@@ -1132,16 +1131,21 @@ export class CommandItemState {
1132
1131
  () => this.#group?.trueValue,
1133
1132
  () => this.opts.forceMount.current,
1134
1133
  ], () => {
1135
- if (this.opts.forceMount.current)
1134
+ if (this.opts.forceMount.current || !this.trueValue)
1136
1135
  return;
1137
1136
  return this.root.registerItem(this.trueValue, this.#group?.trueValue);
1138
1137
  });
1139
1138
  watch([() => this.opts.value.current, () => this.opts.ref.current], () => {
1140
- if (!this.opts.value.current && this.opts.ref.current?.textContent) {
1139
+ if (this.opts.value.current) {
1140
+ this.trueValue = this.opts.value.current;
1141
+ }
1142
+ else if (this.opts.ref.current?.textContent) {
1141
1143
  this.trueValue = this.opts.ref.current.textContent.trim();
1142
1144
  }
1143
- this.root.registerValue(this.trueValue, opts.keywords.current.map((kw) => kw.trim()));
1144
- this.opts.ref.current?.setAttribute(COMMAND_VALUE_ATTR, this.trueValue);
1145
+ if (this.trueValue) {
1146
+ this.root.registerValue(this.trueValue, opts.keywords.current.map((kw) => kw.trim()));
1147
+ this.opts.ref.current?.setAttribute(COMMAND_VALUE_ATTR, this.trueValue);
1148
+ }
1145
1149
  });
1146
1150
  // bindings
1147
1151
  this.onclick = this.onclick.bind(this);
@@ -1,4 +1,4 @@
1
- /* eslint-disable @typescript-eslint/ban-ts-comment */
1
+ // oxlint-disable ban-ts-comment
2
2
  // @ts-nocheck
3
3
  // The scores are arranged so that a continuous match of characters will
4
4
  // result in a total score of 1.
@@ -1,7 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { watch } from "runed";
3
3
  import { box } from "svelte-toolbelt";
4
- import type { DateValue } from "@internationalized/date";
5
4
  import { DateFieldRootState } from "../date-field.svelte.js";
6
5
  import type { DateFieldRootProps } from "../types.js";
7
6
  import { noop } from "../../../internal/noop.js";
@@ -29,15 +28,19 @@
29
28
  children,
30
29
  }: DateFieldRootProps = $props();
31
30
 
32
- function handleDefaultPlaceholder() {
33
- if (placeholder !== undefined) return;
31
+ function handleDefaultPlaceholder(setPlaceholder = true) {
32
+ if (placeholder !== undefined) return placeholder;
34
33
 
35
34
  const defaultPlaceholder = getDefaultDate({
36
35
  granularity,
37
36
  defaultValue: value,
38
37
  });
39
38
 
40
- placeholder = defaultPlaceholder;
39
+ if (setPlaceholder) {
40
+ placeholder = defaultPlaceholder;
41
+ }
42
+
43
+ return defaultPlaceholder;
41
44
  }
42
45
 
43
46
  // SSR
@@ -64,8 +67,12 @@
64
67
  }
65
68
  ),
66
69
  placeholder: box.with(
67
- () => placeholder as DateValue,
70
+ () => {
71
+ if (placeholder === undefined) return handleDefaultPlaceholder(false);
72
+ return placeholder;
73
+ },
68
74
  (v) => {
75
+ if (v === undefined) return;
69
76
  placeholder = v;
70
77
  onPlaceholderChange(v);
71
78
  }
@@ -33,7 +33,11 @@ const SEGMENT_CONFIGS = {
33
33
  max: 12,
34
34
  cycle: 1,
35
35
  padZero: true,
36
- getAnnouncement: (month, root) => `${month} - ${root.formatter.fullMonth(toDate(root.placeholder.current.set({ month })))}`,
36
+ getAnnouncement: (month, root) => {
37
+ if (!root.placeholder.current)
38
+ return "";
39
+ return `${month} - ${root.formatter.fullMonth(toDate(root.placeholder.current.set({ month })))}`;
40
+ },
37
41
  },
38
42
  year: {
39
43
  min: 1,
@@ -384,16 +388,18 @@ export class DateFieldRootState {
384
388
  const inferred = inferGranularity(this.placeholder.current, this.granularity.current);
385
389
  return inferred;
386
390
  });
387
- dateRef = $derived.by(() => this.value.current ?? this.placeholder.current);
388
- allSegmentContent = $derived.by(() => createContent({
389
- segmentValues: this.segmentValues,
390
- formatter: this.formatter,
391
- locale: this.locale.current,
392
- granularity: this.inferredGranularity,
393
- dateRef: this.dateRef,
394
- hideTimeZone: this.hideTimeZone.current,
395
- hourCycle: this.hourCycle.current,
396
- }));
391
+ dateRef = $derived.by(() => this.value.current !== undefined ? this.value.current : this.placeholder.current);
392
+ allSegmentContent = $derived.by(() => {
393
+ return createContent({
394
+ segmentValues: this.segmentValues,
395
+ formatter: this.formatter,
396
+ locale: this.locale.current,
397
+ granularity: this.inferredGranularity,
398
+ dateRef: this.dateRef,
399
+ hideTimeZone: this.hideTimeZone.current,
400
+ hourCycle: this.hourCycle.current,
401
+ });
402
+ });
397
403
  segmentContents = $derived.by(() => this.allSegmentContent.arr);
398
404
  sharedSegmentAttrs = {
399
405
  role: "spinbutton",
@@ -1055,7 +1061,7 @@ class DateFieldHourSegmentState extends BaseNumericSegmentState {
1055
1061
  // Add special handling for hour display with dayPeriod
1056
1062
  if (isNumberString(e.key)) {
1057
1063
  const oldUpdateSegment = this.root.updateSegment.bind(this.root);
1058
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1064
+ // oxlint-disable-next-line no-explicit-any
1059
1065
  this.root.updateSegment = (part, cb) => {
1060
1066
  const result = oldUpdateSegment(part, cb);
1061
1067
  // After updating hour, check if we need to display "12" instead of "0"
@@ -413,7 +413,7 @@ export class PinInputCellState {
413
413
  ...this.attachment,
414
414
  }));
415
415
  }
416
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
416
+ // oxlint-disable-next-line no-explicit-any
417
417
  export function syncTimeouts(cb, domContext) {
418
418
  const t1 = domContext.setTimeout(cb, 0); // For faster machines
419
419
  const t2 = domContext.setTimeout(cb, 1_0);
@@ -73,12 +73,14 @@ export declare class RadioGroupInputState {
73
73
  static create(): RadioGroupInputState;
74
74
  readonly root: RadioGroupRootState;
75
75
  readonly shouldRender: boolean;
76
+ constructor(root: RadioGroupRootState);
77
+ onfocus(_: BitsFocusEvent): void;
76
78
  readonly props: {
77
79
  readonly name: string | undefined;
78
80
  readonly value: string;
79
81
  readonly required: boolean;
80
82
  readonly disabled: boolean;
83
+ readonly onfocus: (_: BitsFocusEvent) => void;
81
84
  };
82
- constructor(root: RadioGroupRootState);
83
85
  }
84
86
  export {};
@@ -130,13 +130,18 @@ export class RadioGroupInputState {
130
130
  }
131
131
  root;
132
132
  shouldRender = $derived.by(() => this.root.opts.name.current !== undefined);
133
+ constructor(root) {
134
+ this.root = root;
135
+ this.onfocus = this.onfocus.bind(this);
136
+ }
137
+ onfocus(_) {
138
+ this.root.rovingFocusGroup.focusCurrentTabStop();
139
+ }
133
140
  props = $derived.by(() => ({
134
141
  name: this.root.opts.name.current,
135
142
  value: this.root.opts.value.current,
136
143
  required: this.root.opts.required.current,
137
144
  disabled: this.root.opts.disabled.current,
145
+ onfocus: this.onfocus,
138
146
  }));
139
- constructor(root) {
140
- this.root = root;
141
- }
142
147
  }
@@ -12,7 +12,7 @@
12
12
  const scrollbarXState = ScrollAreaScrollbarXState.create({
13
13
  mounted: box.with(() => isMounted.current),
14
14
  });
15
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
15
+ // oxlint-disable-next-line no-explicit-any
16
16
  const mergedProps = $derived(mergeProps(restProps, scrollbarXState.props)) as any;
17
17
  </script>
18
18
 
@@ -13,7 +13,7 @@
13
13
  mounted: box.with(() => isMounted.current),
14
14
  });
15
15
 
16
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
+ // oxlint-disable-next-line no-explicit-any
17
17
  const mergedProps = $derived(mergeProps(restProps, scrollbarYState.props)) as any;
18
18
  </script>
19
19
 
@@ -48,7 +48,7 @@
48
48
  () => value!,
49
49
  (v) => {
50
50
  value = v;
51
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
51
+ // oxlint-disable-next-line no-explicit-any
52
52
  onValueChange(v as any);
53
53
  }
54
54
  ) as WritableBox<string> | WritableBox<string[]>,
@@ -796,7 +796,7 @@ class TimeFieldHourSegmentState extends BaseTimeSegmentState {
796
796
  onkeydown(e) {
797
797
  if (isNumberString(e.key)) {
798
798
  const oldUpdateSegment = this.root.updateSegment.bind(this.root);
799
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
799
+ // oxlint-disable-next-line no-explicit-any
800
800
  this.root.updateSegment = (part, cb) => {
801
801
  const result = oldUpdateSegment(part, cb);
802
802
  // after updating hour, check if we need to display "12" instead of "0"
@@ -227,10 +227,10 @@ function createWrappedEvent(e) {
227
227
  return isPrevented;
228
228
  }
229
229
  if (prop in target) {
230
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
230
+ // oxlint-disable-next-line no-explicit-any
231
231
  return target[prop];
232
232
  }
233
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
233
+ // oxlint-disable-next-line no-explicit-any
234
234
  return e[prop];
235
235
  },
236
236
  });
@@ -227,7 +227,7 @@ export function handleCalendarKeydown({ event, handleCellClick, shiftFocus, plac
227
227
  const currentCell = event.target;
228
228
  if (!isCalendarDayNode(currentCell))
229
229
  return;
230
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
230
+ // oxlint-disable-next-line no-explicit-any
231
231
  if (!ARROW_KEYS.includes(event.key) && !SELECT_KEYS.includes(event.key))
232
232
  return;
233
233
  event.preventDefault();
@@ -237,7 +237,7 @@ export function handleCalendarKeydown({ event, handleCellClick, shiftFocus, plac
237
237
  [kbd.ARROW_LEFT]: -1,
238
238
  [kbd.ARROW_RIGHT]: 1,
239
239
  };
240
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
240
+ // oxlint-disable-next-line no-explicit-any
241
241
  if (ARROW_KEYS.includes(event.key)) {
242
242
  const add = kbdFocusMap[event.key];
243
243
  if (add !== undefined) {
@@ -1,4 +1,4 @@
1
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1
+ // oxlint-disable-next-line no-explicit-any
2
2
  export function debounce(fn, wait = 500) {
3
3
  let timeout = null;
4
4
  const debounced = (...args) => {
@@ -84,7 +84,7 @@ export function getTabbableCandidates(container) {
84
84
  const nodes = [];
85
85
  const doc = getDocument(container);
86
86
  const walker = doc.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, {
87
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
87
+ // oxlint-disable-next-line no-explicit-any
88
88
  acceptNode: (node) => {
89
89
  const isHiddenInput = node.tagName === "INPUT" && node.type === "hidden";
90
90
  if (node.disabled || node.hidden || isHiddenInput)
@@ -39,5 +39,6 @@ export declare class RovingFocusGroup {
39
39
  handleKeydown(node: HTMLElement | null | undefined, e: KeyboardEvent, both?: boolean): HTMLElement | undefined;
40
40
  getTabIndex(node: HTMLElement | null | undefined): 0 | -1;
41
41
  setCurrentTabStopId(id: string): void;
42
+ focusCurrentTabStop(): void;
42
43
  }
43
44
  export {};
@@ -3,6 +3,7 @@ import { getElemDirection } from "./locale.js";
3
3
  import { getDirectionalKeys } from "./get-directional-keys.js";
4
4
  import { kbd } from "./kbd.js";
5
5
  import { BROWSER } from "esm-env";
6
+ import { isHTMLElement } from "./is.js";
6
7
  export class RovingFocusGroup {
7
8
  #opts;
8
9
  #currentTabStopId = box(null);
@@ -84,4 +85,13 @@ export class RovingFocusGroup {
84
85
  setCurrentTabStopId(id) {
85
86
  this.#currentTabStopId.current = id;
86
87
  }
88
+ focusCurrentTabStop() {
89
+ const currentTabStopId = this.#currentTabStopId.current;
90
+ if (!currentTabStopId)
91
+ return;
92
+ const currentTabStop = this.#opts.rootNode.current?.querySelector(`#${currentTabStopId}`);
93
+ if (!currentTabStop || !isHTMLElement(currentTabStop))
94
+ return;
95
+ currentTabStop.focus();
96
+ }
87
97
  }
@@ -0,0 +1 @@
1
+ export declare function warn(...messages: string[]): void;
@@ -0,0 +1,14 @@
1
+ import { DEV } from "esm-env";
2
+ let set;
3
+ if (DEV) {
4
+ set = new Set();
5
+ }
6
+ export function warn(...messages) {
7
+ if (!DEV)
8
+ return;
9
+ const msg = messages.join(" ");
10
+ if (set.has(msg))
11
+ return;
12
+ set.add(msg);
13
+ console.warn(`[Bits UI]: ${msg}`);
14
+ }
@@ -1,10 +1,9 @@
1
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
2
1
  import type * as CSS from "csstype";
3
2
 
4
3
  declare module "csstype" {
5
4
  interface Properties {
6
5
  // Allow any CSS Custom Properties
7
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
+ // oxlint-disable-next-line no-explicit-any
8
7
  [index: `--${string}`]: any;
9
8
  }
10
9
  }
@@ -72,7 +72,7 @@ export type EditableTimeSegmentPart = (typeof EDITABLE_TIME_SEGMENT_PARTS)[numbe
72
72
  export type EditableSegmentPart = (typeof EDITABLE_SEGMENT_PARTS)[number];
73
73
  export type NonEditableSegmentPart = (typeof NON_EDITABLE_SEGMENT_PARTS)[number];
74
74
  export type SegmentPart = EditableSegmentPart | NonEditableSegmentPart;
75
- export type TimeSegmentPart = EditableTimeSegmentPart | "literal";
75
+ export type TimeSegmentPart = EditableTimeSegmentPart | "literal" | "timeZoneName";
76
76
  export type AnyTimeExceptLiteral = Exclude<TimeSegmentPart, "literal">;
77
77
  export type AnyExceptLiteral = Exclude<SegmentPart, "literal">;
78
78
  export type DayPeriod = "AM" | "PM" | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bits-ui",
3
- "version": "2.9.6",
3
+ "version": "2.9.8",
4
4
  "license": "MIT",
5
5
  "repository": "github:huntabyte/bits-ui",
6
6
  "funding": "https://github.com/sponsors/huntabyte",
@@ -21,18 +21,18 @@
21
21
  "devDependencies": {
22
22
  "@internationalized/date": "^3.8.2",
23
23
  "@sveltejs/kit": "^2.31.0",
24
- "@sveltejs/package": "^2.4.1",
25
- "@sveltejs/vite-plugin-svelte": "^6.1.2",
24
+ "@sveltejs/package": "2.5.0",
25
+ "@sveltejs/vite-plugin-svelte": "^6.2.0",
26
26
  "@types/node": "^20.19.0",
27
27
  "@types/resize-observer-browser": "^0.1.11",
28
28
  "csstype": "^3.1.3",
29
29
  "jsdom": "^24.1.3",
30
30
  "publint": "^0.2.12",
31
- "svelte": "^5.38.1",
31
+ "svelte": "^5.38.10",
32
32
  "svelte-check": "^4.3.1",
33
- "typescript": "^5.8.3",
34
- "vite": "^7.0.4",
35
- "vitest": "^3.2.3"
33
+ "typescript": "^5.9.2",
34
+ "vite": "^7.1.5",
35
+ "vitest": "^3.2.4"
36
36
  },
37
37
  "svelte": "./dist/index.js",
38
38
  "types": "./dist/index.d.ts",