@dataloop-ai/components 0.18.144 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/package.json +4 -1
  2. package/src/assets/constants.scss +25 -26
  3. package/src/assets/globals.scss +58 -56
  4. package/src/assets/grid.css +1 -1
  5. package/src/assets/grid.scss +4 -4
  6. package/src/components/basic/DlAccordion/DlAccordion.vue +10 -3
  7. package/src/components/basic/DlEmptyState/types.ts +1 -1
  8. package/src/components/basic/DlGrid/DlGrid.vue +2 -1
  9. package/src/components/basic/DlPopup/DlPopup.vue +159 -396
  10. package/src/components/basic/DlWidget/DlWidget.vue +2 -1
  11. package/src/components/basic/utils.ts +0 -9
  12. package/src/components/blocks/DlLabelPicker/DlLabelPicker.vue +127 -0
  13. package/src/components/blocks/DlLabelPicker/index.ts +3 -0
  14. package/src/components/blocks/DlLabelPicker/types.ts +6 -0
  15. package/src/components/blocks/index.ts +1 -0
  16. package/src/components/blocks/types.ts +1 -0
  17. package/src/components/compound/DlCodeEditor/README.md +5 -4
  18. package/src/components/compound/DlCodeEditor/styles/themes-base16.css +16934 -11289
  19. package/src/components/compound/DlCodeEditor/styles/themes.css +5122 -3448
  20. package/src/components/compound/DlInput/DlInput.vue +609 -178
  21. package/src/components/compound/DlInput/components/InputFileElement.vue +97 -0
  22. package/src/components/compound/DlInput/types.ts +12 -0
  23. package/src/components/compound/DlInput/utils.ts +117 -0
  24. package/src/components/compound/DlSearches/DlSearch/DlSearch.vue +10 -1
  25. package/src/components/compound/DlSearches/DlSmartSearch/components/DlSmartSearchInput.vue +12 -3
  26. package/src/components/compound/DlSearches/DlSmartSearch/utils/highlightSyntax.ts +1 -1
  27. package/src/components/compound/DlSlider/sliderStyles.scss +92 -92
  28. package/src/components/compound/DlTable/DlTable.vue +701 -358
  29. package/src/components/compound/DlTable/components/DlTd.vue +11 -9
  30. package/src/components/compound/DlTable/components/DlTh.vue +22 -21
  31. package/src/components/compound/DlTable/components/SortableJS.vue +33 -0
  32. package/src/components/compound/DlTable/hooks/tableActions.ts +3 -3
  33. package/src/components/compound/DlTable/hooks/tableColumnSelection.ts +3 -4
  34. package/src/components/compound/DlTable/hooks/use-sortable.ts +152 -0
  35. package/src/components/compound/DlTable/styles/dl-table-styles.scss +372 -377
  36. package/src/components/compound/DlTable/types.ts +6 -0
  37. package/src/components/compound/DlTable/utils/getCellValue.ts +27 -0
  38. package/src/components/compound/DlTable/utils/insertAtIndex.ts +22 -0
  39. package/src/components/compound/DlTable/utils/tableClasses.ts +22 -0
  40. package/src/components/compound/DlToast/api/useToast.ts +10 -4
  41. package/src/components/compound/DlToast/components/ToastComponent.vue +26 -21
  42. package/src/components/compound/DlToast/utils/render.ts +15 -8
  43. package/src/components/compound/DlTreeTable/DlTreeTable.vue +708 -323
  44. package/src/components/compound/DlTreeTable/components/DlTdTree.vue +10 -9
  45. package/src/components/compound/DlTreeTable/components/DlTrTree.vue +13 -0
  46. package/src/components/compound/DlTreeTable/emits.ts +2 -2
  47. package/src/components/compound/DlTreeTable/utils/index.ts +6 -0
  48. package/src/components/compound/DlTreeTable/utils/moveNestedRow.ts +95 -0
  49. package/src/components/compound/DlTreeTable/utils/trSpacing.ts +12 -1
  50. package/src/components/compound/DlTreeTable/utils/treeTableRowSelection.ts +10 -7
  51. package/src/components/compound/DlTreeTable/views/DlTrTreeView.vue +52 -58
  52. package/src/components/essential/DlMenu/DlMenu.vue +25 -0
  53. package/src/components/index.ts +1 -0
  54. package/src/components/shared/DlVirtualScroll/DlVirtualScroll.vue +2 -1
  55. package/src/components/shared/DlVirtualScroll/styles/dl-virtual-scroll-styles.scss +62 -41
  56. package/src/components/types.ts +1 -0
  57. package/src/demos/DlAccordionDemo.vue +32 -0
  58. package/src/demos/DlButtonDemo.vue +7 -0
  59. package/src/demos/DlDemoPage.vue +1 -0
  60. package/src/demos/DlInputDemo.vue +122 -64
  61. package/src/demos/DlLabelPickerDemo.vue +46 -0
  62. package/src/demos/DlPopupDemo.vue +94 -4
  63. package/src/demos/DlSearchDemo.vue +0 -1
  64. package/src/demos/DlTableDemo.vue +261 -162
  65. package/src/demos/DlToastDemo.vue +28 -1
  66. package/src/demos/DlTreeTableDemo.vue +266 -262
  67. package/src/demos/DlVirtualScrollDemo.vue +1 -4
  68. package/src/demos/index.ts +3 -1
  69. package/src/hooks/use-model-toggle.ts +26 -62
  70. package/src/utils/browse-nested-nodes.ts +26 -0
  71. package/src/utils/draggable-table.ts +100 -488
  72. package/src/utils/get-element-above.ts +8 -0
  73. package/src/utils/getSlotByVersion.ts +11 -0
  74. package/src/utils/index.ts +4 -0
  75. package/src/utils/keyCodes.ts +1 -1
  76. package/src/utils/remove-child-nodes.ts +5 -0
  77. package/src/utils/render-fn.ts +46 -0
  78. package/src/utils/resizable-table.ts +110 -0
  79. package/src/utils/selection.ts +196 -0
  80. package/src/utils/swap-nodes.ts +11 -0
  81. package/src/utils/table-columns.ts +209 -0
  82. package/src/components/compound/DlTable/utils/ResizableManager.ts +0 -236
  83. package/src/components/compound/DlTable/utils/index.ts +0 -1
  84. package/src/components/compound/DlTable/utils/props.ts +0 -120
  85. package/src/components/compound/DlTreeTable/props.ts +0 -134
  86. package/src/components/compound/DlTreeTable/utils/flatTreeData.ts +0 -19
