@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.
@@ -1,11 +1,38 @@
1
1
  'use client'
2
2
 
3
- import React, { forwardRef, Ref, useRef, useEffect, KeyboardEvent, Fragment } from 'react'
4
- import { PktIcon } from '../icon/Icon'
5
- import { PktTag } from '../tag/Tag'
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
- tabs: IPktTab[]
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
- ({ arrowNav = true, tabs, onTabSelected }: IPktTabs, ref: Ref<HTMLDivElement>): JSX.Element => {
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, tabs.length)
33
- }, [tabs])
34
- const selectTab = (index: number, tab: IPktTab): void => {
35
- if (tab.action) tab.action(index)
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 (arrowNav) {
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 < tabs.length - 1) {
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, tabs[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
- <div className="pkt-tabs" ref={ref}>
53
- <div className="pkt-tabs__list" role={arrowNav ? 'tablist' : 'navigation'}>
54
- {tabs.map((tab, index) => (
55
- <Fragment key={index}>
56
- {tab.href ? (
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
- </div>
124
+ </TabsContext.Provider>
106
125
  )
107
126
  },
108
127
  )
128
+
129
+ export { PktTabItem } from './TabItem'