@destyler/dynamic 0.0.2 → 0.0.4

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/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # @destyler/dynamic
2
+
3
+ [![NPM version](https://img.shields.io/npm/v/@destyler/dynamic?color=a1b858&label=)](https://www.npmjs.com/package/@destyler/dynamic)
4
+
5
+ > inputs render tags inside an input, followed by an actual text input.
6
+
7
+ [GitHub](https://github.com/destyler/destyler) | [Documentation](https://destyler-dev.zeabur.app/)
@@ -1,20 +1,622 @@
1
1
  'use strict';
2
2
 
3
- const index = require('./index.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/visually-hidden/dist/component');
11
- require('@destyler/directives');
12
-
13
-
14
-
15
- exports.DestylerDynamicClear = index.DestylerDynamicClear;
16
- exports.DestylerDynamicInput = index.DestylerDynamicInput;
17
- exports.DestylerDynamicItem = index.DestylerDynamicItem;
18
- exports.DestylerDynamicItemDelete = index.DestylerDynamicItemDelete;
19
- exports.DestylerDynamicItemText = index.DestylerDynamicItemText;
20
- exports.DestylerDynamicRoot = index.DestylerDynamicRoot;
3
+ const vue = require('vue');
4
+ const primitive = require('@destyler/primitive');
5
+ const composition = require('@destyler/composition');
6
+ const shared = require('@destyler/shared');
7
+ const core = require('@vueuse/core');
8
+ const collection = require('@destyler/collection');
9
+ const composition$1 = require('@destyler/collection/composition');
10
+ const component = require('@destyler/visually-hidden/component');
11
+ const directives = require('@destyler/directives');
12
+
13
+ const dynamicRootProps = {
14
+ ...primitive.primitiveProps,
15
+ /**
16
+ * The controlled value of the tags input. Can be bind as `v-model`.
17
+ *
18
+ * @default () => []
19
+ */
20
+ modelValue: {
21
+ type: Array,
22
+ required: false,
23
+ default: () => []
24
+ },
25
+ /**
26
+ * The value of the tags that should be added.
27
+ * Use when you do not need to control the state of the tags input
28
+ */
29
+ defaultValue: {
30
+ type: Array,
31
+ required: false
32
+ },
33
+ /**
34
+ * When `true`, allow adding tags on paste.
35
+ * Work in conjunction with delimiter prop.
36
+ *
37
+ * @default false
38
+ */
39
+ addOnPaste: {
40
+ type: Boolean,
41
+ required: false,
42
+ default: false
43
+ },
44
+ /**
45
+ * When `true` allow adding tags on tab keydown
46
+ *
47
+ * @default false
48
+ */
49
+ addOnTab: {
50
+ type: Boolean,
51
+ required: false,
52
+ default: false
53
+ },
54
+ /**
55
+ * When `true` allow adding tags blur input
56
+ *
57
+ * @default false
58
+ */
59
+ addOnBlur: {
60
+ type: Boolean,
61
+ required: false,
62
+ default: false
63
+ },
64
+ /**
65
+ * When `true`, allow duplicated tags.
66
+ *
67
+ * @default false
68
+ */
69
+ duplicate: {
70
+ type: Boolean,
71
+ required: false,
72
+ default: false
73
+ },
74
+ /**
75
+ * When `true`, prevents the user from interacting with the tags input.
76
+ *
77
+ * @default false
78
+ */
79
+ disabled: {
80
+ type: Boolean,
81
+ required: false,
82
+ default: false
83
+ },
84
+ /**
85
+ * The character to trigger the addition of a new tag.
86
+ * Also used to split tags for `@paste` event
87
+ *
88
+ * @default ,
89
+ */
90
+ delimiter: {
91
+ type: String,
92
+ required: false,
93
+ default: ","
94
+ },
95
+ /**
96
+ s* The reading direction of the combobox when applicable.
97
+ */
98
+ dir: {
99
+ type: String,
100
+ required: false
101
+ },
102
+ /**
103
+ * Maximum number of tags.
104
+ *
105
+ * @default 0
106
+ */
107
+ max: {
108
+ type: Number,
109
+ required: false,
110
+ default: 0
111
+ },
112
+ /**
113
+ * When `true`, indicates that the user must add the
114
+ * tags input before the owning form can be submitted.
115
+ *
116
+ * @default false
117
+ */
118
+ required: {
119
+ type: Boolean,
120
+ required: false,
121
+ default: false
122
+ },
123
+ /**
124
+ * The name of the tags input submitted with its
125
+ * owning form as part of a name/value pair.
126
+ */
127
+ name: {
128
+ type: String,
129
+ required: false
130
+ },
131
+ /**
132
+ * The unique identifier of the machine.
133
+ */
134
+ id: {
135
+ type: String,
136
+ required: false
137
+ }
138
+ };
139
+ const dynamicRootEmits = {
140
+ /**
141
+ * Event handler called when the value is invalid
142
+ */
143
+ "invalid": (_payload) => true,
144
+ /**
145
+ * Event handler called when the value changes
146
+ */
147
+ "update:modelValue": (_value) => true
148
+ };
149
+ const [injectDynamicRootContext, provideDynamicRootContext] = shared.createContext("DestylerDynamicRoot");
150
+ const DynamicRoot = vue.defineComponent({
151
+ name: "DestylerDynamicRoot",
152
+ props: dynamicRootProps,
153
+ emits: dynamicRootEmits,
154
+ slots: Object,
155
+ setup(props, { emit }) {
156
+ const { addOnPaste, disabled, delimiter, max, id, dir: propDir, addOnBlur, addOnTab } = vue.toRefs(props);
157
+ const dir = composition.useDirection(propDir);
158
+ const modelValue = composition.useVModel(props, "modelValue", emit, {
159
+ defaultValue: props.defaultValue,
160
+ passive: true,
161
+ deep: true
162
+ });
163
+ const { forwardRef, currentElement } = composition.useForwardExpose();
164
+ const { focused } = core.useFocusWithin(currentElement);
165
+ const isFormControl = composition.useFormControl(currentElement);
166
+ const { getItems } = composition$1.createCollection();
167
+ const selectedElement = vue.ref();
168
+ const isInvalidInput = vue.ref(false);
169
+ provideDynamicRootContext({
170
+ modelValue,
171
+ onAddValue: (payload) => {
172
+ if (modelValue.value.length >= max.value && !!max.value) {
173
+ emit("invalid", payload);
174
+ return false;
175
+ }
176
+ if (props.duplicate) {
177
+ modelValue.value.push(payload);
178
+ return true;
179
+ } else {
180
+ const exist = modelValue.value.includes(payload);
181
+ if (!exist) {
182
+ modelValue.value.push(payload);
183
+ return true;
184
+ } else {
185
+ isInvalidInput.value = true;
186
+ }
187
+ }
188
+ emit("invalid", payload);
189
+ return false;
190
+ },
191
+ onRemoveValue: (index) => {
192
+ if (index !== -1)
193
+ modelValue.value.splice(index, 1);
194
+ },
195
+ onInputKeydown: (event) => {
196
+ const target = event.target;
197
+ const collection = getItems().map((i) => i.ref).filter((i) => i.dataset.disabled !== "");
198
+ if (!collection.length)
199
+ return;
200
+ const lastTag = collection.at(-1);
201
+ switch (event.key) {
202
+ case "Delete":
203
+ case "Backspace": {
204
+ if (target.selectionStart !== 0 || target.selectionEnd !== 0)
205
+ break;
206
+ if (selectedElement.value) {
207
+ const index = collection.findIndex((i) => i === selectedElement.value);
208
+ modelValue.value.splice(index, 1);
209
+ selectedElement.value = selectedElement.value === lastTag ? collection.at(index - 1) : collection.at(index + 1);
210
+ event.preventDefault();
211
+ } else if (event.key === "Backspace") {
212
+ selectedElement.value = lastTag;
213
+ event.preventDefault();
214
+ }
215
+ break;
216
+ }
217
+ case "Home":
218
+ case "End":
219
+ case "ArrowRight":
220
+ case "ArrowLeft": {
221
+ const isArrowRight = event.key === "ArrowRight" && dir.value === "ltr" || event.key === "ArrowLeft" && dir.value === "rtl";
222
+ const isArrowLeft = !isArrowRight;
223
+ if (target.selectionStart !== 0 || target.selectionEnd !== 0)
224
+ break;
225
+ if (isArrowLeft && !selectedElement.value) {
226
+ selectedElement.value = lastTag;
227
+ event.preventDefault();
228
+ } else if (isArrowRight && lastTag && selectedElement.value === lastTag) {
229
+ selectedElement.value = void 0;
230
+ event.preventDefault();
231
+ } else if (selectedElement.value) {
232
+ const el = composition.useArrowNavigation(event, selectedElement.value, void 0, {
233
+ itemsArray: collection,
234
+ loop: false,
235
+ dir: dir.value
236
+ });
237
+ if (el)
238
+ selectedElement.value = el;
239
+ event.preventDefault();
240
+ }
241
+ break;
242
+ }
243
+ case "ArrowUp":
244
+ case "ArrowDown": {
245
+ if (selectedElement.value)
246
+ event.preventDefault();
247
+ break;
248
+ }
249
+ default: {
250
+ selectedElement.value = void 0;
251
+ }
252
+ }
253
+ },
254
+ selectedElement,
255
+ isInvalidInput,
256
+ addOnPaste,
257
+ addOnBlur,
258
+ addOnTab,
259
+ dir,
260
+ disabled,
261
+ delimiter,
262
+ max,
263
+ id
264
+ });
265
+ return {
266
+ forwardRef,
267
+ dir,
268
+ isInvalidInput,
269
+ disabled,
270
+ focused,
271
+ modelValue,
272
+ isFormControl
273
+ };
274
+ },
275
+ render() {
276
+ return vue.h(collection.CollectionSlot, {}, () => vue.h(primitive.Primitive, {
277
+ "ref": (el) => this.forwardRef(el),
278
+ "dir": this.dir,
279
+ "as": this.$props.as,
280
+ "asChild": this.$props.asChild,
281
+ "data-invalid": this.isInvalidInput ? "" : void 0,
282
+ "data-disabled": this.disabled ? "" : void 0,
283
+ "data-focused": this.focused ? "" : void 0
284
+ }, () => [
285
+ this.$slots.default?.({ modelValue: this.modelValue }),
286
+ this.isFormControl && this.$props.name ? vue.h(component.VisuallyHiddenInput, {
287
+ name: this.$props.name,
288
+ value: this.modelValue,
289
+ required: this.required,
290
+ disabled: this.disabled
291
+ }) : null
292
+ ]));
293
+ }
294
+ });
295
+
296
+ const dynamicClearProps = {
297
+ ...primitive.primitiveProps,
298
+ /**
299
+ * @default button
300
+ */
301
+ as: {
302
+ ...primitive.primitiveProps.as,
303
+ default: "button"
304
+ }
305
+ };
306
+ const DynamicClear = vue.defineComponent({
307
+ name: "DestylerDynamicClear",
308
+ props: dynamicClearProps,
309
+ setup() {
310
+ composition.useForwardExpose();
311
+ const context = injectDynamicRootContext();
312
+ function handleCancel() {
313
+ if (context.disabled.value)
314
+ return;
315
+ context.modelValue.value = [];
316
+ }
317
+ return {
318
+ context,
319
+ handleCancel
320
+ };
321
+ },
322
+ render() {
323
+ return vue.h(primitive.Primitive, vue.mergeProps(this.$props, {
324
+ "type": this.$props.as === "button" ? "button" : void 0,
325
+ "data-disabled": this.context.disabled.value ? "" : void 0,
326
+ "onClick": () => {
327
+ this.handleCancel();
328
+ }
329
+ }), () => this.$slots.default?.());
330
+ }
331
+ });
332
+
333
+ const dynamicInputProps = {
334
+ ...primitive.primitiveProps,
335
+ /**
336
+ * @default input
337
+ */
338
+ as: {
339
+ ...primitive.primitiveProps.as,
340
+ default: "input"
341
+ },
342
+ /**
343
+ * The placeholder character to use for empty tags input.
344
+ */
345
+ placeholder: {
346
+ type: String,
347
+ required: false
348
+ },
349
+ /**
350
+ * Focus on element when mounted.
351
+ */
352
+ autoFocus: {
353
+ type: Boolean,
354
+ required: false
355
+ },
356
+ /**
357
+ * Maximum number of character allowed.
358
+ */
359
+ maxLength: {
360
+ type: Number,
361
+ required: false
362
+ }
363
+ };
364
+ const DynamicInput = vue.defineComponent({
365
+ name: "DestylerDynamicInput",
366
+ props: dynamicInputProps,
367
+ setup(props) {
368
+ const context = injectDynamicRootContext();
369
+ const { forwardRef, currentElement } = composition.useForwardExpose();
370
+ function handleBlur(event) {
371
+ if (!context.addOnBlur.value)
372
+ return;
373
+ const target = event.target;
374
+ if (!target.value)
375
+ return;
376
+ const isAdded = context.onAddValue(target.value);
377
+ if (isAdded)
378
+ target.value = "";
379
+ }
380
+ function handleTab(event) {
381
+ if (!context.addOnTab.value)
382
+ return;
383
+ handleCustomKeydown(event);
384
+ }
385
+ async function handleCustomKeydown(event) {
386
+ await vue.nextTick();
387
+ if (event.defaultPrevented)
388
+ return;
389
+ const target = event.target;
390
+ if (!target.value)
391
+ return;
392
+ const isAdded = context.onAddValue(target.value);
393
+ if (isAdded)
394
+ target.value = "";
395
+ event.preventDefault();
396
+ }
397
+ function handleInput(event) {
398
+ context.isInvalidInput.value = false;
399
+ const delimiter = context.delimiter.value;
400
+ if (delimiter === event.data) {
401
+ const target = event.target;
402
+ target.value = target.value.replaceAll(delimiter, "");
403
+ const isAdded = context.onAddValue(target.value);
404
+ if (isAdded)
405
+ target.value = "";
406
+ }
407
+ }
408
+ function handlePaste(event) {
409
+ if (context.addOnPaste.value) {
410
+ event.preventDefault();
411
+ const clipboardData = event.clipboardData;
412
+ if (!clipboardData)
413
+ return;
414
+ const value = clipboardData.getData("text");
415
+ if (context.delimiter.value) {
416
+ const splittedValue = value.split(context.delimiter.value);
417
+ splittedValue.forEach((v) => {
418
+ context.onAddValue(v);
419
+ });
420
+ } else {
421
+ context.onAddValue(value);
422
+ }
423
+ }
424
+ }
425
+ vue.onMounted(() => {
426
+ const inputEl = currentElement.value.nodeName === "INPUT" ? currentElement.value : currentElement.value.querySelector("input");
427
+ if (!inputEl)
428
+ return;
429
+ setTimeout(() => {
430
+ if (props.autoFocus)
431
+ inputEl?.focus();
432
+ }, 1);
433
+ });
434
+ return {
435
+ forwardRef,
436
+ context,
437
+ handleInput,
438
+ handlePaste,
439
+ handleCustomKeydown,
440
+ handleBlur,
441
+ handleTab
442
+ };
443
+ },
444
+ render() {
445
+ return vue.withDirectives(vue.h(primitive.Primitive, {
446
+ "ref": (el) => this.forwardRef(el),
447
+ "type": "text",
448
+ "autocomplete": "off",
449
+ "autocorrect": "off",
450
+ "autocapitalize": "off",
451
+ "as": this.$props.as,
452
+ "asChild": this.$props.asChild,
453
+ "maxlength": this.$props.maxLength,
454
+ "placeholder": this.$props.placeholder,
455
+ "disabled": this.context.disabled.value,
456
+ "data-invalid": this.context.isInvalidInput.value ? "" : void 0,
457
+ "onInput": (event) => {
458
+ this.handleInput(event);
459
+ },
460
+ "onKeydown": [
461
+ vue.withKeys(this.handleCustomKeydown, ["enter"]),
462
+ vue.withKeys(this.handleTab, ["tab"]),
463
+ this.context.onInputKeydown
464
+ ],
465
+ "onBlur": (event) => {
466
+ this.handleBlur(event);
467
+ },
468
+ "onPaste": (event) => {
469
+ this.handlePaste(event);
470
+ }
471
+ }, () => this.$slots.default?.()), [
472
+ [directives.BindOnceDirective, { id: this.context.id?.value }]
473
+ ]);
474
+ }
475
+ });
476
+
477
+ const dynamicItemProps = {
478
+ ...primitive.primitiveProps,
479
+ /**
480
+ * Value associated with the tags
481
+ */
482
+ value: {
483
+ type: String,
484
+ required: true
485
+ },
486
+ /**
487
+ * When `true`, prevents the user from interacting with the tags input.
488
+ */
489
+ disabled: {
490
+ type: Boolean,
491
+ required: false
492
+ }
493
+ };
494
+ const [injectDynamicItemContext, provideDynamicItemContext] = shared.createContext("DestylerDynamicItem");
495
+ const DynamicItem = vue.defineComponent({
496
+ name: "DestylerDynamicItem",
497
+ props: dynamicItemProps,
498
+ setup(props) {
499
+ const { value } = vue.toRefs(props);
500
+ const context = injectDynamicRootContext();
501
+ const { forwardRef, currentElement } = composition.useForwardExpose();
502
+ const isSelected = vue.computed(() => context.selectedElement.value === currentElement.value);
503
+ const textId = composition.useId();
504
+ const disabled = vue.computed(() => props.disabled || context.disabled.value);
505
+ provideDynamicItemContext({
506
+ value,
507
+ isSelected,
508
+ disabled,
509
+ textId
510
+ });
511
+ return {
512
+ forwardRef,
513
+ textId,
514
+ isSelected,
515
+ disabled
516
+ };
517
+ },
518
+ render() {
519
+ return vue.h(collection.CollectionItem, {}, () => vue.h(primitive.Primitive, {
520
+ "ref": this.forwardRef,
521
+ "as": this.$props.as,
522
+ "asChild": this.$props.asChild,
523
+ "aria-labelledby": this.textId,
524
+ "aria-current": this.isSelected,
525
+ "data-disabled": this.disabled ? "" : void 0,
526
+ "data-state": this.isSelected ? "active" : "inactive"
527
+ }, () => this.$slots.default?.()));
528
+ }
529
+ });
530
+
531
+ const dynamicItemDeleteProps = {
532
+ ...primitive.primitiveProps,
533
+ /**
534
+ * @default button
535
+ */
536
+ as: {
537
+ ...primitive.primitiveProps.as,
538
+ default: "button"
539
+ }
540
+ };
541
+ const DynamicItemDelete = vue.defineComponent({
542
+ name: "DestylerDynamicItemDelete",
543
+ props: dynamicItemDeleteProps,
544
+ setup() {
545
+ composition.useForwardExpose();
546
+ const context = injectDynamicRootContext();
547
+ const itemContext = injectDynamicItemContext();
548
+ const disabled = vue.computed(() => itemContext.disabled?.value || context.disabled.value);
549
+ function handleDelete() {
550
+ if (disabled.value)
551
+ return;
552
+ const index = context.modelValue.value.findIndex((i) => i === itemContext.value.value);
553
+ context.onRemoveValue(index);
554
+ }
555
+ return {
556
+ context,
557
+ itemContext,
558
+ disabled,
559
+ handleDelete
560
+ };
561
+ },
562
+ render() {
563
+ return vue.h(primitive.Primitive, vue.mergeProps(this.$props, {
564
+ "tabindex": "-1",
565
+ "aria-labelledby": this.itemContext.textId,
566
+ "aria-current": this.itemContext.isSelected.value,
567
+ "data-state": this.itemContext.isSelected.value ? "active" : "inactive",
568
+ "data-disabled": this.disabled ? "" : void 0,
569
+ "type": this.$props.as === "button" ? "button" : void 0,
570
+ "onClick": () => {
571
+ this.handleDelete();
572
+ }
573
+ }), () => this.$slots.default?.());
574
+ }
575
+ });
576
+
577
+ const dynamicItemTextProps = {
578
+ ...primitive.primitiveProps,
579
+ /**
580
+ * @default span
581
+ */
582
+ as: {
583
+ ...primitive.primitiveProps.as,
584
+ default: "span"
585
+ }
586
+ };
587
+ const DynamicItemText = vue.defineComponent({
588
+ name: "DestylerDynamicItemText",
589
+ props: dynamicItemTextProps,
590
+ slots: Object,
591
+ setup() {
592
+ const itemContext = injectDynamicItemContext();
593
+ composition.useForwardExpose();
594
+ itemContext.textId || (itemContext.textId = composition.useId(void 0, "destyler-tags-input-item-text"));
595
+ return {
596
+ itemContext
597
+ };
598
+ },
599
+ render() {
600
+ return vue.withDirectives(vue.h(
601
+ primitive.Primitive,
602
+ this.$props,
603
+ () => this.$slots.default ? this.$slots.default?.({ text: this.itemContext.value.value }) : this.itemContext.value.value
604
+ ), [
605
+ [directives.BindOnceDirective, { id: this.itemContext.textId }]
606
+ ]);
607
+ }
608
+ });
609
+
610
+ exports.DynamicClear = DynamicClear;
611
+ exports.DynamicInput = DynamicInput;
612
+ exports.DynamicItem = DynamicItem;
613
+ exports.DynamicItemDelete = DynamicItemDelete;
614
+ exports.DynamicItemText = DynamicItemText;
615
+ exports.DynamicRoot = DynamicRoot;
616
+ exports.dynamicClearProps = dynamicClearProps;
617
+ exports.dynamicInputProps = dynamicInputProps;
618
+ exports.dynamicItemDeleteProps = dynamicItemDeleteProps;
619
+ exports.dynamicItemProps = dynamicItemProps;
620
+ exports.dynamicItemTextProps = dynamicItemTextProps;
621
+ exports.dynamicRootEmits = dynamicRootEmits;
622
+ exports.dynamicRootProps = dynamicRootProps;