@coreui/react 4.9.0-alpha.0 → 4.9.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.
Files changed (35) hide show
  1. package/README.md +1 -1
  2. package/dist/components/accordion/index.d.ts +1 -1
  3. package/dist/components/tooltip/CTooltip.d.ts +2 -2
  4. package/dist/hooks/index.d.ts +2 -1
  5. package/dist/hooks/useColorModes.d.ts +6 -0
  6. package/dist/index.d.ts +1 -0
  7. package/dist/index.es.js +184 -243
  8. package/dist/index.es.js.map +1 -1
  9. package/dist/index.js +185 -242
  10. package/dist/index.js.map +1 -1
  11. package/dist/props.d.ts +1 -1
  12. package/dist/utils/index.d.ts +2 -1
  13. package/dist/utils/isRTL.d.ts +2 -0
  14. package/package.json +9 -9
  15. package/src/components/accordion/index.ts +1 -7
  16. package/src/components/dropdown/CDropdownMenu.tsx +5 -4
  17. package/src/components/form/CFormSelect.tsx +3 -1
  18. package/src/components/modal/CModal.tsx +2 -0
  19. package/src/components/popover/CPopover.tsx +60 -25
  20. package/src/components/popover/__tests__/__snapshots__/CPopover.spec.tsx.snap +2 -1
  21. package/src/components/progress/CProgress.tsx +1 -0
  22. package/src/components/sidebar/CSidebar.tsx +2 -2
  23. package/src/components/spinner/CSpinner.tsx +4 -2
  24. package/src/components/spinner/__tests__/__snapshots__/CSpinner.spec.tsx.snap +2 -2
  25. package/src/components/table/CTable.tsx +2 -1
  26. package/src/components/tooltip/CTooltip.tsx +64 -30
  27. package/src/components/tooltip/__tests__/CTooltip.spec.tsx +1 -1
  28. package/src/hooks/index.ts +2 -1
  29. package/src/hooks/useColorModes.ts +55 -0
  30. package/src/hooks/useForkedRef.ts +1 -1
  31. package/src/index.ts +1 -0
  32. package/src/props.ts +4 -1
  33. package/src/utils/index.ts +2 -1
  34. package/src/utils/isRTL.ts +13 -0
  35. package/src/components/accordion/__tests__/__snapshots__/CAccordionCollapse.spec.tsx.snap +0 -11
package/dist/props.d.ts CHANGED
@@ -4,4 +4,4 @@ export declare const colorPropType: PropTypes.Requireable<string>;
4
4
  export declare const placementPropType: PropTypes.Requireable<Placements>;
5
5
  export declare const shapePropType: PropTypes.Requireable<string>;
6
6
  export declare const textColorsPropType: PropTypes.Requireable<string>;
7
- export declare const triggerPropType: PropTypes.Requireable<Triggers>;
7
+ export declare const triggerPropType: PropTypes.Requireable<NonNullable<Triggers | NonNullable<Triggers>[] | null | undefined>>;
@@ -1,2 +1,3 @@
1
1
  import isInViewport from './isInViewport';
2
- export { isInViewport };
2
+ import isRTL from './isRTL';
3
+ export { isInViewport, isRTL };
@@ -0,0 +1,2 @@
1
+ declare const isRTL: (element?: HTMLElement | HTMLDivElement | null) => boolean;
2
+ export default isRTL;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coreui/react",
3
- "version": "4.9.0-alpha.0",
3
+ "version": "4.9.0-beta.0",
4
4
  "description": "UI Components Library for React.js",
5
5
  "keywords": [
6
6
  "react",
@@ -37,13 +37,13 @@
37
37
  "test:update": "jest --coverage --updateSnapshot"
38
38
  },