@@ -4,23 +4,23 @@
4
4
  :style="cssVars"
5
5
  :class="rootContainerClasses"
6
6
  >
7
- <div class="row">
7
+ <div class="row full-width full-height">
8
8
  <div
9
9
  :class="`${
10
10
  isSmall ? 'col' : 'row full-width full-height'
11
11
  } top`"
12
- :style="`${isSmall ? 'flex-grow: 0; max-width: 25%;' : ''}`"
12
+ :style="`${isSmall ? 'flex-grow: 0;' : ''}`"
13
13
  >
14
14
  <div
15
15
  v-if="!!title.length || !!tooltip.length"
16
16
  :class="{
17
- 'dl-text-input__title-container': true,
18
- 'dl-text-input__title-container--s': isSmall
17
+ 'dl-input__title-container': true,
18
+ 'dl-input__title-container--s': isSmall
19
19
  }"
20
20
  >
21
21
  <label
22
22
  v-if="!!title.length"
23
- class="dl-text-input__title"
23
+ class="dl-input__title"
24
24
  :style="`${isSmall ? 'width: 90%' : 'width: 100%'}`"
25
25
  >
26
26
  <dl-ellipsis>
@@ -35,7 +35,7 @@
35
35
  </label>
36
36
  <span v-if="!!tooltip.length">
37
37
  <dl-icon
38
- class="dl-text-input__tooltip-icon"
38
+ class="dl-input__tooltip-icon"
39
39
  icon="icon-dl-info"
40
40
  size="12px"
41
41
  />
@@ -51,8 +51,8 @@
51
51
  <div
52
52
  v-if="!!topMessage.length"
53
53
  :class="{
54
- 'dl-text-input__top-message-container': true,
55
- 'dl-text-input__top-message-container--s': isSmall
54
+ 'dl-input__top-message-container': true,
55
+ 'dl-input__top-message-container--s': isSmall
56
56
  }"
57
57
  >
58
58
  <dl-info-error-message
@@ -68,48 +68,60 @@
68
68
  } bottom-section full-width full-height`"
69
69
  >
70
70
  <div class="row center full-width full-height">
71
- <div style="flex-grow: 1; height: 100%; position: relative">
72
- <input
73
- ref="input"
74
- :value="modelValue"
75
- :class="inputClasses"
76
- :placeholder="placeholder"
77
- :maxlength="maxLength"
78
- :type="showPass ? 'text' : type"
79
- :disabled="disabled"
80
- :readonly="readonly"
81
- @input="debouncedInput"
82
- @focus="onFocus"
83
- @blur="debouncedBlur"
84
- @keyup.enter="onKeyPress"
85
- >
71
+ <div :class="wrapperClasses">
86
72
  <div
87
73
  v-if="hasPrepend"
88
74
  :class="[
89
75
  ...adornmentClasses,
90
- 'dl-text-input__adornment-container--pos-left'
76
+ 'dl-input__adornment-container--pos-left'
91
77
  ]"
92
78
  >
93
79
  <slot name="prepend" />
94
80
  </div>
95
81
  <div
96
- v-if="hasAppend"
82
+ ref="input"
83
+ :contenteditable="!readonly"
84
+ :class="inputClasses"
85
+ :placeholder="placeholder"
86
+ @input="onChange"
87
+ @focus="onFocus"
88
+ @blur="onBlur"
89
+ @keydown="onKeydown"
90
+ @keyup.enter="onEnterPress"
91
+ @paste="handlePaste"
92
+ >
93
+ <span
94
+ v-if="readonly"
95
+ :class="
96
+ showPlaceholder ? 'placeholder-string' : ''
97
+ "
98
+ >{{
99
+ showPlaceholder ? placeholder : modelValue
100
+ }}</span>
101
+ </div>
102
+ <div
103
+ v-if="
104
+ hasAppend ||
105
+ showClearButton ||
106
+ showShowPassButton
107
+ "
97
108
  :class="[
98
109
  ...adornmentClasses,
99
- 'dl-text-input__adornment-container--pos-right'
110
+ 'dl-input__adornment-container--pos-right'
100
111
  ]"
101
112
  >
102
- <slot name="append" />
113
+ <slot
114
+ v-if="hasAppend"
115
+ name="append"
116
+ />
103
117
  <span
104
118
  v-if="showClearButton"
105
- v-show="focused || mouseOverClear"
106
- @mouseenter="mouseOverClear = true"
107
- @mouseleave="mouseOverClear = false"
119
+ class="dl-input__adornment-container--clear"
108
120
  >
109
121
  <dl-button
110
122
  ref="input-clear-button"
111
123
  icon="icon-dl-close"
112
- size="s"
124
+ :size="clearIconSize"
113
125
  text-color="dl-color-darker"
114
126
  flat
115
127
  fluid
@@ -135,17 +147,6 @@
135
147
  </span>
136
148
  </div>
137
149
  </div>
138
- <div
139
- v-if="hasAction"
140
- style="
141
- width: fit-content;
142
- display: flex;
143
- align-items: center;
144
- margin-left: 10px;
145
- "
146
- >
147
- <slot name="action" />
148
- </div>
149
150
  <dl-menu
150
151
  v-if="showSuggestItems"
151
152
  v-model="isMenuOpen"
@@ -154,7 +155,7 @@
154
155
  :offset="[0, 3]"
155
156
  fit-container
156
157
  :fit-content="fitContent"
