1+ /* eslint-disable @typescript-eslint/no-non-null-assertion */
12import {
23 $ ,
34 Slot ,
45 component$ ,
56 useComputed$ ,
67 useContext ,
78 useSignal ,
8- useTask$ ,
99 useVisibleTask$ ,
1010 type QwikIntrinsicElements ,
11- type QwikMouseEvent ,
1211 type Signal
1312} from '@builder.io/qwik' ;
1413import { KeyCode } from '../../utils/key-code.type' ;
@@ -18,14 +17,13 @@ import { tabsContextId } from './tabs-context-id';
1817export const TAB_ID_PREFIX = '_tab_' ;
1918
2019export type TabProps = {
21- onClick$ ?: ( event : QwikMouseEvent ) => void ;
20+ disabled ?: boolean ;
2221 selectedClassName ?: string ;
2322
24- disabled ?: boolean ;
2523 /** @deprecated Internal use only */
26- _tabId ?: string ;
24+ _extraClass ?: QwikIntrinsicElements [ 'div' ] [ 'class' ] ;
2725 /** @deprecated Internal use only */
28- _index ?: number ;
26+ _tabId ?: string ;
2927} & QwikIntrinsicElements [ 'button' ] ;
3028
3129export const preventedKeys = [
@@ -39,72 +37,65 @@ export const preventedKeys = [
3937 KeyCode . ArrowRight
4038] ;
4139
42- export const Tab = component$ ( ( props : TabProps ) => {
43- const contextService = useContext ( tabsContextId ) ;
44-
45- const elementRefSig = useSignal < HTMLElement | undefined > ( ) ;
40+ export const Tab = component$ (
41+ ( { selectedClassName, _extraClass, _tabId, ...props } : TabProps ) => {
42+ const contextService = useContext ( tabsContextId ) ;
4643
47- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
48- const tabId = props . _tabId ! ;
49- const fullTabElementId = contextService . tabsPrefix + TAB_ID_PREFIX + tabId ;
50- const fullPanelElementId = contextService . tabsPrefix + TAB_PANEL_ID_PREFIX + tabId ;
44+ const elementRefSig = useSignal < HTMLElement | undefined > ( ) ;
5145
52- const selectedClassNameSig = useComputed$ ( ( ) => {
53- return props . selectedClassName || contextService . selectedClassName ;
54- } ) ;
46+ const fullTabElementId = contextService . tabsPrefix + TAB_ID_PREFIX + _tabId ! ;
47+ const fullPanelElementId = contextService . tabsPrefix + TAB_PANEL_ID_PREFIX + _tabId ! ;
5548
56- const isSelectedSig = useComputed$ ( ( ) => {
57- return contextService . selectedIndexSig . value === props . _index ;
58- } ) ;
49+ const selectedClassNameSig = useComputed$ ( ( ) => {
50+ return selectedClassName || contextService . selectedClassName ;
51+ } ) ;
5952
60- useTask$ ( function disabledTask ( { track } ) {
61- contextService . setTabDisabledStatus$ ( tabId , ! ! track ( ( ) => props . disabled ) ) ;
62- } ) ;
53+ const isSelectedSig = useComputed$ ( ( ) => {
54+ return contextService . selectedTabIdSig . value === _tabId ;
55+ } ) ;
6356
64- useVisibleTask$ ( function preventDefaultOnKeysVisibleTask ( { cleanup } ) {
65- function handler ( event : KeyboardEvent ) {
66- if ( preventedKeys . includes ( event . key as KeyCode ) ) {
67- event . preventDefault ( ) ;
57+ useVisibleTask$ ( function preventDefaultOnKeysVisibleTask ( { cleanup } ) {
58+ function handler ( event : KeyboardEvent ) {
59+ if ( preventedKeys . includes ( event . key as KeyCode ) ) {
60+ event . preventDefault ( ) ;
61+ }
62+ contextService . onTabKeyDown$ ( event . key as KeyCode , _tabId ! ) ;
6863 }
69- contextService . onTabKeyDown$ ( event . key as KeyCode , tabId ) ;
70- }
71- elementRefSig . value ?. addEventListener ( 'keydown' , handler ) ;
72- cleanup ( ( ) => {
73- elementRefSig . value ?. removeEventListener ( 'keydown' , handler ) ;
64+ // TODO put the listener on TabList
65+ elementRefSig . value ?. addEventListener ( 'keydown' , handler ) ;
66+ cleanup ( ( ) => {
67+ elementRefSig . value ?. removeEventListener ( 'keydown' , handler ) ;
68+ } ) ;
7469 } ) ;
75- } ) ;
7670
77- const selectIfAutomatic$ = $ ( ( ) => {
78- contextService . selectIfAutomatic$ ( tabId ) ;
79- } ) ;
71+ const selectIfAutomatic$ = $ ( ( ) => {
72+ contextService . selectIfAutomatic$ ( _tabId ! ) ;
73+ } ) ;
8074
81- return (
82- < button
83- type = "button"
84- role = "tab"
85- id = { fullTabElementId }
86- data-tab-id = { fullTabElementId }
87- ref = { elementRefSig }
88- disabled = { props . disabled }
89- aria-disabled = { props . disabled }
90- onFocus$ = { selectIfAutomatic$ }
91- onMouseEnter$ = { selectIfAutomatic$ }
92- aria-selected = { isSelectedSig . value }
93- tabIndex = { isSelectedSig . value ? 0 : - 1 }
94- aria-controls = { fullPanelElementId }
95- style = { props . style }
96- class = { [
97- ( props . class as Signal < string > ) ?. value ?? ( props . class as string ) ,
98- isSelectedSig . value && [ 'selected' , selectedClassNameSig . value ]
99- ] }
100- onClick$ = { async ( event ) => {
101- await contextService . selectTab$ ( tabId ) ;
102- if ( props . onClick$ ) {
103- await props . onClick$ ( event ) ;
104- }
105- } }
106- >
107- < Slot />
108- </ button >
109- ) ;
110- } ) ;
75+ return (
76+ < button
77+ { ...props }
78+ type = "button"
79+ role = "tab"
80+ id = { fullTabElementId }
81+ data-tab-id = { fullTabElementId }
82+ ref = { elementRefSig }
83+ aria-disabled = { props . disabled }
84+ onFocus$ = { [ selectIfAutomatic$ , props . onFocus$ ] }
85+ onMouseEnter$ = { [ selectIfAutomatic$ , props . onMouseEnter$ ] }
86+ aria-selected = { isSelectedSig . value }
87+ tabIndex = { isSelectedSig . value ? 0 : - 1 }
88+ aria-controls = { fullPanelElementId }
89+ class = { [
90+ ( props . class as Signal < string > ) ?. value ?? ( props . class as string ) ,
91+ ( _extraClass as Signal < string > ) ?. value ?? ( _extraClass as string ) ,
92+ // TODO only given class if selected
93+ isSelectedSig . value && [ 'selected' , selectedClassNameSig . value ]
94+ ] }
95+ onClick$ = { [ $ ( ( ) => contextService . selectTab$ ( _tabId ! ) ) , props . onClick$ ] }
96+ >
97+ < Slot />
98+ </ button >
99+ ) ;
100+ }
101+ ) ;
0 commit comments