@oslokommune/punkt-react 13.7.0 → 13.8.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/CHANGELOG.md +36 -0
- package/dist/index.d.ts +20 -2
- package/dist/punkt-react.es.js +1713 -1705
- package/dist/punkt-react.umd.js +191 -191
- package/package.json +3 -3
- package/src/components/combobox/Combobox.tsx +1 -1
- package/src/components/index.ts +1 -0
- package/src/components/tabs/TabItem.tsx +77 -0
- package/src/components/tabs/Tabs.test.tsx +393 -1
- package/src/components/tabs/Tabs.tsx +86 -65
|
@@ -1,11 +1,38 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import {
|
|
4
|
+
forwardRef,
|
|
5
|
+
Ref,
|
|
6
|
+
useRef,
|
|
7
|
+
useEffect,
|
|
8
|
+
KeyboardEvent,
|
|
9
|
+
ReactNode,
|
|
10
|
+
Children,
|
|
11
|
+
createContext,
|
|
12
|
+
useContext,
|
|
13
|
+
} from 'react'
|
|
14
|
+
import { PktTabItem } from './TabItem'
|
|
6
15
|
|
|
7
16
|
export type TSkin = 'blue' | 'green' | 'red' | 'beige' | 'yellow' | 'grey' | 'gray' | 'blue-light'
|
|
8
17
|
|
|
18
|
+
// Context for passing tab navigation logic to children
|
|
19
|
+
interface ITabsContext {
|
|
20
|
+
arrowNav: boolean
|
|
21
|
+
registerTabRef: (index: number, el: HTMLAnchorElement | HTMLButtonElement | null) => void
|
|
22
|
+
handleKeyPress: (index: number, event: KeyboardEvent) => void
|
|
23
|
+
selectTab: (index: number) => void
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const TabsContext = createContext<ITabsContext | null>(null)
|
|
27
|
+
|
|
28
|
+
export const useTabsContext = () => {
|
|
29
|
+
const context = useContext(TabsContext)
|
|
30
|
+
if (!context) {
|
|
31
|
+
throw new Error('TabItem must be used within a Tabs component')
|
|
32
|
+
}
|
|
33
|
+
return context
|
|
34
|
+
}
|
|
35
|
+
|
|
9
36
|
export interface IPktTab {
|
|
10
37
|
text: string
|
|
11
38
|
href?: string
|
|
@@ -21,88 +48,82 @@ export interface IPktTab {
|
|
|
21
48
|
|
|
22
49
|
export interface IPktTabs {
|
|
23
50
|
arrowNav?: boolean
|
|
24
|
-
|
|
51
|
+
disableArrowNav?: boolean
|
|
52
|
+
tabs?: IPktTab[]
|
|
25
53
|
onTabSelected?: (index: number) => void
|
|
54
|
+
children?: ReactNode
|
|
26
55
|
}
|
|
27
56
|
|
|
28
57
|
export const PktTabs = forwardRef(
|
|
29
|
-
(
|
|
58
|
+
(
|
|
59
|
+
{ arrowNav = true, disableArrowNav = false, tabs, onTabSelected, children }: IPktTabs,
|
|
60
|
+
ref: Ref<HTMLDivElement>,
|
|
61
|
+
): JSX.Element => {
|
|
30
62
|
const tabRefs = useRef<Array<HTMLAnchorElement | HTMLButtonElement | null>>([])
|
|
63
|
+
|
|
64
|
+
const useArrowNav = arrowNav && !disableArrowNav
|
|
65
|
+
|
|
66
|
+
// Determine if we're using children or tabs array
|
|
67
|
+
const hasChildren = children && Children.count(children) > 0
|
|
68
|
+
const tabCount = hasChildren ? Children.count(children) : tabs?.length || 0
|
|
69
|
+
|
|
31
70
|
useEffect(() => {
|
|
32
|
-
tabRefs.current = tabRefs.current.slice(0,
|
|
33
|
-
}, [
|
|
34
|
-
|
|
35
|
-
|
|
71
|
+
tabRefs.current = tabRefs.current.slice(0, tabCount)
|
|
72
|
+
}, [tabCount])
|
|
73
|
+
|
|
74
|
+
const selectTab = (index: number): void => {
|
|
75
|
+
const tab = tabs?.[index]
|
|
76
|
+
if (tab?.action) {
|
|
77
|
+
tab.action(index)
|
|
78
|
+
}
|
|
36
79
|
if (onTabSelected) onTabSelected(index)
|
|
37
80
|
}
|
|
81
|
+
|
|
38
82
|
const handleKeyPress = (index: number, event: KeyboardEvent) => {
|
|
39
|
-
if (
|
|
83
|
+
if (useArrowNav) {
|
|
40
84
|
if (event.code === 'ArrowLeft' && index !== 0) {
|
|
41
85
|
tabRefs.current[index - 1]?.focus()
|
|
42
86
|
}
|
|
43
|
-
if (event.code === 'ArrowRight' && index <
|
|
87
|
+
if (event.code === 'ArrowRight' && index < tabCount - 1) {
|
|
44
88
|
tabRefs.current[index + 1]?.focus()
|
|
45
89
|
}
|
|
46
90
|
if (event.code === 'ArrowDown' || event.code === 'Space') {
|
|
47
|
-
selectTab(index
|
|
91
|
+
selectTab(index)
|
|
48
92
|
}
|
|
49
93
|
}
|
|
50
94
|
}
|
|
95
|
+
|
|
96
|
+
const registerTabRef = (index: number, el: HTMLAnchorElement | HTMLButtonElement | null) => {
|
|
97
|
+
tabRefs.current[index] = el
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// If tabs as prop instead of children
|
|
101
|
+
const tabItems = tabs?.map((tab, index) => (
|
|
102
|
+
<PktTabItem
|
|
103
|
+
key={index}
|
|
104
|
+
active={tab.active}
|
|
105
|
+
href={tab.href}
|
|
106
|
+
onClick={() => selectTab(index)}
|
|
107
|
+
icon={tab.icon}
|
|
108
|
+
controls={tab.controls}
|
|
109
|
+
tag={tab.tag?.text}
|
|
110
|
+
tagSkin={tab.tag?.skin}
|
|
111
|
+
index={index}
|
|
112
|
+
>
|
|
113
|
+
{tab.text}
|
|
114
|
+
</PktTabItem>
|
|
115
|
+
))
|
|
116
|
+
|
|
51
117
|
return (
|
|
52
|
-
<
|
|
53
|
-
<div className="pkt-
|
|
54
|
-
{
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
<a
|
|
58
|
-
aria-selected={!!tab.active}
|
|
59
|
-
role={arrowNav ? 'tab' : undefined}
|
|
60
|
-
href={tab.href}
|
|
61
|
-
aria-controls={tab.controls}
|
|
62
|
-
className={`pkt-tabs__link ${tab.active ? 'active' : ''}`}
|
|
63
|
-
onKeyUp={(event) => handleKeyPress(index, event)}
|
|
64
|
-
onClick={() => selectTab(index, tab)}
|
|
65
|
-
tabIndex={tab.active || !arrowNav ? undefined : -1}
|
|
66
|
-
ref={(el) => {
|
|
67
|
-
tabRefs.current[index] = el
|
|
68
|
-
}}
|
|
69
|
-
>
|
|
70
|
-
{tab.icon && <PktIcon name={tab.icon} className="pkt-icon--small" />}
|
|
71
|
-
{tab.text}
|
|
72
|
-
{tab.tag && (
|
|
73
|
-
<PktTag skin={tab.tag.skin} size="small">
|
|
74
|
-
{tab.tag.text}
|
|
75
|
-
</PktTag>
|
|
76
|
-
)}
|
|
77
|
-
</a>
|
|
78
|
-
) : (
|
|
79
|
-
<button
|
|
80
|
-
aria-selected={!!tab.active}
|
|
81
|
-
role={arrowNav ? 'tab' : undefined}
|
|
82
|
-
type="button"
|
|
83
|
-
aria-controls={tab.controls}
|
|
84
|
-
className={`pkt-tabs__button pkt-link-button ${tab.active ? 'active' : ''}`}
|
|
85
|
-
key={'b-' + index}
|
|
86
|
-
onKeyUp={(event) => handleKeyPress(index, event)}
|
|
87
|
-
onClick={() => selectTab(index, tab)}
|
|
88
|
-
tabIndex={tab.active || !arrowNav ? undefined : -1}
|
|
89
|
-
ref={(el) => {
|
|
90
|
-
tabRefs.current[index] = el
|
|
91
|
-
}}
|
|
92
|
-
>
|
|
93
|
-
{tab.icon && <PktIcon name={tab.icon} className="pkt-icon--small" />}
|
|
94
|
-
{tab.text}
|
|
95
|
-
{tab.tag && (
|
|
96
|
-
<PktTag skin={tab.tag.skin} size="small">
|
|
97
|
-
{tab.tag.text}
|
|
98
|
-
</PktTag>
|
|
99
|
-
)}
|
|
100
|
-
</button>
|
|
101
|
-
)}
|
|
102
|
-
</Fragment>
|
|
103
|
-
))}
|
|
118
|
+
<TabsContext.Provider value={{ arrowNav: useArrowNav, registerTabRef, handleKeyPress, selectTab }}>
|
|
119
|
+
<div className="pkt-tabs" ref={ref}>
|
|
120
|
+
<div className="pkt-tabs__list" role={useArrowNav ? 'tablist' : 'navigation'}>
|
|
121
|
+
{children || tabItems}
|
|
122
|
+
</div>
|
|
104
123
|
</div>
|
|
105
|
-
</
|
|
124
|
+
</TabsContext.Provider>
|
|
106
125
|
)
|
|
107
126
|
},
|
|
108
127
|
)
|
|
128
|
+
|
|
129
|
+
export { PktTabItem } from './TabItem'
|