155 lines
5.3 KiB
JavaScript
155 lines
5.3 KiB
JavaScript
'use client';
|
|
import { iconFilledClassName, iconRegularClassName } from '@fluentui/react-icons';
|
|
import { tokens, typographyStyles } from '@fluentui/react-theme';
|
|
import { makeResetStyles, makeStyles } from '@griffel/react';
|
|
// Styles shared by several nav components.
|
|
export const navItemTokens = {
|
|
defaultDrawerWidth: 260,
|
|
indicatorOffset: 16,
|
|
indicatorWidth: 4,
|
|
indicatorHeight: 20,
|
|
backgroundColor: tokens.colorNeutralBackground4,
|
|
backgroundColorHover: tokens.colorNeutralBackground4Hover,
|
|
backgroundColorPressed: tokens.colorNeutralBackground4Pressed,
|
|
animationTokens: {
|
|
animationDuration: tokens.durationFaster,
|
|
animationFillMode: 'both',
|
|
animationTimingFunction: tokens.curveLinear
|
|
},
|
|
transitionTokens: {
|
|
transitionDuration: tokens.durationFaster,
|
|
transitionTimingFunction: tokens.curveLinear,
|
|
transitionProperty: 'background'
|
|
}
|
|
};
|
|
/**
|
|
* Styles for the root slot
|
|
* Shared across NavItem, NavCategoryItem, NavSubItem, and AppItem
|
|
*/ export const useRootDefaultClassName = makeResetStyles({
|
|
display: 'flex',
|
|
textTransform: 'none',
|
|
position: 'relative',
|
|
justifyContent: 'start',
|
|
alignItems: 'flex-start',
|
|
textAlign: 'left',
|
|
gap: tokens.spacingVerticalL,
|
|
padding: `${tokens.spacingVerticalMNudge} ${tokens.spacingHorizontalS} ${tokens.spacingVerticalMNudge} ${tokens.spacingHorizontalMNudge}`,
|
|
backgroundColor: navItemTokens.backgroundColor,
|
|
borderRadius: tokens.borderRadiusMedium,
|
|
color: tokens.colorNeutralForeground2,
|
|
textDecorationLine: 'none',
|
|
border: 'none',
|
|
// this element can change between a button and an anchor
|
|
// so we need to reset box sizing to prevent horizontal overflow
|
|
boxSizing: 'border-box',
|
|
cursor: 'pointer',
|
|
transitionDuration: navItemTokens.animationTokens.animationDuration,
|
|
transitionTimingFunction: navItemTokens.animationTokens.animationTimingFunction,
|
|
transitionProperty: 'background',
|
|
width: '100%',
|
|
...typographyStyles.body1,
|
|
':hover': {
|
|
backgroundColor: navItemTokens.backgroundColorHover
|
|
},
|
|
// Use custom insert focus indicator
|
|
'&:focus-visible': {
|
|
outline: `${tokens.strokeWidthThick} solid ${tokens.colorStrokeFocus2}`,
|
|
outlineOffset: `calc(${tokens.strokeWidthThick} * -1)`
|
|
}
|
|
});
|
|
export const useSmallStyles = makeStyles({
|
|
root: {
|
|
padding: `${tokens.spacingVerticalXS} ${tokens.spacingHorizontalS} ${tokens.spacingVerticalXS} ${tokens.spacingHorizontalMNudge}`
|
|
}
|
|
});
|
|
/**
|
|
* Styles for the content slot (children)
|
|
* Shared across NavItem, NavCategoryItem, NavSubItem, and AppItem
|
|
*/ export const useContentStyles = makeStyles({
|
|
selected: typographyStyles.body1Strong
|
|
});
|
|
/**
|
|
* French fry styles
|
|
* Shared across NavItem, NavCategoryItem, and NavSubItem
|
|
*/ export const useIndicatorStyles = makeStyles({
|
|
base: {
|
|
'::after': {
|
|
position: 'absolute',
|
|
...navItemTokens.animationTokens,
|
|
animationName: {
|
|
'0%': {
|
|
background: 'transparent'
|
|
},
|
|
'100%': {
|
|
background: tokens.colorCompoundBrandForeground1
|
|
}
|
|
},
|
|
marginInlineStart: `-${navItemTokens.indicatorOffset}px`,
|
|
backgroundColor: tokens.colorCompoundBrandForeground1,
|
|
height: `${navItemTokens.indicatorHeight}px`,
|
|
width: `${navItemTokens.indicatorWidth}px`,
|
|
borderRadius: tokens.borderRadiusCircular,
|
|
content: '""'
|
|
},
|
|
'@media (forced-colors: active)': {
|
|
'::after': {
|
|
outline: `solid 2px ${tokens.colorTransparentStroke}`,
|
|
outlineOffset: '-2px'
|
|
}
|
|
}
|
|
}
|
|
});
|
|
/**
|
|
* Styles for the icon slot
|
|
* Shared across NavItem, NavCategoryItem, and NavSubItem
|
|
* We use the grid trick to stack the filled and regular icons on top of each other
|
|
*/ export const useIconStyles = makeStyles({
|
|
base: {
|
|
display: 'grid',
|
|
gridTemplateAreas: 'overlay-area',
|
|
minHeight: '20px',
|
|
minWidth: '20px',
|
|
alignItems: 'top',
|
|
justifyContent: 'center',
|
|
overflow: 'hidden',
|
|
[`& .${iconFilledClassName}`]: {
|
|
gridArea: 'overlay-area',
|
|
color: 'transparent',
|
|
display: 'none'
|
|
},
|
|
[`& .${iconRegularClassName}`]: {
|
|
gridArea: 'overlay-area',
|
|
display: 'inline'
|
|
}
|
|
},
|
|
selected: {
|
|
[`& .${iconFilledClassName}`]: {
|
|
...navItemTokens.animationTokens,
|
|
display: 'inline',
|
|
animationName: {
|
|
'0%': {
|
|
opacity: 0,
|
|
color: 'transparent'
|
|
},
|
|
'100%': {
|
|
opacity: 1,
|
|
color: tokens.colorNeutralForeground2BrandSelected
|
|
}
|
|
}
|
|
},
|
|
[`& .${iconRegularClassName}`]: {
|
|
...navItemTokens.animationTokens,
|
|
animationName: {
|
|
'0%': {
|
|
opacity: 1,
|
|
color: tokens.colorNeutralForeground2
|
|
},
|
|
'100%': {
|
|
opacity: 0,
|
|
color: 'transparent'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|