@team-monolith/cds 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/README.md +46 -0
  2. package/dist/CodleDesignSystemProvider.d.ts +5 -0
  3. package/dist/CodleDesignSystemProvider.js +96 -0
  4. package/dist/components/AlertDialog/AlertDialog.d.ts +14 -0
  5. package/dist/components/AlertDialog/AlertDialog.js +45 -0
  6. package/dist/components/AlertDialog/AlertDialogActions.d.ts +8 -0
  7. package/dist/components/AlertDialog/AlertDialogActions.js +35 -0
  8. package/dist/components/AlertDialog/AlertDialogContent.d.ts +8 -0
  9. package/dist/components/AlertDialog/AlertDialogContent.js +36 -0
  10. package/dist/components/AlertDialog/AlertDialogTitle.d.ts +13 -0
  11. package/dist/components/AlertDialog/AlertDialogTitle.js +38 -0
  12. package/dist/components/AlertDialog/index.d.ts +4 -0
  13. package/dist/components/AlertDialog/index.js +4 -0
  14. package/dist/components/Banner.d.ts +29 -0
  15. package/dist/components/Banner.js +65 -0
  16. package/dist/components/Button.d.ts +26 -0
  17. package/dist/components/Button.js +72 -0
  18. package/dist/components/CheckboxInput.d.ts +27 -0
  19. package/dist/components/CheckboxInput.js +77 -0
  20. package/dist/components/Input.d.ts +17 -0
  21. package/dist/components/Input.js +72 -0
  22. package/dist/components/InputBase.d.ts +42 -0
  23. package/dist/components/InputBase.js +52 -0
  24. package/dist/components/Pagination.d.ts +27 -0
  25. package/dist/components/Pagination.js +32 -0
  26. package/dist/components/PinInput.d.ts +36 -0
  27. package/dist/components/PinInput.js +154 -0
  28. package/dist/components/RadioInput.d.ts +23 -0
  29. package/dist/components/RadioInput.js +78 -0
  30. package/dist/components/SquareButton.d.ts +26 -0
  31. package/dist/components/SquareButton.js +80 -0
  32. package/dist/components/Switch.d.ts +19 -0
  33. package/dist/components/Switch.js +59 -0
  34. package/dist/components/Tag.d.ts +21 -0
  35. package/dist/components/Tag.js +61 -0
  36. package/dist/components/Tooltip.d.ts +26 -0
  37. package/dist/components/Tooltip.js +50 -0
  38. package/dist/foundation/color.d.ts +75 -0
  39. package/dist/foundation/color.js +75 -0
  40. package/dist/foundation/shadows.d.ts +9 -0
  41. package/dist/foundation/shadows.js +10 -0
  42. package/dist/icons/arrows.d.ts +16 -0
  43. package/dist/icons/arrows.js +17 -0
  44. package/dist/icons/brand.d.ts +4 -0
  45. package/dist/icons/brand.js +13 -0
  46. package/dist/icons/map.d.ts +4 -0
  47. package/dist/icons/map.js +13 -0
  48. package/dist/icons/system.d.ts +25 -0
  49. package/dist/icons/system.js +20 -0
  50. package/dist/index.d.ts +2 -0
  51. package/dist/index.js +2 -0
  52. package/dist/patterns/Dropdown/Dropdown.d.ts +27 -0
  53. package/dist/patterns/Dropdown/Dropdown.js +41 -0
  54. package/dist/patterns/Dropdown/DropdownItem.d.ts +42 -0
  55. package/dist/patterns/Dropdown/DropdownItem.js +89 -0
  56. package/dist/patterns/Dropdown/DropdownMenu.d.ts +30 -0
  57. package/dist/patterns/Dropdown/DropdownMenu.js +85 -0
  58. package/dist/patterns/Dropdown/index.d.ts +2 -0
  59. package/dist/patterns/Dropdown/index.js +2 -0
  60. package/dist/patterns/EmptyState/EmptyState.d.ts +16 -0
  61. package/dist/patterns/EmptyState/EmptyState.js +36 -0
  62. package/dist/patterns/EmptyState/index.d.ts +2 -0
  63. package/dist/patterns/EmptyState/index.js +2 -0
  64. package/dist/patterns/Grid/EnhancedTableCell.d.ts +9 -0
  65. package/dist/patterns/Grid/EnhancedTableCell.js +122 -0
  66. package/dist/patterns/Grid/Grid.d.ts +51 -0
  67. package/dist/patterns/Grid/Grid.js +140 -0
  68. package/dist/patterns/Grid/index.d.ts +3 -0
  69. package/dist/patterns/Grid/index.js +2 -0
  70. package/dist/patterns/SegmentedControl/SegmentedControlButton.d.ts +8 -0
  71. package/dist/patterns/SegmentedControl/SegmentedControlButton.js +41 -0
  72. package/dist/patterns/SegmentedControl/SegmentedControlGroup.d.ts +26 -0
  73. package/dist/patterns/SegmentedControl/SegmentedControlGroup.js +50 -0
  74. package/dist/patterns/SegmentedControl/SegmentedControlGroupPropsContext.d.ts +5 -0
  75. package/dist/patterns/SegmentedControl/SegmentedControlGroupPropsContext.js +5 -0
  76. package/dist/patterns/SegmentedControl/SegmentedControlSquareButton.d.ts +8 -0
  77. package/dist/patterns/SegmentedControl/SegmentedControlSquareButton.js +45 -0
  78. package/dist/patterns/SegmentedControl/index.d.ts +3 -0
  79. package/dist/patterns/SegmentedControl/index.js +3 -0
  80. package/dist/patterns/Table/Table.d.ts +16 -0
  81. package/dist/patterns/Table/Table.js +33 -0
  82. package/dist/patterns/Table/TableBody.d.ts +8 -0
  83. package/dist/patterns/Table/TableBody.js +26 -0
  84. package/dist/patterns/Table/TableCell.d.ts +15 -0
  85. package/dist/patterns/Table/TableCell.js +78 -0
  86. package/dist/patterns/Table/TableHead.d.ts +8 -0
  87. package/dist/patterns/Table/TableHead.js +26 -0
  88. package/dist/patterns/Table/TableRow.d.ts +12 -0
  89. package/dist/patterns/Table/TableRow.js +29 -0
  90. package/dist/patterns/Table/TableSizeContext.d.ts +7 -0
  91. package/dist/patterns/Table/TableSizeContext.js +3 -0
  92. package/dist/patterns/Table/TableVariantContext.d.ts +6 -0
  93. package/dist/patterns/Table/TableVariantContext.js +3 -0
  94. package/dist/patterns/Table/index.d.ts +7 -0
  95. package/dist/patterns/Table/index.js +6 -0
  96. package/dist/utils/hover.d.ts +3 -0
  97. package/dist/utils/hover.js +14 -0
  98. package/dist/utils/reset.d.ts +2 -0
  99. package/dist/utils/reset.js +8 -0
  100. package/dist/utils/zIndex.d.ts +3 -0
  101. package/dist/utils/zIndex.js +3 -0
  102. package/package.json +52 -0
  103. package/public/favicon.ico +0 -0
  104. package/public/index.html +43 -0
  105. package/public/logo192.png +0 -0
  106. package/public/logo512.png +0 -0
  107. package/public/manifest.json +25 -0
  108. package/public/robots.txt +3 -0
  109. package/src/App.tsx +7 -0
  110. package/src/cds/CodleDesignSystemProvider.tsx +93 -0
  111. package/src/cds/README.md +23 -0
  112. package/src/cds/components/AlertDialog/AlertDialog.tsx +101 -0
  113. package/src/cds/components/AlertDialog/AlertDialogActions.tsx +34 -0
  114. package/src/cds/components/AlertDialog/AlertDialogContent.tsx +38 -0
  115. package/src/cds/components/AlertDialog/AlertDialogTitle.tsx +63 -0
  116. package/src/cds/components/AlertDialog/index.tsx +4 -0
  117. package/src/cds/components/Banner.tsx +176 -0
  118. package/src/cds/components/Button.tsx +239 -0
  119. package/src/cds/components/CheckboxInput.tsx +270 -0
  120. package/src/cds/components/Input.tsx +166 -0
  121. package/src/cds/components/InputBase.tsx +226 -0
  122. package/src/cds/components/Pagination.tsx +99 -0
  123. package/src/cds/components/PinInput.tsx +322 -0
  124. package/src/cds/components/RadioInput.tsx +226 -0
  125. package/src/cds/components/SquareButton.tsx +229 -0
  126. package/src/cds/components/Switch.tsx +129 -0
  127. package/src/cds/components/Tag.tsx +155 -0
  128. package/src/cds/components/Tooltip.tsx +104 -0
  129. package/src/cds/emotion.d.ts +70 -0
  130. package/src/cds/foundation/color.ts +83 -0
  131. package/src/cds/foundation/shadows.ts +17 -0
  132. package/src/cds/icons/arrows.tsx +61 -0
  133. package/src/cds/icons/brand.tsx +13 -0
  134. package/src/cds/icons/map.tsx +14 -0
  135. package/src/cds/icons/system.tsx +113 -0
  136. package/src/cds/index.ts +3 -0
  137. package/src/cds/patterns/Dropdown/Dropdown.tsx +111 -0
  138. package/src/cds/patterns/Dropdown/DropdownItem.tsx +203 -0
  139. package/src/cds/patterns/Dropdown/DropdownMenu.tsx +176 -0
  140. package/src/cds/patterns/Dropdown/index.tsx +2 -0
  141. package/src/cds/patterns/EmptyState/EmptyState.tsx +91 -0
  142. package/src/cds/patterns/EmptyState/empty-state-icon.svg +36 -0
  143. package/src/cds/patterns/EmptyState/index.tsx +2 -0
  144. package/src/cds/patterns/Grid/EnhancedTableCell.tsx +180 -0
  145. package/src/cds/patterns/Grid/Grid.tsx +360 -0
  146. package/src/cds/patterns/Grid/index.tsx +4 -0
  147. package/src/cds/patterns/SegmentedControl/SegmentedControlButton.tsx +41 -0
  148. package/src/cds/patterns/SegmentedControl/SegmentedControlGroup.tsx +81 -0
  149. package/src/cds/patterns/SegmentedControl/SegmentedControlGroupPropsContext.tsx +9 -0
  150. package/src/cds/patterns/SegmentedControl/SegmentedControlSquareButton.tsx +51 -0
  151. package/src/cds/patterns/SegmentedControl/index.ts +3 -0
  152. package/src/cds/patterns/Table/Table.tsx +56 -0
  153. package/src/cds/patterns/Table/TableBody.tsx +30 -0
  154. package/src/cds/patterns/Table/TableCell.tsx +242 -0
  155. package/src/cds/patterns/Table/TableHead.tsx +30 -0
  156. package/src/cds/patterns/Table/TableRow.tsx +54 -0
  157. package/src/cds/patterns/Table/TableSizeContext.tsx +10 -0
  158. package/src/cds/patterns/Table/TableVariantContext.tsx +9 -0
  159. package/src/cds/patterns/Table/index.tsx +15 -0
  160. package/src/cds/utils/hover.tsx +24 -0
  161. package/src/cds/utils/reset.tsx +19 -0
  162. package/src/cds/utils/zIndex.tsx +3 -0
  163. package/src/index.tsx +10 -0
  164. package/src/react-app-env.d.ts +1 -0
  165. package/tsconfig.json +22 -0
