bits-ui 1.0.0-next.34 → 1.0.0-next.36

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.
@@ -22,6 +22,7 @@ declare class PinInputRootState {
22
22
  #private;
23
23
  value: PinInputRootStateProps["value"];
24
24
  constructor(props: PinInputRootStateProps);
25
+ keysToIgnore: string[];
25
26
  rootProps: {
26
27
  readonly id: string;
27
28
  readonly "data-pin-input-root": "";
@@ -80,6 +81,7 @@ declare class PinInputRootState {
80
81
  oninput: (e: Event & {
81
82
  currentTarget: HTMLInputElement;
82
83
  }) => void;
84
+ onkeydown: (e: KeyboardEvent) => void;
83
85
  onmouseover: () => void;
84
86
  onmouseleave: () => void;
85
87
  onfocus: (_: FocusEvent & {
@@ -37,7 +37,10 @@ class PinInputRootState {
37
37
  return this.#pattern.current;
38
38
  }
39
39
  });
40
- #prevInputMetadata = $state([null, null, "none"]);
40
+ #prevInputMetadata = $state({
41
+ prev: [null, null, "none"],
42
+ willSyntheticBlur: false,
43
+ });
41
44
  #pushPasswordManagerStrategy;
42
45
  #pwmb;
43
46
  #initialLoad;
@@ -84,7 +87,7 @@ class PinInputRootState {
84
87
  if (this.#initialLoad.value.current !== input.value) {
85
88
  this.value.current = input.value;
86
89
  }
87
- this.#prevInputMetadata = [
90
+ this.#prevInputMetadata.prev = [
88
91
  input.selectionStart,
89
92
  input.selectionEnd,
90
93
  input.selectionDirection ?? "none",
@@ -127,7 +130,7 @@ class PinInputRootState {
127
130
  if (start !== null && end !== null) {
128
131
  this.#mirrorSelectionStart = start;
129
132
  this.#mirrorSelectionEnd = end;
130
- this.#prevInputMetadata = [start, end, dir];
133
+ this.#prevInputMetadata.prev = [start, end, dir];
131
134
  }
132
135
  });
133
136
  });
@@ -144,6 +147,29 @@ class PinInputRootState {
144
147
  }
145
148
  });
146
149
  }
150
+ keysToIgnore = [
151
+ "Backspace",
152
+ "Delete",
153
+ "ArrowLeft",
154
+ "ArrowRight",
155
+ "ArrowUp",
156
+ "ArrowDown",
157
+ "Home",
158
+ "End",
159
+ "Escape",
160
+ "Enter",
161
+ "Tab",
162
+ "Shift",
163
+ "Control",
164
+ ];
165
+ #onkeydown = (e) => {
166
+ const key = e.key;
167
+ if (this.keysToIgnore.includes(key))
168
+ return;
169
+ if (key && this.#regexPattern && !this.#regexPattern.test(key)) {
170
+ e.preventDefault();
171
+ }
172
+ };
147
173
  #rootStyles = $derived.by(() => ({
148
174
  position: "relative",
149
175
  cursor: this.#disabled.current ? "default" : "text",
