react-stately 3.47.0 → 3.49.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.
Files changed (140) hide show
  1. package/dist/exports/index.cjs +6 -0
  2. package/dist/exports/index.cjs.map +1 -1
  3. package/dist/exports/index.js +5 -1
  4. package/dist/exports/index.js.map +1 -1
  5. package/dist/exports/index.mjs +5 -1
  6. package/dist/exports/index.mjs.map +1 -1
  7. package/dist/exports/useTokenFieldState.cjs +25 -0
  8. package/dist/exports/useTokenFieldState.cjs.map +1 -0
  9. package/dist/exports/useTokenFieldState.js +19 -0
  10. package/dist/exports/useTokenFieldState.js.map +1 -0
  11. package/dist/exports/useTokenFieldState.mjs +19 -0
  12. package/dist/exports/useTokenFieldState.mjs.map +1 -0
  13. package/dist/private/color/Color.cjs.map +1 -1
  14. package/dist/private/color/Color.js.map +1 -1
  15. package/dist/private/color/Color.mjs.map +1 -1
  16. package/dist/private/combobox/useComboBoxState.cjs +2 -1
  17. package/dist/private/combobox/useComboBoxState.cjs.map +1 -1
  18. package/dist/private/combobox/useComboBoxState.js +2 -1
  19. package/dist/private/combobox/useComboBoxState.js.map +1 -1
  20. package/dist/private/combobox/useComboBoxState.mjs +2 -1
  21. package/dist/private/combobox/useComboBoxState.mjs.map +1 -1
  22. package/dist/private/data/useTreeData.cjs +1 -0
  23. package/dist/private/data/useTreeData.cjs.map +1 -1
  24. package/dist/private/data/useTreeData.js +1 -0
  25. package/dist/private/data/useTreeData.js.map +1 -1
  26. package/dist/private/data/useTreeData.mjs +1 -0
  27. package/dist/private/data/useTreeData.mjs.map +1 -1
  28. package/dist/private/datepicker/useDateRangePickerState.cjs +1 -0
  29. package/dist/private/datepicker/useDateRangePickerState.cjs.map +1 -1
  30. package/dist/private/datepicker/useDateRangePickerState.js +1 -0
  31. package/dist/private/datepicker/useDateRangePickerState.js.map +1 -1
  32. package/dist/private/datepicker/useDateRangePickerState.mjs +1 -0
  33. package/dist/private/datepicker/useDateRangePickerState.mjs.map +1 -1
  34. package/dist/private/datepicker/utils.cjs.map +1 -1
  35. package/dist/private/datepicker/utils.js.map +1 -1
  36. package/dist/private/datepicker/utils.mjs.map +1 -1
  37. package/dist/private/form/useFormValidationState.cjs +1 -1
  38. package/dist/private/form/useFormValidationState.cjs.map +1 -1
  39. package/dist/private/form/useFormValidationState.js +1 -1
  40. package/dist/private/form/useFormValidationState.js.map +1 -1
  41. package/dist/private/form/useFormValidationState.mjs +1 -1
  42. package/dist/private/form/useFormValidationState.mjs.map +1 -1
  43. package/dist/private/grid/useGridState.cjs +12 -10
  44. package/dist/private/grid/useGridState.cjs.map +1 -1
  45. package/dist/private/grid/useGridState.js +12 -10
  46. package/dist/private/grid/useGridState.js.map +1 -1
  47. package/dist/private/grid/useGridState.mjs +12 -10
  48. package/dist/private/grid/useGridState.mjs.map +1 -1
  49. package/dist/private/layout/ListLayout.cjs +0 -3
  50. package/dist/private/layout/ListLayout.cjs.map +1 -1
  51. package/dist/private/layout/ListLayout.js +0 -3
  52. package/dist/private/layout/ListLayout.js.map +1 -1
  53. package/dist/private/layout/ListLayout.mjs +0 -3
  54. package/dist/private/layout/ListLayout.mjs.map +1 -1
  55. package/dist/private/menu/useMenuTriggerState.cjs.map +1 -1
  56. package/dist/private/menu/useMenuTriggerState.js.map +1 -1
  57. package/dist/private/menu/useMenuTriggerState.mjs.map +1 -1
  58. package/dist/private/menu/useSubmenuTriggerState.cjs +3 -1
  59. package/dist/private/menu/useSubmenuTriggerState.cjs.map +1 -1
  60. package/dist/private/menu/useSubmenuTriggerState.js +3 -1
  61. package/dist/private/menu/useSubmenuTriggerState.js.map +1 -1
  62. package/dist/private/menu/useSubmenuTriggerState.mjs +3 -1
  63. package/dist/private/menu/useSubmenuTriggerState.mjs.map +1 -1
  64. package/dist/private/overlays/useOverlayTriggerState.cjs +4 -1
  65. package/dist/private/overlays/useOverlayTriggerState.cjs.map +1 -1
  66. package/dist/private/overlays/useOverlayTriggerState.js +5 -2
  67. package/dist/private/overlays/useOverlayTriggerState.js.map +1 -1
  68. package/dist/private/overlays/useOverlayTriggerState.mjs +5 -2
  69. package/dist/private/overlays/useOverlayTriggerState.mjs.map +1 -1
  70. package/dist/private/select/useSelectState.cjs +1 -0
  71. package/dist/private/select/useSelectState.cjs.map +1 -1
  72. package/dist/private/select/useSelectState.js +1 -0
  73. package/dist/private/select/useSelectState.js.map +1 -1
  74. package/dist/private/select/useSelectState.mjs +1 -0
  75. package/dist/private/select/useSelectState.mjs.map +1 -1
  76. package/dist/private/slider/useSliderState.cjs.map +1 -1
  77. package/dist/private/slider/useSliderState.js.map +1 -1
  78. package/dist/private/slider/useSliderState.mjs.map +1 -1
  79. package/dist/private/table/TableUtils.cjs +13 -1
  80. package/dist/private/table/TableUtils.cjs.map +1 -1
  81. package/dist/private/table/TableUtils.js +13 -1
  82. package/dist/private/table/TableUtils.js.map +1 -1
  83. package/dist/private/table/TableUtils.mjs +13 -1
  84. package/dist/private/table/TableUtils.mjs.map +1 -1
  85. package/dist/private/table/useTableState.cjs +1 -0
  86. package/dist/private/table/useTableState.cjs.map +1 -1
  87. package/dist/private/table/useTableState.js +1 -0
  88. package/dist/private/table/useTableState.js.map +1 -1
  89. package/dist/private/table/useTableState.mjs +1 -0
  90. package/dist/private/table/useTableState.mjs.map +1 -1
  91. package/dist/private/tokenfield/TokenFieldValue.cjs +287 -0
  92. package/dist/private/tokenfield/TokenFieldValue.cjs.map +1 -0
  93. package/dist/private/tokenfield/TokenFieldValue.js +285 -0
  94. package/dist/private/tokenfield/TokenFieldValue.js.map +1 -0
  95. package/dist/private/tokenfield/TokenFieldValue.mjs +282 -0
  96. package/dist/private/tokenfield/TokenFieldValue.mjs.map +1 -0
  97. package/dist/private/tokenfield/useTokenFieldState.cjs +37 -0
  98. package/dist/private/tokenfield/useTokenFieldState.cjs.map +1 -0
  99. package/dist/private/tokenfield/useTokenFieldState.js +32 -0
  100. package/dist/private/tokenfield/useTokenFieldState.js.map +1 -0
  101. package/dist/private/tokenfield/useTokenFieldState.mjs +32 -0
  102. package/dist/private/tokenfield/useTokenFieldState.mjs.map +1 -0
  103. package/dist/private/tooltip/useTooltipTriggerState.cjs +18 -6
  104. package/dist/private/tooltip/useTooltipTriggerState.cjs.map +1 -1
  105. package/dist/private/tooltip/useTooltipTriggerState.js +19 -7
  106. package/dist/private/tooltip/useTooltipTriggerState.js.map +1 -1
  107. package/dist/private/tooltip/useTooltipTriggerState.mjs +19 -7
  108. package/dist/private/tooltip/useTooltipTriggerState.mjs.map +1 -1
  109. package/dist/private/virtualizer/ScrollAnchor.cjs +136 -0
  110. package/dist/private/virtualizer/ScrollAnchor.cjs.map +1 -0
  111. package/dist/private/virtualizer/ScrollAnchor.js +132 -0
  112. package/dist/private/virtualizer/ScrollAnchor.js.map +1 -0
  113. package/dist/private/virtualizer/ScrollAnchor.mjs +131 -0
  114. package/dist/private/virtualizer/ScrollAnchor.mjs.map +1 -0
  115. package/dist/private/virtualizer/Virtualizer.cjs +50 -1
  116. package/dist/private/virtualizer/Virtualizer.cjs.map +1 -1
  117. package/dist/private/virtualizer/Virtualizer.js +54 -1
  118. package/dist/private/virtualizer/Virtualizer.js.map +1 -1
  119. package/dist/private/virtualizer/Virtualizer.mjs +50 -1
  120. package/dist/private/virtualizer/Virtualizer.mjs.map +1 -1
  121. package/dist/private/virtualizer/useVirtualizerState.cjs +2 -1
  122. package/dist/private/virtualizer/useVirtualizerState.cjs.map +1 -1
  123. package/dist/private/virtualizer/useVirtualizerState.js +2 -1
  124. package/dist/private/virtualizer/useVirtualizerState.js.map +1 -1
  125. package/dist/private/virtualizer/useVirtualizerState.mjs +2 -1
  126. package/dist/private/virtualizer/useVirtualizerState.mjs.map +1 -1
  127. package/dist/types/exports/index.d.ts +4 -0
  128. package/dist/types/exports/useTokenFieldState.d.ts +4 -0
  129. package/dist/types/src/combobox/useComboBoxState.d.ts +1 -1
  130. package/dist/types/src/menu/useMenuTriggerState.d.ts +1 -1
  131. package/dist/types/src/overlays/useOverlayTriggerState.d.ts +9 -0
  132. package/dist/types/src/table/useTableState.d.ts +7 -0
  133. package/dist/types/src/tokenfield/TokenFieldValue.d.ts +71 -0
  134. package/dist/types/src/tokenfield/useTokenFieldState.d.ts +19 -0
  135. package/dist/types/src/tooltip/useTooltipTriggerState.d.ts +5 -0
  136. package/dist/types/src/virtualizer/ScrollAnchor.d.ts +81 -0
  137. package/dist/types/src/virtualizer/Virtualizer.d.ts +1 -0
  138. package/dist/types/src/virtualizer/types.d.ts +2 -0
  139. package/package.json +5 -5
  140. package/useTokenFieldState/package.json +5 -0
