@lunit/design-system 1.0.0-b.4 → 2.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 (132) hide show
  1. package/dist/cjs/components/Alert/index.js +1 -1
  2. package/dist/cjs/components/Alert/index.js.map +1 -1
  3. package/dist/cjs/components/Button/index.js +1 -1
  4. package/dist/cjs/components/Button/index.js.map +1 -1
  5. package/dist/cjs/components/Checkbox/index.js +1 -1
  6. package/dist/cjs/components/Checkbox/index.js.map +1 -1
  7. package/dist/cjs/components/Chip/index.js +1 -1
  8. package/dist/cjs/components/Chip/index.js.map +1 -1
  9. package/dist/cjs/components/Dialog/index.js +2 -0
  10. package/dist/cjs/components/Dialog/index.js.map +1 -0
  11. package/dist/cjs/components/Radio/index.js +1 -1
  12. package/dist/cjs/components/Radio/index.js.map +1 -1
  13. package/dist/cjs/components/TextField/index.js +1 -1
  14. package/dist/cjs/components/TextField/index.js.map +1 -1
  15. package/dist/cjs/components/ToggleButton/index.js +1 -1
  16. package/dist/cjs/components/ToggleButton/index.js.map +1 -1
  17. package/dist/cjs/index.js +1 -1
  18. package/dist/cjs/index.js.map +1 -1
  19. package/dist/components/Button/Button.js +21 -3
  20. package/dist/components/Button/Button.js.map +1 -1
  21. package/dist/components/Button/Button.styled.js +3 -6
  22. package/dist/components/Button/Button.styled.js.map +1 -1
  23. package/dist/components/Checkbox/Checkbox.js +9 -19
  24. package/dist/components/Checkbox/Checkbox.js.map +1 -1
  25. package/dist/components/Chip/Chip.js +6 -5
  26. package/dist/components/Chip/Chip.js.map +1 -1
  27. package/dist/components/Dialog/Dialog.js +61 -0
  28. package/dist/components/Dialog/Dialog.js.map +1 -0
  29. package/dist/components/Dialog/Dialog.styled.js +137 -0
  30. package/dist/components/Dialog/Dialog.styled.js.map +1 -0
  31. package/dist/components/Dialog/components/DialogAction.js +18 -0
  32. package/dist/components/Dialog/components/DialogAction.js.map +1 -0
  33. package/dist/components/Dialog/index.js +2 -0
  34. package/dist/components/Dialog/index.js.map +1 -0
  35. package/dist/components/Radio/Radio.js +7 -21
  36. package/dist/components/Radio/Radio.js.map +1 -1
  37. package/dist/components/TextField/TextField.js +4 -4
  38. package/dist/components/TextField/TextField.js.map +1 -1
  39. package/dist/components/ToggleButton/ToggleButton.styled.js +2 -3
  40. package/dist/components/ToggleButton/ToggleButton.styled.js.map +1 -1
  41. package/dist/foundation/Elevation/index.js +1 -1
  42. package/dist/foundation/Elevation/index.js.map +1 -1
  43. package/dist/foundation/Typography/index.js +12 -0
  44. package/dist/foundation/Typography/index.js.map +1 -1
  45. package/dist/foundation/Typography/tokens.js +1 -17
  46. package/dist/foundation/Typography/tokens.js.map +1 -1
  47. package/dist/foundation/colors/base/grey.js +3 -1
  48. package/dist/foundation/colors/base/grey.js.map +1 -1
  49. package/dist/foundation/colors/index.js +9 -6
  50. package/dist/foundation/colors/index.js.map +1 -1
  51. package/dist/foundation/colors/token/component.js +31 -115
  52. package/dist/foundation/colors/token/component.js.map +1 -1
  53. package/dist/foundation/colors/token/core.js +43 -75
  54. package/dist/foundation/colors/token/core.js.map +1 -1
  55. package/dist/foundation/index.js +1 -1
  56. package/dist/foundation/index.js.map +1 -1
  57. package/dist/index.js +1 -1
  58. package/dist/index.js.map +1 -1
  59. package/dist/types/components/Alert/Alert.utils.d.ts +3 -3
  60. package/dist/types/components/Button/Button.styled.d.ts +9 -12
  61. package/dist/types/components/Button/Button.types.d.ts +8 -5
  62. package/dist/types/components/Chip/Chip.styled.d.ts +12 -12
  63. package/dist/types/components/Chip/Chip.types.d.ts +7 -2
  64. package/dist/types/components/Dialog/Dialog.d.ts +50 -0
  65. package/dist/types/components/Dialog/Dialog.styled.d.ts +12 -0
  66. package/dist/types/components/Dialog/components/DialogAction.d.ts +8 -0
  67. package/dist/types/components/Dialog/index.d.ts +2 -0
  68. package/dist/types/components/TextField/TextField.types.d.ts +8 -3
  69. package/dist/types/components/Toast/Toast.utils.d.ts +1 -1
  70. package/dist/types/components/ToggleButton/ToggleButton.styled.d.ts +2 -2
  71. package/dist/types/foundation/Elevation/index.d.ts +1 -1
  72. package/dist/types/foundation/Typography/index.d.ts +13 -1
  73. package/dist/types/foundation/Typography/tokens.d.ts +1 -1
  74. package/dist/types/foundation/colors/base/grey.d.ts +2 -0
  75. package/dist/types/foundation/colors/index.d.ts +9 -6
  76. package/dist/types/foundation/colors/token/types.d.ts +0 -2
  77. package/dist/types/foundation/colors/types.d.ts +78 -76
  78. package/dist/types/foundation/index.d.ts +9 -0
  79. package/dist/types/index.d.ts +1 -1
  80. package/package.json +1 -1
  81. package/src/components/Button/Button.styled.ts +3 -6
  82. package/src/components/Button/Button.tsx +117 -42
  83. package/src/components/Button/Button.types.ts +8 -4
  84. package/src/components/Checkbox/Checkbox.tsx +39 -22
  85. package/src/components/Chip/Chip.tsx +15 -4
  86. package/src/components/Chip/Chip.types.ts +9 -2
  87. package/src/components/Dialog/Dialog.styled.ts +165 -0
  88. package/src/components/Dialog/Dialog.tsx +195 -0
  89. package/src/components/Dialog/components/DialogAction.tsx +36 -0
  90. package/src/components/Dialog/index.ts +14 -0
  91. package/src/components/Radio/Radio.tsx +37 -25
  92. package/src/components/TextField/TextField.tsx +10 -10
  93. package/src/components/TextField/TextField.types.ts +11 -4
  94. package/src/components/ToggleButton/ToggleButton.styled.ts +2 -3
  95. package/src/foundation/Elevation/index.ts +1 -1
  96. package/src/foundation/Typography/index.ts +12 -0
  97. package/src/foundation/Typography/tokens.ts +1 -17
  98. package/src/foundation/colors/base/grey.ts +3 -1
  99. package/src/foundation/colors/index.ts +9 -6
  100. package/src/foundation/colors/token/component.ts +24 -108
  101. package/src/foundation/colors/token/core.ts +39 -71
  102. package/src/foundation/colors/token/types.ts +0 -2
  103. package/src/foundation/colors/types.ts +78 -75
  104. package/src/foundation/index.ts +1 -1
  105. package/src/index.ts +1 -1
  106. package/src/stories/GettingStarted.mdx +88 -0
  107. package/src/stories/components/Button/BasicButton.stories.tsx +91 -52
  108. package/src/stories/components/Button/ButtonDocs.mdx +187 -0
  109. package/src/stories/components/Button/Color.stories.tsx +132 -0
  110. package/src/stories/components/Button/IconButton.stories.tsx +42 -25
  111. package/src/stories/components/Button/Kind.stories.tsx +75 -77
  112. package/src/stories/components/{SelectControl/Checkbox.stories.tsx → CheckBox/BasicCheckbox.stories.tsx} +84 -105
  113. package/src/stories/components/CheckBox/CheckboxDocs.mdx +85 -0
  114. package/src/stories/components/Chip/Chip.stories.tsx +80 -5
  115. package/src/stories/components/Chip/ChipDocs.mdx +132 -0
  116. package/src/stories/components/Dialog/Dialog.stories.tsx +320 -0
  117. package/src/stories/components/TextField/BasicTextField.stories.tsx +214 -0
  118. package/src/stories/components/TextField/TextFieldDocs.mdx +140 -0
  119. package/src/stories/components/TextField/TextFieldSize.stories.tsx +26 -3
  120. package/src/stories/components/ToggleButton/Basic.stories.tsx +312 -0
  121. package/src/stories/components/ToggleButton/ToggleButtonDocs.mdx +180 -0
  122. package/src/stories/components/ToggleButton/ToggleButtonKind.stories.tsx +65 -0
  123. package/src/stories/components/ToggleButton/WithIcon.stories.tsx +138 -0
  124. package/src/stories/foundation/Typography/Typography.mdx +31 -46
  125. package/src/stories/foundation/Typography/Typography.stories.tsx +30 -1
  126. package/src/stories/foundation/colors/ColorSystem.tsx +86 -0
  127. package/src/stories/foundation/colors/Docs.mdx +225 -0
  128. package/src/stories/foundation/colors/TokenPaletteTable.tsx +1 -5
  129. package/src/components/Modal/Modal.tsx +0 -8
  130. package/src/components/Modal/index.ts +0 -1
  131. package/src/stories/components/Modal/Modal.stories.tsx +0 -15
  132. package/src/stories/foundation/Typography/TypographyExamples.stories.tsx +0 -44
