@linagora/twake-mui 7.1.0 → 9.0.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 (82) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/dist/components/Apptitle/index.js +9 -5
  3. package/dist/components/Avatar/Avatar.stories.js +58 -37
  4. package/dist/components/Avatar/helpers.d.ts +0 -28
  5. package/dist/components/Avatar/helpers.js +0 -28
  6. package/dist/components/Avatar/index.d.ts +4 -6
  7. package/dist/components/Avatar/index.js +24 -31
  8. package/dist/components/Button/index.d.ts +12 -0
  9. package/dist/components/Button/index.js +8 -0
  10. package/dist/{stories → components/Dialog}/Dialog.stories.d.ts +2 -2
  11. package/dist/components/Dialog/Dialog.stories.js +81 -0
  12. package/dist/components/Dialog/index.d.ts +9 -0
  13. package/dist/components/Dialog/index.js +9 -0
  14. package/dist/components/Nav/index.d.ts +8 -0
  15. package/dist/components/Nav/index.js +14 -0
  16. package/dist/components/NavDesktopDropdown/index.d.ts +8 -0
  17. package/dist/components/NavDesktopDropdown/index.js +24 -0
  18. package/dist/components/NavDesktopLimiter/index.d.ts +6 -0
  19. package/dist/components/NavDesktopLimiter/index.js +31 -0
  20. package/dist/components/NavDesktopLimiter/locales/en.json +6 -0
  21. package/dist/components/NavDesktopLimiter/locales/fr.json +6 -0
  22. package/dist/components/NavDesktopLimiter/locales/ru.json +6 -0
  23. package/dist/components/NavDesktopLimiter/locales/vi.json +6 -0
  24. package/dist/components/NavIcon/index.d.ts +7 -0
  25. package/dist/components/NavIcon/index.js +17 -0
  26. package/dist/components/NavItem/index.d.ts +8 -0
  27. package/dist/components/NavItem/index.js +29 -0
  28. package/dist/components/NavLink/index.d.ts +3 -0
  29. package/dist/components/NavLink/index.js +36 -0
  30. package/dist/components/NavText/index.d.ts +7 -0
  31. package/dist/components/NavText/index.js +14 -0
  32. package/dist/components/Sidebar/Sidebar.stories.d.ts +7 -0
  33. package/dist/components/Sidebar/Sidebar.stories.js +57 -0
  34. package/dist/components/Sidebar/index.d.ts +7 -0
  35. package/dist/components/Sidebar/index.js +21 -0
  36. package/dist/components/Switch/Switch.stories.d.ts +7 -0
  37. package/dist/components/Switch/Switch.stories.js +58 -0
  38. package/dist/components/Switch/index.d.ts +9 -0
  39. package/dist/components/Switch/index.js +8 -0
  40. package/dist/components/Tabs/Tabs.stories.d.ts +7 -0
  41. package/dist/components/Tabs/Tabs.stories.js +52 -0
  42. package/dist/components/Tabs/index.d.ts +10 -0
  43. package/dist/components/Tabs/index.js +5 -0
  44. package/dist/components/ThemeProvider/index.js +9 -10
  45. package/dist/components/ThemeProvider/types.d.ts +6 -0
  46. package/dist/index.d.ts +26 -1
  47. package/dist/index.js +13 -1
  48. package/dist/lib/makePalette.d.ts +1 -1
  49. package/dist/lib/makePalette.js +3 -1
  50. package/dist/lib/makeShadows.d.ts +1 -1
  51. package/dist/lib/makeShadows.js +2 -2
  52. package/dist/lib/makeTheme.d.ts +3 -2
  53. package/dist/lib/makeTheme.js +15 -16
  54. package/dist/lib/overrides.d.ts +2 -0
  55. package/dist/lib/overrides.js +1027 -0
  56. package/dist/lib/types.d.ts +14 -0
  57. package/dist/stories/Alert.stories.js +57 -53
  58. package/dist/stories/Badge.stories.d.ts +7 -0
  59. package/dist/stories/Badge.stories.js +84 -0
  60. package/dist/stories/Button.stories.d.ts +3 -3
  61. package/dist/stories/Button.stories.js +67 -58
  62. package/dist/stories/{Selection.stories.d.ts → Checkbox.stories.d.ts} +1 -1
  63. package/dist/stories/Checkbox.stories.js +59 -0
  64. package/dist/stories/Divider.stories.d.ts +7 -0
  65. package/dist/stories/Divider.stories.js +56 -0
  66. package/dist/stories/Radio.stories.d.ts +7 -0
  67. package/dist/stories/Radio.stories.js +60 -0
  68. package/dist/stories/Snackbar.stories.d.ts +7 -0
  69. package/dist/stories/Snackbar.stories.js +60 -0
  70. package/dist/stories/{Input.stories.d.ts → TextField.stories.d.ts} +1 -1
  71. package/dist/stories/TextField.stories.js +86 -0
  72. package/dist/stories/ThemePlayground.stories.js +20 -113
  73. package/dist/stories/Typography.stories.d.ts +1 -1
  74. package/dist/stories/Typography.stories.js +48 -34
  75. package/package.json +5 -2
  76. package/dist/lib/darkOverrides.d.ts +0 -2
  77. package/dist/lib/darkOverrides.js +0 -3
  78. package/dist/lib/lightOverrides.d.ts +0 -2
  79. package/dist/lib/lightOverrides.js +0 -551
  80. package/dist/stories/Dialog.stories.js +0 -40
  81. package/dist/stories/Input.stories.js +0 -41
  82. package/dist/stories/Selection.stories.js +0 -37