@@ -0,0 +1,270 @@
1
+ /** @jsxImportSource @emotion/react */
2
+ import { SerializedStyles, css, Theme, useTheme } from "@emotion/react";
3
+ import * as React from "react";
4
+ import styled from "@emotion/styled";
5
+ import { ZINDEX } from "../utils/zIndex";
6
+ import { InputHTMLAttributes } from "react";
7
+
8
+ export type CheckboxInputType = "default" | "checked" | "partial";
9
+
10
+ const CHECKBOX_ICON_STYLE = (
11
+ theme: Theme,
12
+ type: CheckboxInputType,
13
+ disabled: boolean
14
+ ): SerializedStyles => {
15
+ return {
16
+ default: css`
17
+ rect {
18
+ fill: ${theme.color.background.neutralBase};
19
+ stroke: ${theme.color.background.neutralAltActive};
20
+ }
21
+ ${disabled
22
+ ? css`
23
+ rect {
24
+ fill: ${theme.color.background.neutralAlt};
25
+ stroke: ${theme.color.background.neutralAltActive};
26
+ }
27
+ `
28
+ : css`
29
+ input[type="checkbox"]:hover + & {
30
+ rect {
31
+ stroke: ${theme.color.background.primary};
32
+ }
33
+ }
34
+ `}
35
+ `,
36
+ checked: css`
37
+ rect {
38
+ fill: ${theme.color.background.primary};
39
+ stroke: ${theme.color.background.primary};
40
+ }
41
+ path {
42
+ fill: ${theme.color.background.neutralBase};
43
+ }
44
+ ${disabled
45
+ ? css`
46
+ rect {
47
+ fill: ${theme.color.background.neutralAlt};
48
+ stroke: ${theme.color.background.primaryDisabled};
49
+ }
50
+ path {
51
+ fill: ${theme.color.background.primaryDisabled};
52
+ }
53
+ `
54
+ : css`
55
+ input[type="checkbox"]:hover + & {
56
+ rect {
57
+ fill: ${theme.color.background.primaryActive};
58
+ stroke: ${theme.color.background.primaryActive};
59
+ }
60
+ }
61
+ `}
62
+ `,
63
+ partial: css`
64
+ rect {
65
+ fill: ${theme.color.background.neutralBase};
66
+ stroke: ${theme.color.background.primary};
67
+ }
68
+ rect + rect {
69
+ fill: ${theme.color.background.primary};
70
+ stroke: none;
71
+ }
72
+ ${disabled
73
+ ? css`
74
+ rect {
75
+ fill: ${theme.color.background.neutralAlt};
76
+ stroke: ${theme.color.background.primaryDisabled};
77
+ }
78
+ rect + rect {
79
+ fill: ${theme.color.background.primaryDisabled};
80
+ stroke: none;
81
+ }
82
+ `
83
+ : css`
84
+ input[type="checkbox"]:hover + & {
85
+ rect {
86
+ stroke: ${theme.color.background.primaryActive};
87
+ }
88
+ rect + rect {
89
+ fill: ${theme.color.background.primaryActive};
90
+ stroke: none;
91
+ }
92
+ }
93
+ `}
94
+ `,
95
+ }[type];
96
+ };
97
+
98
+ export interface CheckboxInputProps {
99
+ className?: string;
100
+ // component: React.ElementType; // 불필요한 dom 중첩을 막기 위해 사용하지 않습니다.
101
+
102
+ /** 체크박스의 체크 여부(Controlled) */
103
+ checked: boolean;
104
+ /** 체크박스의 라벨 */
105
+ label?: string;
106
+ /** 체크박스의 하단 마진(8px) 여부 */
107
+ spacer?: boolean;
108
+ /** 체크박스의 비활성화 여부 */
109
+ disabled?: boolean;
110
+ /** 체크박스의 partial 여부. 체크여부가 우선합니다. */
111
+ partial?: boolean;
112
+ /** 체크박스 값 변경시 호출되는 함수 */
113
+ onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
114
+ /** 체크박스 클릭시 호출되는 함수 */
115
+ onClick?: (event: React.MouseEvent<HTMLInputElement>) => void;
116
+ /** HTML input 태그에 전달될 props */
117
+ inputProps?: InputHTMLAttributes<HTMLInputElement>;
118
+ }
119
+
120
+ /**
121
+ * [피그마](https://www.figma.com/file/yhrRFizzmhPoHdw9FbYow2/Codle-PD-Kit---Components?type=design&node-id=36-907&t=G4t39O7uXZvsGQ9u-0)
122
+ */
123
+ const CheckboxInput = React.forwardRef<any, CheckboxInputProps>(
124
+ (props, ref) => {
125
+ const {
126
+ className,
127
+ checked,
128
+ label,
129
+ spacer,
130
+ disabled,
131
+ partial,
132
+ onChange,
133
+ onClick,
134
+ ...other
135
+ } = props;
136
+ if (!label) {
137
+ return <Checkbox {...props} ref={ref} />;
138
+ }
139
+
140
+ const checkboxProps = { checked, spacer, disabled, partial, onChange };
141
+ return (
142
+ <Label className={className} ref={ref} {...other}>
143
+ <Checkbox {...checkboxProps} />
144
+ <span
145
+ css={css`
146
+ margin-left: 8px;
147
+ `}
148
+ >
149
+ {label}
150
+ </span>
151
+ </Label>
152
+ );
153
+ }
154
+ );
155
+
156
+ const Checkbox = React.forwardRef<any, CheckboxInputProps>((props, ref) => {
157
+ const {
158
+ className,
159
+ checked,
160
+ partial,
161
+ disabled = false,
162
+ spacer = false,
163
+ onChange,
164
+ onClick,
165
+ inputProps,
166
+ ...other
167
+ } = props;
168
+ const type: CheckboxInputType = checked
169
+ ? "checked"
170
+ : partial
171
+ ? "partial"
172
+ : "default";
173
+
174
+ return (
175
+ <CheckboxContainer
176
+ className={className}
177
+ spacer={spacer}
178
+ disabled={disabled}
179
+ ref={ref}
180
+ {...other}
181
+ >
182
+ <StyledInput
183
+ type="checkbox"
184
+ {...inputProps}
185
+ checked={checked}
186
+ disabled={disabled}
187
+ onClick={onClick}
188
+ onChange={onChange}
189
+ />
190
+ <CheckboxIcon type={type} disabled={disabled} />
191
+ </CheckboxContainer>
192
+ );
193
+ });
194
+
195
+ const CheckboxContainer = styled.span<{
196
+ spacer: boolean;
197
+ disabled: boolean;
198
+ }>(
199
+ ({ spacer, disabled }) => css`
200
+ display: inline-block;
201
+ position: relative;
202
+ width: 16px;
203
+ height: 16px;
204
+ cursor: ${disabled ? "default" : "pointer"};
205
+ margin-bottom: ${spacer ? "8px" : "0"};
206
+ `
207
+ );
208
+
209
+ const StyledInput = styled.input`
210
+ cursor: inherit;
211
+ position: absolute;
212
+ top: 0;
213
+ left: 0;
214
+ opacity: 0;
215
+ width: 16px;
216
+ height: 16px;
217
+ margin: 0;
218
+ padding: 0;
219
+ // input 컴포넌트는 보이지 않지만, 클릭이 가능하게 z-index를 설정합니다.
220
+ z-index: ${ZINDEX.inputBase};
221
+ `;
222
+
223
+ const Label = styled.label`
224
+ display: flex;
225
+ position: relative;
226
+ width: fit-content;
227
+ font-size: 14px;
228
+ font-weight: 500;
229
+ line-height: 16px;
230
+ color: ${({ theme }) => theme.color.foreground.neutralBase};
231
+ align-self: center;
232
+ `;
233
+
234
+ const CheckboxIcon = (props: {
235
+ type: CheckboxInputType;
236
+ disabled: boolean;
237
+ }) => {
238
+ const theme = useTheme();
239
+ return (
240
+ <svg
241
+ width="16"
242
+ height="16"
243
+ viewBox="0 0 16 16"
244
+ fill="none"
245
+ xmlns="http://www.w3.org/2000/svg"
246
+ css={CHECKBOX_ICON_STYLE(theme, props.type, props.disabled)}
247
+ >
248
+ <rect
249
+ x="0.5"
250
+ y="0.5"
251
+ width="15"
252
+ height="15"
253
+ rx="1.5"
254
+ fill="currentColor"
255
+ stroke="currentColor"
256
+ />
257
+ {props.type === "checked" && (
258
+ <path
259
+ d="M7.10545 12.6041L7.08783 12.6217L2.68799 8.22185L4.12067 6.78917L7.10551 9.77402L11.8795 5L13.3122 6.43268L7.12313 12.6218L7.10545 12.6041Z"
260
+ fill="currentColor"
261
+ />
262
+ )}
263
+ {props.type === "partial" && (
264
+ <rect x="4" y="4" width="8" height="8" fill="currentColor" />
265
+ )}
266
+ </svg>
267
+ );
268
+ };
269
+
270
+ export default CheckboxInput;
@@ -0,0 +1,166 @@
1
+ /** @jsxImportSource @emotion/react */
2
+ import { Theme, css } from "@emotion/react";
3
+ import styled from "@emotion/styled";
4
+ import React from "react";
5
+ import {
6
+ InputBase,
7
+ InputBaseColor,
8
+ InputBaseProps,
9
+ InputBaseSize,
10
+ } from "./InputBase";
11
+
12
+ export type InputColor = InputBaseColor;
13
+
14
+ export type InputSize = InputBaseSize;
15
+
16
+ export interface InputProps extends InputBaseProps {
17
+ /** Input 컴포넌트 상단에 노출될 문자열 */
18
+ label?: string;
19
+
20
+ /** Input 컴포넌트 하단에 노출될 문자열 */
21
+ hintText?: string;
22
+
23
+ /** hint 영역 좌측에 노출될 아이콘 */
24
+ hintIcon?: React.ReactNode;
25
+ }
26
+
27
+ const COLOR_TO_HINT_STYLES = (
28
+ theme: Theme,
29
+ color: InputColor,
30
+ disabled?: boolean
31
+ ) =>
32
+ disabled
33
+ ? css`
34
+ color: ${theme.color.foreground.neutralBaseDisabled};
35
+ `
36
+ : {
37
+ default: css`
38
+ color: ${theme.color.foreground.neutralBaseDisabled};
39
+ `,
40
+ activePrimary: css`
41
+ color: ${theme.color.foreground.neutralBaseDisabled};
42
+ `,
43
+ activeDanger: css`
44
+ color: ${theme.color.foreground.danger};
45
+ `,
46
+ activeSuccess: css`
47
+ color: ${theme.color.foreground.success};
48
+ `,
49
+ }[color];
50
+
51
+ const SIZE_TO_FONT_STYLES = (size: InputSize) =>
52
+ ({
53
+ small: css`
54
+ font-size: 14px;
55
+ line-height: 20px;
56
+ svg {
57
+ width: 16px;
58
+ height: 16px;
59
+ }
60
+ `,
61
+ medium: css`
62
+ font-size: 16px;
63
+ line-height: 24px;
64
+ svg {
65
+ width: 16px;
66
+ height: 16px;
67
+ }
68
+ `,
69
+ large: css`
70
+ font-size: 18px;
71
+ line-height: 28px;
72
+ svg {
73
+ width: 18px;
74
+ height: 18px;
75
+ }
76
+ `,
77
+ }[size]);
78
+
79
+ /**
80
+ * [피그마](https://www.figma.com/file/yhrRFizzmhPoHdw9FbYow2/Codle-PD-Kit---Components?type=design&node-id=26-10284&t=HnIfxpf8uR6WmzMW-0)
81
+ */
82
+ const Input = React.forwardRef<any, InputProps>(
83
+ (props, ref): React.ReactElement => {
84
+ const {
85
+ label,
86
+ hintText,
87
+ hintIcon,
88
+ className,
89
+ color,
90
+ size,
91
+ placeholder,
92
+ disabled,
93
+ startIcon,
94
+ startLabel,
95
+ endLabel,
96
+ endIcon,
97
+ inputProps,
98
+ inputRef,
99
+ fullWidth,
100
+ onChange,
101
+ onClear,
102
+ defaultValue,
103
+ value,
104
+ ...other
105
+ } = props;
106
+
107
+ return (
108
+ <InputWrapper
109
+ className={props.className}
110
+ size={props.size}
111
+ fullWidth={props.fullWidth}
112
+ ref={ref}
113
+ {...other}
114
+ >
115
+ {label ? (
116
+ <Label disabled={props.disabled}>
117
+ {label} <InputBase {...props} />
118
+ </Label>
119
+ ) : (
120
+ <InputBase {...props} />
121
+ )}
122
+ {hintText && (
123
+ <Hint color={props.color} disabled={props.disabled}>
124
+ {hintIcon}
125
+ {hintText}
126
+ </Hint>
127
+ )}
128
+ </InputWrapper>
129
+ );
130
+ }
131
+ );
132
+
133
+ const InputWrapper = styled.span<{ size: InputSize; fullWidth?: boolean }>`
134
+ display: inline-flex;
135
+ flex-direction: column;
136
+ gap: 8px;
137
+ ${({ size }) => SIZE_TO_FONT_STYLES(size)}
138
+ ${({ fullWidth }) => fullWidth && `width: 100%;`}
139
+
140
+ position: relative;
141
+ `;
142
+
143
+ const Label = styled.label<{ disabled?: boolean }>`
144
+ display: flex;
145
+ flex-direction: column;
146
+ gap: 8px;
147
+ ${({ theme, disabled }) =>
148
+ disabled &&
149
+ css`
150
+ color: ${theme.color.foreground.neutralBaseDisabled};
151
+ `}
152
+ `;
153
+
154
+ const Hint = styled.div<{
155
+ color: InputColor;
156
+ disabled?: boolean;
157
+ }>(
158
+ ({ theme, color, disabled }) => css`
159
+ display: flex;
160
+ align-items: center;
161
+ gap: 4px;
162
+ ${COLOR_TO_HINT_STYLES(theme, color, disabled)};
163
+ `
164
+ );
165
+
166
+ export default Input;
@@ -0,0 +1,226 @@
1
+ import { Theme, css } from "@emotion/react";
2
+ import styled from "@emotion/styled";
3
+ import { InputHTMLAttributes } from "react";
4
+ import { CloseCircleFillIcon } from "../icons/system";
5
+ import { RESET_BUTTON } from "../utils/reset";
6
+
7
+ export type InputBaseColor =
8
+ | "default"
9
+ | "activePrimary"
10
+ | "activeDanger"
11
+ | "activeSuccess";
12
+
13
+ export type InputBaseSize = "large" | "medium" | "small";
14
+
15
+ export interface InputBaseProps {
16
+ className?: string;
17
+ // component?: React.ElementType; Input은 component를 받지 않습니다.
18
+
19
+ /** 컴포넌트 색상 */
20
+ color: InputBaseColor;
21
+
22
+ /** 컴포넌트 크기 */
23
+ size: InputBaseSize;
24
+
25
+ /** 입력되기 전 Input 컴포넌트에 노출될 문자열 */
26
+ placeholder?: string;
27
+
28
+ /** 비활성화 여부 */
29
+ disabled?: boolean;
30
+
31
+ /** Input 컴포넌트 내의 좌측 영역에 노출될 아이콘 */
32
+ startIcon?: React.ReactNode;
33
+
34
+ /** Input 컴포넌트 내의 좌측 영역에 노출될 텍스트.
35
+ * `startIcon` 보다 우측에 노출됩니다. */
36
+ startLabel?: string;
37
+
38
+ /** Input 컴포넌트 내의 우측 영역에 노출될 텍스트.
39
+ * `endIcon` 보다 좌측에 노출됩니다. */
40
+ endLabel?: string;
41
+
42
+ /** Input 컴포넌트 내의 우측 영역에 노출될 아이콘 */
43
+ endIcon?: React.ReactNode;
44
+
45
+ /** HTML input 태그에 전달될 ref */
46
+ inputRef?: React.LegacyRef<HTMLInputElement>;
47
+
48
+ /** HTML input 태그에 전달될 props */
49
+ inputProps?: InputHTMLAttributes<HTMLInputElement>;
50
+
51
+ /** true일 경우, Input이 상위 요소의 전체 길이를 차지하게 됩니다. */
52
+ fullWidth?: boolean;
53
+
54
+ /** 값이 변경될때 호출될 콜백 함수 */
55
+ onChange?: React.ChangeEventHandler<HTMLInputElement>;
56
+
57
+ /**
58
+ * Clear Button을 클릭했을 때의 콜백 함수.
59
+ * 설정하면, Input 컴포넌트 내의 우측 영역에 Clear Button이 노출됩니다.
60
+ */
61
+ onClear?: () => void;
62
+
63
+ /** input의 value의 초기값 (UnControlled Component) */
64
+ defaultValue?: any;
65
+
66
+ /** input의 value (Controlled Component) */
67
+ value?: any;
68
+ }
69
+
70
+ const COLOR_TO_INPUT_STYLES = (
71
+ theme: Theme,
72
+ color: InputBaseColor,
73
+ disabled: boolean
74
+ ) => {
75
+ if (disabled) {
76
+ return css`
77
+ border: none;
78
+ background: ${theme.color.foreground.neutralBaseDisabled};
79
+ color: ${theme.color.background.neutralAlt};
80
+ `;
81
+ }
82
+ return {
83
+ default: css`
84
+ border: none;
85
+ background: ${theme.color.background.neutralAlt};
86
+ color: ${theme.color.foreground.neutralBase};
87
+ `,
88
+ activePrimary: css`
89
+ border: 2px solid ${theme.color.foreground.primary};
90
+ background: ${theme.color.background.neutralBase};
91
+ color: ${theme.color.foreground.neutralBase};
92
+ `,
93
+ activeDanger: css`
94
+ border: 2px solid ${theme.color.foreground.danger};
95
+ background: ${theme.color.background.neutralBase};
96
+ color: ${theme.color.foreground.neutralBase};
97
+ `,
98
+ activeSuccess: css`
99
+ border: 2px solid ${theme.color.foreground.success};
100
+ background: ${theme.color.background.neutralBase};
101
+ color: ${theme.color.foreground.neutralBase};
102
+ `,
103
+ }[color];
104
+ };
105
+
106
+ const SIZE_TO_STYLES = (size: InputBaseSize) =>
107
+ ({
108
+ small: css`
109
+ padding-top: 8px;
110
+ padding-bottom: 8px;
111
+ width: 300px;
112
+ height: 36px;
113
+ `,
114
+ medium: css`
115
+ padding-top: 12px;
116
+ padding-bottom: 12px;
117
+ width: 375px;
118
+ height: 48px;
119
+ `,
120
+ large: css`
121
+ padding-top: 14px;
122
+ padding-bottom: 14px;
123
+ width: 375px;
124
+ height: 56px;
125
+ `,
126
+ }[size]);
127
+
128
+ export function InputBase(props: InputBaseProps): React.ReactElement {
129
+ const {
130
+ className,
131
+ color,
132
+ size,
133
+ placeholder,
134
+ disabled = false,
135
+ startIcon,
136
+ startLabel,
137
+ endLabel,
138
+ endIcon,
139
+ inputProps,
140
+ inputRef,
141
+ fullWidth = false,
142
+ onChange,
143
+ onClear,
144
+ defaultValue,
145
+ value,
146
+ } = props;
147
+
148
+ return (
149
+ <InputContainer
150
+ className={className}
151
+ disabled={disabled}
152
+ color={color}
153
+ inputSize={size}
154
+ fullWidth={fullWidth}
155
+ >
156
+ {startIcon}
157
+ {startLabel && <span>{startLabel}</span>}
158
+ <StyledInput
159
+ {...inputProps}
160
+ ref={inputRef}
161
+ onChange={onChange}
162
+ placeholder={placeholder}
163
+ disabled={disabled}
164
+ defaultValue={defaultValue}
165
+ value={value}
166
+ />
167
+ {endLabel && <span>{endLabel}</span>}
168
+ {endIcon}
169
+ {onClear && (
170
+ <ClearButton onClick={onClear} disabled={disabled}>
171
+ <CloseCircleFillIcon />
172
+ </ClearButton>
173
+ )}
174
+ </InputContainer>
175
+ );
176
+ }
177
+
178
+ const InputContainer = styled.div<{
179
+ color: InputBaseColor;
180
+ inputSize: InputBaseSize;
181
+ disabled: boolean;
182
+ fullWidth: boolean;
183
+ }>(
184
+ ({ theme, color, inputSize, disabled, fullWidth }) => css`
185
+ display: flex;
186
+ align-items: center;
187
+ gap: 16px;
188
+
189
+ box-sizing: border-box;
190
+ padding: 14px 16px;
191
+ border-radius: 8px;
192
+ ${COLOR_TO_INPUT_STYLES(theme, color, disabled)}
193
+ ${SIZE_TO_STYLES(inputSize)}
194
+ ${fullWidth && `width: inherit;`} // InputWrapper의 width를 따릅니다.
195
+ `
196
+ );
197
+
198
+ const StyledInput = styled.input(
199
+ ({ theme }) => css`
200
+ appearance: none;
201
+ flex: 1;
202
+ min-width: 0;
203
+ border: none;
204
+ padding: 0;
205
+ background-color: transparent;
206
+ font: inherit;
207
+ color: currentColor;
208
+
209
+ &:focus-visible {
210
+ outline: none;
211
+ }
212
+ &::placeholder {
213
+ color: ${theme.color.foreground.neutralBaseDisabled};
214
+ }
215
+ `
216
+ );
217
+
218
+ const ClearButton = styled.button`
219
+ ${RESET_BUTTON}
220
+ color: currentColor;
221
+ &:not(:disabled) {
222
+ cursor: pointer;
223
+ }
224
+ display: flex;
225
+ align-items: center;
226
+ `;