@@ -6,7 +6,7 @@ import type { CheckboxProps } from "@mui/material";
6
6
  const CustomCheckbox = styled(MuiCheckbox)(({ theme }) => ({
7
7
  width: 20,
8
8
  height: 20,
9
- padding: 1,
9
+ padding: 0,
10
10
  "&.Mui-disabled": {
11
11
  opacity: 0.38,
12
12
  },
@@ -27,30 +27,47 @@ const iconSize = {
27
27
 
28
28
  const DefaultIcon = styled("span")(({ theme }) => ({
29
29
  ...iconSize,
30
- backgroundColor: theme.palette.lunit_token.component.selectcontrol_off,
31
- maskImage:
32
- "url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 18'%3E%3Cpath" +
33
- " fill-rule='evenodd' clip-rule='evenodd' d='M14 1.5H4C2.61929 1.5 1.5 2.61929 1.5 4V14C1.5 15.3807 2.61929 16.5 4 16.5H14C15.3807 " +
34
- "16.5 16.5 15.3807 16.5 14V4C16.5 2.61929 15.3807 1.5 14 1.5ZM4 0C1.79086 0 0 1.79086 0 4V14C0 16.2091 1.79086 18 4 18H14C16.2091 18 18 16.2091 18 14V4C18 1.79086 16.2091 0 14 0H4Z' /%3E%3C/svg%3E\")",
30
+ borderRadius: "20%",
31
+ boxShadow: `inset 0 0 0 1.5px ${theme.palette.lunit_token.component.selectcontrol_off}`,
35
32
  }));
36
33
 
37
- const CheckedIcon = styled("span")(({ theme }) => ({
38
- ...iconSize,
39
- backgroundColor: theme.palette.lunit_token.component.selectcontrol_on,
40
- maskImage:
41
- "url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 18'%3E%3Cpath" +
42
- " fill-rule='evenodd' clip-rule='evenodd' d='M4 0C1.79086 0 0 1.79086 0 4V14C0 16.2091 1.79086 18 4 18H14C16.2091 18 18 16.2091 18 14V4C18 1.79086 16.2091 0 14 0H4ZM14.2516 " +
43
- "7.14413C14.6074 6.72168 14.5533 6.09083 14.1309 5.73508C13.7084 5.37933 13.0776 5.43341 12.7218 5.85586L8.31824 11.0851L5.14993 8.37556C4.7302 8.01661 4.09895 8.06588 3.74 8.48561C3.38105 8.90533 3.43032 9.53658 3.85005 9.89553L7.78416 13.26C7.98672 13.4332 8.25006 13.5184 8.51571 13.4967C8.78135 13.4749 9.02732 13.348 9.19901 13.1441L14.2516 7.14413Z' /%3E%3C/svg%3E\")",
44
- }));
34
+ const CheckedIcon = () => {
35
+ return (
36
+ <svg
37
+ xmlns="http://www.w3.org/2000/svg"
38
+ width="20"
39
+ height="20"
40
+ viewBox="0 0 20 20"
41
+ fill="none"
42
+ >
43
+ <path
44
+ fill-rule="evenodd"
45
+ clip-rule="evenodd"
46
+ d="M5 1C2.79086 1 1 2.79086 1 5V15C1 17.2091 2.79086 19 5 19H15C17.2091 19 19 17.2091 19 15V5C19 2.79086 17.2091 1 15 1H5ZM15.2516 8.14413C15.6074 7.72168 15.5533 7.09083 15.1309 6.73508C14.7084 6.37933 14.0776 6.43341 13.7218 6.85586L9.31824 12.0851L6.14993 9.37556C5.7302 9.01661 5.09895 9.06588 4.74 9.48561C4.38105 9.90533 4.43032 10.5366 4.85005 10.8955L8.78416 14.26C8.98672 14.4332 9.25006 14.5184 9.51571 14.4967C9.78135 14.4749 10.0273 14.348 10.199 14.1441L15.2516 8.14413Z"
47
+ fill="#00C9EA"
48
+ />
49
+ </svg>
50
+ );
51
+ };
45
52
 
46
- const IndeterminateIcon = styled("span")(({ theme }) => ({
47
- ...iconSize,
48
- backgroundColor: theme.palette.lunit_token.component.selectcontrol_on,
49
- maskImage:
50
- "url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 18'%3E%3Cpath" +
51
- " fill-rule='evenodd' clip-rule='evenodd' d='M4 0C1.79086 0 0 1.79086 0 4V14C0 16.2091 1.79086 18 4 18H14C16.2091 18 18 16.2091 18 14V4C18 1.79086 16.2091 0 " +
52
- "14 0H4ZM4 8C3.44772 8 3 8.44771 3 9C3 9.5523 3.44771 10 4 10H14C14.5523 10 15 9.5523 15 9C15 8.44772 14.5523 8 14 8H4Z' /%3E%3C/svg%3E\")",
53
- }));
53
+ const IndeterminateIcon = () => {
54
+ return (
55
+ <svg
56
+ xmlns="http://www.w3.org/2000/svg"
57
+ width="20"
58
+ height="20"
59
+ viewBox="0 0 20 20"
60
+ fill="none"
61
+ >
62
+ <path
63
+ fill-rule="evenodd"
64
+ clip-rule="evenodd"
65
+ d="M5 1C2.79086 1 1 2.79086 1 5V15C1 17.2091 2.79086 19 5 19H15C17.2091 19 19 17.2091 19 15V5C19 2.79086 17.2091 1 15 1H5ZM5 9C4.44772 9 4 9.44771 4 10C4 10.5523 4.44771 11 5 11L15 11C15.5523 11 16 10.5523 16 10C16 9.44772 15.5523 9 15 9L5 9Z"
66
+ fill="#00C9EA"
67
+ />
68
+ </svg>
69
+ );
70
+ };
54
71
 
55
72
  const Checkbox = (props: CheckboxProps) => {
56
73
  return (
@@ -19,8 +19,11 @@ import type {
19
19
  } from "./Chip.types";
20
20
 
21
21
  const Chip: ChipType = (props: ChipProps) => {
22
- const { kind, onDelete, onClick, ...restProps } = props;
23
- if (kind === "outlined") return <OutlinedChip {...props} />;
22
+ const { kind, variant, onDelete, onClick, ...restProps } = props;
23
+
24
+ const isOutlined = kind === "outlined" || variant === "outlined";
25
+
26
+ if (isOutlined) return <OutlinedChip {...props} />;
24
27
  else if (onClick) return <EnableContainedChip {...props} />;
25
28
  else if (onDelete) return <DeletableContainedChip {...props} />;
26
29
 
@@ -63,7 +66,7 @@ const getLabelMargin = (
63
66
  };
64
67
 
65
68
  const ReadOnlyContainedChip = (props: ReadOnlyContainedChipProps) => {
66
- const { color = "primary", thumbnail, sx, ...restProps } = props;
69
+ const { color = "primary", thumbnail, sx, variant, ...restProps } = props;
67
70
 
68
71
  return (
69
72
  <StyledContainedChipBase
@@ -89,6 +92,7 @@ const EnableContainedChip = (props: EnableContainedChipProps) => {
89
92
  onDelete,
90
93
  onClick,
91
94
  sx,
95
+ variant,
92
96
  ...restProps
93
97
  } = props;
94
98
 
@@ -117,7 +121,14 @@ const DeleteIconWithHoverLayer = ({ onClick }: { onClick: () => void }) => {
117
121
  );
118
122
  };
119
123
  const DeletableContainedChip = (props: DeletableContainedChipProps) => {
120
- const { color = "primary", thumbnail, onDelete, sx, ...restProps } = props;
124
+ const {
125
+ color = "primary",
126
+ thumbnail,
127
+ onDelete,
128
+ sx,
129
+ variant,
130
+ ...restProps
131
+ } = props;
121
132
 
122
133
  return (
123
134
  <StyledContainedChipDeletable
@@ -6,6 +6,8 @@ import type {
6
6
  } from "@mui/material";
7
7
  import type { OverridableComponent } from "@mui/material/OverridableComponent";
8
8
 
9
+ type DesignSystemChipKind = "outlined" | "contained";
10
+ type DesignSystemAndMuiContainedChipKind = "filled" | "contained";
9
11
  type ColorKeys = keyof typeof CHIP_COLORS;
10
12
  export type ChipColor = (typeof CHIP_COLORS)[ColorKeys];
11
13
  export type ChipThumbnail = string | JSX.Element;
@@ -18,12 +20,16 @@ export interface BaseChipProps
18
20
  MuiChipProps,
19
21
  "label" | "sx" | "style" | "classes" | "onDelete"
20
22
  > {
21
- kind?: "outlined" | "contained";
23
+ // either kind or variant
24
+ kind?: DesignSystemChipKind;
25
+ variant?: "outlined" | DesignSystemAndMuiContainedChipKind;
26
+ // mui variant = outlined | "filled" | undefined
22
27
  color?: ChipColor;
23
28
  }
24
29
 
25
30
  export interface OutlinedChipProps extends BaseChipProps {
26
31
  kind?: "outlined";
32
+ variant?: "outlined";
27
33
  onClick?: never;
28
34
  onDelete?: never;
29
35
  }
@@ -32,9 +38,10 @@ export interface BaseContainedChipProps
32
38
  extends BaseChipProps,
33
39
  Omit<
34
40
  MuiChipProps,
35
- "color" | "size" | "variant" | "avatar" | "deleteIcon" | "icon"
41
+ "color" | "size" | "avatar" | "deleteIcon" | "icon" | "variant"
36
42
  > {
37
43
  kind?: "contained";
44
+ variant?: DesignSystemAndMuiContainedChipKind;
38
45
  thumbnail?: ChipThumbnail;
39
46
  onClick?: () => void;
40
47
  }
@@ -0,0 +1,165 @@
1
+ import { styled } from "@mui/material/styles";
2
+
3
+ import type { DialogBase } from "./Dialog";
4
+ import type { CSSObject } from "@mui/material/styles";
5
+
6
+ export interface DialogElementStyle {
7
+ [key: string]: CSSObject;
8
+ }
9
+
10
+ export type DialogStyle = Pick<DialogBase, "size" | "type" | "nonModal">;
11
+
12
+ const DIALOG_WRAPPER_STYLE: DialogElementStyle = {
13
+ small: {
14
+ width: "320px",
15
+ maxWidth: "320px",
16
+ },
17
+ medium: {
18
+ width: "500px",
19
+ maxWidth: "840px",
20
+ },
21
+ modal: {
22
+ position: "relative",
23
+ boxShadow:
24
+ "0px 12px 24px 8px rgba(0, 0, 0, 0.12), 0px 12px 44px 3px rgba(0, 0, 0, 0.18)",
25
+ },
26
+ nonModal: {
27
+ position: "fixed",
28
+ top: "30px",
29
+ right: "30px",
30
+ boxShadow:
31
+ "0px 12px 24px 8px rgba(0, 0, 0, 0.36), 0px 12px 44px 3px rgba(0, 0, 0, 0.48)",
32
+ },
33
+ };
34
+
35
+ const DIALOG_TITLE_STYLE: DialogElementStyle = {
36
+ small: {
37
+ height: "52px",
38
+ maxHeight: "100%",
39
+ padding: "20px 20px 8px 20px",
40
+ },
41
+ medium: {
42
+ height: "64px",
43
+ maxHeight: "100%",
44
+ padding: "30px 32px 6px 32px",
45
+ },
46
+ };
47
+
48
+ const DIALOG_CONTENT_STYLE: DialogElementStyle = {
49
+ small: {
50
+ paddingInline: "20px calc(20px - 10px)",
51
+ paddingBottom: "28px",
52
+ },
53
+ smallAction: {
54
+ paddingInline: "20px calc(20px - 10px)",
55
+ paddingBottom: "8px",
56
+ },
57
+ medium: {
58
+ paddingInline: "32px calc(32px - 14px)",
59
+ paddingBottom: "32px",
60
+ },
61
+ mediumAction: {
62
+ paddingInline: "32px calc(32px - 14px)",
63
+ paddingBottom: "16px",
64
+ },
65
+ };
66
+
67
+ const DIALOG_ACTION_STYLE: DialogElementStyle = {
68
+ small: {
69
+ height: "64px",
70
+ padding: "8px 20px 20px 20px",
71
+ },
72
+ medium: {
73
+ height: "84px",
74
+ padding: "16px 32px 32px 32px",
75
+ },
76
+ };
77
+
78
+ export const StyledBackdrop = styled("div")({
79
+ position: "fixed",
80
+ top: 0,
81
+ left: 0,
82
+ width: "100%",
83
+ height: "100%",
84
+ backgroundColor: "rgba(17, 17, 19, 0.7)",
85
+ zIndex: 1000,
86
+ display: "grid",
87
+ placeItems: "center",
88
+ });
89
+
90
+ export const StyledDialog = styled("div")<DialogStyle>(
91
+ ({ theme, size, nonModal, type }) => ({
92
+ zIndex: 1001,
93
+ maxHeight: "80vh",
94
+ display: "flex",
95
+ flexDirection: "column",
96
+ boxSizing: "border-box",
97
+ borderRadius: "10px",
98
+ backgroundColor: theme.palette.lunit_token.core.bg_03,
99
+ color: theme.palette.lunit_token.core.text_normal,
100
+
101
+ ...DIALOG_WRAPPER_STYLE[size === "small" ? "small" : "medium"],
102
+ ...DIALOG_WRAPPER_STYLE[nonModal ? "nonModal" : "modal"],
103
+
104
+ "& #dialog-title": {
105
+ ...DIALOG_TITLE_STYLE[size === "small" ? "small" : "medium"],
106
+ },
107
+
108
+ "& #dialog-content": {
109
+ ...DIALOG_CONTENT_STYLE[
110
+ size === "small" && type !== "passive"
111
+ ? "smallAction"
112
+ : size === "small"
113
+ ? "small"
114
+ : size === "medium" && type !== "passive"
115
+ ? "mediumAction"
116
+ : "medium"
117
+ ],
118
+
119
+ scrollbarGutter: "stable",
120
+ "::-webkit-scrollbar": {
121
+ width: size === "small" ? "10px" : "14px",
122
+ },
123
+ "::webkit-scrollbar-track": {
124
+ background: "transparent",
125
+ },
126
+ "::-webkit-scrollbar-thumb": {
127
+ backgroundClip: "padding-box",
128
+ border: `2px solid transparent`,
129
+ /**
130
+ * Figma's border-radius is 6px, but actual border radius is 10px since padding 2px is added.
131
+ */
132
+ borderRadius: "10px",
133
+ backgroundColor: theme.palette.lunit_token.component.scrollbars_bg,
134
+ },
135
+ },
136
+
137
+ "& #dialog-action": {
138
+ ...DIALOG_ACTION_STYLE[size === "small" ? "small" : "medium"],
139
+ },
140
+ })
141
+ );
142
+
143
+ export const StyledDialogTitle = styled("header")({
144
+ display: "flex",
145
+ width: "100%",
146
+ flex: "0 0 auto",
147
+ alignItems: "center",
148
+ justifyContent: "flex-start",
149
+ gap: "8px",
150
+ });
151
+
152
+ export const StyledDialogTitleIconWrapper = styled("div")({
153
+ display: "flex",
154
+ justifyContent: "center",
155
+ width: "20px",
156
+ height: "20px",
157
+ position: "relative",
158
+ marginBottom: "1px",
159
+ });
160
+
161
+ export const StyledDialogContent = styled("div")(({ theme }) => ({
162
+ ...theme.typography.body2_14_regular,
163
+ flex: "1 1 auto",
164
+ overflowY: "scroll",
165
+ }));
@@ -0,0 +1,195 @@
1
+ import React, { useEffect } from "react";
2
+ import { createPortal } from "react-dom";
3
+ import { Close } from "@lunit/design-system-icons";
4
+
5
+ import { DialogAction } from "./components/DialogAction";
6
+ import {
7
+ StyledBackdrop,
8
+ StyledDialog,
9
+ StyledDialogContent,
10
+ StyledDialogTitle,
11
+ StyledDialogTitleIconWrapper,
12
+ } from "./Dialog.styled";
13
+ import Button from "../Button";
14
+ import Typography from "../Typography";
15
+
16
+ import type { SxProps } from "@mui/material/styles";
17
+ import type { TypographyProps } from "@mui/material";
18
+
19
+ export interface DialogBase {
20
+ isOpen: boolean;
21
+ onClose(): void;
22
+ type?: "passive" | "action"; // default passive
23
+ nonModal?: boolean; // default false
24
+ title: string;
25
+ titleIcon?: React.ReactNode;
26
+ titleVariant?: TypographyProps["variant"];
27
+ children: React.ReactNode;
28
+ actions?: React.ReactNode;
29
+ enableBackButtonClose?: boolean;
30
+ enableBackdropClose?: boolean;
31
+ size?: "small" | "medium"; // default "small"
32
+ sx?: SxProps;
33
+ style?: React.CSSProperties;
34
+ className?: string;
35
+ }
36
+
37
+ export interface PassiveDialogType extends DialogBase {
38
+ type: "passive";
39
+ actions?: undefined;
40
+ enableBackButtonClose?: true;
41
+ enableBackdropClose?: true;
42
+ }
43
+ export interface ActionDialogType extends DialogBase {
44
+ type: "action";
45
+ actions: React.ReactNode;
46
+ enableBackButtonClose?: boolean;
47
+ enableBackdropClose?: boolean;
48
+ }
49
+
50
+ export interface PassiveModalProps extends PassiveDialogType {
51
+ nonModal?: false;
52
+ }
53
+ export interface ActionModalProps extends ActionDialogType {
54
+ nonModal?: false;
55
+ }
56
+ export type ModalProps = PassiveModalProps | ActionModalProps;
57
+
58
+ export interface PassiveNonModalProps extends PassiveDialogType {
59
+ nonModal?: true;
60
+ }
61
+ export interface ActionNonModalProps extends ActionDialogType {
62
+ nonModal?: true;
63
+ enableBackdropClose?: false;
64
+ }
65
+ export type NonModalProps = PassiveNonModalProps | ActionNonModalProps;
66
+
67
+ export type DialogProps = ModalProps | NonModalProps;
68
+
69
+ function Dialog(props: DialogProps) {
70
+ const { isOpen, type, nonModal = false, onClose } = props;
71
+
72
+ const isActionModal = type === "action" && !nonModal;
73
+ const isPassiveModal = type === "passive" && !nonModal;
74
+ const isActionNonModal = type === "action" && nonModal;
75
+
76
+ function handleBackdropClose(e: React.MouseEvent<HTMLDivElement>) {
77
+ const isClosable =
78
+ isPassiveModal || (isActionModal && props.enableBackdropClose);
79
+
80
+ if (!isClosable) return;
81
+ if (e.target !== e.currentTarget) return;
82
+
83
+ onClose();
84
+ }
85
+
86
+ useEffect(() => {
87
+ const isClosable =
88
+ isOpen &&
89
+ (isPassiveModal ||
90
+ (isActionModal && props.enableBackdropClose) ||
91
+ (isActionNonModal && props.enableBackButtonClose));
92
+
93
+ if (!isClosable) return;
94
+
95
+ function handleEscClose(event: KeyboardEvent) {
96
+ if (event.key === "Escape") onClose();
97
+ }
98
+ function handleBackButtonClose(event: KeyboardEvent) {
99
+ if (event.key === "Backspace") onClose();
100
+ }
101
+
102
+ document.addEventListener("keydown", handleEscClose);
103
+ document.addEventListener("keydown", handleBackButtonClose);
104
+
105
+ return () => {
106
+ document.removeEventListener("keydown", handleEscClose);
107
+ document.removeEventListener("keydown", handleBackButtonClose);
108
+ };
109
+ }, [isOpen, isPassiveModal, onClose]);
110
+
111
+ if (!isOpen) return null;
112
+ return createPortal(
113
+ nonModal ? (
114
+ <DialogBase dialogProps={{ ...props }} />
115
+ ) : (
116
+ <StyledBackdrop
117
+ onClick={handleBackdropClose}
118
+ data-testid="dialog-backdrop"
119
+ >
120
+ <DialogBase dialogProps={{ ...props }} />
121
+ </StyledBackdrop>
122
+ ),
123
+
124
+ document.body
125
+ );
126
+ }
127
+
128
+ function DialogBase({ dialogProps }: { dialogProps: DialogBase }) {
129
+ const {
130
+ nonModal = false,
131
+ onClose,
132
+ title,
133
+ titleIcon,
134
+ titleVariant = "headline5",
135
+ children,
136
+ actions,
137
+ type,
138
+ size = "small",
139
+ sx,
140
+ style,
141
+ className,
142
+ } = dialogProps;
143
+
144
+ return (
145
+ <StyledDialog
146
+ role="dialog"
147
+ aria-labelledby="dialog-title"
148
+ size={size}
149
+ nonModal={nonModal}
150
+ type={type}
151
+ sx={{
152
+ ...sx,
153
+ }}
154
+ style={style}
155
+ className={`dialog ${className ?? ""}`}
156
+ >
157
+ <StyledDialogTitle id="dialog-title" className="dialog-title-wrapper">
158
+ {titleIcon && (
159
+ <StyledDialogTitleIconWrapper className="dialog-title-icon">
160
+ {titleIcon}
161
+ </StyledDialogTitleIconWrapper>
162
+ )}
163
+ <Typography
164
+ component="h2"
165
+ id="dialog-title-text"
166
+ variant={titleVariant}
167
+ >
168
+ {title}
169
+ </Typography>
170
+ {type === "passive" && (
171
+ <Button
172
+ id="dialog-title-close-button"
173
+ data-testid="dialog-title-close-button"
174
+ kind="ghost"
175
+ color="secondary"
176
+ icon={<Close />}
177
+ onClick={onClose}
178
+ sx={{
179
+ marginRight: 0,
180
+ marginLeft: "auto",
181
+ }}
182
+ />
183
+ )}
184
+ </StyledDialogTitle>
185
+ <StyledDialogContent id="dialog-content">{children}</StyledDialogContent>
186
+ {type === "action" && actions !== null ? (
187
+ // `actions !== null` is used to not render DialogAction when actions is undefined
188
+ // There was a case when actions is undefined, but DialogAction is rendered with null children
189
+ <DialogAction>{actions}</DialogAction>
190
+ ) : null}
191
+ </StyledDialog>
192
+ );
193
+ }
194
+
195
+ export default Dialog;
@@ -0,0 +1,36 @@
1
+ import React from "react";
2
+ import { styled } from "@mui/material/styles";
3
+
4
+ interface DialogActionProps {
5
+ children: React.ReactNode;
6
+ justifyContent?: React.CSSProperties["justifyContent"];
7
+ sx?: React.CSSProperties;
8
+ }
9
+
10
+ const StyledDialogActions = styled("div")({
11
+ display: "flex",
12
+ flex: "0 0 auto",
13
+ alignItems: "center",
14
+ justifyContent: "flex-end",
15
+ gap: 8,
16
+ });
17
+
18
+ export function DialogAction(props: DialogActionProps) {
19
+ const { children, justifyContent, sx } = props;
20
+
21
+ return (
22
+ <StyledDialogActions
23
+ id="dialog-action"
24
+ data-testid="dialog-action"
25
+ className="dialog-action"
26
+ sx={{
27
+ justifyContent,
28
+ ...sx,
29
+ }}
30
+ >
31
+ {children}
32
+ </StyledDialogActions>
33
+ );
34
+ }
35
+
36
+ export default DialogAction;
@@ -0,0 +1,14 @@
1
+ export { default } from "./Dialog";
2
+
3
+ export type {
4
+ DialogBase,
5
+ PassiveDialogType,
6
+ ActionDialogType,
7
+ PassiveModalProps,
8
+ ActionModalProps,
9
+ PassiveNonModalProps,
10
+ ActionNonModalProps,
11
+ ModalProps,
12
+ NonModalProps,
13
+ DialogProps,
14
+ } from "./Dialog";
@@ -13,36 +13,48 @@ const CustomRadio = styled(MuiRadio)(({ theme }) => ({
13
13
  display: "block",
14
14
  width: 24,
15
15
  height: 24,
16
- backgroundColor: theme.palette.lunit_token.core.focused,
17
- maskImage:
18
- "url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath" +
19
- " fill-rule='evenodd' clip-rule='evenodd' d='M12 23C18.0751 23 23 18.0751 23 12C23 5.92487 18.0751 1 12 1C5.92487 1 1 5.92487 1 12C1 18.0751 5.92487 23 12 23ZM12 24C18.6274 24 24 18.6274 24 12C24 5.37258 18.6274 0 12 0C5.37258 0 0 " +
20
- "5.37258 0 12C0 18.6274 5.37258 24 12 24Z' /%3E%3C/svg%3E\")",
16
+ borderRadius: "50%",
17
+ border: `1px solid ${theme.palette.lunit_token.core.focused}`,
21
18
  },
22
19
  }));
23
20
 
24
- const iconSize = {
25
- width: 20,
26
- height: 20,
21
+ const DefaultIcon = () => {
22
+ return (
23
+ <svg
24
+ xmlns="http://www.w3.org/2000/svg"
25
+ width="20"
26
+ height="20"
27
+ viewBox="0 0 20 20"
28
+ fill="none"
29
+ >
30
+ <path
31
+ fill-rule="evenodd"
32
+ clip-rule="evenodd"
33
+ d="M10 17.5C14.1421 17.5 17.5 14.1421 17.5 10C17.5 5.85786 14.1421 2.5 10 2.5C5.85786 2.5 2.5 5.85786 2.5 10C2.5 14.1421 5.85786 17.5 10 17.5ZM10 19C14.9706 19 19 14.9706 19 10C19 5.02944 14.9706 1 10 1C5.02944 1 1 5.02944 1 10C1 14.9706 5.02944 19 10 19Z"
34
+ fill="#99999B"
35
+ />
36
+ </svg>
37
+ );
27
38
  };
28
39
 
29
- const DefaultIcon = styled("span")(({ theme }) => ({
30
- ...iconSize,
31
- backgroundColor: theme.palette.lunit_token.component.selectcontrol_off,
32
- maskImage:
33
- "url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath" +
34
- " fill-rule='evenodd' clip-rule='evenodd' d='M10 17.5C14.1421 17.5 17.5 14.1421 17.5 10C17.5 5.85786 14.1421 2.5 10 2.5C5.85786 2.5 2.5 5.85786 2.5 10C2.5 14.1421 5.85786 17.5 " +
35
- "10 17.5ZM10 19C14.9706 19 19 14.9706 19 10C19 5.02944 14.9706 1 10 1C5.02944 1 1 5.02944 1 10C1 14.9706 5.02944 19 10 19Z' /%3E%3C/svg%3E\")",
36
- }));
37
-
38
- const CheckedIcon = styled("span")(({ theme }) => ({
39
- ...iconSize,
40
- backgroundColor: theme.palette.lunit_token.component.selectcontrol_on,
41
- maskImage:
42
- "url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath" +
43
- " fill-rule='evenodd' clip-rule='evenodd' d='M10 15C12.7614 15 15 12.7614 15 10C15 7.23858 12.7614 5 10 5C7.23858 5 5" +
44
- " 7.23858 5 10C5 12.7614 7.23858 15 10 15ZM10 19C14.9706 19 19 14.9706 19 10C19 5.02944 14.9706 1 10 1C5.02944 1 1 5.02944 1 10C1 14.9706 5.02944 19 10 19Z' /%3E%3C/svg%3E\")",
45
- }));
40
+ const CheckedIcon = () => {
41
+ return (
42
+ <svg
43
+ xmlns="http://www.w3.org/2000/svg"
44
+ width="20"
45
+ height="20"
46
+ viewBox="0 0 20 20"
47
+ fill="none"
48
+ >
49
+ <path
50
+ fill-rule="evenodd"
51
+ clip-rule="evenodd"
52
+ d="M10 15C12.7614 15 15 12.7614 15 10C15 7.23858 12.7614 5 10 5C7.23858 5 5 7.23858 5 10C5 12.7614 7.23858 15 10 15ZM10 19C14.9706 19 19 14.9706 19 10C19 5.02944 14.9706 1 10 1C5.02944 1 1 5.02944 1 10C1 14.9706 5.02944 19 10 19Z"
53
+ fill="#00C9EA"
54
+ />
55
+ </svg>
56
+ );
57
+ };
46
58
 
47
59
  const Radio = (props: RadioProps) => {
48
60
  return (