@zag-js/number-input 1.31.1 → 1.33.0

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/index.js CHANGED
@@ -19,6 +19,86 @@ var anatomy = anatomy$1.createAnatomy("numberInput").parts(
19
19
  "scrubber"
20
20
  );
21
21
  var parts = anatomy.build();
22
+
23
+ // src/cursor.ts
24
+ function recordCursor(inputEl, scope) {
25
+ if (!inputEl || !scope.isActiveElement(inputEl)) return;
26
+ try {
27
+ const { selectionStart: start, selectionEnd: end, value } = inputEl;
28
+ if (start == null || end == null) return void 0;
29
+ return { start, end, value };
30
+ } catch {
31
+ return void 0;
32
+ }
33
+ }
34
+ function restoreCursor(inputEl, selection, scope) {
35
+ if (!inputEl || !scope.isActiveElement(inputEl)) return;
36
+ if (!selection) {
37
+ const len = inputEl.value.length;
38
+ inputEl.setSelectionRange(len, len);
39
+ return;
40
+ }
41
+ try {
42
+ const newValue = inputEl.value;
43
+ const { start, end, value: oldValue } = selection;
44
+ if (newValue === oldValue) {
45
+ inputEl.setSelectionRange(start, end);
46
+ return;
47
+ }
48
+ const newStart = getNextCursorPosition(oldValue, newValue, start);
49
+ const newEnd = start === end ? newStart : getNextCursorPosition(oldValue, newValue, end);
50
+ const clampedStart = Math.max(0, Math.min(newStart, newValue.length));
51
+ const clampedEnd = Math.max(clampedStart, Math.min(newEnd, newValue.length));
52
+ inputEl.setSelectionRange(clampedStart, clampedEnd);
53
+ } catch {
54
+ const len = inputEl.value.length;
55
+ inputEl.setSelectionRange(len, len);
56
+ }
57
+ }
58
+ function getNextCursorPosition(oldValue, newValue, oldPosition) {
59
+ const beforeCursor = oldValue.slice(0, oldPosition);
60
+ const afterCursor = oldValue.slice(oldPosition);
61
+ let prefixLength = 0;
62
+ const maxPrefixLength = Math.min(beforeCursor.length, newValue.length);
63
+ for (let i = 0; i < maxPrefixLength; i++) {
64
+ if (beforeCursor[i] === newValue[i]) {
65
+ prefixLength = i + 1;
66
+ } else {
67
+ break;
68
+ }
69
+ }
70
+ let suffixLength = 0;
71
+ const maxSuffixLength = Math.min(afterCursor.length, newValue.length - prefixLength);
72
+ for (let i = 0; i < maxSuffixLength; i++) {
73
+ const oldIndex = afterCursor.length - 1 - i;
74
+ const newIndex = newValue.length - 1 - i;
75
+ if (afterCursor[oldIndex] === newValue[newIndex]) {
76
+ suffixLength = i + 1;
77
+ } else {
78
+ break;
79
+ }
80
+ }
81
+ if (beforeCursor.length > 0 && prefixLength >= beforeCursor.length) {
82
+ return prefixLength;
83
+ }
84
+ if (suffixLength >= afterCursor.length) {
85
+ return newValue.length - suffixLength;
86
+ }
87
+ if (prefixLength > 0) {
88
+ return prefixLength;
89
+ }
90
+ if (suffixLength > 0) {
91
+ return newValue.length - suffixLength;
92
+ }
93
+ if (oldPosition === 0 && prefixLength === 0 && suffixLength === 0) {
94
+ return newValue.length;
95
+ }
96
+ if (oldValue.length > 0) {
97
+ const ratio = oldPosition / oldValue.length;
98
+ return Math.round(ratio * newValue.length);
99
+ }
100
+ return newValue.length;
101
+ }
22
102
  var getRootId = (ctx) => ctx.ids?.root ?? `number-input:${ctx.id}`;
23
103
  var getInputId = (ctx) => ctx.ids?.input ?? `number-input:${ctx.id}:input`;
24
104
  var getIncrementTriggerId = (ctx) => ctx.ids?.incrementTrigger ?? `number-input:${ctx.id}:inc`;
@@ -106,83 +186,6 @@ var createVirtualCursor = (ctx, point) => {
106
186
  doc.body.appendChild(el);
107
187
  };
108
188
 
