@coreui/vue-pro 4.4.2 → 4.5.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coreui/vue-pro",
3
- "version": "4.4.2",
3
+ "version": "4.5.0",
4
4
  "description": "UI Components Library for Vue.js",
5
5
  "keywords": [
6
6
  "vue",
@@ -35,7 +35,7 @@
35
35
  "watch": "rollup -c -w"
36
36
  },
37
37
  "config": {
38
- "version_short": "4.4"
38
+ "version_short": "4.5"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@popperjs/core": "^2.11.6",
@@ -188,7 +188,7 @@ const CFormInput = defineComponent({
188
188
  onInput: (event: InputEvent) => handleInput(event),
189
189
  readonly: props.readonly,
190
190
  type: props.type,
191
- ...(props.modelValue && { value: props.modelValue }),
191
+ ...((props.modelValue || props.modelValue === 0) && { value: props.modelValue })
192
192
  },
193
193
  slots.default && slots.default(),
194
194
  ),
@@ -1,4 +1,7 @@
1
- import { defineComponent, h, onMounted, onUnmounted, PropType, provide, ref, watch } from 'vue'
1
+ import { defineComponent, h, PropType, provide, ref, watch } from 'vue'
2
+
3
+ import { CPicker } from '../picker'
4
+
2
5
  import { CMultiSelectNativeSelect } from './CMultiSelectNativeSelect'
3
6
  import { CMultiSelectOptions } from './CMultiSelectOptions'
4
7
  import { CMultiSelectSelection } from './CMultiSelectSelection'
@@ -25,6 +28,14 @@ const CMultiSelect = defineComponent({
25
28
  required: false,
26
29
  default: true,
27
30
  },
31
+ /**
32
+ * Toggle the disabled state for the component.
33
+ */
34
+ disabled: {
35
+ type: Boolean,
36
+ required: false,
37
+ default: false,
38
+ },
28
39
  /**
29
40
  * It specifies that multiple options can be selected at once.
30
41
  *
@@ -137,6 +148,18 @@ const CMultiSelect = defineComponent({
137
148
  default: 'item(s) selected',
138
149
  required: false,
139
150
  },
151
+ /**
152
+ * Size the component small or large.
153
+ *
154
+ * @values 'sm', 'lg'
155
+ */
156
+ size: {
157
+ type: String,
158
+ required: false,
159
+ validator: (value: string) => {
160
+ return ['sm', 'lg'].includes(value)
161
+ },
162
+ },
140
163
  /**
141
164
  * Toggle the visibility of multi select dropdown.
142
165
  *
@@ -239,7 +262,6 @@ const CMultiSelect = defineComponent({
239
262
  : options.value
240
263
  }
241
264
 
242
- const multiSelectRef = ref<HTMLDivElement>()
243
265
  const nativeSelectRef = ref<HTMLSelectElement>()
244
266
  provide('nativeSelectRef', nativeSelectRef)
245
267
  const searchRef = ref<HTMLInputElement>()
@@ -247,15 +269,15 @@ const CMultiSelect = defineComponent({
247
269
  const options = ref<Option[]>(props.options)
248
270
  const vOptions = ref<Option[]>(props.options)
249
271
  const search = ref('')
250
- const visible = ref(props.visible)
272
+ const visible = ref<Boolean>(props.visible)
251
273
 
252
274
  const selected = ref<Option[]>(getSelectedOptions(props.options))
253
275
  const count = ref<number>(0)
254
276
 
255
277
  watch(
256
278
  () => props.options,
257
- () => {
258
- options.value = props.options
279
+ (newValue, oldValue) => {
280
+ if (JSON.stringify(newValue) !== JSON.stringify(oldValue)) options.value = newValue
259
281
  },
260
282
  )
261
283
 
@@ -283,27 +305,6 @@ const CMultiSelect = defineComponent({
283
305
  nativeSelectRef.value.dispatchEvent(new Event('change', { bubbles: true }))
284
306
  })
285
307
 
286
- onMounted(() => {
287
- window.addEventListener('click', handleClickOutside)
288
- window.addEventListener('keyup', handleKeyup)
289
- })
290
-
291
- onUnmounted(() => {
292
- window.removeEventListener('click', handleClickOutside)
293
- window.removeEventListener('keyup', handleKeyup)
294
- })
295
-
296
- const handleKeyup = (event: Event) => {
297
- if (multiSelectRef.value && !multiSelectRef.value.contains(event.target as HTMLElement)) {
298
- visible.value = false
299
- }
300
- }
301
- const handleClickOutside = (event: Event) => {
302
- if (multiSelectRef.value && !multiSelectRef.value.contains(event.target as HTMLElement)) {
303
- visible.value = false
304
- }
305
- }
306
-
307
308
  const handleSearchChange = (event: InputEvent) => {
308
309
  const target = event.target as HTMLInputElement
309
310
  search.value = target.value.toLowerCase()
@@ -343,74 +344,84 @@ const CMultiSelect = defineComponent({
343
344
  onChange: () => emit('change', selected.value),
344
345
  }),
345
346
  h(
346
- 'div',
347
+ CPicker,
347
348
  {
348
349
  class: [
349
350
  'form-multi-select',
350
351
  {
351
- show: visible.value,
352
+ 'form-multi-select-with-cleaner': props.cleaner,
353
+ disabled: props.disabled,
354
+ [`form-multi-select-${props.size}`]: props.size,
352
355
  'form-multi-select-selection-tags': props.multiple && props.selectionType === 'tags',
356
+ show: visible.value,
353
357
  },
354
358
  ],
355
- onClick: () => {
356
- visible.value = true
359
+ disabled: props.disabled,
360
+ onShow: () => {
357
361
  props.search && searchRef.value && searchRef.value.focus()
358
362
  },
359
- ref: multiSelectRef,
360
363
  },
361
- [
362
- h(CMultiSelectSelection, {
363
- multiple: props.multiple,
364
- onRemove: (option: Option) => handleOptionClick(option),
365
- search: props.search,
366
- selected: selected.value,
367
- selectionType: props.selectionType,
368
- selectionTypeCounterText: props.selectionTypeCounterText,
369
- }),
370
- props.multiple &&
371
- props.cleaner &&
372
- selected.value.length > 0 &&
373
- h('button', {
374
- type: 'button',
375
- class: 'form-multi-select-selection-cleaner',
376
- onClick: () => handleDeselectAll(),
377
- }),
378
- props.search &&
379
- h('input', {
380
- type: 'text',
381
- class: 'form-multi-select-search',
382
- autocomplete: 'off',
383
- ...(selected.value.length === 0 && { placeholder: props.placeholder }),
384
- ...(selected.value.length &&
385
- props.selectionType === 'counter' && {
386
- placeholder: `${selected.value.length} ${props.selectionTypeCounterText}`,
364
+ {
365
+ toggler: () =>
366
+ h('div', {}, [
367
+ h(CMultiSelectSelection, {
368
+ multiple: props.multiple,
369
+ onRemove: (option: Option) => !props.disabled && handleOptionClick(option),
370
+ search: props.search,
371
+ selected: selected.value,
372
+ selectionType: props.selectionType,
373
+ selectionTypeCounterText: props.selectionTypeCounterText,
374
+ }),
375
+ props.multiple &&
376
+ props.cleaner &&
377
+ selected.value.length > 0 &&
378
+ !props.disabled &&
379
+ h('button', {
380
+ type: 'button',
381
+ class: 'form-multi-select-selection-cleaner',
382
+ onClick: () => handleDeselectAll(),
383
+ }),
384
+ props.search &&
385
+ h('input', {
386
+ type: 'text',
387
+ class: 'form-multi-select-search',
388
+ autocomplete: 'off',
389
+ ...(selected.value.length === 0 && { placeholder: props.placeholder }),
390
+ ...(selected.value.length &&
391
+ props.selectionType === 'counter' && {
392
+ placeholder: `${selected.value.length} ${props.selectionTypeCounterText}`,
393
+ }),
394
+ ...(selected.value.length &&
395
+ !props.multiple && {
396
+ placeholder: selected.value.map((option) => option.text)[0],
397
+ }),
398
+ disabled: props.disabled,
399
+ onInput: (event: InputEvent) => handleSearchChange(event),
400
+ onKeydown: (event: KeyboardEvent) => handleSearchKeyDown(event),
401
+ ...(props.multiple &&
402
+ selected.value.length &&
403
+ props.selectionType !== 'counter' && { size: search.value.length + 2 }),
404
+ ref: searchRef,
387
405
  }),
388
- ...(selected.value.length &&
389
- !props.multiple && { placeholder: selected.value.map((option) => option.text)[0] }),
390
- onInput: (event: InputEvent) => handleSearchChange(event),
391
- onKeydown: (event: KeyboardEvent) => handleSearchKeyDown(event),
392
- ...(props.multiple &&
393
- selected.value.length &&
394
- props.selectionType !== 'counter' && { size: search.value.length + 2 }),
395
- ref: searchRef,
396
- }),
397
- h('div', { class: 'form-multi-select-dropdown' }, [
398
- props.multiple &&
399
- props.selectAll &&
400
- h(
401
- 'button',
402
- { class: 'form-multi-select-all', onClick: () => handleSelectAll() },
403
- props.selectAllLabel,
404
- ),
405
- h(CMultiSelectOptions, {
406
- onOptionClick: (option: Option) => handleOptionClick(option),
407
- options: vOptions.value,
408
- optionsMaxHeight: props.optionsMaxHeight,
409
- optionsStyle: props.optionsStyle,
410
- searchNoResultsLabel: props.searchNoResultsLabel,
411
- }),
412
- ]),
413
- ],
406
+ ]),
407
+ default: () =>
408
+ h('div', {}, [
409
+ props.multiple &&
410
+ props.selectAll &&
411
+ h(
412
+ 'button',
413
+ { class: 'form-multi-select-all', onClick: () => handleSelectAll() },
414
+ props.selectAllLabel,
415
+ ),
416
+ h(CMultiSelectOptions, {
417
+ onOptionClick: (option: Option) => handleOptionClick(option),
418
+ options: vOptions.value,
419
+ optionsMaxHeight: props.optionsMaxHeight,
420
+ optionsStyle: props.optionsStyle,
421
+ searchNoResultsLabel: props.searchNoResultsLabel,
422
+ }),
423
+ ]),
424
+ },
414
425
  ),
415
426
  ]
416
427
  },
@@ -388,6 +388,9 @@ const CSmartTable = defineComponent({
388
388
 
389
389
  // functions
390
390
 
391
+ const isLazy = (columnFilter?: boolean | { lazy?: boolean; external?: boolean }) =>
392
+ columnFilter && typeof columnFilter === 'object' && columnFilter.lazy === true
393
+
391
394
  const isSortable = (i: number): boolean | undefined => {
392
395
  const isDataColumn = itemsDataColumns.value.includes(rawColumnNames.value[i])
393
396
  let column
@@ -470,12 +473,9 @@ const CSmartTable = defineComponent({
470
473
  })
471
474
  }
472
475
 
473
- const columnFilterChange = (colName: string, value: string, type: string): void => {
474
- const isLazy =
475
- props.columnFilter &&
476
- typeof props.columnFilter === 'object' &&
477
- props.columnFilter.lazy === true
478
- if ((isLazy && type === 'input') || (!isLazy && type === 'change')) {
476
+ const columnFilterChange = (colName: string, value: any, type?: string): void => {
477
+ const _isLazy = isLazy(props.columnFilter)
478
+ if ((_isLazy && type === 'input') || (!_isLazy && type === 'change')) {
479
479
  return
480
480
  }
481
481
  activePage.value = 1
@@ -484,11 +484,8 @@ const CSmartTable = defineComponent({
484
484
  }
485
485
 
486
486
  const tableFilterChange = (value: string, type: string): void => {
487
- const isLazy =
488
- props.columnFilter &&
489
- typeof props.columnFilter === 'object' &&
490
- props.columnFilter.lazy === true
491
- if ((isLazy && type === 'input') || (!isLazy && type === 'change')) {
487
+ const _isLazy = isLazy(props.columnFilter)
488
+ if ((_isLazy && type === 'input') || (!_isLazy && type === 'change')) {
492
489
  return
493
490
  }
494
491
  activePage.value = 1
@@ -524,7 +521,7 @@ const CSmartTable = defineComponent({
524
521
 
525
522
  // variables
526
523
 
527
- const columnFiltered = () => {
524
+ const filteredColumns = computed(() => {
528
525
  let _items = items.value
529
526
  if (
530
527
  props.columnFilter &&
@@ -534,6 +531,11 @@ const CSmartTable = defineComponent({
534
531
  return _items
535
532
  }
536
533
  Object.entries(columnFilterState.value).forEach(([key, value]) => {
534
+ if (value instanceof Function) {
535
+ _items = _items.filter((item) => value(item[key]))
536
+ return
537
+ }
538
+
537
539
  const columnFilter = String(value).toLowerCase()
538
540
  if (columnFilter && itemsDataColumns.value.includes(key)) {
539
541
  _items = _items.filter((item) => {
@@ -542,10 +544,10 @@ const CSmartTable = defineComponent({
542
544
  }
543
545
  })
544
546
  return _items
545
- }
547
+ })
546
548
 
547
- const tableFiltered = computed(() => {
548
- let items = columnFiltered()
549
+ const filteredTable = computed(() => {
550
+ let items = filteredColumns.value
549
551
  if (
550
552
  !tableFilterState.value ||
551
553
  (props.tableFilter && typeof props.tableFilter === 'object' && props.tableFilter.external)
@@ -570,12 +572,12 @@ const CSmartTable = defineComponent({
570
572
  typeof props.columnSorter === 'object' &&
571
573
  props.columnSorter.external)
572
574
  ) {
573
- return tableFiltered.value
575
+ return filteredTable.value
574
576
  }
575
577
  //if values in column are to be sorted by numeric value they all have to be type number
576
578
  // const flip = sorterState.asc ? 1 : -1
577
579
  const flip = sorterState.state === 'asc' ? 1 : sorterState.state === 'desc' ? -1 : 0
578
- const sorted = tableFiltered.value.slice().sort((item, item2) => {
580
+ const sorted = filteredTable.value.slice().sort((item, item2) => {
579
581
  const value = item[col]
580
582
  const value2 = item2[col]
581
583
  const a = typeof value === 'number' ? value : String(value).toLowerCase()
@@ -683,9 +685,12 @@ const CSmartTable = defineComponent({
683
685
  columnFilterValue: columnFilterState.value,
684
686
  columns: props.columns ? props.columns : rawColumnNames.value,
685
687
  columnSorter: props.columnSorter,
688
+ items: items.value,
686
689
  selectable: props.selectable,
687
690
  selectAll: selectedAll.value,
688
691
  sorterState: sorterState,
692
+ onCustomFilterChange: (key: string, value: any) =>
693
+ columnFilterChange(key, value),
689
694
  onFilterInput: (key: string, value: string) =>
690
695
  columnFilterChange(key, value, 'input'),
691
696
  onFilterChange: (key: string, value: string) =>
@@ -757,10 +762,6 @@ const CSmartTable = defineComponent({
757
762
  columns: props.columns ? props.columns : rawColumnNames.value,
758
763
  selectable: props.selectable,
759
764
  selectAll: selectedAll.value,
760
- onFilterInput: (key: string, value: string) =>
761
- columnFilterChange(key, value, 'input'),
762
- onFilterChange: (key: string, value: string) =>
763
- columnFilterChange(key, value, 'change'),
764
765
  onSelectAllChecked: () => handleSelectAllChecked(),
765
766
  }),
766
767
  ],
@@ -9,6 +9,7 @@ import {
9
9
  Column,
10
10
  ColumnFilter,
11
11
  ColumnFilterValue,
12
+ Item,
12
13
  Sorter,
13
14
  SorterValue,
14
15
  } from './CSmartTableInterface'
@@ -44,6 +45,11 @@ const CSmartTableHead = defineComponent({
44
45
  default: () => [],
45
46
  required: false,
46
47
  },
48
+ items: {
49
+ type: Array as PropType<Item[]>,
50
+ default: () => [],
51
+ required: false,
52
+ },
47
53
  selectable: Boolean,
48
54
  selectAll: [Boolean, String],
49
55
  sorterState: {
@@ -52,7 +58,7 @@ const CSmartTableHead = defineComponent({
52
58
  require: false,
53
59
  },
54
60
  },
55
- emits: ['filterInput', 'filterChange', 'selectAllChecked', 'sortClick'],
61
+ emits: ['customFilterChange', 'filterInput', 'filterChange', 'selectAllChecked', 'sortClick'],
56
62
  setup(props, { slots, emit }) {
57
63
  const handleSortClick = (key: string, index: number) => {
58
64
  emit('sortClick', key, index)
@@ -110,6 +116,10 @@ const CSmartTableHead = defineComponent({
110
116
  return 0
111
117
  }
112
118
 
119
+ const getValues = (items: Item[], key: string) => {
120
+ return items.map((a) => a[key])
121
+ }
122
+
113
123
  const columnSorterIcon = (column: Column | string) => {
114
124
  if (getColumnSorterState(key(column)) === 0) {
115
125
  return h(
@@ -135,6 +145,10 @@ const CSmartTableHead = defineComponent({
135
145
  return
136
146
  }
137
147
 
148
+ const handleOnCustomFilterChange = (key: string, value: any) => {
149
+ emit('customFilterChange', key, value)
150
+ }
151
+
138
152
  const handleFilterInput = (key: string, value: string) => {
139
153
  emit('filterInput', key, value)
140
154
  }
@@ -212,29 +226,36 @@ const CSmartTableHead = defineComponent({
212
226
  },
213
227
  {
214
228
  default: () =>
215
- (typeof column !== 'object'
216
- ? true
217
- : typeof column.filter === 'undefined'
218
- ? true
219
- : column.filter) &&
220
- h(CFormInput, {
221
- size: 'sm',
222
- onInput: (event: Event) =>
223
- handleFilterInput(
224
- key(column),
225
- (event.target as HTMLInputElement).value,
226
- ),
227
- onChange: (event: Event) =>
228
- handleFilterChange(
229
- key(column),
230
- (event.target as HTMLInputElement).value,
231
- ),
232
- 'aria-label': `column name: '${label(column)}' filter input`,
233
- ...(props.columnFilterValue &&
234
- props.columnFilterValue[key(column)] && {
235
- value: props.columnFilterValue[key(column)],
236
- }),
237
- }),
229
+ (
230
+ typeof column !== 'object'
231
+ ? true
232
+ : typeof column.filter === 'undefined'
233
+ ? true
234
+ : column.filter
235
+ )
236
+ ? typeof column !== 'string' && typeof column.filter === 'function'
237
+ ? column.filter(getValues(props.items, key(column)), (value: any) =>
238
+ handleOnCustomFilterChange(key(column), value),
239
+ )
240
+ : h(CFormInput, {
241
+ size: 'sm',
242
+ onInput: (event: Event) =>
243
+ handleFilterInput(
244
+ key(column),
245
+ (event.target as HTMLInputElement).value,
246
+ ),
247
+ onChange: (event: Event) =>
248
+ handleFilterChange(
249
+ key(column),
250
+ (event.target as HTMLInputElement).value,
251
+ ),
252
+ 'aria-label': `column name: '${label(column)}' filter input`,
253
+ ...(props.columnFilterValue &&
254
+ props.columnFilterValue[key(column)] && {
255
+ value: props.columnFilterValue[key(column)],
256
+ }),
257
+ })
258
+ : '',
238
259
  },
239
260
  ),
240
261
  ),
@@ -4,7 +4,7 @@
4
4
  /* eslint-disable no-unused-vars */
5
5
  /* eslint-disable @typescript-eslint/ban-types */
6
6
  export interface Column {
7
- filter?: boolean
7
+ filter?: boolean | ((values: any[], onChange: (value: any) => void) => VNode)
8
8
  key: string
9
9
  label?: string
10
10
  sorter?: boolean
@@ -54,7 +54,7 @@ const CSmartTableItemsPerPageSelector = defineComponent({
54
54
  h(
55
55
  CFormSelect,
56
56
  {
57
- defaultValue: props.itemsPerPage,
57
+ value: props.itemsPerPage,
58
58
  onChange: handleChange,
59
59
  },
60
60
  {