157
- :arrow-nav-items="suggestItems"
158
+ :arrow-nav-items="stringSuggestions"
158
159
  @click="onMenuShow"
159
160
  @highlightedIndex="setHighlightedIndex"
160
161
  @handleSelectedItem="handleSelectedItem"
@@ -165,20 +166,25 @@
165
166
  >
166
167
  <dl-list-item
167
168
  v-for="(item, suggestIndex) in suggestItems"
168
- :key="item"
169
+ :key="item.suggestion"
169
170
  clickable
170
171
  style="font-size: 12px"
171
172
  :highlighted="suggestIndex === highlightedIndex"
172
173
  @click="onClick($event, item)"
173
174
  >
175
+ <img
176
+ v-if="item.image"
177
+ :src="item.image"
178
+ class="dl-input__suggestion--image"
179
+ >
174
180
  <span
175
181
  v-for="(word, index) in getSuggestWords(
176
- item,
182
+ item.suggestion,
177
183
  modelValue
178
184
  )"
179
185
  :key="JSON.stringify(word) + index"
180
186
  :class="{
181
- 'dl-text-input__suggestion--highlighted':
187
+ 'dl-input__suggestion--highlighted':
182
188
  word.highlighted
183
189
  }"
184
190
  >
@@ -199,7 +205,7 @@
199
205
  <div class="row bottom full-width full-height">
200
206
  <div
201
207
  v-if="bottomMessage"
202
- class="row full-width dl-text-input__bottom-message-container"
208
+ class="row full-width dl-input__bottom-message-container"
203
209
  style="justify-content: space-between"
204
210
  >
205
211
  <div>
@@ -235,7 +241,7 @@
235
241
  <div>
236
242
  <span
237
243
  v-if="showCounter && maxLength && maxLength > 0"
238
- class="dl-text-input__counter"
244
+ class="dl-input__counter"
239
245
  >
240
246
  {{ characterCounter }}
241
247
  </span>
@@ -243,19 +249,102 @@
243
249
  </div>
244
250
  </div>
245
251
  </div>