109
- // src/cursor.ts
110
- function recordCursor(inputEl, scope) {
111
- if (!inputEl || !scope.isActiveElement(inputEl)) return;
112
- try {
113
- const { selectionStart: start, selectionEnd: end, value } = inputEl;
114
- if (start == null || end == null) return void 0;
115
- return { start, end, value };
116
- } catch {
117
- return void 0;
118
- }
119
- }
120
- function restoreCursor(inputEl, selection, scope) {
121
- if (!inputEl || !scope.isActiveElement(inputEl)) return;
122
- if (!selection) {
123
- const len = inputEl.value.length;
124
- inputEl.setSelectionRange(len, len);
125
- return;
126
- }
127
- try {
128
- const newValue = inputEl.value;
129
- const { start, end, value: oldValue } = selection;
130
- if (newValue === oldValue) {
131
- inputEl.setSelectionRange(start, end);
132
- return;
133
- }
134
- const newStart = getNewCursorPosition(oldValue, newValue, start);
135
- const newEnd = start === end ? newStart : getNewCursorPosition(oldValue, newValue, end);
136
- const clampedStart = Math.max(0, Math.min(newStart, newValue.length));
137
- const clampedEnd = Math.max(clampedStart, Math.min(newEnd, newValue.length));
138
- inputEl.setSelectionRange(clampedStart, clampedEnd);
139
- } catch {
140
- const len = inputEl.value.length;
141
- inputEl.setSelectionRange(len, len);
142
- }
143
- }
144
- function getNewCursorPosition(oldValue, newValue, oldPosition) {
145
- const beforeCursor = oldValue.slice(0, oldPosition);
146
- const afterCursor = oldValue.slice(oldPosition);
147
- let prefixLength = 0;
148
- const maxPrefixLength = Math.min(beforeCursor.length, newValue.length);
149
- for (let i = 0; i < maxPrefixLength; i++) {
150
- if (beforeCursor[i] === newValue[i]) {
151
- prefixLength = i + 1;
152
- } else {
153
- break;
154
- }
155
- }
156
- let suffixLength = 0;
157
- const maxSuffixLength = Math.min(afterCursor.length, newValue.length - prefixLength);
158
- for (let i = 0; i < maxSuffixLength; i++) {
159
- const oldIndex = afterCursor.length - 1 - i;
160
- const newIndex = newValue.length - 1 - i;
161
- if (afterCursor[oldIndex] === newValue[newIndex]) {
162
- suffixLength = i + 1;
163
- } else {
164
- break;
165
- }
166
- }
167
- if (prefixLength >= beforeCursor.length) {
168
- return prefixLength;
169
- }
170
- if (suffixLength >= afterCursor.length) {
171
- return newValue.length - suffixLength;
172
- }
173
- if (prefixLength > 0) {
174
- return prefixLength;
175
- }
176
- if (suffixLength > 0) {
177
- return newValue.length - suffixLength;
178
- }
179
- if (oldValue.length > 0) {
180
- const ratio = oldPosition / oldValue.length;
181
- return Math.round(ratio * newValue.length);
182
- }
183
- return newValue.length;
184
- }
185
-
186
189
  // src/number-input.connect.ts
187
190
  function connect(service, normalize) {
188
191
  const { state, send, prop, scope, computed } = service;
@@ -244,7 +247,12 @@ function connect(service, normalize) {
244
247
  "data-required": domQuery.dataAttr(required),
245
248
  "data-scrubbing": domQuery.dataAttr(scrubbing),
246
249
  id: getLabelId(scope),
247
- htmlFor: getInputId(scope)
250
+ htmlFor: getInputId(scope),
251
+ onClick() {
252
+ domQuery.raf(() => {
253
+ domQuery.setCaretToEnd(getInputEl(scope));
254
+ });
255
+ }
248
256
  });
249
257
  },
