@coreui/react 5.0.0 → 5.1.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/cjs/components/dropdown/CDropdown.js +2 -1
- package/dist/cjs/components/dropdown/CDropdown.js.map +1 -1
- package/dist/cjs/components/dropdown/utils.d.ts +0 -1
- package/dist/cjs/components/dropdown/utils.js +0 -13
- package/dist/cjs/components/dropdown/utils.js.map +1 -1
- package/dist/cjs/components/popover/CPopover.js +41 -40
- package/dist/cjs/components/popover/CPopover.js.map +1 -1
- package/dist/cjs/components/tabs/CTab.d.ts +12 -0
- package/dist/cjs/components/tabs/CTab.js +25 -0
- package/dist/cjs/components/tabs/CTab.js.map +1 -0
- package/dist/cjs/components/tabs/CTabList.d.ts +16 -0
- package/dist/cjs/components/tabs/CTabList.js +54 -0
- package/dist/cjs/components/tabs/CTabList.js.map +1 -0
- package/dist/cjs/components/tabs/CTabPane.d.ts +6 -0
- package/dist/cjs/components/tabs/CTabPane.js +4 -2
- package/dist/cjs/components/tabs/CTabPane.js.map +1 -1
- package/dist/cjs/components/tabs/CTabPanel.d.ts +28 -0
- package/dist/cjs/components/tabs/CTabPanel.js +43 -0
- package/dist/cjs/components/tabs/CTabPanel.js.map +1 -0
- package/dist/cjs/components/tabs/CTabs.d.ts +22 -0
- package/dist/cjs/components/tabs/CTabs.js +28 -0
- package/dist/cjs/components/tabs/CTabs.js.map +1 -0
- package/dist/cjs/components/tabs/index.d.ts +5 -1
- package/dist/cjs/components/tooltip/CTooltip.js +44 -40
- package/dist/cjs/components/tooltip/CTooltip.js.map +1 -1
- package/dist/cjs/hooks/usePopper.d.ts +1 -0
- package/dist/cjs/hooks/usePopper.js +11 -4
- package/dist/cjs/hooks/usePopper.js.map +1 -1
- package/dist/cjs/index.js +8 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/node_modules/react-transition-group/esm/CSSTransition.js +1 -2
- package/dist/cjs/node_modules/react-transition-group/esm/CSSTransition.js.map +1 -1
- package/dist/cjs/node_modules/react-transition-group/esm/Transition.js +1 -2
- package/dist/cjs/node_modules/react-transition-group/esm/Transition.js.map +1 -1
- package/dist/cjs/utils/getNextActiveElement.d.ts +2 -0
- package/dist/cjs/utils/getNextActiveElement.js +19 -0
- package/dist/cjs/utils/getNextActiveElement.js.map +1 -0
- package/dist/cjs/utils/index.d.ts +2 -1
- package/dist/esm/components/dropdown/CDropdown.js +2 -1
- package/dist/esm/components/dropdown/CDropdown.js.map +1 -1
- package/dist/esm/components/dropdown/utils.d.ts +0 -1
- package/dist/esm/components/dropdown/utils.js +1 -13
- package/dist/esm/components/dropdown/utils.js.map +1 -1
- package/dist/esm/components/popover/CPopover.js +41 -40
- package/dist/esm/components/popover/CPopover.js.map +1 -1
- package/dist/esm/components/tabs/CTab.d.ts +12 -0
- package/dist/esm/components/tabs/CTab.js +23 -0
- package/dist/esm/components/tabs/CTab.js.map +1 -0
- package/dist/esm/components/tabs/CTabList.d.ts +16 -0
- package/dist/esm/components/tabs/CTabList.js +52 -0
- package/dist/esm/components/tabs/CTabList.js.map +1 -0
- package/dist/esm/components/tabs/CTabPane.d.ts +6 -0
- package/dist/esm/components/tabs/CTabPane.js +4 -2
- package/dist/esm/components/tabs/CTabPane.js.map +1 -1
- package/dist/esm/components/tabs/CTabPanel.d.ts +28 -0
- package/dist/esm/components/tabs/CTabPanel.js +41 -0
- package/dist/esm/components/tabs/CTabPanel.js.map +1 -0
- package/dist/esm/components/tabs/CTabs.d.ts +22 -0
- package/dist/esm/components/tabs/CTabs.js +25 -0
- package/dist/esm/components/tabs/CTabs.js.map +1 -0
- package/dist/esm/components/tabs/index.d.ts +5 -1
- package/dist/esm/components/tooltip/CTooltip.js +44 -40
- package/dist/esm/components/tooltip/CTooltip.js.map +1 -1
- package/dist/esm/hooks/usePopper.d.ts +1 -0
- package/dist/esm/hooks/usePopper.js +11 -4
- package/dist/esm/hooks/usePopper.js.map +1 -1
- package/dist/esm/index.js +4 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/node_modules/react-transition-group/esm/CSSTransition.js +1 -2
- package/dist/esm/node_modules/react-transition-group/esm/CSSTransition.js.map +1 -1
- package/dist/esm/node_modules/react-transition-group/esm/Transition.js +1 -2
- package/dist/esm/node_modules/react-transition-group/esm/Transition.js.map +1 -1
- package/dist/esm/utils/getNextActiveElement.d.ts +2 -0
- package/dist/esm/utils/getNextActiveElement.js +15 -0
- package/dist/esm/utils/getNextActiveElement.js.map +1 -0
- package/dist/esm/utils/index.d.ts +2 -1
- package/package.json +12 -12
- package/src/components/dropdown/CDropdown.tsx +2 -2
- package/src/components/dropdown/utils.ts +0 -22
- package/src/components/popover/CPopover.tsx +51 -66
- package/src/components/tabs/CTab.tsx +56 -0
- package/src/components/tabs/CTabList.tsx +92 -0
- package/src/components/tabs/CTabPane.tsx +9 -2
- package/src/components/tabs/CTabPanel.tsx +98 -0
- package/src/components/tabs/CTabs.tsx +54 -0
- package/src/components/tabs/index.ts +5 -1
- package/src/components/tooltip/CTooltip.tsx +55 -66
- package/src/hooks/usePopper.ts +15 -5
- package/src/utils/getNextActiveElement.ts +23 -0
- package/src/utils/index.ts +2 -0
|
@@ -15,10 +15,10 @@ import { PolymorphicRefForwardingComponent } from '../../helpers'
|
|
|
15
15
|
import { useForkedRef, usePopper } from '../../hooks'
|
|
16
16
|
import { placementPropType } from '../../props'
|
|
17
17
|
import type { Placements } from '../../types'
|
|
18
|
-
import { isRTL } from '../../utils'
|
|
18
|
+
import { getNextActiveElement, isRTL } from '../../utils'
|
|
19
19
|
|
|
20
20
|
import type { Alignments, Directions } from './types'
|
|
21
|
-
import {
|
|
21
|
+
import { getPlacement } from './utils'
|
|
22
22
|
|
|
23
23
|
export interface CDropdownProps extends HTMLAttributes<HTMLDivElement | HTMLLIElement> {
|
|
24
24
|
/**
|
|
@@ -19,28 +19,6 @@ export const getAlignmentClassNames = (alignment: Alignments) => {
|
|
|
19
19
|
return classNames
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
export const getNextActiveElement = (
|
|
23
|
-
list: HTMLElement[],
|
|
24
|
-
activeElement: HTMLElement,
|
|
25
|
-
shouldGetNext: boolean,
|
|
26
|
-
isCycleAllowed: boolean,
|
|
27
|
-
) => {
|
|
28
|
-
const listLength = list.length
|
|
29
|
-
let index = list.indexOf(activeElement)
|
|
30
|
-
|
|
31
|
-
if (index === -1) {
|
|
32
|
-
return !shouldGetNext && isCycleAllowed ? list[listLength - 1] : list[0]
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
index += shouldGetNext ? 1 : -1
|
|
36
|
-
|
|
37
|
-
if (isCycleAllowed) {
|
|
38
|
-
index = (index + listLength) % listLength
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return list[Math.max(0, Math.min(index, listLength - 1))]
|
|
42
|
-
}
|
|
43
|
-
|
|
44
22
|
export const getPlacement = (
|
|
45
23
|
placement: Placement,
|
|
46
24
|
direction: string | undefined,
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import React, { forwardRef, HTMLAttributes, ReactNode, useRef, useEffect, useState } from 'react'
|
|
2
2
|
import classNames from 'classnames'
|
|
3
3
|
import PropTypes from 'prop-types'
|
|
4
|
-
import { Transition } from 'react-transition-group'
|
|
5
4
|
|
|
6
5
|
import { CConditionalPortal } from '../conditional-portal'
|
|
7
6
|
import { useForkedRef, usePopper } from '../../hooks'
|
|
8
7
|
import { fallbackPlacementsPropType, triggerPropType } from '../../props'
|
|
9
8
|
import type { Placements, Triggers } from '../../types'
|
|
10
|
-
import {
|
|
9
|
+
import { executeAfterTransition, getRTLPlacement } from '../../utils'
|
|
11
10
|
|
|
12
11
|
export interface CPopoverProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title' | 'content'> {
|
|
13
12
|
/**
|
|
@@ -101,6 +100,7 @@ export const CPopover = forwardRef<HTMLDivElement, CPopoverProps>(
|
|
|
101
100
|
const uID = useRef(`popover${Math.floor(Math.random() * 1_000_000)}`)
|
|
102
101
|
|
|
103
102
|
const { initPopper, destroyPopper } = usePopper()
|
|
103
|
+
const [mounted, setMounted] = useState(false)
|
|
104
104
|
const [_visible, setVisible] = useState(visible)
|
|
105
105
|
|
|
106
106
|
const _delay = typeof delay === 'number' ? { show: delay, hide: delay } : delay
|
|
@@ -133,14 +133,39 @@ export const CPopover = forwardRef<HTMLDivElement, CPopoverProps>(
|
|
|
133
133
|
setVisible(visible)
|
|
134
134
|
}, [visible])
|
|
135
135
|
|
|
136
|
-
|
|
137
|
-
if (
|
|
138
|
-
|
|
139
|
-
|
|
136
|
+
useEffect(() => {
|
|
137
|
+
if (_visible) {
|
|
138
|
+
setMounted(true)
|
|
139
|
+
|
|
140
|
+
if (popoverRef.current) {
|
|
141
|
+
popoverRef.current.classList.remove('fade', 'show')
|
|
142
|
+
destroyPopper()
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
setTimeout(() => {
|
|
146
|
+
if (togglerRef.current && popoverRef.current) {
|
|
147
|
+
if (animation) {
|
|
148
|
+
popoverRef.current.classList.add('fade')
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
initPopper(togglerRef.current, popoverRef.current, popperConfig)
|
|
152
|
+
popoverRef.current.classList.add('show')
|
|
153
|
+
onShow && onShow()
|
|
154
|
+
}
|
|
155
|
+
}, _delay.show)
|
|
140
156
|
}
|
|
141
157
|
|
|
142
|
-
|
|
143
|
-
|
|
158
|
+
return () => {
|
|
159
|
+
if (popoverRef.current) {
|
|
160
|
+
popoverRef.current.classList.remove('show')
|
|
161
|
+
onHide && onHide()
|
|
162
|
+
executeAfterTransition(() => {
|
|
163
|
+
destroyPopper()
|
|
164
|
+
setMounted(false)
|
|
165
|
+
}, popoverRef.current)
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}, [_visible])
|
|
144
169
|
|
|
145
170
|
return (
|
|
146
171
|
<>
|
|
@@ -150,71 +175,31 @@ export const CPopover = forwardRef<HTMLDivElement, CPopoverProps>(
|
|
|
150
175
|
}),
|
|
151
176
|
ref: togglerRef,
|
|
152
177
|
...((trigger === 'click' || trigger.includes('click')) && {
|
|
153
|
-
onClick: () =>
|
|
178
|
+
onClick: () => setVisible(!_visible),
|
|
154
179
|
}),
|
|
155
180
|
...((trigger === 'focus' || trigger.includes('focus')) && {
|
|
156
|
-
onFocus: () =>
|
|
157
|
-
onBlur: () =>
|
|
181
|
+
onFocus: () => setVisible(true),
|
|
182
|
+
onBlur: () => setVisible(false),
|
|
158
183
|
}),
|
|
159
184
|
...((trigger === 'hover' || trigger.includes('hover')) && {
|
|
160
|
-
onMouseEnter: () =>
|
|
161
|
-
onMouseLeave: () =>
|
|
185
|
+
onMouseEnter: () => setVisible(true),
|
|
186
|
+
onMouseLeave: () => setVisible(false),
|
|
162
187
|
}),
|
|
163
188
|
})}
|
|
164
189
|
<CConditionalPortal container={container} portal={true}>
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
popoverRef.current.style.display = 'initial'
|
|
179
|
-
}
|
|
180
|
-
}}
|
|
181
|
-
onExit={onHide}
|
|
182
|
-
onExited={() => {
|
|
183
|
-
destroyPopper()
|
|
184
|
-
}}
|
|
185
|
-
timeout={{
|
|
186
|
-
enter: 0,
|
|
187
|
-
exit: popoverRef.current
|
|
188
|
-
? getTransitionDurationFromElement(popoverRef.current) + 50
|
|
189
|
-
: 200,
|
|
190
|
-
}}
|
|
191
|
-
unmountOnExit
|
|
192
|
-
>
|
|
193
|
-
{(state) => (
|
|
194
|
-
<div
|
|
195
|
-
className={classNames(
|
|
196
|
-
'popover',
|
|
197
|
-
'bs-popover-auto',
|
|
198
|
-
{
|
|
199
|
-
fade: animation,
|
|
200
|
-
show: state === 'entered',
|
|
201
|
-
},
|
|
202
|
-
className,
|
|
203
|
-
)}
|
|
204
|
-
id={uID.current}
|
|
205
|
-
ref={forkedRef}
|
|
206
|
-
role="tooltip"
|
|
207
|
-
style={{
|
|
208
|
-
display: 'none',
|
|
209
|
-
}}
|
|
210
|
-
{...rest}
|
|
211
|
-
>
|
|
212
|
-
<div className="popover-arrow"></div>
|
|
213
|
-
<div className="popover-header">{title}</div>
|
|
214
|
-
<div className="popover-body">{content}</div>
|
|
215
|
-
</div>
|
|
216
|
-
)}
|
|
217
|
-
</Transition>
|
|
190
|
+
{mounted && (
|
|
191
|
+
<div
|
|
192
|
+
className={classNames('popover', 'bs-popover-auto', className)}
|
|
193
|
+
id={uID.current}
|
|
194
|
+
ref={forkedRef}
|
|
195
|
+
role="tooltip"
|
|
196
|
+
{...rest}
|
|
197
|
+
>
|
|
198
|
+
<div className="popover-arrow"></div>
|
|
199
|
+
<div className="popover-header">{title}</div>
|
|
200
|
+
<div className="popover-body">{content}</div>
|
|
201
|
+
</div>
|
|
202
|
+
)}
|
|
218
203
|
</CConditionalPortal>
|
|
219
204
|
</>
|
|
220
205
|
)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React, { forwardRef, HTMLAttributes, useContext } from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import classNames from 'classnames'
|
|
4
|
+
|
|
5
|
+
import { TabsContext } from './CTabs'
|
|
6
|
+
|
|
7
|
+
export interface CTabProps extends HTMLAttributes<HTMLButtonElement> {
|
|
8
|
+
/**
|
|
9
|
+
* A string of all className you want applied to the base component.
|
|
10
|
+
*/
|
|
11
|
+
className?: string
|
|
12
|
+
/**
|
|
13
|
+
* Item key.
|
|
14
|
+
*/
|
|
15
|
+
itemKey: number | string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const CTab = forwardRef<HTMLButtonElement, CTabProps>(
|
|
19
|
+
({ children, className, itemKey, ...rest }, ref) => {
|
|
20
|
+
const { _activeItemKey, setActiveItemKey, id } = useContext(TabsContext)
|
|
21
|
+
|
|
22
|
+
const isActive = () => itemKey === _activeItemKey
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<button
|
|
26
|
+
className={classNames(
|
|
27
|
+
'nav-link',
|
|
28
|
+
{
|
|
29
|
+
active: isActive(),
|
|
30
|
+
},
|
|
31
|
+
className,
|
|
32
|
+
)}
|
|
33
|
+
id={`${id}${itemKey}-tab`}
|
|
34
|
+
onClick={() => setActiveItemKey(itemKey)}
|
|
35
|
+
onFocus={() => setActiveItemKey(itemKey)}
|
|
36
|
+
role="tab"
|
|
37
|
+
tabIndex={isActive() ? 0 : -1}
|
|
38
|
+
type="button"
|
|
39
|
+
aria-controls={`${id}${itemKey}-tab-pane`}
|
|
40
|
+
aria-selected={isActive()}
|
|
41
|
+
ref={ref}
|
|
42
|
+
{...rest}
|
|
43
|
+
>
|
|
44
|
+
{children}
|
|
45
|
+
</button>
|
|
46
|
+
)
|
|
47
|
+
},
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
CTab.propTypes = {
|
|
51
|
+
children: PropTypes.node,
|
|
52
|
+
className: PropTypes.string,
|
|
53
|
+
itemKey: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
CTab.displayName = 'CTab'
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import React, { forwardRef, HTMLAttributes, KeyboardEvent, useRef } from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import classNames from 'classnames'
|
|
4
|
+
|
|
5
|
+
import { useForkedRef } from '../../hooks'
|
|
6
|
+
import { getNextActiveElement } from '../../utils'
|
|
7
|
+
|
|
8
|
+
export interface CTabListProps extends HTMLAttributes<HTMLDivElement> {
|
|
9
|
+
/**
|
|
10
|
+
* A string of all className you want applied to the base component.
|
|
11
|
+
*/
|
|
12
|
+
className?: string
|
|
13
|
+
/**
|
|
14
|
+
* Specify a layout type for component.
|
|
15
|
+
*/
|
|
16
|
+
layout?: 'fill' | 'justified'
|
|
17
|
+
/**
|
|
18
|
+
* Set the nav variant to tabs or pills.
|
|
19
|
+
*/
|
|
20
|
+
variant?: 'pills' | 'tabs' | 'underline' | 'underline-border'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const CTabList = forwardRef<HTMLDivElement, CTabListProps>(
|
|
24
|
+
({ children, className, layout, variant, ...rest }, ref) => {
|
|
25
|
+
const tabListRef = useRef<HTMLDivElement>(null)
|
|
26
|
+
const forkedRef = useForkedRef(ref, tabListRef)
|
|
27
|
+
|
|
28
|
+
const handleKeydown = (event: KeyboardEvent<HTMLDivElement>) => {
|
|
29
|
+
if (
|
|
30
|
+
tabListRef.current !== null &&
|
|
31
|
+
(event.key === 'ArrowDown' ||
|
|
32
|
+
event.key === 'ArrowUp' ||
|
|
33
|
+
event.key === 'ArrowLeft' ||
|
|
34
|
+
event.key === 'ArrowRight' ||
|
|
35
|
+
event.key === 'Home' ||
|
|
36
|
+
event.key === 'End')
|
|
37
|
+
) {
|
|
38
|
+
event.preventDefault()
|
|
39
|
+
const target = event.target as HTMLElement
|
|
40
|
+
// eslint-disable-next-line unicorn/prefer-spread
|
|
41
|
+
const items: HTMLElement[] = Array.from(
|
|
42
|
+
tabListRef.current.querySelectorAll('.nav-link:not(.disabled):not(:disabled)'),
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
let nextActiveElement
|
|
46
|
+
|
|
47
|
+
if (event.key === 'Home' || event.key === 'End') {
|
|
48
|
+
nextActiveElement = event.key === 'End' ? items.at(-1) : items[0]
|
|
49
|
+
} else {
|
|
50
|
+
nextActiveElement = getNextActiveElement(
|
|
51
|
+
items,
|
|
52
|
+
target,
|
|
53
|
+
event.key === 'ArrowDown' || event.key === 'ArrowRight',
|
|
54
|
+
true,
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (nextActiveElement) {
|
|
59
|
+
nextActiveElement.focus({ preventScroll: true })
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<div
|
|
66
|
+
className={classNames(
|
|
67
|
+
'nav',
|
|
68
|
+
{
|
|
69
|
+
[`nav-${layout}`]: layout,
|
|
70
|
+
[`nav-${variant}`]: variant,
|
|
71
|
+
},
|
|
72
|
+
className,
|
|
73
|
+
)}
|
|
74
|
+
role="tablist"
|
|
75
|
+
onKeyDown={handleKeydown}
|
|
76
|
+
ref={forkedRef}
|
|
77
|
+
{...rest}
|
|
78
|
+
>
|
|
79
|
+
{children}
|
|
80
|
+
</div>
|
|
81
|
+
)
|
|
82
|
+
},
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
CTabList.propTypes = {
|
|
86
|
+
children: PropTypes.node,
|
|
87
|
+
className: PropTypes.string,
|
|
88
|
+
layout: PropTypes.oneOf(['fill', 'justified']),
|
|
89
|
+
variant: PropTypes.oneOf(['pills', 'tabs', 'underline', 'underline-border']),
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
CTabList.displayName = 'CTabList'
|
|
@@ -18,6 +18,12 @@ export interface CTabPaneProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
18
18
|
* Callback fired when the component requests to be shown.
|
|
19
19
|
*/
|
|
20
20
|
onShow?: () => void
|
|
21
|
+
/**
|
|
22
|
+
* Enable fade in and fade out transition.
|
|
23
|
+
*
|
|
24
|
+
* @since 5.1.0
|
|
25
|
+
*/
|
|
26
|
+
transition?: boolean
|
|
21
27
|
/**
|
|
22
28
|
* Toggle the visibility of component.
|
|
23
29
|
*/
|
|
@@ -25,7 +31,7 @@ export interface CTabPaneProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
25
31
|
}
|
|
26
32
|
|
|
27
33
|
export const CTabPane = forwardRef<HTMLDivElement, CTabPaneProps>(
|
|
28
|
-
({ children, className, onHide, onShow, visible, ...rest }, ref) => {
|
|
34
|
+
({ children, className, onHide, onShow, transition = true, visible, ...rest }, ref) => {
|
|
29
35
|
const tabPaneRef = useRef()
|
|
30
36
|
const forkedRef = useForkedRef(ref, tabPaneRef)
|
|
31
37
|
|
|
@@ -35,9 +41,9 @@ export const CTabPane = forwardRef<HTMLDivElement, CTabPaneProps>(
|
|
|
35
41
|
<div
|
|
36
42
|
className={classNames(
|
|
37
43
|
'tab-pane',
|
|
38
|
-
'fade',
|
|
39
44
|
{
|
|
40
45
|
active: visible,
|
|
46
|
+
fade: transition,
|
|
41
47
|
show: state === 'entered',
|
|
42
48
|
},
|
|
43
49
|
className,
|
|
@@ -58,6 +64,7 @@ CTabPane.propTypes = {
|
|
|
58
64
|
className: PropTypes.string,
|
|
59
65
|
onHide: PropTypes.func,
|
|
60
66
|
onShow: PropTypes.func,
|
|
67
|
+
transition: PropTypes.bool,
|
|
61
68
|
visible: PropTypes.bool,
|
|
62
69
|
}
|
|
63
70
|
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import React, { HTMLAttributes, forwardRef, useContext, useEffect, useRef, useState } from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import classNames from 'classnames'
|
|
4
|
+
import { Transition } from 'react-transition-group'
|
|
5
|
+
|
|
6
|
+
import { TabsContext } from './CTabs'
|
|
7
|
+
import { useForkedRef } from '../../hooks'
|
|
8
|
+
import { getTransitionDurationFromElement } from '../../utils'
|
|
9
|
+
|
|
10
|
+
export interface CTabPanelProps extends HTMLAttributes<HTMLDivElement> {
|
|
11
|
+
/**
|
|
12
|
+
* A string of all className you want applied to the base component.
|
|
13
|
+
*/
|
|
14
|
+
className?: string
|
|
15
|
+
/**
|
|
16
|
+
* Item key.
|
|
17
|
+
*/
|
|
18
|
+
itemKey: number | string
|
|
19
|
+
/**
|
|
20
|
+
* Callback fired when the component requests to be hidden.
|
|
21
|
+
*/
|
|
22
|
+
onHide?: () => void
|
|
23
|
+
/**
|
|
24
|
+
* Callback fired when the component requests to be shown.
|
|
25
|
+
*/
|
|
26
|
+
onShow?: () => void
|
|
27
|
+
/**
|
|
28
|
+
* Enable fade in and fade out transition.
|
|
29
|
+
*/
|
|
30
|
+
transition?: boolean
|
|
31
|
+
/**
|
|
32
|
+
* Toggle the visibility of component.
|
|
33
|
+
*/
|
|
34
|
+
visible?: boolean
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const CTabPanel = forwardRef<HTMLDivElement, CTabPanelProps>(
|
|
38
|
+
({ children, className, itemKey, onHide, onShow, transition = true, visible, ...rest }, ref) => {
|
|
39
|
+
const { _activeItemKey, id } = useContext(TabsContext)
|
|
40
|
+
|
|
41
|
+
const tabPaneRef = useRef()
|
|
42
|
+
const forkedRef = useForkedRef(ref, tabPaneRef)
|
|
43
|
+
|
|
44
|
+
const [_visible, setVisible] = useState(visible || _activeItemKey === itemKey)
|
|
45
|
+
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
visible !== undefined && setVisible(visible)
|
|
48
|
+
}, [visible])
|
|
49
|
+
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
setVisible(_activeItemKey === itemKey)
|
|
52
|
+
}, [_activeItemKey])
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<Transition
|
|
56
|
+
in={_visible}
|
|
57
|
+
nodeRef={tabPaneRef}
|
|
58
|
+
onEnter={onShow}
|
|
59
|
+
onExit={onHide}
|
|
60
|
+
timeout={tabPaneRef.current ? getTransitionDurationFromElement(tabPaneRef.current) : 0}
|
|
61
|
+
>
|
|
62
|
+
{(state) => (
|
|
63
|
+
<div
|
|
64
|
+
className={classNames(
|
|
65
|
+
'tab-pane',
|
|
66
|
+
{
|
|
67
|
+
active: _visible,
|
|
68
|
+
fade: transition,
|
|
69
|
+
show: state === 'entered',
|
|
70
|
+
},
|
|
71
|
+
className,
|
|
72
|
+
)}
|
|
73
|
+
id={`${id}${itemKey}-tab-pane`}
|
|
74
|
+
role="tabpanel"
|
|
75
|
+
aria-labelledby={`${id}${itemKey}-tab`}
|
|
76
|
+
tabIndex={0}
|
|
77
|
+
ref={forkedRef}
|
|
78
|
+
{...rest}
|
|
79
|
+
>
|
|
80
|
+
{children}
|
|
81
|
+
</div>
|
|
82
|
+
)}
|
|
83
|
+
</Transition>
|
|
84
|
+
)
|
|
85
|
+
},
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
CTabPanel.propTypes = {
|
|
89
|
+
children: PropTypes.node,
|
|
90
|
+
className: PropTypes.string,
|
|
91
|
+
itemKey: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
|
|
92
|
+
onHide: PropTypes.func,
|
|
93
|
+
onShow: PropTypes.func,
|
|
94
|
+
transition: PropTypes.bool,
|
|
95
|
+
visible: PropTypes.bool,
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
CTabPanel.displayName = 'CTabPanel'
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import React, { createContext, forwardRef, HTMLAttributes, useEffect, useId, useState } from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import classNames from 'classnames'
|
|
4
|
+
|
|
5
|
+
export interface CTabsProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {
|
|
6
|
+
/**
|
|
7
|
+
* The active item key.
|
|
8
|
+
*/
|
|
9
|
+
activeItemKey: number | string
|
|
10
|
+
/**
|
|
11
|
+
* A string of all className you want applied to the base component.
|
|
12
|
+
*/
|
|
13
|
+
className?: string
|
|
14
|
+
/**
|
|
15
|
+
* The callback is fired when the active tab changes.
|
|
16
|
+
*/
|
|
17
|
+
onChange?: (value: number | string) => void
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface TabsContextProps {
|
|
21
|
+
_activeItemKey?: number | string
|
|
22
|
+
setActiveItemKey: React.Dispatch<React.SetStateAction<number | string | undefined>>
|
|
23
|
+
id?: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const TabsContext = createContext({} as TabsContextProps)
|
|
27
|
+
|
|
28
|
+
export const CTabs = forwardRef<HTMLDivElement, CTabsProps>(
|
|
29
|
+
({ children, activeItemKey, className, onChange }, ref) => {
|
|
30
|
+
const id = useId()
|
|
31
|
+
const [_activeItemKey, setActiveItemKey] = useState(activeItemKey)
|
|
32
|
+
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
_activeItemKey && onChange && onChange(_activeItemKey)
|
|
35
|
+
}, [_activeItemKey])
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<TabsContext.Provider value={{ _activeItemKey, setActiveItemKey, id }}>
|
|
39
|
+
<div className={classNames('tabs', className)} ref={ref}>
|
|
40
|
+
{children}
|
|
41
|
+
</div>
|
|
42
|
+
</TabsContext.Provider>
|
|
43
|
+
)
|
|
44
|
+
},
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
CTabs.propTypes = {
|
|
48
|
+
activeItemKey: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
|
|
49
|
+
children: PropTypes.node,
|
|
50
|
+
className: PropTypes.string,
|
|
51
|
+
onChange: PropTypes.func,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
CTabs.displayName = 'CTabs'
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import { CTab } from './CTab'
|
|
1
2
|
import { CTabContent } from './CTabContent'
|
|
2
3
|
import { CTabPane } from './CTabPane'
|
|
4
|
+
import { CTabPanel } from './CTabPanel'
|
|
5
|
+
import { CTabList } from './CTabList'
|
|
6
|
+
import { CTabs } from './CTabs'
|
|
3
7
|
|
|
4
|
-
export { CTabContent, CTabPane }
|
|
8
|
+
export { CTab, CTabContent, CTabList, CTabPane, CTabPanel, CTabs }
|