@linagora/twake-mui 10.0.0 → 10.1.0

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.
Files changed (39) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/components/Layout/Layout.stories.js +4 -4
  3. package/dist/components/NavDesktopLimiter/index.js +3 -3
  4. package/dist/components/NavItem/NavBadge.d.ts +2 -0
  5. package/dist/components/NavItem/NavBadge.js +12 -0
  6. package/dist/components/NavItem/NavIndicator.d.ts +2 -0
  7. package/dist/components/NavItem/NavIndicator.js +7 -0
  8. package/dist/components/NavItem/NavItem.stories.d.ts +30 -0
  9. package/dist/components/NavItem/NavItem.stories.js +110 -0
  10. package/dist/components/NavItem/NavItemActions.d.ts +1 -0
  11. package/dist/components/NavItem/NavItemActions.js +11 -0
  12. package/dist/components/NavItem/NavItemRoot.d.ts +3 -0
  13. package/dist/components/NavItem/NavItemRoot.js +49 -0
  14. package/dist/components/NavItem/index.d.ts +9 -5
  15. package/dist/components/NavItem/index.js +19 -30
  16. package/dist/components/NavLink/NavDropdown.d.ts +7 -0
  17. package/dist/components/NavLink/NavDropdown.js +19 -0
  18. package/dist/components/NavLink/NavLink.d.ts +4 -0
  19. package/dist/components/NavLink/NavLink.js +6 -0
  20. package/dist/components/NavLink/NavLinkBase.d.ts +5 -0
  21. package/dist/components/NavLink/NavLinkBase.js +15 -0
  22. package/dist/components/NavLink/NavLinkRoot.d.ts +2 -0
  23. package/dist/components/NavLink/NavLinkRoot.js +41 -0
  24. package/dist/components/NavText/NavPrimaryText.d.ts +2 -0
  25. package/dist/components/NavText/NavPrimaryText.js +12 -0
  26. package/dist/components/NavText/NavSecondaryText.d.ts +2 -0
  27. package/dist/components/NavText/NavSecondaryText.js +8 -0
  28. package/dist/components/NavText/NavTertiaryText.d.ts +2 -0
  29. package/dist/components/NavText/NavTertiaryText.js +16 -0
  30. package/dist/components/NavText/NavTextRoot.d.ts +1 -0
  31. package/dist/components/NavText/NavTextRoot.js +7 -0
  32. package/dist/components/NavText/index.d.ts +6 -2
  33. package/dist/components/NavText/index.js +15 -14
  34. package/dist/components/Sidebar/Sidebar.stories.js +11 -6
  35. package/dist/index.d.ts +7 -3
  36. package/dist/index.js +4 -2
  37. package/package.json +1 -1
  38. package/dist/components/NavLink/index.d.ts +0 -3
  39. package/dist/components/NavLink/index.js +0 -38
