Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commite0f155f

Browse files
committed
refactor(CTabs): fully implement a controlled/uncontrolled pattern
1 parentf451d62 commite0f155f

15 files changed

+184
-34
lines changed

‎packages/coreui-react/src/components/tabs/CTab.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React, { forwardRef, HTMLAttributes, useContext } from 'react'
22
importPropTypesfrom'prop-types'
33
importclassNamesfrom'classnames'
44

5-
import{TabsContext}from'./CTabs'
5+
import{CTabsContext}from'./CTabsContext'
66

77
exportinterfaceCTabPropsextendsHTMLAttributes<HTMLButtonElement>{
88
/**
@@ -21,7 +21,7 @@ export interface CTabProps extends HTMLAttributes<HTMLButtonElement> {
2121

2222
exportconstCTab=forwardRef<HTMLButtonElement,CTabProps>(
2323
({ children, className, itemKey, ...rest},ref)=>{
24-
const{ _activeItemKey, setActiveItemKey, id}=useContext(TabsContext)
24+
const{ _activeItemKey, setActiveItemKey, id}=useContext(CTabsContext)
2525

2626
constisActive=()=>itemKey===_activeItemKey
2727

‎packages/coreui-react/src/components/tabs/CTabPanel.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types'
33
importclassNamesfrom'classnames'
44
import{Transition}from'react-transition-group'
55

6-
import{TabsContext}from'./CTabs'
6+
import{CTabsContext}from'./CTabsContext'
77
import{useForkedRef}from'../../hooks'
88
import{getTransitionDurationFromElement}from'../../utils'
99

@@ -36,7 +36,7 @@ export interface CTabPanelProps extends HTMLAttributes<HTMLDivElement> {
3636

3737
exportconstCTabPanel=forwardRef<HTMLDivElement,CTabPanelProps>(
3838
({ children, className, itemKey, onHide, onShow, transition=true, visible, ...rest},ref)=>{
39-
const{ _activeItemKey, id}=useContext(TabsContext)
39+
const{ _activeItemKey, id}=useContext(CTabsContext)
4040

4141
consttabPaneRef=useRef(null)
4242
constforkedRef=useForkedRef(ref,tabPaneRef)

‎packages/coreui-react/src/components/tabs/CTabs.tsx‎

Lines changed: 54 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,86 @@
1-
importReact,{createContext,forwardRef,HTMLAttributes,useEffect,useId,useState}from'react'
1+
importReact,{forwardRef,HTMLAttributes,useId,useState}from'react'
22
importPropTypesfrom'prop-types'
33
importclassNamesfrom'classnames'
44

5+
import{CTabsContext}from'./CTabsContext'
6+
57
exportinterfaceCTabsPropsextendsOmit<HTMLAttributes<HTMLDivElement>,'onChange'>{
68
/**
7-
* The active item key.
9+
* Controls the currently active tab.
10+
*
11+
* When provided, the component operates in a controlled mode.
12+
* You must handle tab switching manually by updating this prop.
13+
*
14+
*@example
15+
* const [activeTab, setActiveTab] = useState(0);
16+
* <CTabs activeItemKey={activeTab} onChange={setActiveTab} />
817
*/
9-
activeItemKey:number|string
18+
activeItemKey?:number|string
19+
1020
/**
1121
* A string of all className you want applied to the base component.
1222
*/
1323
className?:string
24+
1425
/**
15-
* The callback is fired when the active tab changes.
26+
* Sets the initially active tab when the component mounts.
27+
*
28+
* After initialization, the component manages active tab changes internally.
29+
*
30+
* Use `defaultActiveItemKey` for uncontrolled usage.
31+
*
32+
*@example
33+
* <CTabs defaultActiveItemKey={1} />
1634
*/
17-
onChange?:(value:number|string)=>void
18-
}
35+
defaultActiveItemKey?:number|string
1936

20-
exportinterfaceTabsContextProps{
21-
_activeItemKey?:number|string
22-
setActiveItemKey:React.Dispatch<React.SetStateAction<number|string|undefined>>
23-
id?:string
37+
/**
38+
* Callback fired when the active tab changes.
39+
*
40+
* - In controlled mode (`activeItemKey` provided), you must update `activeItemKey` yourself based on the value received.
41+
* - In uncontrolled mode, this callback is called after internal state updates.
42+
*
43+
*@param value - The newly selected tab key.
44+
*
45+
*@example
46+
* <CTabs onChange={(key) => console.log('Tab changed to', key)} />
47+
*/
48+
onChange?:(value:number|string)=>void
2449
}
2550

