33 import type { SelectedTab , TabCtxType , TabsProps } from " $lib/types" ;
44 import { createSingleSelectionContext , useSingleSelection } from " $lib/utils/singleselection.svelte" ;
55 import clsx from " clsx" ;
6- import { setContext , type Snippet } from " svelte" ;
6+ import { setContext } from " svelte" ;
77 import { tabs } from " ." ;
88
9- let { children, tabStyle = " none" , ulClass, contentClass, divider = true , class : className, classes, ... restProps }: TabsProps = $props ();
9+ let { children, selected = $bindable (), tabStyle = " none" , ulClass, contentClass, divider = true , class : className, classes, ... restProps }: TabsProps = $props ();
1010
1111 if (classes ?.active ) {
1212 setContext (" activeClasses" , classes .active );
1313 }
1414
15- // base, content, divider, active, inactive
1615 warnThemeDeprecation (" Tabs" , { ulClass , contentClass }, { ulClass: " class" , contentClass: " content" });
17- const styling = $derived (classes ?? { content: contentClass });
18-
16+
1917 const theme = getTheme (" tabs" );
20-
18+ const styling = $derived ( classes ?? { content: contentClass });
2119 const { base, content, divider : dividerClass } = $derived (tabs ({ tabStyle , hasDivider: divider }));
2220
23- // Generate a unique ID for the tab panel
2421 const uuid = $props .id ();
2522 const panelId = ` tab-panel-${uuid } ` ;
26-
2723 const ctx: TabCtxType = $state ({ tabStyle , panelId });
28-
29- let dividerBool = $derived ([" full" , " pill" ].includes (tabStyle ) ? false : divider );
24+ const dividerBool = $derived ([" full" , " pill" ].includes (tabStyle ) ? false : divider );
3025
3126 setContext (" ctx" , ctx );
32-
3327 createSingleSelectionContext <SelectedTab >();
3428
35- let selected: SelectedTab = $state ({});
36- useSingleSelection <SelectedTab >((v ) => (selected = v ?? {}));
29+ const tabRegistry = $state (new Map <string , SelectedTab >());
30+ let selectedTab: SelectedTab = $state ({});
31+
32+ const updateSelection = useSingleSelection <SelectedTab >((v ) => {
33+ selectedTab = v ?? {};
34+ selected = v ?.id ;
35+ });
36+
37+ // Handle external changes to selected
38+ $effect (() => {
39+ if (selected && selected !== selectedTab .id ) {
40+ const targetTab = tabRegistry .get (selected );
41+ if (targetTab ) {
42+ updateSelection (true , targetTab );
43+ }
44+ }
45+ });
46+
47+ // Auto-select logic
48+ $effect (() => {
49+ if (tabRegistry .size > 0 && ! selectedTab .id ) {
50+ const targetTab = selected ? tabRegistry .get (selected ) : tabRegistry .values ().next ().value ;
51+ if (targetTab ) {
52+ updateSelection (true , targetTab );
53+ }
54+ }
55+ });
56+
57+ setContext (" registerTab" , (tabData : SelectedTab ) => {
58+ if (tabData .id ) {
59+ tabRegistry .set (tabData .id , tabData );
60+ }
61+ });
62+
63+ setContext (" unregisterTab" , (tabId : string ) => {
64+ tabRegistry .delete (tabId );
65+ });
3766 </script >
3867
3968<ul role ="tablist" {...restProps } class ={base ({ class: clsx (theme ?.base , className ?? ulClass ) })}>
4271{#if dividerBool }
4372 <div class ={dividerClass ({ class: clsx (theme ?.divider , classes ?.divider ) })}></div >
4473{/if }
45- <div id ={panelId } class ={content ({ class: clsx (theme ?.content , styling .content ) })} role ="tabpanel" aria-labelledby ={selected .id }>
46- {@render selected .snippet ?.()}
47- </div >
48-
49- <!--
50- @component
51- [Go to docs](https://flowbite-svelte.com/)
52- ## Type
53- [TabsProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L1721)
54- ## Props
55- @prop children
56- @prop tabStyle = "none"
57- @prop ulClass
58- @prop contentClass
59- @prop divider = true
60- @prop class: className
61- @prop classes
62- @prop ...restProps
63- -->
74+
75+ <div id ={panelId } class ={content ({ class: clsx (theme ?.content , styling .content ) })} role ="tabpanel" aria-labelledby ={selectedTab .id }>
76+ {@render selectedTab .snippet ?.()}
77+ </div >
0 commit comments