@coreui/react-pro 4.1.3 → 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.
Files changed (48) hide show
  1. package/README.md +1 -1
  2. package/dist/components/calendar/CCalendar.d.ts +106 -0
  3. package/dist/components/calendar/CCalendar.utils.d.ts +21 -0
  4. package/dist/components/datepicker/CDatePicker.d.ts +13 -0
  5. package/dist/components/daterangepicker/CDateRangePicker.d.ts +65 -0
  6. package/dist/components/dropdown/CDropdown.d.ts +10 -1
  7. package/dist/components/dropdown/CDropdownToggle.d.ts +4 -0
  8. package/dist/components/dropdown/CDropdownToggleCustom.d.ts +15 -0
  9. package/dist/components/picker/CPicker.d.ts +78 -0
  10. package/dist/components/smart-table/CSmartTableInterface.d.ts +0 -9
  11. package/dist/components/timepicker/CTimePicker.d.ts +45 -0
  12. package/dist/components/timepicker/CTimePicker.utils.d.ts +17 -0
  13. package/dist/components/timepicker/CTimePickerRollCol.d.ts +11 -0
  14. package/dist/index.d.ts +5 -1
  15. package/dist/index.es.js +2568 -718
  16. package/dist/index.es.js.map +1 -1
  17. package/dist/index.js +2849 -995
  18. package/dist/index.js.map +1 -1
  19. package/dist/utils/calendar.d.ts +21 -0
  20. package/dist/utils/time.d.ts +17 -0
  21. package/package.json +11 -8
  22. package/src/components/accordion/CAccordionButton.tsx +2 -1
  23. package/src/components/accordion/__tests__/__snapshots__/CAccordionButton.spec.tsx.snap +2 -0
  24. package/src/components/accordion/__tests__/__snapshots__/CAccordionHeader.spec.tsx.snap +2 -0
  25. package/src/components/calendar/CCalendar.tsx +528 -0
  26. package/src/components/calendar/__tests__/CCalendar.spec.tsx +9 -0
  27. package/src/components/calendar/__tests__/__snapshots__/CCalendar.spec.tsx.snap +573 -0
  28. package/src/components/datepicker/CDatePicker.tsx +50 -0
  29. package/src/components/datepicker/__tests__/CDatePicker.spec.tsx +9 -0
  30. package/src/components/datepicker/__tests__/__snapshots__/CDatePicker.spec.tsx.snap +617 -0
  31. package/src/components/daterangepicker/CDateRangePicker.tsx +427 -0
  32. package/src/components/daterangepicker/__tests__/CDateRangePicker.spec.tsx +9 -0
  33. package/src/components/daterangepicker/__tests__/__snapshots__/CDateRangePicker.spec.tsx.snap +1201 -0
  34. package/src/components/dropdown/CDropdown.tsx +19 -24
  35. package/src/components/dropdown/CDropdownMenu.tsx +57 -3
  36. package/src/components/dropdown/CDropdownToggle.tsx +28 -5
  37. package/src/components/picker/CPicker.tsx +207 -0
  38. package/src/components/picker/__tests__/CPicker.spec.tsx +9 -0
  39. package/src/components/picker/__tests__/__snapshots__/CPicker.spec.tsx.snap +23 -0
  40. package/src/components/smart-table/CSmartTable.tsx +44 -96
  41. package/src/components/smart-table/CSmartTableCleaner.tsx +3 -16
  42. package/src/components/smart-table/CSmartTableInterface.tsx +0 -9
  43. package/src/components/timepicker/CTimePicker.tsx +320 -0
  44. package/src/components/timepicker/CTimePickerRollCol.tsx +63 -0
  45. package/src/components/timepicker/__tests__/CTimePicker.spec.tsx +9 -0
  46. package/src/index.ts +8 -0
  47. package/src/utils/calendar.ts +193 -0
  48. package/src/utils/time.ts +58 -0
@@ -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,
@@ -1,5 +1,5 @@
1
+ import React, { ElementType, FC, HTMLAttributes, useContext, useEffect, useRef } from 'react'
1
2
  import PropTypes from 'prop-types'
2
- import React, { ElementType, FC, HTMLAttributes, useContext } from 'react'
3
3
  import classNames from 'classnames'
4
4
  import { Popper, PopperChildrenProps } from 'react-popper'
5
5
 
@@ -35,7 +35,59 @@ export const CDropdownMenu: FC<CDropdownMenuProps> = ({
35
35
  component: Component = 'ul',
36
36
  ...rest
37
37
  }) => {
38
- const { alignment, dark, direction, placement, popper, visible } = useContext(CDropdownContext)
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: KeyboardEvent) => {
63
+ if (autoClose === false) {
64
+ return
65
+ }
66
+
67
+ if (event.key === 'Escape') {
68
+ setVisible(false)
69
+ }
70
+ }
71
+
72
+ const handleMouseUp = (event: Event) => {
73
+ if (dropdownToggleRef && dropdownToggleRef.current.contains(event.target as HTMLElement)) {
74
+ return
75
+ }
76
+ if (autoClose === true) {
77
+ setVisible(false)
78
+ return
79
+ }
80
+ if (autoClose === 'inside' && dropdownMenuRef.current?.contains(event.target as HTMLElement)) {
81
+ setVisible(false)
82
+ return
83
+ }
84
+ if (
85
+ autoClose === 'outside' &&
86
+ !dropdownMenuRef.current?.contains(event.target as HTMLElement)
87
+ ) {
88
+ setVisible(false)
89
+ }
90
+ }
39
91
 