package/CHANGELOG.md CHANGED
@@ -1,3 +1,61 @@
1
+ # @linagora/twake-mui [9.0.0](https://github.com/linagora/twake-ui/compare/@linagora/twake-mui@8.0.0...@linagora/twake-mui@9.0.0) (2026-09-21)
2
+
3
+
4
+ ### Features
5
+
6
+ * **twake-mui:** Migrate Nav from cozy-ui ([5dd0516](https://github.com/linagora/twake-ui/commit/5dd05165741159437f796935f81d9d907eb808f2))
7
+ * **twake-mui:** Migrate Sidebar from cozy-ui ([08f4179](https://github.com/linagora/twake-ui/commit/08f4179493090399cca1e59878d405b945777225))
8
+
9
+
10
+ ### BREAKING CHANGES
11
+
12
+ * **twake-mui:** You need "twake-i18n" >= "0.5.0"
13
+
14
+ # @linagora/twake-mui [8.0.0](https://github.com/linagora/twake-ui/compare/@linagora/twake-mui@7.1.0...@linagora/twake-mui@8.0.0) (2026-09-18)
15
+
16
+
17
+ ### Bug Fixes
18
+
19
+ * **twake-mui:** Anchor the static Snackbar story top left ([e1cee47](https://github.com/linagora/twake-ui/commit/e1cee476de86ec828cd836b5ce5dd2925360ff29))
20
+ * **twake-mui:** Center the Alert message vertically ([38cb5c2](https://github.com/linagora/twake-ui/commit/38cb5c2200adcaa8b9348fca235a54698b5e26e8))
21
+ * **twake-mui:** Clean old Avatar override ([202ffb6](https://github.com/linagora/twake-ui/commit/202ffb652010dee61e61b0386a4fbe45f6074859))
22
+ * **twake-mui:** Clean old Button override ([f0c45eb](https://github.com/linagora/twake-ui/commit/f0c45eb10e4610176509a1fd89179dcd43d4144a))
23
+ * **twake-mui:** Clean old Dialog override ([50544ef](https://github.com/linagora/twake-ui/commit/50544ef9426294d0d54a843e61d04b829cb914c2))
24
+ * **twake-mui:** Clean old TextField override ([23b4253](https://github.com/linagora/twake-ui/commit/23b42530b26909abd822e6da8b99b250f5fead39))
25
+ * **twake-mui:** Draw the secondary Switch in the success colour like cozy-ui ([cff53f0](https://github.com/linagora/twake-ui/commit/cff53f0cc0c7f0098843d6f5c0acc5dd9ca4553d))
26
+ * **twake-mui:** Emit colorSchemes with our own tokens for both modes ([962054b](https://github.com/linagora/twake-ui/commit/962054b74fa214494b6786bd3bc2ce4c98fd8714))
27
+ * **twake-mui:** Replace the runtime lodash import with @mui/utils deepmerge ([e6940ff](https://github.com/linagora/twake-ui/commit/e6940ff4d99987ea49ef7a370c01aef43e862373))
28
+ * **twake-mui:** Thread mode into ThemeProvider's defaultMode ([1f98a09](https://github.com/linagora/twake-ui/commit/1f98a09b4421497b50717bc36353043838a36c63))
29
+ * **twake-mui:** Use the 16px Alert icons of cozy-ui ([c781a7d](https://github.com/linagora/twake-ui/commit/c781a7dca3ccacf04c94eb9dd7b0f14b4fcecf70))
30
+
31
+
32
+ ### Features
33
+
34
+ * Expose cssVariables in TypeScript ([23a151e](https://github.com/linagora/twake-ui/commit/23a151eaf47287d1cc49364073d10fd0acceccc9))
35
+ * **twake-mui:** Accept the primary and secondary Button variants of cozy-ui ([daf67fb](https://github.com/linagora/twake-ui/commit/daf67fb9544a11338556201657a6f0e687aab8cd))
36
+ * **twake-mui:** Let apps customize the whole theme ([9af16db](https://github.com/linagora/twake-ui/commit/9af16db3720708f79d69bf48c14522ef65b6b7cc))
37
+ * **twake-mui:** Migrate Alert from cozy-ui ([916405d](https://github.com/linagora/twake-ui/commit/916405d6c2ce25bd83c445a47ba240303291a7b8))
38
+ * **twake-mui:** Migrate Avatar from cozy-ui ([214787f](https://github.com/linagora/twake-ui/commit/214787fbb514b1fe7b510234405821f0256e34e6))
39
+ * **twake-mui:** Migrate Badge from cozy-ui ([3d9509d](https://github.com/linagora/twake-ui/commit/3d9509d318e048505bee95ce80f5369da9fb25b5))
40
+ * **twake-mui:** Migrate Button from cozy-ui ([704f267](https://github.com/linagora/twake-ui/commit/704f267cd13f9998faf3fbb095b3ab798b518950))
41
+ * **twake-mui:** Migrate Checkbox from cozy-ui ([5281cb1](https://github.com/linagora/twake-ui/commit/5281cb1c5eea50ec24bfb528106f13cfb705572a))
42
+ * **twake-mui:** Migrate Dialog from cozy-ui ([d420cf5](https://github.com/linagora/twake-ui/commit/d420cf5c4f9e7aa37c60f6122fd0e903586e046b))
43
+ * **twake-mui:** Migrate Divider from cozy-ui ([44e1d6c](https://github.com/linagora/twake-ui/commit/44e1d6cc13f612a2295895f4be03856eaf90c7a9))
44
+ * **twake-mui:** Migrate Radio from cozy-ui ([effd21f](https://github.com/linagora/twake-ui/commit/effd21fddab6caf43a7e480b16cbd6e93611c34b))
45
+ * **twake-mui:** Migrate Snackbar from cozy-ui ([56f2cac](https://github.com/linagora/twake-ui/commit/56f2cac2d632d19bab976ccee9e70f1b20e5ffdb))
46
+ * **twake-mui:** Migrate Switch from cozy-ui ([545280b](https://github.com/linagora/twake-ui/commit/545280bd72ef81eb62868e7f8b3dc7e058924466))
47
+ * **twake-mui:** Migrate Tabs from cozy-ui ([076ff98](https://github.com/linagora/twake-ui/commit/076ff986612d447c97385898faf10d4ba50bcca1))
48
+ * **twake-mui:** Migrate TextField from cozy-ui ([6a4fd77](https://github.com/linagora/twake-ui/commit/6a4fd77ec5e49c76603b7bd8cd899157ef72fa6b))
49
+ * **twake-mui:** Migrate Typography from cozy-ui ([7f59c34](https://github.com/linagora/twake-ui/commit/7f59c34e4f0a6502f8a30e676c9d7bb2a48f3025))
50
+ * **twake-mui:** Re-export useColorScheme for runtime theme switching ([f3ed328](https://github.com/linagora/twake-ui/commit/f3ed328d45c6b5006403f975a5f07447f5ef2172))
51
+
52
+
53
+ ### BREAKING CHANGES
54
+
55
+ * **twake-mui:** You must use `palette` prop instead of
56
+ `themeOptions` of `TwakeMuiThemeProvider` to override
57
+ the palette
58
+
1
59
  # @linagora/twake-mui [7.1.0](https://github.com/linagora/twake-ui/compare/@linagora/twake-mui@7.0.0...@linagora/twake-mui@7.1.0) (2026-09-10)
2
60
 
3
61
 
@@ -1,15 +1,19 @@
1
1
  import { Icon, TwakeText } from '@linagora/twake-icons';
2
- import { Box, useTheme } from '@mui/material';
2
+ import { Box } from '@mui/material';
3
3
  import React from 'react';
4
4
  const AppTitle = ({ appIcon, appTextIcon }) => {
5
- const theme = useTheme();
6
- const isLight = theme.palette.mode === 'light';
7
5
  if (!appIcon || !appTextIcon)
8
6
  return null;
9
7
  const AppTextIcon = appTextIcon;
10
8
  return (React.createElement(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
11
9
  React.createElement(Icon, { icon: appIcon, size: "32" }),
12
- React.createElement(TwakeText, { height: "22", style: { fill: isLight ? '#000' : '#fff' } }),
13
- React.createElement(AppTextIcon, { height: 22, style: { width: 'auto', fill: isLight ? '#000' : '#fff' } })));
10
+ React.createElement(Box, { component: TwakeText, height: "22", sx: [
11
+ { fill: '#000' },
12
+ (theme) => theme.applyStyles('dark', { fill: '#fff' })
13
+ ] }),
14
+ React.createElement(Box, { component: AppTextIcon, height: 22, sx: [
15
+ { width: 'auto', fill: '#000' },
16
+ (theme) => theme.applyStyles('dark', { fill: '#fff' })
17
+ ] })));
14
18
  };
15
19
  export default AppTitle;
@@ -1,6 +1,10 @@
1
+ import { Icon, Link } from '@linagora/twake-icons';
2
+ import { AvatarGroup, Stack } from '@mui/material';
1
3
  import React from 'react';
2
4
  import { supportedColors } from './helpers';
3
5
  import { Avatar } from './index';
6
+ const sizes = ['xs', 's', 'm', 'l', 'xl'];
7
+ const image = 'https://i.pravatar.cc/150?img=11';
4
8
  const meta = {
5
9
  title: 'Avatar',
6
10
  component: Avatar,
@@ -13,8 +17,9 @@ const meta = {
13
17
  },
14
18
  size: {
15
19
  control: 'select',
16
- options: ['xs', 's', 'm', 'l', 'xl']
20
+ options: sizes
17
21
  },
22
+ textColor: { control: 'color' },
18
23
  border: { control: 'boolean' },
19
24
  innerBorder: { control: 'boolean' },
20
25
  disabled: { control: 'boolean' },
@@ -30,44 +35,60 @@ export const Default = {
30
35
  children: 'JD'
31
36
  }
32
37
  };
38
+ const Row = ({ title, children }) => (React.createElement("section", null,
39
+ React.createElement("h3", { style: { marginBottom: '12px' } }, title),
40
+ React.createElement(Stack, { direction: "row", spacing: 1, useFlexGap: true, sx: { flexWrap: 'wrap' } }, children)));
33
41
  // Visual Regression - Combined view for Argos testing
34
42
  export const Screenshot = {
35
43
  tags: ['argos'],
36
- render: () => (React.createElement("div", { style: { display: 'flex', flexDirection: 'column', gap: '32px' } },
44
+ render: () => (React.createElement(Stack, { spacing: 4 },
45
+ React.createElement(Row, { title: "Sizes" }, sizes.map(size => (React.createElement(React.Fragment, { key: size },
46
+ React.createElement(Avatar, { size: size, color: "sunrise" }),
47
+ React.createElement(Avatar, { size: size, color: "sunrise" }, "AB"),
48
+ React.createElement(Avatar, { size: size, color: "sunrise" },
49
+ React.createElement(Icon, { icon: Link })))))),
50
+ React.createElement(Row, { title: "Colors" }, supportedColors.map(color => (React.createElement(Avatar, { key: color, color: color, size: "l" }, color.slice(0, 2).toUpperCase())))),
51
+ React.createElement(Row, { title: "Name-based colors" },
52
+ React.createElement(Avatar, { size: "l" }, "Alice"),
53
+ React.createElement(Avatar, { size: "l" }, "Bob"),
54
+ React.createElement(Avatar, { size: "l" }, "Charlie"),
55
+ React.createElement(Avatar, { size: "l" }, "Diana")),
56
+ React.createElement(Row, { title: "Default color" },
57
+ React.createElement(Avatar, { src: image, alt: "Image" }),
58
+ React.createElement(Avatar, { color: "none", src: image, alt: "Image, no color" }),
59
+ React.createElement(Avatar, { color: "sunrise", src: image, alt: "Image, sunrise" }),
60
+ React.createElement(Avatar, null),
61
+ React.createElement(Avatar, { color: "none" }),
62
+ React.createElement(Avatar, { color: "sunrise" }),
63
+ React.createElement(Avatar, null, "AB"),
64
+ React.createElement(Avatar, { color: "none" }, "BC"),
65
+ React.createElement(Avatar, { color: "sunrise" }, "CD")),
66
+ React.createElement(Row, { title: "Custom size and color" },
67
+ React.createElement(Avatar, { color: "sunrise", size: 94 }, "DE"),
68
+ React.createElement(Avatar, { color: "#0000ff" }, "EF"),
69
+ React.createElement(Avatar, { color: "grey", textColor: "white" }, "FG")),
70
+ React.createElement(Row, { title: "Borders" },
71
+ React.createElement(Avatar, { size: "l" }, "Default"),
72
+ React.createElement(Avatar, { size: "l", border: true }, "Border"),
73
+ React.createElement(Avatar, { size: "l", innerBorder: true }, "Inner"),
74
+ React.createElement(Avatar, { size: "l", border: true, innerBorder: true }, "Both"),
75
+ React.createElement(Avatar, { size: "l", color: "none", border: true, innerBorder: true },
76
+ React.createElement(Icon, { icon: Link }))),
77
+ React.createElement(Row, { title: "Disabled" },
78
+ React.createElement(Avatar, { size: "l", disabled: true }, "AB"),
79
+ React.createElement(Avatar, { size: "l", disabled: true, color: "sunrise" }, "CD"),
80
+ React.createElement(Avatar, { size: "l", disabled: true, src: image, alt: "Disabled" }),
81
+ React.createElement(Avatar, { size: "l", disabled: true, color: "none" },
82
+ React.createElement(Icon, { icon: Link }))),
37
83
  React.createElement("section", null,
38
- React.createElement("h3", { style: { marginBottom: '12px' } }, "Sizes"),
39
- React.createElement("div", { style: { display: 'flex', gap: '16px', alignItems: 'center' } },
40
- React.createElement(Avatar, { size: "xs" }, "XS"),
41
- React.createElement(Avatar, { size: "s" }, "S"),
42
- React.createElement(Avatar, { size: "m" }, "M"),
43
- React.createElement(Avatar, { size: "l" }, "L"),
44
- React.createElement(Avatar, { size: "xl" }, "XL"))),
45
- React.createElement("section", null,
46
- React.createElement("h3", { style: { marginBottom: '12px' } }, "Colors"),
47
- React.createElement("div", { style: { display: 'flex', gap: '16px', flexWrap: 'wrap' } }, supportedColors.map(color => (React.createElement(Avatar, { key: color, color: color, size: "l" }, color.slice(0, 2).toUpperCase()))))),
48
- React.createElement("section", null,
49
- React.createElement("h3", { style: { marginBottom: '12px' } }, "Name-based Colors"),
50
- React.createElement("div", { style: { display: 'flex', gap: '16px' } },
51
- React.createElement(Avatar, { size: "l" }, "Alice"),
52
- React.createElement(Avatar, { size: "l" }, "Bob"),
53
- React.createElement(Avatar, { size: "l" }, "Charlie"),
54
- React.createElement(Avatar, { size: "l" }, "Diana"))),
55
- React.createElement("section", null,
56
- React.createElement("h3", { style: { marginBottom: '12px' } }, "With Image"),
57
- React.createElement("div", { style: { display: 'flex', gap: '16px' } },
58
- React.createElement(Avatar, { src: "https://i.pravatar.cc/150?img=11", alt: "User 1", size: "l" }),
59
- React.createElement(Avatar, { src: "https://i.pravatar.cc/150?img=12", alt: "User 2", size: "l" }),
60
- React.createElement(Avatar, { src: "https://i.pravatar.cc/150?img=13", alt: "User 3", size: "l" }))),
61
- React.createElement("section", null,
62
- React.createElement("h3", { style: { marginBottom: '12px' } }, "Border Variants"),
63
- React.createElement("div", { style: { display: 'flex', gap: '16px' } },
64
- React.createElement(Avatar, { size: "l" }, "Default"),
65
- React.createElement(Avatar, { size: "l", border: true }, "Border"),
66
- React.createElement(Avatar, { size: "l", innerBorder: true }, "Inner"),
67
- React.createElement(Avatar, { size: "l", border: true, innerBorder: true }, "Both"))),
68
- React.createElement("section", null,
69
- React.createElement("h3", { style: { marginBottom: '12px' } }, "Disabled State"),
70
- React.createElement("div", { style: { display: 'flex', gap: '16px' } },
71
- React.createElement(Avatar, { size: "l", disabled: true }, "D"),
72
- React.createElement(Avatar, { size: "l", disabled: true, src: "https://i.pravatar.cc/150?img=14", alt: "Disabled" })))))
84
+ React.createElement("h3", { style: { marginBottom: '12px' } }, "Grouped"),
85
+ React.createElement(Stack, { spacing: 1 }, sizes.map(size => (React.createElement(Stack, { key: size, direction: "row", sx: { alignItems: 'center' } },
86
+ React.createElement(AvatarGroup, { max: 4, slotProps: { surplus: { className: `size-${size}` } } },
87
+ React.createElement(Avatar, { size: size, color: supportedColors[0] }, "AB"),
88
+ React.createElement(Avatar, { size: size, color: supportedColors[1] }, "BC"),
89
+ React.createElement(Avatar, { size: size, src: image, alt: "Image" }),
90
+ React.createElement(Avatar, { size: size, color: supportedColors[4] }, "EF"),
91
+ React.createElement(Avatar, { size: size, color: supportedColors[5] }, "FG")),
92
+ React.createElement(Avatar, { size: size, color: "none", border: true, innerBorder: true },
93
+ React.createElement(Icon, { icon: Link })))))))))
73
94
  };
@@ -1,31 +1,3 @@
1
- export declare const sizeToFontSize: {
2
- xs: number;
3
- s: number;
4
- m: number;
5
- l: number;
6
- xl: number;
7
- };
8
- export declare const sizeToNb: {
9
- xs: number;
10
- s: number;
11
- m: number;
12
- l: number;
13
- xl: number;
14
- };
15
- export declare const sizeToFontWeight: {
16
- xs: number;
17
- s: number;
18
- m: number;
19
- l: number;
20
- xl: number;
21
- };
22
- export declare const sizeToLineHeight: {
23
- xs: number;
24
- s: number;
25
- m: number;
26
- l: number;
27
- xl: number;
28
- };
29
1
  export declare const supportedColors: readonly ["sunrise", "downy", "sugarCoral", "pinkBonnet", "blueMana", "nightBlue", "snowPea", "pluviophile", "cornflower", "paleGreen", "moonBlue"];
30
2
  export declare const colorMapping: Record<string, string>;
31
3
  /**
@@ -1,31 +1,3 @@
1
- export const sizeToFontSize = {
2
- xs: 9.109,
3
- s: 12.525,
4
- m: 18.219,
5
- l: 27.328,
6
- xl: 36.438
7
- };
8
- export const sizeToNb = {
9
- xs: 18,
10
- s: 28,
11
- m: 36,
12
- l: 54,
13
- xl: 72
14
- };
15
- export const sizeToFontWeight = {
16
- xs: 500,
17
- s: 500,
18
- m: 500,
19
- l: 600,
20
- xl: 600
21
- };
22
- export const sizeToLineHeight = {
23
- xs: 18.219,
24
- s: 18.219,
25
- m: 27.328,
26
- l: 36.438,
27
- xl: 45.547
28
- };
29
1
  export const supportedColors = [
30
2
  'sunrise',
31
3
  'downy',
@@ -1,16 +1,14 @@
1
1
  import { AvatarProps as MuiAvatarProps } from '@mui/material';
2
2
  import React from 'react';
3
- import { supportedColors } from './helpers';
4
- type AvatarSize = 'xs' | 's' | 'm' | 'l' | 'xl';
5
- type AvatarColor = (typeof supportedColors)[number] | 'none';
6
- type AvatarDisplay = 'initial' | 'inline';
3
+ export type AvatarSize = 'xs' | 's' | 'm' | 'l' | 'xl';
7
4
  export interface AvatarProps extends Omit<MuiAvatarProps, 'color'> {
8
- color?: AvatarColor;
5
+ color?: string;
9
6
  size?: AvatarSize | number;
7
+ textColor?: string;
10
8
  border?: boolean;
11
9
  innerBorder?: boolean;
12
10
  disabled?: boolean;
13
- display?: AvatarDisplay;
11
+ display?: 'initial' | 'inline';
14
12
  }
15
13
  export declare const Avatar: React.FC<AvatarProps>;
16
14
  export default Avatar;
@@ -1,38 +1,31 @@
1
1
  import { Avatar as MuiAvatar } from '@mui/material';
2
2
  import { styled } from '@mui/material/styles';
3
+ import cx from 'classnames';
3
4
  import React from 'react';
4
- import { colorMapping, nameToColor, supportedColors } from './helpers';
5
- const capitalize = (str) => {
6
- return str.charAt(0).toUpperCase() + str.slice(1);
7
- };
5
+ import { colorMapping, nameToColor } from './helpers';
6
+ const transientProps = ['background', 'textColor', 'customSize'];
8
7
  const StyledAvatar = styled(MuiAvatar, {
9
- shouldForwardProp: prop => prop !== 'gradientColor'
10
- })(({ theme, gradientColor }) => ({
11
- ...(gradientColor && {
12
- color: theme.palette.primary.contrastText,
13
- background: colorMapping[gradientColor] || undefined
14
- })
8
+ shouldForwardProp: prop => !transientProps.includes(String(prop))
9
+ })(({ background, textColor, customSize }) => ({
10
+ ...(customSize && {
11
+ width: customSize,
12
+ height: customSize,
13
+ fontSize: customSize / 2
14
+ }),
15
+ // The text stays white on a coloured avatar whatever the mode
16
+ ...(background && { background, color: '#fff' }),
17
+ ...(textColor && { color: textColor })
15
18
  }));
16
- export const Avatar = ({ className, color, size, border, innerBorder, disabled, display = 'initial', sx, ...props }) => {
17
- const defaultColor = typeof props.children === 'string' ? nameToColor(props.children) : undefined;
18
- const finalColor = color === 'none'
19
- ? undefined
20
- : color && supportedColors.includes(color)
21
- ? color
22
- : defaultColor;
23
- const classNames = [];
24
- if (className)
25
- classNames.push(className);
26
- if (size && typeof size === 'string')
27
- classNames.push(`size-${size}`);
28
- if (disabled)
29
- classNames.push('disabled');
30
- if (border)
31
- classNames.push('border');
32
- if (innerBorder)
33
- classNames.push('innerBorder');
34
- if (display !== 'initial')
35
- classNames.push(`display${capitalize(display)}`);
36
- return (React.createElement(StyledAvatar, { gradientColor: finalColor, className: classNames.join(' ') || undefined, sx: sx, ...props }));
19
+ export const Avatar = ({ className, color, size = 'm', textColor, border, innerBorder, disabled, display = 'initial', children, ...props }) => {
20
+ const isCustomSize = typeof size === 'number';
21
+ const nameColor = typeof children === 'string' ? nameToColor(children) : '';
22
+ const madeColor = color === 'none' ? '' : color || nameColor;
23
+ return (React.createElement(StyledAvatar, { className: cx(className, {
24
+ [`size-${size}`]: !isCustomSize,
25
+ disabled,
26
+ border,
27
+ innerBorder,
28
+ displayInline: display === 'inline'
29
+ }), customSize: isCustomSize ? size : undefined, background: madeColor ? (colorMapping[madeColor] ?? madeColor) : undefined, textColor: textColor, ...props }, children));
37
30
  };
38
31
  export default Avatar;
@@ -0,0 +1,12 @@
1
+ import { ButtonProps as MuiButtonProps } from '@mui/material';
2
+ import React from 'react';
3
+ declare const legacyVariants: {
4
+ readonly primary: "contained";
5
+ readonly secondary: "outlined";
6
+ };
7
+ type LegacyVariant = keyof typeof legacyVariants;
8
+ export interface ButtonProps extends Omit<MuiButtonProps, 'variant'> {
9
+ variant?: MuiButtonProps['variant'] | LegacyVariant;
10
+ }
11
+ export declare const Button: React.ForwardRefExoticComponent<Omit<ButtonProps, "ref"> & React.RefAttributes<HTMLButtonElement>>;
12
+ export default Button;
@@ -0,0 +1,8 @@
1
+ import { Button as MuiButton } from '@mui/material';
2
+ import React from 'react';
3
+ // legacy variant names, mapped to their MUI counterpart
4
+ const legacyVariants = { primary: 'contained', secondary: 'outlined' };
5
+ const isLegacy = (variant) => typeof variant === 'string' && variant in legacyVariants;
6
+ export const Button = React.forwardRef(({ variant, ...props }, ref) => (React.createElement(MuiButton, { ref: ref, variant: isLegacy(variant) ? legacyVariants[variant] : variant, ...props })));
7
+ Button.displayName = 'Button';
8
+ export default Button;
@@ -1,7 +1,7 @@
1
- import { Dialog } from '@mui/material';
2
1
  import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { Dialog } from './index';
3
3
  declare const meta: Meta<typeof Dialog>;
4
4
  export default meta;
5
- type Story = StoryObj<typeof Dialog>;
5
+ type Story = StoryObj<typeof meta>;
6
6
  export declare const Default: Story;
7
7
  export declare const Screenshot: Story;
@@ -0,0 +1,81 @@
1
+ import { CrossMedium, FileTypeFolder, FileTypeText, Icon } from '@linagora/twake-icons';
2
+ import { Button, DialogActions, DialogContent, DialogContentText, DialogTitle, Divider, IconButton, List, ListItemButton, ListItemIcon, ListItemText, Stack } from '@mui/material';
3
+ import React from 'react';
4
+ import { Dialog } from './index';
5
+ const sizes = ['small', 'medium', 'large', 'full'];
6
+ const meta = {
7
+ title: 'Dialog',
8
+ component: Dialog,
9
+ parameters: { layout: 'centered' },
10
+ tags: ['autodocs'],
11
+ argTypes: {
12
+ size: { control: 'select', options: sizes },
13
+ fullScreen: { control: 'boolean' },
14
+ scroll: { control: 'select', options: ['paper', 'body'] }
15
+ }
16
+ };
17
+ export default meta;
18
+ // The cozy-ui doc example: a close button, a title, a list and two actions
19
+ const ExampleDialog = props => (React.createElement(Dialog, { "aria-labelledby": "dialog-title", ...props },
20
+ React.createElement(IconButton, { "aria-label": "Close", size: "medium", sx: { position: 'absolute', top: '1.15rem', right: '1.15rem' }, onClick: event => props.onClose?.(event, 'backdropClick') },
21
+ React.createElement(Icon, { icon: CrossMedium })),
22
+ React.createElement(DialogTitle, { id: "dialog-title", className: "dialogTitleWithClose" }, "Ada Lovelace"),
23
+ React.createElement(Divider, null),
24
+ React.createElement(List, { sx: { my: 1 } },
25
+ React.createElement(ListItemButton, null,
26
+ React.createElement(ListItemIcon, null,
27
+ React.createElement(Icon, { icon: FileTypeFolder, size: 32 })),
28
+ React.createElement(ListItemText, { primary: "I'm a primary text" })),
29
+ React.createElement(Divider, { variant: "inset" }),
30
+ React.createElement(ListItemButton, null,
31
+ React.createElement(ListItemIcon, null,
32
+ React.createElement(Icon, { icon: FileTypeText, size: 32 })),
33
+ React.createElement(ListItemText, { primary: "I'm a primary text", secondary: "I'm a secondary text" })),
34
+ React.createElement(Divider, { variant: "inset" }),
35
+ React.createElement(ListItemButton, null,
36
+ React.createElement(ListItemIcon, null,
37
+ React.createElement(Icon, { icon: FileTypeText, size: 32 })),
38
+ React.createElement(ListItemText, { primary: "I'm a primary text" }))),
39
+ React.createElement(Divider, null),
40
+ React.createElement(DialogActions, null,
41
+ React.createElement(Button, { variant: "outlined", onClick: event => props.onClose?.(event, 'backdropClick') }, "Close Modal"),
42
+ React.createElement(Button, null, "Click Me"))));
43
+ const OpenableDialog = props => {
44
+ const [open, setOpen] = React.useState(false);
45
+ return (React.createElement(React.Fragment, null,
46
+ React.createElement(Button, { onClick: () => setOpen(true) }, "Toggle modal"),
47
+ React.createElement(ExampleDialog, { ...props, open: open, onClose: () => setOpen(false) })));
48
+ };
49
+ export const Default = {
50
+ args: { size: 'medium' },
51
+ render: args => React.createElement(OpenableDialog, { ...args })
52
+ };
53
+ // Renders the dialog in the flow of the page rather than over it
54
+ const staticProps = {
55
+ open: true,
56
+ disablePortal: true,
57
+ hideBackdrop: true,
58
+ disableAutoFocus: true,
59
+ disableScrollLock: true,
60
+ sx: { position: 'static', '& .MuiDialog-container': { height: 'auto' } }
61
+ };
62
+ // Visual Regression - Dialog styles
63
+ export const Screenshot = {
64
+ tags: ['argos'],
65
+ render: () => (React.createElement(Stack, { spacing: 4 },
66
+ React.createElement("section", null,
67
+ React.createElement("h3", { style: { marginBottom: '12px' } }, "Example"),
68
+ React.createElement(ExampleDialog, { ...staticProps })),
69
+ sizes.map(size => (React.createElement("section", { key: size },
70
+ React.createElement("h3", { style: { marginBottom: '12px' } }, size),
71
+ React.createElement(Dialog, { ...staticProps, size: size, fullScreen: false },
72
+ React.createElement(DialogTitle, null, "Dialog Title"),
73
+ React.createElement(DialogContent, null,
74
+ React.createElement(DialogContentText, null,
75
+ "This is a ",
76
+ size,
77
+ " dialog with a title, some content and two action buttons.")),
78
+ React.createElement(DialogActions, null,
79
+ React.createElement(Button, { variant: "outlined" }, "Cancel"),
80
+ React.createElement(Button, null, "Confirm"))))))))
81
+ };
@@ -0,0 +1,9 @@
1
+ import { DialogProps as MuiDialogProps } from '@mui/material';
2
+ import React from 'react';
3
+ export type DialogSize = 'small' | 'medium' | 'large' | 'full';
4
+ export interface DialogProps extends MuiDialogProps {
5
+ /** Paper width. Every size but `small` goes full screen on mobile */
6
+ size?: DialogSize;
7
+ }
8
+ export declare const Dialog: React.FC<DialogProps>;
9
+ export default Dialog;
@@ -0,0 +1,9 @@
1
+ import { Dialog as MuiDialog } from '@mui/material';
2
+ import cx from 'classnames';
3
+ import React from 'react';
4
+ import { useBreakpoints } from '../../hooks/useBreakpoints';
5
+ export const Dialog = ({ size = 'medium', fullScreen, classes, ...props }) => {
6
+ const { isMobile } = useBreakpoints();
7
+ return (React.createElement(MuiDialog, { fullScreen: fullScreen ?? (size !== 'small' && isMobile), classes: { ...classes, paper: cx(size, classes?.paper) }, ...props }));
8
+ };
9
+ export default Dialog;
@@ -0,0 +1,8 @@
1
+ import { Theme } from '@mui/material/styles';
2
+ import React from 'react';
3
+ declare const NavRoot: import("@emotion/styled").StyledComponent<import("@mui/material").ListOwnProps & Omit<import("@mui/material").ListOwnProps, keyof import("@mui/material").ListOwnProps> & import("@mui/material/OverridableComponent").CommonProps & Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>, "ref"> & {
4
+ ref?: ((instance: HTMLUListElement | null) => void) | React.RefObject<HTMLUListElement> | null | undefined;
5
+ }, "style" | "className" | "children" | "classes" | "sx" | "dense" | "disablePadding" | "subheader"> & import("@mui/system").MUIStyledCommonProps<Theme>, {}, {}>;
6
+ export type NavProps = React.ComponentProps<typeof NavRoot>;
7
+ export declare const Nav: (props: NavProps) => React.ReactElement;
8
+ export {};
@@ -0,0 +1,14 @@
1
+ import { List } from '@mui/material';
2
+ import { styled } from '@mui/material/styles';
3
+ import React from 'react';
4
+ const NavRoot = styled(List)(({ theme }) => ({
5
+ margin: '24px 0',
6
+ padding: 0,
7
+ [theme.breakpoints.down('lg')]: {
8
+ display: 'flex',
9
+ justifyContent: 'space-around',
10
+ margin: '6px 0 4px'
11
+ }
12
+ }));
13
+ export const Nav = (props) => (React.createElement("nav", null,
14
+ React.createElement(NavRoot, { ...props })));
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ export interface NavDesktopDropdownProps {
3
+ label: string;
4
+ children: React.ReactNode;
5
+ defaultOpen?: boolean;
6
+ limit?: number;
7
+ }
8
+ export declare const NavDesktopDropdown: ({ label, children, defaultOpen, limit }: NavDesktopDropdownProps) => React.ReactElement | null;
@@ -0,0 +1,24 @@
1
+ import { Bottom, Icon } from '@linagora/twake-icons';
2
+ import { ListItem, Typography } from '@mui/material';
3
+ import React, { Children, isValidElement, useState } from 'react';
4
+ import { useBreakpoints } from '../../hooks/useBreakpoints';
5
+ export const NavDesktopDropdown = ({ label, children, defaultOpen = true, limit = 5 }) => {
6
+ const { isDesktop } = useBreakpoints();
7
+ const [open, setOpen] = useState(defaultOpen);
8
+ const isActivated = Children.toArray(children).filter(isValidElement).length > limit;
9
+ const onToggle = () => setOpen(current => !current);
10
+ if (!isDesktop)
11
+ return null;
12
+ return (React.createElement(React.Fragment, null,
13
+ React.createElement(ListItem, { onClick: isActivated ? onToggle : undefined, sx: {
14
+ minHeight: 48,
15
+ py: 1,
16
+ px: 2,
17
+ justifyContent: 'space-between',
18
+ cursor: isActivated ? 'pointer' : undefined
19
+ } },
20
+ React.createElement(Typography, { variant: "caption", sx: { color: 'text.secondary' } }, label),
21
+ isActivated && (React.createElement(Typography, { component: "span", sx: { display: 'flex', ml: '5px', color: 'text.secondary' } },
22
+ React.createElement(Icon, { icon: Bottom, size: 10, rotate: open ? 0 : -90 })))),
23
+ open && children));
24
+ };
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ export interface NavDesktopLimiterProps {
3
+ children: React.ReactNode;
4
+ max?: number;
5
+ }
6
+ export declare const NavDesktopLimiter: ({ children, max }: NavDesktopLimiterProps) => React.ReactElement | null;
@@ -0,0 +1,31 @@
1
+ import { Bottom, Top } from '@linagora/twake-icons';
2
+ import React, { Children, isValidElement, useState } from 'react';
3
+ import { useExtendI18n, useI18n } from 'twake-i18n';
4
+ import { NavIcon } from '../NavIcon';
5
+ import { NavItem } from '../NavItem';
6
+ import { NavLink } from '../NavLink';
7
+ import { NavText } from '../NavText';
8
+ import en from './locales/en.json';
9
+ import fr from './locales/fr.json';
10
+ import ru from './locales/ru.json';
11
+ import vi from './locales/vi.json';
12
+ import { useBreakpoints } from '../../hooks/useBreakpoints';
13
+ const locales = { en, fr, ru, vi };
14
+ export const NavDesktopLimiter = ({ children, max = 5 }) => {
15
+ useExtendI18n(locales);
16
+ const { t } = useI18n();
17
+ const [viewingAll, setViewingAll] = useState(false);
18
+ const { isMobile } = useBreakpoints();
19
+ const items = Children.toArray(children).filter(isValidElement);
20
+ const amountHidden = Math.max(0, items.length - max);
21
+ if (isMobile)
22
+ return null;
23
+ return (React.createElement(React.Fragment, null,
24
+ viewingAll ? items : items.slice(0, max),
25
+ amountHidden > 0 && (React.createElement(NavItem, { secondary: true },
26
+ React.createElement(NavLink, { onClick: () => setViewingAll(current => !current) },
27
+ React.createElement(NavIcon, { icon: viewingAll ? Top : Bottom }),
28
+ React.createElement(NavText, null, viewingAll
29
+ ? t('NavDesktopLimiter.showLess')
30
+ : `${t('NavDesktopLimiter.showMore')} (${amountHidden})`))))));
31
+ };
@@ -0,0 +1,6 @@
1
+ {
2
+ "NavDesktopLimiter": {
3
+ "showMore": "Show More",
4
+ "showLess": "Show Less"
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "NavDesktopLimiter": {
3
+ "showMore": "Voir plus",
4
+ "showLess": "Voir moins"
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "NavDesktopLimiter": {
3
+ "showMore": "Показать больше",
4
+ "showLess": "Показать меньше"
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "NavDesktopLimiter": {
3
+ "showMore": "Hiện thêm",
4
+ "showLess": "Hiện ít hơn"
5
+ }
6
+ }