@@ -219,7 +245,7 @@ class PinInputRootState {
219
245
  const selDir = input.selectionDirection ?? "none";
220
246
  const maxLength = input.maxLength;
221
247
  const val = input.value;
222
- const prev = this.#prevInputMetadata;
248
+ const prev = this.#prevInputMetadata.prev;
223
249
  let start = -1;
224
250
  let end = -1;
225
251
  let direction;
@@ -261,7 +287,7 @@ class PinInputRootState {
261
287
  const dir = direction ?? selDir;
262
288
  this.#mirrorSelectionStart = s;
263
289
  this.#mirrorSelectionEnd = e;
264
- this.#prevInputMetadata = [s, e, dir];
290
+ this.#prevInputMetadata.prev = [s, e, dir];
265
291
  };
266
292
  #oninput = (e) => {
267
293
  const newValue = e.currentTarget.value.slice(0, this.#maxLength.current);
@@ -293,11 +319,28 @@ class PinInputRootState {
293
319
  };
294
320
  #onpaste = (e) => {
295
321
  const input = this.#inputRef.current;
322
+ if (!this.#initialLoad.isIOS) {
323
+ if (!e.clipboardData || !input)
324
+ return;
325
+ const content = e.clipboardData.getData("text/plain");
326
+ const sanitizedContent = this.#onPaste?.current?.(content) ?? content;
327
+ if (sanitizedContent.length > 0 &&
328
+ this.#regexPattern &&
329
+ !this.#regexPattern.test(sanitizedContent)) {
330
+ e.preventDefault();
331
+ return;
332
+ }
333
+ }
296
334
  if (!this.#initialLoad.isIOS || !e.clipboardData || !input)
297
335
  return;
298
336
  const content = e.clipboardData.getData("text/plain");
299
337
  e.preventDefault();
300
338
  const sanitizedContent = this.#onPaste?.current?.(content) ?? content;
339
+ if (sanitizedContent.length > 0 &&
340
+ this.#regexPattern &&
341
+ !this.#regexPattern.test(sanitizedContent)) {
342
+ return;
343
+ }
301
344
  const start = input.selectionStart === null ? undefined : input.selectionStart;
302
345
  const end = input.selectionEnd === null ? undefined : input.selectionEnd;
303
346
  const isReplacing = start !== end;
@@ -324,6 +367,10 @@ class PinInputRootState {
324
367
  this.#isHoveringInput = false;
325
368
  };
326
369
  #onblur = () => {
370
+ if (this.#prevInputMetadata.willSyntheticBlur) {
371
+ this.#prevInputMetadata.willSyntheticBlur = false;
372
+ return;
373
+ }
327
374
  this.#isFocused.current = false;
328
375
  };
329
376
  inputProps = $derived.by(() => ({
@@ -341,6 +388,7 @@ class PinInputRootState {
341
388
  //
342
389
  onpaste: this.#onpaste,
343
390
  oninput: this.#oninput,
391
+ onkeydown: this.#onkeydown,
344
392
  onmouseover: this.#onmouseover,
345
393
  onmouseleave: this.#onmouseleave,
346
394
  onfocus: this.#onfocus,
@@ -16,10 +16,10 @@ export function usePasswordManagerBadge({ containerRef, inputRef, pushPasswordMa
16
16
  let hasPwmBadgeSpace = $state(false);
17
17
  let done = $state(false);
18
18
  function willPushPwmBadge() {
19
- const strat = pushPasswordManagerStrategy.current;
20
- if (strat === "none")
19
+ const strategy = pushPasswordManagerStrategy.current;
20
+ if (strategy === "none")
21
21
  return false;
22
- const increaseWidthCase = strat === "increase-width" && hasPwmBadge && hasPwmBadgeSpace;
22
+ const increaseWidthCase = strategy === "increase-width" && hasPwmBadge && hasPwmBadgeSpace;
23
23
  return increaseWidthCase;
24
24
  }
25
25
  function trackPwmBadge() {
@@ -35,10 +35,10 @@ export function usePasswordManagerBadge({ containerRef, inputRef, pushPasswordMa
35
35
  const x = rightCornerX - PWM_BADGE_MARGIN_RIGHT;
36
36
  const y = centeredY;
37
37
  // do an extra search to check for all the password manager badges
38
- const pwms = document.querySelectorAll(PASSWORD_MANAGER_SELECTORS);
39
- // if no password manager is detected, dispatch document.elementfrompoint to
38
+ const passwordManagerStrategy = document.querySelectorAll(PASSWORD_MANAGER_SELECTORS);
39
+ // if no password manager is detected, dispatch document.elementFromPoint to
40
40
  // identify the badges
41
- if (pwms.length === 0) {
41
+ if (passwordManagerStrategy.length === 0) {
42
42
  const maybeBadgeEl = document.elementFromPoint(x, y);
43
43
  // if the found element is the container,
44
44
  // then it is not a badge, most times there is no badge in this case
@@ -63,8 +63,6 @@
63
63
  }
64
64
  }
65
65
 
66
- value === undefined && (value = { start: undefined, end: undefined });
67
-
68
66
  const rootState = useRangeCalendarRoot({
69
67
  id: box.with(() => id),
70
68
  ref: box.with(
@@ -114,6 +114,23 @@ export class RangeCalendarRootState {
114
114
  return;
115
115
  node.textContent = this.fullCalendarLabel;
116
116
  });
117
+ /**
118
+ * Synchronize the start and end values with the `value` in case
119
+ * it is updated externally.
120
+ */
121
+ $effect(() => {
122
+ const value = this.value.current;
123
+ untrack(() => {
124
+ if (value.start && value.end) {
125
+ this.startValue.current = value.start;
126
+ this.endValue.current = value.end;
127
+ }
128
+ else if (value.start) {
129
+ this.startValue.current = value.start;
130
+ this.endValue.current = undefined;
131
+ }
132
+ });
133
+ });
117
134
  /**
118
135
  * Synchronize the placeholder value with the current start value
119
136
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bits-ui",
3
- "version": "1.0.0-next.34",
3
+ "version": "1.0.0-next.36",
4
4
  "license": "MIT",
5
5
  "repository": "github:huntabyte/bits-ui",
6
6
  "funding": "https://github.com/sponsors/huntabyte",