bard-tag_field 0.5.1 → 0.5.2
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.
- checksums.yaml +4 -4
- data/CLAUDE.md +9 -8
- data/Rakefile +1 -1
- data/app/assets/javascripts/input-tag.js +1 -0
- data/input-tag/.gitignore +6 -2
- data/input-tag/CLAUDE.md +87 -0
- data/input-tag/LICENSE +21 -0
- data/input-tag/README.md +135 -0
- data/input-tag/TESTING.md +99 -0
- data/input-tag/bun.lock +821 -0
- data/input-tag/index.html +331 -0
- data/input-tag/package.json +52 -8
- data/input-tag/rollup.config.js +4 -4
- data/input-tag/src/input-tag.js +849 -0
- data/input-tag/src/taggle.js +546 -0
- data/input-tag/test/api-methods.test.js +684 -0
- data/input-tag/test/autocomplete.test.js +615 -0
- data/input-tag/test/basic-functionality.test.js +567 -0
- data/input-tag/test/dom-mutation.test.js +466 -0
- data/input-tag/test/edge-cases.test.js +524 -0
- data/input-tag/test/events.test.js +425 -0
- data/input-tag/test/form-integration.test.js +447 -0
- data/input-tag/test/input-tag.test.js +90 -0
- data/input-tag/test/lib/fail-only.mjs +24 -0
- data/input-tag/test/lib/test-utils.js +187 -0
- data/input-tag/test/nested-datalist.test.js +328 -0
- data/input-tag/test/value-label-separation.test.js +357 -0
- data/input-tag/web-test-runner.config.mjs +20 -0
- data/lib/bard/tag_field/version.rb +1 -1
- metadata +23 -4
- data/app/assets/javascripts/input-tag.js +0 -1859
- data/input-tag/bun.lockb +0 -0
- data/input-tag/index.js +0 -1
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Taggle - dependency-less tagging library
|
|
3
|
+
* @author Sean Coker <hello@sean.is>
|
|
4
|
+
* @version 1.15.0 (modified)
|
|
5
|
+
* @license MIT
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/////////////////////
|
|
9
|
+
// Default options //
|
|
10
|
+
/////////////////////
|
|
11
|
+
|
|
12
|
+
const BACKSPACE = 8;
|
|
13
|
+
const DELETE = 46;
|
|
14
|
+
const COMMA = 188;
|
|
15
|
+
const TAB = 9;
|
|
16
|
+
const ENTER = 13;
|
|
17
|
+
|
|
18
|
+
const DEFAULTS = {
|
|
19
|
+
/**
|
|
20
|
+
* Class added to the container div when focused
|
|
21
|
+
* @type {String}
|
|
22
|
+
*/
|
|
23
|
+
containerFocusClass: 'active',
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Spaces will be removed from the tags by default
|
|
27
|
+
* @type {Boolean}
|
|
28
|
+
*/
|
|
29
|
+
trimTags: true,
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Limit the number of tags that can be added
|
|
33
|
+
* @type {Number}
|
|
34
|
+
*/
|
|
35
|
+
maxTags: null,
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Placeholder string to be placed in an empty taggle field
|
|
39
|
+
* @type {String}
|
|
40
|
+
*/
|
|
41
|
+
placeholder: 'Enter tags...',
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Keycodes that will add a tag
|
|
45
|
+
* @type {Array}
|
|
46
|
+
*/
|
|
47
|
+
submitKeys: [COMMA, TAB, ENTER],
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Preserve case of tags being added ie
|
|
51
|
+
* "tag" is different than "Tag"
|
|
52
|
+
* @type {Boolean}
|
|
53
|
+
*/
|
|
54
|
+
preserveCase: false,
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Function hook called when a tag is added
|
|
58
|
+
* @param {Event} event Event triggered when tag was added
|
|
59
|
+
* @param {String} tag The tag added
|
|
60
|
+
*/
|
|
61
|
+
onTagAdd: () => {},
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Function hook called when a tag is removed
|
|
65
|
+
* @param {Event} event Event triggered when tag was removed
|
|
66
|
+
* @param {String} tag The tag removed
|
|
67
|
+
*/
|
|
68
|
+
onTagRemove: () => {}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
//////////////////////
|
|
72
|
+
// Helper functions //
|
|
73
|
+
//////////////////////
|
|
74
|
+
function _clamp(val, min, max) {
|
|
75
|
+
return Math.min(Math.max(val, min), max);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Taggle ES6 Class - Modern tagging library
|
|
80
|
+
*/
|
|
81
|
+
class Taggle {
|
|
82
|
+
/**
|
|
83
|
+
* Constructor
|
|
84
|
+
* @param {Mixed} el ID of an element or the actual element
|
|
85
|
+
* @param {Object} options
|
|
86
|
+
*/
|
|
87
|
+
constructor(el, options) {
|
|
88
|
+
this.settings = Object.assign({}, DEFAULTS, options);
|
|
89
|
+
this.measurements = {
|
|
90
|
+
container: {
|
|
91
|
+
rect: null,
|
|
92
|
+
style: null,
|
|
93
|
+
padding: null
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
this.container = el;
|
|
97
|
+
this.tag = {
|
|
98
|
+
values: [],
|
|
99
|
+
elements: []
|
|
100
|
+
};
|
|
101
|
+
this.inputContainer = options.inputContainer;
|
|
102
|
+
this.input = document.createElement('input');
|
|
103
|
+
this.sizer = document.createElement('div');
|
|
104
|
+
this.pasting = false;
|
|
105
|
+
this.placeholder = null;
|
|
106
|
+
|
|
107
|
+
if (this.settings.placeholder) {
|
|
108
|
+
this.placeholder = document.createElement('span');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
this._backspacePressed = false;
|
|
112
|
+
this._inputPosition = 0;
|
|
113
|
+
this._setMeasurements();
|
|
114
|
+
this._setupTextarea();
|
|
115
|
+
this._attachEvents();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Gets all the layout measurements up front
|
|
120
|
+
*/
|
|
121
|
+
_setMeasurements() {
|
|
122
|
+
this.measurements.container.rect = this.container.getBoundingClientRect();
|
|
123
|
+
const style = window.getComputedStyle(this.container);
|
|
124
|
+
this.measurements.container.style = style;
|
|
125
|
+
|
|
126
|
+
const lpad = parseInt(style.paddingLeft, 10);
|
|
127
|
+
const rpad = parseInt(style.paddingRight, 10);
|
|
128
|
+
const lborder = parseInt(style.borderLeftWidth, 10);
|
|
129
|
+
const rborder = parseInt(style.borderRightWidth, 10);
|
|
130
|
+
|
|
131
|
+
this.measurements.container.padding = lpad + rpad + lborder + rborder;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Setup the div container for tags to be entered
|
|
136
|
+
*/
|
|
137
|
+
_setupTextarea() {
|
|
138
|
+
this.input.type = 'text';
|
|
139
|
+
// Make sure no left/right padding messes with the input sizing
|
|
140
|
+
this.input.style.paddingLeft = 0;
|
|
141
|
+
this.input.style.paddingRight = 0;
|
|
142
|
+
this.input.className = 'taggle_input';
|
|
143
|
+
this.input.tabIndex = 1;
|
|
144
|
+
this.sizer.className = 'taggle_sizer';
|
|
145
|
+
|
|
146
|
+
[...this.container.children].filter(child => child.tagName === 'TAG-OPTION').forEach(tagOption => {
|
|
147
|
+
this.tag.values.push(tagOption.value);
|
|
148
|
+
this.tag.elements.push(tagOption);
|
|
149
|
+
this._inputPosition = _clamp(this._inputPosition + 1, 0, this.tag.values.length);
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
if (this.placeholder) {
|
|
154
|
+
this._hidePlaceholder();
|
|
155
|
+
this.placeholder.classList.add('taggle_placeholder');
|
|
156
|
+
this.container.appendChild(this.placeholder);
|
|
157
|
+
this.placeholder.textContent = this.settings.placeholder;
|
|
158
|
+
|
|
159
|
+
if (!this.tag.values.length) {
|
|
160
|
+
this._showPlaceholder();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
const div = document.createElement('div');
|
|
166
|
+
div.appendChild(this.input);
|
|
167
|
+
div.appendChild(this.sizer);
|
|
168
|
+
this.inputContainer.appendChild(div);
|
|
169
|
+
const fontSize = window.getComputedStyle(this.input).fontSize;
|
|
170
|
+
this.sizer.style.fontSize = fontSize;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Attaches neccessary events
|
|
175
|
+
*/
|
|
176
|
+
_attachEvents() {
|
|
177
|
+
if (this._eventsAttached) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
this._eventsAttached = true;
|
|
181
|
+
|
|
182
|
+
this._handleContainerClick = () => this.input.focus();
|
|
183
|
+
this.container.addEventListener('click', this._handleContainerClick);
|
|
184
|
+
|
|
185
|
+
this._handleFocus = this._setFocusStateForContainer.bind(this);
|
|
186
|
+
this._handleBlur = this._blurEvent.bind(this);
|
|
187
|
+
this._handleKeydown = this._keydownEvents.bind(this);
|
|
188
|
+
this._handleKeyup = this._keyupEvents.bind(this);
|
|
189
|
+
|
|
190
|
+
this.input.addEventListener('focus', this._handleFocus);
|
|
191
|
+
this.input.addEventListener('blur', this._handleBlur);
|
|
192
|
+
this.input.addEventListener('keydown', this._handleKeydown);
|
|
193
|
+
this.input.addEventListener('keyup', this._handleKeyup);
|
|
194
|
+
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
_detachEvents() {
|
|
199
|
+
if (!this._eventsAttached) {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
this._eventsAttached = false;
|
|
203
|
+
|
|
204
|
+
this.container.removeEventListener('click', this._handleContainerClick);
|
|
205
|
+
this.input.removeEventListener('focus', this._handleFocus);
|
|
206
|
+
this.input.removeEventListener('blur', this._handleBlur);
|
|
207
|
+
this.input.removeEventListener('keydown', this._handleKeydown);
|
|
208
|
+
this.input.removeEventListener('keyup', this._handleKeyup);
|
|
209
|
+
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Returns whether or not the specified tag text can be added
|
|
215
|
+
* @param {Event} e event causing the potentially added tag
|
|
216
|
+
* @param {String} text tag value
|
|
217
|
+
* @return {Boolean}
|
|
218
|
+
*/
|
|
219
|
+
_canAdd(e, text) {
|
|
220
|
+
if (!text) {
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
const limit = this.settings.maxTags;
|
|
224
|
+
if (limit !== null && limit <= this.getTagValues().length) {
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Check for duplicates
|
|
229
|
+
return this.tag.values.indexOf(text) === -1;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Appends tag with its corresponding input to the list
|
|
234
|
+
* @param {Event} e
|
|
235
|
+
* @param {String} text
|
|
236
|
+
* @param {Number} index
|
|
237
|
+
*/
|
|
238
|
+
_add(e, text, index) {
|
|
239
|
+
let values = text || '';
|
|
240
|
+
const delimiter = ',';
|
|
241
|
+
|
|
242
|
+
if (typeof text !== 'string') {
|
|
243
|
+
values = this.input.value;
|
|
244
|
+
|
|
245
|
+
if (this.settings.trimTags) {
|
|
246
|
+
if (values[0] === delimiter) {
|
|
247
|
+
values = values.replace(delimiter, '');
|
|
248
|
+
}
|
|
249
|
+
values = values.trim();
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
values.split(delimiter).map(val => {
|
|
254
|
+
if (this.settings.trimTags) {
|
|
255
|
+
val = val.trim();
|
|
256
|
+
}
|
|
257
|
+
return this._formatTag(val);
|
|
258
|
+
}).forEach(val => {
|
|
259
|
+
if (!this._canAdd(e, val)) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const currentTagLength = this.tag.values.length;
|
|
264
|
+
const tagIndex = _clamp(index || currentTagLength, 0, currentTagLength);
|
|
265
|
+
const tagOption = this._createTag(val, tagIndex);
|
|
266
|
+
this.container.append(tagOption);
|
|
267
|
+
|
|
268
|
+
val = this.tag.values[tagIndex];
|
|
269
|
+
|
|
270
|
+
this.settings.onTagAdd(e, val);
|
|
271
|
+
|
|
272
|
+
this.input.value = '';
|
|
273
|
+
this._setMeasurements();
|
|
274
|
+
this._setInputWidth();
|
|
275
|
+
this._setFocusStateForContainer();
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Removes last tag if it has already been probed
|
|
281
|
+
* @param {Event} e
|
|
282
|
+
*/
|
|
283
|
+
_checkPrevOrNextTag(e) {
|
|
284
|
+
const taggles = this.container.querySelectorAll('tag-option');
|
|
285
|
+
const prevTagIndex = _clamp(this._inputPosition - 1, 0, taggles.length - 1);
|
|
286
|
+
const nextTagIndex = _clamp(this._inputPosition, 0, taggles.length - 1);
|
|
287
|
+
let index = prevTagIndex;
|
|
288
|
+
|
|
289
|
+
if (e.keyCode === DELETE) {
|
|
290
|
+
index = nextTagIndex;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const targetTaggle = taggles[index];
|
|
294
|
+
const hotClass = 'taggle_hot';
|
|
295
|
+
const isDeleteOrBackspace = [BACKSPACE, DELETE].includes(e.keyCode);
|
|
296
|
+
|
|
297
|
+
// prevent holding backspace from deleting all tags
|
|
298
|
+
if (this.input.value === '' && isDeleteOrBackspace && !this._backspacePressed) {
|
|
299
|
+
if (targetTaggle.classList.contains(hotClass)) {
|
|
300
|
+
this._backspacePressed = true;
|
|
301
|
+
this._remove(targetTaggle, e);
|
|
302
|
+
this._setMeasurements();
|
|
303
|
+
this._setInputWidth();
|
|
304
|
+
this._setFocusStateForContainer();
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
targetTaggle.classList.add(hotClass);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
else if (targetTaggle.classList.contains(hotClass)) {
|
|
311
|
+
targetTaggle.classList.remove(hotClass);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Setter for the hidden input.
|
|
317
|
+
* @param {Number} width
|
|
318
|
+
*/
|
|
319
|
+
_setInputWidth() {
|
|
320
|
+
const width = this.sizer.getBoundingClientRect().width;
|
|
321
|
+
const max = this.measurements.container.rect.width - this.measurements.container.padding;
|
|
322
|
+
const size = parseInt(this.sizer.style.fontSize, 10);
|
|
323
|
+
|
|
324
|
+
// 1.5 just seems to be a good multiplier here
|
|
325
|
+
const newWidth = Math.round(_clamp(width + (size * 1.5), 10, max));
|
|
326
|
+
|
|
327
|
+
this.input.style.width = `${newWidth}px`;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Handles focus state of div container.
|
|
332
|
+
*/
|
|
333
|
+
_setFocusStateForContainer() {
|
|
334
|
+
this._setMeasurements();
|
|
335
|
+
this._setInputWidth();
|
|
336
|
+
|
|
337
|
+
if (!this.container.classList.contains(this.settings.containerFocusClass)) {
|
|
338
|
+
this.container.classList.add(this.settings.containerFocusClass);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
this._hidePlaceholder();
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Runs all the events that need to happen on a blur
|
|
346
|
+
* @param {Event} e
|
|
347
|
+
*/
|
|
348
|
+
_blurEvent(e) {
|
|
349
|
+
if (this.container.classList.contains(this.settings.containerFocusClass)) {
|
|
350
|
+
this.container.classList.remove(this.settings.containerFocusClass);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (!this.tag.values.length && !this.input.value) {
|
|
354
|
+
this._showPlaceholder();
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Runs all the events that need to run on keydown
|
|
360
|
+
* @param {Event} e
|
|
361
|
+
*/
|
|
362
|
+
_keydownEvents(e) {
|
|
363
|
+
const key = e.keyCode;
|
|
364
|
+
this.pasting = false;
|
|
365
|
+
|
|
366
|
+
this._setInputWidth();
|
|
367
|
+
|
|
368
|
+
if (key === 86 && e.metaKey) {
|
|
369
|
+
this.pasting = true;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (this.settings.submitKeys.includes(key) && this.input.value !== '') {
|
|
373
|
+
this._confirmValidTagEvent(e);
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (this.tag.values.length) {
|
|
378
|
+
this._checkPrevOrNextTag(e);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Runs all the events that need to run on keyup
|
|
384
|
+
* @param {Event} e
|
|
385
|
+
*/
|
|
386
|
+
_keyupEvents(e) {
|
|
387
|
+
this._backspacePressed = false;
|
|
388
|
+
|
|
389
|
+
this.sizer.textContent = this.input.value;
|
|
390
|
+
|
|
391
|
+
// If we break to a new line because the text is too long
|
|
392
|
+
// and decide to delete everything, we should resize the input
|
|
393
|
+
// so it falls back inline
|
|
394
|
+
if (!this.input.value) {
|
|
395
|
+
this._setInputWidth();
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (this.pasting && this.input.value !== '') {
|
|
399
|
+
this._add(e);
|
|
400
|
+
this.pasting = false;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Confirms the inputted value to be converted to a tag
|
|
406
|
+
* @param {Event} e
|
|
407
|
+
*/
|
|
408
|
+
_confirmValidTagEvent(e) {
|
|
409
|
+
// prevents from jumping out of textarea
|
|
410
|
+
e.preventDefault();
|
|
411
|
+
|
|
412
|
+
this._add(e, null, this._inputPosition);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
_createTag(text, index) {
|
|
416
|
+
const tagOption = document.createElement('tag-option');
|
|
417
|
+
|
|
418
|
+
text = this._formatTag(text);
|
|
419
|
+
tagOption.textContent = text;
|
|
420
|
+
tagOption.setAttribute('value', text);
|
|
421
|
+
|
|
422
|
+
this.tag.values.splice(index, 0, text);
|
|
423
|
+
this.tag.elements.splice(index, 0, tagOption);
|
|
424
|
+
this._inputPosition = _clamp(this._inputPosition + 1, 0, this.tag.values.length);
|
|
425
|
+
|
|
426
|
+
return tagOption;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
_showPlaceholder() {
|
|
430
|
+
if (this.placeholder) {
|
|
431
|
+
this.placeholder.style.opacity = 1;
|
|
432
|
+
this.placeholder.setAttribute('aria-hidden', 'false');
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
_hidePlaceholder() {
|
|
437
|
+
if (this.placeholder) {
|
|
438
|
+
this.placeholder.style.opacity = 0;
|
|
439
|
+
this.placeholder.setAttribute('aria-hidden', 'true');
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Removes tag from the tags collection
|
|
445
|
+
* @param {HTMLElement} tagOption List item to remove
|
|
446
|
+
* @param {Event} e
|
|
447
|
+
*/
|
|
448
|
+
_remove(tagOption, e) {
|
|
449
|
+
const index = this.tag.elements.indexOf(tagOption);
|
|
450
|
+
if (index === -1) return;
|
|
451
|
+
|
|
452
|
+
const text = this.tag.values[index];
|
|
453
|
+
|
|
454
|
+
tagOption.remove();
|
|
455
|
+
this.tag.elements.splice(index, 1);
|
|
456
|
+
this.tag.values.splice(index, 1);
|
|
457
|
+
this.settings.onTagRemove(e, text);
|
|
458
|
+
|
|
459
|
+
if (index < this._inputPosition) {
|
|
460
|
+
this._inputPosition = _clamp(this._inputPosition - 1, 0, this.tag.values.length);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
this._setFocusStateForContainer();
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Format the text for a tag
|
|
468
|
+
* @param {String} text Tag text
|
|
469
|
+
* @return {String}
|
|
470
|
+
*/
|
|
471
|
+
_formatTag(text) {
|
|
472
|
+
return this.settings.preserveCase ? text : text.toLowerCase();
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// @todo
|
|
476
|
+
// @deprecated use getTags().values
|
|
477
|
+
getTagValues() {
|
|
478
|
+
return [...this.tag.values];
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
getInput() {
|
|
482
|
+
return this.input;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
add(text, index) {
|
|
486
|
+
const isArr = Array.isArray(text);
|
|
487
|
+
|
|
488
|
+
if (isArr) {
|
|
489
|
+
text.forEach((tag, i) => {
|
|
490
|
+
if (typeof tag === 'string') {
|
|
491
|
+
this._add(null, tag, index ? index + i : index);
|
|
492
|
+
}
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
else {
|
|
496
|
+
this._add(null, text, index);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
return this;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
remove(text) {
|
|
503
|
+
const index = this.tag.values.indexOf(text);
|
|
504
|
+
if (index > -1) {
|
|
505
|
+
this._remove(this.tag.elements[index]);
|
|
506
|
+
}
|
|
507
|
+
return this;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
removeAll() {
|
|
511
|
+
[...this.tag.elements].forEach(element => this._remove(element));
|
|
512
|
+
this._showPlaceholder();
|
|
513
|
+
return this;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
_setDisabledState(disabled) {
|
|
517
|
+
const elements = [
|
|
518
|
+
...this.container.querySelectorAll('button'),
|
|
519
|
+
...this.container.querySelectorAll('input')
|
|
520
|
+
];
|
|
521
|
+
|
|
522
|
+
elements.forEach((el) => {
|
|
523
|
+
if (disabled) {
|
|
524
|
+
el.setAttribute('disabled', '');
|
|
525
|
+
} else {
|
|
526
|
+
el.removeAttribute('disabled');
|
|
527
|
+
}
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
return this;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
enable() {
|
|
534
|
+
return this._setDisabledState(false);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
disable() {
|
|
538
|
+
return this._setDisabledState(true);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
destroy() {
|
|
542
|
+
this._detachEvents();
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
export default Taggle;
|