@khanacademy/wonder-blocks-tabs 0.5.14 → 0.5.15
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 +16 -0
- package/dist/es/index.js +6 -6
- package/dist/index.js +6 -6
- package/package.json +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @khanacademy/wonder-blocks-tabs
|
|
2
2
|
|
|
3
|
+
## 0.5.15
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 40cb70f: Add `require-logical-properties-for-rtl` ESLint rule to `eslint-plugin-wonder-blocks` recommended config, and migrate all Wonder Blocks component source files to use CSS logical properties for improved RTL layout support.
|
|
8
|
+
- 40cb70f: Enable RTL logical-properties ESLint rule in recommended config
|
|
9
|
+
- Updated dependencies [40cb70f]
|
|
10
|
+
- Updated dependencies [40cb70f]
|
|
11
|
+
- Updated dependencies [c97ece4]
|
|
12
|
+
- @khanacademy/wonder-blocks-button@11.6.1
|
|
13
|
+
- @khanacademy/wonder-blocks-core@12.4.4
|
|
14
|
+
- @khanacademy/wonder-blocks-dropdown@10.9.1
|
|
15
|
+
- @khanacademy/wonder-blocks-link@10.3.0
|
|
16
|
+
- @khanacademy/wonder-blocks-icon@5.3.15
|
|
17
|
+
- @khanacademy/wonder-blocks-typography@4.3.5
|
|
18
|
+
|
|
3
19
|
## 0.5.14
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
package/dist/es/index.js
CHANGED
|
@@ -24,20 +24,20 @@ const FOCUSABLE_ELEMENTS='button, [href], input, select, textarea, [tabindex]:no
|
|
|
24
24
|
|
|
25
25
|
const StyledDiv$2=addStyle("div");const TabPanel=props=>{const{children,id,"aria-labelledby":ariaLabelledby,active=false,testId,style}=props;const ref=React.useRef(null);const[hasFocusableElement,setHasFocusableElement]=React.useState(null);const updateHasFocusableElement=React.useCallback(element=>{setHasFocusableElement(findFocusableNodes(element).length>0);},[setHasFocusableElement]);React.useEffect(()=>{if(ref.current&&children){updateHasFocusableElement(ref.current);if(active){const mutationObserver=new MutationObserver(()=>{if(ref.current){updateHasFocusableElement(ref.current);}});mutationObserver.observe(ref.current,{childList:true,subtree:true});return ()=>{mutationObserver.disconnect();}}}},[active,ref,children,updateHasFocusableElement]);return jsx(StyledDiv$2,{ref:ref,role:"tabpanel",id:id,"aria-labelledby":ariaLabelledby,tabIndex:hasFocusableElement===false?0:undefined,hidden:!active,"data-testid":testId,style:active&&[styles$7.tabPanel,style],children:children})};const styles$7=StyleSheet.create({tabPanel:{display:"flex",...focusStyles.focus}});
|
|
26
26
|
|
|
27
|
-
const StyledButton=addStyle("button");const Tab=React.forwardRef(function Tab(props,ref){const{children,onClick,id,"aria-controls":ariaControls,selected,onKeyDown,testId,style,icon,...otherProps}=props;return jsxs(StyledButton,{...otherProps,type:"button",role:"tab",onClick:onClick,ref:ref,id:id,"aria-controls":ariaControls,"aria-selected":selected,tabIndex:selected?0:-1,onKeyDown:onKeyDown,"data-testid":testId,style:[styles$b.BodyTextMediumMediumWeight,styles$6.tab,selected&&styles$6.selectedTab,style],children:[icon&&jsx(View,{children:React.cloneElement(icon,{size:icon.props.size??"medium"})}),children]})});const bottomSpacing=sizing.size_140;const styles$6=StyleSheet.create({tab:{display:"flex",alignItems:"center",textWrap:"nowrap",backgroundColor:"transparent",border:"none",margin:0,padding:0,cursor:"pointer",marginBlockStart:sizing.size_080,marginBlockEnd:bottomSpacing,gap:sizing.size_080,position:"relative",color:semanticColor.core.foreground.neutral.subtle,...focusStyles.focus,":after":{content:"''",position:"absolute",
|
|
27
|
+
const StyledButton=addStyle("button");const Tab=React.forwardRef(function Tab(props,ref){const{children,onClick,id,"aria-controls":ariaControls,selected,onKeyDown,testId,style,icon,...otherProps}=props;return jsxs(StyledButton,{...otherProps,type:"button",role:"tab",onClick:onClick,ref:ref,id:id,"aria-controls":ariaControls,"aria-selected":selected,tabIndex:selected?0:-1,onKeyDown:onKeyDown,"data-testid":testId,style:[styles$b.BodyTextMediumMediumWeight,styles$6.tab,selected&&styles$6.selectedTab,style],children:[icon&&jsx(View,{children:React.cloneElement(icon,{size:icon.props.size??"medium"})}),children]})});const bottomSpacing=sizing.size_140;const styles$6=StyleSheet.create({tab:{display:"flex",alignItems:"center",textWrap:"nowrap",backgroundColor:"transparent",border:"none",margin:0,padding:0,cursor:"pointer",marginBlockStart:sizing.size_080,marginBlockEnd:bottomSpacing,gap:sizing.size_080,position:"relative",color:semanticColor.core.foreground.neutral.subtle,...focusStyles.focus,":after":{content:"''",position:"absolute",insetInlineStart:0,insetInlineEnd:0,insetBlockEnd:`calc(${bottomSpacing} * -1)`},[":hover:not([aria-selected='true'])"]:{color:semanticColor.link.hover,[":after"]:{height:border.width.thin,backgroundColor:semanticColor.link.hover}},[":active:not([aria-selected='true'])"]:{color:semanticColor.link.press,[":after"]:{height:border.width.thick,backgroundColor:semanticColor.link.press}}},selectedTab:{color:semanticColor.link.rest}});
|
|
28
28
|
|
|
29
|
-
const StyledDiv$1=addStyle("div");const Tablist=React.forwardRef(function Tablist(props,ref){const{id,children,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,onBlur,testId,style}=props;return jsx(StyledDiv$1,{id:id,role:"tablist",style:[styles$5.tablist,style],ref:ref,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,onBlur:onBlur,"data-testid":testId,children:children})});const styles$5=StyleSheet.create({tablist:{display:"flex",gap:sizing.size_240,
|
|
29
|
+
const StyledDiv$1=addStyle("div");const Tablist=React.forwardRef(function Tablist(props,ref){const{id,children,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,onBlur,testId,style}=props;return jsx(StyledDiv$1,{id:id,role:"tablist",style:[styles$5.tablist,style],ref:ref,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,onBlur:onBlur,"data-testid":testId,children:children})});const styles$5=StyleSheet.create({tablist:{display:"flex",gap:sizing.size_240,borderBlockEnd:`${border.width.thin} solid ${semanticColor.core.border.neutral.subtle}`,paddingInline:sizing.size_040}});
|
|
30
30
|
|
|
31
31
|
function getTabId(tabId){return `${tabId}-tab`}function getTabPanelId(tabId){return `${tabId}-panel`}const StyledDiv=addStyle("div");const Tabs=React.forwardRef(function Tabs(props,ref){const{tabs,selectedTabId,onTabSelected,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,activationMode="manual",id,testId,animated=false,styles:stylesProp,mountAllPanels=false,scrollableElementRef}=props;const focusedTabId=React.useRef(selectedTabId);const tabRefs=React.useRef({});const generatedUniqueId=React.useId();const uniqueId=id??generatedUniqueId;const tablistId=`${uniqueId}-tablist`;const visitedTabsRef=React.useRef(new Set);visitedTabsRef.current.add(selectedTabId);const tablistRef=React.useRef(null);const isTabActive=React.useCallback(tabElement=>{return tabElement.ariaSelected==="true"},[]);const{indicatorProps}=useTabIndicator({animated,tabsContainerRef:tablistRef,isTabActive});React.useEffect(()=>{focusedTabId.current=selectedTabId;},[selectedTabId]);const selectTab=React.useCallback(tabId=>{if(tabId!==selectedTabId){onTabSelected(tabId);}},[onTabSelected,selectedTabId]);const handleKeyInteraction=React.useCallback(tabId=>{const tabElement=tabRefs.current[tabId];tabElement?.focus();switch(activationMode){case"manual":{focusedTabId.current=tabId;break}case"automatic":{selectTab(tabId);break}}},[activationMode,selectTab,focusedTabId]);const handleKeyDown=React.useCallback(event=>{const currentIndex=tabs.findIndex(tab=>tab.id===focusedTabId.current);const element=event.currentTarget;const isRtl=!!element.closest("[dir=rtl]");switch(event.key){case isRtl&&keys.right:case!isRtl&&keys.left:{event.preventDefault();const prevIndex=(currentIndex-1+tabs.length)%tabs.length;handleKeyInteraction(tabs[prevIndex].id);break}case isRtl&&keys.left:case!isRtl&&keys.right:{event.preventDefault();const nextIndex=(currentIndex+1)%tabs.length;handleKeyInteraction(tabs[nextIndex].id);break}case keys.home:{event.preventDefault();handleKeyInteraction(tabs[0].id);break}case keys.end:{event.preventDefault();handleKeyInteraction(tabs[tabs.length-1].id);break}case keys.enter:case keys.space:{selectTab(focusedTabId.current);break}}},[handleKeyInteraction,selectTab,tabs,focusedTabId]);const handleTablistBlur=React.useCallback(()=>{focusedTabId.current=selectedTabId;},[selectedTabId]);if(tabs.length===0){return jsx(React.Fragment,{})}return jsxs(StyledDiv,{ref:ref,id:uniqueId,"data-testid":testId,style:[styles$4.tabs,stylesProp?.root],children:[jsxs(StyledDiv,{ref:scrollableElementRef,style:styles$4.tablistWrapper,children:[jsx(Tablist,{"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,onBlur:handleTablistBlur,id:tablistId,testId:testId&&`${testId}-tablist`,ref:tablistRef,style:stylesProp?.tablist,children:tabs.map(tab=>{const{id,label,panel:_,testId:tabTestId,icon,...otherProps}=tab;const tabProps={...otherProps,key:id,id:getTabId(id),testId:tabTestId&&getTabId(tabTestId),selected:id===selectedTabId,icon,"aria-controls":getTabPanelId(id),onClick:()=>{onTabSelected(id);},onKeyDown:handleKeyDown,ref:element=>{tabRefs.current[tab.id]=element;},style:stylesProp?.tab};if(typeof label==="function"){return label(tabProps)}return jsx(Tab,{...tabProps,children:label})})}),jsx("div",{...indicatorProps})]}),tabs.map(tab=>{return jsx(TabPanel,{id:getTabPanelId(tab.id),"aria-labelledby":getTabId(tab.id),active:selectedTabId===tab.id,testId:tab.testId&&getTabPanelId(tab.testId),style:stylesProp?.tabPanel,children:(mountAllPanels||visitedTabsRef.current.has(tab.id))&&tab.panel},tab.id)})]})});const styles$4=StyleSheet.create({tabs:{display:"inline-flex",flexDirection:"column",alignItems:"stretch",position:"relative"},tablistWrapper:{position:"relative",overflowX:"auto",flexShrink:0}});
|
|
32
32
|
|
|
33
|
-
const defaultLabels$1={defaultOpenerLabel:"Tabs"};const TabsDropdown=React.forwardRef((props,ref)=>{const{tabs,selectedTabId,onTabSelected,labels:labelsProp,opened,id:idProp,testId,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,styles:stylesProp}=props;const labels=React.useMemo(()=>{return {...defaultLabels$1,...labelsProp}},[labelsProp]);const generatedUniqueId=React.useId();const uniqueId=idProp??generatedUniqueId;const openerId=`${uniqueId}-opener`;const panelId=`${uniqueId}-panel`;const selectedTabItem=React.useMemo(()=>{return tabs.find(tab=>tab.id===selectedTabId)},[tabs,selectedTabId]);const processedTabs=React.useMemo(()=>{return tabs.map(tab=>({...tab,leftAccessory:tab.icon?React.cloneElement(tab.icon,{size:tab.icon.props.size??"medium"}):undefined,handleClick:()=>{onTabSelected(tab.id);}}))},[tabs,onTabSelected]);if(tabs.length===0){return jsx(React.Fragment,{})}const menuText=selectedTabItem?.label||labels.defaultOpenerLabel;return jsxs(View,{ref:ref,id:uniqueId,testId:testId,style:stylesProp?.root,role:"region","aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,children:[jsx(ActionMenu,{opened:opened,id:openerId,menuText:menuText,opener:()=>jsx(Button,{testId:testId?`${testId}-opener`:undefined,kind:"tertiary",endIcon:caretDown,style:[styles$3.opener,stylesProp?.opener],labelStyle:styles$3.labelStyle,"aria-label":selectedTabItem?.["aria-label"],startIcon:selectedTabItem?.icon,children:menuText}),style:[styles$3.actionMenu,stylesProp?.actionMenu],children:processedTabs.map(tab=>{return jsx(ActionItem,{label:tab.label,"aria-label":tab["aria-label"],onClick:tab.handleClick,active:tab.id===selectedTabId,testId:tab.testId,rightAccessory:tab.id===selectedTabId?jsx(PhosphorIcon,{icon:checkCircleIcon,size:"medium","aria-hidden":"true"}):undefined,leftAccessory:tab.leftAccessory},tab.id)})}),jsx(View,{id:panelId,role:"group","aria-labelledby":openerId,testId:testId?`${testId}-panel`:undefined,style:stylesProp?.tabPanel,children:selectedTabItem?.panel})]})});const styles$3=StyleSheet.create({actionMenu:{width:"100%",alignItems:"flex-start",borderBlockEnd:`${border.width.thin} solid ${semanticColor.core.border.neutral.subtle}`},opener:{position:"relative",height:"unset",paddingBlockStart:sizing.size_120,paddingBlockEnd:sizing.size_140,paddingInline:sizing.size_180,width:"100%",justifyContent:"space-between",gap:sizing.size_020},labelStyle:{flexGrow:1,
|
|
33
|
+
const defaultLabels$1={defaultOpenerLabel:"Tabs"};const TabsDropdown=React.forwardRef((props,ref)=>{const{tabs,selectedTabId,onTabSelected,labels:labelsProp,opened,id:idProp,testId,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,styles:stylesProp}=props;const labels=React.useMemo(()=>{return {...defaultLabels$1,...labelsProp}},[labelsProp]);const generatedUniqueId=React.useId();const uniqueId=idProp??generatedUniqueId;const openerId=`${uniqueId}-opener`;const panelId=`${uniqueId}-panel`;const selectedTabItem=React.useMemo(()=>{return tabs.find(tab=>tab.id===selectedTabId)},[tabs,selectedTabId]);const processedTabs=React.useMemo(()=>{return tabs.map(tab=>({...tab,leftAccessory:tab.icon?React.cloneElement(tab.icon,{size:tab.icon.props.size??"medium"}):undefined,handleClick:()=>{onTabSelected(tab.id);}}))},[tabs,onTabSelected]);if(tabs.length===0){return jsx(React.Fragment,{})}const menuText=selectedTabItem?.label||labels.defaultOpenerLabel;return jsxs(View,{ref:ref,id:uniqueId,testId:testId,style:stylesProp?.root,role:"region","aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,children:[jsx(ActionMenu,{opened:opened,id:openerId,menuText:menuText,opener:()=>jsx(Button,{testId:testId?`${testId}-opener`:undefined,kind:"tertiary",endIcon:caretDown,style:[styles$3.opener,stylesProp?.opener],labelStyle:styles$3.labelStyle,"aria-label":selectedTabItem?.["aria-label"],startIcon:selectedTabItem?.icon,children:menuText}),style:[styles$3.actionMenu,stylesProp?.actionMenu],children:processedTabs.map(tab=>{return jsx(ActionItem,{label:tab.label,"aria-label":tab["aria-label"],onClick:tab.handleClick,active:tab.id===selectedTabId,testId:tab.testId,rightAccessory:tab.id===selectedTabId?jsx(PhosphorIcon,{icon:checkCircleIcon,size:"medium","aria-hidden":"true"}):undefined,leftAccessory:tab.leftAccessory},tab.id)})}),jsx(View,{id:panelId,role:"group","aria-labelledby":openerId,testId:testId?`${testId}-panel`:undefined,style:stylesProp?.tabPanel,children:selectedTabItem?.panel})]})});const styles$3=StyleSheet.create({actionMenu:{width:"100%",alignItems:"flex-start",borderBlockEnd:`${border.width.thin} solid ${semanticColor.core.border.neutral.subtle}`},opener:{position:"relative",height:"unset",paddingBlockStart:sizing.size_120,paddingBlockEnd:sizing.size_140,paddingInline:sizing.size_180,width:"100%",justifyContent:"space-between",gap:sizing.size_020},labelStyle:{flexGrow:1,maxInlineSize:"100%",textAlign:"start"}});
|
|
34
34
|
|
|
35
35
|
function useResponsiveLayout(options){const{tabs,elementWithOverflowRef,containerRef,onLayoutChange}=options;const[showDropdown,setShowDropdown]=React.useState(false);const tabsWidthRef=React.useRef(null);const containerWidthRef=React.useRef(null);const tabsSignature=React.useMemo(()=>tabs.map(t=>`${t.id}:${t.label}-${t.icon?"with-icon":"without-icon"}`).join("|"),[tabs]);const checkOverflow=React.useCallback(()=>{const container=containerRef.current;if(!container){return}if(!showDropdown&&elementWithOverflowRef.current){const scrollableWrapper=elementWithOverflowRef.current;if(scrollableWrapper){const hasOverflow=scrollableWrapper.scrollWidth>scrollableWrapper.clientWidth;if(hasOverflow){tabsWidthRef.current=scrollableWrapper.scrollWidth+(container.clientWidth-scrollableWrapper.clientWidth);setShowDropdown(true);}}}else if(showDropdown&&tabsWidthRef.current){const containerWidth=container.clientWidth;if(containerWidth>=tabsWidthRef.current){setShowDropdown(false);}}},[showDropdown,elementWithOverflowRef,containerRef]);React.useEffect(()=>{if(showDropdown){tabsWidthRef.current=null;setShowDropdown(false);}},[tabsSignature]);React.useEffect(()=>{const container=containerRef.current;if(!container||!window.ResizeObserver){return}const resizeObserver=new ResizeObserver(()=>{if(containerWidthRef.current!==container.clientWidth){containerWidthRef.current=container.clientWidth;checkOverflow();}});resizeObserver.observe(container);return ()=>{resizeObserver.disconnect();}},[checkOverflow,containerRef]);React.useEffect(()=>{const element=elementWithOverflowRef.current;if(!element){return}checkOverflow();const mutationObserver=new MutationObserver(()=>{checkOverflow();});mutationObserver.observe(element,{childList:true,subtree:true,characterData:true});return ()=>{mutationObserver.disconnect();}},[checkOverflow,elementWithOverflowRef]);React.useEffect(()=>{onLayoutChange?.(showDropdown?"dropdown":"tabs");},[showDropdown,onLayoutChange]);return {showDropdown}}
|
|
36
36
|
|
|
37
|
-
const ResponsiveTabs=props=>{const{tabs,selectedTabId,onTabSelected,onLayoutChange,id,testId,tabsProps,dropdownProps,styles:stylesProp,...ariaProps}=props;const tabsRef=React.useRef(null);const scrollableTabsRef=React.useRef(null);const containerRef=React.useRef(null);const{showDropdown}=useResponsiveLayout({tabs,elementWithOverflowRef:scrollableTabsRef,containerRef,onLayoutChange});return jsx(View,{ref:containerRef,style:[styles$2.container,stylesProp?.root],id:id,testId:testId,children:showDropdown?createElement(TabsDropdown,{...dropdownProps,...ariaProps,key:"dropdown",tabs:tabs,selectedTabId:selectedTabId,onTabSelected:onTabSelected,styles:{...dropdownProps?.styles,root:[styles$2.fadeIn,dropdownProps?.styles?.root]}}):createElement(Tabs,{...tabsProps,...ariaProps,key:"tabs",ref:tabsRef,scrollableElementRef:scrollableTabsRef,tabs:tabs,selectedTabId:selectedTabId,onTabSelected:onTabSelected,styles:{...tabsProps?.styles,root:[styles$2.fadeIn,tabsProps?.styles?.root]}})})};const fadeInKeyframes$1={from:{opacity:0},to:{opacity:1}};const styles$2=StyleSheet.create({fadeIn:{animationName:fadeInKeyframes$1,animationDuration:"150ms",animationTimingFunction:"ease-in-out"},container:{width:"100%",
|
|
37
|
+
const ResponsiveTabs=props=>{const{tabs,selectedTabId,onTabSelected,onLayoutChange,id,testId,tabsProps,dropdownProps,styles:stylesProp,...ariaProps}=props;const tabsRef=React.useRef(null);const scrollableTabsRef=React.useRef(null);const containerRef=React.useRef(null);const{showDropdown}=useResponsiveLayout({tabs,elementWithOverflowRef:scrollableTabsRef,containerRef,onLayoutChange});return jsx(View,{ref:containerRef,style:[styles$2.container,stylesProp?.root],id:id,testId:testId,children:showDropdown?createElement(TabsDropdown,{...dropdownProps,...ariaProps,key:"dropdown",tabs:tabs,selectedTabId:selectedTabId,onTabSelected:onTabSelected,styles:{...dropdownProps?.styles,root:[styles$2.fadeIn,dropdownProps?.styles?.root]}}):createElement(Tabs,{...tabsProps,...ariaProps,key:"tabs",ref:tabsRef,scrollableElementRef:scrollableTabsRef,tabs:tabs,selectedTabId:selectedTabId,onTabSelected:onTabSelected,styles:{...tabsProps?.styles,root:[styles$2.fadeIn,tabsProps?.styles?.root]}})})};const fadeInKeyframes$1={from:{opacity:0},to:{opacity:1}};const styles$2=StyleSheet.create({fadeIn:{animationName:fadeInKeyframes$1,animationDuration:"150ms",animationTimingFunction:"ease-in-out"},container:{width:"100%",minBlockSize:"auto"}});
|
|
38
38
|
|
|
39
|
-
const defaultLabels={defaultOpenerLabel:"Tabs"};const NavigationTabsDropdown=React.forwardRef((props,ref)=>{const{tabs,selectedTabId,onTabSelected,id:idProp,testId,labels:labelsProp,opened,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,tag="nav",styles:stylesProp,showDivider=false}=props;const StyledTag=React.useMemo(()=>addStyle(tag),[tag]);const labels=React.useMemo(()=>{return {...defaultLabels,...labelsProp}},[labelsProp]);const selectedTabItem=React.useMemo(()=>{return tabs.find(tab=>tab.id===selectedTabId)},[tabs,selectedTabId]);const processedTabs=React.useMemo(()=>{return tabs.map(tab=>({...tab,leftAccessory:tab.icon?React.cloneElement(tab.icon,{size:tab.icon.props.size??"medium"}):undefined,handleClick:onTabSelected?()=>onTabSelected(tab.id):undefined}))},[tabs,onTabSelected]);const generatedUniqueId=React.useId();const uniqueId=idProp??generatedUniqueId;const openerId=`${uniqueId}-opener`;if(tabs.length===0){return jsx(React.Fragment,{})}const menuText=selectedTabItem?.label||labels.defaultOpenerLabel;return jsx(StyledTag,{ref:ref,id:uniqueId,"data-testid":testId,style:[showDivider&&styles$1.divider,stylesProp?.root],"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,children:jsx(ActionMenu,{opened:opened,id:openerId,menuText:menuText,opener:()=>jsx(Button,{testId:testId?`${testId}-opener`:undefined,kind:"tertiary",endIcon:caretDown,style:[styles$1.opener,stylesProp?.opener],labelStyle:styles$1.labelStyle,"aria-label":selectedTabItem?.["aria-label"],startIcon:selectedTabItem?.icon,children:menuText}),style:[styles$1.actionMenu,stylesProp?.actionMenu],children:processedTabs.map(tab=>{return jsx(ActionItem,{label:tab.label,href:tab.href,"aria-label":tab["aria-label"],active:tab.id===selectedTabId,testId:tab.testId,leftAccessory:tab.leftAccessory,onClick:tab.handleClick,rightAccessory:tab.id===selectedTabId?jsx(PhosphorIcon,{icon:checkCircleIcon,size:"medium","aria-hidden":"true"}):undefined},tab.id)})})})});const styles$1=StyleSheet.create({actionMenu:{width:"100%",alignItems:"flex-start"},divider:{borderBlockEnd:`${border.width.thin} solid ${semanticColor.core.border.neutral.subtle}`},opener:{position:"relative",height:"unset",paddingBlockStart:sizing.size_120,paddingBlockEnd:sizing.size_140,paddingInline:sizing.size_180,width:"100%",justifyContent:"space-between",gap:sizing.size_020},labelStyle:{flexGrow:1,
|
|
39
|
+
const defaultLabels={defaultOpenerLabel:"Tabs"};const NavigationTabsDropdown=React.forwardRef((props,ref)=>{const{tabs,selectedTabId,onTabSelected,id:idProp,testId,labels:labelsProp,opened,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,tag="nav",styles:stylesProp,showDivider=false}=props;const StyledTag=React.useMemo(()=>addStyle(tag),[tag]);const labels=React.useMemo(()=>{return {...defaultLabels,...labelsProp}},[labelsProp]);const selectedTabItem=React.useMemo(()=>{return tabs.find(tab=>tab.id===selectedTabId)},[tabs,selectedTabId]);const processedTabs=React.useMemo(()=>{return tabs.map(tab=>({...tab,leftAccessory:tab.icon?React.cloneElement(tab.icon,{size:tab.icon.props.size??"medium"}):undefined,handleClick:onTabSelected?()=>onTabSelected(tab.id):undefined}))},[tabs,onTabSelected]);const generatedUniqueId=React.useId();const uniqueId=idProp??generatedUniqueId;const openerId=`${uniqueId}-opener`;if(tabs.length===0){return jsx(React.Fragment,{})}const menuText=selectedTabItem?.label||labels.defaultOpenerLabel;return jsx(StyledTag,{ref:ref,id:uniqueId,"data-testid":testId,style:[showDivider&&styles$1.divider,stylesProp?.root],"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,children:jsx(ActionMenu,{opened:opened,id:openerId,menuText:menuText,opener:()=>jsx(Button,{testId:testId?`${testId}-opener`:undefined,kind:"tertiary",endIcon:caretDown,style:[styles$1.opener,stylesProp?.opener],labelStyle:styles$1.labelStyle,"aria-label":selectedTabItem?.["aria-label"],startIcon:selectedTabItem?.icon,children:menuText}),style:[styles$1.actionMenu,stylesProp?.actionMenu],children:processedTabs.map(tab=>{return jsx(ActionItem,{label:tab.label,href:tab.href,"aria-label":tab["aria-label"],active:tab.id===selectedTabId,testId:tab.testId,leftAccessory:tab.leftAccessory,onClick:tab.handleClick,rightAccessory:tab.id===selectedTabId?jsx(PhosphorIcon,{icon:checkCircleIcon,size:"medium","aria-hidden":"true"}):undefined},tab.id)})})})});const styles$1=StyleSheet.create({actionMenu:{width:"100%",alignItems:"flex-start"},divider:{borderBlockEnd:`${border.width.thin} solid ${semanticColor.core.border.neutral.subtle}`},opener:{position:"relative",height:"unset",paddingBlockStart:sizing.size_120,paddingBlockEnd:sizing.size_140,paddingInline:sizing.size_180,width:"100%",justifyContent:"space-between",gap:sizing.size_020},labelStyle:{flexGrow:1,maxInlineSize:"100%",textAlign:"start"}});
|
|
40
40
|
|
|
41
|
-
const ResponsiveNavigationTabs=props=>{const{tabs,selectedTabId,onTabSelected,onLayoutChange,id,testId,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,tag,tabsProps,dropdownProps,styles:stylesProp,showDivider=false}=props;const navigationTabsRef=React.useRef(null);const containerRef=React.useRef(null);const{showDropdown}=useResponsiveLayout({tabs,elementWithOverflowRef:navigationTabsRef,containerRef,onLayoutChange});const processedTabs=React.useMemo(()=>{return tabs.map(tab=>({...tab,startIcon:tab.icon?React.cloneElement(tab.icon,{size:tab.icon.props.size??"medium"}):undefined,handleClick:onTabSelected?()=>onTabSelected(tab.id):undefined}))},[tabs,onTabSelected]);return jsx(View,{ref:containerRef,style:[styles.container,stylesProp?.root],id:id,testId:testId,children:showDropdown?createElement(NavigationTabsDropdown,{...dropdownProps,key:"dropdown",tabs:tabs,selectedTabId:selectedTabId,onTabSelected:onTabSelected,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,tag:tag,styles:{...dropdownProps?.styles,root:[styles.fadeIn,dropdownProps?.styles?.root]},showDivider:showDivider}):createElement(NavigationTabs,{...tabsProps,key:"tabs",ref:navigationTabsRef,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,tag:tag,styles:{...tabsProps?.styles,root:[styles.fadeIn,tabsProps?.styles?.root]},showDivider:showDivider},processedTabs.map(tab=>jsx(NavigationTabItem,{current:tab.id===selectedTabId,children:jsx(Link,{href:tab.href,onClick:tab.handleClick,startIcon:tab.startIcon,"aria-label":tab["aria-label"],testId:tab.testId,children:tab.label})},tab.id)))})};const fadeInKeyframes={from:{opacity:0},to:{opacity:1}};const styles=StyleSheet.create({fadeIn:{animationName:fadeInKeyframes,animationDuration:"150ms",animationTimingFunction:"ease-in-out"},container:{width:"100%",
|
|
41
|
+
const ResponsiveNavigationTabs=props=>{const{tabs,selectedTabId,onTabSelected,onLayoutChange,id,testId,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,tag,tabsProps,dropdownProps,styles:stylesProp,showDivider=false}=props;const navigationTabsRef=React.useRef(null);const containerRef=React.useRef(null);const{showDropdown}=useResponsiveLayout({tabs,elementWithOverflowRef:navigationTabsRef,containerRef,onLayoutChange});const processedTabs=React.useMemo(()=>{return tabs.map(tab=>({...tab,startIcon:tab.icon?React.cloneElement(tab.icon,{size:tab.icon.props.size??"medium"}):undefined,handleClick:onTabSelected?()=>onTabSelected(tab.id):undefined}))},[tabs,onTabSelected]);return jsx(View,{ref:containerRef,style:[styles.container,stylesProp?.root],id:id,testId:testId,children:showDropdown?createElement(NavigationTabsDropdown,{...dropdownProps,key:"dropdown",tabs:tabs,selectedTabId:selectedTabId,onTabSelected:onTabSelected,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,tag:tag,styles:{...dropdownProps?.styles,root:[styles.fadeIn,dropdownProps?.styles?.root]},showDivider:showDivider}):createElement(NavigationTabs,{...tabsProps,key:"tabs",ref:navigationTabsRef,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,tag:tag,styles:{...tabsProps?.styles,root:[styles.fadeIn,tabsProps?.styles?.root]},showDivider:showDivider},processedTabs.map(tab=>jsx(NavigationTabItem,{current:tab.id===selectedTabId,children:jsx(Link,{href:tab.href,onClick:tab.handleClick,startIcon:tab.startIcon,"aria-label":tab["aria-label"],testId:tab.testId,children:tab.label})},tab.id)))})};const fadeInKeyframes={from:{opacity:0},to:{opacity:1}};const styles=StyleSheet.create({fadeIn:{animationName:fadeInKeyframes,animationDuration:"150ms",animationTimingFunction:"ease-in-out"},container:{width:"100%",minBlockSize:"auto"}});
|
|
42
42
|
|
|
43
43
|
export { NavigationTabItem, NavigationTabs, ResponsiveNavigationTabs, ResponsiveTabs, Tab, Tabs };
|
package/dist/index.js
CHANGED
|
@@ -53,21 +53,21 @@ const FOCUSABLE_ELEMENTS='button, [href], input, select, textarea, [tabindex]:no
|
|
|
53
53
|
|
|
54
54
|
const StyledDiv$2=wonderBlocksCore.addStyle("div");const TabPanel=props=>{const{children,id,"aria-labelledby":ariaLabelledby,active=false,testId,style}=props;const ref=React__namespace.useRef(null);const[hasFocusableElement,setHasFocusableElement]=React__namespace.useState(null);const updateHasFocusableElement=React__namespace.useCallback(element=>{setHasFocusableElement(findFocusableNodes(element).length>0);},[setHasFocusableElement]);React__namespace.useEffect(()=>{if(ref.current&&children){updateHasFocusableElement(ref.current);if(active){const mutationObserver=new MutationObserver(()=>{if(ref.current){updateHasFocusableElement(ref.current);}});mutationObserver.observe(ref.current,{childList:true,subtree:true});return ()=>{mutationObserver.disconnect();}}}},[active,ref,children,updateHasFocusableElement]);return jsxRuntime.jsx(StyledDiv$2,{ref:ref,role:"tabpanel",id:id,"aria-labelledby":ariaLabelledby,tabIndex:hasFocusableElement===false?0:undefined,hidden:!active,"data-testid":testId,style:active&&[styles$7.tabPanel,style],children:children})};const styles$7=aphrodite.StyleSheet.create({tabPanel:{display:"flex",...focusStyles.focus}});
|
|
55
55
|
|
|
56
|
-
const StyledButton=wonderBlocksCore.addStyle("button");const Tab=React__namespace.forwardRef(function Tab(props,ref){const{children,onClick,id,"aria-controls":ariaControls,selected,onKeyDown,testId,style,icon,...otherProps}=props;return jsxRuntime.jsxs(StyledButton,{...otherProps,type:"button",role:"tab",onClick:onClick,ref:ref,id:id,"aria-controls":ariaControls,"aria-selected":selected,tabIndex:selected?0:-1,onKeyDown:onKeyDown,"data-testid":testId,style:[wonderBlocksTypography.styles.BodyTextMediumMediumWeight,styles$6.tab,selected&&styles$6.selectedTab,style],children:[icon&&jsxRuntime.jsx(wonderBlocksCore.View,{children:React__namespace.cloneElement(icon,{size:icon.props.size??"medium"})}),children]})});const bottomSpacing=wonderBlocksTokens.sizing.size_140;const styles$6=aphrodite.StyleSheet.create({tab:{display:"flex",alignItems:"center",textWrap:"nowrap",backgroundColor:"transparent",border:"none",margin:0,padding:0,cursor:"pointer",marginBlockStart:wonderBlocksTokens.sizing.size_080,marginBlockEnd:bottomSpacing,gap:wonderBlocksTokens.sizing.size_080,position:"relative",color:wonderBlocksTokens.semanticColor.core.foreground.neutral.subtle,...focusStyles.focus,":after":{content:"''",position:"absolute",
|
|
56
|
+
const StyledButton=wonderBlocksCore.addStyle("button");const Tab=React__namespace.forwardRef(function Tab(props,ref){const{children,onClick,id,"aria-controls":ariaControls,selected,onKeyDown,testId,style,icon,...otherProps}=props;return jsxRuntime.jsxs(StyledButton,{...otherProps,type:"button",role:"tab",onClick:onClick,ref:ref,id:id,"aria-controls":ariaControls,"aria-selected":selected,tabIndex:selected?0:-1,onKeyDown:onKeyDown,"data-testid":testId,style:[wonderBlocksTypography.styles.BodyTextMediumMediumWeight,styles$6.tab,selected&&styles$6.selectedTab,style],children:[icon&&jsxRuntime.jsx(wonderBlocksCore.View,{children:React__namespace.cloneElement(icon,{size:icon.props.size??"medium"})}),children]})});const bottomSpacing=wonderBlocksTokens.sizing.size_140;const styles$6=aphrodite.StyleSheet.create({tab:{display:"flex",alignItems:"center",textWrap:"nowrap",backgroundColor:"transparent",border:"none",margin:0,padding:0,cursor:"pointer",marginBlockStart:wonderBlocksTokens.sizing.size_080,marginBlockEnd:bottomSpacing,gap:wonderBlocksTokens.sizing.size_080,position:"relative",color:wonderBlocksTokens.semanticColor.core.foreground.neutral.subtle,...focusStyles.focus,":after":{content:"''",position:"absolute",insetInlineStart:0,insetInlineEnd:0,insetBlockEnd:`calc(${bottomSpacing} * -1)`},[":hover:not([aria-selected='true'])"]:{color:wonderBlocksTokens.semanticColor.link.hover,[":after"]:{height:wonderBlocksTokens.border.width.thin,backgroundColor:wonderBlocksTokens.semanticColor.link.hover}},[":active:not([aria-selected='true'])"]:{color:wonderBlocksTokens.semanticColor.link.press,[":after"]:{height:wonderBlocksTokens.border.width.thick,backgroundColor:wonderBlocksTokens.semanticColor.link.press}}},selectedTab:{color:wonderBlocksTokens.semanticColor.link.rest}});
|
|
57
57
|
|
|
58
|
-
const StyledDiv$1=wonderBlocksCore.addStyle("div");const Tablist=React__namespace.forwardRef(function Tablist(props,ref){const{id,children,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,onBlur,testId,style}=props;return jsxRuntime.jsx(StyledDiv$1,{id:id,role:"tablist",style:[styles$5.tablist,style],ref:ref,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,onBlur:onBlur,"data-testid":testId,children:children})});const styles$5=aphrodite.StyleSheet.create({tablist:{display:"flex",gap:wonderBlocksTokens.sizing.size_240,
|
|
58
|
+
const StyledDiv$1=wonderBlocksCore.addStyle("div");const Tablist=React__namespace.forwardRef(function Tablist(props,ref){const{id,children,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,onBlur,testId,style}=props;return jsxRuntime.jsx(StyledDiv$1,{id:id,role:"tablist",style:[styles$5.tablist,style],ref:ref,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,onBlur:onBlur,"data-testid":testId,children:children})});const styles$5=aphrodite.StyleSheet.create({tablist:{display:"flex",gap:wonderBlocksTokens.sizing.size_240,borderBlockEnd:`${wonderBlocksTokens.border.width.thin} solid ${wonderBlocksTokens.semanticColor.core.border.neutral.subtle}`,paddingInline:wonderBlocksTokens.sizing.size_040}});
|
|
59
59
|
|
|
60
60
|
function getTabId(tabId){return `${tabId}-tab`}function getTabPanelId(tabId){return `${tabId}-panel`}const StyledDiv=wonderBlocksCore.addStyle("div");const Tabs=React__namespace.forwardRef(function Tabs(props,ref){const{tabs,selectedTabId,onTabSelected,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,activationMode="manual",id,testId,animated=false,styles:stylesProp,mountAllPanels=false,scrollableElementRef}=props;const focusedTabId=React__namespace.useRef(selectedTabId);const tabRefs=React__namespace.useRef({});const generatedUniqueId=React__namespace.useId();const uniqueId=id??generatedUniqueId;const tablistId=`${uniqueId}-tablist`;const visitedTabsRef=React__namespace.useRef(new Set);visitedTabsRef.current.add(selectedTabId);const tablistRef=React__namespace.useRef(null);const isTabActive=React__namespace.useCallback(tabElement=>{return tabElement.ariaSelected==="true"},[]);const{indicatorProps}=useTabIndicator({animated,tabsContainerRef:tablistRef,isTabActive});React__namespace.useEffect(()=>{focusedTabId.current=selectedTabId;},[selectedTabId]);const selectTab=React__namespace.useCallback(tabId=>{if(tabId!==selectedTabId){onTabSelected(tabId);}},[onTabSelected,selectedTabId]);const handleKeyInteraction=React__namespace.useCallback(tabId=>{const tabElement=tabRefs.current[tabId];tabElement?.focus();switch(activationMode){case"manual":{focusedTabId.current=tabId;break}case"automatic":{selectTab(tabId);break}}},[activationMode,selectTab,focusedTabId]);const handleKeyDown=React__namespace.useCallback(event=>{const currentIndex=tabs.findIndex(tab=>tab.id===focusedTabId.current);const element=event.currentTarget;const isRtl=!!element.closest("[dir=rtl]");switch(event.key){case isRtl&&wonderBlocksCore.keys.right:case!isRtl&&wonderBlocksCore.keys.left:{event.preventDefault();const prevIndex=(currentIndex-1+tabs.length)%tabs.length;handleKeyInteraction(tabs[prevIndex].id);break}case isRtl&&wonderBlocksCore.keys.left:case!isRtl&&wonderBlocksCore.keys.right:{event.preventDefault();const nextIndex=(currentIndex+1)%tabs.length;handleKeyInteraction(tabs[nextIndex].id);break}case wonderBlocksCore.keys.home:{event.preventDefault();handleKeyInteraction(tabs[0].id);break}case wonderBlocksCore.keys.end:{event.preventDefault();handleKeyInteraction(tabs[tabs.length-1].id);break}case wonderBlocksCore.keys.enter:case wonderBlocksCore.keys.space:{selectTab(focusedTabId.current);break}}},[handleKeyInteraction,selectTab,tabs,focusedTabId]);const handleTablistBlur=React__namespace.useCallback(()=>{focusedTabId.current=selectedTabId;},[selectedTabId]);if(tabs.length===0){return jsxRuntime.jsx(React__namespace.Fragment,{})}return jsxRuntime.jsxs(StyledDiv,{ref:ref,id:uniqueId,"data-testid":testId,style:[styles$4.tabs,stylesProp?.root],children:[jsxRuntime.jsxs(StyledDiv,{ref:scrollableElementRef,style:styles$4.tablistWrapper,children:[jsxRuntime.jsx(Tablist,{"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,onBlur:handleTablistBlur,id:tablistId,testId:testId&&`${testId}-tablist`,ref:tablistRef,style:stylesProp?.tablist,children:tabs.map(tab=>{const{id,label,panel:_,testId:tabTestId,icon,...otherProps}=tab;const tabProps={...otherProps,key:id,id:getTabId(id),testId:tabTestId&&getTabId(tabTestId),selected:id===selectedTabId,icon,"aria-controls":getTabPanelId(id),onClick:()=>{onTabSelected(id);},onKeyDown:handleKeyDown,ref:element=>{tabRefs.current[tab.id]=element;},style:stylesProp?.tab};if(typeof label==="function"){return label(tabProps)}return jsxRuntime.jsx(Tab,{...tabProps,children:label})})}),jsxRuntime.jsx("div",{...indicatorProps})]}),tabs.map(tab=>{return jsxRuntime.jsx(TabPanel,{id:getTabPanelId(tab.id),"aria-labelledby":getTabId(tab.id),active:selectedTabId===tab.id,testId:tab.testId&&getTabPanelId(tab.testId),style:stylesProp?.tabPanel,children:(mountAllPanels||visitedTabsRef.current.has(tab.id))&&tab.panel},tab.id)})]})});const styles$4=aphrodite.StyleSheet.create({tabs:{display:"inline-flex",flexDirection:"column",alignItems:"stretch",position:"relative"},tablistWrapper:{position:"relative",overflowX:"auto",flexShrink:0}});
|
|
61
61
|
|
|
62
|
-
const defaultLabels$1={defaultOpenerLabel:"Tabs"};const TabsDropdown=React__namespace.forwardRef((props,ref)=>{const{tabs,selectedTabId,onTabSelected,labels:labelsProp,opened,id:idProp,testId,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,styles:stylesProp}=props;const labels=React__namespace.useMemo(()=>{return {...defaultLabels$1,...labelsProp}},[labelsProp]);const generatedUniqueId=React__namespace.useId();const uniqueId=idProp??generatedUniqueId;const openerId=`${uniqueId}-opener`;const panelId=`${uniqueId}-panel`;const selectedTabItem=React__namespace.useMemo(()=>{return tabs.find(tab=>tab.id===selectedTabId)},[tabs,selectedTabId]);const processedTabs=React__namespace.useMemo(()=>{return tabs.map(tab=>({...tab,leftAccessory:tab.icon?React__namespace.cloneElement(tab.icon,{size:tab.icon.props.size??"medium"}):undefined,handleClick:()=>{onTabSelected(tab.id);}}))},[tabs,onTabSelected]);if(tabs.length===0){return jsxRuntime.jsx(React__namespace.Fragment,{})}const menuText=selectedTabItem?.label||labels.defaultOpenerLabel;return jsxRuntime.jsxs(wonderBlocksCore.View,{ref:ref,id:uniqueId,testId:testId,style:stylesProp?.root,role:"region","aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,children:[jsxRuntime.jsx(wonderBlocksDropdown.ActionMenu,{opened:opened,id:openerId,menuText:menuText,opener:()=>jsxRuntime.jsx(Button__default["default"],{testId:testId?`${testId}-opener`:undefined,kind:"tertiary",endIcon:caretDown__default["default"],style:[styles$3.opener,stylesProp?.opener],labelStyle:styles$3.labelStyle,"aria-label":selectedTabItem?.["aria-label"],startIcon:selectedTabItem?.icon,children:menuText}),style:[styles$3.actionMenu,stylesProp?.actionMenu],children:processedTabs.map(tab=>{return jsxRuntime.jsx(wonderBlocksDropdown.ActionItem,{label:tab.label,"aria-label":tab["aria-label"],onClick:tab.handleClick,active:tab.id===selectedTabId,testId:tab.testId,rightAccessory:tab.id===selectedTabId?jsxRuntime.jsx(wonderBlocksIcon.PhosphorIcon,{icon:checkCircleIcon__default["default"],size:"medium","aria-hidden":"true"}):undefined,leftAccessory:tab.leftAccessory},tab.id)})}),jsxRuntime.jsx(wonderBlocksCore.View,{id:panelId,role:"group","aria-labelledby":openerId,testId:testId?`${testId}-panel`:undefined,style:stylesProp?.tabPanel,children:selectedTabItem?.panel})]})});const styles$3=aphrodite.StyleSheet.create({actionMenu:{width:"100%",alignItems:"flex-start",borderBlockEnd:`${wonderBlocksTokens.border.width.thin} solid ${wonderBlocksTokens.semanticColor.core.border.neutral.subtle}`},opener:{position:"relative",height:"unset",paddingBlockStart:wonderBlocksTokens.sizing.size_120,paddingBlockEnd:wonderBlocksTokens.sizing.size_140,paddingInline:wonderBlocksTokens.sizing.size_180,width:"100%",justifyContent:"space-between",gap:wonderBlocksTokens.sizing.size_020},labelStyle:{flexGrow:1,
|
|
62
|
+
const defaultLabels$1={defaultOpenerLabel:"Tabs"};const TabsDropdown=React__namespace.forwardRef((props,ref)=>{const{tabs,selectedTabId,onTabSelected,labels:labelsProp,opened,id:idProp,testId,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,styles:stylesProp}=props;const labels=React__namespace.useMemo(()=>{return {...defaultLabels$1,...labelsProp}},[labelsProp]);const generatedUniqueId=React__namespace.useId();const uniqueId=idProp??generatedUniqueId;const openerId=`${uniqueId}-opener`;const panelId=`${uniqueId}-panel`;const selectedTabItem=React__namespace.useMemo(()=>{return tabs.find(tab=>tab.id===selectedTabId)},[tabs,selectedTabId]);const processedTabs=React__namespace.useMemo(()=>{return tabs.map(tab=>({...tab,leftAccessory:tab.icon?React__namespace.cloneElement(tab.icon,{size:tab.icon.props.size??"medium"}):undefined,handleClick:()=>{onTabSelected(tab.id);}}))},[tabs,onTabSelected]);if(tabs.length===0){return jsxRuntime.jsx(React__namespace.Fragment,{})}const menuText=selectedTabItem?.label||labels.defaultOpenerLabel;return jsxRuntime.jsxs(wonderBlocksCore.View,{ref:ref,id:uniqueId,testId:testId,style:stylesProp?.root,role:"region","aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,children:[jsxRuntime.jsx(wonderBlocksDropdown.ActionMenu,{opened:opened,id:openerId,menuText:menuText,opener:()=>jsxRuntime.jsx(Button__default["default"],{testId:testId?`${testId}-opener`:undefined,kind:"tertiary",endIcon:caretDown__default["default"],style:[styles$3.opener,stylesProp?.opener],labelStyle:styles$3.labelStyle,"aria-label":selectedTabItem?.["aria-label"],startIcon:selectedTabItem?.icon,children:menuText}),style:[styles$3.actionMenu,stylesProp?.actionMenu],children:processedTabs.map(tab=>{return jsxRuntime.jsx(wonderBlocksDropdown.ActionItem,{label:tab.label,"aria-label":tab["aria-label"],onClick:tab.handleClick,active:tab.id===selectedTabId,testId:tab.testId,rightAccessory:tab.id===selectedTabId?jsxRuntime.jsx(wonderBlocksIcon.PhosphorIcon,{icon:checkCircleIcon__default["default"],size:"medium","aria-hidden":"true"}):undefined,leftAccessory:tab.leftAccessory},tab.id)})}),jsxRuntime.jsx(wonderBlocksCore.View,{id:panelId,role:"group","aria-labelledby":openerId,testId:testId?`${testId}-panel`:undefined,style:stylesProp?.tabPanel,children:selectedTabItem?.panel})]})});const styles$3=aphrodite.StyleSheet.create({actionMenu:{width:"100%",alignItems:"flex-start",borderBlockEnd:`${wonderBlocksTokens.border.width.thin} solid ${wonderBlocksTokens.semanticColor.core.border.neutral.subtle}`},opener:{position:"relative",height:"unset",paddingBlockStart:wonderBlocksTokens.sizing.size_120,paddingBlockEnd:wonderBlocksTokens.sizing.size_140,paddingInline:wonderBlocksTokens.sizing.size_180,width:"100%",justifyContent:"space-between",gap:wonderBlocksTokens.sizing.size_020},labelStyle:{flexGrow:1,maxInlineSize:"100%",textAlign:"start"}});
|
|
63
63
|
|
|
64
64
|
function useResponsiveLayout(options){const{tabs,elementWithOverflowRef,containerRef,onLayoutChange}=options;const[showDropdown,setShowDropdown]=React__namespace.useState(false);const tabsWidthRef=React__namespace.useRef(null);const containerWidthRef=React__namespace.useRef(null);const tabsSignature=React__namespace.useMemo(()=>tabs.map(t=>`${t.id}:${t.label}-${t.icon?"with-icon":"without-icon"}`).join("|"),[tabs]);const checkOverflow=React__namespace.useCallback(()=>{const container=containerRef.current;if(!container){return}if(!showDropdown&&elementWithOverflowRef.current){const scrollableWrapper=elementWithOverflowRef.current;if(scrollableWrapper){const hasOverflow=scrollableWrapper.scrollWidth>scrollableWrapper.clientWidth;if(hasOverflow){tabsWidthRef.current=scrollableWrapper.scrollWidth+(container.clientWidth-scrollableWrapper.clientWidth);setShowDropdown(true);}}}else if(showDropdown&&tabsWidthRef.current){const containerWidth=container.clientWidth;if(containerWidth>=tabsWidthRef.current){setShowDropdown(false);}}},[showDropdown,elementWithOverflowRef,containerRef]);React__namespace.useEffect(()=>{if(showDropdown){tabsWidthRef.current=null;setShowDropdown(false);}},[tabsSignature]);React__namespace.useEffect(()=>{const container=containerRef.current;if(!container||!window.ResizeObserver){return}const resizeObserver=new ResizeObserver(()=>{if(containerWidthRef.current!==container.clientWidth){containerWidthRef.current=container.clientWidth;checkOverflow();}});resizeObserver.observe(container);return ()=>{resizeObserver.disconnect();}},[checkOverflow,containerRef]);React__namespace.useEffect(()=>{const element=elementWithOverflowRef.current;if(!element){return}checkOverflow();const mutationObserver=new MutationObserver(()=>{checkOverflow();});mutationObserver.observe(element,{childList:true,subtree:true,characterData:true});return ()=>{mutationObserver.disconnect();}},[checkOverflow,elementWithOverflowRef]);React__namespace.useEffect(()=>{onLayoutChange?.(showDropdown?"dropdown":"tabs");},[showDropdown,onLayoutChange]);return {showDropdown}}
|
|
65
65
|
|
|
66
|
-
const ResponsiveTabs=props=>{const{tabs,selectedTabId,onTabSelected,onLayoutChange,id,testId,tabsProps,dropdownProps,styles:stylesProp,...ariaProps}=props;const tabsRef=React__namespace.useRef(null);const scrollableTabsRef=React__namespace.useRef(null);const containerRef=React__namespace.useRef(null);const{showDropdown}=useResponsiveLayout({tabs,elementWithOverflowRef:scrollableTabsRef,containerRef,onLayoutChange});return jsxRuntime.jsx(wonderBlocksCore.View,{ref:containerRef,style:[styles$2.container,stylesProp?.root],id:id,testId:testId,children:showDropdown?React.createElement(TabsDropdown,{...dropdownProps,...ariaProps,key:"dropdown",tabs:tabs,selectedTabId:selectedTabId,onTabSelected:onTabSelected,styles:{...dropdownProps?.styles,root:[styles$2.fadeIn,dropdownProps?.styles?.root]}}):React.createElement(Tabs,{...tabsProps,...ariaProps,key:"tabs",ref:tabsRef,scrollableElementRef:scrollableTabsRef,tabs:tabs,selectedTabId:selectedTabId,onTabSelected:onTabSelected,styles:{...tabsProps?.styles,root:[styles$2.fadeIn,tabsProps?.styles?.root]}})})};const fadeInKeyframes$1={from:{opacity:0},to:{opacity:1}};const styles$2=aphrodite.StyleSheet.create({fadeIn:{animationName:fadeInKeyframes$1,animationDuration:"150ms",animationTimingFunction:"ease-in-out"},container:{width:"100%",
|
|
66
|
+
const ResponsiveTabs=props=>{const{tabs,selectedTabId,onTabSelected,onLayoutChange,id,testId,tabsProps,dropdownProps,styles:stylesProp,...ariaProps}=props;const tabsRef=React__namespace.useRef(null);const scrollableTabsRef=React__namespace.useRef(null);const containerRef=React__namespace.useRef(null);const{showDropdown}=useResponsiveLayout({tabs,elementWithOverflowRef:scrollableTabsRef,containerRef,onLayoutChange});return jsxRuntime.jsx(wonderBlocksCore.View,{ref:containerRef,style:[styles$2.container,stylesProp?.root],id:id,testId:testId,children:showDropdown?React.createElement(TabsDropdown,{...dropdownProps,...ariaProps,key:"dropdown",tabs:tabs,selectedTabId:selectedTabId,onTabSelected:onTabSelected,styles:{...dropdownProps?.styles,root:[styles$2.fadeIn,dropdownProps?.styles?.root]}}):React.createElement(Tabs,{...tabsProps,...ariaProps,key:"tabs",ref:tabsRef,scrollableElementRef:scrollableTabsRef,tabs:tabs,selectedTabId:selectedTabId,onTabSelected:onTabSelected,styles:{...tabsProps?.styles,root:[styles$2.fadeIn,tabsProps?.styles?.root]}})})};const fadeInKeyframes$1={from:{opacity:0},to:{opacity:1}};const styles$2=aphrodite.StyleSheet.create({fadeIn:{animationName:fadeInKeyframes$1,animationDuration:"150ms",animationTimingFunction:"ease-in-out"},container:{width:"100%",minBlockSize:"auto"}});
|
|
67
67
|
|
|
68
|
-
const defaultLabels={defaultOpenerLabel:"Tabs"};const NavigationTabsDropdown=React__namespace.forwardRef((props,ref)=>{const{tabs,selectedTabId,onTabSelected,id:idProp,testId,labels:labelsProp,opened,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,tag="nav",styles:stylesProp,showDivider=false}=props;const StyledTag=React__namespace.useMemo(()=>wonderBlocksCore.addStyle(tag),[tag]);const labels=React__namespace.useMemo(()=>{return {...defaultLabels,...labelsProp}},[labelsProp]);const selectedTabItem=React__namespace.useMemo(()=>{return tabs.find(tab=>tab.id===selectedTabId)},[tabs,selectedTabId]);const processedTabs=React__namespace.useMemo(()=>{return tabs.map(tab=>({...tab,leftAccessory:tab.icon?React__namespace.cloneElement(tab.icon,{size:tab.icon.props.size??"medium"}):undefined,handleClick:onTabSelected?()=>onTabSelected(tab.id):undefined}))},[tabs,onTabSelected]);const generatedUniqueId=React__namespace.useId();const uniqueId=idProp??generatedUniqueId;const openerId=`${uniqueId}-opener`;if(tabs.length===0){return jsxRuntime.jsx(React__namespace.Fragment,{})}const menuText=selectedTabItem?.label||labels.defaultOpenerLabel;return jsxRuntime.jsx(StyledTag,{ref:ref,id:uniqueId,"data-testid":testId,style:[showDivider&&styles$1.divider,stylesProp?.root],"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,children:jsxRuntime.jsx(wonderBlocksDropdown.ActionMenu,{opened:opened,id:openerId,menuText:menuText,opener:()=>jsxRuntime.jsx(Button__default["default"],{testId:testId?`${testId}-opener`:undefined,kind:"tertiary",endIcon:caretDown__default["default"],style:[styles$1.opener,stylesProp?.opener],labelStyle:styles$1.labelStyle,"aria-label":selectedTabItem?.["aria-label"],startIcon:selectedTabItem?.icon,children:menuText}),style:[styles$1.actionMenu,stylesProp?.actionMenu],children:processedTabs.map(tab=>{return jsxRuntime.jsx(wonderBlocksDropdown.ActionItem,{label:tab.label,href:tab.href,"aria-label":tab["aria-label"],active:tab.id===selectedTabId,testId:tab.testId,leftAccessory:tab.leftAccessory,onClick:tab.handleClick,rightAccessory:tab.id===selectedTabId?jsxRuntime.jsx(wonderBlocksIcon.PhosphorIcon,{icon:checkCircleIcon__default["default"],size:"medium","aria-hidden":"true"}):undefined},tab.id)})})})});const styles$1=aphrodite.StyleSheet.create({actionMenu:{width:"100%",alignItems:"flex-start"},divider:{borderBlockEnd:`${wonderBlocksTokens.border.width.thin} solid ${wonderBlocksTokens.semanticColor.core.border.neutral.subtle}`},opener:{position:"relative",height:"unset",paddingBlockStart:wonderBlocksTokens.sizing.size_120,paddingBlockEnd:wonderBlocksTokens.sizing.size_140,paddingInline:wonderBlocksTokens.sizing.size_180,width:"100%",justifyContent:"space-between",gap:wonderBlocksTokens.sizing.size_020},labelStyle:{flexGrow:1,
|
|
68
|
+
const defaultLabels={defaultOpenerLabel:"Tabs"};const NavigationTabsDropdown=React__namespace.forwardRef((props,ref)=>{const{tabs,selectedTabId,onTabSelected,id:idProp,testId,labels:labelsProp,opened,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,tag="nav",styles:stylesProp,showDivider=false}=props;const StyledTag=React__namespace.useMemo(()=>wonderBlocksCore.addStyle(tag),[tag]);const labels=React__namespace.useMemo(()=>{return {...defaultLabels,...labelsProp}},[labelsProp]);const selectedTabItem=React__namespace.useMemo(()=>{return tabs.find(tab=>tab.id===selectedTabId)},[tabs,selectedTabId]);const processedTabs=React__namespace.useMemo(()=>{return tabs.map(tab=>({...tab,leftAccessory:tab.icon?React__namespace.cloneElement(tab.icon,{size:tab.icon.props.size??"medium"}):undefined,handleClick:onTabSelected?()=>onTabSelected(tab.id):undefined}))},[tabs,onTabSelected]);const generatedUniqueId=React__namespace.useId();const uniqueId=idProp??generatedUniqueId;const openerId=`${uniqueId}-opener`;if(tabs.length===0){return jsxRuntime.jsx(React__namespace.Fragment,{})}const menuText=selectedTabItem?.label||labels.defaultOpenerLabel;return jsxRuntime.jsx(StyledTag,{ref:ref,id:uniqueId,"data-testid":testId,style:[showDivider&&styles$1.divider,stylesProp?.root],"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,children:jsxRuntime.jsx(wonderBlocksDropdown.ActionMenu,{opened:opened,id:openerId,menuText:menuText,opener:()=>jsxRuntime.jsx(Button__default["default"],{testId:testId?`${testId}-opener`:undefined,kind:"tertiary",endIcon:caretDown__default["default"],style:[styles$1.opener,stylesProp?.opener],labelStyle:styles$1.labelStyle,"aria-label":selectedTabItem?.["aria-label"],startIcon:selectedTabItem?.icon,children:menuText}),style:[styles$1.actionMenu,stylesProp?.actionMenu],children:processedTabs.map(tab=>{return jsxRuntime.jsx(wonderBlocksDropdown.ActionItem,{label:tab.label,href:tab.href,"aria-label":tab["aria-label"],active:tab.id===selectedTabId,testId:tab.testId,leftAccessory:tab.leftAccessory,onClick:tab.handleClick,rightAccessory:tab.id===selectedTabId?jsxRuntime.jsx(wonderBlocksIcon.PhosphorIcon,{icon:checkCircleIcon__default["default"],size:"medium","aria-hidden":"true"}):undefined},tab.id)})})})});const styles$1=aphrodite.StyleSheet.create({actionMenu:{width:"100%",alignItems:"flex-start"},divider:{borderBlockEnd:`${wonderBlocksTokens.border.width.thin} solid ${wonderBlocksTokens.semanticColor.core.border.neutral.subtle}`},opener:{position:"relative",height:"unset",paddingBlockStart:wonderBlocksTokens.sizing.size_120,paddingBlockEnd:wonderBlocksTokens.sizing.size_140,paddingInline:wonderBlocksTokens.sizing.size_180,width:"100%",justifyContent:"space-between",gap:wonderBlocksTokens.sizing.size_020},labelStyle:{flexGrow:1,maxInlineSize:"100%",textAlign:"start"}});
|
|
69
69
|
|
|
70
|
-
const ResponsiveNavigationTabs=props=>{const{tabs,selectedTabId,onTabSelected,onLayoutChange,id,testId,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,tag,tabsProps,dropdownProps,styles:stylesProp,showDivider=false}=props;const navigationTabsRef=React__namespace.useRef(null);const containerRef=React__namespace.useRef(null);const{showDropdown}=useResponsiveLayout({tabs,elementWithOverflowRef:navigationTabsRef,containerRef,onLayoutChange});const processedTabs=React__namespace.useMemo(()=>{return tabs.map(tab=>({...tab,startIcon:tab.icon?React__namespace.cloneElement(tab.icon,{size:tab.icon.props.size??"medium"}):undefined,handleClick:onTabSelected?()=>onTabSelected(tab.id):undefined}))},[tabs,onTabSelected]);return jsxRuntime.jsx(wonderBlocksCore.View,{ref:containerRef,style:[styles.container,stylesProp?.root],id:id,testId:testId,children:showDropdown?React.createElement(NavigationTabsDropdown,{...dropdownProps,key:"dropdown",tabs:tabs,selectedTabId:selectedTabId,onTabSelected:onTabSelected,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,tag:tag,styles:{...dropdownProps?.styles,root:[styles.fadeIn,dropdownProps?.styles?.root]},showDivider:showDivider}):React.createElement(NavigationTabs,{...tabsProps,key:"tabs",ref:navigationTabsRef,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,tag:tag,styles:{...tabsProps?.styles,root:[styles.fadeIn,tabsProps?.styles?.root]},showDivider:showDivider},processedTabs.map(tab=>jsxRuntime.jsx(NavigationTabItem,{current:tab.id===selectedTabId,children:jsxRuntime.jsx(Link__default["default"],{href:tab.href,onClick:tab.handleClick,startIcon:tab.startIcon,"aria-label":tab["aria-label"],testId:tab.testId,children:tab.label})},tab.id)))})};const fadeInKeyframes={from:{opacity:0},to:{opacity:1}};const styles=aphrodite.StyleSheet.create({fadeIn:{animationName:fadeInKeyframes,animationDuration:"150ms",animationTimingFunction:"ease-in-out"},container:{width:"100%",
|
|
70
|
+
const ResponsiveNavigationTabs=props=>{const{tabs,selectedTabId,onTabSelected,onLayoutChange,id,testId,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,tag,tabsProps,dropdownProps,styles:stylesProp,showDivider=false}=props;const navigationTabsRef=React__namespace.useRef(null);const containerRef=React__namespace.useRef(null);const{showDropdown}=useResponsiveLayout({tabs,elementWithOverflowRef:navigationTabsRef,containerRef,onLayoutChange});const processedTabs=React__namespace.useMemo(()=>{return tabs.map(tab=>({...tab,startIcon:tab.icon?React__namespace.cloneElement(tab.icon,{size:tab.icon.props.size??"medium"}):undefined,handleClick:onTabSelected?()=>onTabSelected(tab.id):undefined}))},[tabs,onTabSelected]);return jsxRuntime.jsx(wonderBlocksCore.View,{ref:containerRef,style:[styles.container,stylesProp?.root],id:id,testId:testId,children:showDropdown?React.createElement(NavigationTabsDropdown,{...dropdownProps,key:"dropdown",tabs:tabs,selectedTabId:selectedTabId,onTabSelected:onTabSelected,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,tag:tag,styles:{...dropdownProps?.styles,root:[styles.fadeIn,dropdownProps?.styles?.root]},showDivider:showDivider}):React.createElement(NavigationTabs,{...tabsProps,key:"tabs",ref:navigationTabsRef,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledby,tag:tag,styles:{...tabsProps?.styles,root:[styles.fadeIn,tabsProps?.styles?.root]},showDivider:showDivider},processedTabs.map(tab=>jsxRuntime.jsx(NavigationTabItem,{current:tab.id===selectedTabId,children:jsxRuntime.jsx(Link__default["default"],{href:tab.href,onClick:tab.handleClick,startIcon:tab.startIcon,"aria-label":tab["aria-label"],testId:tab.testId,children:tab.label})},tab.id)))})};const fadeInKeyframes={from:{opacity:0},to:{opacity:1}};const styles=aphrodite.StyleSheet.create({fadeIn:{animationName:fadeInKeyframes,animationDuration:"150ms",animationTimingFunction:"ease-in-out"},container:{width:"100%",minBlockSize:"auto"}});
|
|
71
71
|
|
|
72
72
|
exports.NavigationTabItem = NavigationTabItem;
|
|
73
73
|
exports.NavigationTabs = NavigationTabs;
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Tabs are used to control what content is shown",
|
|
4
4
|
"author": "Khan Academy",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "0.5.
|
|
6
|
+
"version": "0.5.15",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
@@ -21,13 +21,13 @@
|
|
|
21
21
|
"types": "dist/index.d.ts",
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@phosphor-icons/core": "^2.0.2",
|
|
24
|
-
"@khanacademy/wonder-blocks-button": "11.6.
|
|
25
|
-
"@khanacademy/wonder-blocks-core": "12.4.
|
|
26
|
-
"@khanacademy/wonder-blocks-dropdown": "10.9.
|
|
27
|
-
"@khanacademy/wonder-blocks-icon": "5.3.
|
|
28
|
-
"@khanacademy/wonder-blocks-link": "10.
|
|
24
|
+
"@khanacademy/wonder-blocks-button": "11.6.1",
|
|
25
|
+
"@khanacademy/wonder-blocks-core": "12.4.4",
|
|
26
|
+
"@khanacademy/wonder-blocks-dropdown": "10.9.1",
|
|
27
|
+
"@khanacademy/wonder-blocks-icon": "5.3.15",
|
|
28
|
+
"@khanacademy/wonder-blocks-link": "10.3.0",
|
|
29
29
|
"@khanacademy/wonder-blocks-tokens": "16.5.0",
|
|
30
|
-
"@khanacademy/wonder-blocks-typography": "4.3.
|
|
30
|
+
"@khanacademy/wonder-blocks-typography": "4.3.5"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"aphrodite": "^1.2.5",
|