39
39
  "devDependencies": {
40
- "@popperjs/core": "^2.11.7",
41
- "@rollup/plugin-commonjs": "^24.1.0",
42
- "@rollup/plugin-node-resolve": "^15.0.2",
43
- "@rollup/plugin-typescript": "^11.1.0",
40
+ "@popperjs/core": "^2.11.8",
41
+ "@rollup/plugin-commonjs": "^25.0.1",
42
+ "@rollup/plugin-node-resolve": "^15.1.0",
43
+ "@rollup/plugin-typescript": "^11.1.1",
44
44
  "@testing-library/jest-dom": "^5.16.5",
45
45
  "@testing-library/react": "^14.0.0",
46
- "@types/react": "18.2.6",
46
+ "@types/react": "18.2.11",
47
47
  "@types/react-dom": "^18.2.4",
48
48
  "@types/react-transition-group": "^4.4.6",
49
49
  "classnames": "^2.3.2",
@@ -54,13 +54,13 @@
54
54
  "react-dom": "^18.2.0",
55
55
  "react-popper": "^2.3.0",
56
56
  "react-transition-group": "^4.4.5",
57
- "rollup": "^3.21.6",
58
- "tslib": "^2.5.0",
57
+ "rollup": "^3.25.0",
58
+ "tslib": "^2.5.3",
59
59
  "ts-jest": "^29.1.0",
60
60
  "typescript": "^4.9.5"
61
61
  },
62
62
  "peerDependencies": {
63
- "@coreui/coreui": "4.3.0-alpha.0",
63
+ "@coreui/coreui": "4.3.0-beta.0",
64
64
  "react": ">=17",
65
65
  "react-dom": ">=17"
66
66
  }
@@ -4,10 +4,4 @@ import { CAccordionButton } from './CAccordionButton'
4
4
  import { CAccordionHeader } from './CAccordionHeader'
5
5
  import { CAccordionItem } from './CAccordionItem'
6
6
 
7
- export {
8
- CAccordion,
9
- CAccordionBody,
10
- CAccordionButton,
11
- CAccordionHeader,
12
- CAccordionItem,
13
- }
7
+ export { CAccordion, CAccordionBody, CAccordionButton, CAccordionHeader, CAccordionItem }
@@ -7,6 +7,7 @@ import { Alignments, CDropdownContext } from './CDropdown'
7
7
  import { CConditionalPortal } from '../conditional-portal'
8
8
 
9
9
  import type { Placements } from '../../types'
10
+ import { isRTL } from '../../utils'
10
11
 
11
12
  export interface CDropdownMenuProps
12
13
  extends HTMLAttributes<HTMLDivElement | HTMLUListElement>,