26-
exportconstTabsContext=createContext({}asTabsContextProps)
27-
2851
exportconstCTabs=forwardRef<HTMLDivElement,CTabsProps>(
29-
({ children, activeItemKey, className, onChange},ref)=>{
52+
({ children, activeItemKey, className,defaultActiveItemKey,onChange},ref)=>{
3053
constid=useId()
31-
const[_activeItemKey,setActiveItemKey]=useState(activeItemKey)
54+
constisControlled=activeItemKey!==undefined
55+
const[internalActiveItemKey,setInternalActiveItemKey]=useState<number|string|undefined>(
56+
()=>(isControlled ?undefined :defaultActiveItemKey)
57+
)
58+
59+
constcurrentActiveItemKey=isControlled ?activeItemKey :internalActiveItemKey
60+
61+
constsetActiveItemKey=(value:number|string)=>{
62+
if(!isControlled){
63+
setInternalActiveItemKey(value)
64+
}
3265

33-
useEffect(()=>{
34-
_activeItemKey&&onChange&&onChange(_activeItemKey)
35-
},[_activeItemKey])
66+
onChange?.(value)
67+
}
3668

3769
return(
38-
<TabsContext.Providervalue={{ _activeItemKey, setActiveItemKey, id}}>
70+
<CTabsContext.Providervalue={{_activeItemKey:currentActiveItemKey, setActiveItemKey, id}}>
3971
<divclassName={classNames('tabs',className)}ref={ref}>
4072
{children}
4173
</div>
42-
</TabsContext.Provider>
74+
</CTabsContext.Provider>
4375
)
44-
},
76+
}
4577
)
4678

4779
CTabs.propTypes={
48-
activeItemKey:PropTypes.oneOfType([PropTypes.number,PropTypes.string]).isRequired,
80+
activeItemKey:PropTypes.oneOfType([PropTypes.number,PropTypes.string]),
4981
children:PropTypes.node,
5082
className:PropTypes.string,
83+
defaultActiveItemKey:PropTypes.oneOfType([PropTypes.number,PropTypes.string]),
5184
onChange:PropTypes.func,
5285
}
5386

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import{createContext}from'react'
2+
3+
exportinterfaceCTabsContextProps{
4+
_activeItemKey?:number|string
5+
setActiveItemKey:React.Dispatch<React.SetStateAction<number|string|undefined>>
6+
id?:string
7+
}
8+
9+
exportconstCTabsContext=createContext({}asCTabsContextProps)

