@@ -11,19 +11,49 @@ import {
11
11
useTask$ ,
12
12
useSignal ,
13
13
useVisibleTask$ ,
14
+ useId ,
14
15
} from '@builder.io/qwik' ;
15
16
17
+ /**
18
+ * TABS TODOs
19
+ * - Get storybook testing to work
20
+ *
21
+ * - selectedIndex / default
22
+ * - Orientation
23
+ * - aria-label https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-labelledby
24
+ * - NOTE: Radix manually handle the value/id for each tab while we calculate it behind the scenes
25
+ * If we end up implementing this, we need to expose a way to set this value in the root
26
+ * - keyboard interactions (arrowDown, ARrowRight, ArrowUp, ArrowLeft, Home, End, PageUp, PageDown)
27
+ * Support Loop
28
+ * - expose selectedIndex in the root
29
+ * - onValueChange
30
+ * POST V1:
31
+ * - RTL
32
+
33
+ *
34
+ * TAB
35
+ * Disable
36
+ * NOTE: radix / headlessui: expose data-state data-disable data-orientation
37
+ * NOTE: Headless UI: explorer the render props
38
+ * NOTE: remove tab, switch position
39
+ * NOTE: scrolling support? or multiple lines? (probably not for headless but for tailwind / material )
40
+ *
41
+ * PANEL
42
+ *
43
+ * aria Tabs Pattern https://www.w3.org/WAI/ARIA/apg/patterns/tabs/
44
+ * a11y lint plugin https://www.npmjs.com/package/eslint-plugin-jsx-a11y
45
+ *
46
+ */
16
47
export type Behavior = 'automatic' | 'manual' ;
17
48
18
49
interface TabsContext {
19
50
selectedIndex : Signal < number > ;
20
51
getNextTabIndex : QRL < ( ) => number > ;
21
52
getNextPanelIndex : QRL < ( ) => number > ;
22
- tabsHash : string ;
23
53
behavior : Behavior ;
24
54
}
25
55
26
- export const tabsContext = createContextId < TabsContext > ( 'tabList' ) ;
56
+ export const tabsContextId = createContextId < TabsContext > ( 'qui-- tabList' ) ;
27
57
28
58
export interface TabsProps {
29
59
behavior ?: Behavior ;
@@ -35,8 +65,6 @@ export const Tabs = component$((props: TabsProps) => {
35
65
const lastTabIndex = useSignal ( 0 ) ;
36
66
const lastPanelIndex = useSignal ( 0 ) ;
37
67
38
- const tabsHash = `${ Math . random ( ) * 1000 } ` ;
39
-
40
68
const getNextTabIndex = $ ( ( ) => {
41
69
return lastTabIndex . value ++ ;
42
70
} ) ;
@@ -51,11 +79,10 @@ export const Tabs = component$((props: TabsProps) => {
51
79
selectedIndex : selected ,
52
80
getNextTabIndex,
53
81
getNextPanelIndex,
54
- tabsHash,
55
82
behavior,
56
83
} ;
57
84
58
- useContextProvider ( tabsContext , contextService ) ;
85
+ useContextProvider ( tabsContextId , contextService ) ;
59
86
60
87
return (
61
88
< div { ...props } >
@@ -89,12 +116,15 @@ interface TabProps {
89
116
// Tab button inside of a tab list
90
117
export const Tab = component$ (
91
118
( { selectedClassName, onClick, ...props } : TabProps ) => {
92
- const contextService = useContext ( tabsContext ) ;
119
+ const contextService = useContext ( tabsContextId ) ;
93
120
const thisTabIndex = useSignal ( 0 ) ;
94
121
95
- useTask $( async ( ) => {
122
+ useVisibleTask $( async ( ) => {
96
123
thisTabIndex . value = await contextService . getNextTabIndex ( ) ;
124
+ console . log ( 'useVisibleTask$' , thisTabIndex . value ) ;
97
125
} ) ;
126
+
127
+ // TODO: Ask Manu about this 😊
98
128
const isSelected = ( ) =>
99
129
thisTabIndex . value === contextService . selectedIndex . value ;
100
130
@@ -106,7 +136,7 @@ export const Tab = component$(
106
136
107
137
return (
108
138
< button
109
- id = { ` ${ contextService . tabsHash } -tab- ${ thisTabIndex . value } ` }
139
+ id = { useId ( ) }
110
140
type = "button"
111
141
role = "tab"
112
142
onFocus$ = { selectIfAutomatic }
@@ -136,7 +166,7 @@ interface TabPanelProps {
136
166
// Tab Panel implementation
137
167
export const TabPanel = component$ ( ( { ...props } : TabPanelProps ) => {
138
168
const { class : classNames , ...rest } = props ;
139
- const contextService = useContext ( tabsContext ) ;
169
+ const contextService = useContext ( tabsContextId ) ;
140
170
const thisPanelIndex = useSignal ( 0 ) ;
141
171
const isSelected = ( ) =>
142
172
thisPanelIndex . value === contextService . selectedIndex . value ;
@@ -145,7 +175,7 @@ export const TabPanel = component$(({ ...props }: TabPanelProps) => {
145
175
} ) ;
146
176
return (
147
177
< div
148
- id = { ` ${ contextService . tabsHash } -tabpanel- ${ thisPanelIndex } ` }
178
+ id = { useId ( ) }
149
179
role = "tabpanel"
150
180
tabIndex = { 0 }
151
181
aria-labelledby = { `tab-${ thisPanelIndex } ` }
@@ -155,6 +185,8 @@ export const TabPanel = component$(({ ...props }: TabPanelProps) => {
155
185
style = { isSelected ( ) ? 'display: block' : 'display: none' }
156
186
{ ...rest }
157
187
>
188
+ < p > thisPanelIndex.value: { thisPanelIndex . value } </ p >
189
+ < p > contextService.selectedIndex: { contextService . selectedIndex } </ p >
158
190
< Slot />
159
191
</ div >
160
192
) ;
0 commit comments