@linagora/twake-mui 7.0.0 → 8.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 (64) hide show
  1. package/CHANGELOG.md +57 -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/components/Chip/index.js +3 -3
  11. package/dist/{stories → components/Dialog}/Dialog.stories.d.ts +2 -2
  12. package/dist/components/Dialog/Dialog.stories.js +81 -0
  13. package/dist/components/Dialog/index.d.ts +9 -0
  14. package/dist/components/Dialog/index.js +9 -0
  15. package/dist/components/Switch/Switch.stories.d.ts +7 -0
  16. package/dist/components/Switch/Switch.stories.js +58 -0
  17. package/dist/components/Switch/index.d.ts +9 -0
  18. package/dist/components/Switch/index.js +8 -0
  19. package/dist/components/Tabs/Tabs.stories.d.ts +7 -0
  20. package/dist/components/Tabs/Tabs.stories.js +52 -0
  21. package/dist/components/Tabs/index.d.ts +10 -0
  22. package/dist/components/Tabs/index.js +5 -0
  23. package/dist/components/ThemeProvider/index.js +9 -10
  24. package/dist/components/ThemeProvider/types.d.ts +6 -0
  25. package/dist/index.d.ts +10 -1
  26. package/dist/index.js +5 -1
  27. package/dist/lib/makePalette.d.ts +1 -1
  28. package/dist/lib/makePalette.js +3 -1
  29. package/dist/lib/makeShadows.d.ts +1 -1
  30. package/dist/lib/makeShadows.js +2 -2
  31. package/dist/lib/makeTheme.d.ts +3 -2
  32. package/dist/lib/makeTheme.js +15 -16
  33. package/dist/lib/overrides.d.ts +2 -0
  34. package/dist/lib/overrides.js +1027 -0
  35. package/dist/lib/types.d.ts +19 -0
  36. package/dist/stories/Alert.stories.js +57 -53
  37. package/dist/stories/Badge.stories.d.ts +7 -0
  38. package/dist/stories/Badge.stories.js +84 -0
  39. package/dist/stories/Button.stories.d.ts +3 -3
  40. package/dist/stories/Button.stories.js +67 -58
  41. package/dist/stories/{Selection.stories.d.ts → Checkbox.stories.d.ts} +1 -1
  42. package/dist/stories/Checkbox.stories.js +72 -0
  43. package/dist/stories/Chip.stories.js +2 -2
  44. package/dist/stories/Divider.stories.d.ts +7 -0
  45. package/dist/stories/Divider.stories.js +56 -0
  46. package/dist/stories/IconButton.stories.d.ts +7 -0
  47. package/dist/stories/IconButton.stories.js +59 -0
  48. package/dist/stories/Radio.stories.d.ts +7 -0
  49. package/dist/stories/Radio.stories.js +60 -0
  50. package/dist/stories/Snackbar.stories.d.ts +7 -0
  51. package/dist/stories/Snackbar.stories.js +60 -0
  52. package/dist/stories/{Input.stories.d.ts → TextField.stories.d.ts} +1 -1
  53. package/dist/stories/TextField.stories.js +86 -0
  54. package/dist/stories/ThemePlayground.stories.js +20 -113
  55. package/dist/stories/Typography.stories.d.ts +1 -1
  56. package/dist/stories/Typography.stories.js +48 -34
  57. package/package.json +2 -1
  58. package/dist/lib/darkOverrides.d.ts +0 -2
  59. package/dist/lib/darkOverrides.js +0 -3
  60. package/dist/lib/lightOverrides.d.ts +0 -2
  61. package/dist/lib/lightOverrides.js +0 -520
  62. package/dist/stories/Dialog.stories.js +0 -40
  63. package/dist/stories/Input.stories.js +0 -41
  64. package/dist/stories/Selection.stories.js +0 -37
