@coreui/react-pro 4.12.0-beta.1 → 4.12.0-beta.3

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 (40) hide show
  1. package/README.md +1 -1
  2. package/dist/components/dropdown/CDropdown.d.ts +8 -1
  3. package/dist/components/dropdown/CDropdownMenu.d.ts +3 -4
  4. package/dist/components/multi-select/CMultiSelect.d.ts +2 -2
  5. package/dist/components/popover/CPopover.d.ts +22 -1
  6. package/dist/components/progress/CProgress.d.ts +6 -0
  7. package/dist/components/progress/CProgressStacked.d.ts +12 -0
  8. package/dist/components/progress/index.d.ts +2 -1
  9. package/dist/components/tooltip/CTooltip.d.ts +22 -1
  10. package/dist/hooks/index.d.ts +2 -1
  11. package/dist/hooks/useColorModes.d.ts +3 -2
  12. package/dist/hooks/usePopper.d.ts +8 -0
  13. package/dist/index.es.js +6682 -7089
  14. package/dist/index.es.js.map +1 -1
  15. package/dist/index.js +6300 -6723
  16. package/dist/index.js.map +1 -1
  17. package/dist/props.d.ts +1 -0
  18. package/dist/utils/getRTLPlacement.d.ts +3 -0
  19. package/dist/utils/index.d.ts +2 -1
  20. package/package.json +5 -5
  21. package/src/components/date-range-picker/CDateRangePicker.tsx +10 -7
  22. package/src/components/dropdown/CDropdown.tsx +130 -45
  23. package/src/components/dropdown/CDropdownMenu.tsx +41 -138
  24. package/src/components/dropdown/CDropdownToggle.tsx +6 -15
  25. package/src/components/dropdown/__tests__/__snapshots__/CDropdownMenu.spec.tsx.snap +0 -1
  26. package/src/components/multi-select/CMultiSelect.tsx +30 -39
  27. package/src/components/picker/CPicker.tsx +24 -34
  28. package/src/components/popover/CPopover.tsx +76 -51
  29. package/src/components/popover/__tests__/__snapshots__/CPopover.spec.tsx.snap +0 -21
  30. package/src/components/progress/CProgress.tsx +43 -8
  31. package/src/components/progress/CProgressBar.tsx +5 -6
  32. package/src/components/progress/CProgressStacked.tsx +39 -0
  33. package/src/components/progress/index.ts +2 -1
  34. package/src/components/tooltip/CTooltip.tsx +77 -52
  35. package/src/hooks/index.ts +2 -1
  36. package/src/hooks/useColorModes.ts +21 -11
  37. package/src/hooks/usePopper.ts +31 -0
  38. package/src/props.ts +5 -0
  39. package/src/utils/getRTLPlacement.ts +18 -0
  40. package/src/utils/index.ts +2 -1
@@ -3,13 +3,19 @@ import { createPortal } from 'react-dom'
3
3
  import classNames from 'classnames'
4
4
  import PropTypes from 'prop-types'
5
5
  import { Transition } from 'react-transition-group'
6
- import { createPopper, Instance, Placement } from '@popperjs/core'
7
6
 
8
- import { triggerPropType } from '../../props'
9
- import type { Triggers } from '../../types'
10
- import { isRTL } from '../../utils'
7
+ import { usePopper } from '../../hooks'
8
+ import { fallbackPlacementsPropType, triggerPropType } from '../../props'
9
+ import type { Placements, Triggers } from '../../types'
10
+ import { getRTLPlacement } from '../../utils'
11
11
 
12
12
  export interface CTooltipProps extends Omit<HTMLAttributes<HTMLDivElement>, 'content'> {
13
+ /**
14
+ * Apply a CSS fade transition to the tooltip.
15
+ *
16
+ * @since 4.9.0-beta.1
17
+ */
18
+ animation?: boolean
13
19
  /**
14
20
  * A string of all className you want applied to the component.
15
21
  */
@@ -18,6 +24,18 @@ export interface CTooltipProps extends Omit<HTMLAttributes<HTMLDivElement>, 'con
18
24
  * Content node for your component.
19
25
  */
20
26
  content: ReactNode | string
27
+ /**
28
+ * The delay for displaying and hiding the tooltip (in milliseconds). When a numerical value is provided, the delay applies to both the hide and show actions. The object structure for specifying the delay is as follows: delay: `{ 'show': 500, 'hide': 100 }`.
29
+ *
30
+ * @since 4.9.0-beta.1
31
+ */
32
+ delay?: number | { show: number; hide: number }
33
+ /**
34
+ * Specify the desired order of fallback placements by providing a list of placements as an array. The placements should be prioritized based on preference.
35
+ *
36
+ * @since 4.9.0-beta.1
37
+ */
38
+ fallbackPlacements?: Placements | Placements[]
21
39
  /**
22
40
  * Offset of the tooltip relative to its target.
23
41
  */
@@ -46,44 +64,59 @@ export interface CTooltipProps extends Omit<HTMLAttributes<HTMLDivElement>, 'con
46
64
  visible?: boolean
47
65
  }
