@coreui/react-pro 4.2.0-beta.0 → 4.2.0-beta.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.
- package/README.md +1 -1
- package/dist/components/calendar/CCalendar.d.ts +27 -10
- package/dist/components/calendar/CCalendar.utils.d.ts +2 -1
- package/dist/components/daterangepicker/CDateRangePicker.d.ts +26 -11
- package/dist/components/picker/CPicker.d.ts +50 -9
- package/dist/components/timepicker/CTimePicker.d.ts +9 -13
- package/dist/components/timepicker/CTimePicker.utils.d.ts +13 -0
- package/dist/index.es.js +243 -206
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +243 -206
- package/dist/index.js.map +1 -1
- package/dist/utils/calendar.d.ts +21 -0
- package/dist/utils/time.d.ts +17 -0
- package/package.json +3 -2
- package/src/components/accordion/__tests__/__snapshots__/CAccordionButton.spec.tsx.snap +2 -0
- package/src/components/accordion/__tests__/__snapshots__/CAccordionHeader.spec.tsx.snap +2 -0
- package/src/components/calendar/CCalendar.tsx +104 -58
- package/src/components/calendar/__tests__/CCalendar.spec.tsx +9 -0
- package/src/components/calendar/__tests__/__snapshots__/CCalendar.spec.tsx.snap +573 -0
- package/src/components/datepicker/CDatePicker.tsx +1 -0
- package/src/components/datepicker/__tests__/CDatePicker.spec.tsx +9 -0
- package/src/components/datepicker/__tests__/__snapshots__/CDatePicker.spec.tsx.snap +617 -0
- package/src/components/daterangepicker/CDateRangePicker.tsx +152 -91
- package/src/components/daterangepicker/__tests__/CDateRangePicker.spec.tsx +9 -0
- package/src/components/daterangepicker/__tests__/__snapshots__/CDateRangePicker.spec.tsx.snap +1201 -0
- package/src/components/dropdown/CDropdownMenu.tsx +5 -3
- package/src/components/picker/CPicker.tsx +99 -34
- package/src/components/picker/__tests__/CPicker.spec.tsx +9 -0
- package/src/components/picker/__tests__/__snapshots__/CPicker.spec.tsx.snap +23 -0
- package/src/components/timepicker/CTimePicker.tsx +66 -87
- package/src/components/timepicker/CTimePickerRollCol.tsx +7 -1
- package/src/components/timepicker/__tests__/CTimePicker.spec.tsx +9 -0
- package/src/{components/calendar/CCalendar.utils.ts → utils/calendar.ts} +11 -1
- package/src/utils/time.ts +58 -0
- package/src/components/timepicker/CTimePicker.utils.ts +0 -27
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import PropTypes from 'prop-types'
|
|
2
1
|
import React, { ElementType, FC, HTMLAttributes, useContext, useEffect, useRef } from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
3
|
import classNames from 'classnames'
|
|
4
4
|
import { Popper, PopperChildrenProps } from 'react-popper'
|
|
5
5
|
|
|
@@ -59,14 +59,16 @@ export const CDropdownMenu: FC<CDropdownMenuProps> = ({
|
|
|
59
59
|
}
|
|
60
60
|
}, [visible])
|
|
61
61
|
|
|
62
|
-
const handleKeyup = (event:
|
|
62
|
+
const handleKeyup = (event: KeyboardEvent) => {
|
|
63
63
|
if (autoClose === false) {
|
|
64
64
|
return
|
|
65
65
|
}
|
|
66
|
-
|
|
66
|
+
|
|
67
|
+
if (event.key === 'Escape') {
|
|
67
68
|
setVisible(false)
|
|
68
69
|
}
|
|
69
70
|
}
|
|
71
|
+
|
|
70
72
|
const handleMouseUp = (event: Event) => {
|
|
71
73
|
if (dropdownToggleRef && dropdownToggleRef.current.contains(event.target as HTMLElement)) {
|
|
72
74
|
return
|
|
@@ -7,11 +7,57 @@ import { CDropdown, CDropdownProps } from '../dropdown/CDropdown'
|
|
|
7
7
|
import { CDropdownMenu } from '../dropdown/CDropdownMenu'
|
|
8
8
|
import { CDropdownToggle } from '../dropdown/CDropdownToggle'
|
|
9
9
|
|
|
10
|
+
import { Colors } from '../Types'
|
|
11
|
+
|
|
10
12
|
export interface CPickerProps extends Omit<CDropdownProps, 'variant'> {
|
|
11
13
|
/**
|
|
12
|
-
*
|
|
14
|
+
* Toggle visibility or set the content of cancel button.
|
|
15
|
+
*
|
|
16
|
+
* @default 'Cancel'
|
|
17
|
+
*/
|
|
18
|
+
cancelButton?: boolean | ReactNode
|
|
19
|
+
/**
|
|
20
|
+
* Sets the color context of the cancel button to one of CoreUI’s themed colors.
|
|
21
|
+
*
|
|
22
|
+
* @type 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info' | 'dark' | 'light' | string
|
|
23
|
+
* @default 'primary'
|
|
24
|
+
*/
|
|
25
|
+
cancelButtonColor?: Colors
|
|
26
|
+
/**
|
|
27
|
+
* Size the cancel button small or large.
|
|
28
|
+
*
|
|
29
|
+
* @default 'sm'
|
|
30
|
+
*/
|
|
31
|
+
cancelButtonSize?: 'sm' | 'lg'
|
|
32
|
+
/**
|
|
33
|
+
* Set the cancel button variant to an outlined button or a ghost button.
|
|
34
|
+
*
|
|
35
|
+
* @default 'ghost'
|
|
36
|
+
*/
|
|
37
|
+
cancelButtonVariant?: 'outline' | 'ghost'
|
|
38
|
+
/**
|
|
39
|
+
* Toggle visibility or set the content of confirm button.
|
|
40
|
+
*
|
|
41
|
+
* @default 'OK'
|
|
13
42
|
*/
|
|
14
|
-
|
|
43
|
+
confirmButton?: boolean | ReactNode
|
|
44
|
+
/**
|
|
45
|
+
* Sets the color context of the confirm button to one of CoreUI’s themed colors.
|
|
46
|
+
*
|
|
47
|
+
* @type 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info' | 'dark' | 'light' | string
|
|
48
|
+
* @default 'primary'
|
|
49
|
+
*/
|
|
50
|
+
confirmButtonColor?: Colors
|
|
51
|
+
/**
|
|
52
|
+
* Size the confirm button small or large.
|
|
53
|
+
*
|
|
54
|
+
* @default 'sm'
|
|
55
|
+
*/
|
|
56
|
+
confirmButtonSize?: 'sm' | 'lg'
|
|
57
|
+
/**
|
|
58
|
+
* Set the confirm button variant to an outlined button or a ghost button.
|
|
59
|
+
*/
|
|
60
|
+
confirmButtonVariant?: 'outline' | 'ghost'
|
|
15
61
|
/**
|
|
16
62
|
* Set container type for the component.
|
|
17
63
|
*/
|
|
@@ -24,18 +70,14 @@ export interface CPickerProps extends Omit<CDropdownProps, 'variant'> {
|
|
|
24
70
|
* Toggle visibility of footer element or set the content of footer.
|
|
25
71
|
*/
|
|
26
72
|
footer?: boolean | ReactNode
|
|
27
|
-
/**
|
|
28
|
-
* Ok button text
|
|
29
|
-
*/
|
|
30
|
-
okButtonText?: ReactNode
|
|
31
73
|
/**
|
|
32
74
|
* Callback fired when the cancel button has been clicked.
|
|
33
75
|
*/
|
|
34
|
-
|
|
76
|
+
onCancel?: () => void
|
|
35
77
|
/**
|
|
36
|
-
* Callback fired when the
|
|
78
|
+
* Callback fired when the confirm button has been clicked.
|
|
37
79
|
*/
|
|
38
|
-
|
|
80
|
+
onConfirm?: () => void
|
|
39
81
|
/**
|
|
40
82
|
* The content of toggler.
|
|
41
83
|
*/
|
|
@@ -46,14 +88,20 @@ export const CPicker = forwardRef<HTMLDivElement | HTMLLIElement, CPickerProps>(
|
|
|
46
88
|
(
|
|
47
89
|
{
|
|
48
90
|
children,
|
|
49
|
-
|
|
91
|
+
cancelButton = 'Cancel',
|
|
92
|
+
cancelButtonColor = 'primary',
|
|
93
|
+
cancelButtonSize = 'sm',
|
|
94
|
+
cancelButtonVariant = 'ghost',
|
|
95
|
+
confirmButton = 'OK',
|
|
96
|
+
confirmButtonColor = 'primary',
|
|
97
|
+
confirmButtonSize = 'sm',
|
|
98
|
+
confirmButtonVariant,
|
|
50
99
|
className,
|
|
51
100
|
container = 'dropdown',
|
|
52
101
|
disabled,
|
|
53
102
|
footer,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
onOkClick,
|
|
103
|
+
onCancel,
|
|
104
|
+
onConfirm,
|
|
57
105
|
onHide,
|
|
58
106
|
onShow,
|
|
59
107
|
toggler: Toggler,
|
|
@@ -70,24 +118,29 @@ export const CPicker = forwardRef<HTMLDivElement | HTMLLIElement, CPickerProps>(
|
|
|
70
118
|
|
|
71
119
|
const Footer = () => (
|
|
72
120
|
<>
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
121
|
+
{cancelButton && (
|
|
122
|
+
<CButton
|
|
123
|
+
color={cancelButtonColor}
|
|
124
|
+
size={cancelButtonSize}
|
|
125
|
+
variant={cancelButtonVariant}
|
|
126
|
+
onClick={() => onCancel && onCancel()}
|
|
127
|
+
>
|
|
128
|
+
{cancelButton}
|
|
129
|
+
</CButton>
|
|
130
|
+
)}
|
|
131
|
+
{confirmButton && (
|
|
132
|
+
<CButton
|
|
133
|
+
color={confirmButtonColor}
|
|
134
|
+
size={confirmButtonSize}
|
|
135
|
+
variant={confirmButtonVariant}
|
|
136
|
+
onClick={() => {
|
|
137
|
+
onConfirm && onConfirm()
|
|
138
|
+
setVisible(false)
|
|
139
|
+
}}
|
|
140
|
+
>
|
|
141
|
+
{confirmButton}
|
|
142
|
+
</CButton>
|
|
143
|
+
)}
|
|
91
144
|
</>
|
|
92
145
|
)
|
|
93
146
|
|
|
@@ -134,9 +187,21 @@ export const CPicker = forwardRef<HTMLDivElement | HTMLLIElement, CPickerProps>(
|
|
|
134
187
|
CPicker.displayName = 'CPicker'
|
|
135
188
|
|
|
136
189
|
CPicker.propTypes = {
|
|
137
|
-
|
|
190
|
+
cancelButton: PropTypes.oneOfType([PropTypes.bool, PropTypes.node]),
|
|
191
|
+
cancelButtonColor: CButton.propTypes?.color,
|
|
192
|
+
cancelButtonSize: CButton.propTypes?.size,
|
|
193
|
+
cancelButtonVariant: CButton.propTypes?.variant,
|
|
194
|
+
confirmButton: PropTypes.oneOfType([PropTypes.bool, PropTypes.node]),
|
|
195
|
+
confirmButtonColor: CButton.propTypes?.color,
|
|
196
|
+
confirmButtonSize: CButton.propTypes?.size,
|
|
197
|
+
confirmButtonVariant: CButton.propTypes?.variant,
|
|
198
|
+
children: PropTypes.node,
|
|
199
|
+
className: PropTypes.string,
|
|
138
200
|
container: PropTypes.oneOf(['dropdown', 'inline']),
|
|
139
201
|
disabled: PropTypes.bool,
|
|
140
|
-
|
|
141
|
-
|
|
202
|
+
footer: PropTypes.oneOfType([PropTypes.bool, PropTypes.node]),
|
|
203
|
+
onCancel: PropTypes.func,
|
|
204
|
+
onConfirm: PropTypes.func,
|
|
205
|
+
toggler: PropTypes.node,
|
|
206
|
+
...CDropdown.propTypes,
|
|
142
207
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { render } from '@testing-library/react'
|
|
3
|
+
import '@testing-library/jest-dom/extend-expect'
|
|
4
|
+
import { CPicker } from '../CPicker'
|
|
5
|
+
|
|
6
|
+
test('loads and displays CPicker component', async () => {
|
|
7
|
+
const { container } = render(<CPicker>Test</CPicker>)
|
|
8
|
+
expect(container).toMatchSnapshot()
|
|
9
|
+
})
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`loads and displays CPicker component 1`] = `
|
|
4
|
+
<div>
|
|
5
|
+
<div
|
|
6
|
+
class="dropdown picker"
|
|
7
|
+
>
|
|
8
|
+
<button
|
|
9
|
+
aria-expanded="false"
|
|
10
|
+
class="btn btn-primary dropdown-toggle"
|
|
11
|
+
tabindex="0"
|
|
12
|
+
type="button"
|
|
13
|
+
/>
|
|
14
|
+
<div
|
|
15
|
+
aria-hidden="true"
|
|
16
|
+
class="dropdown-menu"
|
|
17
|
+
role="menu"
|
|
18
|
+
>
|
|
19
|
+
Test
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
`;
|
|
@@ -10,7 +10,18 @@ import { CInputGroupText } from '../form/CInputGroupText'
|
|
|
10
10
|
|
|
11
11
|
import { CTimePickerRollCol } from './CTimePickerRollCol'
|
|
12
12
|
import { CPicker, CPickerProps } from './../picker/CPicker'
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
convert12hTo24h,
|
|
15
|
+
convertTimeToDate,
|
|
16
|
+
getAmPm,
|
|
17
|
+
getListOfHours,
|
|
18
|
+
getMinutesOrSeconds,
|
|
19
|
+
getSelectedHour,
|
|
20
|
+
getSelectedMinutes,
|
|
21
|
+
getSelectedSeconds,
|
|
22
|
+
isAmPm,
|
|
23
|
+
isValidTime,
|
|
24
|
+
} from '../../utils/time'
|
|
14
25
|
|
|
15
26
|
export interface CTimePickerProps
|
|
16
27
|
extends Omit<
|
|
@@ -22,29 +33,25 @@ export interface CTimePickerProps
|
|
|
22
33
|
*/
|
|
23
34
|
className?: string
|
|
24
35
|
/**
|
|
25
|
-
*
|
|
36
|
+
* Toggle visibility or set the content of the cleaner button.
|
|
26
37
|
*/
|
|
27
|
-
cleaner?: boolean
|
|
38
|
+
cleaner?: ReactNode | boolean
|
|
28
39
|
/**
|
|
29
|
-
*
|
|
40
|
+
* Toggle visibility or set the content of the input indicator.
|
|
30
41
|
*/
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Default input icon.
|
|
34
|
-
*/
|
|
35
|
-
defaultIcon?: ReactNode | boolean
|
|
42
|
+
indicator?: ReactNode | boolean
|
|
36
43
|
/**
|
|
37
44
|
* Toggle the readonly state for the component.
|
|
38
45
|
*/
|
|
39
46
|
inputReadOnly?: boolean
|
|
40
47
|
/**
|
|
41
|
-
* Sets the default locale for components. If not set, it is inherited from the
|
|
48
|
+
* Sets the default locale for components. If not set, it is inherited from the browser.
|
|
42
49
|
*/
|
|
43
50
|
locale?: string
|
|
44
51
|
/**
|
|
45
|
-
* Callback fired when the
|
|
52
|
+
* Callback fired when the time changed.
|
|
46
53
|
*/
|
|
47
|
-
|
|
54
|
+
onTimeChange?: (timeString: string, localeTimeString: string, date: Date) => void
|
|
48
55
|
/**
|
|
49
56
|
* Specifies a short hint that is visible in the input.
|
|
50
57
|
*/
|
|
@@ -54,9 +61,9 @@ export interface CTimePickerProps
|
|
|
54
61
|
*/
|
|
55
62
|
size?: 'sm' | 'lg'
|
|
56
63
|
/**
|
|
57
|
-
*
|
|
64
|
+
* Initial selected time.
|
|
58
65
|
*/
|
|
59
|
-
|
|
66
|
+
time?: Date | string | null
|
|
60
67
|
/**
|
|
61
68
|
* Set the time picker variant to a roll or select.
|
|
62
69
|
*/
|
|
@@ -68,65 +75,39 @@ export const CTimePicker = forwardRef<HTMLDivElement | HTMLLIElement, CTimePicke
|
|
|
68
75
|
{
|
|
69
76
|
className,
|
|
70
77
|
cleaner = true,
|
|
71
|
-
cleanerIcon,
|
|
72
78
|
container = 'dropdown',
|
|
73
|
-
defaultIcon,
|
|
74
79
|
disabled,
|
|
75
80
|
footer = true,
|
|
81
|
+
indicator = true,
|
|
76
82
|
inputReadOnly,
|
|
77
|
-
locale =
|
|
78
|
-
|
|
83
|
+
locale = 'default',
|
|
84
|
+
onTimeChange,
|
|
85
|
+
onHide,
|
|
86
|
+
onShow,
|
|
79
87
|
placeholder = 'Select time',
|
|
80
88
|
size,
|
|
81
|
-
|
|
89
|
+
time,
|
|
82
90
|
variant = 'roll',
|
|
83
91
|
...rest
|
|
84
92
|
},
|
|
85
93
|
ref,
|
|
86
94
|
) => {
|
|
87
95
|
const inputRef = useRef<HTMLInputElement>(null)
|
|
88
|
-
const [date, setDate] = useState<Date | null>(
|
|
89
|
-
|
|
90
|
-
)
|
|
96
|
+
const [date, setDate] = useState<Date | null>(convertTimeToDate(time))
|
|
97
|
+
const [initialDate, setInitialDate] = useState<Date | null>(null)
|
|
91
98
|
const [_ampm, setAmPm] = useState<'am' | 'pm'>(date ? getAmPm(new Date(date), locale) : 'am')
|
|
92
99
|
|
|
93
100
|
useEffect(() => {
|
|
94
|
-
setDate(
|
|
95
|
-
|
|
96
|
-
)
|
|
97
|
-
}, [value])
|
|
101
|
+
setDate(convertTimeToDate(time))
|
|
102
|
+
}, [time])
|
|
98
103
|
|
|
99
104
|
useEffect(() => {
|
|
100
105
|
if (inputRef.current) {
|
|
101
106
|
inputRef.current.value = date ? date.toLocaleTimeString(locale) : ''
|
|
102
107
|
}
|
|
103
|
-
}, [date])
|
|
104
|
-
|
|
105
|
-
const getHours = (locale: string) =>
|
|
106
|
-
Array.from(new Array(isAmPm(locale) ? 12 : 24), (_, i) => {
|
|
107
|
-
return {
|
|
108
|
-
value: isAmPm(locale) ? i + 1 : i,
|
|
109
|
-
label: (isAmPm(locale) ? i + 1 : i).toLocaleString(locale),
|
|
110
|
-
}
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
const getMinutesOrSeconds = (locale: string) =>
|
|
114
|
-
Array.from(new Array(60), (_, i) => {
|
|
115
|
-
return {
|
|
116
|
-
value: i,
|
|
117
|
-
label: i.toLocaleString(locale).padStart(2, (0).toLocaleString(locale)),
|
|
118
|
-
}
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
const selectedHour = date
|
|
122
|
-
? isAmPm(locale)
|
|
123
|
-
? convert24hTo12h(date.getHours())
|
|
124
|
-
: date.getHours()
|
|
125
|
-
: null
|
|
126
108
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const selectedSeconds = date ? date.getSeconds() : null
|
|
109
|
+
date && setAmPm(getAmPm(new Date(date), locale))
|
|
110
|
+
}, [date])
|
|
130
111
|
|
|
131
112
|
const handleClear = (event: React.MouseEvent<HTMLElement>) => {
|
|
132
113
|
event.stopPropagation()
|
|
@@ -138,11 +119,9 @@ export const CTimePicker = forwardRef<HTMLDivElement | HTMLLIElement, CTimePicke
|
|
|
138
119
|
|
|
139
120
|
if (set === 'toggle') {
|
|
140
121
|
if (value === 'am') {
|
|
141
|
-
setAmPm('am')
|
|
142
122
|
_date.setHours(_date.getHours() - 12)
|
|
143
123
|
}
|
|
144
124
|
if (value === 'pm') {
|
|
145
|
-
setAmPm('pm')
|
|
146
125
|
_date.setHours(_date.getHours() + 12)
|
|
147
126
|
}
|
|
148
127
|
}
|
|
@@ -164,38 +143,41 @@ export const CTimePicker = forwardRef<HTMLDivElement | HTMLLIElement, CTimePicke
|
|
|
164
143
|
}
|
|
165
144
|
|
|
166
145
|
setDate(new Date(_date))
|
|
167
|
-
|
|
146
|
+
onTimeChange && onTimeChange(_date.toTimeString(), _date.toLocaleTimeString(), _date)
|
|
168
147
|
}
|
|
169
148
|
|
|
170
149
|
const InputGroup = () => (
|
|
171
150
|
<CInputGroup className="picker-input-group" size={size}>
|
|
172
151
|
<CFormInput
|
|
173
152
|
disabled={disabled}
|
|
153
|
+
onChange={(event) =>
|
|
154
|
+
isValidTime(event.target.value) && setDate(convertTimeToDate(event.target.value))
|
|
155
|
+
}
|
|
174
156
|
placeholder={placeholder}
|
|
175
157
|
readOnly={inputReadOnly}
|
|
176
158
|
ref={inputRef}
|
|
177
159
|
/>
|
|
178
|
-
{(
|
|
160
|
+
{(indicator || cleaner) && (
|
|
179
161
|
<CInputGroupText>
|
|
180
|
-
{
|
|
162
|
+
{indicator && (
|
|
181
163
|
<span className="picker-input-group-indicator">
|
|
182
|
-
{
|
|
183
|
-
defaultIcon
|
|
184
|
-
) : (
|
|
164
|
+
{typeof indicator === 'boolean' ? (
|
|
185
165
|
<span className="picker-input-group-icon time-picker-input-icon" />
|
|
166
|
+
) : (
|
|
167
|
+
indicator
|
|
186
168
|
)}
|
|
187
169
|
</span>
|
|
188
170
|
)}
|
|
189
171
|
{cleaner && date && (
|
|
190
172
|
<span
|
|
191
173
|
className="picker-input-group-cleaner"
|
|
192
|
-
role="button"
|
|
193
174
|
onClick={(event) => handleClear(event)}
|
|
175
|
+
role="button"
|
|
194
176
|
>
|
|
195
|
-
{
|
|
196
|
-
cleanerIcon
|
|
197
|
-
) : (
|
|
177
|
+
{typeof cleaner === 'boolean' ? (
|
|
198
178
|
<span className="picker-input-group-icon time-picker-cleaner-icon" />
|
|
179
|
+
) : (
|
|
180
|
+
cleaner
|
|
199
181
|
)}
|
|
200
182
|
</span>
|
|
201
183
|
)}
|
|
@@ -210,7 +192,7 @@ export const CTimePicker = forwardRef<HTMLDivElement | HTMLLIElement, CTimePicke
|
|
|
210
192
|
<span className="time-picker-inline-icon" />
|
|
211
193
|
<CFormSelect
|
|
212
194
|
disabled={disabled}
|
|
213
|
-
options={
|
|
195
|
+
options={getListOfHours(locale).map((option) => {
|
|
214
196
|
return {
|
|
215
197
|
value: option.value.toString(),
|
|
216
198
|
label: option.label,
|
|
@@ -219,12 +201,12 @@ export const CTimePicker = forwardRef<HTMLDivElement | HTMLLIElement, CTimePicke
|
|
|
219
201
|
onChange={(event: React.ChangeEvent<HTMLSelectElement>) =>
|
|
220
202
|
handleTimeChange('hours', event.target.value)
|
|
221
203
|
}
|
|
222
|
-
value={
|
|
204
|
+
value={getSelectedHour(date, locale)}
|
|
223
205
|
/>
|
|
224
|
-
|
|
206
|
+
<>:</>
|
|
225
207
|
<CFormSelect
|
|
226
208
|
disabled={disabled}
|
|
227
|
-
options={Array.from(
|
|
209
|
+
options={Array.from({ length: 60 }, (_, i) => {
|
|
228
210
|
return {
|
|
229
211
|
value: i.toString(),
|
|
230
212
|
label: i.toLocaleString(locale).padStart(2, (0).toLocaleString(locale)),
|
|
@@ -233,12 +215,12 @@ export const CTimePicker = forwardRef<HTMLDivElement | HTMLLIElement, CTimePicke
|
|
|
233
215
|
onChange={(event: React.ChangeEvent<HTMLSelectElement>) =>
|
|
234
216
|
handleTimeChange('minutes', event.target.value)
|
|
235
217
|
}
|
|
236
|
-
value={
|
|
218
|
+
value={getSelectedMinutes(date)}
|
|
237
219
|
/>
|
|
238
|
-
|
|
220
|
+
<>:</>
|
|
239
221
|
<CFormSelect
|
|
240
222
|
disabled={disabled}
|
|
241
|
-
options={Array.from(
|
|
223
|
+
options={Array.from({ length: 60 }, (_, i) => {
|
|
242
224
|
return {
|
|
243
225
|
value: i.toString(),
|
|
244
226
|
label: i.toLocaleString(locale).padStart(2, (0).toLocaleString(locale)),
|
|
@@ -247,7 +229,7 @@ export const CTimePicker = forwardRef<HTMLDivElement | HTMLLIElement, CTimePicke
|
|
|
247
229
|
onChange={(event: React.ChangeEvent<HTMLSelectElement>) =>
|
|
248
230
|
handleTimeChange('seconds', event.target.value)
|
|
249
231
|
}
|
|
250
|
-
value={
|
|
232
|
+
value={getSelectedSeconds(date)}
|
|
251
233
|
/>
|
|
252
234
|
{isAmPm(locale) && (
|
|
253
235
|
<CFormSelect
|
|
@@ -269,19 +251,19 @@ export const CTimePicker = forwardRef<HTMLDivElement | HTMLLIElement, CTimePicke
|
|
|
269
251
|
const TimePickerRoll = () => (
|
|
270
252
|
<>
|
|
271
253
|
<CTimePickerRollCol
|
|
272
|
-
elements={
|
|
254
|
+
elements={getListOfHours(locale)}
|
|
273
255
|
onClick={(index: number) => handleTimeChange('hours', index.toString())}
|
|
274
|
-
selected={
|
|
256
|
+
selected={getSelectedHour(date, locale)}
|
|
275
257
|
/>
|
|
276
258
|
<CTimePickerRollCol
|
|
277
259
|
elements={getMinutesOrSeconds(locale)}
|
|
278
|
-
selected={selectedMinutes}
|
|
279
260
|
onClick={(index: number) => handleTimeChange('minutes', index.toString())}
|
|
261
|
+
selected={getSelectedMinutes(date)}
|
|
280
262
|
/>
|
|
281
263
|
<CTimePickerRollCol
|
|
282
264
|
elements={getMinutesOrSeconds(locale)}
|
|
283
|
-
selected={selectedSeconds}
|
|
284
265
|
onClick={(index: number) => handleTimeChange('seconds', index.toString())}
|
|
266
|
+
selected={getSelectedSeconds(date)}
|
|
285
267
|
/>
|
|
286
268
|
{isAmPm(locale) && (
|
|
287
269
|
<CTimePickerRollCol
|
|
@@ -289,8 +271,8 @@ export const CTimePicker = forwardRef<HTMLDivElement | HTMLLIElement, CTimePicke
|
|
|
289
271
|
{ value: 'am', label: 'AM' },
|
|
290
272
|
{ value: 'pm', label: 'PM' },
|
|
291
273
|
]}
|
|
292
|
-
selected={_ampm}
|
|
293
274
|
onClick={(value: string) => handleTimeChange('toggle', value)}
|
|
275
|
+
selected={_ampm}
|
|
294
276
|
/>
|
|
295
277
|
)}
|
|
296
278
|
</>
|
|
@@ -304,14 +286,11 @@ export const CTimePicker = forwardRef<HTMLDivElement | HTMLLIElement, CTimePicke
|
|
|
304
286
|
container={container}
|
|
305
287
|
disabled={disabled}
|
|
306
288
|
footer={footer}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
: new Date(`1970-01-01 ${value}`)
|
|
313
|
-
: new Date(),
|
|
314
|
-
)
|
|
289
|
+
onCancel={() => initialDate && setDate(new Date(initialDate))}
|
|
290
|
+
onHide={() => onHide && onHide()}
|
|
291
|
+
onShow={() => {
|
|
292
|
+
date && setInitialDate(new Date(date))
|
|
293
|
+
onShow && onShow()
|
|
315
294
|
}}
|
|
316
295
|
toggler={InputGroup()}
|
|
317
296
|
{...rest}
|
|
@@ -333,8 +312,8 @@ CTimePicker.propTypes = {
|
|
|
333
312
|
...CPicker.propTypes,
|
|
334
313
|
className: PropTypes.string,
|
|
335
314
|
locale: PropTypes.string,
|
|
336
|
-
|
|
337
|
-
|
|
315
|
+
onTimeChange: PropTypes.func,
|
|
316
|
+
time: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.string]),
|
|
338
317
|
variant: PropTypes.oneOf(['roll', 'select']),
|
|
339
318
|
}
|
|
340
319
|
|
|
@@ -17,14 +17,20 @@ export interface CTimePickerRollColProps {
|
|
|
17
17
|
|
|
18
18
|
export const CTimePickerRollCol = forwardRef<HTMLDivElement, CTimePickerRollColProps>(
|
|
19
19
|
({ elements, onClick, selected }, ref) => {
|
|
20
|
+
const init = useRef(true)
|
|
20
21
|
const colRef = useRef<HTMLDivElement>(null)
|
|
21
22
|
const forkedRef = useForkedRef(ref, colRef)
|
|
22
23
|
|
|
23
24
|
useEffect(() => {
|
|
24
25
|
const nodeEl = colRef.current?.querySelector('.selected')
|
|
25
26
|
if (nodeEl && nodeEl instanceof HTMLElement) {
|
|
26
|
-
colRef.current?.scrollTo({
|
|
27
|
+
colRef.current?.scrollTo({
|
|
28
|
+
top: nodeEl.offsetTop,
|
|
29
|
+
behavior: init.current ? 'auto' : 'smooth',
|
|
30
|
+
})
|
|
27
31
|
}
|
|
32
|
+
|
|
33
|
+
init.current = false
|
|
28
34
|
}, [selected])
|
|
29
35
|
|
|
30
36
|
return (
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { render } from '@testing-library/react'
|
|
3
|
+
import '@testing-library/jest-dom/extend-expect'
|
|
4
|
+
import { CTimePicker } from '../CTimePicker'
|
|
5
|
+
|
|
6
|
+
test('loads and displays CTimePicker component', async () => {
|
|
7
|
+
const { container } = render(<CTimePicker />)
|
|
8
|
+
expect(container).toMatchSnapshot()
|
|
9
|
+
})
|
|
@@ -115,7 +115,12 @@ export const getMonthDetails = (year: number, month: number, firstDayOfWeek: num
|
|
|
115
115
|
return weeks
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
export const
|
|
118
|
+
export const isDateDisabled = (
|
|
119
|
+
date: Date,
|
|
120
|
+
min?: Date | null,
|
|
121
|
+
max?: Date | null,
|
|
122
|
+
dates?: Date[] | Date[][],
|
|
123
|
+
) => {
|
|
119
124
|
let disabled
|
|
120
125
|
if (dates) {
|
|
121
126
|
dates.forEach((_date: Date | Date[]) => {
|
|
@@ -181,3 +186,8 @@ export const isToday = (date: Date) => {
|
|
|
181
186
|
date.getFullYear() == today.getFullYear()
|
|
182
187
|
)
|
|
183
188
|
}
|
|
189
|
+
|
|
190
|
+
export const isValidDate = (date: string) => {
|
|
191
|
+
const d = new Date(date)
|
|
192
|
+
return d instanceof Date && d.getTime()
|
|
193
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export const convert12hTo24h = (abbr: 'am' | 'pm', hour: number) => {
|
|
2
|
+
if (abbr === 'am' && hour === 12) {
|
|
3
|
+
return 0
|
|
4
|
+
}
|
|
5
|
+
if (abbr === 'am') {
|
|
6
|
+
return hour
|
|
7
|
+
}
|
|
8
|
+
if (abbr === 'pm' && hour === 12) {
|
|
9
|
+
return 12
|
|
10
|
+
}
|
|
11
|
+
return hour + 12
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const convert24hTo12h = (hour: number) => hour % 12 || 12
|
|
15
|
+
|
|
16
|
+
export const convertTimeToDate = (time: Date | string | null | undefined) =>
|
|
17
|
+
time ? (time instanceof Date ? new Date(time) : new Date(`1970-01-01 ${time}`)) : null
|
|
18
|
+
|
|
19
|
+
export const getAmPm = (date: Date, locale: string) => {
|
|
20
|
+
if (date.toLocaleTimeString(locale).includes('AM')) {
|
|
21
|
+
return 'am'
|
|
22
|
+
}
|
|
23
|
+
if (date.toLocaleTimeString(locale).includes('PM')) {
|
|
24
|
+
return 'pm'
|
|
25
|
+
}
|
|
26
|
+
return date.getHours() >= 12 ? 'pm' : 'am'
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const getListOfHours = (locale: string) =>
|
|
30
|
+
Array.from({ length: isAmPm(locale) ? 12 : 24 }, (_, i) => {
|
|
31
|
+
return {
|
|
32
|
+
value: isAmPm(locale) ? i + 1 : i,
|
|
33
|
+
label: (isAmPm(locale) ? i + 1 : i).toLocaleString(locale),
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
export const getMinutesOrSeconds = (locale: string) =>
|
|
38
|
+
Array.from({ length: 60 }, (_, i) => {
|
|
39
|
+
return {
|
|
40
|
+
value: i,
|
|
41
|
+
label: i.toLocaleString(locale).padStart(2, (0).toLocaleString(locale)),
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
export const getSelectedHour = (date: Date | null, locale: string) =>
|
|
46
|
+
date ? (isAmPm(locale) ? convert24hTo12h(date.getHours()) : date.getHours()) : ''
|
|
47
|
+
|
|
48
|
+
export const getSelectedMinutes = (date: Date | null) => (date ? date.getMinutes() : '')
|
|
49
|
+
|
|
50
|
+
export const getSelectedSeconds = (date: Date | null) => (date ? date.getSeconds() : '')
|
|
51
|
+
|
|
52
|
+
export const isAmPm = (locale: string) =>
|
|
53
|
+
['am', 'AM', 'pm', 'PM'].some((el) => new Date().toLocaleString(locale).includes(el))
|
|
54
|
+
|
|
55
|
+
export const isValidTime = (time: string) => {
|
|
56
|
+
const d = new Date(`1970-01-01 ${time}`)
|
|
57
|
+
return d instanceof Date && d.getTime()
|
|
58
|
+
}
|