250
258
  getControlProps() {
@@ -432,6 +440,9 @@ function connect(service, normalize) {
432
440
  point.y = point.y - utils.roundToDpr(7.5, dpr);
433
441
  send({ type: "SCRUBBER.PRESS_DOWN", point });
434
442
  event.preventDefault();
443
+ domQuery.raf(() => {
444
+ domQuery.setCaretToEnd(getInputEl(scope));
445
+ });
435
446
  },
436
447
  style: {
437
448
  cursor: disabled ? void 0 : "ew-resize"
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createAnatomy } from '@zag-js/anatomy';
2
- import { raf, setElementValue, addDomEvent, isSafari, requestPointerLock, observeAttributes, trackFormControl, MAX_Z_INDEX, dataAttr, isLeftClick, getEventPoint, getWindow, ariaAttr, isComposingEvent, getEventStep, isModifierKey } from '@zag-js/dom-query';
2
+ import { raf, setElementValue, addDomEvent, isSafari, requestPointerLock, observeAttributes, trackFormControl, MAX_Z_INDEX, dataAttr, isLeftClick, getEventPoint, getWindow, setCaretToEnd, ariaAttr, isComposingEvent, getEventStep, isModifierKey } from '@zag-js/dom-query';
3
3
  import { isValueAtMax, isValueAtMin, clampValue, decrementValue, incrementValue, callAll, isValueWithinRange, createSplitProps, roundToDpr, wrap } from '@zag-js/utils';
4
4
  import { setup, memo } from '@zag-js/core';
5
5
  import { NumberParser } from '@internationalized/number';
@@ -17,6 +17,86 @@ var anatomy = createAnatomy("numberInput").parts(
17
17
  "scrubber"
18
18
  );
19
19
  var parts = anatomy.build();
20
+
21
+ // src/cursor.ts
22
+ function recordCursor(inputEl, scope) {
23
+ if (!inputEl || !scope.isActiveElement(inputEl)) return;
24
+ try {
25
+ const { selectionStart: start, selectionEnd: end, value } = inputEl;
26
+ if (start == null || end == null) return void 0;
27
+ return { start, end, value };
28
+ } catch {
29
+ return void 0;
30
+ }
31
+ }
32
+ function restoreCursor(inputEl, selection, scope) {
33
+ if (!inputEl || !scope.isActiveElement(inputEl)) return;
34
+ if (!selection) {
35
+ const len = inputEl.value.length;
36
+ inputEl.setSelectionRange(len, len);
37
+ return;
38
+ }
39
+ try {
40
+ const newValue = inputEl.value;
41
+ const { start, end, value: oldValue } = selection;
42
+ if (newValue === oldValue) {
43
+ inputEl.setSelectionRange(start, end);
44
+ return;
45
+ }
46
+ const newStart = getNextCursorPosition(oldValue, newValue, start);
47
+ const newEnd = start === end ? newStart : getNextCursorPosition(oldValue, newValue, end);
48
+ const clampedStart = Math.max(0, Math.min(newStart, newValue.length));
49
+ const clampedEnd = Math.max(clampedStart, Math.min(newEnd, newValue.length));
50
+ inputEl.setSelectionRange(clampedStart, clampedEnd);
51
+ } catch {
52
+ const len = inputEl.value.length;
53
+ inputEl.setSelectionRange(len, len);
54
+ }
55
+ }
56
+ function getNextCursorPosition(oldValue, newValue, oldPosition) {
57
+ const beforeCursor = oldValue.slice(0, oldPosition);
58
+ const afterCursor = oldValue.slice(oldPosition);
59
+ let prefixLength = 0;
60
+ const maxPrefixLength = Math.min(beforeCursor.length, newValue.length);
61
+ for (let i = 0; i < maxPrefixLength; i++) {
62
+ if (beforeCursor[i] === newValue[i]) {
63
+ prefixLength = i + 1;
64
+ } else {
65
+ break;
66
+ }
67
+ }
68
+ let suffixLength = 0;
69
+ const maxSuffixLength = Math.min(afterCursor.length, newValue.length - prefixLength);
70
+ for (let i = 0; i < maxSuffixLength; i++) {
71
+ const oldIndex = afterCursor.length - 1 - i;
72
+ const newIndex = newValue.length - 1 - i;
73
+ if (afterCursor[oldIndex] === newValue[newIndex]) {
74
+ suffixLength = i + 1;
75
+ } else {
76
+ break;
77
+ }
78
+ }
79
+ if (beforeCursor.length > 0 && prefixLength >= beforeCursor.length) {
80
+ return prefixLength;
81
+ }
82
+ if (suffixLength >= afterCursor.length) {
83
+ return newValue.length - suffixLength;
84
+ }
85
+ if (prefixLength > 0) {
86
+ return prefixLength;
87
+ }
88
+ if (suffixLength > 0) {
89
+ return newValue.length - suffixLength;
90
+ }
91
+ if (oldPosition === 0 && prefixLength === 0 && suffixLength === 0) {
92
+ return newValue.length;
93
+ }
94
+ if (oldValue.length > 0) {
95
+ const ratio = oldPosition / oldValue.length;
96
+ return Math.round(ratio * newValue.length);
97
+ }
98
+ return newValue.length;
99
+ }
20
100
  var getRootId = (ctx) => ctx.ids?.root ?? `number-input:${ctx.id}`;
21
101
  var getInputId = (ctx) => ctx.ids?.input ?? `number-input:${ctx.id}:input`;
22
102
  var getIncrementTriggerId = (ctx) => ctx.ids?.incrementTrigger ?? `number-input:${ctx.id}:inc`;
@@ -104,83 +184,6 @@ var createVirtualCursor = (ctx, point) => {
104
184
  doc.body.appendChild(el);
105
185
  };
106
186
 
107
- // src/cursor.ts
108
- function recordCursor(inputEl, scope) {
109
- if (!inputEl || !scope.isActiveElement(inputEl)) return;
110
- try {
111
- const { selectionStart: start, selectionEnd: end, value } = inputEl;
112
- if (start == null || end == null) return void 0;
113
- return { start, end, value };
114
- } catch {
115
- return void 0;
116
- }
117
- }
118
- function restoreCursor(inputEl, selection, scope) {
119
- if (!inputEl || !scope.isActiveElement(inputEl)) return;
120
- if (!selection) {
121
- const len = inputEl.value.length;
122
- inputEl.setSelectionRange(len, len);
123
- return;
124
- }
125
- try {
126
- const newValue = inputEl.value;
127
- const { start, end, value: oldValue } = selection;
128
- if (newValue === oldValue) {
129
- inputEl.setSelectionRange(start, end);
130
- return;
131
- }
132
- const newStart = getNewCursorPosition(oldValue, newValue, start);
133
- const newEnd = start === end ? newStart : getNewCursorPosition(oldValue, newValue, end);
134
- const clampedStart = Math.max(0, Math.min(newStart, newValue.length));
135
- const clampedEnd = Math.max(clampedStart, Math.min(newEnd, newValue.length));
136
- inputEl.setSelectionRange(clampedStart, clampedEnd);
137
- } catch {
138
- const len = inputEl.value.length;
139
- inputEl.setSelectionRange(len, len);
140
- }
141
- }
142
- function getNewCursorPosition(oldValue, newValue, oldPosition) {
143
- const beforeCursor = oldValue.slice(0, oldPosition);
144
- const afterCursor = oldValue.slice(oldPosition);
145
- let prefixLength = 0;
146
- const maxPrefixLength = Math.min(beforeCursor.length, newValue.length);
147
- for (let i = 0; i < maxPrefixLength; i++) {
148
- if (beforeCursor[i] === newValue[i]) {
149
- prefixLength = i + 1;
150
- } else {
151
- break;
152
- }
153
- }
154
- let suffixLength = 0;
155
- const maxSuffixLength = Math.min(afterCursor.length, newValue.length - prefixLength);
156
- for (let i = 0; i < maxSuffixLength; i++) {
157
- const oldIndex = afterCursor.length - 1 - i;
158
- const newIndex = newValue.length - 1 - i;
159
- if (afterCursor[oldIndex] === newValue[newIndex]) {
160
- suffixLength = i + 1;
161
- } else {
162
- break;
163
- }
164
- }
165
- if (prefixLength >= beforeCursor.length) {
166
- return prefixLength;
167
- }
168
- if (suffixLength >= afterCursor.length) {
169
- return newValue.length - suffixLength;
170
- }
171
- if (prefixLength > 0) {
172
- return prefixLength;
173
- }
174
- if (suffixLength > 0) {
175
- return newValue.length - suffixLength;
176
- }
177
- if (oldValue.length > 0) {
178
- const ratio = oldPosition / oldValue.length;
179
- return Math.round(ratio * newValue.length);
180
- }
181
- return newValue.length;
182
- }
183
-
184
187
  // src/number-input.connect.ts
185
188
  function connect(service, normalize) {
186
189
  const { state, send, prop, scope, computed } = service;
@@ -242,7 +245,12 @@ function connect(service, normalize) {
242
245
  "data-required": dataAttr(required),
243
246
  "data-scrubbing": dataAttr(scrubbing),
244
247
  id: getLabelId(scope),
245
- htmlFor: getInputId(scope)
248
+ htmlFor: getInputId(scope),
249
+ onClick() {
250
+ raf(() => {
251
+ setCaretToEnd(getInputEl(scope));
252
+ });
253
+ }
246
254
  });
247
255
  },