252
+ <div
253
+ v-if="files.length"
254
+ class="dl-input__files"
255
+ >
256
+ <input-file-element
257
+ v-for="file in files"
258
+ :key="file.id"
259
+ :file="file"
260
+ @remove-file="emitRemoveFile"
261
+ @zoom-image="handleZoomImage"
262
+ @rename-file="handleRenameFileModal"
263
+ />
264
+ </div>
265
+ </div>
266
+ <dl-dialog-box
267
+ v-if="currentZoomImage"
268
+ v-model="zoomImageModel"
269
+ volatile
270
+ width="60vw"
271
+ style="--dl-dialog-box-content-padding: 0"
272
+ >
273
+ <template #body>
274
+ <img
275
+ class="dl-input__zoom-image"
276
+ :src="currentZoomImage"
277
+ >
278
+ </template>
279
+ </dl-dialog-box>
280
+ <dl-dialog-box
281
+ v-model="renameFileModel"
282
+ volatile
283
+ :style="`--dl-dialog-box-header-height: 25px;
284
+ --dl-dialog-box-footer-height: 25px;`"
285
+ >
286
+ <template #header>
287
+ <dl-dialog-box-header
288
+ title="Rename File"
289
+ close-button
290
+ @onClose="renameFileModel = false"
291
+ />
292
+ </template>
293
+ <template #body>
294
+ <dl-input
295
+ v-model="newFileName"
296
+ title="Name"
297
+ red-asterisk
298
+ min-width="80%"
299
+ hide-clear-button
300
+ :placeholder="`Enter new file ${currentFile.name}`"
301
+ />
302
+ </template>
303
+ <template #footer>
304
+ <dl-dialog-box-footer>
305
+ <div style="display: flex; justify-content: flex-end">
306
+ <dl-button @click="handleRenameFile">
307
+ Rename
308
+ </dl-button>
309
+ </div>
310
+ </dl-dialog-box-footer>
311
+ </template>
312
+ </dl-dialog-box>
313
+ <div
314
+ v-if="hasAction"
315
+ class="dl-input__action"
316
+ >
317
+ <slot name="action" />
246
318
  </div>
247
319
  </div>
248
320
  </template>
249
321
 
250
322
  <script lang="ts">
251
- import { debounce } from 'lodash'
252
- import { computed, defineComponent, PropType, ref } from 'vue-demi'
323
+ import { debounce, cloneDeep } from 'lodash'
324
+ import { computed, defineComponent, PropType, ref, toRefs } from 'vue-demi'
253
325
  import { DlInfoErrorMessage, DlTooltip } from '../../shared'
254
326
  import { DlListItem } from '../../basic'
255
327
  import { DlMenu, DlIcon, DlList, DlEllipsis } from '../../essential'
328
+ import {
329
+ DlDialogBox,
330
+ DlDialogBoxHeader,
331
+ DlDialogBoxFooter
332
+ } from '../../compound/DlDialogBox'
256
333
  import { DlButton } from '../../basic'
257
334
  import { InputSizes, TInputSizes } from '../../../utils/input-sizes'
335
+ import {
336
+ addEventListenersToElement,
337
+ clearSuggestion,
338
+ getSuggestItems,
339
+ isArrayBufferImage,
340
+ readBlob,
341
+ readClipboard,
342
+ setInnerHTMLWithCursor
343
+ } from './utils'
258
344
  import { v4 } from 'uuid'
345
+ import { setCaretAtTheEnd } from '../../../utils'
346
+ import { DlInputFile, DlInputSuggestion } from './types'
347
+ import InputFileElement from './components/InputFileElement.vue'
259
348
  import { stateManager } from '../../../StateManager'
260
349
 
261
350
  export default defineComponent({
@@ -268,134 +357,294 @@ export default defineComponent({
268
357
  DlMenu,
269
358
  DlList,
270
359
  DlListItem,
271
- DlEllipsis
360
+ DlEllipsis,
361
+ DlDialogBox,
362
+ DlDialogBoxHeader,
363
+ DlDialogBoxFooter,
364
+ InputFileElement
272
365
  },
273
366
  model: {
274
367
  prop: 'modelValue',
275
368
  event: 'update:model-value'
276
369
  },
277
370
  props: {
371
+ /**
372
+ * The text value of the input
373
+ */
278
374
  modelValue: {
279
375
  type: [String, Number],
280
376
  default: null
281
377
  },
378
+ /**
379
+ * An array of InputFile objects contained and modeled in the input
380
+ */
381
+ files: {
382
+ type: Array as PropType<DlInputFile[]>,
383
+ default: (): DlInputFile[] => []
384
+ },
385
+ /**
386
+ * Input height
387
+ */
388
+ height: {
389
+ type: String,
390
+ default: null
391
+ },
392
+ /**
393
+ * Input max height
394
+ */
395
+ maxHeight: {
396
+ type: String,
397
+ default: '100px'
398
+ },
399
+ /**
400
+ * The input will expand like a textarea while typing
401
+ */
402
+ expandable: {
403
+ type: Boolean,
404
+ default: false
405
+ },
406
+ /**
407
+ * Input width
408
+ */
409
+ width: {
410
+ type: String,
411
+ default: '100%'
412
+ },
413
+ /**
414
+ * Input max-width
415
+ */
416
+ maxWidth: {
417
+ type: String,
418
+ default: 'fit-content'
419
+ },
420
+ /**
421
+ * General size: s,m,l
422
+ */
282
423
  size: {
283
424
  type: String as PropType<TInputSizes>,
284
425
  default: InputSizes.l
285
426
  },
427
+ /**
428
+ * Input placeholder
429
+ */
286
430
  placeholder: {
287
431
  type: String,
288
432
  default: ''
289
433
  },
434
+ /**
435
+ * Text to be displayed above the input as title
436
+ */
290
437
  title: {
291
438
  type: String,
292
439
  default: ''
293
440
  },
441
+ /**
442
+ * Whether or not this field is required
443
+ */
294
444
  required: {
295
445
  type: Boolean,
296
446
  default: false
297
447
  },
448
+ /**
449
+ * Whether or not this field is optional
450
+ */
298
451
  optional: {
299
452
  type: Boolean,
300
453
  default: false
301
454
  },
455
+ /**
456
+ * Text to be displayed in a tooltip while hovering over the info icon
457
+ */
302
458
  tooltip: {
303
459
  type: String,
304
460
  default: ''
305
461
  },
462
+ /**
463
+ * Type "password" will mask the characters
464
+ */
306
465
  type: {
307
466
  type: String,
308
467
  default: 'text'
309
468
  },
469
+ /**
470
+ * Message displayed above the input
471
+ */
310
472
  topMessage: {
311
473
  type: String,
312
474
  default: ''
313
475
  },
476
+ /**
477
+ * Input will have a red asterisk
478
+ */
314
479
  redAsterisk: {
315
480
  type: Boolean,
316
481
  default: false
317
482
  },
483
+ /**
484
+ * Message to be displayed when in info mode
485
+ */
318
486
  infoMessage: {
319
487
  type: String,
320
488
  default: ''
321
489
  },
490
+ /**
491
+ * Message to be displayed when in error mode
492
+ */
322
493
  errorMessage: {
323
494
  type: String,
324
495
  default: ''
325
496
  },
497
+ /**
498
+ * Input will be in error mode
499
+ */
326
500
  error: {
327
501
  type: Boolean,
328
502
  default: false
329
503
  },
504
+ /**
505
+ * Message to be displayed when in warning mode
506
+ */
330
507
  warningMessage: {
331
508
  type: String,
332
509
  default: ''
333
510
  },
511
+ /**
512
+ * Input will be in warning mode
513
+ */
334
514
  warning: {
335
515
  type: Boolean,
336
516
  default: false
337
517
  },
518
+ /**
519
+ * Input will be disabled
520
+ */
338
521
  disabled: {
339
522
  type: Boolean,
340
523
  default: false
341
524
  },
525
+ /**
526
+ * Input will be readonly
527
+ */
342
528
  readonly: {
343
529
  type: Boolean,
344
530
  default: false
345
531
  },
532
+ /**
533
+ * Maximum amount of characters the user can type
534
+ */
346
535
  maxLength: {
347
536
  type: Number,
348
537
  default: null
349
538
  },
539
+ /**
540
+ * Input will show a counter for how many characters have been typed
541
+ */
350
542
  showCounter: {
351
543
  type: Boolean,
352
544
  default: false
353
545
  },
546
+ /**
547
+ * Input will be in dense mode
548
+ */
354
549
  dense: Boolean,
550
+ /**
551
+ * Input will not have a clear text button
552
+ */
355
553
  hideClearButton: {
356
554
  type: Boolean,
357
555
  default: false
358
556
  },
557
+ /**
558
+ * Count the characters backwards
559
+ */
359
560
  counterReverse: {
360
561
  type: Boolean,
361
562
  default: false
362
563
  },
564
+ /**
565
+ * An array of InputSuggestion objects containing the suggestions the input will show while typing
566
+ */
363
567
  autoSuggestItems: {
364
- type: Array as PropType<string[]>,
365
- default: (): string[] => []
568
+ type: Array as PropType<DlInputSuggestion[]>,
569
+ default: (): DlInputSuggestion[] => []
366
570
  },
571
+ /**
572
+ * Highlight when a suggestion is matched
573
+ */
367
574
  highlightMatches: {
368
575
  type: Boolean,
369
576
  default: false
370
577
  },
578
+ /**
579
+ * Width of the suggestion menu
580
+ */
371
581
  suggestMenuWidth: {
372
582
  type: String,
373
583
  default: 'auto'
374
584
  },
585
+ /**
586
+ * Tooltip showed when hovering over the clear button
587
+ */
375
588
  clearButtonTooltip: {
376
589
  type: Boolean,
377
590
  default: false
378
591
  },
592
+ /**
593
+ * Input will be in fit-content mode
594
+ */
379
595
  fitContent: Boolean,
596
+ /**
597
+ * CSS margin for input
598
+ */
380
599
  margin: {
381
600
  type: String,
382
601
  default: null
383
602
  },
603
+ /**
604
+ * The color of the highlighted text
605
+ */
606
+ syntaxHighlightColor: {
607
+ type: String,
608
+ default: 'var(--dl-color-secondary)'
609
+ },
610
+ /**
611
+ * Debounce time for input
612
+ */
384
613
  debounce: {
385
614
  type: Number,
386
615
  default: 100
387
616
  }
388
617
  },
389
- emits: ['input', 'focus', 'blur', 'clear', 'enter', 'update:model-value'],
618
+ emits: [
619
+ 'input',
620
+ 'focus',
621
+ 'blur',
622
+ 'clear',
623
+ 'enter',
624
+ 'file-update',
625
+ 'update:model-value',
626
+ 'suggestion-click'
627
+ ],
390
628
  setup(props, { emit }) {
391
629
  const mouseOverClear = ref(false)
392
630
  const highlightedIndex = ref(-1)
393
631
  const isMenuOpen = ref(false)
394
- const suggestItems = computed<string[]>(() => {
395
- return props.autoSuggestItems.filter((item) =>
396
- item.includes(`${props.modelValue}`)
632
+ const {
633
+ modelValue,
634
+ autoSuggestItems,
635
+ maxLength,
636
+ files,
637
+ syntaxHighlightColor
638
+ } = toRefs(props)
639
+
640
+ const suggestItems = computed<DlInputSuggestion[]>(() => {
641
+ if (!modelValue.value) return []
642
+ return getSuggestItems(
643
+ autoSuggestItems.value,
644
+ modelValue.value?.toString()
397
645
  )
398
646
  })
647
+ const input = ref(null)
399
648
 
400
649
  const setHighlightedIndex = (value: any) => {
401
650
  highlightedIndex.value = value
@@ -403,16 +652,65 @@ export default defineComponent({
403
652
  const handleSelectedItem = (value: any) => {
404
653
  onAutoSuggestClick(null, value)
405
654
  }
406
- const inputRef = ref<HTMLInputElement>(null)
407
- const onAutoSuggestClick = (
408
- e: Event,
409
- item: string | HTMLInputElement
410
- ): void => {
411
- emit('input', item, e)
412
- emit('update:model-value', item)
413
- inputRef.value = item as HTMLInputElement
655
+ const onAutoSuggestClick = (e: Event, item: string): void => {
656
+ const newValue = clearSuggestion(modelValue.value.toString(), item)
657
+ if (!maxLength.value || newValue.length < maxLength.value) {
658
+ emit('input', newValue, e)
659
+ emit('update:model-value', newValue)
660
+ input.value.innerHTML = newValue
661
+ setCaretAtTheEnd(input.value)
662
+ }
663
+ }
664
+
665
+ const emitAddFile = (file: DlInputFile) => {
666
+ const newFiles = cloneDeep(files.value)
667
+ newFiles.push(file)
668
+ emit('file-update', newFiles)
669
+ }
670
+ const emitRemoveFile = (fileId: string) => {
671
+ emit(
672
+ 'file-update',
673
+ files.value.filter((file) => file.id !== fileId)
674
+ )
675
+ }
676
+
677
+ const isSpecialWord = (word: string) => {
678
+ return (
679
+ word.startsWith('@') ||
680
+ autoSuggestItems.value.some(
681
+ (s) => s.suggestion?.trim() === word?.trim()
682
+ )
683
+ )
414
684
  }
415
685
 
686
+ const updateSyntax = () => {
687
+ setInnerHTMLWithCursor(input.value, (text) => {
688
+ const words = text.split(' ').map((word) => {
689
+ if (isSpecialWord(word)) {
690
+ return `<span class="clickable" style="color: ${syntaxHighlightColor.value}">${word}</span>`
691
+ }
692
+ return word
693
+ })
694
+ input.value.innerHTML = words.join(' ')
695
+ })
696
+ Array.from(input.value.children).forEach((el: any) => {
697
+ if (!isSpecialWord(el.innerText)) return
698
+ addEventListenersToElement(el, {
699
+ click: () => {
700
+ emit('suggestion-click', el.innerText)
701
+ }
702
+ })
703
+ })
704
+ }
705
+
706
+ const stringSuggestions = computed<string[]>(() => {
707
+ return suggestItems.value.map((item) => item.suggestion)
708
+ })
709
+
710
+ const showPlaceholder = computed<boolean>(
711
+ () => !modelValue.value || !String(modelValue.value)?.length
712
+ )
713
+
416
714
  return {
417
715
  suggestItems,
418
716
  highlightedIndex,
@@ -420,13 +718,24 @@ export default defineComponent({
420
718
  isMenuOpen,
421
719
  setHighlightedIndex,
422
720
  handleSelectedItem,
423
- mouseOverClear
721
+ mouseOverClear,
722
+ input,
723
+ emitAddFile,
724
+ emitRemoveFile,
725
+ updateSyntax,
726
+ stringSuggestions,
727
+ showPlaceholder
424
728
  }
425
729
  },
426
730
  data() {
427
731
  return {
428
- uuid: `dl-text-input-${v4()}`,
732
+ uuid: `dl-input-${v4()}`,
429
733
  showPass: false,
734
+ zoomImageModel: false,
735
+ renameFileModel: false,
736
+ currentZoomImage: null,
737
+ currentFile: null,
738
+ newFileName: null,
430
739
  focused: false
431
740
  }
432
741
  },
@@ -440,12 +749,12 @@ export default defineComponent({
440
749
  )
441
750
  },
442
751
  rootContainerClasses(): string[] {
443
- const classes = ['dl-text-input']
752
+ const classes = ['dl-input']
444
753
  if (this.isSmall) {
445
- classes.push('dl-text-input--s')
754
+ classes.push('dl-input--s')
446
755
  }
447
756
  if (this.dense) {
448
- classes.push('dl-text-input--dense')
757
+ classes.push('dl-input--dense')
449
758
  }
450
759
  return classes
451
760
  },
@@ -466,45 +775,54 @@ export default defineComponent({
466
775
  }
467
776
  return {
468
777
  '--dl-input-margin': inputMargin,
469
- '--dl-input-border-color-hover': this.getBorderColor
778
+ '--dl-input-border-color-hover': this.getBorderColor,
779
+ '--dl-input-height': this.height ? this.height : null,
780
+ '--dl-input-align-items':
781
+ this.expandable || this.height ? 'flex-start' : 'center',
782
+ '--dl-input-max-width': this.maxWidth,
783
+ '--dl-input-width': this.width,
784
+ '--dl-input-max-height': this.maxHeight,
785
+ '--dl-input-white-space': this.expandable ? 'normal' : 'nowrap'
470
786
  }
471
787
  },
472
788
  inputClasses(): string[] {
473
- const classes = [
474
- 'dl-text-input__input',
475
- `dl-text-input__input--${this.size}`
476
- ]
789
+ const classes = ['dl-input__input', `dl-input__input--${this.size}`]
477
790
 
478
791
  if (this.hasPrepend) {
479
- classes.push('dl-text-input__input--prepend')
792
+ classes.push('dl-input__input--prepend')
480
793
  }
481
- if (this.hasAppend) {
482
- classes.push('dl-text-input__input--append')
794
+ if (
795
+ this.hasAppend ||
796
+ this.showClearButton ||
797
+ this.showShowPassButton
798
+ ) {
799
+ classes.push('dl-input__input--append')
483
800
  }
484
801
  if (this.hasPrepend && this.hasAppend) {
485
- classes.push('dl-text-input__input--both-adornments')
802
+ classes.push('dl-input__input--both-adornments')
486
803
  }
487
- if (this.error) {
488
- classes.push('dl-text-input__input--error')
489
- } else if (this.warning) {
490
- classes.push('dl-text-input__input--warning')
804
+ if (this.type === 'password' && !this.showPass) {
805
+ classes.push('dl-input__input--password')
491
806
  }
492
807
 
493
808
  return classes
494
809
  },
495
810
  asteriskClasses(): string[] {
496
- const classes = ['dl-text-input__asterisk']
811
+ const classes = ['dl-input__asterisk']
497
812
 
498
813
  if (this.redAsterisk) {
499
- classes.push('dl-text-input__asterisk--red')
814
+ classes.push('dl-input__asterisk--red')
500
815
  }
501
816
 
502
817
  return classes
503
818
  },
504
819
  adornmentClasses(): string[] {
505
820
  return [
506
- 'dl-text-input__adornment-container',
507
- `dl-text-input__adornment-container--${this.size}`
821
+ 'dl-input__adornment-container',
822
+ `dl-input__adornment-container--${this.size}`,
823
+ `dl-input__adornment-container--${
824
+ this.expandable ? 'expandable' : ''
825
+ }`
508
826
  ]
509
827
  },
510
828
  isSmall(): boolean {
@@ -517,16 +835,15 @@ export default defineComponent({
517
835
  return !!this.$slots.prepend
518
836
  },
519
837
  hasAppend(): boolean {
520
- return (
521
- (!!this.$slots.append ||
522
- !this.hideClearButton ||
523
- this.type === 'password') &&
524
- !this.isSmall
525
- )
838
+ return !!this.$slots.append && !this.isSmall
526
839
  },
527
840
  hasAction(): boolean {
528
841
  return !!this.$slots.action && !this.isSmall
529
842
  },
843
+ clearIconSize(): string {
844
+ if (this.isSmall) return '7px'
845
+ return 's'
846
+ },
530
847
  passShowIcon(): string {
531
848
  return this.showPass ? 'icon-dl-hide' : 'icon-dl-show'
532
849
  },
@@ -543,18 +860,24 @@ export default defineComponent({
543
860
  return !this.$slots.append && this.type === 'password'
544
861
  },
545
862
  showSuggestItems(): boolean {
546
- return !!this.suggestItems.length && !!this.modelValue
863
+ return !!this.suggestItems?.length && !!this.modelValue
547
864
  },
548
865
  debouncedBlur(): any {
549
866
  if (stateManager.disableDebounce) {
550
867
  return this.onBlur.bind(this)
551
868
  }
552
- const debounced = debounce(this.onBlur.bind(this), 50)
869
+ const debounced = debounce(
870
+ this.onBlur.bind(this),
871
+ this.debounce ?? 50
872
+ )
553
873
  return debounced
554
874
  },
555
875
  inputLength(): number {
556
876
  return `${this.modelValue}`.length
557
877
  },
878
+ isWithinMaxLength(): boolean {
879
+ return !this.maxLength || this.inputLength < this.maxLength
880
+ },
558
881
  characterCounter(): string {
559
882
  if (!this.maxLength) {
560
883
  return ''
@@ -566,37 +889,89 @@ export default defineComponent({
566
889
 
567
890
  return `${chars}/${this.maxLength}`
568
891
  },
569
- wrapperClasses(): string {
570
- let classes = 'flex items-center'
571
-
572
- if (this.isSmall) {
573
- classes += ' no-wrap'
892
+ wrapperClasses(): string[] {
893
+ const classes = [
894
+ 'dl-input__wrapper',
895
+ `dl-input__wrapper--${this.size}`
896
+ ]
897
+ if (this.disabled) {
898
+ classes.push('dl-input__wrapper--disabled')
899
+ }
900
+ if (this.error) {
901
+ classes.push('dl-input__wrapper--error')
902
+ } else if (this.warning) {
903
+ classes.push('dl-input__wrapper--warning')
904
+ }
905
+ if (this.readonly) {
906
+ classes.push('dl-input__wrapper--readonly')
574
907
  }
575
908
 
576
909
  return classes
577
- },
578
- debouncedInput(): any {
579
- if (stateManager.disableDebounce) {
580
- return this.onChange.bind(this)
581
- }
582
- const debounced = debounce(this.onChange.bind(this), this.debounce)
583
- return debounced
584
910
  }
585
911
  },
586
912
  methods: {
587
- onClick(e: Event, item: string) {
588
- this.onAutoSuggestClick(e, item)
913
+ onKeydown(e: KeyboardEvent) {
914
+ if (!this.isWithinMaxLength && e.key !== 'Backspace') {
915
+ e.preventDefault()
916
+ return
917
+ }
589
918
  },
590
- onChange(e: any): void {
919
+ onClick(e: Event, item: DlInputSuggestion) {
920
+ this.onAutoSuggestClick(e, item.suggestion)
921
+ },
922
+ onChange(e: Event): void {
591
923
  this.isMenuOpen = true
592
- this.$emit('input', e.target.value, e)
593
- this.$emit('update:model-value', e.target.value)
924
+ this.updateSyntax()
925
+ const target = e.target as HTMLElement
926
+ this.$emit('input', target.innerText, e)
927
+ this.$emit('update:model-value', target.innerText)
928
+ },
929
+ async handlePaste() {
930
+ const content = await readClipboard()
931
+
932
+ for (const item of content) {
933
+ if (item.type === 'text/plain') {
934
+ return
935
+ }
936
+ }
937
+
938
+ for (const item of content) {
939
+ const data = await readBlob(item.data)
940
+ if (isArrayBufferImage(data)) {
941
+ const objectUrl = URL.createObjectURL(item.data)
942
+ this.emitAddFile({
943
+ id: v4(),
944
+ name: `new_image_${this.files.length + 1}`,
945
+ image: objectUrl,
946
+ data
947
+ })
948
+ }
949
+ }
950
+ },
951
+ handleZoomImage(file: DlInputFile) {
952
+ this.currentZoomImage = file.image
953
+ this.zoomImageModel = true
954
+ },
955
+ handleRenameFileModal(file: DlInputFile) {
956
+ this.currentFile = file
957
+ this.renameFileModel = true
958
+ },
959
+ handleRenameFile() {
960
+ const newFiles = cloneDeep(this.files)
961
+ const fileToRenameIndex = newFiles.findIndex(
962
+ (file) => file.id === this.currentFile.id
963
+ )
964
+ newFiles[fileToRenameIndex].name = this.newFileName
965
+ this.$emit('file-update', newFiles)
966
+ this.renameFileModel = false
594
967
  },
595
968
  focus(): void {
596
969
  const inputRef = this.$refs.input as HTMLInputElement
597
970
  inputRef.focus()
598
971
  },
599
972
  onFocus(e: InputEvent): void {
973
+ const el = e.target as HTMLElement
974
+ el.scroll(el.scrollWidth, 0)
600
975
  this.focused = true
601
976
  this.$emit('focus', e)
602
977
  },
@@ -605,11 +980,13 @@ export default defineComponent({
605
980
  inputRef.blur()
606
981
  },
607
982
  onBlur(e: InputEvent): void {
983
+ const el = e.target as HTMLElement
984
+ el.scroll(0, 0)
608
985
  this.focused = false
609
986
  this.$emit('blur', e)
610
987
  },
611
- onKeyPress(e: any): void {
612
- this.$emit('enter', e.target.value, e)
988
+ onEnterPress(e: any): void {
989
+ this.$emit('enter', e.target.innerText, e)
613
990
  },
614
991
  onClear(e: any): void {
615
992
  this.$emit('clear', this.modelValue)
@@ -617,7 +994,7 @@ export default defineComponent({
617
994
  this.$emit('update:model-value', '')
618
995
 
619
996
  const inputRef = this.$refs.input as HTMLInputElement
620
- inputRef.value = ''
997
+ inputRef.innerHTML = ''
621
998
  inputRef.focus()
622
999
  },
623
1000
  onPassShowClick(): void {
@@ -707,7 +1084,15 @@ export default defineComponent({
707
1084
  </script>
708
1085
 
709
1086
  <style scoped lang="scss">
710
- .dl-text-input {
1087
+ [contenteditable='true']:empty:before {
1088
+ content: attr(placeholder);
1089
+ pointer-events: none;
1090
+ display: block;
1091
+ opacity: 0.5;
1092
+ -webkit-text-security: none;
1093
+ }
1094
+
1095
+ .dl-input {
711
1096
  margin: var(--dl-input-margin);
712
1097
 
713
1098
  /* Chrome, Safari, Edge, Opera */
@@ -722,6 +1107,9 @@ export default defineComponent({
722
1107
  -moz-appearance: textfield;
723
1108
  }
724
1109
 
1110
+ display: flex;
1111
+ align-items: center;
1112
+
725
1113
  &--dense {
726
1114
  padding: 0;
727
1115
  }
@@ -773,35 +1161,88 @@ export default defineComponent({
773
1161
  }
774
1162
  }
775
1163
 
776
- &__input {
1164
+ &__wrapper {
1165
+ // flex-grow: 1;
1166
+ position: relative;
1167
+ display: flex;
1168
+ justify-content: space-between;
777
1169
  border: 1px solid var(--dl-color-separator);
1170
+ min-width: var(--dl-input-width);
1171
+ max-width: var(--dl-input-max-width);
1172
+ max-height: var(--dl-input-max-height);
1173
+ height: var(--dl-input-height);
1174
+
1175
+ &--readonly {
1176
+ border-color: var(--dl-color-separator) !important;
1177
+ }
1178
+
1179
+ &:hover {
1180
+ border-color: var(--dl-input-border-color-hover);
1181
+ }
1182
+ &--error {
1183
+ border-color: var(--dl-color-negative);
1184
+ }
1185
+ &--warning {
1186
+ border-color: var(--dl-input-border-color-hover);
1187
+ }
1188
+ &--disabled {
1189
+ border-color: var(--dl-color-separator);
1190
+ color: var(--dl-color-disabled);
1191
+ pointer-events: none;
1192
+ cursor: not-allowed;
1193
+ }
1194
+ &--s {
1195
+ height: 18px;
1196
+ }
1197
+ }
1198
+
1199
+ &__input {
1200
+ display: inline-block;
1201
+ font-family: Arial, Helvetica, sans-serif;
1202
+ border-right: none;
778
1203
  border-radius: 2px;
779
1204
  color: var(--dl-color-darker);
780
- width: calc(100% - 20px);
1205
+ white-space: var(--dl-input-white-space);
1206
+ font-size: var(--dl-font-size-body);
1207
+ overflow: hidden scroll;
1208
+ text-overflow: ellipsis;
781
1209
  box-sizing: content-box;
782
- height: 12px;
783
- padding: 10px;
1210
+ word-wrap: break-word;
784
1211
  outline: none;
785
1212
  background: none;
786
1213
  transition: border 0.3s;
787
- font-size: var(--dl-font-size-body);
1214
+ min-height: 10px;
1215
+ padding: 10px;
788
1216
  position: relative;
1217
+ line-height: 10px;
1218
+ width: 100%;
789
1219
 
790
1220
  &--prepend {
791
- padding-left: 28px;
792
1221
  width: calc(100% - 10px - 28px);
793
1222
  }
794
1223
 
795
1224
  &--append {
796
- padding-right: 28px;
797
- width: calc(100% - 10px - 28px);
1225
+ margin-right: 10px;
798
1226
  }
799
1227
 
800
1228
  &--both-adornments {
801
1229
  width: calc(100% - 28px - 28px);
802
1230
  }
803
1231
 
1232
+ &--l {
1233
+ min-height: 16px;
1234
+ line-height: 16px;
1235
+ padding-top: 10px;
1236
+ padding-bottom: 10px;
1237
+ }
1238
+ &--large {
1239
+ line-height: 16px;
1240
+ padding-top: 10px;
1241
+ padding-bottom: 10px;
1242
+ }
1243
+
804
1244
  &--m {
1245
+ line-height: 12px;
805
1246
  padding-top: 7px;
806
1247
  padding-bottom: 7px;
807
1248
  }
@@ -812,7 +1253,7 @@ export default defineComponent({
812
1253
 
813
1254
  &--s {
814
1255
  padding-top: 4px;
815
- padding-bottom: 3px;
1256
+ padding-bottom: 4px;
816
1257
  padding-right: 5px;
817
1258
  }
818
1259
  &--small {
@@ -822,18 +1263,11 @@ export default defineComponent({
822
1263
  padding-right: 5px;
823
1264
  }
824
1265
 
825
- &--error {
826
- border-color: var(--dl-color-negative);
827
- &:hover {
828
- }
829
- }
830
-
831
- &--warning {
832
- border-color: var(--dl-input-border-color-hover);
833
- &:hover {
834
- }
1266
+ &--password {
1267
+ -webkit-text-security: disc;
835
1268
  }
836
1269
 
1270
+ .placeholder-string,
837
1271
  &::placeholder {
838
1272
  color: var(--dl-color-lighter);
839
1273
  opacity: 1;
@@ -845,13 +1279,9 @@ export default defineComponent({
845
1279
 
846
1280
  &:focus {
847
1281
  border-color: var(--dl-input-border-color-hover);
1282
+ text-overflow: clip;
848
1283
  }
849
1284
 
850
- &:disabled {
851
- border-color: var(--dl-color-separator);
852
- color: var(--dl-color-disabled);
853
- cursor: not-allowed;
854
- }
855
1285
  &:read-only {
856
1286
  border-color: var(--dl-color-separator);
857
1287
  cursor: text;
@@ -864,47 +1294,19 @@ export default defineComponent({
864
1294
  &__adornment-container {
865
1295
  display: flex;
866
1296
  justify-content: center;
867
- align-items: center;
868
- position: absolute;
869
- width: 28px;
870
-
871
- &--l {
872
- height: 34px;
873
- }
874
-
875
- &--large {
876
- height: 34px;
877
- }
878
-
879
- &--m {
880
- height: 28px;
881
- }
882
-
883
- &--medium {
884
- height: 28px;
1297
+ align-items: var(--dl-input-align-items);
1298
+ width: 30px;
1299
+ &--expandable {
1300
+ margin-top: 3px;
1301
+ margin-right: 3px;
1302
+ &.dl-input__adornment-container--l {
1303
+ margin-top: 5px;
1304
+ margin-right: 5px;
1305
+ }
885
1306
  }
886
1307
 
887
1308
  &--s {
888
- margin-top: 1px;
889
- height: 20px;
890
- }
891
-
892
- &--small {
893
- height: 20px;
894
- }
895
-
896
- &--pos-left {
897
- top: 0;
898
- left: 0;
899
- }
900
-
901
- &--pos-right {
902
- top: 0;
903
- right: 0;
904
- }
905
- &--pos-right-out {
906
- top: 0;
907
- right: -30px;
1309
+ height: 100%;
908
1310
  }
909
1311
  }
910
1312
 
@@ -927,6 +1329,35 @@ export default defineComponent({
927
1329
  border-radius: 2px;
928
1330
  color: var(--dl-color-text-darker-buttons);
929
1331
  }
1332
+ &--image {
1333
+ margin-right: 5px;
1334
+ border-radius: 50%;
1335
+ height: 30px;
1336
+ width: 30px;
1337
+ }
1338
+ }
1339
+
1340
+ &__files {
1341
+ display: flex;
1342
+ gap: 16px;
1343
+ overflow-y: scroll;
1344
+ }
1345
+
1346
+ &__zoom-image {
1347
+ width: 100%;
1348
+ object-fit: contain;
1349
+ }
1350
+
1351
+ &__action {
1352
+ width: fit-content;
1353
+ display: flex;
1354
+ align-items: center;
1355
+ margin-left: 10px;
1356
+ }
1357
+
1358
+ .clickable:hover {
1359
+ cursor: pointer;
1360
+ text-decoration: underline;
930
1361
  }
931
1362
  }
932
1363
  </style>