@coreui/vue-pro 5.0.0-alpha.0 → 5.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +1 -1
  2. package/dist/components/calendar/CCalendar.d.ts +58 -32
  3. package/dist/components/calendar/utils.d.ts +11 -4
  4. package/dist/components/close-button/CCloseButton.d.ts +9 -0
  5. package/dist/components/date-picker/CDatePicker.d.ts +89 -0
  6. package/dist/components/date-range-picker/CDateRangePicker.d.ts +89 -0
  7. package/dist/components/dropdown/CDropdown.d.ts +13 -28
  8. package/dist/components/dropdown/CDropdownToggle.d.ts +19 -9
  9. package/dist/components/dropdown/types.d.ts +15 -0
  10. package/dist/components/dropdown/utils.d.ts +6 -0
  11. package/dist/components/form/CFormCheck.d.ts +4 -4
  12. package/dist/components/modal/CModal.d.ts +19 -0
  13. package/dist/components/multi-select/CMultiSelect.d.ts +1 -1
  14. package/dist/components/multi-select/CMultiSelectSelection.d.ts +1 -1
  15. package/dist/components/multi-select/types.d.ts +2 -2
  16. package/dist/components/multi-select/utils.d.ts +2 -2
  17. package/dist/components/offcanvas/COffcanvas.d.ts +9 -0
  18. package/dist/components/progress/CProgress.d.ts +102 -3
  19. package/dist/components/progress/CProgressStacked.d.ts +10 -0
  20. package/dist/components/progress/index.d.ts +2 -1
  21. package/dist/components/smart-table/CSmartTable.d.ts +0 -4
  22. package/dist/components/smart-table/CSmartTableBody.d.ts +12 -1
  23. package/dist/components/smart-table/types.d.ts +2 -2
  24. package/dist/components/smart-table/utils.d.ts +4 -2
  25. package/dist/composables/index.d.ts +2 -1
  26. package/dist/composables/useDebouncedCallback.d.ts +1 -0
  27. package/dist/index.es.js +1035 -2716
  28. package/dist/index.es.js.map +1 -1
  29. package/dist/index.js +1035 -2714
  30. package/dist/index.js.map +1 -1
  31. package/package.json +14 -14
  32. package/src/components/breadcrumb/CBreadcrumb.ts +1 -0
  33. package/src/components/button/CButton.ts +5 -5
  34. package/src/components/calendar/CCalendar.ts +444 -179
  35. package/src/components/calendar/utils.ts +93 -55
  36. package/src/components/carousel/CCarousel.ts +2 -5
  37. package/src/components/close-button/CCloseButton.ts +5 -0
  38. package/src/components/date-picker/CDatePicker.ts +43 -0
  39. package/src/components/date-range-picker/CDateRangePicker.ts +130 -83
  40. package/src/components/date-range-picker/utils.ts +2 -2
  41. package/src/components/dropdown/CDropdown.ts +23 -43
  42. package/src/components/dropdown/CDropdownMenu.ts +4 -19
  43. package/src/components/dropdown/CDropdownToggle.ts +44 -38
  44. package/src/components/dropdown/types.ts +11 -0
  45. package/src/components/dropdown/utils.ts +71 -0
  46. package/src/components/form/CFormCheck.ts +4 -4
  47. package/src/components/modal/CModal.ts +15 -1
  48. package/src/components/multi-select/CMultiSelect.ts +5 -8
  49. package/src/components/multi-select/CMultiSelectOptions.ts +1 -1
  50. package/src/components/multi-select/CMultiSelectSelection.ts +3 -3
  51. package/src/components/multi-select/types.ts +2 -2
  52. package/src/components/multi-select/utils.ts +5 -5
  53. package/src/components/navbar/CNavbar.ts +1 -1
  54. package/src/components/offcanvas/COffcanvas.ts +6 -0
  55. package/src/components/picker/CPicker.ts +1 -1
  56. package/src/components/progress/CProgress.ts +67 -9
  57. package/src/components/progress/CProgressBar.ts +4 -6
  58. package/src/components/progress/CProgressStacked.ts +19 -0
  59. package/src/components/progress/index.ts +3 -1
  60. package/src/components/sidebar/CSidebar.ts +1 -1
  61. package/src/components/smart-pagination/CSmartPagination.ts +20 -5
  62. package/src/components/smart-table/CSmartTable.ts +21 -12
  63. package/src/components/smart-table/CSmartTableBody.ts +30 -31
  64. package/src/components/smart-table/CSmartTableHead.ts +39 -12
  65. package/src/components/smart-table/types.ts +2 -2
  66. package/src/components/smart-table/utils.ts +41 -5
  67. package/src/components/time-picker/CTimePicker.ts +4 -2
  68. package/src/components/tooltip/CTooltip.ts +1 -1
  69. package/src/components/widgets/CWidgetStatsA.ts +1 -3
  70. package/src/components/widgets/CWidgetStatsB.ts +2 -4
  71. package/src/components/widgets/CWidgetStatsC.ts +2 -2
  72. package/src/components/widgets/CWidgetStatsD.ts +1 -1
  73. package/src/components/widgets/CWidgetStatsE.ts +1 -1
  74. package/src/components/widgets/CWidgetStatsF.ts +1 -1
  75. package/src/components/widgets/__tests__/__snapshots__/CWidgetStatsE.spec.ts.snap +1 -1
  76. package/src/composables/index.ts +2 -1
  77. package/src/composables/useDebouncedCallback.ts +16 -0
  78. package/src/utils/isObjectInArray.ts +1 -1
