@destyler/dynamic 0.0.5 → 0.0.7-beta.1

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