‎packages/docs/content/api/CTabs.api.mdx‎

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ import CTabs from '@coreui/react/src/components/tabs/CTabs'
2222
</tr>
2323
<tr>
2424
<tdcolSpan="3">
25-
<p>The active item key.</p>
25+
<p>Controls the currently active tab.</p>
26+
<p>When provided, the component operates in a controlled mode.<br />
27+
You must handle tab switching manually by updating this prop.</p>
28+
<JSXDocscode={`const [activeTab, setActiveTab] = useState(0);
29+
<CTabs activeItemKey={activeTab} onChange={setActiveTab} />`} />
2630
</td>
2731
</tr>
2832
<trid="ctabs-class-name">
@@ -35,14 +39,32 @@ import CTabs from '@coreui/react/src/components/tabs/CTabs'
3539
<p>A string of all className you want applied to the base component.</p>
3640
</td>
3741
</tr>
42+
<trid="ctabs-default-active-item-key">
43+
<tdclassName="text-primary fw-semibold">defaultActiveItemKey<ahref="#ctabs-default-active-item-key"aria-label="CTabs defaultActiveItemKey permalink"className="anchor-link after">#</a></td>
44+
<td>-</td>
45+
<td><code>{`string`}</code>, <code>{`number`}</code></td>
46+
</tr>
47+
<tr>
48+
<tdcolSpan="3">
49+
<p>Sets the initially active tab when the component mounts.</p>
50+
<p>After initialization, the component manages active tab changes internally.</p>
51+
<p>Use <code>{`defaultActiveItemKey`}</code> for uncontrolled usage.</p>
52+
<JSXDocscode={`<CTabs defaultActiveItemKey={1} />`} />
53+
</td>
54+
</tr>
3855
<trid="ctabs-on-change">
3956
<tdclassName="text-primary fw-semibold">onChange<ahref="#ctabs-on-change"aria-label="CTabs onChange permalink"className="anchor-link after">#</a></td>
4057
<td>-</td>
4158
<td><code>{`(value: string | number) => void`}</code></td>
4259
</tr>
4360
<tr>
4461
<tdcolSpan="3">
45-
<p>The callback is fired when the active tab changes.</p>
62+
<p>Callback fired when the active tab changes.</p>
63+
<ul>
64+
<li>In controlled mode (<code>{`activeItemKey`}</code> provided), you must update <code>{`activeItemKey`}</code> yourself based on the value received.</li>
65+
<li>In uncontrolled mode, this callback is called after internal state updates.</li>
66+
</ul>
67+
<JSXDocscode={`<CTabs onChange={(key) => console.log('Tab changed to', key)} />`} />
4668
</td>
4769
</tr>
4870
</tbody>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
importReact,{useState}from'react'
2+
import{CTab,CTabContent,CTabList,CTabPanel,CTabs}from'@coreui/react'
3+
4+
exportconstTabsControlledExample=()=>{
5+
const[activeTab,setActiveTab]=useState('profile')
6+
7+
return(
8+
<CTabsactiveItemKey={activeTab}onChange={setActiveTab}>
9+
<CTabListvariant="tabs">
10+
<CTabitemKey="home">Home</CTab>
11+
<CTabitemKey="profile">Profile</CTab>
12+
<CTabitemKey="contact">Contact</CTab>
13+
<CTabdisableditemKey="disabled">
14+
Disabled
15+
</CTab>
16+
</CTabList>
17+
<CTabContent>
18+
<CTabPanelclassName="p-3"itemKey="home">
19+
Home tab content
20+
</CTabPanel>
21+
<CTabPanelclassName="p-3"itemKey="profile">
22+
Profile tab content
23+
</CTabPanel>
24+
<CTabPanelclassName="p-3"itemKey="contact">
25+
Contact tab content
26+
</CTabPanel>
27+
<CTabPanelclassName="p-3"itemKey="disabled">
28+
Disabled tab content
29+
</CTabPanel>
30+
</CTabContent>
31+
</CTabs>
32+
)
33+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
importReact,{useState}from'react'
2+
import{CTab,CTabContent,CTabList,CTabPanel,CTabs}from'@coreui/react'
3+
4+
exportconstTabsControlledExample=()=>{
5+
const[activeTab,setActiveTab]=useState<number|string>('profile')
6+
7+
return(
8+
<CTabsactiveItemKey={activeTab}onChange={setActiveTab}>
9+
<CTabListvariant="tabs">
10+
<CTabitemKey="home">Home</CTab>
11+
<CTabitemKey="profile">Profile</CTab>
12+
<CTabitemKey="contact">Contact</CTab>
13+
<CTabdisableditemKey="disabled">
14+
Disabled
15+
</CTab>
16+
</CTabList>
17+
<CTabContent>
18+
<CTabPanelclassName="p-3"itemKey="home">
19+
Home tab content
20+
</CTabPanel>
21+
<CTabPanelclassName="p-3"itemKey="profile">
22+
Profile tab content
23+
</CTabPanel>
24+
<CTabPanelclassName="p-3"itemKey="contact">
25+
Contact tab content
26+
</CTabPanel>
27+
<CTabPanelclassName="p-3"itemKey="disabled">
28+
Disabled tab content
29+
</CTabPanel>
30+
</CTabContent>
31+
</CTabs>
32+
)
33+
}

‎packages/docs/content/components/tabs/examples/TabsExample.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { CTab, CTabContent, CTabList, CTabPanel, CTabs } from '@coreui/react'
33

44
exportconstTabsExample=()=>{
55
return(
6-
<CTabsactiveItemKey="profile">
6+
<CTabsdefaultActiveItemKey="profile">
77
<CTabListvariant="tabs">
88
<CTabitemKey="home">Home</CTab>
99
<CTabitemKey="profile">Profile</CTab>

‎packages/docs/content/components/tabs/examples/TabsPillsExample.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { CTab, CTabContent, CTabList, CTabPanel, CTabs } from '@coreui/react'
33

44
exportconstTabsPillsExample=()=>{
55
return(
6-
<CTabsactiveItemKey={2}>
6+
<CTabsdefaultActiveItemKey={2}>
77
<CTabListvariant="pills">
88
<CTabaria-controls="home-tab-pane"itemKey={1}>Home</CTab>
99
<CTabaria-controls="profile-tab-pane"itemKey={2}>Profile</CTab>

‎packages/docs/content/components/tabs/examples/TabsUnderlineBorderExample.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { CTab, CTabContent, CTabList, CTabPanel, CTabs } from '@coreui/react'
33

44
exportconstTabsUnderlineBorderExample=()=>{
55
return(
6-
<CTabsactiveItemKey={2}>
6+
<CTabsdefaultActiveItemKey={2}>
77
<CTabListvariant="underline-border">
88
<CTabaria-controls="home-tab-pane"itemKey={1}>Home</CTab>
99
<CTabaria-controls="profile-tab-pane"itemKey={2}>Profile</CTab>

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp