@destyler/dynamic 0.0.7-beta.7 → 0.0.7-beta.9

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 ADDED
@@ -0,0 +1,1049 @@
1
+ import { createAnatomy } from "@destyler/anatomy";
2
+ import { ariaAttr, contains, createScope, dataAttr, dispatchInputValueEvent, getEventKey, getNativeEvent, indexOfId, isComposingEvent, nextById, prevById, queryAll, raf, trackFormControl } from "@destyler/dom";
3
+ import { autoResizeInput } from "@destyler/auto-resize";
4
+ import { trackInteractOutside } from "@destyler/interact-outside";
5
+ import { createLiveRegion } from "@destyler/live-region";
6
+ import { compact, createSplitProps, isEqual, removeAt, uniq } from "@destyler/utils";
7
+ import { createMachine, guards } from "@destyler/xstate";
8
+ import { createProps } from "@destyler/types";
9
+
10
+ //#region src/anatomy.ts
11
+ const anatomy = createAnatomy("tagsInput").parts("root", "label", "control", "input", "clearTrigger", "item", "itemPreview", "itemInput", "itemText", "itemDeleteTrigger");
12
+ const parts = anatomy.build();
13
+
14
+ //#endregion
15
+ //#region src/dom.ts
16
+ const dom = createScope({
17
+ getRootId: (ctx) => ctx.ids?.root ?? `dynamic:${ctx.id}`,
18
+ getInputId: (ctx) => ctx.ids?.input ?? `dynamic:${ctx.id}:input`,
19
+ getClearTriggerId: (ctx) => ctx.ids?.clearBtn ?? `dynamic:${ctx.id}:clear-btn`,
20
+ getHiddenInputId: (ctx) => ctx.ids?.hiddenInput ?? `dynamic:${ctx.id}:hidden-input`,
21
+ getLabelId: (ctx) => ctx.ids?.label ?? `dynamic:${ctx.id}:label`,
22
+ getControlId: (ctx) => ctx.ids?.control ?? `dynamic:${ctx.id}:control`,
23
+ getItemId: (ctx, opt) => ctx.ids?.item?.(opt) ?? `dynamic:${ctx.id}:tag:${opt.value}:${opt.index}`,
24
+ getItemDeleteTriggerId: (ctx, opt) => ctx.ids?.itemDeleteTrigger?.(opt) ?? `${dom.getItemId(ctx, opt)}:delete-btn`,
25
+ getItemInputId: (ctx, opt) => ctx.ids?.itemInput?.(opt) ?? `${dom.getItemId(ctx, opt)}:input`,
26
+ getEditInputId: (ctx) => `${ctx.editedTagId}:input`,
27
+ getTagInputEl: (ctx, opt) => dom.getById(ctx, dom.getItemInputId(ctx, opt)),
28
+ getRootEl: (ctx) => dom.getById(ctx, dom.getRootId(ctx)),
29
+ getInputEl: (ctx) => dom.getById(ctx, dom.getInputId(ctx)),
30
+ getHiddenInputEl: (ctx) => dom.getById(ctx, dom.getHiddenInputId(ctx)),
31
+ getEditInputEl: (ctx) => dom.getById(ctx, dom.getEditInputId(ctx)),
32
+ getTagElements: (ctx) => queryAll(dom.getRootEl(ctx), `[data-part=item-preview]:not([data-disabled])`),
33
+ getFirstEl: (ctx) => dom.getTagElements(ctx)[0],
34
+ getLastEl: (ctx) => dom.getTagElements(ctx)[dom.getTagElements(ctx).length - 1],
35
+ getPrevEl: (ctx, id) => prevById(dom.getTagElements(ctx), id, false),
36
+ getNextEl: (ctx, id) => nextById(dom.getTagElements(ctx), id, false),
37
+ getTagElAtIndex: (ctx, index) => dom.getTagElements(ctx)[index],
38
+ getIndexOfId: (ctx, id) => indexOfId(dom.getTagElements(ctx), id),
39
+ isInputFocused: (ctx) => dom.getDoc(ctx).activeElement === dom.getInputEl(ctx),
40
+ getHighlightedTagValue: (ctx) => {
41
+ if (!ctx.highlightedTagId) return null;
42
+ const tagEl = dom.getById(ctx, ctx.highlightedTagId);
43
+ return tagEl?.dataset.value ?? null;
44
+ },
45
+ setHoverIntent: (el) => {
46
+ const tagEl = el.closest("[data-part=item-preview]");
47
+ if (!tagEl) return;
48
+ tagEl.dataset.deleteIntent = "";
49
+ },
50
+ clearHoverIntent: (el) => {
51
+ const tagEl = el.closest("[data-part=item-preview]");
52
+ if (!tagEl) return;
53
+ delete tagEl.dataset.deleteIntent;
54
+ },
55
+ dispatchInputEvent(ctx) {
56
+ const inputEl = dom.getHiddenInputEl(ctx);
57
+ if (!inputEl) return;
58
+ dispatchInputValueEvent(inputEl, { value: ctx.valueAsString });
59
+ }
60
+ });
61
+
62
+ //#endregion
63
+ //#region src/connect.ts
64
+ function connect(state, send, normalize) {
65
+ const interactive = state.context.isInteractive;
66
+ const disabled = state.context.disabled;
67
+ const readOnly = state.context.readOnly;
68
+ const invalid = state.context.invalid || state.context.isOverflowing;
69
+ const translations = state.context.translations;
70
+ const focused = state.hasTag("focused");
71
+ const editingTag = state.matches("editing:tag");
72
+ const empty = state.context.count === 0;
73
+ function getItemState(options) {
74
+ const id = dom.getItemId(state.context, options);
75
+ return {
76
+ id,
77
+ editing: editingTag && state.context.editedTagId === id,
78
+ highlighted: id === state.context.highlightedTagId,
79
+ disabled: Boolean(options.disabled || disabled)
80
+ };
81
+ }
82
+ return {
83
+ empty,
84
+ inputValue: state.context.trimmedInputValue,
85
+ value: state.context.value,
86
+ valueAsString: state.context.valueAsString,
87
+ count: state.context.count,
88
+ atMax: state.context.isAtMax,
89
+ setValue(value) {
90
+ send({
91
+ type: "SET_VALUE",
92
+ value
93
+ });
94
+ },
95
+ clearValue(id) {
96
+ if (id) send({
97
+ type: "CLEAR_TAG",
98
+ id
99
+ });
100
+ else send("CLEAR_VALUE");
101
+ },
102
+ addValue(value) {
103
+ send({
104
+ type: "ADD_TAG",
105
+ value
106
+ });
107
+ },
108
+ setValueAtIndex(index, value) {
109
+ send({
110
+ type: "SET_VALUE_AT_INDEX",
111
+ index,
112
+ value
113
+ });
114
+ },
115
+ setInputValue(value) {
116
+ send({
117
+ type: "SET_INPUT_VALUE",
118
+ value
119
+ });
120
+ },
121
+ clearInputValue() {
122
+ send({
123
+ type: "SET_INPUT_VALUE",
124
+ value: ""
125
+ });
126
+ },
127
+ focus() {
128
+ dom.getInputEl(state.context)?.focus();
129
+ },
130
+ getItemState,
131
+ getRootProps() {
132
+ return normalize.element({
133
+ "dir": state.context.dir,
134
+ ...parts.root.attrs,
135
+ "data-invalid": dataAttr(invalid),
136
+ "data-readonly": dataAttr(readOnly),
137
+ "data-disabled": dataAttr(disabled),
138
+ "data-focus": dataAttr(focused),
139
+ "data-empty": dataAttr(empty),
140
+ "id": dom.getRootId(state.context),
141
+ onPointerDown() {
142
+ if (!interactive) return;
143
+ send("POINTER_DOWN");
144
+ }
145
+ });
146
+ },
147
+ getLabelProps() {
148
+ return normalize.label({
149
+ ...parts.label.attrs,
150
+ "data-disabled": dataAttr(disabled),
151
+ "data-invalid": dataAttr(invalid),
152
+ "data-readonly": dataAttr(readOnly),
153
+ "id": dom.getLabelId(state.context),
154
+ "dir": state.context.dir,
155
+ "htmlFor": dom.getInputId(state.context)
156
+ });
157
+ },
158
+ getControlProps() {
159
+ return normalize.element({
160
+ "id": dom.getControlId(state.context),
161
+ ...parts.control.attrs,
162
+ "dir": state.context.dir,
163
+ "tabIndex": readOnly ? 0 : void 0,
164
+ "data-disabled": dataAttr(disabled),
165
+ "data-readonly": dataAttr(readOnly),
166
+ "data-invalid": dataAttr(invalid),
167
+ "data-focus": dataAttr(focused)
168
+ });
169
+ },
170
+ getInputProps() {
171
+ return normalize.input({
172
+ ...parts.input.attrs,
173
+ "dir": state.context.dir,
174
+ "data-invalid": dataAttr(invalid),
175
+ "aria-invalid": ariaAttr(invalid),
176
+ "data-readonly": dataAttr(readOnly),
177
+ "maxLength": state.context.maxLength,
178
+ "id": dom.getInputId(state.context),
179
+ "defaultValue": state.context.inputValue,
180
+ "autoComplete": "off",
181
+ "autoCorrect": "off",
182
+ "autoCapitalize": "none",
183
+ "disabled": disabled || readOnly,
184
+ onInput(event) {
185
+ const evt = getNativeEvent(event);
186
+ const value = event.currentTarget.value;
187
+ if (evt.inputType === "insertFromPaste") {
188
+ send({
189
+ type: "PASTE",
190
+ value
191
+ });
192
+ return;
193
+ }
194
+ if (endsWith(value, state.context.delimiter)) {
195
+ send("DELIMITER_KEY");
196
+ return;
197
+ }
198
+ send({
199
+ type: "TYPE",
200
+ value,
201
+ key: evt.inputType
202
+ });
203
+ },
204
+ onFocus() {
205
+ send("FOCUS");
206
+ },
207
+ onKeyDown(event) {
208
+ if (event.defaultPrevented) return;
209
+ if (isComposingEvent(event)) return;
210
+ const target = event.currentTarget;
211
+ const isCombobox = target.getAttribute("role") === "combobox";
212
+ const isExpanded = target.ariaExpanded === "true";
213
+ const keyMap = {
214
+ ArrowDown() {
215
+ send("ARROW_DOWN");
216
+ },
217
+ ArrowLeft() {
218
+ if (isCombobox && isExpanded) return;
219
+ send("ARROW_LEFT");
220
+ },
221
+ ArrowRight(event$1) {
222
+ if (state.context.highlightedTagId) event$1.preventDefault();
223
+ if (isCombobox && isExpanded) return;
224
+ send("ARROW_RIGHT");
225
+ },
226
+ Escape(event$1) {
227
+ event$1.preventDefault();
228
+ send("ESCAPE");
229
+ },
230
+ Backspace() {
231
+ send("BACKSPACE");
232
+ },
233
+ Delete() {
234
+ send("DELETE");
235
+ },
236
+ Enter(event$1) {
237
+ if (isCombobox && isExpanded) return;
238
+ send("ENTER");
239
+ event$1.preventDefault();
240
+ }
241
+ };
242
+ const key = getEventKey(event, state.context);
243
+ const exec = keyMap[key];
244
+ if (exec) exec(event);
245
+ }
246
+ });
247
+ },
248
+ getHiddenInputProps() {
249
+ return normalize.input({
250
+ type: "text",
251
+ hidden: true,
252
+ name: state.context.name,
253
+ form: state.context.form,
254
+ disabled,
255
+ readOnly,
256
+ required: state.context.required,
257
+ id: dom.getHiddenInputId(state.context),
258
+ defaultValue: state.context.valueAsString
259
+ });
260
+ },
261
+ getItemProps(props$1) {
262
+ return normalize.element({
263
+ ...parts.item.attrs,
264
+ "dir": state.context.dir,
265
+ "data-value": props$1.value,
266
+ "data-disabled": dataAttr(disabled)
267
+ });
268
+ },
269
+ getItemPreviewProps(props$1) {
270
+ const itemState = getItemState(props$1);
271
+ return normalize.element({
272
+ ...parts.itemPreview.attrs,
273
+ "id": itemState.id,
274
+ "dir": state.context.dir,
275
+ "hidden": itemState.editing,
276
+ "data-value": props$1.value,
277
+ "data-disabled": dataAttr(disabled),
278
+ "data-highlighted": dataAttr(itemState.highlighted),
279
+ onPointerDown(event) {
280
+ if (!interactive || itemState.disabled) return;
281
+ event.preventDefault();
282
+ send({
283
+ type: "POINTER_DOWN_TAG",
284
+ id: itemState.id
285
+ });
286
+ },
287
+ onDoubleClick() {
288
+ if (!interactive || itemState.disabled) return;
289
+ send({
290
+ type: "DOUBLE_CLICK_TAG",
291
+ id: itemState.id
292
+ });
293
+ }
294
+ });
295
+ },
296
+ getItemTextProps(props$1) {
297
+ const itemState = getItemState(props$1);
298
+ return normalize.element({
299
+ ...parts.itemText.attrs,
300
+ "dir": state.context.dir,
301
+ "data-disabled": dataAttr(disabled),
302
+ "data-highlighted": dataAttr(itemState.highlighted)
303
+ });
304
+ },
305
+ getItemInputProps(props$1) {
306
+ const itemState = getItemState(props$1);
307
+ return normalize.input({
308
+ ...parts.itemInput.attrs,
309
+ "dir": state.context.dir,
310
+ "aria-label": translations.tagEdited(props$1.value),
311
+ "aria-hidden": true,
312
+ "disabled": disabled,
313
+ "id": dom.getItemInputId(state.context, props$1),
314
+ "tabIndex": -1,
315
+ "hidden": !itemState.editing,
316
+ "defaultValue": itemState.editing ? state.context.editedTagValue : "",
317
+ onInput(event) {
318
+ send({
319
+ type: "TAG_INPUT_TYPE",
320
+ value: event.currentTarget.value
321
+ });
322
+ },
323
+ onBlur(event) {
324
+ send({
325
+ type: "TAG_INPUT_BLUR",
326
+ target: event.relatedTarget,
327
+ id: itemState.id
328
+ });
329
+ },
330
+ onKeyDown(event) {
331
+ if (event.defaultPrevented) return;
332
+ if (isComposingEvent(event)) return;
333
+ const keyMap = {
334
+ Enter() {
335
+ send("TAG_INPUT_ENTER");
336
+ },
337
+ Escape() {
338
+ send("TAG_INPUT_ESCAPE");
339
+ }
340
+ };
341
+ const exec = keyMap[event.key];
342
+ if (exec) {
343
+ event.preventDefault();
344
+ exec(event);
345
+ }
346
+ }
347
+ });
348
+ },
349
+ getItemDeleteTriggerProps(props$1) {
350
+ const id = dom.getItemId(state.context, props$1);
351
+ return normalize.button({
352
+ ...parts.itemDeleteTrigger.attrs,
353
+ "dir": state.context.dir,
354
+ "id": dom.getItemDeleteTriggerId(state.context, props$1),
355
+ "type": "button",
356
+ "disabled": disabled,
357
+ "aria-label": translations.deleteTagTriggerLabel(props$1.value),
358
+ "tabIndex": -1,
359
+ onPointerDown(event) {
360
+ if (!interactive) event.preventDefault();
361
+ },
362
+ onPointerMove(event) {
363
+ if (!interactive) return;
364
+ dom.setHoverIntent(event.currentTarget);
365
+ },
366
+ onPointerLeave(event) {
367
+ if (!interactive) return;
368
+ dom.clearHoverIntent(event.currentTarget);
369
+ },
370
+ onClick() {
371
+ if (!interactive) return;
372
+ send({
373
+ type: "CLICK_DELETE_TAG",
374
+ id
375
+ });
376
+ }
377
+ });
378
+ },
379
+ getClearTriggerProps() {
380
+ return normalize.button({
381
+ ...parts.clearTrigger.attrs,
382
+ "dir": state.context.dir,
383
+ "id": dom.getClearTriggerId(state.context),
384
+ "type": "button",
385
+ "data-readonly": dataAttr(readOnly),
386
+ "disabled": disabled,
387
+ "aria-label": translations.clearTriggerLabel,
388
+ "hidden": empty,
389
+ onClick() {
390
+ if (!interactive) return;
391
+ send("CLEAR_VALUE");
392
+ }
393
+ });
394
+ }
395
+ };
396
+ }
397
+ function endsWith(str, del) {
398
+ if (!del) return false;
399
+ if (typeof del === "string") return str.endsWith(del);
400
+ return (/* @__PURE__ */ new RegExp(`${del.source}$`)).test(str);
401
+ }
402
+
403
+ //#endregion
404
+ //#region src/machine.ts
405
+ const { and, not, or } = guards;
406
+ const invoke = {
407
+ change: (ctx) => {
408
+ ctx.onValueChange?.({ value: Array.from(ctx.value) });
409
+ dom.dispatchInputEvent(ctx);
410
+ },
411
+ highlightChange: (ctx) => {
412
+ const highlightedValue = dom.getHighlightedTagValue(ctx);
413
+ ctx.onHighlightChange?.({ highlightedValue });
414
+ },
415
+ valueChange: (ctx) => {
416
+ ctx.onInputValueChange?.({ inputValue: ctx.inputValue });
417
+ }
418
+ };
419
+ const set = {
420
+ value: (ctx, value) => {
421
+ if (isEqual(ctx.value, value)) return;
422
+ ctx.value = value;
423
+ invoke.change(ctx);
424
+ },
425
+ valueAtIndex: (ctx, index, value) => {
426
+ if (isEqual(ctx.value[index], value)) return;
427
+ ctx.value[index] = value;
428
+ invoke.change(ctx);
429
+ },
430
+ highlightedId: (ctx, id) => {
431
+ if (isEqual(ctx.highlightedTagId, id)) return;
432
+ ctx.highlightedTagId = id;
433
+ invoke.highlightChange(ctx);
434
+ },
435
+ inputValue: (ctx, value) => {
436
+ if (isEqual(ctx.inputValue, value)) return;
437
+ ctx.inputValue = value;
438
+ invoke.valueChange(ctx);
439
+ }
440
+ };
441
+ function machine(userContext) {
442
+ const ctx = compact(userContext);
443
+ return createMachine({
444
+ id: "tags-input",
445
+ initial: ctx.autoFocus ? "focused:input" : "idle",
446
+ context: {
447
+ inputValue: "",
448
+ editedTagValue: "",
449
+ editedTagId: null,
450
+ highlightedTagId: null,
451
+ value: [],
452
+ dir: "ltr",
453
+ max: Infinity,
454
+ blurBehavior: void 0,
455
+ addOnPaste: false,
456
+ editable: true,
457
+ validate: () => true,
458
+ delimiter: ",",
459
+ disabled: false,
460
+ readOnly: false,
461
+ ...ctx,
462
+ liveRegion: null,
463
+ log: {
464
+ current: null,
465
+ prev: null
466
+ },
467
+ fieldsetDisabled: false,
468
+ translations: {
469
+ clearTriggerLabel: "Clear all tags",
470
+ deleteTagTriggerLabel: (value) => `Delete tag ${value}`,
471
+ tagAdded: (value) => `Added tag ${value}`,
472
+ tagsPasted: (values) => `Pasted ${values.length} tags`,
473
+ tagEdited: (value) => `Editing tag ${value}. Press enter to save or escape to cancel.`,
474
+ tagUpdated: (value) => `Tag update to ${value}`,
475
+ tagDeleted: (value) => `Tag ${value} deleted`,
476
+ tagSelected: (value) => `Tag ${value} selected. Press enter to edit, delete or backspace to remove.`,
477
+ ...ctx.translations
478
+ }
479
+ },
480
+ computed: {
481
+ count: (ctx$1) => ctx$1.value.length,
482
+ valueAsString: (ctx$1) => JSON.stringify(ctx$1.value),
483
+ trimmedInputValue: (ctx$1) => ctx$1.inputValue.trim(),
484
+ isDisabled: (ctx$1) => !!ctx$1.disabled || ctx$1.fieldsetDisabled,
485
+ isInteractive: (ctx$1) => !(ctx$1.readOnly || ctx$1.isDisabled),
486
+ isAtMax: (ctx$1) => ctx$1.count === ctx$1.max,
487
+ isOverflowing: (ctx$1) => ctx$1.count > ctx$1.max
488
+ },
489
+ watch: {
490
+ highlightedTagId: "logHighlightedTag",
491
+ isOverflowing: "invokeOnInvalid",
492
+ log: "announceLog",
493
+ inputValue: "syncInputValue",
494
+ editedTagValue: "syncEditedTagInputValue"
495
+ },
496
+ activities: ["trackLiveRegion", "trackFormControlState"],
497
+ exit: ["clearLog"],
498
+ on: {
499
+ DOUBLE_CLICK_TAG: {
500
+ internal: true,
501
+ guard: "isTagEditable",
502
+ target: "editing:tag",
503
+ actions: ["setEditedId", "initializeEditedTagValue"]
504
+ },
505
+ POINTER_DOWN_TAG: {
506
+ internal: true,
507
+ target: "navigating:tag",
508
+ actions: ["highlightTag", "focusInput"]
509
+ },
510
+ CLICK_DELETE_TAG: {
511
+ target: "focused:input",
512
+ actions: ["deleteTag"]
513
+ },
514
+ SET_INPUT_VALUE: { actions: ["setInputValue"] },
515
+ SET_VALUE: { actions: ["setValue"] },
516
+ CLEAR_TAG: { actions: ["deleteTag"] },
517
+ SET_VALUE_AT_INDEX: { actions: ["setValueAtIndex"] },
518
+ CLEAR_VALUE: { actions: [
519
+ "clearTags",
520
+ "clearInputValue",
521
+ "focusInput"
522
+ ] },
523
+ ADD_TAG: { actions: ["addTag"] },
524
+ INSERT_TAG: {
525
+ guard: and(or(not("isAtMax"), "allowOverflow"), not("isInputValueEmpty")),
526
+ actions: ["addTag", "clearInputValue"]
527
+ },
528
+ EXTERNAL_BLUR: [{
529
+ guard: "addOnBlur",
530
+ actions: "raiseInsertTagEvent"
531
+ }, {
532
+ guard: "clearOnBlur",
533
+ actions: "clearInputValue"
534
+ }]
535
+ },
536
+ states: {
537
+ "idle": { on: {
538
+ FOCUS: "focused:input",
539
+ POINTER_DOWN: {
540
+ guard: not("hasHighlightedTag"),
541
+ target: "focused:input"
542
+ }
543
+ } },
544
+ "focused:input": {
545
+ tags: ["focused"],
546
+ entry: ["focusInput", "clearHighlightedId"],
547
+ activities: ["trackInteractOutside"],
548
+ on: {
549
+ TYPE: { actions: "setInputValue" },
550
+ BLUR: [
551
+ {
552
+ guard: "addOnBlur",
553
+ target: "idle",
554
+ actions: "raiseInsertTagEvent"
555
+ },
556
+ {
557
+ guard: "clearOnBlur",
558
+ target: "idle",
559
+ actions: "clearInputValue"
560
+ },
561
+ { target: "idle" }
562
+ ],
563
+ ENTER: { actions: ["raiseInsertTagEvent"] },
564
+ DELIMITER_KEY: { actions: ["raiseInsertTagEvent"] },
565
+ ARROW_LEFT: {
566
+ guard: and("hasTags", "isInputCaretAtStart"),
567
+ target: "navigating:tag",
568
+ actions: "highlightLastTag"
569
+ },
570
+ BACKSPACE: {
571
+ target: "navigating:tag",
572
+ guard: and("hasTags", "isInputCaretAtStart"),
573
+ actions: "highlightLastTag"
574
+ },
575
+ DELETE: {
576
+ guard: "hasHighlightedTag",
577
+ actions: ["deleteHighlightedTag", "highlightTagAtIndex"]
578
+ },
579
+ PASTE: [{
580
+ guard: "addOnPaste",
581
+ actions: ["setInputValue", "addTagFromPaste"]
582
+ }, { actions: "setInputValue" }]
583
+ }
584
+ },
585
+ "navigating:tag": {
586
+ tags: ["focused"],
587
+ activities: ["trackInteractOutside"],
588
+ on: {
589
+ ARROW_RIGHT: [{
590
+ guard: and("hasTags", "isInputCaretAtStart", not("isLastTagHighlighted")),
591
+ actions: "highlightNextTag"
592
+ }, { target: "focused:input" }],
593
+ ARROW_LEFT: { actions: "highlightPrevTag" },
594
+ BLUR: {
595
+ target: "idle",
596
+ actions: "clearHighlightedId"
597
+ },
598
+ ENTER: {
599
+ guard: and("isTagEditable", "hasHighlightedTag"),
600
+ target: "editing:tag",
601
+ actions: [
602
+ "setEditedId",
603
+ "initializeEditedTagValue",
604
+ "focusEditedTagInput"
605
+ ]
606
+ },
607
+ ARROW_DOWN: "focused:input",
608
+ ESCAPE: "focused:input",
609
+ TYPE: {
610
+ target: "focused:input",
611
+ actions: "setInputValue"
612
+ },
613
+ BACKSPACE: [
614
+ {
615
+ guard: "isFirstTagHighlighted",
616
+ actions: ["deleteHighlightedTag", "highlightFirstTag"]
617
+ },
618
+ {
619
+ guard: "hasHighlightedTag",
620
+ actions: ["deleteHighlightedTag", "highlightPrevTag"]
621
+ },
622
+ { actions: ["highlightLastTag"] }
623
+ ],
624
+ DELETE: {
625
+ target: "focused:input",
626
+ actions: ["deleteHighlightedTag", "highlightTagAtIndex"]
627
+ },
628
+ PASTE: [{
629
+ guard: "addOnPaste",
630
+ target: "focused:input",
631
+ actions: ["setInputValue", "addTagFromPaste"]
632
+ }, {
633
+ target: "focused:input",
634
+ actions: "setInputValue"
635
+ }]
636
+ }
637
+ },
638
+ "editing:tag": {
639
+ tags: ["editing", "focused"],
640
+ entry: "focusEditedTagInput",
641
+ activities: ["autoResize"],
642
+ on: {
643
+ TAG_INPUT_TYPE: { actions: "setEditedTagValue" },
644
+ TAG_INPUT_ESCAPE: {
645
+ target: "navigating:tag",
646
+ actions: [
647
+ "clearEditedTagValue",
648
+ "focusInput",
649
+ "clearEditedId",
650
+ "highlightTagAtIndex"
651
+ ]
652
+ },
653
+ TAG_INPUT_BLUR: [{
654
+ guard: "isInputRelatedTarget",
655
+ target: "navigating:tag",
656
+ actions: [
657
+ "clearEditedTagValue",
658
+ "clearHighlightedId",
659
+ "clearEditedId"
660
+ ]
661
+ }, {
662
+ target: "idle",
663
+ actions: [
664
+ "clearEditedTagValue",
665
+ "clearHighlightedId",
666
+ "clearEditedId",
667
+ "raiseExternalBlurEvent"
668
+ ]
669
+ }],
670
+ TAG_INPUT_ENTER: [{
671
+ guard: "isEditedTagEmpty",
672
+ target: "navigating:tag",
673
+ actions: [
674
+ "deleteHighlightedTag",
675
+ "focusInput",
676
+ "clearEditedId",
677
+ "highlightTagAtIndex"
678
+ ]
679
+ }, {
680
+ target: "navigating:tag",
681
+ actions: [
682
+ "submitEditedTagValue",
683
+ "focusInput",
684
+ "clearEditedId",
685
+ "highlightTagAtIndex"
686
+ ]
687
+ }]
688
+ }
689
+ }
690
+ }
691
+ }, {
692
+ guards: {
693
+ isInputRelatedTarget: (ctx$1, evt) => evt.relatedTarget === dom.getInputEl(ctx$1),
694
+ isAtMax: (ctx$1) => ctx$1.isAtMax,
695
+ hasHighlightedTag: (ctx$1) => ctx$1.highlightedTagId !== null,
696
+ isFirstTagHighlighted: (ctx$1) => {
697
+ const firstItemId = dom.getItemId(ctx$1, {
698
+ value: ctx$1.value[0],
699
+ index: 0
700
+ });
701
+ return firstItemId === ctx$1.highlightedTagId;
702
+ },
703
+ isEditedTagEmpty: (ctx$1) => ctx$1.editedTagValue.trim() === "",
704
+ isLastTagHighlighted: (ctx$1) => {
705
+ const lastIndex = ctx$1.value.length - 1;
706
+ const lastItemId = dom.getItemId(ctx$1, {
707
+ value: ctx$1.value[lastIndex],
708
+ index: lastIndex
709
+ });
710
+ return lastItemId === ctx$1.highlightedTagId;
711
+ },
712
+ isInputValueEmpty: (ctx$1) => ctx$1.trimmedInputValue.length === 0,
713
+ hasTags: (ctx$1) => ctx$1.value.length > 0,
714
+ allowOverflow: (ctx$1) => !!ctx$1.allowOverflow,
715
+ autoFocus: (ctx$1) => !!ctx$1.autoFocus,
716
+ addOnBlur: (ctx$1) => ctx$1.blurBehavior === "add",
717
+ clearOnBlur: (ctx$1) => ctx$1.blurBehavior === "clear",
718
+ addOnPaste: (ctx$1) => !!ctx$1.addOnPaste,
719
+ isTagEditable: (ctx$1) => !!ctx$1.editable,
720
+ isInputCaretAtStart(ctx$1) {
721
+ const input = dom.getInputEl(ctx$1);
722
+ if (!input) return false;
723
+ try {
724
+ return input.selectionStart === 0 && input.selectionEnd === 0;
725
+ } catch {
726
+ return input.value === "";
727
+ }
728
+ }
729
+ },
730
+ activities: {
731
+ trackInteractOutside(ctx$1, _evt, { send }) {
732
+ return trackInteractOutside(dom.getInputEl(ctx$1), {
733
+ exclude(target) {
734
+ return contains(dom.getRootEl(ctx$1), target);
735
+ },
736
+ onFocusOutside: ctx$1.onFocusOutside,
737
+ onPointerDownOutside: ctx$1.onPointerDownOutside,
738
+ onInteractOutside(event) {
739
+ ctx$1.onInteractOutside?.(event);
740
+ if (event.defaultPrevented) return;
741
+ send({
742
+ type: "BLUR",
743
+ src: "interact-outside"
744
+ });
745
+ }
746
+ });
747
+ },
748
+ trackFormControlState(ctx$1, _evt, { send, initialContext }) {
749
+ return trackFormControl(dom.getHiddenInputEl(ctx$1), {
750
+ onFieldsetDisabledChange(disabled) {
751
+ ctx$1.fieldsetDisabled = disabled;
752
+ },
753
+ onFormReset() {
754
+ send({
755
+ type: "SET_VALUE",
756
+ value: initialContext.value,
757
+ src: "form-reset"
758
+ });
759
+ }
760
+ });
761
+ },
762
+ autoResize(ctx$1) {
763
+ if (!ctx$1.editedTagValue || ctx$1.idx == null || !ctx$1.editable) return;
764
+ const input = dom.getTagInputEl(ctx$1, {
765
+ value: ctx$1.editedTagValue,
766
+ index: ctx$1.idx
767
+ });
768
+ return autoResizeInput(input);
769
+ },
770
+ trackLiveRegion(ctx$1) {
771
+ ctx$1.liveRegion = createLiveRegion({
772
+ level: "assertive",
773
+ document: dom.getDoc(ctx$1)
774
+ });
775
+ return () => ctx$1.liveRegion?.destroy();
776
+ }
777
+ },
778
+ actions: {
779
+ raiseInsertTagEvent(_, __, { self }) {
780
+ self.send("INSERT_TAG");
781
+ },
782
+ raiseExternalBlurEvent(_, evt, { self }) {
783
+ self.send({
784
+ type: "EXTERNAL_BLUR",
785
+ id: evt.id
786
+ });
787
+ },
788
+ dispatchChangeEvent(ctx$1) {
789
+ dom.dispatchInputEvent(ctx$1);
790
+ },
791
+ highlightNextTag(ctx$1) {
792
+ if (ctx$1.highlightedTagId == null) return;
793
+ const next = dom.getNextEl(ctx$1, ctx$1.highlightedTagId);
794
+ set.highlightedId(ctx$1, next?.id ?? null);
795
+ },
796
+ highlightFirstTag(ctx$1) {
797
+ raf(() => {
798
+ const first = dom.getFirstEl(ctx$1);
799
+ set.highlightedId(ctx$1, first?.id ?? null);
800
+ });
801
+ },
802
+ highlightLastTag(ctx$1) {
803
+ const last = dom.getLastEl(ctx$1);
804
+ set.highlightedId(ctx$1, last?.id ?? null);
805
+ },
806
+ highlightPrevTag(ctx$1) {
807
+ if (ctx$1.highlightedTagId == null) return;
808
+ const prev = dom.getPrevEl(ctx$1, ctx$1.highlightedTagId);
809
+ set.highlightedId(ctx$1, prev?.id ?? null);
810
+ },
811
+ highlightTag(ctx$1, evt) {
812
+ set.highlightedId(ctx$1, evt.id);
813
+ },
814
+ highlightTagAtIndex(ctx$1) {
815
+ raf(() => {
816
+ if (ctx$1.idx == null) return;
817
+ const tagEl = dom.getTagElAtIndex(ctx$1, ctx$1.idx);
818
+ if (tagEl == null) return;
819
+ set.highlightedId(ctx$1, tagEl.id);
820
+ ctx$1.idx = void 0;
821
+ });
822
+ },
823
+ deleteTag(ctx$1, evt) {
824
+ const index = dom.getIndexOfId(ctx$1, evt.id);
825
+ const value = ctx$1.value[index];
826
+ ctx$1.log.prev = ctx$1.log.current;
827
+ ctx$1.log.current = {
828
+ type: "delete",
829
+ value
830
+ };
831
+ set.value(ctx$1, removeAt(ctx$1.value, index));
832
+ },
833
+ deleteHighlightedTag(ctx$1) {
834
+ if (ctx$1.highlightedTagId == null) return;
835
+ const index = dom.getIndexOfId(ctx$1, ctx$1.highlightedTagId);
836
+ ctx$1.idx = index;
837
+ const value = ctx$1.value[index];
838
+ ctx$1.log.prev = ctx$1.log.current;
839
+ ctx$1.log.current = {
840
+ type: "delete",
841
+ value
842
+ };
843
+ set.value(ctx$1, removeAt(ctx$1.value, index));
844
+ },
845
+ setEditedId(ctx$1, evt) {
846
+ ctx$1.editedTagId = evt.id ?? ctx$1.highlightedTagId;
847
+ ctx$1.idx = dom.getIndexOfId(ctx$1, ctx$1.editedTagId);
848
+ },
849
+ clearEditedId(ctx$1) {
850
+ ctx$1.editedTagId = null;
851
+ },
852
+ clearEditedTagValue(ctx$1) {
853
+ ctx$1.editedTagValue = "";
854
+ },
855
+ setEditedTagValue(ctx$1, evt) {
856
+ ctx$1.editedTagValue = evt.value;
857
+ },
858
+ submitEditedTagValue(ctx$1) {
859
+ if (!ctx$1.editedTagId) return;
860
+ const index = dom.getIndexOfId(ctx$1, ctx$1.editedTagId);
861
+ set.valueAtIndex(ctx$1, index, ctx$1.editedTagValue ?? "");
862
+ ctx$1.log.prev = ctx$1.log.current;
863
+ ctx$1.log.current = {
864
+ type: "update",
865
+ value: ctx$1.editedTagValue
866
+ };
867
+ },
868
+ setValueAtIndex(ctx$1, evt) {
869
+ if (evt.value) {
870
+ ctx$1.value[evt.index] = evt.value;
871
+ ctx$1.log.prev = ctx$1.log.current;
872
+ ctx$1.log.current = {
873
+ type: "update",
874
+ value: evt.value
875
+ };
876
+ } else throw new Error("You need to provide a value for the tag");
877
+ },
878
+ initializeEditedTagValue(ctx$1) {
879
+ if (!ctx$1.editedTagId) return;
880
+ const index = dom.getIndexOfId(ctx$1, ctx$1.editedTagId);
881
+ ctx$1.editedTagValue = ctx$1.value[index];
882
+ },
883
+ focusEditedTagInput(ctx$1) {
884
+ raf(() => {
885
+ dom.getEditInputEl(ctx$1)?.select();
886
+ });
887
+ },
888
+ setInputValue(ctx$1, evt) {
889
+ set.inputValue(ctx$1, evt.value);
890
+ },
891
+ clearHighlightedId(ctx$1) {
892
+ ctx$1.highlightedTagId = null;
893
+ },
894
+ focusInput(ctx$1) {
895
+ raf(() => {
896
+ dom.getInputEl(ctx$1)?.focus();
897
+ });
898
+ },
899
+ clearInputValue(ctx$1) {
900
+ raf(() => {
901
+ set.inputValue(ctx$1, "");
902
+ });
903
+ },
904
+ syncInputValue(ctx$1) {
905
+ const inputEl = dom.getInputEl(ctx$1);
906
+ dom.setValue(inputEl, ctx$1.inputValue);
907
+ },
908
+ syncEditedTagInputValue(ctx$1, evt) {
909
+ const id = ctx$1.editedTagId || ctx$1.highlightedTagId || evt.id;
910
+ if (id == null) return;
911
+ const editTagInputEl = dom.getById(ctx$1, `${id}:input`);
912
+ dom.setValue(editTagInputEl, ctx$1.editedTagValue);
913
+ },
914
+ addTag(ctx$1, evt) {
915
+ const value = evt.value ?? ctx$1.trimmedInputValue;
916
+ const guard = ctx$1.validate?.({
917
+ inputValue: value,
918
+ value: Array.from(ctx$1.value)
919
+ });
920
+ if (guard) {
921
+ const nextValue = uniq(ctx$1.value.concat(value));
922
+ set.value(ctx$1, nextValue);
923
+ ctx$1.log.prev = ctx$1.log.current;
924
+ ctx$1.log.current = {
925
+ type: "add",
926
+ value
927
+ };
928
+ } else ctx$1.onValueInvalid?.({ reason: "invalidTag" });
929
+ },
930
+ addTagFromPaste(ctx$1) {
931
+ raf(() => {
932
+ const value = ctx$1.trimmedInputValue;
933
+ const guard = ctx$1.validate?.({
934
+ inputValue: value,
935
+ value: Array.from(ctx$1.value)
936
+ });
937
+ if (guard) {
938
+ const trimmedValue = ctx$1.delimiter ? value.split(ctx$1.delimiter).map((v) => v.trim()) : [value];
939
+ const nextValue = uniq(ctx$1.value.concat(...trimmedValue));
940
+ set.value(ctx$1, nextValue);
941
+ ctx$1.log.prev = ctx$1.log.current;
942
+ ctx$1.log.current = {
943
+ type: "paste",
944
+ values: trimmedValue
945
+ };
946
+ } else ctx$1.onValueInvalid?.({ reason: "invalidTag" });
947
+ set.inputValue(ctx$1, "");
948
+ });
949
+ },
950
+ clearTags(ctx$1) {
951
+ set.value(ctx$1, []);
952
+ ctx$1.log.prev = ctx$1.log.current;
953
+ ctx$1.log.current = { type: "clear" };
954
+ },
955
+ setValue(ctx$1, evt) {
956
+ set.value(ctx$1, evt.value);
957
+ },
958
+ invokeOnInvalid(ctx$1) {
959
+ if (ctx$1.isOverflowing) ctx$1.onValueInvalid?.({ reason: "rangeOverflow" });
960
+ },
961
+ clearLog(ctx$1) {
962
+ ctx$1.log = {
963
+ prev: null,
964
+ current: null
965
+ };
966
+ },
967
+ logHighlightedTag(ctx$1) {
968
+ if (ctx$1.highlightedTagId == null) return;
969
+ const index = dom.getIndexOfId(ctx$1, ctx$1.highlightedTagId);
970
+ ctx$1.log.prev = ctx$1.log.current;
971
+ ctx$1.log.current = {
972
+ type: "select",
973
+ value: ctx$1.value[index]
974
+ };
975
+ },
976
+ announceLog(ctx$1) {
977
+ if (!ctx$1.log.current || ctx$1.liveRegion == null) return;
978
+ const region = ctx$1.liveRegion;
979
+ const { current, prev } = ctx$1.log;
980
+ let msg;
981
+ switch (current.type) {
982
+ case "add":
983
+ msg = ctx$1.translations.tagAdded(current.value);
984
+ break;
985
+ case "delete":
986
+ msg = ctx$1.translations.tagDeleted(current.value);
987
+ break;
988
+ case "update":
989
+ msg = ctx$1.translations.tagUpdated(current.value);
990
+ break;
991
+ case "paste":
992
+ msg = ctx$1.translations.tagsPasted(current.values);
993
+ break;
994
+ case "select":
995
+ msg = ctx$1.translations.tagSelected(current.value);
996
+ if (prev?.type === "delete") msg = `${ctx$1.translations.tagDeleted(prev.value)}. ${msg}`;
997
+ else if (prev?.type === "update") msg = `${ctx$1.translations.tagUpdated(prev.value)}. ${msg}`;
998
+ break;
999
+ default: break;
1000
+ }
1001
+ if (msg) region.announce(msg);
1002
+ }
1003
+ }
1004
+ });
1005
+ }
1006
+
1007
+ //#endregion
1008
+ //#region src/props.ts
1009
+ const props = createProps()([
1010
+ "addOnPaste",
1011
+ "allowOverflow",
1012
+ "autoFocus",
1013
+ "blurBehavior",
1014
+ "delimiter",
1015
+ "dir",
1016
+ "disabled",
1017
+ "editable",
1018
+ "form",
1019
+ "getRootNode",
1020
+ "id",
1021
+ "ids",
1022
+ "inputValue",
1023
+ "invalid",
1024
+ "max",
1025
+ "maxLength",
1026
+ "name",
1027
+ "onFocusOutside",
1028
+ "onHighlightChange",
1029
+ "onInputValueChange",
1030
+ "onInteractOutside",
1031
+ "onPointerDownOutside",
1032
+ "onValueChange",
1033
+ "onValueInvalid",
1034
+ "required",
1035
+ "readOnly",
1036
+ "translations",
1037
+ "validate",
1038
+ "value"
1039
+ ]);
1040
+ const splitProps = createSplitProps(props);
1041
+ const itemProps = createProps()([
1042
+ "index",
1043
+ "disabled",
1044
+ "value"
1045
+ ]);
1046
+ const splitItemProps = createSplitProps(itemProps);
1047
+
1048
+ //#endregion
1049
+ export { anatomy, connect, itemProps, machine, props, splitItemProps, splitProps };