package/CHANGELOG.md CHANGED
@@ -1,3 +1,60 @@
1
+ # @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)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **twake-mui:** Anchor the static Snackbar story top left ([e1cee47](https://github.com/linagora/twake-ui/commit/e1cee476de86ec828cd836b5ce5dd2925360ff29))
7
+ * **twake-mui:** Center the Alert message vertically ([38cb5c2](https://github.com/linagora/twake-ui/commit/38cb5c2200adcaa8b9348fca235a54698b5e26e8))
8
+ * **twake-mui:** Clean old Avatar override ([202ffb6](https://github.com/linagora/twake-ui/commit/202ffb652010dee61e61b0386a4fbe45f6074859))
9
+ * **twake-mui:** Clean old Button override ([f0c45eb](https://github.com/linagora/twake-ui/commit/f0c45eb10e4610176509a1fd89179dcd43d4144a))
10
+ * **twake-mui:** Clean old Dialog override ([50544ef](https://github.com/linagora/twake-ui/commit/50544ef9426294d0d54a843e61d04b829cb914c2))
11
+ * **twake-mui:** Clean old TextField override ([23b4253](https://github.com/linagora/twake-ui/commit/23b42530b26909abd822e6da8b99b250f5fead39))
12
+ * **twake-mui:** Draw the secondary Switch in the success colour like cozy-ui ([cff53f0](https://github.com/linagora/twake-ui/commit/cff53f0cc0c7f0098843d6f5c0acc5dd9ca4553d))
13
+ * **twake-mui:** Emit colorSchemes with our own tokens for both modes ([962054b](https://github.com/linagora/twake-ui/commit/962054b74fa214494b6786bd3bc2ce4c98fd8714))
14
+ * **twake-mui:** Replace the runtime lodash import with @mui/utils deepmerge ([e6940ff](https://github.com/linagora/twake-ui/commit/e6940ff4d99987ea49ef7a370c01aef43e862373))
15
+ * **twake-mui:** Thread mode into ThemeProvider's defaultMode ([1f98a09](https://github.com/linagora/twake-ui/commit/1f98a09b4421497b50717bc36353043838a36c63))
16
+ * **twake-mui:** Use the 16px Alert icons of cozy-ui ([c781a7d](https://github.com/linagora/twake-ui/commit/c781a7dca3ccacf04c94eb9dd7b0f14b4fcecf70))
17
+
18
+
19
+ ### Features
20
+
21
+ * Expose cssVariables in TypeScript ([23a151e](https://github.com/linagora/twake-ui/commit/23a151eaf47287d1cc49364073d10fd0acceccc9))
22
+ * **twake-mui:** Accept the primary and secondary Button variants of cozy-ui ([daf67fb](https://github.com/linagora/twake-ui/commit/daf67fb9544a11338556201657a6f0e687aab8cd))
23
+ * **twake-mui:** Let apps customize the whole theme ([9af16db](https://github.com/linagora/twake-ui/commit/9af16db3720708f79d69bf48c14522ef65b6b7cc))
24
+ * **twake-mui:** Migrate Alert from cozy-ui ([916405d](https://github.com/linagora/twake-ui/commit/916405d6c2ce25bd83c445a47ba240303291a7b8))
25
+ * **twake-mui:** Migrate Avatar from cozy-ui ([214787f](https://github.com/linagora/twake-ui/commit/214787fbb514b1fe7b510234405821f0256e34e6))
26
+ * **twake-mui:** Migrate Badge from cozy-ui ([3d9509d](https://github.com/linagora/twake-ui/commit/3d9509d318e048505bee95ce80f5369da9fb25b5))
27
+ * **twake-mui:** Migrate Button from cozy-ui ([704f267](https://github.com/linagora/twake-ui/commit/704f267cd13f9998faf3fbb095b3ab798b518950))
28
+ * **twake-mui:** Migrate Checkbox from cozy-ui ([5281cb1](https://github.com/linagora/twake-ui/commit/5281cb1c5eea50ec24bfb528106f13cfb705572a))
29
+ * **twake-mui:** Migrate Dialog from cozy-ui ([d420cf5](https://github.com/linagora/twake-ui/commit/d420cf5c4f9e7aa37c60f6122fd0e903586e046b))
30
+ * **twake-mui:** Migrate Divider from cozy-ui ([44e1d6c](https://github.com/linagora/twake-ui/commit/44e1d6cc13f612a2295895f4be03856eaf90c7a9))
31
+ * **twake-mui:** Migrate Radio from cozy-ui ([effd21f](https://github.com/linagora/twake-ui/commit/effd21fddab6caf43a7e480b16cbd6e93611c34b))
32
+ * **twake-mui:** Migrate Snackbar from cozy-ui ([56f2cac](https://github.com/linagora/twake-ui/commit/56f2cac2d632d19bab976ccee9e70f1b20e5ffdb))
33
+ * **twake-mui:** Migrate Switch from cozy-ui ([545280b](https://github.com/linagora/twake-ui/commit/545280bd72ef81eb62868e7f8b3dc7e058924466))
34
+ * **twake-mui:** Migrate Tabs from cozy-ui ([076ff98](https://github.com/linagora/twake-ui/commit/076ff986612d447c97385898faf10d4ba50bcca1))
35
+ * **twake-mui:** Migrate TextField from cozy-ui ([6a4fd77](https://github.com/linagora/twake-ui/commit/6a4fd77ec5e49c76603b7bd8cd899157ef72fa6b))
36
+ * **twake-mui:** Migrate Typography from cozy-ui ([7f59c34](https://github.com/linagora/twake-ui/commit/7f59c34e4f0a6502f8a30e676c9d7bb2a48f3025))
37
+ * **twake-mui:** Re-export useColorScheme for runtime theme switching ([f3ed328](https://github.com/linagora/twake-ui/commit/f3ed328d45c6b5006403f975a5f07447f5ef2172))
38
+
39
+
40
+ ### BREAKING CHANGES
41
+
42
+ * **twake-mui:** You must use `palette` prop instead of
43
+ `themeOptions` of `TwakeMuiThemeProvider` to override
44
+ the palette
45
+
46
+ # @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)
47
+
48
+
49
+ ### Bug Fixes
50
+
51
+ * **twake-mui:** Clean old IconButton override ([b5a7733](https://github.com/linagora/twake-ui/commit/b5a7733ad9451ea9950e93a787133a2f9bf951d5))
52
+
53
+
54
+ ### Features
55
+
56
+ * **twake-mui:** Migrate IconButton from cozy-ui ([d6aca04](https://github.com/linagora/twake-ui/commit/d6aca0454195f1d3ffd5170b2463f5bec9254678))
57
+
1
58
  # @linagora/twake-mui [7.0.0](https://github.com/linagora/twake-ui/compare/@linagora/twake-mui@6.0.0...@linagora/twake-mui@7.0.0) (2026-09-08)
2
59
 
3
60
 
@@ -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;
@@ -2,9 +2,9 @@ import { Icon, CrossSmall } from '@linagora/twake-icons';
2
2
  import { Box, Chip as MuiChip, IconButton } from '@mui/material';
3
3
  import cx from 'classnames';
4
4
  import React from 'react';
5
- export const Chip = ({ className, deleteIcon = (React.createElement(IconButton, null,
6
- React.createElement(Icon, { icon: CrossSmall }))), label, endIcon, square = false, ...props }) => {
7
- return (React.createElement(MuiChip, { className: cx(className, { square }), deleteIcon: deleteIcon, label: endIcon ? (React.createElement(Box, { component: "span", sx: { display: 'flex', alignItems: 'center', gap: 1 } },
5
+ export const Chip = ({ className, label, endIcon, square = false, size, deleteIcon = (React.createElement(IconButton, { size: size === 'small' ? 'xsmall' : 'small' },
6
+ React.createElement(Icon, { icon: CrossSmall }))), ...props }) => {
7
+ return (React.createElement(MuiChip, { size: size, className: cx(className, { square }), deleteIcon: deleteIcon, label: endIcon ? (React.createElement(Box, { component: "span", sx: { display: 'flex', alignItems: 'center', gap: 1 } },
8
8
  label,
9
9
  React.createElement("span", null, endIcon))) : (label), ...props }));
10
10
  };
@@ -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,7 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { Switch } from './index';
3
+ declare const meta: Meta<typeof Switch>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof meta>;
6
+ export declare const Default: Story;
7
+ export declare const Screenshot: Story;
@@ -0,0 +1,58 @@
1
+ import { Check } from '@linagora/twake-icons';
2
+ import { Divider, FormControlLabel, FormGroup, Stack, Typography } from '@mui/material';
3
+ import React from 'react';
4
+ import { Switch } from './index';
5
+ const colors = ['primary', 'secondary', 'success', 'default'];
6
+ const meta = {
7
+ title: 'Switch',
8
+ component: Switch,
9
+ parameters: { layout: 'centered' },
10
+ tags: ['autodocs'],
11
+ argTypes: {
12
+ color: { control: 'select', options: colors },
13
+ checked: { control: 'boolean' },
14
+ disabled: { control: 'boolean' },
15
+ icon: { control: 'boolean', mapping: { true: Check, false: undefined } }
16
+ }
17
+ };
18
+ export default meta;
19
+ const StateSwitch = ({ defaultChecked, ...props }) => {
20
+ const [checked, setChecked] = React.useState(!!defaultChecked);
21
+ return (React.createElement(Switch, { checked: checked, onChange: event => setChecked(event.target.checked), ...props }));
22
+ };
23
+ export const Default = {
24
+ args: { color: 'primary' },
25
+ render: args => React.createElement(StateSwitch, { defaultChecked: true, ...args })
26
+ };
27
+ const Labelled = ({ labelPlacement }) => (React.createElement(Stack, { spacing: 1 },
28
+ React.createElement(Typography, { variant: "h4" }, "With labels"),
29
+ React.createElement(FormGroup, { row: true },
30
+ React.createElement(FormControlLabel, { control: React.createElement(Switch, { color: "primary" }), label: "With label", labelPlacement: labelPlacement }),
31
+ React.createElement(FormControlLabel, { control: React.createElement(Switch, { color: "primary", checked: true }), label: "With label - checked", labelPlacement: labelPlacement })),
32
+ React.createElement(Typography, { variant: "h5" }, "With disable"),
33
+ colors.map(color => (React.createElement(FormGroup, { key: color, row: true },
34
+ React.createElement(FormControlLabel, { control: React.createElement(Switch, { color: color, disabled: true }), label: color, labelPlacement: labelPlacement }),
35
+ React.createElement(FormControlLabel, { control: React.createElement(Switch, { color: color, checked: true, disabled: true }), label: `${color} - checked`, labelPlacement: labelPlacement })))),
36
+ React.createElement(FormGroup, { row: true },
37
+ React.createElement(FormControlLabel, { control: React.createElement(Switch, { color: "primary", icon: Check, disabled: true }), label: "primary with icon", labelPlacement: labelPlacement }),
38
+ React.createElement(FormControlLabel, { control: React.createElement(Switch, { color: "primary", icon: Check, checked: true, disabled: true }), label: "primary with icon - checked", labelPlacement: labelPlacement }))));
39
+ // Visual Regression - Every example of the cozy-ui Switch doc
40
+ export const Screenshot = {
41
+ tags: ['argos'],
42
+ render: () => (React.createElement(Stack, { spacing: 2 },
43
+ React.createElement("section", null,
44
+ React.createElement("h3", { style: { marginBottom: '12px' } }, "Colors, with icon"),
45
+ colors.map(color => (React.createElement(Stack, { key: color, direction: "row", sx: { alignItems: 'center' } },
46
+ React.createElement(Typography, { sx: { width: 96 } },
47
+ color,
48
+ ":"),
49
+ React.createElement(Switch, { color: color, icon: Check }),
50
+ React.createElement(Switch, { color: color, icon: Check, checked: true }))))),
51
+ React.createElement(Divider, null),
52
+ React.createElement("section", null,
53
+ React.createElement(Labelled, null)),
54
+ React.createElement(Divider, null),
55
+ React.createElement("section", null,
56
+ React.createElement("h3", { style: { marginBottom: '12px' } }, "Label before"),
57
+ React.createElement(Labelled, { labelPlacement: "start" }))))
58
+ };
@@ -0,0 +1,9 @@
1
+ import { IconProps } from '@linagora/twake-icons';
2
+ import { SwitchProps as MuiSwitchProps } from '@mui/material';
3
+ import React from 'react';
4
+ export interface SwitchProps extends Omit<MuiSwitchProps, 'icon' | 'checkedIcon'> {
5
+ /** A twake icon drawn inside the thumb */
6
+ icon?: IconProps['icon'];
7
+ }
8
+ export declare const Switch: React.FC<SwitchProps>;
9
+ export default Switch;
@@ -0,0 +1,8 @@
1
+ import { Icon } from '@linagora/twake-icons';
2
+ import { Switch as MuiSwitch } from '@mui/material';
3
+ import React from 'react';
4
+ export const Switch = ({ icon, ...props }) => {
5
+ const thumb = (React.createElement("span", { className: "switchThumb" }, !!icon && React.createElement(Icon, { icon: icon, size: 14 })));
6
+ return React.createElement(MuiSwitch, { icon: thumb, checkedIcon: thumb, ...props });
7
+ };
8
+ export default Switch;
@@ -0,0 +1,7 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { Tabs } from './index';
3
+ declare const meta: Meta<typeof Tabs>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof meta>;
6
+ export declare const Default: Story;
7
+ export declare const Screenshot: Story;