package/CHANGELOG.md CHANGED
@@ -1,3 +1,16 @@
1
+ # @linagora/twake-mui [10.1.0](https://github.com/linagora/twake-ui/compare/@linagora/twake-mui@10.0.0...@linagora/twake-mui@10.1.0) (2026-10-06)
2
+
3
+
4
+ ### Features
5
+
6
+ * **twake-mui:** Extract NavLink into dumber components ([c5c3581](https://github.com/linagora/twake-ui/commit/c5c358176c6cdd47666e261575a9e9576bbb8b84))
7
+ * **twake-mui:** Resolve comments ([2b5a9c8](https://github.com/linagora/twake-ui/commit/2b5a9c8aa828991804858aedd1319ce78f910572))
8
+ * **twake-mui:** Update NavDesktopLimiter as secondary variant of NavItem ([8124b58](https://github.com/linagora/twake-ui/commit/8124b58176f38919f8be4399b05854a097ed043f))
9
+ * **twake-mui:** Update NavText to handle secondary and tertiary text ([bd976ce](https://github.com/linagora/twake-ui/commit/bd976ce73fd715d927f2fae967d4a27b01b89f77))
10
+ * **twake-mui:** Update new NavItem ([915a508](https://github.com/linagora/twake-ui/commit/915a50801197c39dc3f60d4d6f00da833d34554d))
11
+ * **twake-mui:** Update new NavLink ([e966794](https://github.com/linagora/twake-ui/commit/e966794971daa5fccd4205d0cf87bc0ccb964bc2))
12
+ * **twake-mui:** Update storybooks of Layout and Sidebar with new NavItem ([b792097](https://github.com/linagora/twake-ui/commit/b7920972992db48fb271aa4daf3dc078de72b605))
13
+
1
14
  # @linagora/twake-mui [10.0.0](https://github.com/linagora/twake-ui/compare/@linagora/twake-mui@9.17.0...@linagora/twake-mui@10.0.0) (2026-10-05)
2
15
 
3
16
 
@@ -7,8 +7,8 @@ import { NavDesktopDropdown } from '../NavDesktopDropdown';
7
7
  import { NavDesktopLimiter } from '../NavDesktopLimiter';
8
8
  import { NavIcon } from '../NavIcon';
9
9
  import { NavItem } from '../NavItem';
10
- import { NavLink } from '../NavLink';
11
- import { NavText } from '../NavText';
10
+ import { NavLink } from '../NavLink/NavLink';
11
+ import NavText from '../NavText';
12
12
  import { Sidebar } from '../Sidebar';
13
13
  const paragraph = 'Ada Lovelace was an English mathematician chiefly known for her work on Charles Babbage’s proposed mechanical general-purpose computer, the Analytical Engine. She was the first to recognise that the machine had applications beyond pure calculation.';
14
14
  const TopBar = () => (React.createElement(Box, { id: "coz-bar", role: "banner", sx: theme => ({
@@ -42,7 +42,7 @@ const LayoutDemo = ({ monoColumn = false, withTopBar = true, ...props }) => {
42
42
  React.createElement(NavLink, { ...makeProps(['Section 1']) },
43
43
  React.createElement(NavIcon, { icon: Warn }),
44
44
  React.createElement(NavText, null, "Section 1"))),
45
- React.createElement(NavDesktopLimiter, null, Array.from(Array(10).keys()).map(i => (React.createElement(NavItem, { secondary: true, key: i },
45
+ React.createElement(NavDesktopLimiter, null, Array.from(Array(10).keys()).map(i => (React.createElement(NavItem, { variant: "secondary", key: i },
46
46
  React.createElement(NavLink, { ...makeProps(['Section 1', `Subsection ${i}`]) },
47
47
  React.createElement(NavText, null,
48
48
  "Subsection ",
@@ -55,7 +55,7 @@ const LayoutDemo = ({ monoColumn = false, withTopBar = true, ...props }) => {
55
55
  React.createElement(NavLink, { ...makeProps(['Section 3']) },
56
56
  React.createElement(NavIcon, { icon: Download }),
57
57
  React.createElement(NavText, null, "Section 3"))),
58
- React.createElement(NavDesktopDropdown, { label: "Section 4" }, Array.from(Array(6).keys()).map(i => (React.createElement(NavItem, { secondary: true, key: i },
58
+ React.createElement(NavDesktopDropdown, { label: "Section 4" }, Array.from(Array(6).keys()).map(i => (React.createElement(NavItem, { variant: "secondary", key: i },
59
59
  React.createElement(NavLink, { ...makeProps(['Section 4', `Subsection ${i}`]) },
60
60
  React.createElement(NavText, null,
61
61
  "Subsection ",
@@ -3,8 +3,8 @@ import React, { Children, isValidElement, useState } from 'react';
3
3
  import { useExtendI18n, useI18n } from 'twake-i18n';
4
4
  import { NavIcon } from '../NavIcon';
5
5
  import { NavItem } from '../NavItem';
6
- import { NavLink } from '../NavLink';
7
- import { NavText } from '../NavText';
6
+ import { NavLink } from '../NavLink/NavLink';
7
+ import NavText from '../NavText';
8
8
  import en from './locales/en.json';
9
9
  import fr from './locales/fr.json';
10
10
  import ru from './locales/ru.json';
@@ -22,7 +22,7 @@ export const NavDesktopLimiter = ({ children, max = 5 }) => {
22
22
  return null;
23
23
  return (React.createElement(React.Fragment, null,
24
24
  viewingAll ? items : items.slice(0, max),
25
- amountHidden > 0 && (React.createElement(NavItem, { secondary: true },
25
+ amountHidden > 0 && (React.createElement(NavItem, { variant: "secondary" },
26
26
  React.createElement(NavLink, { onClick: () => setViewingAll(current => !current) },
27
27
  React.createElement(NavIcon, { icon: viewingAll ? Top : Bottom }),
28
28
  React.createElement(NavText, null, viewingAll
@@ -0,0 +1,2 @@
1
+ import { Theme } from '@mui/material/styles';
2
+ export declare const NavBadge: import("@emotion/styled").StyledComponent<import("@mui/material").BadgeOwnProps & import("@mui/material/OverridableComponent").CommonProps & Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "style" | "color" | "className" | "max" | "children" | "classes" | "sx" | "variant" | "slotProps" | "slots" | "anchorOrigin" | "badgeContent" | "invisible" | "overlap" | "showZero"> & import("@mui/system").MUIStyledCommonProps<Theme>, {}, {}>;
@@ -0,0 +1,12 @@
1
+ import { Badge, badgeClasses } from '@mui/material';
2
+ import { styled } from '@mui/material/styles';
3
+ export const NavBadge = styled(Badge)(({ theme }) => ({
4
+ [`& .${badgeClasses.badge}`]: {
5
+ border: 'none',
6
+ position: 'static',
7
+ transform: 'none',
8
+ backgroundColor: theme.vars.palette.action.selected,
9
+ color: theme.vars.palette.text.primary,
10
+ fontWeight: 500
11
+ }
12
+ }));
@@ -0,0 +1,2 @@
1
+ import { Theme } from '@mui/material/styles';
2
+ export declare const NavIndicator: import("@emotion/styled").StyledComponent<import("@mui/system").MUIStyledCommonProps<Theme>, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {}>;
@@ -0,0 +1,7 @@
1
+ import { styled } from '@mui/material/styles';
2
+ export const NavIndicator = styled('div')(({ theme }) => ({
3
+ width: 10,
4
+ height: 10,
5
+ borderRadius: '50%',
6
+ backgroundColor: theme.vars.palette.error.main
7
+ }));
@@ -0,0 +1,30 @@
1
+ import { Heart } from '@linagora/twake-icons';
2
+ import { Meta, StoryObj } from '@storybook/react-vite';
3
+ import React from 'react';
4
+ import { NavItemProps } from './index';
5
+ declare const icons: {
6
+ Heart: typeof Heart;
7
+ None: undefined;
8
+ };
9
+ declare const secondaryActions: {
10
+ None: undefined;
11
+ Dots: React.JSX.Element;
12
+ };
13
+ type NavItemStoryArgs = NavItemProps & {
14
+ text: string;
15
+ secondaryText: string;
16
+ tertiaryText: string;
17
+ tertiaryIcon: keyof typeof icons;
18
+ leftIcon: keyof typeof icons;
19
+ divider: boolean;
20
+ badge: number | string;
21
+ hasIndicator: boolean;
22
+ secondaryAction: keyof typeof secondaryActions;
23
+ hasDropdown: boolean;
24
+ disablePadding: boolean;
25
+ };
26
+ declare const meta: Meta<NavItemStoryArgs>;
27
+ export default meta;
28
+ type Story = StoryObj<NavItemStoryArgs>;
29
+ export declare const Default: Story;
30
+ export declare const Screenshot: Story;
@@ -0,0 +1,110 @@
1
+ import { Folder, Heart, Warn, Dots, Icon } from '@linagora/twake-icons';
2
+ import { IconButton } from '@mui/material';
3
+ import React from 'react';
4
+ import { NavIcon } from '../NavIcon';
5
+ import { NavDropdown } from '../NavLink/NavDropdown';
6
+ import { NavLink } from '../NavLink/NavLink';
7
+ import NavText from '../NavText';
8
+ import { NavItem } from './index';
9
+ const icons = { Heart, None: undefined };
10
+ const secondaryActions = {
11
+ None: undefined,
12
+ Dots: (React.createElement(IconButton, { size: "small", edge: "end", sx: { p: 0.5 }, onClick: event => event.stopPropagation() },
13
+ React.createElement(Icon, { icon: Dots, rotate: 90, size: 10 })))
14
+ };
15
+ const meta = {
16
+ title: 'NavItem',
17
+ component: NavItem,
18
+ tags: ['autodocs'],
19
+ argTypes: {
20
+ variant: {
21
+ control: 'select',
22
+ options: ['primary', 'secondary', 'tertiary'],
23
+ description: 'The indentation variant of the NavItem.'
24
+ },
25
+ selected: { control: 'boolean' },
26
+ text: { control: 'text' },
27
+ secondaryText: { control: 'text' },
28
+ tertiaryText: { control: 'text' },
29
+ tertiaryIcon: {
30
+ options: Object.keys(icons),
31
+ control: { type: 'select' }
32
+ },
33
+ leftIcon: {
34
+ options: Object.keys(icons),
35
+ control: { type: 'select' }
36
+ },
37
+ divider: { control: 'boolean' },
38
+ badge: { control: 'text' },
39
+ hasIndicator: { control: 'boolean' },
40
+ secondaryAction: {
41
+ options: Object.keys(secondaryActions),
42
+ mapping: secondaryActions,
43
+ control: { type: 'select' }
44
+ },
45
+ hasDropdown: { control: 'boolean' },
46
+ disablePadding: { control: 'boolean' }
47
+ }
48
+ };
49
+ export default meta;
50
+ export const Default = {
51
+ args: {
52
+ variant: 'primary',
53
+ selected: false,
54
+ text: 'Item',
55
+ secondaryText: '',
56
+ tertiaryText: '',
57
+ tertiaryIcon: 'None',
58
+ leftIcon: 'Heart',
59
+ divider: false,
60
+ badge: 1,
61
+ hasIndicator: true,
62
+ secondaryAction: 'Dots',
63
+ hasDropdown: false,
64
+ disablePadding: true
65
+ },
66
+ render: ({ selected, text, secondaryText, tertiaryText, tertiaryIcon, leftIcon, divider, badge, hasIndicator, secondaryAction, hasDropdown, disablePadding, ...args }) => {
67
+ const IconComponent = icons[leftIcon];
68
+ const TertiaryIconComponent = icons[tertiaryIcon];
69
+ const [open, setOpen] = React.useState(false);
70
+ return (React.createElement("div", { style: { width: 300 } },
71
+ React.createElement(NavItem, { ...args, hasIndicator: hasIndicator, badge: badge, secondaryAction: secondaryAction, divider: divider, disablePadding: disablePadding }, hasDropdown ? (React.createElement(NavDropdown, { selected: Boolean(selected), open: open, onClick: () => setOpen(!open) },
72
+ IconComponent && React.createElement(NavIcon, { icon: IconComponent }),
73
+ React.createElement(NavText, { secondaryText: secondaryText, tertiaryText: tertiaryText, tertiaryIcon: TertiaryIconComponent ? React.createElement(TertiaryIconComponent, null) : undefined }, text))) : (React.createElement(NavLink, { selected: Boolean(selected) },
74
+ IconComponent && React.createElement(NavIcon, { icon: IconComponent }),
75
+ React.createElement(NavText, { secondaryText: secondaryText, tertiaryText: tertiaryText, tertiaryIcon: TertiaryIconComponent ? React.createElement(TertiaryIconComponent, null) : undefined }, text))))));
76
+ }
77
+ };
78
+ export const Screenshot = {
79
+ tags: ['argos'],
80
+ render: () => {
81
+ return (React.createElement("div", { style: {
82
+ display: 'flex',
83
+ flexDirection: 'column',
84
+ gap: '16px',
85
+ width: 300
86
+ } },
87
+ React.createElement("section", null,
88
+ React.createElement("h3", null, "Primary"),
89
+ React.createElement(NavItem, { disablePadding: true, hasIndicator: true, badge: 1, secondaryAction: secondaryActions.Dots },
90
+ React.createElement(NavDropdown, null,
91
+ React.createElement(NavIcon, { icon: Heart }),
92
+ React.createElement(NavText, { secondaryText: "Secondary", tertiaryText: "Tertiary" }, "Primary Item")))),
93
+ React.createElement("section", null,
94
+ React.createElement("h3", null, "Secondary"),
95
+ React.createElement(NavItem, { disablePadding: true, variant: "secondary", selected: true },
96
+ React.createElement(NavDropdown, null,
97
+ React.createElement(NavText, null, "Secondary Item")))),
98
+ React.createElement("section", null,
99
+ React.createElement("h3", null, "Tertiary"),
100
+ React.createElement(NavItem, { disablePadding: true, variant: "tertiary" },
101
+ React.createElement(NavLink, null,
102
+ React.createElement(NavText, { tertiaryIcon: React.createElement(Warn, null) }, "Tertiary Item")))),
103
+ React.createElement("section", null,
104
+ React.createElement("h3", null, "Divider"),
105
+ React.createElement(NavItem, { disablePadding: true, divider: true },
106
+ React.createElement(NavLink, null,
107
+ React.createElement(NavIcon, { icon: Folder }),
108
+ React.createElement(NavText, null, "Item with Divider"))))));
109
+ }
110
+ };
@@ -0,0 +1 @@
1
+ export declare const NavItemActions: import("@emotion/styled").StyledComponent<import("@mui/system").MUIStyledCommonProps<import("@mui/material/styles").Theme>, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {}>;
@@ -0,0 +1,11 @@
1
+ import { styled } from '@mui/material/styles';
2
+ export const NavItemActions = styled('div')({
3
+ display: 'flex',
4
+ alignItems: 'center',
5
+ gap: 8,
6
+ position: 'absolute',
7
+ right: 8,
8
+ top: '50%',
9
+ transform: 'translateY(-50%)',
10
+ flexShrink: 0
11
+ });
@@ -0,0 +1,3 @@
1
+ export declare const NavItemRoot: import("@emotion/styled").StyledComponent<import("@mui/material").ListItemOwnProps & import("@mui/material/OverridableComponent").CommonProps & Omit<import("react").DetailedHTMLProps<import("react").LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>, "style" | "className" | "children" | "alignItems" | "dense" | "disablePadding" | "disableGutters" | "classes" | "sx" | "divider" | "slotProps" | "slots" | "secondaryAction"> & import("@mui/system").MUIStyledCommonProps<import("@mui/material").Theme> & {
2
+ variant?: "primary" | "secondary" | "tertiary";
3
+ }, {}, {}>;
@@ -0,0 +1,49 @@
1
+ import { ListItem } from '@mui/material';
2
+ import { styled } from '@mui/material/styles';
3
+ export const NavItemRoot = styled(ListItem, {
4
+ shouldForwardProp: prop => prop !== 'secondary' && prop !== 'variant'
5
+ })(({ theme, variant = 'primary' }) => {
6
+ const isSecondary = variant === 'secondary';
7
+ const isTertiary = variant === 'tertiary';
8
+ return {
9
+ padding: 0,
10
+ minHeight: 36,
11
+ height: 'auto',
12
+ display: 'flex',
13
+ alignItems: 'center',
14
+ position: 'relative',
15
+ borderRadius: 8,
16
+ cursor: 'pointer',
17
+ margin: '0 16px',
18
+ width: 'calc(100% - 32px)',
19
+ transition: theme.transitions.create('background-color'),
20
+ '&:hover': {
21
+ backgroundColor: theme.vars.palette.action.hover
22
+ },
23
+ [`&.Mui-selected, &:has(.Mui-selected), &:has(.active)`]: {
24
+ color: theme.vars.palette.primary.main,
25
+ backgroundColor: theme.vars.palette.action.selected,
26
+ '&:hover': {
27
+ backgroundColor: theme.vars.palette.action.focus
28
+ }
29
+ },
30
+ [theme.breakpoints.down('lg')]: {
31
+ display: 'block',
32
+ height: 'auto',
33
+ margin: '0 12px',
34
+ flex: '0 0 40px',
35
+ [`&.Mui-selected, &:has(.Mui-selected), &:has(.active)`]: {
36
+ color: theme.vars.palette.text.primary,
37
+ backgroundColor: 'transparent',
38
+ '&:hover': {
39
+ backgroundColor: 'transparent'
40
+ }
41
+ }
42
+ },
43
+ ...((isSecondary || isTertiary) && {
44
+ margin: '3px 16px',
45
+ [theme.breakpoints.down('lg')]: { display: 'none' },
46
+ paddingLeft: isTertiary ? '3rem' : '2rem'
47
+ })
48
+ };
49
+ });
@@ -1,6 +1,10 @@
1
- import { Theme } from '@mui/material/styles';
1
+ import { ListItem } from '@mui/material';
2
2
  import React from 'react';
3
- export declare const NavItem: import("@emotion/styled").StyledComponent<import("@mui/material").ListItemOwnProps & import("@mui/material/OverridableComponent").CommonProps & Omit<React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>, "style" | "className" | "children" | "alignItems" | "dense" | "disablePadding" | "disableGutters" | "classes" | "sx" | "divider" | "slotProps" | "slots" | "secondaryAction"> & import("@mui/system").MUIStyledCommonProps<Theme> & {
4
- secondary?: boolean;
5
- }, {}, {}>;
6
- export type NavItemProps = React.ComponentProps<typeof NavItem>;
3
+ export interface NavItemProps extends React.ComponentProps<typeof ListItem> {
4
+ variant?: 'primary' | 'secondary' | 'tertiary';
5
+ selected?: boolean;
6
+ badge?: React.ReactNode;
7
+ hasIndicator?: boolean;
8
+ divider?: boolean;
9
+ }
10
+ export declare const NavItem: React.ForwardRefExoticComponent<Omit<NavItemProps, "ref"> & React.RefAttributes<HTMLLIElement>>;
@@ -1,30 +1,19 @@
1
- import { ListItem, listItemButtonClasses } from '@mui/material';
2
- import { styled } from '@mui/material/styles';
3
- export const NavItem = styled(ListItem, {
4
- shouldForwardProp: prop => prop !== 'secondary'
5
- })(({ theme, secondary }) => ({
6
- padding: 0,
7
- height: 36,
8
- minHeight: 0,
9
- [theme.breakpoints.down('lg')]: {
10
- display: 'block',
11
- height: 'auto',
12
- margin: '0 12px',
13
- flex: '0 0 40px'
14
- },
15
- ...(secondary && {
16
- height: 'auto',
17
- margin: '3px 0',
18
- [theme.breakpoints.down('lg')]: { display: 'none' },
19
- [`& .${listItemButtonClasses.root}`]: {
20
- margin: '0 16px 0 2.8rem',
21
- padding: '8px 16px',
22
- height: 'auto',
23
- fontSize: theme.typography.pxToRem(14),
24
- [`&.${listItemButtonClasses.selected}, &.active`]: {
25
- color: theme.vars.palette.secondary.contrastText,
26
- backgroundColor: theme.vars.palette.secondary.main
27
- }
28
- }
29
- })
30
- }));
1
+ import { Divider } from '@mui/material';
2
+ import cx from 'classnames';
3
+ import React, { forwardRef } from 'react';
4
+ import { NavBadge } from './NavBadge';
5
+ import { NavIndicator } from './NavIndicator';
6
+ import { NavItemActions } from './NavItemActions';
7
+ import { NavItemRoot } from './NavItemRoot';
8
+ export const NavItem = forwardRef((props, ref) => {
9
+ const { variant = 'primary', selected, children, className, badge, hasIndicator, secondaryAction, divider, ...rest } = props;
10
+ return (React.createElement(React.Fragment, null,
11
+ React.createElement(NavItemRoot, { ref: ref, variant: variant, className: cx(className, selected && 'Mui-selected'), ...rest },
12
+ children,
13
+ (hasIndicator || badge || secondaryAction) && (React.createElement(NavItemActions, null,
14
+ hasIndicator && React.createElement(NavIndicator, null),
15
+ badge && React.createElement(NavBadge, { badgeContent: badge }),
16
+ secondaryAction))),
17
+ divider && React.createElement(Divider, { className: "u-mt-half u-mh-1" })));
18
+ });
19
+ NavItem.displayName = 'NavItem';
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ import { NavLinkBaseProps } from './NavLinkBase';
3
+ export interface NavDropdownProps extends Omit<NavLinkBaseProps, 'onToggle'> {
4
+ open?: boolean;
5
+ onToggle?: React.MouseEventHandler<HTMLDivElement>;
6
+ }
7
+ export declare const NavDropdown: React.ForwardRefExoticComponent<NavDropdownProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,19 @@
1
+ import { Icon, Dropdown } from '@linagora/twake-icons';
2
+ import { useTheme } from '@mui/material/styles';
3
+ import React, { forwardRef } from 'react';
4
+ import { NavLinkBase } from './NavLinkBase';
5
+ export const NavDropdown = forwardRef(({ open, onToggle, onClick, children, ...rest }, ref) => {
6
+ const theme = useTheme();
7
+ const handleClick = (e) => {
8
+ if (onToggle) {
9
+ onToggle(e);
10
+ }
11
+ if (onClick) {
12
+ onClick(e);
13
+ }
14
+ };
15
+ return (React.createElement(NavLinkBase, { ref: ref, onClick: handleClick, "aria-expanded": open, ...rest },
16
+ children,
17
+ React.createElement(Icon, { className: "u-ml-half", icon: Dropdown, rotate: open ? 0 : -90, size: 14, color: theme.vars.palette.text.primary })));
18
+ });
19
+ NavDropdown.displayName = 'NavDropdown';
@@ -0,0 +1,4 @@
1
+ import React from 'react';
2
+ import { NavLinkBaseProps } from './NavLinkBase';
3
+ export type NavLinkProps = NavLinkBaseProps;
4
+ export declare const NavLink: React.ForwardRefExoticComponent<NavLinkBaseProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,6 @@
1
+ import React, { forwardRef } from 'react';
2
+ import { NavLinkBase } from './NavLinkBase';
3
+ export const NavLink = forwardRef((props, ref) => {
4
+ return React.createElement(NavLinkBase, { ref: ref, ...props });
5
+ });
6
+ NavLink.displayName = 'NavLink';
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ export interface NavLinkBaseProps extends React.ComponentPropsWithoutRef<'div'> {
3
+ selected?: boolean;
4
+ }
5
+ export declare const NavLinkBase: React.ForwardRefExoticComponent<NavLinkBaseProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,15 @@
1
+ import cx from 'classnames';
2
+ import React, { forwardRef } from 'react';
3
+ import { NavLinkRoot } from './NavLinkRoot';
4
+ export const NavLinkBase = forwardRef(({ selected, onClick, onKeyDown, className, children, ...rest }, ref) => {
5
+ return (React.createElement(NavLinkRoot, { ref: ref, role: "button", tabIndex: 0, onClick: onClick, onKeyDown: (e) => {
6
+ if (onKeyDown) {
7
+ onKeyDown(e);
8
+ }
9
+ if (!e.defaultPrevented && (e.key === 'Enter' || e.key === ' ')) {
10
+ e.preventDefault();
11
+ e.currentTarget.click();
12
+ }
13
+ }, className: cx(className, { active: selected }), ...rest }, children));
14
+ });
15
+ NavLinkBase.displayName = 'NavLinkBase';
@@ -0,0 +1,2 @@
1
+ import { Theme } from '@mui/material/styles';
2
+ export declare const NavLinkRoot: import("@emotion/styled").StyledComponent<import("@mui/system").MUIStyledCommonProps<Theme>, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {}>;
@@ -0,0 +1,41 @@
1
+ import { styled } from '@mui/material/styles';
2
+ export const NavLinkRoot = styled('div')(({ theme }) => ({
3
+ padding: 8,
4
+ width: '100%',
5
+ height: '100%',
6
+ minHeight: 0,
7
+ gap: 0,
8
+ borderRadius: 8,
9
+ lineHeight: 1.375,
10
+ color: theme.vars.palette.text.primary,
11
+ display: 'flex',
12
+ alignItems: 'center',
13
+ cursor: 'pointer',
14
+ '&:hover, &:focus, &:active': {
15
+ backgroundColor: 'transparent'
16
+ },
17
+ '&.selected, &.active': {
18
+ color: theme.vars.palette.primary.main,
19
+ backgroundColor: 'transparent',
20
+ '&:hover': { backgroundColor: 'transparent' },
21
+ '& svg, & span': {
22
+ color: theme.vars.palette.primary.main
23
+ }
24
+ },
25
+ [theme.breakpoints.down('lg')]: {
26
+ display: 'block',
27
+ height: 'auto',
28
+ margin: 0,
29
+ padding: 0,
30
+ textAlign: 'center',
31
+ fontSize: theme.typography.pxToRem(11),
32
+ lineHeight: '12px',
33
+ '&.selected, &.active': {
34
+ color: theme.vars.palette.text.primary,
35
+ '&:hover': { backgroundColor: 'transparent' },
36
+ '& svg, & span': {
37
+ color: theme.vars.palette.text.primary
38
+ }
39
+ }
40
+ }
41
+ }));
@@ -0,0 +1,2 @@
1
+ import { Theme } from '@mui/material/styles';
2
+ export declare const NavPrimaryText: import("@emotion/styled").StyledComponent<import("@mui/system").MUIStyledCommonProps<Theme>, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, {}>;
@@ -0,0 +1,12 @@
1
+ import { styled } from '@mui/material/styles';
2
+ export const NavPrimaryText = styled('span')(({ theme }) => ({
3
+ fontSize: theme.typography.pxToRem(14),
4
+ fontWeight: 500,
5
+ letterSpacing: '.15px',
6
+ [theme.breakpoints.down('lg')]: {
7
+ display: 'block',
8
+ textAlign: 'center',
9
+ whiteSpace: 'nowrap',
10
+ fontSize: theme.typography.pxToRem(12)
11
+ }
12
+ }));
@@ -0,0 +1,2 @@
1
+ import { Theme } from '@mui/material/styles';
2
+ export declare const NavSecondaryText: import("@emotion/styled").StyledComponent<import("@mui/system").MUIStyledCommonProps<Theme>, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, {}>;
@@ -0,0 +1,8 @@
1
+ import { styled } from '@mui/material/styles';
2
+ export const NavSecondaryText = styled('span')(({ theme }) => ({
3
+ display: 'block',
4
+ fontSize: theme.typography.pxToRem(12),
5
+ color: theme.vars.palette.text.secondary,
6
+ lineHeight: 1.5,
7
+ fontWeight: 400
8
+ }));
@@ -0,0 +1,2 @@
1
+ import { Theme } from '@mui/material/styles';
2
+ export declare const NavTertiaryText: import("@emotion/styled").StyledComponent<import("@mui/system").MUIStyledCommonProps<Theme>, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, {}>;
@@ -0,0 +1,16 @@
1
+ import { styled } from '@mui/material/styles';
2
+ export const NavTertiaryText = styled('span')(({ theme }) => ({
3
+ display: 'flex',
4
+ alignItems: 'center',
5
+ fontSize: theme.typography.pxToRem(11),
6
+ color: theme.vars.palette.text.secondary,
7
+ lineHeight: 1.5,
8
+ fontWeight: 400,
9
+ '& svg': {
10
+ width: 14,
11
+ height: 14,
12
+ marginRight: 4,
13
+ color: 'inherit',
14
+ fill: 'currentColor'
15
+ }
16
+ }));
@@ -0,0 +1 @@
1
+ export declare const NavTextRoot: import("@emotion/styled").StyledComponent<import("@mui/system").MUIStyledCommonProps<import("@mui/material/styles").Theme>, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {}>;
@@ -0,0 +1,7 @@
1
+ import { styled } from '@mui/material/styles';
2
+ export const NavTextRoot = styled('div')(() => ({
3
+ display: 'flex',
4
+ flexDirection: 'column',
5
+ minWidth: 0,
6
+ marginRight: 1
7
+ }));
@@ -1,7 +1,11 @@
1
1
  import { SxProps } from '@mui/material';
2
2
  import { Theme } from '@mui/material/styles';
3
3
  import React from 'react';
4
- export interface NavTextProps extends React.ComponentPropsWithoutRef<'span'> {
4
+ export interface NavTextProps extends React.ComponentPropsWithoutRef<'div'> {
5
5
  sx?: SxProps<Theme>;
6
+ secondaryText?: React.ReactNode;
7
+ tertiaryText?: React.ReactNode;
8
+ tertiaryIcon?: React.ReactNode;
6
9
  }
7
- export declare const NavText: React.FC<NavTextProps>;
10
+ declare const NavText: React.ForwardRefExoticComponent<NavTextProps & React.RefAttributes<HTMLDivElement>>;
11
+ export default NavText;
@@ -1,14 +1,15 @@
1
- import { styled } from '@mui/material/styles';
2
- // Explicit annotation: declaration emit for styled('span') otherwise
3
- // widens the element props to the whole JSX.IntrinsicElements map.
4
- export const NavText = styled('span')(({ theme }) => ({
5
- fontSize: theme.typography.pxToRem(14),
6
- fontWeight: 500,
7
- letterSpacing: '.15px',
8
- [theme.breakpoints.down('lg')]: {
9
- display: 'block',
10
- textAlign: 'center',
11
- whiteSpace: 'nowrap',
12
- fontSize: theme.typography.pxToRem(12)
13
- }
14
- }));
1
+ import React from 'react';
2
+ import { NavPrimaryText } from './NavPrimaryText';
3
+ import { NavSecondaryText } from './NavSecondaryText';
4
+ import { NavTertiaryText } from './NavTertiaryText';
5
+ import { NavTextRoot } from './NavTextRoot';
6
+ const NavText = React.forwardRef(({ secondaryText, tertiaryText, tertiaryIcon, children, sx, ...rest }, ref) => {
7
+ return (React.createElement(NavTextRoot, { ref: ref, sx: sx, ...rest },
8
+ React.createElement(NavPrimaryText, null, children),
9
+ secondaryText && React.createElement(NavSecondaryText, null, secondaryText),
10
+ (tertiaryText || tertiaryIcon) && (React.createElement(NavTertiaryText, null,
11
+ tertiaryIcon,
12
+ tertiaryText))));
13
+ });
14
+ NavText.displayName = 'NavText';
15
+ export default NavText;
@@ -6,11 +6,13 @@ import { NavDesktopDropdown } from '../NavDesktopDropdown';
6
6
  import { NavDesktopLimiter } from '../NavDesktopLimiter';
7
7
  import { NavIcon } from '../NavIcon';
8
8
  import { NavItem } from '../NavItem';
9
- import { NavLink } from '../NavLink';
10
- import { NavText } from '../NavText';
9
+ import NavText from '../NavText';
11
10
  import { Sidebar } from './index';
11
+ import { NavDropdown } from '../NavLink/NavDropdown';
12
+ import { NavLink } from '../NavLink/NavLink';
12
13
  const SidebarDemo = (props) => {
13
14
  const [active, setActive] = useState(['Section 1', 'Subsection 1']);
15
+ const [openSection1, setOpenSection1] = useState(true);
14
16
  const makeProps = (route) => ({
15
17
  selected: active.slice(0, route.length).join('/') === route.join('/'),
16
18
  onClick: () => setActive(route)
@@ -19,14 +21,17 @@ const SidebarDemo = (props) => {
19
21
  React.createElement(Sidebar, { ...props },
20
22
  React.createElement(Nav, null,
21
23
  React.createElement(NavItem, null,
22
- React.createElement(NavLink, { ...makeProps(['Section 1']) },
24
+ React.createElement(NavDropdown, { ...makeProps(['Section 1']), open: openSection1, onToggle: (e) => {
25
+ e.stopPropagation();
26
+ setOpenSection1(prev => !prev);
27
+ } },
23
28
  React.createElement(NavIcon, { icon: Warn }),
24
29
  React.createElement(NavText, null, "Section 1"))),
25
- React.createElement(NavDesktopLimiter, null, Array.from(Array(10).keys()).map(i => (React.createElement(NavItem, { secondary: true, key: i },
30
+ openSection1 && (React.createElement(NavDesktopLimiter, null, Array.from(Array(10).keys()).map(i => (React.createElement(NavItem, { variant: "secondary", key: i },
26
31
  React.createElement(NavLink, { ...makeProps(['Section 1', `Subsection ${i}`]) },
27
32
  React.createElement(NavText, null,
28
33
  "Subsection ",
29
- i)))))),
34
+ i))))))),
30
35
  React.createElement(NavItem, null,
31
36
  React.createElement(NavLink, { ...makeProps(['Section 2']) },
32
37
  React.createElement(NavIcon, { icon: Check }),
@@ -35,7 +40,7 @@ const SidebarDemo = (props) => {
35
40
  React.createElement(NavLink, { ...makeProps(['Section 3']) },
36
41
  React.createElement(NavIcon, { icon: Download }),
37
42
  React.createElement(NavText, null, "Section 3"))),
38
- React.createElement(NavDesktopDropdown, { label: "Section 4" }, Array.from(Array(6).keys()).map(i => (React.createElement(NavItem, { secondary: true, key: i },
43
+ React.createElement(NavDesktopDropdown, { label: "Section 4" }, Array.from(Array(6).keys()).map(i => (React.createElement(NavItem, { variant: "secondary", key: i },
39
44
  React.createElement(NavLink, { ...makeProps(['Section 4', `Subsection ${i}`]) },
40
45
  React.createElement(NavText, null,
41
46
  "Subsection ",
package/dist/index.d.ts CHANGED
@@ -29,9 +29,11 @@ export { Sidebar, SIDEBAR_MOBILE_HEIGHT } from './components/Sidebar';
29
29
  export { Layout, Main, Content } from './components/Layout';
30
30
  export { Nav } from './components/Nav';
31
31
  export { NavItem } from './components/NavItem';
32
- export { NavLink } from './components/NavLink';
32
+ export { NavLink } from './components/NavLink/NavLink';
33
+ export { NavLinkBase } from './components/NavLink/NavLinkBase';
34
+ export { NavDropdown } from './components/NavLink/NavDropdown';
33
35
  export { NavIcon } from './components/NavIcon';
34
- export { NavText } from './components/NavText';
36
+ export { default as NavText } from './components/NavText';
35
37
  export { NavDesktopLimiter } from './components/NavDesktopLimiter';
36
38
  export { NavDesktopDropdown } from './components/NavDesktopDropdown';
37
39
  export { nameToColor, supportedColors, getInitials } from './components/Avatar/helpers';
@@ -70,7 +72,9 @@ export type { SidebarProps } from './components/Sidebar';
70
72
  export type { LayoutProps, MainProps, ContentProps } from './components/Layout';
71
73
  export type { NavProps } from './components/Nav';
72
74
  export type { NavItemProps } from './components/NavItem';
73
- export type { NavLinkProps } from './components/NavLink';
75
+ export type { NavLinkProps } from './components/NavLink/NavLink';
76
+ export type { NavLinkBaseProps } from './components/NavLink/NavLinkBase';
77
+ export type { NavDropdownProps } from './components/NavLink/NavDropdown';
74
78
  export type { NavIconProps } from './components/NavIcon';
75
79
  export type { NavTextProps } from './components/NavText';
76
80
  export type { NavDesktopLimiterProps } from './components/NavDesktopLimiter';
package/dist/index.js CHANGED
@@ -32,9 +32,11 @@ export { Sidebar, SIDEBAR_MOBILE_HEIGHT } from './components/Sidebar';
32
32
  export { Layout, Main, Content } from './components/Layout';
33
33
  export { Nav } from './components/Nav';
34
34
  export { NavItem } from './components/NavItem';
35
- export { NavLink } from './components/NavLink';
35
+ export { NavLink } from './components/NavLink/NavLink';
36
+ export { NavLinkBase } from './components/NavLink/NavLinkBase';
37
+ export { NavDropdown } from './components/NavLink/NavDropdown';
36
38
  export { NavIcon } from './components/NavIcon';
37
- export { NavText } from './components/NavText';
39
+ export { default as NavText } from './components/NavText';
38
40
  export { NavDesktopLimiter } from './components/NavDesktopLimiter';
39
41
  export { NavDesktopDropdown } from './components/NavDesktopDropdown';
40
42
  export { nameToColor, supportedColors, getInitials } from './components/Avatar/helpers';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@linagora/twake-mui",
3
3
  "type": "module",
4
- "version": "10.0.0",
4
+ "version": "10.1.0",
5
5
  "description": "MUI v9 theme system for Twake applications",
6
6
  "author": "Twake",
7
7
  "license": "MIT",
@@ -1,3 +0,0 @@
1
- import { ListItemButton, ListItemButtonProps } from '@mui/material';
2
- export type NavLinkProps = ListItemButtonProps;
3
- export declare const NavLink: typeof ListItemButton;
@@ -1,38 +0,0 @@
1
- import { ListItemButton, listItemButtonClasses, listItemIconClasses } from '@mui/material';
2
- import { styled } from '@mui/material/styles';
3
- // Cast keeps ListItemButton's `component` generic (react-router NavLink etc.)
4
- export const NavLink = styled(ListItemButton)(({ theme }) => ({
5
- margin: '0 16px',
6
- padding: '0 8px',
7
- height: '100%',
8
- minHeight: 0,
9
- gap: 0,
10
- borderRadius: 8,
11
- lineHeight: 1.375,
12
- color: theme.vars.palette.text.primary,
13
- [`&.${listItemButtonClasses.selected}, &.active`]: {
14
- color: theme.vars.palette.primary.main,
15
- backgroundColor: theme.vars.palette.action.selected,
16
- '&:hover': { backgroundColor: theme.vars.palette.action.selected },
17
- [`& .${listItemIconClasses.root}`]: {
18
- color: theme.vars.palette.primary.main
19
- }
20
- },
21
- [theme.breakpoints.down('lg')]: {
22
- display: 'block',
23
- height: 'auto',
24
- margin: 0,
25
- padding: 0,
26
- textAlign: 'center',
27
- fontSize: theme.typography.pxToRem(11),
28
- lineHeight: '12px',
29
- color: theme.vars.palette.text.secondary,
30
- [`&.${listItemButtonClasses.selected}, &.active`]: {
31
- color: theme.vars.palette.text.primary,
32
- backgroundColor: 'transparent',
33
- [`& .${listItemIconClasses.root}`]: {
34
- color: theme.vars.palette.text.primary
35
- }
36
- }
37
- }
38
- }));