@coreui/react-pro 4.1.2 → 4.2.0-beta.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/README.md +1 -1
- package/dist/components/calendar/CCalendar.d.ts +89 -0
- package/dist/components/calendar/CCalendar.utils.d.ts +20 -0
- package/dist/components/datepicker/CDatePicker.d.ts +13 -0
- package/dist/components/daterangepicker/CDateRangePicker.d.ts +50 -0
- package/dist/components/dropdown/CDropdown.d.ts +10 -1
- package/dist/components/dropdown/CDropdownToggle.d.ts +4 -0
- package/dist/components/dropdown/CDropdownToggleCustom.d.ts +15 -0
- package/dist/components/modal/CModal.d.ts +1 -0
- package/dist/components/picker/CPicker.d.ts +37 -0
- package/dist/components/smart-table/CSmartTableInterface.d.ts +0 -9
- package/dist/components/timepicker/CTimePicker.d.ts +49 -0
- package/dist/components/timepicker/CTimePicker.utils.d.ts +4 -0
- package/dist/components/timepicker/CTimePickerRollCol.d.ts +11 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.es.js +2976 -1148
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +4691 -2859
- package/dist/index.js.map +1 -1
- package/package.json +10 -8
- package/src/components/accordion/CAccordionButton.tsx +2 -1
- package/src/components/calendar/CCalendar.tsx +482 -0
- package/src/components/calendar/CCalendar.utils.ts +183 -0
- package/src/components/datepicker/CDatePicker.tsx +49 -0
- package/src/components/daterangepicker/CDateRangePicker.tsx +366 -0
- package/src/components/dropdown/CDropdown.tsx +19 -24
- package/src/components/dropdown/CDropdownMenu.tsx +55 -3
- package/src/components/dropdown/CDropdownToggle.tsx +28 -5
- package/src/components/modal/CModal.tsx +42 -19
- package/src/components/modal/__tests__/CModal.spec.tsx +1 -1
- package/src/components/modal/__tests__/__snapshots__/CModal.spec.tsx.snap +2 -10
- package/src/components/picker/CPicker.tsx +142 -0
- package/src/components/smart-table/CSmartTable.tsx +44 -96
- package/src/components/smart-table/CSmartTableCleaner.tsx +3 -16
- package/src/components/smart-table/CSmartTableInterface.tsx +0 -9
- package/src/components/timepicker/CTimePicker.tsx +341 -0
- package/src/components/timepicker/CTimePicker.utils.ts +27 -0
- package/src/components/timepicker/CTimePickerRollCol.tsx +57 -0
- package/src/index.ts +8 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
export const convertToLocalDate = (d: Date, locale: string, options = {}) =>
|
|
2
|
+
d.toLocaleDateString(locale, options)
|
|
3
|
+
|
|
4
|
+
export const convertToLocalTime = (d: Date, locale: string, options = {}) =>
|
|
5
|
+
d.toLocaleTimeString(locale, options)
|
|
6
|
+
|
|
7
|
+
export const createGroupsInArray = (arr: any[], numberOfGroups: number) => {
|
|
8
|
+
const perGroup = Math.ceil(arr.length / numberOfGroups)
|
|
9
|
+
return new Array(numberOfGroups)
|
|
10
|
+
.fill('')
|
|
11
|
+
.map((_, i) => arr.slice(i * perGroup, (i + 1) * perGroup))
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const getCurrentYear = () => new Date().getFullYear()
|
|
15
|
+
|
|
16
|
+
export const getCurrentMonth = () => new Date().getMonth()
|
|
17
|
+
|
|
18
|
+
export const getMonthName = (month: number, locale: string) => {
|
|
19
|
+
const d = new Date()
|
|
20
|
+
d.setDate(1)
|
|
21
|
+
d.setMonth(month)
|
|
22
|
+
return d.toLocaleString(locale, { month: 'long' })
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const getMonthsNames = (locale: string) => {
|
|
26
|
+
const months = []
|
|
27
|
+
const d = new Date()
|
|
28
|
+
d.setDate(1)
|
|
29
|
+
|
|
30
|
+
for (let i = 0; i < 12; i++) {
|
|
31
|
+
d.setMonth(i)
|
|
32
|
+
months.push(d.toLocaleString(locale, { month: 'short' }))
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return months
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const getYears = (year: number) => {
|
|
39
|
+
const years = []
|
|
40
|
+
for (let _year = year - 6; _year < year + 6; _year++) {
|
|
41
|
+
years.push(_year)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return years
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const getLeadingDays = (year: number, month: number, firstDayOfWeek: number) => {
|
|
48
|
+
// 0: sunday
|
|
49
|
+
// 1: monday
|
|
50
|
+
const dates = []
|
|
51
|
+
const d = new Date(year, month)
|
|
52
|
+
const y = d.getFullYear()
|
|
53
|
+
const m = d.getMonth()
|
|
54
|
+
const firstWeekday = new Date(y, m, 1).getDay()
|
|
55
|
+
let leadingDays = 6 - (6 - firstWeekday) - firstDayOfWeek
|
|
56
|
+
|
|
57
|
+
if (firstDayOfWeek) {
|
|
58
|
+
leadingDays = leadingDays < 0 ? 7 + leadingDays : leadingDays
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
for (let i = leadingDays * -1; i < 0; i++) {
|
|
62
|
+
dates.push({
|
|
63
|
+
date: new Date(y, m, i + 1),
|
|
64
|
+
month: 'previous',
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return dates
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const getMonthDays = (year: number, month: number) => {
|
|
72
|
+
const dates = []
|
|
73
|
+
const lastDay = new Date(year, month + 1, 0).getDate()
|
|
74
|
+
for (let i = 1; i <= lastDay; i++) {
|
|
75
|
+
dates.push({
|
|
76
|
+
date: new Date(year, month, i),
|
|
77
|
+
month: 'current',
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
return dates
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const getTrailingDays = (
|
|
84
|
+
year: number,
|
|
85
|
+
month: number,
|
|
86
|
+
leadingDays: { date: Date; month: string }[],
|
|
87
|
+
monthDays: { date: Date; month: string }[],
|
|
88
|
+
) => {
|
|
89
|
+
const dates = []
|
|
90
|
+
const days = 42 - (leadingDays.length + monthDays.length)
|
|
91
|
+
for (let i = 1; i <= days; i++) {
|
|
92
|
+
dates.push({
|
|
93
|
+
date: new Date(year, month + 1, i),
|
|
94
|
+
month: 'next',
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
return dates
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export const getMonthDetails = (year: number, month: number, firstDayOfWeek: number) => {
|
|
101
|
+
const daysPrevMonth = getLeadingDays(year, month, firstDayOfWeek)
|
|
102
|
+
const daysThisMonth = getMonthDays(year, month)
|
|
103
|
+
const daysNextMonth = getTrailingDays(year, month, daysPrevMonth, daysThisMonth)
|
|
104
|
+
const days = [...daysPrevMonth, ...daysThisMonth, ...daysNextMonth]
|
|
105
|
+
|
|
106
|
+
const weeks: { date: Date; month: string }[][] = []
|
|
107
|
+
|
|
108
|
+
days.forEach((day, index) => {
|
|
109
|
+
if (index % 7 === 0 || weeks.length === 0) {
|
|
110
|
+
weeks.push([])
|
|
111
|
+
}
|
|
112
|
+
weeks[weeks.length - 1].push(day)
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
return weeks
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export const isDisabled = (date: Date, min?: Date, max?: Date, dates?: Date[] | Date[][]) => {
|
|
119
|
+
let disabled
|
|
120
|
+
if (dates) {
|
|
121
|
+
dates.forEach((_date: Date | Date[]) => {
|
|
122
|
+
if (Array.isArray(_date)) {
|
|
123
|
+
if (isDateInRange(date, _date[0], _date[1])) {
|
|
124
|
+
disabled = true
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (_date instanceof Date) {
|
|
128
|
+
if (isSameDateAs(date, _date)) {
|
|
129
|
+
disabled = true
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
}
|
|
134
|
+
if (min && date < min) {
|
|
135
|
+
disabled = true
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (max && date > max) {
|
|
139
|
+
disabled = true
|
|
140
|
+
}
|
|
141
|
+
return disabled
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export const isDateInRange = (date: Date, start: Date | null, end: Date | null) => {
|
|
145
|
+
return start && end && start <= date && date <= end
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export const isDateSelected = (date: Date, start: Date | null, end: Date | null) => {
|
|
149
|
+
return (start && isSameDateAs(start, date)) || (end && isSameDateAs(end, date))
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export const isEndDate = (date: Date, start: Date | null, end: Date | null) => {
|
|
153
|
+
return start && end && isSameDateAs(end, date) && start < end
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export const isLastDayOfMonth = (date: Date) => {
|
|
157
|
+
const test = new Date(date.getTime())
|
|
158
|
+
const month = test.getMonth()
|
|
159
|
+
|
|
160
|
+
test.setDate(test.getDate() + 1)
|
|
161
|
+
return test.getMonth() !== month
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export const isSameDateAs = (date: Date, date2: Date) => {
|
|
165
|
+
return (
|
|
166
|
+
date.getDate() == date2.getDate() &&
|
|
167
|
+
date.getMonth() == date2.getMonth() &&
|
|
168
|
+
date.getFullYear() == date2.getFullYear()
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export const isStartDate = (date: Date, start: Date | null, end: Date | null) => {
|
|
173
|
+
return start && end && isSameDateAs(start, date) && start < end
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export const isToday = (date: Date) => {
|
|
177
|
+
const today = new Date()
|
|
178
|
+
return (
|
|
179
|
+
date.getDate() == today.getDate() &&
|
|
180
|
+
date.getMonth() == today.getMonth() &&
|
|
181
|
+
date.getFullYear() == today.getFullYear()
|
|
182
|
+
)
|
|
183
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import { CDateRangePicker, CDateRangePickerProps } from '../daterangepicker/CDateRangePicker'
|
|
4
|
+
|
|
5
|
+
export interface CDatePickerProps
|
|
6
|
+
extends Omit<
|
|
7
|
+
CDateRangePickerProps,
|
|
8
|
+
| 'calendars'
|
|
9
|
+
| 'endDate'
|
|
10
|
+
| 'range'
|
|
11
|
+
| 'onEndDateChange'
|
|
12
|
+
| 'onStartDateChange'
|
|
13
|
+
| 'ranges'
|
|
14
|
+
| 'selectEndDate'
|
|
15
|
+
| 'startDate'
|
|
16
|
+
> {
|
|
17
|
+
/**
|
|
18
|
+
* Initial selected date.
|
|
19
|
+
*/
|
|
20
|
+
date?: Date | string | null
|
|
21
|
+
/**
|
|
22
|
+
* Callback fired when the date changed.
|
|
23
|
+
*/
|
|
24
|
+
onDateChange?: (date: Date | null) => void
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const CDatePicker = forwardRef<HTMLDivElement | HTMLLIElement, CDatePickerProps>(
|
|
28
|
+
({ date, onDateChange, placeholder = 'Select date', ...rest }, ref) => {
|
|
29
|
+
return (
|
|
30
|
+
<CDateRangePicker
|
|
31
|
+
calendars={1}
|
|
32
|
+
startDate={date}
|
|
33
|
+
onStartDateChange={onDateChange}
|
|
34
|
+
placeholder={placeholder}
|
|
35
|
+
range={false}
|
|
36
|
+
ref={ref}
|
|
37
|
+
{...rest}
|
|
38
|
+
/>
|
|
39
|
+
)
|
|
40
|
+
},
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
CDatePicker.displayName = 'CDatePicker'
|
|
44
|
+
|
|
45
|
+
CDatePicker.propTypes = {
|
|
46
|
+
...CDateRangePicker.propTypes,
|
|
47
|
+
date: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.string]),
|
|
48
|
+
onDateChange: PropTypes.func,
|
|
49
|
+
}
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import React, { forwardRef, ReactNode, useEffect, useRef, useState } from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import classNames from 'classnames'
|
|
4
|
+
import { isMobile } from 'react-device-detect'
|
|
5
|
+
|
|
6
|
+
import { CCalendar, CCalendarProps } from '../calendar/CCalendar'
|
|
7
|
+
import { CPicker, CPickerProps } from './../picker/CPicker'
|
|
8
|
+
import { CTimePicker } from './../timepicker/CTimePicker'
|
|
9
|
+
import { convertToLocalDate } from '../calendar/CCalendar.utils'
|
|
10
|
+
import { CButton } from '../button/CButton'
|
|
11
|
+
import { CFormInput } from '../form/CFormInput'
|
|
12
|
+
import { CInputGroup } from '../form/CInputGroup'
|
|
13
|
+
import { CInputGroupText } from '../form/CInputGroupText'
|
|
14
|
+
|
|
15
|
+
export interface CDateRangePickerProps
|
|
16
|
+
extends Omit<CPickerProps, 'autoClose' | 'component' | 'dark' | 'placeholder' | 'variant'>,
|
|
17
|
+
Omit<CCalendarProps, 'onCalendarCellHover' | 'onCalendarDateChange'> {
|
|
18
|
+
/**
|
|
19
|
+
* The number of calendars that render on desktop devices.
|
|
20
|
+
*/
|
|
21
|
+
calendars?: number
|
|
22
|
+
/**
|
|
23
|
+
* Enables selection cleaner element.
|
|
24
|
+
*/
|
|
25
|
+
cleaner?: boolean
|
|
26
|
+
/**
|
|
27
|
+
* Cleaner icon.
|
|
28
|
+
*/
|
|
29
|
+
cleanerIcon?: ReactNode | boolean
|
|
30
|
+
/**
|
|
31
|
+
* Default input icon.
|
|
32
|
+
*/
|
|
33
|
+
defaultIcon?: ReactNode | boolean
|
|
34
|
+
/**
|
|
35
|
+
* Default icon between date input fields.
|
|
36
|
+
*/
|
|
37
|
+
infixIcon?: ReactNode | boolean
|
|
38
|
+
/**
|
|
39
|
+
* Toggle the readonly state for the component.
|
|
40
|
+
*/
|
|
41
|
+
inputReadOnly?: boolean
|
|
42
|
+
/**
|
|
43
|
+
* Specifies short hints that are visible in start date and end date inputs.
|
|
44
|
+
*/
|
|
45
|
+
placeholder?: string | string[]
|
|
46
|
+
/**
|
|
47
|
+
* @ignore
|
|
48
|
+
*/
|
|
49
|
+
range?: boolean
|
|
50
|
+
/**
|
|
51
|
+
* Predefined date ranges the user can select from.
|
|
52
|
+
*/
|
|
53
|
+
ranges?: object
|
|
54
|
+
/**
|
|
55
|
+
* Size the component small or large.
|
|
56
|
+
*/
|
|
57
|
+
size?: 'sm' | 'lg'
|
|
58
|
+
/**
|
|
59
|
+
* Provide an additional time selection by adding select boxes to choose times.
|
|
60
|
+
*/
|
|
61
|
+
timepicker?: boolean
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const CDateRangePicker = forwardRef<HTMLDivElement | HTMLLIElement, CDateRangePickerProps>(
|
|
65
|
+
(
|
|
66
|
+
{
|
|
67
|
+
calendars = 2,
|
|
68
|
+
className,
|
|
69
|
+
cleaner = true,
|
|
70
|
+
cleanerIcon,
|
|
71
|
+
calendarDate,
|
|
72
|
+
disabled,
|
|
73
|
+
disabledDates,
|
|
74
|
+
endDate,
|
|
75
|
+
defaultIcon,
|
|
76
|
+
infixIcon,
|
|
77
|
+
inputReadOnly,
|
|
78
|
+
locale = typeof navigator === 'undefined' ? 'en-US' : navigator.language,
|
|
79
|
+
maxDate,
|
|
80
|
+
minDate,
|
|
81
|
+
onEndDateChange,
|
|
82
|
+
onHide,
|
|
83
|
+
onStartDateChange,
|
|
84
|
+
onShow,
|
|
85
|
+
placeholder = ['Start date', 'End date'],
|
|
86
|
+
range = true,
|
|
87
|
+
ranges,
|
|
88
|
+
size,
|
|
89
|
+
startDate,
|
|
90
|
+
timepicker,
|
|
91
|
+
toggler,
|
|
92
|
+
visible,
|
|
93
|
+
...rest
|
|
94
|
+
},
|
|
95
|
+
ref,
|
|
96
|
+
) => {
|
|
97
|
+
const inputStartRef = useRef<HTMLInputElement>(null)
|
|
98
|
+
const inputEndRef = useRef<HTMLInputElement>(null)
|
|
99
|
+
const [_visible, setVisible] = useState(visible)
|
|
100
|
+
const [_startDate, setStartDate] = useState<Date | null>(startDate ? new Date(startDate) : null)
|
|
101
|
+
const [_endDate, setEndDate] = useState<Date | null>(endDate ? new Date(endDate) : null)
|
|
102
|
+
const [inputHoverValue, setInputHoverValue] = useState<Date | null>(null)
|
|
103
|
+
const [inputToHoverValue, setInputToHoverValue] = useState<Date | null>(null)
|
|
104
|
+
const [selectEndDate, setSelectEndDate] = useState(false)
|
|
105
|
+
const [_calendarDate, setCalendarDate] = useState<Date>(
|
|
106
|
+
calendarDate ? new Date(calendarDate) : new Date(),
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
useEffect(() => {
|
|
110
|
+
startDate && setCalendarDate(new Date(startDate))
|
|
111
|
+
}, [startDate])
|
|
112
|
+
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
endDate && setCalendarDate(new Date(endDate))
|
|
115
|
+
}, [endDate])
|
|
116
|
+
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
_visible && onShow && onShow()
|
|
119
|
+
!_visible && onHide && onHide()
|
|
120
|
+
}, [_visible])
|
|
121
|
+
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
if (inputStartRef.current) {
|
|
124
|
+
if (inputHoverValue) {
|
|
125
|
+
inputStartRef.current.value = convertToLocalDate(inputHoverValue, locale)
|
|
126
|
+
return
|
|
127
|
+
}
|
|
128
|
+
if (_startDate) {
|
|
129
|
+
inputStartRef.current.value = timepicker
|
|
130
|
+
? _startDate.toLocaleString(locale)
|
|
131
|
+
: _startDate.toLocaleDateString(locale)
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
inputStartRef.current.value = ''
|
|
135
|
+
}
|
|
136
|
+
}, [inputHoverValue, _startDate])
|
|
137
|
+
|
|
138
|
+
useEffect(() => {
|
|
139
|
+
if (inputEndRef.current) {
|
|
140
|
+
if (inputToHoverValue) {
|
|
141
|
+
inputEndRef.current.value = convertToLocalDate(inputToHoverValue, locale)
|
|
142
|
+
return
|
|
143
|
+
}
|
|
144
|
+
if (_endDate) {
|
|
145
|
+
inputEndRef.current.value = timepicker
|
|
146
|
+
? _endDate.toLocaleString(locale)
|
|
147
|
+
: _endDate.toLocaleDateString(locale)
|
|
148
|
+
return
|
|
149
|
+
}
|
|
150
|
+
inputEndRef.current.value = ''
|
|
151
|
+
}
|
|
152
|
+
}, [inputToHoverValue, _endDate])
|
|
153
|
+
|
|
154
|
+
const handleCalendarDateChange = (date: Date, difference?: number) => {
|
|
155
|
+
difference
|
|
156
|
+
? setCalendarDate(new Date(date.getFullYear(), date.getMonth() - difference, 1))
|
|
157
|
+
: setCalendarDate(date)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const handleStartDateChange = (date: Date | null) => {
|
|
161
|
+
setStartDate(date)
|
|
162
|
+
range && setSelectEndDate(true)
|
|
163
|
+
onStartDateChange && onStartDateChange(date)
|
|
164
|
+
setInputHoverValue(null)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const handleEndDateChange = (date: Date | null) => {
|
|
168
|
+
setEndDate(date)
|
|
169
|
+
setSelectEndDate(false)
|
|
170
|
+
onEndDateChange && onEndDateChange(date)
|
|
171
|
+
setInputToHoverValue(null)
|
|
172
|
+
|
|
173
|
+
if (timepicker !== true) {
|
|
174
|
+
_startDate !== null && setVisible(false)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const handleCalendarCellHover = (date: Date | null) => {
|
|
179
|
+
selectEndDate ? setInputToHoverValue(date) : setInputHoverValue(date)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const handleClear = (event: React.MouseEvent<HTMLElement>) => {
|
|
183
|
+
event.stopPropagation()
|
|
184
|
+
setStartDate(null)
|
|
185
|
+
setEndDate(null)
|
|
186
|
+
setInputHoverValue(null)
|
|
187
|
+
setInputToHoverValue(null)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const _className = classNames('date-picker', className)
|
|
191
|
+
|
|
192
|
+
const InputGroup = () => (
|
|
193
|
+
<CInputGroup className="picker-input-group" size={size}>
|
|
194
|
+
<CFormInput
|
|
195
|
+
className={classNames({
|
|
196
|
+
hover: inputHoverValue,
|
|
197
|
+
})}
|
|
198
|
+
disabled={disabled}
|
|
199
|
+
placeholder={Array.isArray(placeholder) ? placeholder[0] : placeholder}
|
|
200
|
+
readOnly={inputReadOnly}
|
|
201
|
+
onClick={() => setSelectEndDate(false)}
|
|
202
|
+
ref={inputStartRef}
|
|
203
|
+
/>
|
|
204
|
+
{range && infixIcon !== false && (
|
|
205
|
+
<CInputGroupText>
|
|
206
|
+
{infixIcon ? (
|
|
207
|
+
infixIcon
|
|
208
|
+
) : (
|
|
209
|
+
<span className="picker-input-group-icon date-picker-arrow-icon" />
|
|
210
|
+
)}
|
|
211
|
+
</CInputGroupText>
|
|
212
|
+
)}
|
|
213
|
+
{range && (
|
|
214
|
+
<CFormInput
|
|
215
|
+
className={classNames({
|
|
216
|
+
hover: inputToHoverValue,
|
|
217
|
+
})}
|
|
218
|
+
disabled={disabled}
|
|
219
|
+
placeholder={placeholder[1]}
|
|
220
|
+
readOnly={inputReadOnly}
|
|
221
|
+
onClick={() => setSelectEndDate(true)}
|
|
222
|
+
ref={inputEndRef}
|
|
223
|
+
/>
|
|
224
|
+
)}
|
|
225
|
+
{(defaultIcon !== false || cleaner) && (
|
|
226
|
+
<CInputGroupText>
|
|
227
|
+
{defaultIcon !== false && (
|
|
228
|
+
<span className="picker-input-group-indicator">
|
|
229
|
+
{defaultIcon ? (
|
|
230
|
+
defaultIcon
|
|
231
|
+
) : (
|
|
232
|
+
<span className="picker-input-group-icon date-picker-input-icon" />
|
|
233
|
+
)}
|
|
234
|
+
</span>
|
|
235
|
+
)}
|
|
236
|
+
{cleaner && _startDate && (
|
|
237
|
+
<span
|
|
238
|
+
className="picker-input-group-cleaner"
|
|
239
|
+
role="button"
|
|
240
|
+
onClick={(event) => handleClear(event)}
|
|
241
|
+
>
|
|
242
|
+
{cleanerIcon ? (
|
|
243
|
+
cleanerIcon
|
|
244
|
+
) : (
|
|
245
|
+
<span className="picker-input-group-icon date-picker-cleaner-icon" />
|
|
246
|
+
)}
|
|
247
|
+
</span>
|
|
248
|
+
)}
|
|
249
|
+
</CInputGroupText>
|
|
250
|
+
)}
|
|
251
|
+
</CInputGroup>
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
return (
|
|
255
|
+
<CPicker
|
|
256
|
+
className={_className}
|
|
257
|
+
disabled={disabled}
|
|
258
|
+
footer={timepicker}
|
|
259
|
+
toggler={toggler ? toggler : InputGroup()}
|
|
260
|
+
onCancelClick={() => {
|
|
261
|
+
handleStartDateChange(startDate ? new Date(startDate) : null)
|
|
262
|
+
handleEndDateChange(endDate ? new Date(endDate) : null)
|
|
263
|
+
}}
|
|
264
|
+
visible={_visible}
|
|
265
|
+
{...rest}
|
|
266
|
+
ref={ref}
|
|
267
|
+
>
|
|
268
|
+
<div className="date-picker-body">
|
|
269
|
+
{ranges && (
|
|
270
|
+
<div className="date-picker-ranges">
|
|
271
|
+
{Object.keys(ranges).map((key: string, index: number) => (
|
|
272
|
+
<CButton
|
|
273
|
+
color="secondary"
|
|
274
|
+
key={index}
|
|
275
|
+
onClick={() => {
|
|
276
|
+
setStartDate(ranges[key][0])
|
|
277
|
+
setEndDate(ranges[key][1])
|
|
278
|
+
}}
|
|
279
|
+
variant="ghost"
|
|
280
|
+
>
|
|
281
|
+
{key}
|
|
282
|
+
</CButton>
|
|
283
|
+
))}
|
|
284
|
+
</div>
|
|
285
|
+
)}
|
|
286
|
+
<div className="date-picker-calendars">
|
|
287
|
+
{[...Array(isMobile ? 1 : calendars)].map((_, index) => (
|
|
288
|
+
<div className="date-picker-calendar" key={index}>
|
|
289
|
+
<CCalendar
|
|
290
|
+
calendarDate={
|
|
291
|
+
new Date(_calendarDate.getFullYear(), _calendarDate.getMonth() + index, 1)
|
|
292
|
+
}
|
|
293
|
+
disabledDates={disabledDates}
|
|
294
|
+
endDate={_endDate}
|
|
295
|
+
locale={locale}
|
|
296
|
+
maxDate={maxDate}
|
|
297
|
+
minDate={minDate}
|
|
298
|
+
range={range}
|
|
299
|
+
selectEndDate={selectEndDate}
|
|
300
|
+
startDate={_startDate}
|
|
301
|
+
onCalendarCellHover={(date) => handleCalendarCellHover(date)}
|
|
302
|
+
onCalendarDateChange={(date) => handleCalendarDateChange(date, index)}
|
|
303
|
+
onStartDateChange={(date) => handleStartDateChange(date)}
|
|
304
|
+
onEndDateChange={(date) => handleEndDateChange(date)}
|
|
305
|
+
/>
|
|
306
|
+
{!isMobile && timepicker && (index === 0 || calendars - index === 1) && (
|
|
307
|
+
<CTimePicker
|
|
308
|
+
container="inline"
|
|
309
|
+
disabled={
|
|
310
|
+
index === 0
|
|
311
|
+
? _startDate === null
|
|
312
|
+
? true
|
|
313
|
+
: false
|
|
314
|
+
: _endDate === null
|
|
315
|
+
? true
|
|
316
|
+
: false
|
|
317
|
+
}
|
|
318
|
+
locale={locale}
|
|
319
|
+
onChange={(_, __, date) =>
|
|
320
|
+
index === 0 ? handleStartDateChange(date) : handleEndDateChange(date)
|
|
321
|
+
}
|
|
322
|
+
value={index === 0 ? _startDate : _endDate}
|
|
323
|
+
variant="select"
|
|
324
|
+
/>
|
|
325
|
+
)}
|
|
326
|
+
{isMobile && timepicker && (
|
|
327
|
+
<>
|
|
328
|
+
<CTimePicker
|
|
329
|
+
container="inline"
|
|
330
|
+
disabled={_startDate === null ? true : false}
|
|
331
|
+
locale={locale}
|
|
332
|
+
onChange={(_, __, date) => handleStartDateChange(date)}
|
|
333
|
+
value={_startDate}
|
|
334
|
+
variant="select"
|
|
335
|
+
/>
|
|
336
|
+
<CTimePicker
|
|
337
|
+
container="inline"
|
|
338
|
+
disabled={_endDate === null ? true : false}
|
|
339
|
+
locale={locale}
|
|
340
|
+
onChange={(_, __, date) => handleEndDateChange(date)}
|
|
341
|
+
value={_endDate}
|
|
342
|
+
variant="select"
|
|
343
|
+
/>
|
|
344
|
+
</>
|
|
345
|
+
)}
|
|
346
|
+
</div>
|
|
347
|
+
))}
|
|
348
|
+
</div>
|
|
349
|
+
</div>
|
|
350
|
+
</CPicker>
|
|
351
|
+
)
|
|
352
|
+
},
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
CDateRangePicker.displayName = 'CDateRangePicker'
|
|
356
|
+
|
|
357
|
+
CDateRangePicker.propTypes = {
|
|
358
|
+
...CCalendar.propTypes,
|
|
359
|
+
...CPicker.propTypes,
|
|
360
|
+
calendars: PropTypes.number,
|
|
361
|
+
inputReadOnly: PropTypes.bool,
|
|
362
|
+
placeholder: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.any)]),
|
|
363
|
+
range: PropTypes.bool,
|
|
364
|
+
ranges: PropTypes.object,
|
|
365
|
+
timepicker: PropTypes.bool,
|
|
366
|
+
}
|
|
@@ -3,6 +3,7 @@ import React, {
|
|
|
3
3
|
ElementType,
|
|
4
4
|
forwardRef,
|
|
5
5
|
HTMLAttributes,
|
|
6
|
+
RefObject,
|
|
6
7
|
useEffect,
|
|
7
8
|
useRef,
|
|
8
9
|
useState,
|
|
@@ -33,6 +34,14 @@ export interface CDropdownProps extends HTMLAttributes<HTMLDivElement | HTMLLIEl
|
|
|
33
34
|
* @type 'start' | 'end' | { xs: 'start' | 'end' } | { sm: 'start' | 'end' } | { md: 'start' | 'end' } | { lg: 'start' | 'end' } | { xl: 'start' | 'end'} | { xxl: 'start' | 'end'}
|
|
34
35
|
*/
|
|
35
36
|
alignment?: Alignments
|
|
37
|
+
/**
|
|
38
|
+
* Configure the auto close behavior of the dropdown:
|
|
39
|
+
* - `true` - the dropdown will be closed by clicking outside or inside the dropdown menu.
|
|
40
|
+
* - `false` - the dropdown will be closed by clicking the toggle button and manually calling hide or toggle method. (Also will not be closed by pressing esc key)
|
|
41
|
+
* - `'inside'` - the dropdown will be closed (only) by clicking inside the dropdown menu.
|
|
42
|
+
* - `'outside'` - the dropdown will be closed (only) by clicking outside the dropdown menu.
|
|
43
|
+
*/
|
|
44
|
+
autoClose?: 'inside' | 'outside' | boolean
|
|
36
45
|
/**
|
|
37
46
|
* A string of all className you want applied to the base component.
|
|
38
47
|
*/
|
|
@@ -80,6 +89,8 @@ export interface CDropdownProps extends HTMLAttributes<HTMLDivElement | HTMLLIEl
|
|
|
80
89
|
}
|
|
81
90
|
|
|
82
91
|
interface ContextProps extends CDropdownProps {
|
|
92
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
93
|
+
dropdownToggleRef: RefObject<any> | undefined
|
|
83
94
|
setVisible: React.Dispatch<React.SetStateAction<boolean | undefined>>
|
|
84
95
|
}
|
|
85
96
|
|
|
@@ -90,6 +101,7 @@ export const CDropdown = forwardRef<HTMLDivElement | HTMLLIElement, CDropdownPro
|
|
|
90
101
|
{
|
|
91
102
|
children,
|
|
92
103
|
alignment,
|
|
104
|
+
autoClose = true,
|
|
93
105
|
className,
|
|
94
106
|
dark,
|
|
95
107
|
direction,
|
|
@@ -106,6 +118,7 @@ export const CDropdown = forwardRef<HTMLDivElement | HTMLLIElement, CDropdownPro
|
|
|
106
118
|
) => {
|
|
107
119
|
const [_visible, setVisible] = useState(visible)
|
|
108
120
|
const dropdownRef = useRef<HTMLDivElement>(null)
|
|
121
|
+
const dropdownToggleRef = useRef(null)
|
|
109
122
|
const forkedRef = useForkedRef(ref, dropdownRef)
|
|
110
123
|
|
|
111
124
|
const Component = variant === 'nav-item' ? 'li' : component
|
|
@@ -117,8 +130,10 @@ export const CDropdown = forwardRef<HTMLDivElement | HTMLLIElement, CDropdownPro
|
|
|
117
130
|
|
|
118
131
|
const contextValues = {
|
|
119
132
|
alignment,
|
|
133
|
+
autoClose,
|
|
120
134
|
dark,
|
|
121
135
|
direction: direction,
|
|
136
|
+
dropdownToggleRef,
|
|
122
137
|
placement: placement,
|
|
123
138
|
popper,
|
|
124
139
|
variant,
|
|
@@ -135,19 +150,6 @@ export const CDropdown = forwardRef<HTMLDivElement | HTMLLIElement, CDropdownPro
|
|
|
135
150
|
className,
|
|
136
151
|
)
|
|
137
152
|
|
|
138
|
-
useEffect(() => {
|
|
139
|
-
_visible &&
|
|
140
|
-
setTimeout(() => {
|
|
141
|
-
window.addEventListener('click', handleClickOutside)
|
|
142
|
-
window.addEventListener('keyup', handleKeyup)
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
return () => {
|
|
146
|
-
window.removeEventListener('click', handleClickOutside)
|
|
147
|
-
window.removeEventListener('keyup', handleKeyup)
|
|
148
|
-
}
|
|
149
|
-
}, [_visible])
|
|
150
|
-
|
|
151
153
|
useEffect(() => {
|
|
152
154
|
setVisible(visible)
|
|
153
155
|
}, [visible])
|
|
@@ -157,17 +159,6 @@ export const CDropdown = forwardRef<HTMLDivElement | HTMLLIElement, CDropdownPro
|
|
|
157
159
|
!_visible && onHide && onHide()
|
|
158
160
|
}, [_visible])
|
|
159
161
|
|
|
160
|
-
const handleKeyup = (event: Event) => {
|
|
161
|
-
if (!dropdownRef.current?.contains(event.target as HTMLElement)) {
|
|
162
|
-
setVisible(false)
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
const handleClickOutside = (event: Event) => {
|
|
166
|
-
if (!dropdownRef.current?.contains(event.target as HTMLElement)) {
|
|
167
|
-
setVisible(false)
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
162
|
const dropdownContent = () => {
|
|
172
163
|
return variant === 'input-group' ? (
|
|
173
164
|
<>{children}</>
|
|
@@ -203,6 +194,10 @@ CDropdown.propTypes = {
|
|
|
203
194
|
PropTypes.shape({ xl: alignmentDirection }),
|
|
204
195
|
PropTypes.shape({ xxl: alignmentDirection }),
|
|
205
196
|
]),
|
|
197
|
+
autoClose: PropTypes.oneOfType([
|
|
198
|
+
PropTypes.bool,
|
|
199
|
+
PropTypes.oneOf<'inside' | 'outside'>(['inside', 'outside']),
|
|
200
|
+
]),
|
|
206
201
|
children: PropTypes.node,
|
|
207
202
|
className: PropTypes.string,
|
|
208
203
|
component: PropTypes.elementType,
|