@@ -109,7 +110,7 @@ export const CDropdownMenu: FC<CDropdownMenuProps> = ({
109
110
  }
110
111
 
111
112
  if (direction === 'dropup') {
112
- _placement = 'top-start'
113
+ _placement = isRTL(dropdownMenuRef.current) ? 'top-end' : 'top-start'
113
114
  }
114
115
 
115
116
  if (direction === 'dropup-center') {
@@ -117,15 +118,15 @@ export const CDropdownMenu: FC<CDropdownMenuProps> = ({
117
118
  }
118
119
 
119
120
  if (direction === 'dropend') {
120
- _placement = 'right-start'
121
+ _placement = isRTL(dropdownMenuRef.current) ? 'left-start' : 'right-start'
121
122
  }
122
123
 
123
124
  if (direction === 'dropstart') {
124
- _placement = 'left-start'
125
+ _placement = isRTL(dropdownMenuRef.current) ? 'right-start' : 'left-start'
125
126
  }
126
127
 
127
128
  if (alignment === 'end') {
128
- _placement = 'bottom-end'
129
+ _placement = isRTL(dropdownMenuRef.current) ? 'bottom-start' : 'bottom-end'
129
130
  }
130
131
 
131
132
  const dropdownMenuComponent = (style?: React.CSSProperties, ref?: React.Ref<HTMLDivElement>) => (
@@ -9,6 +9,7 @@ type Option = {
9
9
  label?: string
10
10
  value?: string
11
11
  }
12
+
12
13
  export interface CFormSelectProps
13
14
  extends CFormControlWrapperProps,
14
15
  Omit<InputHTMLAttributes<HTMLSelectElement>, 'size'> {
@@ -102,7 +103,8 @@ export const CFormSelect = forwardRef<HTMLSelectElement, CFormSelectProps>(
102
103
  <option
103
104
  {...(typeof option === 'object' &&
104
105
  option.disabled && { disabled: option.disabled })}
105
- {...(typeof option === 'object' && option.value && { value: option.value })}
106
+ {...(typeof option === 'object' &&
107
+ option.value !== undefined && { value: option.value })}
106
108
  key={index}
107
109
  >
108
110
  {typeof option === 'string' ? option : option.label}
@@ -142,6 +142,7 @@ export const CModal = forwardRef<HTMLDivElement, CModalProps>(
142
142
  return setStaticBackdrop(true)
143
143
  }
144
144
 
145
+ setVisible(false)
145
146
  return onClose && onClose()
146
147
  }
147
148
 
@@ -174,6 +175,7 @@ export const CModal = forwardRef<HTMLDivElement, CModalProps>(
174
175
  document.body.style.removeProperty('padding-right')
175
176
  }
176
177
  }
178
+
177
179
  return () => {
178
180
  document.body.classList.remove('modal-open')
179
181
  if (backdrop) {
@@ -1,12 +1,13 @@
1
1
  import React, { FC, HTMLAttributes, ReactNode, useRef, useEffect, useState } from 'react'
2
2
  import { createPortal } from 'react-dom'
3
- import PropTypes from 'prop-types'
4
3
  import classNames from 'classnames'
5
- import { usePopper } from 'react-popper'
4
+ import PropTypes from 'prop-types'
6
5
  import { Transition } from 'react-transition-group'
6
+ import { createPopper, Instance, Placement } from '@popperjs/core'
7
7
 
8
8
  import { triggerPropType } from '../../props'
9
9
  import type { Triggers } from '../../types'
10
+ import { isRTL } from '../../utils'
10
11
 
11
12
  export interface CPopoverProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title' | 'content'> {
12
13
  /**
@@ -49,6 +50,20 @@ export interface CPopoverProps extends Omit<HTMLAttributes<HTMLDivElement>, 'tit
49
50
  visible?: boolean
50
51
  }
51
52
 
53
+ const getPlacement = (placement: string, element: HTMLDivElement | null): Placement => {
54
+ switch (placement) {
55
+ case 'right': {
56
+ return isRTL(element) ? 'left' : 'right'
57
+ }
58
+ case 'left': {
59
+ return isRTL(element) ? 'right' : 'left'
60
+ }
61
+ default: {
62
+ return placement as Placement
63
+ }
64
+ }
65
+ }
66
+
52
67
  export const CPopover: FC<CPopoverProps> = ({
53
68
  children,
54
69
  className,
@@ -62,33 +77,53 @@ export const CPopover: FC<CPopoverProps> = ({
62
77
  visible,
63
78
  ...rest
64
79
  }) => {
80
+ const popoverRef = useRef(null)
81
+ const togglerRef = useRef(null)
82
+ const popper = useRef<Instance>()
65
83
  const [_visible, setVisible] = useState(visible)
66
- const popoverRef = useRef()
67
-
68
- const [referenceElement, setReferenceElement] = useState(null)
69
- const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null)
70
- const [arrowElement, setArrowElement] = useState<HTMLDivElement | null>(null)
71
- const { styles, attributes } = usePopper(referenceElement, popperElement, {
72
- modifiers: [
73
- { name: 'arrow', options: { element: arrowElement } },
74
- {
75
- name: 'offset',
76
- options: {
77
- offset: offset,
78
- },
79
- },
80
- ],
81
- placement: placement,
82
- })
83
84
 
84
85
  useEffect(() => {
85
86
  setVisible(visible)
86
87
  }, [visible])
87
88
 
89
+ useEffect(() => {
90
+ if (_visible) {
91
+ initPopper()
92
+ }
93
+
94
+ return () => {
95
+ destroyPopper()
96
+ }
97
+ }, [_visible])
98
+
99
+ const initPopper = () => {
100
+ if (togglerRef.current && popoverRef.current) {
101
+ popper.current = createPopper(togglerRef.current, popoverRef.current, {
102
+ modifiers: [
103
+ {
104
+ name: 'offset',
105
+ options: {
106
+ offset: offset,
107
+ },
108
+ },
109
+ ],
110
+ placement: getPlacement(placement, togglerRef.current),
111
+ })
112
+ }
113
+ }
114
+
115
+ const destroyPopper = () => {
116
+ if (popper.current) {
117
+ popper.current.destroy()
118
+ }
119
+
120
+ popper.current = undefined
121
+ }
122
+
88
123
  return (
89
124
  <>
90
125
  {React.cloneElement(children as React.ReactElement<any>, {
91
- ref: setReferenceElement,
126
+ ref: togglerRef,
92
127
  ...((trigger === 'click' || trigger.includes('click')) && {
93
128
  onClick: () => setVisible(!_visible),
94
129
  }),
@@ -119,20 +154,20 @@ export const CPopover: FC<CPopoverProps> = ({
119
154
  <div
120
155
  className={classNames(
121
156
  'popover',
122
- `bs-popover-${placement.replace('left', 'start').replace('right', 'end')}`,
157
+ `bs-popover-${getPlacement(placement, togglerRef.current)
158
+ .replace('left', 'start')
159
+ .replace('right', 'end')}`,
123
160
  'fade',
124
161
  {
125
162
  show: state === 'entered',
126
163
  },
127
164
  className,
128
165
  )}
129
- ref={setPopperElement}
166
+ ref={popoverRef}
130
167
  role="tooltip"
131
- style={styles.popper}
132
- {...attributes.popper}
133
168
  {...rest}
134
169
  >
135
- <div className="popover-arrow" style={styles.arrow} ref={setArrowElement}></div>
170
+ <div data-popper-arrow className="popover-arrow"></div>
136
171
  <div className="popover-header">{title}</div>
137
172
  <div className="popover-body">{content}</div>
138
173
  </div>
@@ -11,10 +11,11 @@ exports[`CPopover customize 1`] = `
11
11
  <div
12
12
  class="popover bs-popover-end fade"
13
13
  role="tooltip"
14
- style="position: absolute; left: 0px; top: 0px;"
14
+ style="position: absolute; left: 0px; top: 0px; margin: 0px;"
15
15
  >
16
16
  <div
17
17
  class="popover-arrow"
18
+ data-popper-arrow="true"
18
19
  style="position: absolute;"
19
20
  />
20
21
  <div
@@ -29,6 +29,7 @@ export interface CProgressProps
29
29
  white?: boolean
30
30
  }
31
31
 
32
+ // TODO: update markup and add '.progress-stacked' in v5
32
33
  export const CProgress = forwardRef<HTMLDivElement, CProgressProps>(
33
34
  ({ children, className, height, thin, value = 0, white, ...rest }, ref) => {
34
35
  return (
@@ -98,7 +98,7 @@ export const CSidebar = forwardRef<HTMLDivElement, CSidebarProps>(
98
98
  sidebarRef.current && setMobile(isOnMobile(sidebarRef.current))
99
99
  sidebarRef.current && setInViewport(isInViewport(sidebarRef.current))
100
100
 
101
- window.addEventListener('resize', () => handleResize())
101
+ window.addEventListener('resize', handleResize)
102
102
  window.addEventListener('mouseup', handleClickOutside)
103
103
  window.addEventListener('keyup', handleKeyup)
104
104
 
@@ -108,7 +108,7 @@ export const CSidebar = forwardRef<HTMLDivElement, CSidebarProps>(
108
108
  })
109
109
 
110
110
  return () => {
111
- window.removeEventListener('resize', () => handleResize())
111
+ window.removeEventListener('resize', handleResize)
112
112
  window.removeEventListener('mouseup', handleClickOutside)
113
113
  window.removeEventListener('keyup', handleKeyup)
114
114
 
@@ -51,8 +51,10 @@ export const CSpinner = forwardRef<HTMLDivElement | HTMLSpanElement, CSpinnerPro
51
51
  <Component
52
52
  className={classNames(
53
53
  `spinner-${variant}`,
54
- `text-${color}`,
55
- size && `spinner-${variant}-${size}`,
54
+ {
55
+ [`spinner-${variant}-${size}`]: size,
56
+ [`text-${color}`]: color,
57
+ },
56
58
  className,
57
59
  )}
58
60
  role="status"
@@ -3,7 +3,7 @@
3
3
  exports[`CSpinner customize 1`] = `
4
4
  <div>
5
5
  <h3
6
- class="spinner-grow text-warning spinner-grow-sm bazinga"
6
+ class="spinner-grow spinner-grow-sm text-warning bazinga"
7
7
  role="status"
8
8
  >
9
9
  <span
@@ -18,7 +18,7 @@ exports[`CSpinner customize 1`] = `
18
18
  exports[`loads and displays CSpinner component 1`] = `
19
19
  <div>
20
20
  <div
21
- class="spinner-border text-undefined"
21
+ class="spinner-border"
22
22
  role="status"
23
23
  >
24
24
  <span
@@ -201,7 +201,8 @@ export const CTable = forwardRef<HTMLTableElement, CTableProps>(
201
201
  <CTableRow {...(item._props && { ...item._props })} key={index}>
202
202
  {columnNames &&
203
203
  columnNames.map((colName: string, index: number) => {
204
- return item[colName] ? (
204
+ // eslint-disable-next-line unicorn/no-negated-condition
205
+ return item[colName] !== undefined ? (
205
206
  <CTableDataCell
206
207
  {...(item._cellProps && {
207
208
  ...(item._cellProps['all'] && { ...item._cellProps['all'] }),
@@ -1,12 +1,13 @@
1
- import React, { FC, HTMLAttributes, ReactNode, useEffect, useRef, useState } from 'react'
1
+ import React, { FC, HTMLAttributes, ReactNode, useRef, useEffect, useState } from 'react'
2
2
  import { createPortal } from 'react-dom'
3
- import PropTypes from 'prop-types'
4
3
  import classNames from 'classnames'
5
- import { usePopper } from 'react-popper'
4
+ import PropTypes from 'prop-types'
6
5
  import { Transition } from 'react-transition-group'
6
+ import { createPopper, Instance, Placement } from '@popperjs/core'
7
7
 
8
8
  import { triggerPropType } from '../../props'
9
9
  import type { Triggers } from '../../types'
10
+ import { isRTL } from '../../utils'
10
11
 
11
12
  export interface CTooltipProps extends Omit<HTMLAttributes<HTMLDivElement>, 'content'> {
12
13
  /**
@@ -18,7 +19,7 @@ export interface CTooltipProps extends Omit<HTMLAttributes<HTMLDivElement>, 'con
18
19
  */
19
20
  content: ReactNode | string
20
21
  /**
21
- * Offset of the popover relative to its target.
22
+ * Offset of the tooltip relative to its target.
22
23
  */
23
24
  offset?: [number, number]
24
25
  /**
@@ -40,16 +41,30 @@ export interface CTooltipProps extends Omit<HTMLAttributes<HTMLDivElement>, 'con
40
41
  */
41
42
  placement?: 'auto' | 'top' | 'right' | 'bottom' | 'left'
42
43
  /**
43
- * Toggle the visibility of popover component.
44
+ * Toggle the visibility of tooltip component.
44
45
  */
45
46
  visible?: boolean
46
47
  }
47
48
 
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
+
48
63
  export const CTooltip: FC<CTooltipProps> = ({
49
64
  children,
50
65
  className,
51
66
  content,
52
- offset = [0, 0],
67
+ offset = [0, 6],
53
68
  onHide,
54
69
  onShow,
55
70
  placement = 'top',
@@ -57,33 +72,53 @@ export const CTooltip: FC<CTooltipProps> = ({
57
72
  visible,
58
73
  ...rest
59
74
  }) => {
60
- const tooltipRef = useRef()
75
+ const tooltipRef = useRef(null)
76
+ const togglerRef = useRef(null)
77
+ const popper = useRef<Instance>()
61
78
  const [_visible, setVisible] = useState(visible)
62
79
 
63
- const [referenceElement, setReferenceElement] = useState(null)
64
- const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null)
65
- const [arrowElement, setArrowElement] = useState<HTMLDivElement | null>(null)
66
- const { styles, attributes } = usePopper(referenceElement, popperElement, {
67
- modifiers: [
68
- { name: 'arrow', options: { element: arrowElement } },
69
- {
70
- name: 'offset',
71
- options: {
72
- offset: offset,
73
- },
74
- },
75
- ],
76
- placement: placement,
77
- })
78
-
79
80
  useEffect(() => {
80
81
  setVisible(visible)
81
82
  }, [visible])
82
83
 
84
+ useEffect(() => {
85
+ if (_visible) {
86
+ initPopper()
87
+ }
88
+
89
+ return () => {
90
+ destroyPopper()
91
+ }
92
+ }, [_visible])
93
+
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()
113
+ }
114
+
115
+ popper.current = undefined
116
+ }
117
+
83
118
  return (
84
119
  <>
85
120
  {React.cloneElement(children as React.ReactElement<any>, {
86
- ref: setReferenceElement,
121
+ ref: togglerRef,
87
122
  ...((trigger === 'click' || trigger.includes('click')) && {
88
123
  onClick: () => setVisible(!_visible),
89
124
  }),
@@ -101,7 +136,6 @@ export const CTooltip: FC<CTooltipProps> = ({
101
136
  <Transition
102
137
  in={_visible}
103
138
  mountOnEnter
104
- nodeRef={tooltipRef}
105
139
  onEnter={onShow}
106
140
  onExit={onHide}
107
141
  timeout={{
@@ -114,20 +148,20 @@ export const CTooltip: FC<CTooltipProps> = ({
114
148
  <div
115
149
  className={classNames(
116
150
  'tooltip',
117
- `bs-popover-${placement.replace('left', 'start').replace('right', 'end')}`,
151
+ `bs-tooltip-${getPlacement(placement, togglerRef.current)
152
+ .replace('left', 'start')
153
+ .replace('right', 'end')}`,
118
154
  'fade',
119
155
  {
120
156
  show: state === 'entered',
121
157
  },
122
158
  className,
123
159
  )}
124
- ref={setPopperElement}
160
+ ref={tooltipRef}
125
161
  role="tooltip"
126
- style={styles.popper}
127
- {...attributes.popper}
128
162
  {...rest}
129
163
  >
130
- <div className="tooltip-arrow" style={styles.arrow} ref={setArrowElement}></div>
164
+ <div data-popper-arrow className="tooltip-arrow"></div>
131
165
  <div className="tooltip-inner">{content}</div>
132
166
  </div>
133
167
  )}
@@ -13,7 +13,7 @@ beforeEach(() => {
13
13
  })
14
14
 
15
15
  afterEach(() => {
16
- container && document.body.removeChild(container)
16
+ container && container.remove()
17
17
  container = null
18
18
  })
19
19
 
@@ -1,3 +1,4 @@
1
+ import { useColorModes } from './useColorModes'
1
2
  import { useForkedRef } from './useForkedRef'
2
3
 
3
- export { useForkedRef }
4
+ export { useColorModes, useForkedRef }
@@ -0,0 +1,55 @@
1
+ import { useEffect, useState } from 'react'
2
+
3
+ interface UseColorModesOutput {
4
+ getColorMode: () => string
5
+ setColorMode: (mode: string) => void
6
+ }
7
+
8
+ const getStoredTheme = (localStorageItemName: string) => localStorage.getItem(localStorageItemName)
9
+ const setStoredTheme = (localStorageItemName: string, colorMode: string) =>
10
+ localStorage.setItem(localStorageItemName, colorMode)
11
+
12
+ const getPreferredColorScheme = (localStorageItemName: string) => {
13
+ const storedTheme = getStoredTheme(localStorageItemName)
14
+
15
+ if (storedTheme) {
16
+ return storedTheme
17
+ }
18
+
19
+ return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
20
+ }
21
+
22
+ const setTheme = (colorMode: string) => {
23
+ document.documentElement.dataset.coreuiTheme =
24
+ colorMode === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches
25
+ ? 'dark'
26
+ : colorMode
27
+
28
+ const event = new Event('ColorSchemeChange')
29
+ document.documentElement.dispatchEvent(event)
30
+ }
31
+
32
+ export const useColorModes = (
33
+ localStorageItemName = 'coreui-react-color-scheme',
34
+ ): UseColorModesOutput => {
35
+ const [colorMode, setColorMode] = useState<string>(getPreferredColorScheme(localStorageItemName))
36
+
37
+ useEffect(() => {
38
+ setStoredTheme(localStorageItemName, colorMode)
39
+ setTheme(colorMode)
40
+ }, [colorMode])
41
+
42
+ useEffect(() => {
43
+ window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
44
+ const storedTheme = getStoredTheme(localStorageItemName)
45
+ if (storedTheme !== 'light' && storedTheme !== 'dark') {
46
+ setTheme(colorMode)
47
+ }
48
+ })
49
+ }, [])
50
+
51
+ return {
52
+ getColorMode: () => colorMode,
53
+ setColorMode: (mode: string) => setColorMode(mode),
54
+ }
55
+ }
@@ -38,7 +38,7 @@ export function assignRef<RefValueType = any>(
38
38
  } else {
39
39
  try {
40
40
  ref.current = value
41
- } catch (error) {
41
+ } catch {
42
42
  throw new Error(`Cannot assign value "${value}" to ref "${ref}"`)
43
43
  }
44
44
  }
package/src/index.ts CHANGED
@@ -1 +1,2 @@
1
1
  export * from './components/'
2
+ export * from './hooks/'
package/src/props.ts CHANGED
@@ -57,4 +57,7 @@ export const textColorsPropType = PropTypes.oneOfType([
57
57
  PropTypes.string,
58
58
  ])
59
59
 
60
- export const triggerPropType = PropTypes.oneOf<Triggers>(['hover', 'focus', 'click'])
60
+ export const triggerPropType = PropTypes.oneOfType([
61
+ PropTypes.arrayOf(PropTypes.oneOf<Triggers>(['hover', 'focus', 'click']).isRequired),
62
+ PropTypes.oneOf<Triggers>(['hover', 'focus', 'click']),
63
+ ])
@@ -1,3 +1,4 @@
1
1
  import isInViewport from './isInViewport'
2
+ import isRTL from './isRTL'
2
3
 
3
- export { isInViewport }
4
+ export { isInViewport, isRTL }
@@ -0,0 +1,13 @@
1
+ const isRTL = (element?: HTMLElement | HTMLDivElement | null) => {
2
+ if (typeof document !== 'undefined' && document.documentElement.dir === 'rtl') {
3
+ return true
4
+ }
5
+
6
+ if (element) {
7
+ return element.closest('[dir="rtl"]') !== null
8
+ }
9
+
10
+ return false
11
+ }
12
+
13
+ export default isRTL
@@ -1,11 +0,0 @@
1
- // Jest Snapshot v1, https://goo.gl/fbAQLP
2
-
3
- exports[`loads and displays CAccordionCollapse component 1`] = `
4
- <div>
5
- <div
6
- class="accordion-collapse collapse"
7
- >
8
- Test
9
- </div>
10
- </div>
11
- `;