@@ -0,0 +1,287 @@
1
+
2
+ function $parcel$export(e, n, v, s) {
3
+ Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true});
4
+ }
5
+
6
+ $parcel$export(module.exports, "TokenFieldValue", function () { return $8b1d61ca9a986fd4$export$f3decd0c2cd2fe2e; });
7
+ /*
8
+ * Copyright 2026 Adobe. All rights reserved.
9
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
10
+ * you may not use this file except in compliance with the License. You may obtain a copy
11
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing, software distributed under
14
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
15
+ * OF ANY KIND, either express or implied. See the License for the specific language
16
+ * governing permissions and limitations under the License.
17
+ */ var $8b1d61ca9a986fd4$var$Direction = /*#__PURE__*/ function(Direction) {
18
+ Direction[Direction["Forward"] = 1] = "Forward";
19
+ Direction[Direction["Backward"] = -1] = "Backward";
20
+ return Direction;
21
+ }($8b1d61ca9a986fd4$var$Direction || {});
22
+ class $8b1d61ca9a986fd4$export$f3decd0c2cd2fe2e {
23
+ static{
24
+ this.Direction = $8b1d61ca9a986fd4$var$Direction;
25
+ }
26
+ /** Create a new list with the given segments. */ constructor(tokens, options){
27
+ /** The caret position. */ this.caretPosition = {
28
+ index: 0,
29
+ offset: 0
30
+ };
31
+ // Linked list representing the undo/redo history.
32
+ this.previous = null;
33
+ this.next = null;
34
+ this.isCoalescing = true;
35
+ this.segments = tokens;
36
+ this.caretPosition = options?.caretPosition ?? {
37
+ index: 0,
38
+ offset: 0
39
+ };
40
+ }
41
+ createFieldValue(segments) {
42
+ const Constructor = this.constructor;
43
+ return new Constructor(segments);
44
+ }
45
+ /** Create a new list with the caret position set to the given position. */ withCaretPosition(caretPosition) {
46
+ if (this.caretPosition.index === caretPosition.index && this.caretPosition.offset === caretPosition.offset) return this;
47
+ let result = this.createFieldValue(this.segments);
48
+ result.caretPosition = caretPosition;
49
+ result.previous = this.previous;
50
+ result.next = this.next;
51
+ result.isCoalescing = this.isCoalescing;
52
+ return result;
53
+ }
54
+ splitSegment(segment, offset) {
55
+ if (!segment) {
56
+ let empty = this.createTextSegment('');
57
+ return [
58
+ offset > 0 ? empty : null,
59
+ offset > 0 ? null : empty
60
+ ];
61
+ }
62
+ if (segment.type === 'token') return [
63
+ offset > 0 ? {
64
+ ...segment
65
+ } : null,
66
+ offset > 0 ? null : {
67
+ ...segment
68
+ }
69
+ ];
70
+ return [
71
+ offset > 0 ? {
72
+ type: 'text',
73
+ text: segment.text.slice(0, offset)
74
+ } : null,
75
+ offset < segment.text.length ? this.createTextSegment(segment.text.slice(offset)) : null
76
+ ];
77
+ }
78
+ createTextSegment(text) {
79
+ return {
80
+ type: 'text',
81
+ text: text
82
+ };
83
+ }
84
+ tokenize(text) {
85
+ return [
86
+ this.createTextSegment(text)
87
+ ];
88
+ }
89
+ clampPosition(position) {
90
+ if (this.segments.length > 0 && position.index >= this.segments.length) return {
91
+ index: this.segments.length - 1,
92
+ offset: this.segments[this.segments.length - 1].text.length
93
+ };
94
+ if (position.index < 0) return {
95
+ index: 0,
96
+ offset: 0
97
+ };
98
+ return position;
99
+ }
100
+ /** Replace the text between two positions with new text. */ replaceRange(start, end, text, coalesce = true) {
101
+ return this.replaceRangeWithSegments(start, end, text.length > 0 ? [
102
+ this.createTextSegment(text)
103
+ ] : [], coalesce);
104
+ }
105
+ /** Replace the text between two positions with new segments. */ replaceRangeWithSegments(start, end, insert, coalesce = true) {
106
+ start = this.clampPosition(start);
107
+ end = this.clampPosition(end);
108
+ let startSegment = this.segments[start.index];
109
+ let endSegment = this.segments[end.index];
110
+ let [startSplit] = this.splitSegment(startSegment, start.offset);
111
+ let [, endSplit] = this.splitSegment(endSegment, end.offset);
112
+ let newSegments = this.segments.slice(0, start.index);
113
+ if (startSplit) $8b1d61ca9a986fd4$var$appendSegments(newSegments, [
114
+ startSplit
115
+ ]);
116
+ if (insert.length) $8b1d61ca9a986fd4$var$appendSegments(newSegments, insert, (text)=>this.tokenize(text));
117
+ let lastSegment = newSegments[newSegments.length - 1];
118
+ let lastIsText = lastSegment && lastSegment.type === 'text';
119
+ let caret = {
120
+ index: lastIsText ? newSegments.length - 1 : newSegments.length,
121
+ offset: lastIsText ? lastSegment.text.length : 0
122
+ };
123
+ if (endSplit) $8b1d61ca9a986fd4$var$appendSegments(newSegments, [
124
+ endSplit
125
+ ]);
126
+ $8b1d61ca9a986fd4$var$appendSegments(newSegments, this.segments.slice(end.index + 1));
127
+ let segments = this.createFieldValue(newSegments);
128
+ segments.caretPosition = caret;
129
+ segments.isCoalescing = coalesce;
130
+ if (this.isCoalescing && coalesce && this.previous) {
131
+ segments.previous = this.previous;
132
+ segments.previous.next = segments;
133
+ } else {
134
+ segments.previous = this;
135
+ this.caretPosition = end;
136
+ this.next = segments;
137
+ }
138
+ return segments;
139
+ }
140
+ /** Find the boundary before or after a position using an Intl.Segmenter. */ findBoundaryWithSegmenter(position, segmenter, direction) {
141
+ position = this.clampPosition(position);
142
+ for(let i = position.index; i >= 0 && i < this.segments.length; i += direction){
143
+ let segment = this.segments[i];
144
+ switch(segment.type){
145
+ case 'token':
146
+ if (i !== position.index || (direction === -1 ? position.offset > 0 : position.offset === 0)) {
147
+ let index = i + direction;
148
+ return {
149
+ index: index >= 0 ? index : 0,
150
+ offset: direction === -1 && index >= 0 ? this.segments[index].text.length : 0
151
+ };
152
+ }
153
+ continue;
154
+ case 'text':
155
+ {
156
+ let offset = direction === -1 ? segment.text.length : 0;
157
+ if (i === position.index) offset = position.offset;
158
+ if (direction === -1) offset--;
159
+ if (offset < 0 || offset >= segment.text.length) continue;
160
+ let part = segmenter.segment(segment.text).containing(offset);
161
+ while(part && part.isWordLike === false){
162
+ offset += direction;
163
+ part = segmenter.segment(segment.text).containing(offset);
164
+ }
165
+ if (part) return {
166
+ index: i,
167
+ offset: direction === -1 ? part.index : part.index + part.segment.length
168
+ };
169
+ continue;
170
+ }
171
+ }
172
+ }
173
+ return null;
174
+ }
175
+ /** Find a line boundary before or after a position. */ findLineBoundary(position, direction) {
176
+ let res = this.findText(position, direction, '\n');
177
+ if (res) return res;
178
+ return direction === -1 ? {
179
+ index: 0,
180
+ offset: 0
181
+ } : {
182
+ index: this.segments.length - 1,
183
+ offset: this.segments[this.segments.length - 1].text.length
184
+ };
185
+ }
186
+ /** Find a string or regular expression match before or after a position. */ findText(position, direction, search) {
187
+ if (this.segments.length === 0) return null;
188
+ for(let i = position.index; i >= 0 && i < this.segments.length; i += direction){
189
+ let segment = this.segments[i];
190
+ if (segment.type !== 'text') continue;
191
+ let offset = $8b1d61ca9a986fd4$var$findInText(segment.text, search, direction, i === position.index ? position.offset : undefined);
192
+ if (offset >= 0) return {
193
+ index: i,
194
+ offset: offset
195
+ };
196
+ }
197
+ return null;
198
+ }
199
+ /** Delete text at a position using a segmenter. */ delete(position, segmenter, direction, coalesce = true) {
200
+ let boundary = this.findBoundaryWithSegmenter(position, segmenter, direction);
201
+ if (boundary) return this.replaceRange(direction === -1 ? boundary : position, direction === -1 ? position : boundary, '', coalesce);
202
+ this.caretPosition = position;
203
+ return this;
204
+ }
205
+ /** Delete text to the next or previous line break. */ deleteLine(position, direction, coalesce = true) {
206
+ if (this.segments.length === 0) return this;
207
+ let boundary = this.findLineBoundary(position, direction);
208
+ if (boundary) return this.replaceRange(direction === -1 ? boundary : position, direction === -1 ? position : boundary, '', coalesce);
209
+ return this;
210
+ }
211
+ /** Create a new list containing a subset of the segments. */ slice(start, end) {
212
+ start = this.clampPosition(start);
213
+ end = this.clampPosition(end);
214
+ if (start.index === end.index && start.offset === end.offset) return this.createFieldValue([]);
215
+ if (start.index === end.index) {
216
+ let segment = this.segments[start.index];
217
+ if (segment.type === 'text') return this.createFieldValue([
218
+ {
219
+ type: 'text',
220
+ text: segment.text.slice(start.offset, end.offset)
221
+ }
222
+ ]);
223
+ return this.createFieldValue([
224
+ segment
225
+ ]);
226
+ }
227
+ let startSegment = this.segments[start.index];
228
+ let endSegment = this.segments[end.index];
229
+ let [, startSplit] = this.splitSegment(startSegment, start.offset);
230
+ let [endSplit] = this.splitSegment(endSegment, end.offset);
231
+ let result = [];
232
+ if (startSplit) result.push(startSplit);
233
+ result.push(...this.segments.slice(start.index + 1, end.index));
234
+ if (endSplit) result.push(endSplit);
235
+ return this.createFieldValue(result);
236
+ }
237
+ /** Convert the list to a string. */ toString() {
238
+ return this.segments.map((seg)=>seg.text).join('');
239
+ }
240
+ /** Returns the previous list in the undo history. */ undo() {
241
+ return this.previous ?? this;
242
+ }
243
+ /** Returns the next list in the redo history. */ redo() {
244
+ return this.next ?? this;
245
+ }
246
+ /** End coalescing undo/redo history. */ endCoalescing() {
247
+ this.isCoalescing = false;
248
+ }
249
+ }
250
+ function $8b1d61ca9a986fd4$var$findInText(text, search, direction, fromOffset) {
251
+ if (typeof search === 'string') {
252
+ if (direction === -1) return text.lastIndexOf(search, fromOffset !== undefined ? fromOffset - 1 : text.length - 1);
253
+ return text.indexOf(search, fromOffset ?? 0);
254
+ }
255
+ if (direction === 1) {
256
+ let start = fromOffset ?? 0;
257
+ let index = text.slice(start).search(search);
258
+ return index >= 0 ? start + index : -1;
259
+ }
260
+ let limit = fromOffset !== undefined ? fromOffset : text.length;
261
+ if (limit < 0) return -1;
262
+ let re = search.flags.includes('g') ? search : new RegExp(search.source, search.flags + 'g');
263
+ let matches = Array.from(text.slice(0, limit).matchAll(re));
264
+ return matches.at(-1)?.index ?? -1;
265
+ }
266
+ function $8b1d61ca9a986fd4$var$appendSegments(segments, insert, tokenize) {
267
+ for (let segment of insert){
268
+ if (segment.type === 'text' && segment.text.length === 0) continue;
269
+ let last = segments[segments.length - 1];
270
+ if (last && last.type === 'text' && segment.type === 'text') {
271
+ if (tokenize) {
272
+ let tokenized = tokenize(last.text + segment.text);
273
+ segments.splice(segments.length - 1, 1, ...tokenized);
274
+ } else segments[segments.length - 1] = {
275
+ type: 'text',
276
+ text: last.text + segment.text
277
+ };
278
+ } else if (tokenize && segment.type === 'text') {
279
+ let tokenized = tokenize(segment.text);
280
+ segments.push(...tokenized);
281
+ } else segments.push(segment);
282
+ }
283
+ return segments;
284
+ }
285
+
286
+
287
+ //# sourceMappingURL=TokenFieldValue.cjs.map
@@ -0,0 +1 @@
1
+ {"mappings":"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","sources":["packages/react-stately/src/tokenfield/TokenFieldValue.ts"],"sourcesContent":["/*\n * Copyright 2026 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nexport type TokenFieldSegment<T = any> = TextSegment | TokenSegment<T>;\n\nexport interface TextSegment {\n type: 'text';\n text: string;\n}\n\nexport interface TokenSegment<T = any> {\n type: 'token';\n text: string;\n /** An arbitrary value associated with the token. */\n value?: T;\n}\n\nexport interface Position {\n /** Index of the segment in the list. */\n index: number;\n /** Text offset within the segment in UTF-16 code units. */\n offset: number;\n}\n\nenum Direction {\n Forward = 1,\n Backward = -1\n}\n\nexport interface TokenFieldValueOptions {\n caretPosition?: Position | null;\n}\n\n/**\n * A list of segments containing editable text and non-editable tokens.\n */\nexport class TokenFieldValue<T = any> {\n static readonly Direction = Direction;\n\n /** The text and token segments in the list. */\n readonly segments: readonly TokenFieldSegment<T>[];\n /** The caret position. */\n caretPosition: Position = {index: 0, offset: 0};\n // Linked list representing the undo/redo history.\n private previous: this | null = null;\n private next: this | null = null;\n private isCoalescing = true;\n\n /** Create a new list with the given segments. */\n constructor(tokens: readonly TokenFieldSegment<T>[], options?: TokenFieldValueOptions) {\n this.segments = tokens;\n this.caretPosition = options?.caretPosition ?? {index: 0, offset: 0};\n }\n\n protected createFieldValue(segments: readonly TokenFieldSegment<T>[]): this {\n const Constructor = this.constructor as new (\n segments: readonly TokenFieldSegment<T>[],\n options?: TokenFieldValueOptions\n ) => this;\n return new Constructor(segments);\n }\n\n /** Create a new list with the caret position set to the given position. */\n withCaretPosition(caretPosition: Position): this {\n if (\n this.caretPosition.index === caretPosition.index &&\n this.caretPosition.offset === caretPosition.offset\n ) {\n return this;\n }\n\n let result = this.createFieldValue(this.segments);\n result.caretPosition = caretPosition;\n result.previous = this.previous;\n result.next = this.next;\n result.isCoalescing = this.isCoalescing;\n return result;\n }\n\n private splitSegment(\n segment: TokenFieldSegment | undefined,\n offset: number\n ): [TokenFieldSegment | null, TokenFieldSegment | null] {\n if (!segment) {\n let empty = this.createTextSegment('');\n return [offset > 0 ? empty : null, offset > 0 ? null : empty];\n }\n\n if (segment.type === 'token') {\n return [offset > 0 ? {...segment} : null, offset > 0 ? null : {...segment}];\n }\n\n return [\n offset > 0 ? {type: 'text', text: segment.text.slice(0, offset)} : null,\n offset < segment.text.length ? this.createTextSegment(segment.text.slice(offset)) : null\n ];\n }\n\n private createTextSegment(text: string): TextSegment {\n return {type: 'text', text};\n }\n\n protected tokenize(text: string): TokenFieldSegment<T>[] {\n return [this.createTextSegment(text)];\n }\n\n private clampPosition(position: Position): Position {\n if (this.segments.length > 0 && position.index >= this.segments.length) {\n return {\n index: this.segments.length - 1,\n offset: this.segments[this.segments.length - 1].text.length\n };\n }\n\n if (position.index < 0) {\n return {index: 0, offset: 0};\n }\n return position;\n }\n\n /** Replace the text between two positions with new text. */\n replaceRange(start: Position, end: Position, text: string, coalesce = true): this {\n return this.replaceRangeWithSegments(\n start,\n end,\n text.length > 0 ? [this.createTextSegment(text)] : [],\n coalesce\n );\n }\n\n /** Replace the text between two positions with new segments. */\n replaceRangeWithSegments(\n start: Position,\n end: Position,\n insert: TokenFieldSegment<T>[],\n coalesce = true\n ): this {\n start = this.clampPosition(start);\n end = this.clampPosition(end);\n let startSegment = this.segments[start.index];\n let endSegment = this.segments[end.index];\n let [startSplit] = this.splitSegment(startSegment, start.offset);\n let [, endSplit] = this.splitSegment(endSegment, end.offset);\n\n let newSegments = this.segments.slice(0, start.index);\n if (startSplit) {\n appendSegments(newSegments, [startSplit]);\n }\n\n if (insert.length) {\n appendSegments(newSegments, insert, text => this.tokenize(text));\n }\n\n let lastSegment = newSegments[newSegments.length - 1];\n let lastIsText = lastSegment && lastSegment.type === 'text';\n let caret = {\n index: lastIsText ? newSegments.length - 1 : newSegments.length,\n offset: lastIsText ? lastSegment.text.length : 0\n };\n\n if (endSplit) {\n appendSegments(newSegments, [endSplit]);\n }\n\n appendSegments(newSegments, this.segments.slice(end.index + 1));\n\n let segments = this.createFieldValue(newSegments);\n segments.caretPosition = caret;\n segments.isCoalescing = coalesce;\n if (this.isCoalescing && coalesce && this.previous) {\n segments.previous = this.previous;\n segments.previous.next = segments;\n } else {\n segments.previous = this;\n this.caretPosition = end;\n this.next = segments;\n }\n return segments;\n }\n\n /** Find the boundary before or after a position using an Intl.Segmenter. */\n findBoundaryWithSegmenter(\n position: Position,\n segmenter: Intl.Segmenter,\n direction: Direction\n ): Position | null {\n position = this.clampPosition(position);\n for (let i = position.index; i >= 0 && i < this.segments.length; i += direction) {\n let segment = this.segments[i];\n switch (segment.type) {\n case 'token':\n if (\n i !== position.index ||\n (direction === Direction.Backward ? position.offset > 0 : position.offset === 0)\n ) {\n let index = i + direction;\n return {\n index: index >= 0 ? index : 0,\n offset:\n direction === Direction.Backward && index >= 0\n ? this.segments[index].text.length\n : 0\n };\n }\n continue;\n case 'text': {\n let offset = direction === Direction.Backward ? segment.text.length : 0;\n if (i === position.index) {\n offset = position.offset;\n }\n if (direction === Direction.Backward) {\n offset--;\n }\n if (offset < 0 || offset >= segment.text.length) {\n continue;\n }\n\n let part = segmenter.segment(segment.text).containing(offset);\n while (part && part.isWordLike === false) {\n offset += direction;\n part = segmenter.segment(segment.text).containing(offset);\n }\n\n if (part) {\n return {\n index: i,\n offset:\n direction === Direction.Backward ? part.index : part.index + part.segment.length\n };\n }\n continue;\n }\n }\n }\n\n return null;\n }\n\n /** Find a line boundary before or after a position. */\n findLineBoundary(position: Position, direction: Direction): Position | null {\n let res = this.findText(position, direction, '\\n');\n if (res) {\n return res;\n }\n return direction === Direction.Backward\n ? {index: 0, offset: 0}\n : {\n index: this.segments.length - 1,\n offset: this.segments[this.segments.length - 1].text.length\n };\n }\n\n /** Find a string or regular expression match before or after a position. */\n findText(position: Position, direction: Direction, search: string | RegExp): Position | null {\n if (this.segments.length === 0) {\n return null;\n }\n\n for (let i = position.index; i >= 0 && i < this.segments.length; i += direction) {\n let segment = this.segments[i];\n if (segment.type !== 'text') {\n continue;\n }\n let offset = findInText(\n segment.text,\n search,\n direction,\n i === position.index ? position.offset : undefined\n );\n if (offset >= 0) {\n return {\n index: i,\n offset: offset\n };\n }\n }\n\n return null;\n }\n\n /** Delete text at a position using a segmenter. */\n delete(\n position: Position,\n segmenter: Intl.Segmenter,\n direction: Direction,\n coalesce = true\n ): this {\n let boundary = this.findBoundaryWithSegmenter(position, segmenter, direction);\n if (boundary) {\n return this.replaceRange(\n direction === Direction.Backward ? boundary : position,\n direction === Direction.Backward ? position : boundary,\n '',\n coalesce\n );\n }\n\n this.caretPosition = position;\n return this;\n }\n\n /** Delete text to the next or previous line break. */\n deleteLine(position: Position, direction: Direction, coalesce = true): this {\n if (this.segments.length === 0) {\n return this;\n }\n\n let boundary = this.findLineBoundary(position, direction);\n if (boundary) {\n return this.replaceRange(\n direction === Direction.Backward ? boundary : position,\n direction === Direction.Backward ? position : boundary,\n '',\n coalesce\n );\n }\n\n return this;\n }\n\n /** Create a new list containing a subset of the segments. */\n slice(start: Position, end: Position): this {\n start = this.clampPosition(start);\n end = this.clampPosition(end);\n if (start.index === end.index && start.offset === end.offset) {\n return this.createFieldValue([]);\n }\n if (start.index === end.index) {\n let segment = this.segments[start.index];\n if (segment.type === 'text') {\n return this.createFieldValue([\n {type: 'text', text: segment.text.slice(start.offset, end.offset)}\n ]);\n }\n return this.createFieldValue([segment]);\n }\n let startSegment = this.segments[start.index];\n let endSegment = this.segments[end.index];\n let [, startSplit] = this.splitSegment(startSegment, start.offset);\n let [endSplit] = this.splitSegment(endSegment, end.offset);\n\n let result: TokenFieldSegment<T>[] = [];\n if (startSplit) {\n result.push(startSplit);\n }\n result.push(...this.segments.slice(start.index + 1, end.index));\n if (endSplit) {\n result.push(endSplit);\n }\n return this.createFieldValue(result);\n }\n\n /** Convert the list to a string. */\n toString(): string {\n return this.segments.map(seg => seg.text).join('');\n }\n\n /** Returns the previous list in the undo history. */\n undo(): this {\n return this.previous ?? this;\n }\n\n /** Returns the next list in the redo history. */\n redo(): this {\n return this.next ?? this;\n }\n\n /** End coalescing undo/redo history. */\n endCoalescing(): void {\n this.isCoalescing = false;\n }\n}\n\nfunction findInText(\n text: string,\n search: string | RegExp,\n direction: Direction,\n fromOffset?: number\n): number {\n if (typeof search === 'string') {\n if (direction === Direction.Backward) {\n return text.lastIndexOf(search, fromOffset !== undefined ? fromOffset - 1 : text.length - 1);\n }\n return text.indexOf(search, fromOffset ?? 0);\n }\n\n if (direction === Direction.Forward) {\n let start = fromOffset ?? 0;\n let index = text.slice(start).search(search);\n return index >= 0 ? start + index : -1;\n }\n\n let limit = fromOffset !== undefined ? fromOffset : text.length;\n if (limit < 0) {\n return -1;\n }\n\n let re = search.flags.includes('g') ? search : new RegExp(search.source, search.flags + 'g');\n let matches = Array.from(text.slice(0, limit).matchAll(re));\n return matches.at(-1)?.index ?? -1;\n}\n\nfunction appendSegments(\n segments: TokenFieldSegment[],\n insert: TokenFieldSegment[],\n tokenize?: (text: string) => TokenFieldSegment[]\n): TokenFieldSegment[] {\n for (let segment of insert) {\n if (segment.type === 'text' && segment.text.length === 0) {\n continue;\n }\n\n let last = segments[segments.length - 1];\n if (last && last.type === 'text' && segment.type === 'text') {\n if (tokenize) {\n let tokenized = tokenize(last.text + segment.text);\n segments.splice(segments.length - 1, 1, ...tokenized);\n } else {\n segments[segments.length - 1] = {type: 'text', text: last.text + segment.text};\n }\n } else if (tokenize && segment.type === 'text') {\n let tokenized = tokenize(segment.text);\n segments.push(...tokenized);\n } else {\n segments.push(segment);\n }\n }\n return segments;\n}\n"],"names":[],"version":3,"file":"TokenFieldValue.cjs.map"}
@@ -0,0 +1,285 @@
1
+ /*
2
+ * Copyright 2026 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */ var $bb130d0e51bd3001$var$Direction = /*#__PURE__*/ function(Direction) {
12
+ Direction[Direction["Forward"] = 1] = "Forward";
13
+ Direction[Direction["Backward"] = -1] = "Backward";
14
+ return Direction;
15
+ }($bb130d0e51bd3001$var$Direction || {});
16
+ class $bb130d0e51bd3001$export$f3decd0c2cd2fe2e {
17
+ createFieldValue(segments) {
18
+ const Constructor = this.constructor;
19
+ return new Constructor(segments);
20
+ }
21
+ /** Create a new list with the caret position set to the given position. */ withCaretPosition(caretPosition) {
22
+ if (this.caretPosition.index === caretPosition.index && this.caretPosition.offset === caretPosition.offset) return this;
23
+ let result = this.createFieldValue(this.segments);
24
+ result.caretPosition = caretPosition;
25
+ result.previous = this.previous;
26
+ result.next = this.next;
27
+ result.isCoalescing = this.isCoalescing;
28
+ return result;
29
+ }
30
+ splitSegment(segment, offset) {
31
+ if (!segment) {
32
+ let empty = this.createTextSegment('');
33
+ return [
34
+ offset > 0 ? empty : null,
35
+ offset > 0 ? null : empty
36
+ ];
37
+ }
38
+ if (segment.type === 'token') return [
39
+ offset > 0 ? {
40
+ ...segment
41
+ } : null,
42
+ offset > 0 ? null : {
43
+ ...segment
44
+ }
45
+ ];
46
+ return [
47
+ offset > 0 ? {
48
+ type: 'text',
49
+ text: segment.text.slice(0, offset)
50
+ } : null,
51
+ offset < segment.text.length ? this.createTextSegment(segment.text.slice(offset)) : null
52
+ ];
53
+ }
54
+ createTextSegment(text) {
55
+ return {
56
+ type: 'text',
57
+ text: text
58
+ };
59
+ }
60
+ tokenize(text) {
61
+ return [
62
+ this.createTextSegment(text)
63
+ ];
64
+ }
65
+ clampPosition(position) {
66
+ if (this.segments.length > 0 && position.index >= this.segments.length) return {
67
+ index: this.segments.length - 1,
68
+ offset: this.segments[this.segments.length - 1].text.length
69
+ };
70
+ if (position.index < 0) return {
71
+ index: 0,
72
+ offset: 0
73
+ };
74
+ return position;
75
+ }
76
+ /** Replace the text between two positions with new text. */ replaceRange(start, end, text, coalesce = true) {
77
+ return this.replaceRangeWithSegments(start, end, text.length > 0 ? [
78
+ this.createTextSegment(text)
79
+ ] : [], coalesce);
80
+ }
81
+ /** Replace the text between two positions with new segments. */ replaceRangeWithSegments(start, end, insert, coalesce = true) {
82
+ start = this.clampPosition(start);
83
+ end = this.clampPosition(end);
84
+ let startSegment = this.segments[start.index];
85
+ let endSegment = this.segments[end.index];
86
+ let [startSplit] = this.splitSegment(startSegment, start.offset);
87
+ let [, endSplit] = this.splitSegment(endSegment, end.offset);
88
+ let newSegments = this.segments.slice(0, start.index);
89
+ if (startSplit) $bb130d0e51bd3001$var$appendSegments(newSegments, [
90
+ startSplit
91
+ ]);
92
+ if (insert.length) $bb130d0e51bd3001$var$appendSegments(newSegments, insert, (text)=>this.tokenize(text));
93
+ let lastSegment = newSegments[newSegments.length - 1];
94
+ let lastIsText = lastSegment && lastSegment.type === 'text';
95
+ let caret = {
96
+ index: lastIsText ? newSegments.length - 1 : newSegments.length,
97
+ offset: lastIsText ? lastSegment.text.length : 0
98
+ };
99
+ if (endSplit) $bb130d0e51bd3001$var$appendSegments(newSegments, [
100
+ endSplit
101
+ ]);
102
+ $bb130d0e51bd3001$var$appendSegments(newSegments, this.segments.slice(end.index + 1));
103
+ let segments = this.createFieldValue(newSegments);
104
+ segments.caretPosition = caret;
105
+ segments.isCoalescing = coalesce;
106
+ if (this.isCoalescing && coalesce && this.previous) {
107
+ segments.previous = this.previous;
108
+ segments.previous.next = segments;
109
+ } else {
110
+ segments.previous = this;
111
+ this.caretPosition = end;
112
+ this.next = segments;
113
+ }
114
+ return segments;
115
+ }
116
+ /** Find the boundary before or after a position using an Intl.Segmenter. */ findBoundaryWithSegmenter(position, segmenter, direction) {
117
+ position = this.clampPosition(position);
118
+ for(let i = position.index; i >= 0 && i < this.segments.length; i += direction){
119
+ let segment = this.segments[i];
120
+ switch(segment.type){
121
+ case 'token':
122
+ if (i !== position.index || (direction === -1 ? position.offset > 0 : position.offset === 0)) {
123
+ let index = i + direction;
124
+ return {
125
+ index: index >= 0 ? index : 0,
126
+ offset: direction === -1 && index >= 0 ? this.segments[index].text.length : 0
127
+ };
128
+ }
129
+ continue;
130
+ case 'text':
131
+ {
132
+ let offset = direction === -1 ? segment.text.length : 0;
133
+ if (i === position.index) offset = position.offset;
134
+ if (direction === -1) offset--;
135
+ if (offset < 0 || offset >= segment.text.length) continue;
136
+ let part = segmenter.segment(segment.text).containing(offset);
137
+ while(part && part.isWordLike === false){
138
+ offset += direction;
139
+ part = segmenter.segment(segment.text).containing(offset);
140
+ }
141
+ if (part) return {
142
+ index: i,
143
+ offset: direction === -1 ? part.index : part.index + part.segment.length
144
+ };
145
+ continue;
146
+ }
147
+ }
148
+ }
149
+ return null;
150
+ }
151
+ /** Find a line boundary before or after a position. */ findLineBoundary(position, direction) {
152
+ let res = this.findText(position, direction, '\n');
153
+ if (res) return res;
154
+ return direction === -1 ? {
155
+ index: 0,
156
+ offset: 0
157
+ } : {
158
+ index: this.segments.length - 1,
159
+ offset: this.segments[this.segments.length - 1].text.length
160
+ };
161
+ }
162
+ /** Find a string or regular expression match before or after a position. */ findText(position, direction, search) {
163
+ if (this.segments.length === 0) return null;
164
+ for(let i = position.index; i >= 0 && i < this.segments.length; i += direction){
165
+ let segment = this.segments[i];
166
+ if (segment.type !== 'text') continue;
167
+ let offset = $bb130d0e51bd3001$var$findInText(segment.text, search, direction, i === position.index ? position.offset : undefined);
168
+ if (offset >= 0) return {
169
+ index: i,
170
+ offset: offset
171
+ };
172
+ }
173
+ return null;
174
+ }
175
+ /** Delete text at a position using a segmenter. */ delete(position, segmenter, direction, coalesce = true) {
176
+ let boundary = this.findBoundaryWithSegmenter(position, segmenter, direction);
177
+ if (boundary) return this.replaceRange(direction === -1 ? boundary : position, direction === -1 ? position : boundary, '', coalesce);
178
+ this.caretPosition = position;
179
+ return this;
180
+ }
181
+ /** Delete text to the next or previous line break. */ deleteLine(position, direction, coalesce = true) {
182
+ if (this.segments.length === 0) return this;
183
+ let boundary = this.findLineBoundary(position, direction);
184
+ if (boundary) return this.replaceRange(direction === -1 ? boundary : position, direction === -1 ? position : boundary, '', coalesce);
185
+ return this;
186
+ }
187
+ /** Create a new list containing a subset of the segments. */ slice(start, end) {
188
+ start = this.clampPosition(start);
189
+ end = this.clampPosition(end);
190
+ if (start.index === end.index && start.offset === end.offset) return this.createFieldValue([]);
191
+ if (start.index === end.index) {
192
+ let segment = this.segments[start.index];
193
+ if (segment.type === 'text') return this.createFieldValue([
194
+ {
195
+ type: 'text',
196
+ text: segment.text.slice(start.offset, end.offset)
197
+ }
198
+ ]);
199
+ return this.createFieldValue([
200
+ segment
201
+ ]);
202
+ }
203
+ let startSegment = this.segments[start.index];
204
+ let endSegment = this.segments[end.index];
205
+ let [, startSplit] = this.splitSegment(startSegment, start.offset);
206
+ let [endSplit] = this.splitSegment(endSegment, end.offset);
207
+ let result = [];
208
+ if (startSplit) result.push(startSplit);
209
+ result.push(...this.segments.slice(start.index + 1, end.index));
210
+ if (endSplit) result.push(endSplit);
211
+ return this.createFieldValue(result);
212
+ }
213
+ /** Convert the list to a string. */ toString() {
214
+ return this.segments.map((seg)=>seg.text).join('');
215
+ }
216
+ /** Returns the previous list in the undo history. */ undo() {
217
+ var _this_previous;
218
+ return (_this_previous = this.previous) !== null && _this_previous !== void 0 ? _this_previous : this;
219
+ }
220
+ /** Returns the next list in the redo history. */ redo() {
221
+ var _this_next;
222
+ return (_this_next = this.next) !== null && _this_next !== void 0 ? _this_next : this;
223
+ }
224
+ /** End coalescing undo/redo history. */ endCoalescing() {
225
+ this.isCoalescing = false;
226
+ }
227
+ /** Create a new list with the given segments. */ constructor(tokens, options){
228
+ /** The caret position. */ this.caretPosition = {
229
+ index: 0,
230
+ offset: 0
231
+ };
232
+ // Linked list representing the undo/redo history.
233
+ this.previous = null;
234
+ this.next = null;
235
+ this.isCoalescing = true;
236
+ this.segments = tokens;
237
+ var _options_caretPosition;
238
+ this.caretPosition = (_options_caretPosition = options === null || options === void 0 ? void 0 : options.caretPosition) !== null && _options_caretPosition !== void 0 ? _options_caretPosition : {
239
+ index: 0,
240
+ offset: 0
241
+ };
242
+ }
243
+ }
244
+ $bb130d0e51bd3001$export$f3decd0c2cd2fe2e.Direction = $bb130d0e51bd3001$var$Direction;
245
+ function $bb130d0e51bd3001$var$findInText(text, search, direction, fromOffset) {
246
+ var _matches_at;
247
+ if (typeof search === 'string') {
248
+ if (direction === -1) return text.lastIndexOf(search, fromOffset !== undefined ? fromOffset - 1 : text.length - 1);
249
+ return text.indexOf(search, fromOffset !== null && fromOffset !== void 0 ? fromOffset : 0);
250
+ }
251
+ if (direction === 1) {
252
+ let start = fromOffset !== null && fromOffset !== void 0 ? fromOffset : 0;
253
+ let index = text.slice(start).search(search);
254
+ return index >= 0 ? start + index : -1;
255
+ }
256
+ let limit = fromOffset !== undefined ? fromOffset : text.length;
257
+ if (limit < 0) return -1;
258
+ let re = search.flags.includes('g') ? search : new RegExp(search.source, search.flags + 'g');
259
+ let matches = Array.from(text.slice(0, limit).matchAll(re));
260
+ var _matches_at_index;
261
+ return (_matches_at_index = (_matches_at = matches.at(-1)) === null || _matches_at === void 0 ? void 0 : _matches_at.index) !== null && _matches_at_index !== void 0 ? _matches_at_index : -1;
262
+ }
263
+ function $bb130d0e51bd3001$var$appendSegments(segments, insert, tokenize) {
264
+ for (let segment of insert){
265
+ if (segment.type === 'text' && segment.text.length === 0) continue;
266
+ let last = segments[segments.length - 1];
267
+ if (last && last.type === 'text' && segment.type === 'text') {
268
+ if (tokenize) {
269
+ let tokenized = tokenize(last.text + segment.text);
270
+ segments.splice(segments.length - 1, 1, ...tokenized);
271
+ } else segments[segments.length - 1] = {
272
+ type: 'text',
273
+ text: last.text + segment.text
274
+ };
275
+ } else if (tokenize && segment.type === 'text') {
276
+ let tokenized = tokenize(segment.text);
277
+ segments.push(...tokenized);
278
+ } else segments.push(segment);
279
+ }
280
+ return segments;
281
+ }
282
+
283
+
284
+ export {$bb130d0e51bd3001$export$f3decd0c2cd2fe2e as TokenFieldValue};
285
+ //# sourceMappingURL=TokenFieldValue.js.map