@khanacademy/wonder-blocks-tabs 0.5.8 → 0.5.9
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 +9 -0
- package/dist/es/index.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# @khanacademy/wonder-blocks-tabs
|
|
2
2
|
|
|
3
|
+
## 0.5.9
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 77156ee: Improve robustness of responsive logic in ResponsiveTabs and ResponsiveNavigationTabs by:
|
|
8
|
+
- Using a MutationObserver to trigger when we should check for overflow to decide if a dropdown or horizontal tabs layout should be used
|
|
9
|
+
- Fixing edge cases where additional container padding/margin could cause continuous layout changes
|
|
10
|
+
- Only trigger the check for overflow when the width changes in the ResizeObserver
|
|
11
|
+
|
|
3
12
|
## 0.5.8
|
|
4
13
|
|
|
5
14
|
### Patch Changes
|
package/dist/es/index.js
CHANGED
|
@@ -32,7 +32,7 @@ function getTabId(tabId){return `${tabId}-tab`}function getTabPanelId(tabId){ret
|
|
|
32
32
|
|
|
33
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,maxWidth:"100%",textAlign:"start"}});
|
|
34
34
|
|
|
35
|
-
function useResponsiveLayout(options){const{tabs,elementWithOverflowRef,containerRef,onLayoutChange}=options;const[showDropdown,setShowDropdown]=React.useState(false);const tabsWidthRef=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;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);}
|
|
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
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%",minHeight:"auto"}});
|
|
38
38
|
|
package/dist/index.js
CHANGED
|
@@ -61,7 +61,7 @@ function getTabId(tabId){return `${tabId}-tab`}function getTabPanelId(tabId){ret
|
|
|
61
61
|
|
|
62
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,maxWidth:"100%",textAlign:"start"}});
|
|
63
63
|
|
|
64
|
-
function useResponsiveLayout(options){const{tabs,elementWithOverflowRef,containerRef,onLayoutChange}=options;const[showDropdown,setShowDropdown]=React__namespace.useState(false);const tabsWidthRef=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;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);}
|
|
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
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%",minHeight:"auto"}});
|
|
67
67
|
|
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.9",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"types": "dist/index.d.ts",
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@phosphor-icons/core": "^2.0.2",
|
|
24
|
-
"@khanacademy/wonder-blocks-core": "12.4.3",
|
|
25
24
|
"@khanacademy/wonder-blocks-button": "11.5.1",
|
|
25
|
+
"@khanacademy/wonder-blocks-core": "12.4.3",
|
|
26
26
|
"@khanacademy/wonder-blocks-icon": "5.3.10",
|
|
27
27
|
"@khanacademy/wonder-blocks-dropdown": "10.8.5",
|
|
28
28
|
"@khanacademy/wonder-blocks-link": "10.1.8",
|