248
256
  getControlProps() {
@@ -430,6 +438,9 @@ function connect(service, normalize) {
430
438
  point.y = point.y - roundToDpr(7.5, dpr);
431
439
  send({ type: "SCRUBBER.PRESS_DOWN", point });
432
440
  event.preventDefault();
441
+ raf(() => {
442
+ setCaretToEnd(getInputEl(scope));
443
+ });
433
444
  },
434
445
  style: {
435
446
  cursor: disabled ? void 0 : "ew-resize"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zag-js/number-input",
3
- "version": "1.31.1",
3
+ "version": "1.33.0",
4
4
  "description": "Core logic for the number-input widget implemented as a state machine",
5
5
  "keywords": [
6
6
  "js",
@@ -27,11 +27,11 @@
27
27
  },
28
28
  "dependencies": {
29
29
  "@internationalized/number": "3.6.5",
30
- "@zag-js/anatomy": "1.31.1",
31
- "@zag-js/core": "1.31.1",
32
- "@zag-js/dom-query": "1.31.1",
33
- "@zag-js/types": "1.31.1",
34
- "@zag-js/utils": "1.31.1"
30
+ "@zag-js/anatomy": "1.33.0",
31
+ "@zag-js/core": "1.33.0",
32
+ "@zag-js/dom-query": "1.33.0",
33
+ "@zag-js/types": "1.33.0",
34
+ "@zag-js/utils": "1.33.0"
35
35
  },
36
36
  "devDependencies": {
37
37
  "clean-package": "2.2.0"