@@ -1,7 +1,5 @@
1
1
  import { defineComponent, h, onMounted, PropType, ref, watch } from 'vue'
2
2
 
3
- import { format as dateFormat } from 'date-fns'
4
-
5
3
  import { CButton } from '../button'
6
4
  import { CCalendar } from '../calendar'
7
5
  import { CFormControlWrapper } from './../form/CFormControlWrapper'
@@ -10,6 +8,7 @@ import { CTimePicker } from '../time-picker'
10
8
 
11
9
  import { getLocalDateFromString } from './utils'
12
10
 
11
+ import { useDebouncedCallback } from '../../composables'
13
12
  import { Color } from '../props'
14
13
 
15
14
  const CDateRangePicker = defineComponent({
@@ -216,6 +215,27 @@ const CDateRangePicker = defineComponent({
216
215
  * Toggle the readonly state for the component.
217
216
  */
218
217
  inputReadOnly: Boolean,
218
+ /**
219
+ * Custom function to format the selected date into a string according to a custom format.
220
+ *
221
+ * @since v5.0.0-alpha.4
222
+ */
223
+ inputDateFormat: Function,
224
+ /**
225
+ * Custom function to parse the input value into a valid Date object.
226
+ *
227
+ * @since v5.0.0-alpha.4
228
+ */
229
+ inputDateParse: Function,
230
+ /**
231
+ * Defines the delay (in milliseconds) for the input field's onChange event.
232
+ *
233
+ * @since v5.0.0-alpha.4
234
+ */
235
+ inputOnChangeDelay: {
236
+ type: Number,
237
+ default: 750,
238
+ },
219
239
  /**
220
240
  * Set component validation state to invalid.
221
241
  *
@@ -293,6 +313,16 @@ const CDateRangePicker = defineComponent({
293
313
  * @since 4.9.0
294
314
  */
295
315
  selectAdjacementDays: Boolean,
316
+ /**
317
+ * Specify the type of date selection as day, week, month, or year.
318
+ *
319
+ * @since 5.0.0-alpha.1
320
+ */
321
+ selectionType: {
322
+ type: String as PropType<'day' | 'week' | 'month' | 'year'>,
323
+ default: 'day',
324
+ validator: (value: string) => ['day', 'week', 'month', 'year'].includes(value),
325
+ },
296
326
  /**
297
327
  * Set whether to display dates in adjacent months (non-selectable) at the start and end of the current month.
298
328
  *
@@ -302,6 +332,12 @@ const CDateRangePicker = defineComponent({
302
332
  type: Boolean,
303
333
  default: true,
304
334
  },
335
+ /**
336
+ * Set whether to display week numbers in the calendar.
337
+ *
338
+ * @since 5.0.0-alpha.1
339
+ */
340
+ showWeekNumber: Boolean,
305
341
  /**
306
342
  * Default icon or character character that separates two dates.
307
343
  */
@@ -414,6 +450,12 @@ const CDateRangePicker = defineComponent({
414
450
  return false
415
451
  },
416
452
  },
453
+ /**
454
+ * Label displayed over week numbers in the calendar.
455
+ *
456
+ * @since 5.0.0-alpha.1
457
+ */
458
+ weekNumbersLabel: String,
417
459
  },
418
460
  emits: [
419
461
  /**
@@ -458,27 +500,22 @@ const CDateRangePicker = defineComponent({
458
500
  const inputStartRef = ref()
459
501
  const formRef = ref()
460
502
 
503
+ const calendarDate = ref<Date | string | null>(props.calendarDate ?? null)
504
+ const endDate = ref<Date | string | null>(props.endDate ?? null)
505
+ const maxDate = ref<Date | string | null>(props.maxDate ?? null)
506
+ const minDate = ref<Date | string | null>(props.minDate ?? null)
507
+ const startDate = ref<Date | string | null>(props.startDate ?? null)
461
508
  const visible = ref(props.visible)
462
- const calendarDate = ref<Date>(
463
- props.calendarDate
464
- ? new Date(props.calendarDate)
465
- : props.startDate
466
- ? new Date(props.startDate)
467
- : props.endDate
468
- ? new Date(props.endDate)
469
- : new Date(),
509
+
510
+ const initialStartDate = ref<Date | string | null>(props.startDate ?? null)
511
+ const initialEndDate = ref<Date | string | null>(props.endDate ?? null)
512
+ const inputStartHoverValue = ref<Date | string | null>(null)
513
+ const inputEndHoverValue = ref<Date | string | null>(null)
514
+ const isValid = ref<boolean | undefined>(
515
+ props.valid ?? (props.invalid === true ? false : undefined),
470
516
  )
471
- const inputStartHoverValue = ref<Date | null>(null)
472
- const inputEndHoverValue = ref<Date | null>(null)
473
- const startDate = ref<Date | null>(props.startDate ? new Date(props.startDate) : null)
474
- const endDate = ref<Date | null>(props.endDate ? new Date(props.endDate) : null)
475
- const initialStartDate = ref<Date | null>(startDate.value ? new Date(startDate.value) : null)
476
- const initialEndDate = ref<Date | null>(endDate.value ? new Date(endDate.value) : null)
477
- const maxDate = ref(props.maxDate && new Date(props.maxDate))
478
- const minDate = ref(props.minDate && new Date(props.minDate))
479
- const selectEndDate = ref(false)
480
- const isValid = ref(props.valid || (props.invalid && false))
481
517
  const isMobile = ref(false)
518
+ const selectEndDate = ref(false)
482
519
 
483
520
  onMounted(() => {
484
521
  isMobile.value = window.innerWidth < 768
@@ -487,7 +524,7 @@ const CDateRangePicker = defineComponent({
487
524
  watch(
488
525
  () => [props.valid, props.invalid],
489
526
  () => {
490
- isValid.value = props.valid || (props.invalid && false)
527
+ isValid.value = props.valid ?? (props.invalid === true ? false : undefined)
491
528
  },
492
529
  )
493
530
 
@@ -495,9 +532,8 @@ const CDateRangePicker = defineComponent({
495
532
  () => props.startDate,
496
533
  () => {
497
534
  if (props.startDate) {
498
- const date = new Date(props.startDate)
499
- calendarDate.value = date
500
- startDate.value = date
535
+ calendarDate.value = props.startDate
536
+ startDate.value = props.startDate
501
537
  }
502
538
  },
503
539
  )
@@ -506,9 +542,8 @@ const CDateRangePicker = defineComponent({
506
542
  () => props.endDate,
507
543
  () => {
508
544
  if (props.endDate) {
509
- const date = new Date(props.endDate)
510
- calendarDate.value = date
511
- endDate.value = date
545
+ calendarDate.value = props.endDate
546
+ endDate.value = props.endDate
512
547
  }
513
548
  },
514
549
  )
@@ -517,7 +552,7 @@ const CDateRangePicker = defineComponent({
517
552
  () => props.maxDate,
518
553
  () => {
519
554
  if (props.maxDate) {
520
- maxDate.value = new Date(props.maxDate)
555
+ maxDate.value = props.maxDate
521
556
  }
522
557
  },
523
558
  )
@@ -526,7 +561,7 @@ const CDateRangePicker = defineComponent({
526
561
  () => props.minDate,
527
562
  () => {
528
563
  if (props.minDate) {
529
- minDate.value = new Date(props.minDate)
564
+ minDate.value = props.minDate
530
565
  }
531
566
  },
532
567
  )
@@ -547,15 +582,21 @@ const CDateRangePicker = defineComponent({
547
582
  }
548
583
  })
549
584
 
550
- const formatDate = (date: Date) => {
551
- return props.format
552
- ? dateFormat(date, props.format)
585
+ const formatDate = (date: Date | string) => {
586
+ if (props.selectionType !== 'day') {
587
+ return date
588
+ }
589
+
590
+ const _date = new Date(date)
591
+
592
+ return props.inputDateFormat
593
+ ? props.inputDateFormat(_date)
553
594
  : props.timepicker
554
- ? date.toLocaleString(props.locale)
555
- : date.toLocaleDateString(props.locale)
595
+ ? _date.toLocaleString(props.locale)
596
+ : _date.toLocaleDateString(props.locale)
556
597
  }
557
598
 
558
- const setInputValue = (date: Date | null) => {
599
+ const setInputValue = (date: Date | string | null) => {
559
600
  if (date) {
560
601
  return formatDate(date)
561
602
  }
@@ -563,21 +604,13 @@ const CDateRangePicker = defineComponent({
563
604
  return ''
564
605
  }
565
606
 
566
- const handleCalendarCellHover = (date: Date | null) => {
607
+ const handleDateHover = (date: Date | string | null) => {
567
608
  if (selectEndDate.value) {
568
609
  inputEndHoverValue.value = date
569
610
  return
570
611
  }
571
- inputStartHoverValue.value = date
572
- }
573
612
 
574
- const handleCalendarDateChange = (date: Date, difference?: number) => {
575
- if (difference) {
576
- calendarDate.value = new Date(date.getFullYear(), date.getMonth() - difference, 1)
577
- return
578
- }
579
-
580
- calendarDate.value = date
613
+ inputStartHoverValue.value = date
581
614
  }
582
615
 
583
616
  const handleFormValidation = (form: HTMLFormElement) => {
@@ -643,6 +676,20 @@ const CDateRangePicker = defineComponent({
643
676
  emit('update:end-date', null)
644
677
  }
645
678
 
679
+ const handleOnChange = (value: string, input: string) => {
680
+ const date = props.inputDateParse
681
+ ? props.inputDateParse(value)
682
+ : getLocalDateFromString(value, props.locale, props.timepicker)
683
+ if (date instanceof Date && date.getTime()) {
684
+ calendarDate.value = date
685
+ if (input === 'start') {
686
+ startDate.value = date
687
+ } else {
688
+ endDate.value = date
689
+ }
690
+ }
691
+ }
692
+
646
693
  const InputGroup = () =>
647
694
  h(
648
695
  'div',
@@ -663,17 +710,13 @@ const CDateRangePicker = defineComponent({
663
710
  onClick: () => {
664
711
  selectEndDate.value = false
665
712
  },
666
- onInput: (event: Event) => {
667
- const date = getLocalDateFromString(
668
- (event.target as HTMLInputElement).value,
669
- props.locale,
670
- props.timepicker,
671
- )
672
- if (date instanceof Date && date.getTime()) {
673
- calendarDate.value = date
674
- startDate.value = date
675
- }
676
- },
713
+ onChange: (event: Event) =>
714
+ handleOnChange((event.target as HTMLInputElement).value, 'start'),
715
+ onInput: (event: Event) =>
716
+ useDebouncedCallback(
717
+ () => handleOnChange((event.target as HTMLInputElement).value, 'start'),
718
+ props.inputOnChangeDelay,
719
+ ),
677
720
  placeholder: Array.isArray(props.placeholder)
678
721
  ? props.placeholder[0]
679
722
  : props.placeholder,
@@ -699,17 +742,13 @@ const CDateRangePicker = defineComponent({
699
742
  onClick: () => {
700
743
  selectEndDate.value = true
701
744
  },
702
- onInput: (event: Event) => {
703
- const date = getLocalDateFromString(
704
- (event.target as HTMLInputElement).value,
705
- props.locale,
706
- props.timepicker,
707
- )
708
- if (date instanceof Date && date.getTime()) {
709
- calendarDate.value = date
710
- endDate.value = date
711
- }
712
- },
745
+ onChange: (event: Event) =>
746
+ handleOnChange((event.target as HTMLInputElement).value, 'end'),
747
+ onInput: (event: Event) =>
748
+ useDebouncedCallback(
749
+ () => handleOnChange((event.target as HTMLInputElement).value, 'end'),
750
+ props.inputOnChangeDelay,
751
+ ),
713
752
  placeholder: props.placeholder[1],
714
753
  readonly: props.inputReadOnly || typeof props.format === 'string',
715
754
  required: props.required,
@@ -863,31 +902,36 @@ const CDateRangePicker = defineComponent({
863
902
  h(
864
903
  CCalendar,
865
904
  {
866
- calendarDate: new Date(
867
- calendarDate.value.getFullYear(),
868
- calendarDate.value.getMonth(),
869
- 1,
870
- ),
871
- calendars: props.calendars,
905
+ calendarDate: calendarDate.value,
906
+ calendars: isMobile.value ? 1 : props.calendars,
907
+ class: 'date-picker-calendars',
872
908
  dayFormat: props.dayFormat,
873
909
  disabledDates: props.disabledDates,
874
- ...(endDate.value && { endDate: endDate.value }),
910
+ endDate: endDate.value,
875
911
  firstDayOfWeek: props.firstDayOfWeek,
876
912
  locale: props.locale,
877
913
  maxDate: maxDate.value,
878
914
  minDate: minDate.value,
879
- navYearFirst: props.navYearFirst,
880
915
  navigation: props.navigation,
916
+ navYearFirst: props.navYearFirst,
881
917
  range: props.range,
882
- selectEndDate: selectEndDate.value,
883
918
  selectAdjacementDays: props.selectAdjacementDays,
919
+ selectEndDate: selectEndDate.value,
920
+ selectionType: props.selectionType,
884
921
  showAdjacementDays: props.showAdjacementDays,
885
- ...(startDate.value && { startDate: startDate.value }),
886
- onCalendarCellHover: (date: Date | null) =>
887
- handleCalendarCellHover(date),
888
- onCalendarDateChange: (date: Date) => handleCalendarDateChange(date),
922
+ showWeekNumber: props.showWeekNumber,
923
+ startDate: startDate.value,
924
+ weekdayFormat: props.weekdayFormat,
925
+ weekNumbersLabel: props.weekNumbersLabel,
926
+ onDateHover: (date: Date | null) => handleDateHover(date),
927
+ onCalendarDateChange: (date: Date) => {
928
+ calendarDate.value = date
929
+ },
889
930
  onStartDateChange: (date: Date) => handleStartDateChange(date),
890
931
  onEndDateChange: (date: Date) => handleEndDateChange(date),
932
+ onSelectEndChange: (value: boolean) => {
933
+ selectEndDate.value = value
934
+ },
891
935
  },
892
936
  {
893
937
  /**
@@ -931,7 +975,7 @@ const CDateRangePicker = defineComponent({
931
975
  locale: props.locale,
932
976
  onChange: (_: any, __: any, date: Date) =>
933
977
  handleStartDateChange(date),
934
- time: startDate.value,
978
+ time: startDate.value && new Date(startDate.value),
935
979
  variant: 'select',
936
980
  }),
937
981
  h(CTimePicker, {
@@ -940,7 +984,7 @@ const CDateRangePicker = defineComponent({
940
984
  locale: props.locale,
941
985
  onChange: (_: any, __: any, date: Date) =>
942
986
  handleEndDateChange(date),
943
- time: endDate.value,
987
+ time: endDate.value && new Date(endDate.value),
944
988
  variant: 'select',
945
989
  }),
946
990
  ]
@@ -960,7 +1004,10 @@ const CDateRangePicker = defineComponent({
960
1004
  index === 0
961
1005
  ? handleStartDateChange(date)
962
1006
  : handleEndDateChange(date),
963
- time: index === 0 ? startDate.value : endDate.value,
1007
+ time:
1008
+ index === 0
1009
+ ? startDate.value && new Date(startDate.value)
1010
+ : endDate.value && new Date(endDate.value),
964
1011
  variant: 'select',
965
1012
  }),
966
1013
  ),
@@ -32,9 +32,9 @@ export const getLocalDateFromString = (string: string, locale: string, time?: bo
32
32
  Number(partials.groups['month']) - 1,
33
33
  Number(partials.groups['day']),
34
34
  partials.groups['ampm']
35
- ? partials.groups['ampm'] === 'PM'
35
+ ? (partials.groups['ampm'] === 'PM'
36
36
  ? Number(partials.groups['hour']) + 12
37
- : Number(partials.groups['hour'])
37
+ : Number(partials.groups['hour']))
38
38
  : Number(partials.groups['hour']),
39
39
  Number(partials.groups['minute']),
40
40
  Number(partials.groups['second']),
@@ -2,51 +2,11 @@ import { defineComponent, h, ref, provide, watch, PropType } from 'vue'
2
2
  import type { Placement } from '@popperjs/core'
3
3
 
4
4
  import { usePopper } from '../../composables'
5
- import type { Placements, Triggers } from '../../types'
5
+ import type { Triggers } from '../../types'
6
6
  import { isRTL } from '../../utils'
7
7
 
8
- export type Directions = 'start' | 'end'
9
-
10
- export type Breakpoints =
11
- | { xs: Directions }
12
- | { sm: Directions }
13
- | { md: Directions }
14
- | { lg: Directions }
15
- | { xl: Directions }
16
- | { xxl: Directions }
17
-
18
- export type Alignments = Directions | Breakpoints
19
-
20
- const getPlacement = (
21
- placement: Placement,
22
- direction: string | undefined,
23
- alignment: Alignments | string | undefined,
24
- isRTL: boolean,
25
- ): Placements => {
26
- let _placement = placement
27
-
28
- if (direction === 'dropup') {
29
- _placement = isRTL ? 'top-end' : 'top-start'
30
- }
31
-
32
- if (direction === 'dropup-center') {
33
- _placement = 'top'
34
- }
35
-
36
- if (direction === 'dropend') {
37
- _placement = isRTL ? 'left-start' : 'right-start'
38
- }
39
-
40
- if (direction === 'dropstart') {
41
- _placement = isRTL ? 'right-start' : 'left-start'
42
- }
43
-
44
- if (alignment === 'end') {
45
- _placement = isRTL ? 'bottom-start' : 'bottom-end'
46
- }
47
-
48
- return _placement
49
- }
8
+ import type { Alignments } from './types'
9
+ import { getNextActiveElement, getPlacement } from './utils'
50
10
 
51
11
  const CDropdown = defineComponent({
52
12
  name: 'CDropdown',
@@ -214,6 +174,8 @@ const CDropdown = defineComponent({
214
174
  popper.value && initPopper(dropdownToggleRef.value, dropdownMenuRef.value, popperConfig)
215
175
  window.addEventListener('mouseup', handleMouseUp)
216
176
  window.addEventListener('keyup', handleKeyup)
177
+ dropdownToggleRef.value.addEventListener('keydown', handleKeydown)
178
+ dropdownMenuRef.value.addEventListener('keydown', handleKeydown)
217
179
  emit('show')
218
180
  return
219
181
  }
@@ -221,6 +183,9 @@ const CDropdown = defineComponent({
221
183
  popper.value && destroyPopper()
222
184
  window.removeEventListener('mouseup', handleMouseUp)
223
185
  window.removeEventListener('keyup', handleKeyup)
186
+ dropdownToggleRef.value &&
187
+ dropdownToggleRef.value.removeEventListener('keydown', handleKeydown)
188
+ dropdownMenuRef.value && dropdownMenuRef.value.removeEventListener('keydown', handleKeydown)
224
189
  emit('hide')
225
190
  })
226
191
 
@@ -235,6 +200,21 @@ const CDropdown = defineComponent({
235
200
  provide('dropdownToggleRef', dropdownToggleRef)
236
201
  provide('dropdownMenuRef', dropdownMenuRef)
237
202
 
203
+ const handleKeydown = (event: KeyboardEvent) => {
204
+ if (
205
+ visible.value &&
206
+ dropdownMenuRef.value &&
207
+ (event.key === 'ArrowDown' || event.key === 'ArrowUp')
208
+ ) {
209
+ event.preventDefault()
210
+ const target = event.target as HTMLElement
211
+ const items: HTMLElement[] = Array.from(
212
+ dropdownMenuRef.value.querySelectorAll('.dropdown-item:not(.disabled):not(:disabled)'),
213
+ )
214
+ getNextActiveElement(items, target, event.key === 'ArrowDown', true).focus()
215
+ }
216
+ }
217
+
238
218
  const handleKeyup = (event: KeyboardEvent) => {
239
219
  if (props.autoClose === false) {
240
220
  return
@@ -1,5 +1,7 @@
1
1
  import { defineComponent, h, inject, Ref } from 'vue'
2
2
 
3
+ import { getAlignmentClassNames } from './utils'
4
+
3
5
  const CDropdownMenu = defineComponent({
4
6
  name: 'CDropdownMenu',
5
7
  props: {
@@ -20,32 +22,15 @@ const CDropdownMenu = defineComponent({
20
22
 
21
23
  const { alignment, dark, popper } = config
22
24
 
23
- // eslint-disable-next-line @typescript-eslint/ban-types, unicorn/consistent-function-scoping
24
- const alignmentClassNames = (alignment: object | string) => {
25
- const classNames: string[] = []
26
- if (typeof alignment === 'object') {
27
- Object.keys(alignment).map((key) => {
28
- classNames.push(`dropdown-menu${key === 'xs' ? '' : `-${key}`}-${alignment[key]}`)
29
- })
30
- }
31
- if (typeof alignment === 'string') {
32
- classNames.push(`dropdown-menu-${alignment}`)
33
- }
34
- return classNames
35
- }
36
-
37
25
  return () =>
38
26
  h(
39
27
  props.component,
40
28
  {
41
- class: [
42
- 'dropdown-menu',
43
- { 'dropdown-menu-dark': dark, show: visible.value },
44
- alignmentClassNames(alignment),
45
- ],
29
+ class: ['dropdown-menu', { show: visible.value }, getAlignmentClassNames(alignment)],
46
30
  ...((typeof alignment === 'object' || !popper) && {
47
31
  'data-coreui-popper': 'static',
48
32
  }),
33
+ ...(dark && { 'data-coreui-theme': 'dark' }),
49
34
  ref: dropdownMenuRef,
50
35
  },
51
36
  props.component === 'ul'
@@ -1,4 +1,14 @@
1
- import { cloneVNode, defineComponent, h, inject, onMounted, PropType, Ref, ref } from 'vue'
1
+ import {
2
+ cloneVNode,
3
+ computed,
4
+ defineComponent,
5
+ h,
6
+ inject,
7
+ onMounted,
8
+ PropType,
9
+ Ref,
10
+ ref,
11
+ } from 'vue'
2
12
 
3
13
  import { CButton } from '../button'
4
14
 
@@ -8,10 +18,6 @@ import type { Triggers } from '../../types'
8
18
  const CDropdownToggle = defineComponent({
9
19
  name: 'CDropdownToggle',
10
20
  props: {
11
- /**
12
- * Toggle the active state for the component.
13
- */
14
- active: Boolean,
15
21
  /**
16
22
  * Sets the color context of the component to one of CoreUI’s themed colors.
17
23
  *
@@ -40,6 +46,15 @@ const CDropdownToggle = defineComponent({
40
46
  * Toggle the disabled state for the component.
41
47
  */
42
48
  disabled: Boolean,
49
+ /**
50
+ * If a dropdown `variant` is set to `nav-item` then render the toggler as a link instead of a button.
51
+ *
52
+ * @since v5.0.0-alpha.1
53
+ */
54
+ navLink: {
55
+ type: Boolean,
56
+ default: true,
57
+ },
43
58
  /**
44
59
  * @values 'rounded', 'rounded-top', 'rounded-end', 'rounded-bottom', 'rounded-start', 'rounded-circle', 'rounded-pill', 'rounded-0', 'rounded-1', 'rounded-2', 'rounded-3'
45
60
  */
@@ -87,18 +102,10 @@ const CDropdownToggle = defineComponent({
87
102
  const visible = inject('visible') as Ref<boolean>
88
103
  const setVisible = inject('setVisible') as (_visible?: boolean) => void
89
104
 
90
- const className = [
91
- {
92
- 'dropdown-toggle': props.caret,
93
- 'dropdown-toggle-split': props.split,
94
- active: props.active,
95
- disabled: props.disabled,
96
- },
97
- ]
98
-
99
105
  const triggers = {
100
106
  ...((props.trigger === 'click' || props.trigger.includes('click')) && {
101
- onClick: () => {
107
+ onClick: (event: Event) => {
108
+ event.preventDefault()
102
109
  if (props.disabled) {
103
110
  return
104
111
  }
@@ -123,6 +130,20 @@ const CDropdownToggle = defineComponent({
123
130
  }),
124
131
  }
125
132
 
133
+ const togglerProps = computed(() => {
134
+ return {
135
+ class: {
136
+ 'nav-link': dropdownVariant === 'nav-item' && props.navLink,
137
+ 'dropdown-toggle': props.caret,
138
+ 'dropdown-toggle-split': props.split,
139
+ disabled: props.disabled,
140
+ show: visible.value,
141
+ },
142
+ 'aria-expanded': visible.value,
143
+ ...(!props.disabled && { ...triggers }),
144
+ }
145
+ })
146
+
126
147
  onMounted(() => {
127
148
  if (togglerRef.value) {
128
149
  dropdownToggleRef.value = togglerRef.value.$el
@@ -140,45 +161,30 @@ const CDropdownToggle = defineComponent({
140
161
  ...triggers,
141
162
  }),
142
163
  )
143
- : dropdownVariant === 'nav-item'
164
+ : dropdownVariant === 'nav-item' && props.navLink
144
165
  ? h(
145
166
  'a',
146
167
  {
147
- active: props.active,
148
- class: [
149
- 'nav-link',
150
- className,
151
- {
152
- show: visible.value,
153
- },
154
- ],
155
- disabled: props.disabled,
156
168
  href: '#',
169
+ ...togglerProps.value,
170
+ role: 'button',
157
171
  ref: dropdownToggleRef,
158
- ...triggers,
159
172
  },
160
173
  { default: () => slots.default && slots.default() },
161
174
  )
162
175
  : h(
163
176
  CButton,
164
177
  {
165
- class: [
166
- className,
167
- {
168
- show: visible.value,
169
- },
170
- ],
171
- active: props.active,
178
+ ...togglerProps.value,
172
179
  color: props.color,
180
+ component: props.component,
173
181
  disabled: props.disabled,
174
- ref: (el) => {
175
- togglerRef.value = el
176
- },
177
182
  shape: props.shape,
178
183
  size: props.size,
179
- ...triggers,
180
- ...(props.component === 'button' && { type: 'button' }),
181
184
  variant: props.variant,
185
+ ref: (el) => {
186
+ togglerRef.value = el
187
+ },
182
188
  },
183
189
  () =>
184
190
  props.split
@@ -0,0 +1,11 @@
1
+ export type Directions = 'start' | 'end'
2
+
3
+ export type Breakpoints =
4
+ | { xs: Directions }
5
+ | { sm: Directions }
6
+ | { md: Directions }
7
+ | { lg: Directions }
8
+ | { xl: Directions }
9
+ | { xxl: Directions }
10
+
11
+ export type Alignments = Directions | Breakpoints