@coreui/react-pro 4.1.4 → 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/Types.d.ts +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/picker/CPicker.d.ts +37 -0
- 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 -2
- package/dist/index.es.js +2528 -750
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +2714 -933
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/components/Types.tsx +2 -2
- 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/picker/CPicker.tsx +142 -0
- 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 -2
- package/src/components/dropdown/CDropdownToggleCustom.tsx +0 -81
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import PropTypes from 'prop-types'
|
|
2
|
-
import React, { ElementType, FC, HTMLAttributes, useContext } from 'react'
|
|
2
|
+
import React, { ElementType, FC, HTMLAttributes, useContext, useEffect, useRef } from 'react'
|
|
3
3
|
import classNames from 'classnames'
|
|
4
4
|
import { Popper, PopperChildrenProps } from 'react-popper'
|
|
5
5
|
|
|
@@ -35,7 +35,57 @@ export const CDropdownMenu: FC<CDropdownMenuProps> = ({
|
|
|
35
35
|
component: Component = 'ul',
|
|
36
36
|
...rest
|
|
37
37
|
}) => {
|
|
38
|
-
const {
|
|
38
|
+
const {
|
|
39
|
+
alignment,
|
|
40
|
+
autoClose,
|
|
41
|
+
dark,
|
|
42
|
+
direction,
|
|
43
|
+
dropdownToggleRef,
|
|
44
|
+
placement,
|
|
45
|
+
popper,
|
|
46
|
+
visible,
|
|
47
|
+
setVisible,
|
|
48
|
+
} = useContext(CDropdownContext)
|
|
49
|
+
|
|
50
|
+
const dropdownMenuRef = useRef<HTMLDivElement>(null)
|
|
51
|
+
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
visible && window.addEventListener('mouseup', handleMouseUp)
|
|
54
|
+
visible && window.addEventListener('keyup', handleKeyup)
|
|
55
|
+
|
|
56
|
+
return () => {
|
|
57
|
+
window.removeEventListener('mouseup', handleMouseUp)
|
|
58
|
+
window.removeEventListener('keyup', handleKeyup)
|
|
59
|
+
}
|
|
60
|
+
}, [visible])
|
|
61
|
+
|
|
62
|
+
const handleKeyup = (event: Event) => {
|
|
63
|
+
if (autoClose === false) {
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
if (!dropdownMenuRef.current?.contains(event.target as HTMLElement)) {
|
|
67
|
+
setVisible(false)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const handleMouseUp = (event: Event) => {
|
|
71
|
+
if (dropdownToggleRef && dropdownToggleRef.current.contains(event.target as HTMLElement)) {
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
if (autoClose === true) {
|
|
75
|
+
setVisible(false)
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
if (autoClose === 'inside' && dropdownMenuRef.current?.contains(event.target as HTMLElement)) {
|
|
79
|
+
setVisible(false)
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
if (
|
|
83
|
+
autoClose === 'outside' &&
|
|
84
|
+
!dropdownMenuRef.current?.contains(event.target as HTMLElement)
|
|
85
|
+
) {
|
|
86
|
+
setVisible(false)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
39
89
|
|
|
40
90
|
let _placement: Placements = placement
|
|
41
91
|
|
|
@@ -101,7 +151,9 @@ export const CDropdownMenu: FC<CDropdownMenuProps> = ({
|
|
|
101
151
|
}
|
|
102
152
|
|
|
103
153
|
return popper && visible ? (
|
|
104
|
-
<Popper placement={_placement}>
|
|
154
|
+
<Popper innerRef={dropdownMenuRef} placement={_placement}>
|
|
155
|
+
{({ ref, style }) => dropdownMenuComponent(style, ref)}
|
|
156
|
+
</Popper>
|
|
105
157
|
) : (
|
|
106
158
|
dropdownMenuComponent()
|
|
107
159
|
)
|
|
@@ -3,6 +3,8 @@ import PropTypes from 'prop-types'
|
|
|
3
3
|
import classNames from 'classnames'
|
|
4
4
|
import { Reference } from 'react-popper'
|
|
5
5
|
|
|
6
|
+
import { useForkedRef } from '../../utils/hooks'
|
|
7
|
+
|
|
6
8
|
import { Triggers, triggerPropType } from '../Types'
|
|
7
9
|
|
|
8
10
|
import { CButton, CButtonProps } from '../button/CButton'
|
|
@@ -13,6 +15,10 @@ export interface CDropdownToggleProps extends Omit<CButtonProps, 'type'> {
|
|
|
13
15
|
* Enables pseudo element caret on toggler.
|
|
14
16
|
*/
|
|
15
17
|
caret?: boolean
|
|
18
|
+
/**
|
|
19
|
+
* Create a custom toggler which accepts any content.
|
|
20
|
+
*/
|
|
21
|
+
custom?: boolean
|
|
16
22
|
/**
|
|
17
23
|
* Similarly, create split button dropdowns with virtually the same markup as single button dropdowns, but with the addition of `.dropdown-toggle-split` className for proper spacing around the dropdown caret.
|
|
18
24
|
*/
|
|
@@ -28,12 +34,13 @@ export interface CDropdownToggleProps extends Omit<CButtonProps, 'type'> {
|
|
|
28
34
|
export const CDropdownToggle: FC<CDropdownToggleProps> = ({
|
|
29
35
|
children,
|
|
30
36
|
caret = true,
|
|
37
|
+
custom,
|
|
31
38
|
className,
|
|
32
39
|
split,
|
|
33
40
|
trigger = 'click',
|
|
34
41
|
...rest
|
|
35
42
|
}) => {
|
|
36
|
-
const { popper, variant, visible, setVisible } = useContext(CDropdownContext)
|
|
43
|
+
const { dropdownToggleRef, popper, variant, visible, setVisible } = useContext(CDropdownContext)
|
|
37
44
|
const _className = classNames(
|
|
38
45
|
{
|
|
39
46
|
'dropdown-toggle': caret,
|
|
@@ -59,31 +66,47 @@ export const CDropdownToggle: FC<CDropdownToggleProps> = ({
|
|
|
59
66
|
const togglerProps = {
|
|
60
67
|
className: _className,
|
|
61
68
|
'aria-expanded': visible,
|
|
69
|
+
...(!rest.disabled && { ...triggers }),
|
|
62
70
|
...triggers,
|
|
63
71
|
}
|
|
64
72
|
|
|
65
73
|
// We use any because Toggler can be `a` as well as `button`.
|
|
66
74
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
67
75
|
const Toggler = (ref?: React.Ref<any>) => {
|
|
68
|
-
return
|
|
69
|
-
|
|
76
|
+
return custom && React.isValidElement(children) ? (
|
|
77
|
+
<>
|
|
78
|
+
{React.cloneElement(children, {
|
|
79
|
+
'aria-expanded': visible,
|
|
80
|
+
...(!rest.disabled && { ...triggers }),
|
|
81
|
+
ref: useForkedRef(ref, dropdownToggleRef),
|
|
82
|
+
})}
|
|
83
|
+
</>
|
|
84
|
+
) : variant === 'nav-item' ? (
|
|
85
|
+
<a href="#" {...togglerProps} ref={useForkedRef(ref, dropdownToggleRef)}>
|
|
70
86
|
{children}
|
|
71
87
|
</a>
|
|
72
88
|
) : (
|
|
73
|
-
<CButton
|
|
89
|
+
<CButton
|
|
90
|
+
type="button"
|
|
91
|
+
{...togglerProps}
|
|
92
|
+
tabIndex={0}
|
|
93
|
+
{...rest}
|
|
94
|
+
ref={useForkedRef(ref, dropdownToggleRef)}
|
|
95
|
+
>
|
|
74
96
|
{children}
|
|
75
97
|
{split && <span className="visually-hidden">Toggle Dropdown</span>}
|
|
76
98
|
</CButton>
|
|
77
99
|
)
|
|
78
100
|
}
|
|
79
101
|
|
|
80
|
-
return popper ? <Reference>{({ ref }) => Toggler(ref)}</Reference> : Toggler()
|
|
102
|
+
return popper ? <Reference>{({ ref }) => Toggler(ref)}</Reference> : Toggler(dropdownToggleRef)
|
|
81
103
|
}
|
|
82
104
|
|
|
83
105
|
CDropdownToggle.propTypes = {
|
|
84
106
|
caret: PropTypes.bool,
|
|
85
107
|
children: PropTypes.node,
|
|
86
108
|
className: PropTypes.string,
|
|
109
|
+
custom: PropTypes.bool,
|
|
87
110
|
split: PropTypes.bool,
|
|
88
111
|
trigger: triggerPropType,
|
|
89
112
|
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import React, { forwardRef, ReactNode, useEffect, useState } from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import classNames from 'classnames'
|
|
4
|
+
|
|
5
|
+
import { CButton } from '../button/CButton'
|
|
6
|
+
import { CDropdown, CDropdownProps } from '../dropdown/CDropdown'
|
|
7
|
+
import { CDropdownMenu } from '../dropdown/CDropdownMenu'
|
|
8
|
+
import { CDropdownToggle } from '../dropdown/CDropdownToggle'
|
|
9
|
+
|
|
10
|
+
export interface CPickerProps extends Omit<CDropdownProps, 'variant'> {
|
|
11
|
+
/**
|
|
12
|
+
* Cancel button text.
|
|
13
|
+
*/
|
|
14
|
+
cancelButtonText?: ReactNode
|
|
15
|
+
/**
|
|
16
|
+
* Set container type for the component.
|
|
17
|
+
*/
|
|
18
|
+
container?: 'dropdown' | 'inline'
|
|
19
|
+
/**
|
|
20
|
+
* Toggle the disabled state for the component.
|
|
21
|
+
*/
|
|
22
|
+
disabled?: boolean
|
|
23
|
+
/**
|
|
24
|
+
* Toggle visibility of footer element or set the content of footer.
|
|
25
|
+
*/
|
|
26
|
+
footer?: boolean | ReactNode
|
|
27
|
+
/**
|
|
28
|
+
* Ok button text
|
|
29
|
+
*/
|
|
30
|
+
okButtonText?: ReactNode
|
|
31
|
+
/**
|
|
32
|
+
* Callback fired when the cancel button has been clicked.
|
|
33
|
+
*/
|
|
34
|
+
onCancelClick?: () => void
|
|
35
|
+
/**
|
|
36
|
+
* Callback fired when the ok button has been clicked.
|
|
37
|
+
*/
|
|
38
|
+
onOkClick?: () => void
|
|
39
|
+
/**
|
|
40
|
+
* The content of toggler.
|
|
41
|
+
*/
|
|
42
|
+
toggler?: ReactNode
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const CPicker = forwardRef<HTMLDivElement | HTMLLIElement, CPickerProps>(
|
|
46
|
+
(
|
|
47
|
+
{
|
|
48
|
+
children,
|
|
49
|
+
cancelButtonText = 'Cancel',
|
|
50
|
+
className,
|
|
51
|
+
container = 'dropdown',
|
|
52
|
+
disabled,
|
|
53
|
+
footer,
|
|
54
|
+
okButtonText = 'OK',
|
|
55
|
+
onCancelClick,
|
|
56
|
+
onOkClick,
|
|
57
|
+
onHide,
|
|
58
|
+
onShow,
|
|
59
|
+
toggler: Toggler,
|
|
60
|
+
visible,
|
|
61
|
+
...rest
|
|
62
|
+
},
|
|
63
|
+
ref,
|
|
64
|
+
) => {
|
|
65
|
+
const [_visible, setVisible] = useState(visible)
|
|
66
|
+
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
setVisible(visible)
|
|
69
|
+
}, [visible])
|
|
70
|
+
|
|
71
|
+
const Footer = () => (
|
|
72
|
+
<>
|
|
73
|
+
<CButton
|
|
74
|
+
color="primary"
|
|
75
|
+
size="sm"
|
|
76
|
+
variant="ghost"
|
|
77
|
+
onClick={() => onCancelClick && onCancelClick()}
|
|
78
|
+
>
|
|
79
|
+
{cancelButtonText}
|
|
80
|
+
</CButton>
|
|
81
|
+
<CButton
|
|
82
|
+
color="primary"
|
|
83
|
+
size="sm"
|
|
84
|
+
onClick={() => {
|
|
85
|
+
onOkClick && onOkClick()
|
|
86
|
+
setVisible(false)
|
|
87
|
+
}}
|
|
88
|
+
>
|
|
89
|
+
{okButtonText}
|
|
90
|
+
</CButton>
|
|
91
|
+
</>
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
const _className = classNames('picker', className)
|
|
95
|
+
|
|
96
|
+
switch (container) {
|
|
97
|
+
case 'inline':
|
|
98
|
+
return <div className={_className}>{children}</div>
|
|
99
|
+
default:
|
|
100
|
+
return (
|
|
101
|
+
<CDropdown
|
|
102
|
+
autoClose="outside"
|
|
103
|
+
className={_className}
|
|
104
|
+
onHide={() => {
|
|
105
|
+
setVisible(false)
|
|
106
|
+
onHide && onHide()
|
|
107
|
+
}}
|
|
108
|
+
onShow={() => {
|
|
109
|
+
setVisible(true)
|
|
110
|
+
onShow && onShow()
|
|
111
|
+
}}
|
|
112
|
+
variant="dropdown"
|
|
113
|
+
visible={_visible}
|
|
114
|
+
{...rest}
|
|
115
|
+
ref={ref}
|
|
116
|
+
>
|
|
117
|
+
<CDropdownToggle custom disabled={_visible || disabled}>
|
|
118
|
+
{Toggler}
|
|
119
|
+
</CDropdownToggle>
|
|
120
|
+
<CDropdownMenu component="div">
|
|
121
|
+
{children}
|
|
122
|
+
{footer && (
|
|
123
|
+
<div className="picker-footer">
|
|
124
|
+
{typeof footer === 'boolean' ? Footer() : footer}
|
|
125
|
+
</div>
|
|
126
|
+
)}
|
|
127
|
+
</CDropdownMenu>
|
|
128
|
+
</CDropdown>
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
CPicker.displayName = 'CPicker'
|
|
135
|
+
|
|
136
|
+
CPicker.propTypes = {
|
|
137
|
+
...CDropdown.propTypes,
|
|
138
|
+
container: PropTypes.oneOf(['dropdown', 'inline']),
|
|
139
|
+
disabled: PropTypes.bool,
|
|
140
|
+
onCancelClick: PropTypes.func,
|
|
141
|
+
onOkClick: PropTypes.func,
|
|
142
|
+
}
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
/* eslint-disable react/prop-types */
|
|
2
|
+
import React, { forwardRef, ReactNode, useEffect, useRef, useState } from 'react'
|
|
3
|
+
import PropTypes from 'prop-types'
|
|
4
|
+
import classNames from 'classnames'
|
|
5
|
+
|
|
6
|
+
import { CFormInput } from '../form/CFormInput'
|
|
7
|
+
import { CFormSelect } from '../form/CFormSelect'
|
|
8
|
+
import { CInputGroup } from '../form/CInputGroup'
|
|
9
|
+
import { CInputGroupText } from '../form/CInputGroupText'
|
|
10
|
+
|
|
11
|
+
import { CTimePickerRollCol } from './CTimePickerRollCol'
|
|
12
|
+
import { CPicker, CPickerProps } from './../picker/CPicker'
|
|
13
|
+
import { convert12hTo24h, convert24hTo12h, getAmPm, isAmPm } from './CTimePicker.utils'
|
|
14
|
+
|
|
15
|
+
export interface CTimePickerProps
|
|
16
|
+
extends Omit<
|
|
17
|
+
CPickerProps,
|
|
18
|
+
'autoClose' | 'component' | 'dark' | 'placeholder' | 'variant' | 'onChange'
|
|
19
|
+
> {
|
|
20
|
+
/**
|
|
21
|
+
* A string of all className you want applied to the component.
|
|
22
|
+
*/
|
|
23
|
+
className?: string
|
|
24
|
+
/**
|
|
25
|
+
* Enables selection cleaner element.
|
|
26
|
+
*/
|
|
27
|
+
cleaner?: boolean
|
|
28
|
+
/**
|
|
29
|
+
* Cleaner icon.
|
|
30
|
+
*/
|
|
31
|
+
cleanerIcon?: ReactNode | boolean
|
|
32
|
+
/**
|
|
33
|
+
* Default input icon.
|
|
34
|
+
*/
|
|
35
|
+
defaultIcon?: ReactNode | boolean
|
|
36
|
+
/**
|
|
37
|
+
* Toggle the readonly state for the component.
|
|
38
|
+
*/
|
|
39
|
+
inputReadOnly?: boolean
|
|
40
|
+
/**
|
|
41
|
+
* Sets the default locale for components. If not set, it is inherited from the navigator.language.
|
|
42
|
+
*/
|
|
43
|
+
locale?: string
|
|
44
|
+
/**
|
|
45
|
+
* Callback fired when the user hovers over the calendar cell.
|
|
46
|
+
*/
|
|
47
|
+
onChange?: (timeString: string, localeTimeString: string, date: Date) => void
|
|
48
|
+
/**
|
|
49
|
+
* Specifies a short hint that is visible in the input.
|
|
50
|
+
*/
|
|
51
|
+
placeholder?: string
|
|
52
|
+
/**
|
|
53
|
+
* Size the component small or large.
|
|
54
|
+
*/
|
|
55
|
+
size?: 'sm' | 'lg'
|
|
56
|
+
/**
|
|
57
|
+
* Default value of the component
|
|
58
|
+
*/
|
|
59
|
+
value?: Date | string | null
|
|
60
|
+
/**
|
|
61
|
+
* Set the time picker variant to a roll or select.
|
|
62
|
+
*/
|
|
63
|
+
variant?: 'roll' | 'select'
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export const CTimePicker = forwardRef<HTMLDivElement | HTMLLIElement, CTimePickerProps>(
|
|
67
|
+
(
|
|
68
|
+
{
|
|
69
|
+
className,
|
|
70
|
+
cleaner = true,
|
|
71
|
+
cleanerIcon,
|
|
72
|
+
container = 'dropdown',
|
|
73
|
+
defaultIcon,
|
|
74
|
+
disabled,
|
|
75
|
+
footer = true,
|
|
76
|
+
inputReadOnly,
|
|
77
|
+
locale = typeof navigator === 'undefined' ? 'en-US' : navigator.language,
|
|
78
|
+
onChange,
|
|
79
|
+
placeholder = 'Select time',
|
|
80
|
+
size,
|
|
81
|
+
value,
|
|
82
|
+
variant = 'roll',
|
|
83
|
+
...rest
|
|
84
|
+
},
|
|
85
|
+
ref,
|
|
86
|
+
) => {
|
|
87
|
+
const inputRef = useRef<HTMLInputElement>(null)
|
|
88
|
+
const [date, setDate] = useState<Date | null>(
|
|
89
|
+
value ? (value instanceof Date ? new Date(value) : new Date(`1970-01-01 ${value}`)) : null,
|
|
90
|
+
)
|
|
91
|
+
const [_ampm, setAmPm] = useState<'am' | 'pm'>(date ? getAmPm(new Date(date), locale) : 'am')
|
|
92
|
+
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
setDate(
|
|
95
|
+
value ? (value instanceof Date ? new Date(value) : new Date(`1970-01-01 ${value}`)) : null,
|
|
96
|
+
)
|
|
97
|
+
}, [value])
|
|
98
|
+
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
if (inputRef.current) {
|
|
101
|
+
inputRef.current.value = date ? date.toLocaleTimeString(locale) : ''
|
|
102
|
+
}
|
|
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
|
+
|
|
127
|
+
const selectedMinutes = date ? date.getMinutes() : null
|
|
128
|
+
|
|
129
|
+
const selectedSeconds = date ? date.getSeconds() : null
|
|
130
|
+
|
|
131
|
+
const handleClear = (event: React.MouseEvent<HTMLElement>) => {
|
|
132
|
+
event.stopPropagation()
|
|
133
|
+
setDate(null)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const handleTimeChange = (set: 'hours' | 'minutes' | 'seconds' | 'toggle', value: string) => {
|
|
137
|
+
const _date = date || new Date('1970-01-01')
|
|
138
|
+
|
|
139
|
+
if (set === 'toggle') {
|
|
140
|
+
if (value === 'am') {
|
|
141
|
+
setAmPm('am')
|
|
142
|
+
_date.setHours(_date.getHours() - 12)
|
|
143
|
+
}
|
|
144
|
+
if (value === 'pm') {
|
|
145
|
+
setAmPm('pm')
|
|
146
|
+
_date.setHours(_date.getHours() + 12)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (set === 'hours') {
|
|
151
|
+
if (isAmPm(locale)) {
|
|
152
|
+
_date.setHours(convert12hTo24h(_ampm, parseInt(value)))
|
|
153
|
+
} else {
|
|
154
|
+
_date.setHours(parseInt(value))
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (set === 'minutes') {
|
|
159
|
+
_date.setMinutes(parseInt(value))
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (set === 'seconds') {
|
|
163
|
+
_date.setSeconds(parseInt(value))
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
setDate(new Date(_date))
|
|
167
|
+
onChange && onChange(_date.toTimeString(), _date.toLocaleTimeString(), _date)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const InputGroup = () => (
|
|
171
|
+
<CInputGroup className="picker-input-group" size={size}>
|
|
172
|
+
<CFormInput
|
|
173
|
+
disabled={disabled}
|
|
174
|
+
placeholder={placeholder}
|
|
175
|
+
readOnly={inputReadOnly}
|
|
176
|
+
ref={inputRef}
|
|
177
|
+
/>
|
|
178
|
+
{(defaultIcon !== false || cleaner) && (
|
|
179
|
+
<CInputGroupText>
|
|
180
|
+
{defaultIcon !== false && (
|
|
181
|
+
<span className="picker-input-group-indicator">
|
|
182
|
+
{defaultIcon ? (
|
|
183
|
+
defaultIcon
|
|
184
|
+
) : (
|
|
185
|
+
<span className="picker-input-group-icon time-picker-input-icon" />
|
|
186
|
+
)}
|
|
187
|
+
</span>
|
|
188
|
+
)}
|
|
189
|
+
{cleaner && date && (
|
|
190
|
+
<span
|
|
191
|
+
className="picker-input-group-cleaner"
|
|
192
|
+
role="button"
|
|
193
|
+
onClick={(event) => handleClear(event)}
|
|
194
|
+
>
|
|
195
|
+
{cleanerIcon ? (
|
|
196
|
+
cleanerIcon
|
|
197
|
+
) : (
|
|
198
|
+
<span className="picker-input-group-icon time-picker-cleaner-icon" />
|
|
199
|
+
)}
|
|
200
|
+
</span>
|
|
201
|
+
)}
|
|
202
|
+
</CInputGroupText>
|
|
203
|
+
)}
|
|
204
|
+
</CInputGroup>
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
const TimePickerSelect = () => {
|
|
208
|
+
return (
|
|
209
|
+
<>
|
|
210
|
+
<span className="time-picker-inline-icon" />
|
|
211
|
+
<CFormSelect
|
|
212
|
+
disabled={disabled}
|
|
213
|
+
options={getHours(locale).map((option) => {
|
|
214
|
+
return {
|
|
215
|
+
value: option.value.toString(),
|
|
216
|
+
label: option.label,
|
|
217
|
+
}
|
|
218
|
+
})}
|
|
219
|
+
onChange={(event: React.ChangeEvent<HTMLSelectElement>) =>
|
|
220
|
+
handleTimeChange('hours', event.target.value)
|
|
221
|
+
}
|
|
222
|
+
value={selectedHour ? selectedHour : '0'}
|
|
223
|
+
/>
|
|
224
|
+
<span className="px-1">:</span>
|
|
225
|
+
<CFormSelect
|
|
226
|
+
disabled={disabled}
|
|
227
|
+
options={Array.from(new Array(60), (_, i) => {
|
|
228
|
+
return {
|
|
229
|
+
value: i.toString(),
|
|
230
|
+
label: i.toLocaleString(locale).padStart(2, (0).toLocaleString(locale)),
|
|
231
|
+
}
|
|
232
|
+
})}
|
|
233
|
+
onChange={(event: React.ChangeEvent<HTMLSelectElement>) =>
|
|
234
|
+
handleTimeChange('minutes', event.target.value)
|
|
235
|
+
}
|
|
236
|
+
value={selectedMinutes ? selectedMinutes : '0'}
|
|
237
|
+
/>
|
|
238
|
+
<span className="px-1">:</span>
|
|
239
|
+
<CFormSelect
|
|
240
|
+
disabled={disabled}
|
|
241
|
+
options={Array.from(new Array(60), (_, i) => {
|
|
242
|
+
return {
|
|
243
|
+
value: i.toString(),
|
|
244
|
+
label: i.toLocaleString(locale).padStart(2, (0).toLocaleString(locale)),
|
|
245
|
+
}
|
|
246
|
+
})}
|
|
247
|
+
onChange={(event: React.ChangeEvent<HTMLSelectElement>) =>
|
|
248
|
+
handleTimeChange('seconds', event.target.value)
|
|
249
|
+
}
|
|
250
|
+
value={selectedSeconds ? selectedSeconds : '0'}
|
|
251
|
+
/>
|
|
252
|
+
{isAmPm(locale) && (
|
|
253
|
+
<CFormSelect
|
|
254
|
+
disabled={disabled}
|
|
255
|
+
options={[
|
|
256
|
+
{ value: 'am', label: 'AM' },
|
|
257
|
+
{ value: 'pm', label: 'PM' },
|
|
258
|
+
]}
|
|
259
|
+
onChange={(event: React.ChangeEvent<HTMLSelectElement>) =>
|
|
260
|
+
handleTimeChange('toggle', event.target.value)
|
|
261
|
+
}
|
|
262
|
+
value={_ampm}
|
|
263
|
+
/>
|
|
264
|
+
)}
|
|
265
|
+
</>
|
|
266
|
+
)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const TimePickerRoll = () => (
|
|
270
|
+
<>
|
|
271
|
+
<CTimePickerRollCol
|
|
272
|
+
elements={getHours(locale)}
|
|
273
|
+
onClick={(index: number) => handleTimeChange('hours', index.toString())}
|
|
274
|
+
selected={selectedHour}
|
|
275
|
+
/>
|
|
276
|
+
<CTimePickerRollCol
|
|
277
|
+
elements={getMinutesOrSeconds(locale)}
|
|
278
|
+
selected={selectedMinutes}
|
|
279
|
+
onClick={(index: number) => handleTimeChange('minutes', index.toString())}
|
|
280
|
+
/>
|
|
281
|
+
<CTimePickerRollCol
|
|
282
|
+
elements={getMinutesOrSeconds(locale)}
|
|
283
|
+
selected={selectedSeconds}
|
|
284
|
+
onClick={(index: number) => handleTimeChange('seconds', index.toString())}
|
|
285
|
+
/>
|
|
286
|
+
{isAmPm(locale) && (
|
|
287
|
+
<CTimePickerRollCol
|
|
288
|
+
elements={[
|
|
289
|
+
{ value: 'am', label: 'AM' },
|
|
290
|
+
{ value: 'pm', label: 'PM' },
|
|
291
|
+
]}
|
|
292
|
+
selected={_ampm}
|
|
293
|
+
onClick={(value: string) => handleTimeChange('toggle', value)}
|
|
294
|
+
/>
|
|
295
|
+
)}
|
|
296
|
+
</>
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
const _className = classNames('time-picker', className)
|
|
300
|
+
|
|
301
|
+
return (
|
|
302
|
+
<CPicker
|
|
303
|
+
className={_className}
|
|
304
|
+
container={container}
|
|
305
|
+
disabled={disabled}
|
|
306
|
+
footer={footer}
|
|
307
|
+
onCancelClick={() => {
|
|
308
|
+
setDate(
|
|
309
|
+
value
|
|
310
|
+
? value instanceof Date
|
|
311
|
+
? new Date(value)
|
|
312
|
+
: new Date(`1970-01-01 ${value}`)
|
|
313
|
+
: new Date(),
|
|
314
|
+
)
|
|
315
|
+
}}
|
|
316
|
+
toggler={InputGroup()}
|
|
317
|
+
{...rest}
|
|
318
|
+
ref={ref}
|
|
319
|
+
>
|
|
320
|
+
<div
|
|
321
|
+
className={classNames('time-picker-body', {
|
|
322
|
+
['time-picker-roll']: variant === 'roll',
|
|
323
|
+
})}
|
|
324
|
+
>
|
|
325
|
+
{variant === 'select' ? <TimePickerSelect /> : TimePickerRoll()}
|
|
326
|
+
</div>
|
|
327
|
+
</CPicker>
|
|
328
|
+
)
|
|
329
|
+
},
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
CTimePicker.propTypes = {
|
|
333
|
+
...CPicker.propTypes,
|
|
334
|
+
className: PropTypes.string,
|
|
335
|
+
locale: PropTypes.string,
|
|
336
|
+
onChange: PropTypes.func,
|
|
337
|
+
value: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.string]),
|
|
338
|
+
variant: PropTypes.oneOf(['roll', 'select']),
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
CTimePicker.displayName = 'CTimePicker'
|
|
@@ -0,0 +1,27 @@
|
|
|
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 getAmPm = (date: Date, locale: string) => {
|
|
17
|
+
if (date.toLocaleTimeString(locale).includes('AM')) {
|
|
18
|
+
return 'am'
|
|
19
|
+
}
|
|
20
|
+
if (date.toLocaleTimeString(locale).includes('PM')) {
|
|
21
|
+
return 'pm'
|
|
22
|
+
}
|
|
23
|
+
return date.getHours() >= 12 ? 'pm' : 'am'
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const isAmPm = (locale: string) =>
|
|
27
|
+
['am', 'AM', 'pm', 'PM'].some((el) => new Date().toLocaleString(locale).includes(el))
|