40
92
  let _placement: Placements = placement
41
93
 
@@ -101,7 +153,9 @@ export const CDropdownMenu: FC<CDropdownMenuProps> = ({
101
153
  }
102
154
 
103
155
  return popper && visible ? (
104
- <Popper placement={_placement}>{({ ref, style }) => dropdownMenuComponent(style, ref)}</Popper>
156
+ <Popper innerRef={dropdownMenuRef} placement={_placement}>
157
+ {({ ref, style }) => dropdownMenuComponent(style, ref)}
158
+ </Popper>
105
159
  ) : (
106
160
  dropdownMenuComponent()
107
161
  )
@@ -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 variant === 'nav-item' ? (
69
- <a href="#" {...togglerProps} ref={ref}>
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 type="button" {...togglerProps} tabIndex={0} {...rest} ref={ref}>
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,207 @@
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
+ import { Colors } from '../Types'
11
+
12
+ export interface CPickerProps extends Omit<CDropdownProps, 'variant'> {
13
+ /**
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'
42
+ */
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'
61
+ /**
62
+ * Set container type for the component.
63
+ */
64
+ container?: 'dropdown' | 'inline'
65
+ /**
66
+ * Toggle the disabled state for the component.
67
+ */
68
+ disabled?: boolean
69
+ /**
70
+ * Toggle visibility of footer element or set the content of footer.
71
+ */
72
+ footer?: boolean | ReactNode
73
+ /**
74
+ * Callback fired when the cancel button has been clicked.
75
+ */
76
+ onCancel?: () => void
77
+ /**
78
+ * Callback fired when the confirm button has been clicked.
79
+ */
80
+ onConfirm?: () => void
81
+ /**
82
+ * The content of toggler.
83
+ */
84
+ toggler?: ReactNode
85
+ }
86
+
87
+ export const CPicker = forwardRef<HTMLDivElement | HTMLLIElement, CPickerProps>(
88
+ (
89
+ {
90
+ children,
91
+ cancelButton = 'Cancel',
92
+ cancelButtonColor = 'primary',
93
+ cancelButtonSize = 'sm',
94
+ cancelButtonVariant = 'ghost',
95
+ confirmButton = 'OK',
96
+ confirmButtonColor = 'primary',
97
+ confirmButtonSize = 'sm',
98
+ confirmButtonVariant,
99
+ className,
100
+ container = 'dropdown',
101
+ disabled,
102
+ footer,
103
+ onCancel,
104
+ onConfirm,
105
+ onHide,
106
+ onShow,
107
+ toggler: Toggler,
108
+ visible,
109
+ ...rest
110
+ },
111
+ ref,
112
+ ) => {
113
+ const [_visible, setVisible] = useState(visible)
114
+
115
+ useEffect(() => {
116
+ setVisible(visible)
117
+ }, [visible])
118
+
119
+ const Footer = () => (
120
+ <>
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
+ )}
144
+ </>
145
+ )
146
+
147
+ const _className = classNames('picker', className)
148
+
149
+ switch (container) {
150
+ case 'inline':
151
+ return <div className={_className}>{children}</div>
152
+ default:
153
+ return (
154
+ <CDropdown
155
+ autoClose="outside"
156
+ className={_className}
157
+ onHide={() => {
158
+ setVisible(false)
159
+ onHide && onHide()
160
+ }}
161
+ onShow={() => {
162
+ setVisible(true)
163
+ onShow && onShow()
164
+ }}
165
+ variant="dropdown"
166
+ visible={_visible}
167
+ {...rest}
168
+ ref={ref}
169
+ >
170
+ <CDropdownToggle custom disabled={_visible || disabled}>
171
+ {Toggler}
172
+ </CDropdownToggle>
173
+ <CDropdownMenu component="div">
174
+ {children}
175
+ {footer && (
176
+ <div className="picker-footer">
177
+ {typeof footer === 'boolean' ? Footer() : footer}
178
+ </div>
179
+ )}
180
+ </CDropdownMenu>
181
+ </CDropdown>
182
+ )
183
+ }
184
+ },
185
+ )
186
+
187
+ CPicker.displayName = 'CPicker'
188
+
189
+ CPicker.propTypes = {
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,
200
+ container: PropTypes.oneOf(['dropdown', 'inline']),
201
+ disabled: PropTypes.bool,
202
+ footer: PropTypes.oneOfType([PropTypes.bool, PropTypes.node]),
203
+ onCancel: PropTypes.func,
204
+ onConfirm: PropTypes.func,
205
+ toggler: PropTypes.node,
206
+ ...CDropdown.propTypes,
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
+ `;