react-tab-set 1.2.4 → 2.0.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.
- package/README.md +2 -2
- package/package.json +2 -1
- package/src/tab-set/index.tsx +78 -116
- package/src/tab-set/is-tab-group.mts +1 -1
- package/src/tab-set/is-tab-panel.mts +1 -1
- package/src/tab-set/tab-group/index.tsx +54 -71
- package/src/tab-set/tab-group/tab/index.tsx +27 -45
- package/src/tab-set/tab-panel/index.tsx +13 -37
package/README.md
CHANGED
|
@@ -47,7 +47,7 @@ Content can be declared either as children or as the return of a function assign
|
|
|
47
47
|
|
|
48
48
|
If your component extends `PureComponent` or is presentational, you might prefer to declare content as children.
|
|
49
49
|
|
|
50
|
-
Otherwise, you
|
|
50
|
+
Otherwise, you can assign a function to the `render` prop, so that rendering is deferred until the tab is selected.
|
|
51
51
|
|
|
52
52
|
### Content as children
|
|
53
53
|
|
|
@@ -99,7 +99,7 @@ Similarly, they can be declared in any combination.
|
|
|
99
99
|
|
|
100
100
|
## Demonstrating _react-tab-set_
|
|
101
101
|
|
|
102
|
-
[Example `Storybooks` are available
|
|
102
|
+
[Example `Storybooks` are available](https://github.com/sequencemedia/react-tab-set).
|
|
103
103
|
|
|
104
104
|
Clone the repository, then:
|
|
105
105
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-tab-set",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "A React Tab Set component",
|
|
5
5
|
"main": "./src/index.mjs",
|
|
6
6
|
"type": "module",
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"@babel/register": "^7.22.15",
|
|
40
40
|
"@sequencemedia/hooks": "^1.0.454",
|
|
41
41
|
"@storybook/addon-essentials": "^7.4.4",
|
|
42
|
+
"@storybook/addon-links": "^7.4.4",
|
|
42
43
|
"@storybook/react": "^7.4.4",
|
|
43
44
|
"@storybook/react-webpack5": "^7.4.4",
|
|
44
45
|
"@types/debug": "^4.1.9",
|
package/src/tab-set/index.tsx
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, {
|
|
2
|
+
Children,
|
|
3
|
+
cloneElement,
|
|
4
|
+
useState,
|
|
5
|
+
useEffect
|
|
6
|
+
} from 'react'
|
|
2
7
|
import {
|
|
3
8
|
v4
|
|
4
9
|
} from 'uuid'
|
|
@@ -10,132 +15,89 @@ import isTabGroup from './is-tab-group.mts'
|
|
|
10
15
|
const error = debug('react-tab-set/tab-set')
|
|
11
16
|
|
|
12
17
|
export interface TabSetProps {
|
|
13
|
-
onClick: (selectedTab: string) => void
|
|
14
|
-
onChange: (selectedTab: string) => void
|
|
15
|
-
selectedTab: string
|
|
16
18
|
children: JSX.Element | JSX.Element[]
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface TabSetState {
|
|
20
19
|
selectedTab: string
|
|
20
|
+
onChange: (selectedTab: string) => void
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
23
|
+
function mapChildren (
|
|
24
|
+
children: JSX.Element | JSX.Element[],
|
|
25
|
+
selectedTab: string,
|
|
26
|
+
onTabSelect: (value: React.SetStateAction<string>) => void
|
|
27
|
+
): JSX.Element[] {
|
|
28
|
+
return Children.map(children, (child) => {
|
|
29
|
+
const { type } = child
|
|
30
|
+
|
|
31
|
+
if (type) { // eslint-disable-line @typescript-eslint/strict-boolean-expressions
|
|
32
|
+
const { props } = child
|
|
33
|
+
|
|
34
|
+
if (isTabGroup(type)) {
|
|
35
|
+
return cloneElement(
|
|
36
|
+
child,
|
|
37
|
+
{
|
|
38
|
+
...props,
|
|
39
|
+
selectedTab,
|
|
40
|
+
onTabSelect
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (isTabPanel(type)) {
|
|
46
|
+
return cloneElement(
|
|
47
|
+
child,
|
|
48
|
+
{
|
|
49
|
+
...props,
|
|
50
|
+
selectedTab
|
|
51
|
+
}
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const {
|
|
56
|
+
children
|
|
57
|
+
} = props
|
|
58
|
+
|
|
59
|
+
if (children) { // eslint-disable-line @typescript-eslint/strict-boolean-expressions
|
|
60
|
+
return cloneElement(
|
|
61
|
+
child,
|
|
62
|
+
{
|
|
63
|
+
...props,
|
|
64
|
+
children: mapChildren(children, selectedTab, onTabSelect)
|
|
65
|
+
}
|
|
66
|
+
)
|
|
67
|
+
}
|
|
43
68
|
}
|
|
44
|
-
}
|
|
45
69
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
70
|
+
return child
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export default function TabSet (props: TabSetProps): JSX.Element {
|
|
75
|
+
const {
|
|
76
|
+
children,
|
|
77
|
+
selectedTab: tab
|
|
78
|
+
} = props
|
|
79
|
+
|
|
80
|
+
const [selectedTab, setSelectedTab] = useState(tab)
|
|
81
|
+
|
|
82
|
+
useEffect(() => { setSelectedTab(tab) }, [tab])
|
|
52
83
|
|
|
53
|
-
|
|
54
|
-
const {
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
const { onChange } = props
|
|
55
86
|
|
|
56
87
|
try {
|
|
57
|
-
|
|
88
|
+
onChange(selectedTab)
|
|
58
89
|
} catch {
|
|
59
|
-
error('Error `
|
|
90
|
+
error('Error `onChange`')
|
|
60
91
|
}
|
|
92
|
+
}, [selectedTab])
|
|
61
93
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
} catch {
|
|
69
|
-
error('Error `onChange`')
|
|
70
|
-
}
|
|
71
|
-
})
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
mapChildren (children: JSX.Element | JSX.Element[], selectedTab: string): JSX.Element[] {
|
|
76
|
-
return Children.map(children, (child) => {
|
|
77
|
-
const { type } = child
|
|
78
|
-
|
|
79
|
-
if (type) { // eslint-disable-line @typescript-eslint/strict-boolean-expressions
|
|
80
|
-
const { props } = child
|
|
81
|
-
|
|
82
|
-
if (isTabGroup(type)) {
|
|
83
|
-
return cloneElement(
|
|
84
|
-
child,
|
|
85
|
-
{
|
|
86
|
-
...props,
|
|
87
|
-
selectedTab,
|
|
88
|
-
onTabSelect: this.handleTabSelect
|
|
89
|
-
}
|
|
90
|
-
)
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (isTabPanel(type)) {
|
|
94
|
-
return cloneElement(
|
|
95
|
-
child,
|
|
96
|
-
{
|
|
97
|
-
...props,
|
|
98
|
-
selectedTab
|
|
99
|
-
}
|
|
100
|
-
)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const {
|
|
104
|
-
children
|
|
105
|
-
} = props
|
|
106
|
-
|
|
107
|
-
if (children) { // eslint-disable-line @typescript-eslint/strict-boolean-expressions
|
|
108
|
-
return cloneElement(
|
|
109
|
-
child,
|
|
110
|
-
{
|
|
111
|
-
...props,
|
|
112
|
-
children: this.mapChildren(children, selectedTab)
|
|
113
|
-
}
|
|
114
|
-
)
|
|
115
|
-
}
|
|
116
|
-
}
|
|
94
|
+
return (
|
|
95
|
+
<div className='selectedTab-set'>
|
|
96
|
+
{mapChildren(children, selectedTab, setSelectedTab)}
|
|
97
|
+
</div>
|
|
98
|
+
)
|
|
99
|
+
}
|
|
117
100
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
getChildren (): JSX.Element[] {
|
|
123
|
-
const {
|
|
124
|
-
children
|
|
125
|
-
} = this.props
|
|
126
|
-
|
|
127
|
-
const {
|
|
128
|
-
selectedTab
|
|
129
|
-
} = this.state
|
|
130
|
-
|
|
131
|
-
return this.mapChildren(children, selectedTab)
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
render (): JSX.Element {
|
|
135
|
-
return (
|
|
136
|
-
<div className='tab-set'>
|
|
137
|
-
{this.getChildren()}
|
|
138
|
-
</div>
|
|
139
|
-
)
|
|
140
|
-
}
|
|
101
|
+
TabSet.defaultProps = {
|
|
102
|
+
selectedTab: v4()
|
|
141
103
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, {
|
|
2
|
+
Children,
|
|
3
|
+
cloneElement
|
|
4
|
+
} from 'react'
|
|
2
5
|
import {
|
|
3
6
|
v4
|
|
4
7
|
} from 'uuid'
|
|
@@ -6,87 +9,67 @@ import {
|
|
|
6
9
|
import isTab from './is-tab.mts'
|
|
7
10
|
|
|
8
11
|
export interface TabGroupProps {
|
|
9
|
-
onTabSelect: (selectedTab: string) => void
|
|
10
12
|
children: JSX.Element | JSX.Element[]
|
|
11
13
|
selectedTab: string
|
|
14
|
+
onTabSelect: (value: React.SetStateAction<string>) => void
|
|
12
15
|
}
|
|
13
16
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
onTabSelect () {},
|
|
22
|
-
selectedTab: v4(),
|
|
23
|
-
children: []
|
|
24
|
-
}
|
|
17
|
+
function mapChildren (
|
|
18
|
+
children: JSX.Element | JSX.Element[],
|
|
19
|
+
selectedTab: string,
|
|
20
|
+
onTabSelect: (value: React.SetStateAction<string>) => void
|
|
21
|
+
): JSX.Element[] {
|
|
22
|
+
return Children.map(children, (child) => {
|
|
23
|
+
const { type } = child
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
props.children !== this.props.children ||
|
|
29
|
-
props.selectedTab !== this.props.selectedTab
|
|
30
|
-
)
|
|
31
|
-
}
|
|
25
|
+
if (type) { // eslint-disable-line @typescript-eslint/strict-boolean-expressions
|
|
26
|
+
const { props } = child
|
|
32
27
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (type) { // eslint-disable-line @typescript-eslint/strict-boolean-expressions
|
|
44
|
-
const { props } = child
|
|
45
|
-
|
|
46
|
-
if (isTab(type)) {
|
|
47
|
-
return cloneElement(
|
|
48
|
-
child,
|
|
49
|
-
{
|
|
50
|
-
...props,
|
|
51
|
-
selectedTab,
|
|
52
|
-
onTabClick: this.handleTabClick
|
|
53
|
-
}
|
|
54
|
-
)
|
|
55
|
-
}
|
|
28
|
+
if (isTab(type)) {
|
|
29
|
+
return cloneElement(
|
|
30
|
+
child,
|
|
31
|
+
{
|
|
32
|
+
...props,
|
|
33
|
+
selectedTab,
|
|
34
|
+
onTabSelect
|
|
35
|
+
}
|
|
36
|
+
)
|
|
37
|
+
}
|
|
56
38
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
39
|
+
const {
|
|
40
|
+
children
|
|
41
|
+
} = props
|
|
60
42
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
43
|
+
if (children) { // eslint-disable-line @typescript-eslint/strict-boolean-expressions
|
|
44
|
+
return cloneElement(
|
|
45
|
+
child,
|
|
46
|
+
{
|
|
47
|
+
...props,
|
|
48
|
+
children: mapChildren(children, selectedTab, onTabSelect)
|
|
49
|
+
}
|
|
50
|
+
)
|
|
70
51
|
}
|
|
52
|
+
}
|
|
71
53
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
54
|
+
return child
|
|
55
|
+
})
|
|
56
|
+
}
|
|
75
57
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
58
|
+
export default function TabGroup (props: TabGroupProps): JSX.Element {
|
|
59
|
+
const {
|
|
60
|
+
children,
|
|
61
|
+
selectedTab,
|
|
62
|
+
onTabSelect
|
|
63
|
+
} = props
|
|
81
64
|
|
|
82
|
-
|
|
83
|
-
|
|
65
|
+
return (
|
|
66
|
+
<ul className='tab-group'>
|
|
67
|
+
{mapChildren(children, selectedTab, onTabSelect)}
|
|
68
|
+
</ul>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
84
71
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
{this.getChildren()}
|
|
89
|
-
</ul>
|
|
90
|
-
)
|
|
91
|
-
}
|
|
72
|
+
TabGroup.defaultProps = {
|
|
73
|
+
selectedTab: v4(),
|
|
74
|
+
onTabSelect () { }
|
|
92
75
|
}
|
|
@@ -1,57 +1,39 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from 'react'
|
|
2
2
|
import {
|
|
3
3
|
v4
|
|
4
4
|
} from 'uuid'
|
|
5
5
|
|
|
6
6
|
export interface TabProps {
|
|
7
|
-
onTabClick: (tab: string) => void
|
|
8
7
|
children: JSX.Element | JSX.Element[] | string | number | boolean
|
|
9
8
|
tab: string
|
|
10
9
|
selectedTab: string
|
|
10
|
+
onTabSelect: (value: React.SetStateAction<string>) => void
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export default
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const { tab, onTabClick } = this.props
|
|
36
|
-
|
|
37
|
-
onTabClick(tab)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
render (): JSX.Element {
|
|
41
|
-
const {
|
|
42
|
-
tab,
|
|
43
|
-
selectedTab,
|
|
44
|
-
children
|
|
45
|
-
} = this.props
|
|
46
|
-
|
|
47
|
-
const className = (tab === selectedTab)
|
|
48
|
-
? 'tab selected'
|
|
49
|
-
: 'tab'
|
|
13
|
+
export default function Tab (props: TabProps): JSX.Element {
|
|
14
|
+
const {
|
|
15
|
+
children,
|
|
16
|
+
tab,
|
|
17
|
+
selectedTab,
|
|
18
|
+
onTabSelect
|
|
19
|
+
} = props
|
|
20
|
+
|
|
21
|
+
const className = (tab === selectedTab)
|
|
22
|
+
? 'tab selected'
|
|
23
|
+
: 'tab'
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<li
|
|
27
|
+
className={className}
|
|
28
|
+
onClick={(): void => {
|
|
29
|
+
onTabSelect(tab)
|
|
30
|
+
}}>
|
|
31
|
+
{children}
|
|
32
|
+
</li>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
50
35
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
</li>
|
|
55
|
-
)
|
|
56
|
-
}
|
|
36
|
+
Tab.defaultProps = {
|
|
37
|
+
selectedTab: v4(),
|
|
38
|
+
onTabSelect () { }
|
|
57
39
|
}
|
|
@@ -1,41 +1,26 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from 'react'
|
|
2
2
|
import {
|
|
3
3
|
v4
|
|
4
4
|
} from 'uuid'
|
|
5
5
|
|
|
6
6
|
export interface TabPanelProps {
|
|
7
|
-
children
|
|
7
|
+
children?: JSX.Element | JSX.Element[] | string | number | boolean | undefined | null
|
|
8
8
|
tab: string
|
|
9
9
|
selectedTab: string
|
|
10
10
|
render?: () => JSX.Element | JSX.Element[] | string | number | boolean | undefined | null
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export default
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
*/
|
|
19
|
-
static defaultProps = {
|
|
20
|
-
tab: v4(),
|
|
21
|
-
selectedTab: v4()
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
shouldComponentUpdate (props: TabPanelProps): boolean {
|
|
25
|
-
if (props.render instanceof Function) return true
|
|
13
|
+
export default function TabPanel (props: TabPanelProps): JSX.Element | null {
|
|
14
|
+
const {
|
|
15
|
+
tab,
|
|
16
|
+
selectedTab
|
|
17
|
+
} = props
|
|
26
18
|
|
|
27
|
-
|
|
28
|
-
props.children !== this.props.children ||
|
|
29
|
-
props.tab !== this.props.tab ||
|
|
30
|
-
props.selectedTab !== this.props.selectedTab
|
|
31
|
-
)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
renderPanel (): JSX.Element | null {
|
|
19
|
+
if (tab === selectedTab) {
|
|
35
20
|
const {
|
|
36
21
|
render = () => null,
|
|
37
22
|
children = render()
|
|
38
|
-
} =
|
|
23
|
+
} = props
|
|
39
24
|
|
|
40
25
|
if (children) { // eslint-disable-line @typescript-eslint/strict-boolean-expressions
|
|
41
26
|
return (
|
|
@@ -44,20 +29,11 @@ export default class TabPanel extends Component<TabPanelProps> {
|
|
|
44
29
|
</div>
|
|
45
30
|
)
|
|
46
31
|
}
|
|
47
|
-
|
|
48
|
-
return null
|
|
49
32
|
}
|
|
50
33
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
tab,
|
|
54
|
-
selectedTab
|
|
55
|
-
} = this.props
|
|
56
|
-
|
|
57
|
-
if (tab === selectedTab) {
|
|
58
|
-
return this.renderPanel()
|
|
59
|
-
}
|
|
34
|
+
return null
|
|
35
|
+
}
|
|
60
36
|
|
|
61
|
-
|
|
62
|
-
|
|
37
|
+
TabPanel.defaultProps = {
|
|
38
|
+
selectedTab: v4()
|
|
63
39
|
}
|