@coreui/vue-pro 4.10.4 → 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 (79) 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/CFormInput.d.ts +6 -18
  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 +3 -1
  26. package/dist/composables/useDebouncedCallback.d.ts +1 -0
  27. package/dist/index.es.js +1142 -2823
  28. package/dist/index.es.js.map +1 -1
  29. package/dist/index.js +1143 -2821
  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 +140 -126
  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 +1 -1
  47. package/src/components/modal/CModal.ts +15 -1
  48. package/src/components/multi-select/CMultiSelect.ts +68 -62
  49. package/src/components/multi-select/CMultiSelectOptions.ts +1 -1
  50. package/src/components/multi-select/CMultiSelectSelection.ts +14 -13
  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 +7 -22
  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-table/CSmartTable.ts +17 -12
  62. package/src/components/smart-table/CSmartTableBody.ts +30 -31
  63. package/src/components/smart-table/CSmartTableHead.ts +34 -11
  64. package/src/components/smart-table/types.ts +2 -2
  65. package/src/components/smart-table/utils.ts +41 -5
  66. package/src/components/time-picker/CTimePicker.ts +34 -63
  67. package/src/components/tooltip/CTooltip.ts +1 -1
  68. package/src/components/widgets/CWidgetStatsA.ts +1 -3
  69. package/src/components/widgets/CWidgetStatsB.ts +2 -4
  70. package/src/components/widgets/CWidgetStatsC.ts +2 -2
  71. package/src/components/widgets/CWidgetStatsD.ts +1 -1
  72. package/src/components/widgets/CWidgetStatsE.ts +1 -1
  73. package/src/components/widgets/CWidgetStatsF.ts +1 -1
  74. package/src/components/widgets/__tests__/__snapshots__/CWidgetStatsE.spec.ts.snap +1 -1
  75. package/src/composables/index.ts +3 -1
  76. package/src/composables/useColorModes.ts +63 -0
  77. package/src/composables/useDebouncedCallback.ts +16 -0
  78. package/src/utils/getRTLPlacement.ts +1 -1
  79. 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,29 +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(),
470
- )
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)
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)
480
514
  const isValid = ref<boolean | undefined>(
481
515
  props.valid ?? (props.invalid === true ? false : undefined),
482
516
  )
483
517
  const isMobile = ref(false)
518
+ const selectEndDate = ref(false)
484
519
 
485
520
  onMounted(() => {
486
521
  isMobile.value = window.innerWidth < 768
@@ -497,9 +532,8 @@ const CDateRangePicker = defineComponent({
497
532
  () => props.startDate,
498
533
  () => {
499
534
  if (props.startDate) {
500
- const date = new Date(props.startDate)
501
- calendarDate.value = date
502
- startDate.value = date
535
+ calendarDate.value = props.startDate
536
+ startDate.value = props.startDate
503
537
  }
504
538
  },
505
539
  )
@@ -508,9 +542,8 @@ const CDateRangePicker = defineComponent({
508
542
  () => props.endDate,
509
543
  () => {
510
544
  if (props.endDate) {
511
- const date = new Date(props.endDate)
512
- calendarDate.value = date
513
- endDate.value = date
545
+ calendarDate.value = props.endDate
546
+ endDate.value = props.endDate
514
547
  }
515
548
  },
516
549
  )
@@ -519,7 +552,7 @@ const CDateRangePicker = defineComponent({
519
552
  () => props.maxDate,
520
553
  () => {
521
554
  if (props.maxDate) {
522
- maxDate.value = new Date(props.maxDate)
555
+ maxDate.value = props.maxDate
523
556
  }
524
557
  },
525
558
  )
@@ -528,7 +561,7 @@ const CDateRangePicker = defineComponent({
528
561
  () => props.minDate,
529
562
  () => {
530
563
  if (props.minDate) {
531
- minDate.value = new Date(props.minDate)
564
+ minDate.value = props.minDate
532
565
  }
533
566
  },
534
567
  )
@@ -549,15 +582,21 @@ const CDateRangePicker = defineComponent({
549
582
  }
550
583
  })
551
584
 
552
- const formatDate = (date: Date) => {
553
- return props.format
554
- ? 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)
555
594
  : props.timepicker
556
- ? date.toLocaleString(props.locale)
557
- : date.toLocaleDateString(props.locale)
595
+ ? _date.toLocaleString(props.locale)
596
+ : _date.toLocaleDateString(props.locale)
558
597
  }
559
598
 
560
- const setInputValue = (date: Date | null) => {
599
+ const setInputValue = (date: Date | string | null) => {
561
600
  if (date) {
562
601
  return formatDate(date)
563
602
  }
@@ -565,21 +604,13 @@ const CDateRangePicker = defineComponent({
565
604
  return ''
566
605
  }
567
606
 
568
- const handleCalendarCellHover = (date: Date | null) => {
607
+ const handleDateHover = (date: Date | string | null) => {
569
608
  if (selectEndDate.value) {
570
609
  inputEndHoverValue.value = date
571
610
  return
572
611
  }
573
- inputStartHoverValue.value = date
574
- }
575
-
576
- const handleCalendarDateChange = (date: Date, difference?: number) => {
577
- if (difference) {
578
- calendarDate.value = new Date(date.getFullYear(), date.getMonth() - difference, 1)
579
- return
580
- }
581
612
 
582
- calendarDate.value = date
613
+ inputStartHoverValue.value = date
583
614
  }
584
615
 
585
616
  const handleFormValidation = (form: HTMLFormElement) => {
@@ -645,23 +676,31 @@ const CDateRangePicker = defineComponent({
645
676
  emit('update:end-date', null)
646
677
  }
647
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
+
648
693
  const InputGroup = () =>
649
694
  h(
650
695
  'div',
651
696
  {
652
- class: [
653
- 'input-group',
654
- 'picker-input-group',
655
- {
656
- [`input-group-${props.size}`]: props.size,
657
- },
658
- ],
697
+ class: 'date-picker-input-group',
659
698
  },
660
699
  [
661
700
  h('input', {
662
701
  autocomplete: 'off',
663
702
  class: [
664
- 'form-control date-picker-input',
703
+ 'date-picker-input',
665
704
  {
666
705
  hover: inputStartHoverValue.value,
667
706
  },
@@ -671,17 +710,13 @@ const CDateRangePicker = defineComponent({
671
710
  onClick: () => {
672
711
  selectEndDate.value = false
673
712
  },
674
- onInput: (event: Event) => {
675
- const date = getLocalDateFromString(
676
- (event.target as HTMLInputElement).value,
677
- props.locale,
678
- props.timepicker,
679
- )
680
- if (date instanceof Date && date.getTime()) {
681
- calendarDate.value = date
682
- startDate.value = date
683
- }
684
- },
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
+ ),
685
720
  placeholder: Array.isArray(props.placeholder)
686
721
  ? props.placeholder[0]
687
722
  : props.placeholder,
@@ -692,20 +727,12 @@ const CDateRangePicker = defineComponent({
692
727
  ? setInputValue(inputStartHoverValue.value)
693
728
  : setInputValue(startDate.value),
694
729
  }),
695
- props.range &&
696
- props.separator !== false &&
697
- h(
698
- 'span',
699
- { class: 'input-group-text' },
700
- slots.separator
701
- ? slots.separator()
702
- : h('span', { class: 'picker-input-group-icon date-picker-arrow-icon' }),
703
- ),
730
+ props.range && props.separator !== false && h('div', { class: 'date-picker-separator' }),
704
731
  props.range &&
705
732
  h('input', {
706
733
  autocomplete: 'off',
707
734
  class: [
708
- 'form-control date-picker-input',
735
+ 'date-picker-input',
709
736
  {
710
737
  hover: inputEndHoverValue.value,
711
738
  },
@@ -715,17 +742,13 @@ const CDateRangePicker = defineComponent({
715
742
  onClick: () => {
716
743
  selectEndDate.value = true
717
744
  },
718
- onInput: (event: Event) => {
719
- const date = getLocalDateFromString(
720
- (event.target as HTMLInputElement).value,
721
- props.locale,
722
- props.timepicker,
723
- )
724
- if (date instanceof Date && date.getTime()) {
725
- calendarDate.value = date
726
- endDate.value = date
727
- }
728
- },
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
+ ),
729
752
  placeholder: props.placeholder[1],
730
753
  readonly: props.inputReadOnly || typeof props.format === 'string',
731
754
  required: props.required,
@@ -734,31 +757,13 @@ const CDateRangePicker = defineComponent({
734
757
  ? setInputValue(inputEndHoverValue.value)
735
758
  : setInputValue(endDate.value),
736
759
  }),
737
- (props.indicator || props.cleaner) &&
738
- h('span', { class: 'input-group-text' }, [
739
- props.indicator &&
740
- h(
741
- 'span',
742
- {
743
- class: 'picker-input-group-indicator',
744
- },
745
- slots.indicator
746
- ? slots.indicator()
747
- : h('span', { class: 'picker-input-group-icon date-picker-input-icon' }),
748
- ),
749
- props.cleaner &&
750
- h(
751
- 'span',
752
- {
753
- class: 'picker-input-group-cleaner',
754
- onClick: (event: Event) => handleClear(event),
755
- role: 'button',
756
- },
757
- slots.cleaner
758
- ? slots.cleaner()
759
- : h('span', { class: 'picker-input-group-icon date-picker-cleaner-icon' }),
760
- ),
761
- ]),
760
+ props.indicator && h('div', { class: 'date-picker-indicator' }),
761
+ props.cleaner &&
762
+ (startDate.value || endDate.value) &&
763
+ h('div', {
764
+ class: 'date-picker-cleaner',
765
+ onClick: (event: Event) => handleClear(event),
766
+ }),
762
767
  ],
763
768
  )
764
769
 
@@ -785,6 +790,7 @@ const CDateRangePicker = defineComponent({
785
790
  class: [
786
791
  'date-picker',
787
792
  {
793
+ [`date-picker-${props.size}`]: props.size,
788
794
  disabled: props.disabled,
789
795
  'is-invalid': isValid.value === false ? true : false,
790
796
  'is-valid': isValid.value,
@@ -815,7 +821,7 @@ const CDateRangePicker = defineComponent({
815
821
  {
816
822
  toggler: () => InputGroup(),
817
823
  footer: () =>
818
- h('div', { class: 'picker-footer' }, [
824
+ h('div', { class: 'date-picker-footer' }, [
819
825
  props.todayButton &&
820
826
  h(
821
827
  CButton,
@@ -896,31 +902,36 @@ const CDateRangePicker = defineComponent({
896
902
  h(
897
903
  CCalendar,
898
904
  {
899
- calendarDate: new Date(
900
- calendarDate.value.getFullYear(),
901
- calendarDate.value.getMonth(),
902
- 1,
903
- ),
904
- calendars: props.calendars,
905
+ calendarDate: calendarDate.value,
906
+ calendars: isMobile.value ? 1 : props.calendars,
907
+ class: 'date-picker-calendars',
905
908
  dayFormat: props.dayFormat,
906
909
  disabledDates: props.disabledDates,
907
- ...(endDate.value && { endDate: endDate.value }),
910
+ endDate: endDate.value,
908
911
  firstDayOfWeek: props.firstDayOfWeek,
909
912
  locale: props.locale,
910
913
  maxDate: maxDate.value,
911
914
  minDate: minDate.value,
912
- navYearFirst: props.navYearFirst,
913
915
  navigation: props.navigation,
916
+ navYearFirst: props.navYearFirst,
914
917
  range: props.range,
915
- selectEndDate: selectEndDate.value,
916
918
  selectAdjacementDays: props.selectAdjacementDays,
919
+ selectEndDate: selectEndDate.value,
920
+ selectionType: props.selectionType,
917
921
  showAdjacementDays: props.showAdjacementDays,
918
- ...(startDate.value && { startDate: startDate.value }),
919
- onCalendarCellHover: (date: Date | null) =>
920
- handleCalendarCellHover(date),
921
- 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
+ },
922
930
  onStartDateChange: (date: Date) => handleStartDateChange(date),
923
931
  onEndDateChange: (date: Date) => handleEndDateChange(date),
932
+ onSelectEndChange: (value: boolean) => {
933
+ selectEndDate.value = value
934
+ },
924
935
  },
925
936
  {
926
937
  /**
@@ -964,7 +975,7 @@ const CDateRangePicker = defineComponent({
964
975
  locale: props.locale,
965
976
  onChange: (_: any, __: any, date: Date) =>
966
977
  handleStartDateChange(date),
967
- time: startDate.value,
978
+ time: startDate.value && new Date(startDate.value),
968
979
  variant: 'select',
969
980
  }),
970
981
  h(CTimePicker, {
@@ -973,7 +984,7 @@ const CDateRangePicker = defineComponent({
973
984
  locale: props.locale,
974
985
  onChange: (_: any, __: any, date: Date) =>
975
986
  handleEndDateChange(date),
976
- time: endDate.value,
987
+ time: endDate.value && new Date(endDate.value),
977
988
  variant: 'select',
978
989
  }),
979
990
  ]
@@ -993,7 +1004,10 @@ const CDateRangePicker = defineComponent({
993
1004
  index === 0
994
1005
  ? handleStartDateChange(date)
995
1006
  : handleEndDateChange(date),
996
- 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),
997
1011
  variant: 'select',
998
1012
  }),
999
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'