48
66
 
49
- const getPlacement = (placement: string, element: HTMLDivElement | null): Placement => {
50
- switch (placement) {
51
- case 'right': {
52
- return isRTL(element) ? 'left' : 'right'
53
- }
54
- case 'left': {
55
- return isRTL(element) ? 'right' : 'left'
56
- }
57
- default: {
58
- return placement as Placement
59
- }
60
- }
61
- }
62
-
63
67
  export const CTooltip: FC<CTooltipProps> = ({
64
68
  children,
69
+ animation = true,
65
70
  className,
66
71
  content,
72
+ delay = 0,
73
+ fallbackPlacements = ['top', 'right', 'bottom', 'left'],
67
74
  offset = [0, 6],
68
75
  onHide,
69
76
  onShow,
70
77
  placement = 'top',
71
- trigger = 'hover',
78
+ trigger = ['hover', 'focus'],
72
79
  visible,
73
80
  ...rest
74
81
  }) => {
75
82
  const tooltipRef = useRef(null)
76
83
  const togglerRef = useRef(null)
77
- const popper = useRef<Instance>()
84
+ const { initPopper, destroyPopper } = usePopper()
78
85
  const [_visible, setVisible] = useState(visible)
79
86
 
87
+ const _delay = typeof delay === 'number' ? { show: delay, hide: delay } : delay
88
+
89
+ const popperConfig = {
90
+ modifiers: [
91
+ {
92
+ name: 'arrow',
93
+ options: {
94
+ element: '.tooltip-arrow',
95
+ },
96
+ },
97
+ {
98
+ name: 'flip',
99
+ options: {
100
+ fallbackPlacements: fallbackPlacements,
101
+ },
102
+ },
103
+ {
104
+ name: 'offset',
105
+ options: {
106
+ offset: offset,
107
+ },
108
+ },
109
+ ],
110
+ placement: getRTLPlacement(placement, togglerRef.current),
111
+ }
112
+
80
113
  useEffect(() => {
81
114
  setVisible(visible)
82
115
  }, [visible])
83
116
 
84
117
  useEffect(() => {
85
- if (_visible) {
86
- initPopper()
118
+ if (_visible && togglerRef.current && tooltipRef.current) {
119
+ initPopper(togglerRef.current, tooltipRef.current, popperConfig)
87
120
  }
88
121
 
89
122
  return () => {
@@ -91,28 +124,13 @@ export const CTooltip: FC<CTooltipProps> = ({
91
124
  }
92
125
  }, [_visible])
93
126
 
94
- const initPopper = () => {
95
- if (togglerRef.current && tooltipRef.current) {
96
- popper.current = createPopper(togglerRef.current, tooltipRef.current, {
97
- modifiers: [
98
- {
99
- name: 'offset',
100
- options: {
101
- offset: offset,
102
- },
103
- },
104
- ],
105
- placement: getPlacement(placement, togglerRef.current),
106
- })
107
- }
108
- }
109
-
110
- const destroyPopper = () => {
111
- if (popper.current) {
112
- popper.current.destroy()
127
+ const toggleVisible = (visible: boolean) => {
128
+ if (visible) {
129
+ setTimeout(() => setVisible(true), _delay.show)
130
+ return
113
131
  }
114
132
 
115
- popper.current = undefined
133
+ setTimeout(() => setVisible(false), _delay.hide)
116
134
  }
117
135
 
118
136
  return (
@@ -120,15 +138,15 @@ export const CTooltip: FC<CTooltipProps> = ({
120
138
  {React.cloneElement(children as React.ReactElement<any>, {
121
139
  ref: togglerRef,
122
140
  ...((trigger === 'click' || trigger.includes('click')) && {
123
- onClick: () => setVisible(!_visible),
141
+ onClick: () => toggleVisible(!_visible),
124
142
  }),
125
143
  ...((trigger === 'focus' || trigger.includes('focus')) && {
126
- onFocus: () => setVisible(true),
127
- onBlur: () => setVisible(false),
144
+ onFocus: () => toggleVisible(true),
145
+ onBlur: () => toggleVisible(false),
128
146
  }),
129
147
  ...((trigger === 'hover' || trigger.includes('hover')) && {
130
- onMouseEnter: () => setVisible(true),
131
- onMouseLeave: () => setVisible(false),
148
+ onMouseEnter: () => toggleVisible(true),
149
+ onMouseLeave: () => toggleVisible(false),
132
150
  }),
133
151
  })}
134
152
  {typeof window !== 'undefined' &&
@@ -148,11 +166,9 @@ export const CTooltip: FC<CTooltipProps> = ({
148
166
  <div
149
167
  className={classNames(
150
168
  'tooltip',
151
- `bs-tooltip-${getPlacement(placement, togglerRef.current)
152
- .replace('left', 'start')
153
- .replace('right', 'end')}`,
154
- 'fade',
169
+ 'bs-tooltip-auto',
155
170
  {
171
+ fade: animation,
156
172
  show: state === 'entered',
157
173
  },
158
174
  className,
@@ -161,7 +177,7 @@ export const CTooltip: FC<CTooltipProps> = ({
161
177
  role="tooltip"
162
178
  {...rest}
163
179
  >
164
- <div data-popper-arrow className="tooltip-arrow"></div>
180
+ <div className="tooltip-arrow"></div>
165
181
  <div className="tooltip-inner">{content}</div>
166
182
  </div>
167
183
  )}
@@ -173,8 +189,17 @@ export const CTooltip: FC<CTooltipProps> = ({
173
189
  }
174
190
 
175
191
  CTooltip.propTypes = {
192
+ animation: PropTypes.bool,
176
193
  children: PropTypes.node,
177
194
  content: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
195
+ delay: PropTypes.oneOfType([
196
+ PropTypes.number,
197
+ PropTypes.shape({
198
+ show: PropTypes.number.isRequired,
199
+ hide: PropTypes.number.isRequired,
200
+ }),
201
+ ]),
202
+ fallbackPlacements: fallbackPlacementsPropType,
178
203
  offset: PropTypes.any, // TODO: find good proptype
179
204
  onHide: PropTypes.func,
180
205
  onShow: PropTypes.func,
@@ -1,5 +1,6 @@
1
1
  import { useColorModes } from './useColorModes'
2
2
  import { useForkedRef } from './useForkedRef'
3
+ import { usePopper } from './usePopper'
3
4
  import { useStateWithCallback } from './useStateWithCallback'
4
5
 
5
- export { useColorModes, useForkedRef, useStateWithCallback }
6
+ export { useColorModes, useForkedRef, usePopper, useStateWithCallback }
@@ -1,16 +1,22 @@
1
- import { useEffect, useState } from 'react'
1
+ import { Dispatch, SetStateAction, useEffect, useState } from 'react'
2
2
 
3
3
  interface UseColorModesOutput {
4
- getColorMode: () => string
4
+ colorMode: string | undefined
5
5
  isColorModeSet: () => boolean
6
- setColorMode: (mode: string) => void
6
+ setColorMode: Dispatch<SetStateAction<string>>
7
7
  }
8
8
 
9
- const getStoredTheme = (localStorageItemName: string) => localStorage.getItem(localStorageItemName)
9
+ const getStoredTheme = (localStorageItemName: string) =>
10
+ typeof window !== 'undefined' && localStorage.getItem(localStorageItemName)
11
+
10
12
  const setStoredTheme = (localStorageItemName: string, colorMode: string) =>
11
13
  localStorage.setItem(localStorageItemName, colorMode)
12
14
 
13
15
  const getPreferredColorScheme = (localStorageItemName: string) => {
16
+ if (typeof window === 'undefined') {
17
+ return 'light'
18
+ }
19
+
14
20
  const storedTheme = getStoredTheme(localStorageItemName)
15
21
 
16
22
  if (storedTheme) {
@@ -33,25 +39,29 @@ const setTheme = (colorMode: string) => {
33
39
  export const useColorModes = (
34
40
  localStorageItemName = 'coreui-react-color-scheme',
35
41
  ): UseColorModesOutput => {
36
- const [colorMode, setColorMode] = useState<string>(getPreferredColorScheme(localStorageItemName))
42
+ const [colorMode, setColorMode] = useState<string | undefined>(
43
+ getPreferredColorScheme(localStorageItemName),
44
+ )
37
45
 
38
46
  useEffect(() => {
39
- setStoredTheme(localStorageItemName, colorMode)
40
- setTheme(colorMode)
47
+ if (colorMode) {
48
+ setStoredTheme(localStorageItemName, colorMode)
49
+ setTheme(colorMode)
50
+ }
41
51
  }, [colorMode])
42
52
 
43
53
  useEffect(() => {
44
54
  window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
45
55
  const storedTheme = getStoredTheme(localStorageItemName)
46
- if (storedTheme !== 'light' && storedTheme !== 'dark') {
56
+ if (storedTheme !== 'light' && storedTheme !== 'dark' && colorMode) {
47
57
  setTheme(colorMode)
48
58
  }
49
59
  })
50
- }, [])
60
+ })
51
61
 
52
62
  return {
53
- getColorMode: () => colorMode,
63
+ colorMode,
54
64
  isColorModeSet: () => Boolean(getStoredTheme(localStorageItemName)),
55
- setColorMode: (mode: string) => setColorMode(mode),
65
+ setColorMode,
56
66
  }
57
67
  }
@@ -0,0 +1,31 @@
1
+ import { useRef } from 'react'
2
+ import { createPopper } from '@popperjs/core'
3
+ import type { Instance, Options } from '@popperjs/core'
4
+
5
+ interface UsePopperOutput {
6
+ popper: Instance | undefined
7
+ initPopper: (reference: HTMLElement, popper: HTMLElement, options: Partial<Options>) => void
8
+ destroyPopper: () => void
9
+ }
10
+
11
+ export const usePopper = (): UsePopperOutput => {
12
+ const _popper = useRef<Instance>()
13
+
14
+ const initPopper = (reference: HTMLElement, popper: HTMLElement, options: Partial<Options>) => {
15
+ _popper.current = createPopper(reference, popper, options)
16
+ }
17
+
18
+ const destroyPopper = () => {
19
+ if (_popper.current) {
20
+ _popper.current.destroy()
21
+ }
22
+
23
+ _popper.current = undefined
24
+ }
25
+
26
+ return {
27
+ popper: _popper.current,
28
+ initPopper,
29
+ destroyPopper,
30
+ }
31
+ }
package/src/props.ts CHANGED
@@ -16,6 +16,11 @@ export const colorPropType = PropTypes.oneOfType([
16
16
  PropTypes.string,
17
17
  ])
18
18
 
19
+ export const fallbackPlacementsPropType = PropTypes.oneOfType([
20
+ PropTypes.arrayOf(PropTypes.oneOf<Placements>(['top', 'bottom', 'right', 'left']).isRequired),
21
+ PropTypes.oneOf<Placements>(['top', 'bottom', 'right', 'left']),
22
+ ])
23
+
19
24
  export const gradientsPropType = PropTypes.oneOf([
20
25
  'primary-gradient',
21
26
  'secondary-gradient',
@@ -0,0 +1,18 @@
1
+ import { Placement } from '@popperjs/core'
2
+ import { isRTL } from '../utils'
3
+
4
+ const getRTLPlacement = (placement: string, element: HTMLDivElement | null): Placement => {
5
+ switch (placement) {
6
+ case 'right': {
7
+ return isRTL(element) ? 'left' : 'right'
8
+ }
9
+ case 'left': {
10
+ return isRTL(element) ? 'right' : 'left'
11
+ }
12
+ default: {
13
+ return placement as Placement
14
+ }
15
+ }
16
+ }
17
+
18
+ export default getRTLPlacement
@@ -1,5 +1,6 @@
1
+ import getRTLPlacement from './getRTLPlacement'
1
2
  import isInViewport from './isInViewport'
2
3
  import isObjectInArray from './isObjectInArray'
3
4
  import isRTL from './isRTL'
4
5
 
5
- export { isInViewport, isObjectInArray, isRTL }
6
+ export { getRTLPlacement, isInViewport, isObjectInArray, isRTL }