@remember-web/primitive 0.4.7 → 0.5.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 (46) hide show
  1. package/dist/src/Common/Divider/index.cjs.js +32 -23
  2. package/dist/src/Common/Divider/index.cjs.js.map +1 -1
  3. package/dist/src/Common/Divider/index.d.ts +15 -2
  4. package/dist/src/Common/Divider/index.d.ts.map +1 -1
  5. package/dist/src/Common/Divider/index.esm.js +30 -3
  6. package/dist/src/Common/Divider/index.esm.js.map +1 -1
  7. package/dist/src/ModalScreen/index.cjs.js +63 -0
  8. package/dist/src/ModalScreen/index.cjs.js.map +1 -0
  9. package/dist/src/ModalScreen/index.d.ts +23 -0
  10. package/dist/src/ModalScreen/index.d.ts.map +1 -0
  11. package/dist/src/ModalScreen/index.esm.js +57 -0
  12. package/dist/src/ModalScreen/index.esm.js.map +1 -0
  13. package/dist/src/ModalScreen/radix.cjs.js +49 -0
  14. package/dist/src/ModalScreen/radix.cjs.js.map +1 -0
  15. package/dist/src/ModalScreen/radix.d.ts +15 -0
  16. package/dist/src/ModalScreen/radix.d.ts.map +1 -0
  17. package/dist/src/ModalScreen/radix.esm.js +43 -0
  18. package/dist/src/ModalScreen/radix.esm.js.map +1 -0
  19. package/dist/src/ModalScreen/styles.cjs.js +20 -0
  20. package/dist/src/ModalScreen/styles.cjs.js.map +1 -0
  21. package/dist/src/ModalScreen/styles.d.ts +5 -0
  22. package/dist/src/ModalScreen/styles.d.ts.map +1 -0
  23. package/dist/src/ModalScreen/styles.esm.js +11 -0
  24. package/dist/src/ModalScreen/styles.esm.js.map +1 -0
  25. package/dist/src/index.cjs.js +39 -62
  26. package/dist/src/index.cjs.js.map +1 -1
  27. package/dist/src/index.d.ts +8 -7
  28. package/dist/src/index.d.ts.map +1 -1
  29. package/dist/src/index.esm.js +7 -13
  30. package/dist/src/index.esm.js.map +1 -1
  31. package/dist/src/radix.cjs.js +33 -0
  32. package/dist/src/radix.cjs.js.map +1 -0
  33. package/dist/src/radix.d.ts +20 -0
  34. package/dist/src/radix.d.ts.map +1 -0
  35. package/dist/src/radix.esm.js +9 -0
  36. package/dist/src/radix.esm.js.map +1 -0
  37. package/package.json +8 -4
  38. package/src/Common/Divider/index.tsx +52 -3
  39. package/src/ModalScreen/ModalScreen.Bottom.stories.tsx +308 -0
  40. package/src/ModalScreen/ModalScreen.Header.stories.tsx +261 -0
  41. package/src/ModalScreen/ModalScreen.stories.tsx +269 -0
  42. package/src/ModalScreen/index.tsx +79 -0
  43. package/src/ModalScreen/radix.tsx +77 -0
  44. package/src/ModalScreen/styles.ts +54 -0
  45. package/src/index.ts +8 -7
  46. package/src/radix.ts +24 -0
@@ -0,0 +1,269 @@
1
+ import { IconCloseM, IconMailM, IconProfileM } from '@remember-web/icon';
2
+ import {
3
+ bgModal100,
4
+ bgModal200,
5
+ contents000,
6
+ contents150,
7
+ contents200,
8
+ divider,
9
+ roleRed,
10
+ } from '@remember-web/mixin';
11
+ import type { Meta, StoryObj } from '@storybook/react-vite';
12
+ import { Button } from '@/Buttons';
13
+ import { Divider, Flex, Typography } from '@/Common';
14
+ import { RememberLogo } from '@/Logos';
15
+ import { ModalScreen } from './index';
16
+
17
+ const meta = {
18
+ title: 'Primitive/ModalScreen',
19
+ parameters: {
20
+ docs: {
21
+ description: {
22
+ component: 'Header와 Bottom을 함께 사용하는 실제 모달 화면 예시입니다.',
23
+ },
24
+ },
25
+ },
26
+ decorators: [
27
+ (Story) => (
28
+ <div
29
+ style={{
30
+ backgroundColor: '#f2f2f2',
31
+ padding: '40px',
32
+ minWidth: '400px',
33
+ }}
34
+ >
35
+ <Story />
36
+ </div>
37
+ ),
38
+ ],
39
+ } satisfies Meta;
40
+
41
+ export default meta;
42
+ type Story = StoryObj<typeof meta>;
43
+
44
+ // 기본 모달 예시
45
+ export const BasicModal: Story = {
46
+ render: () => (
47
+ <div>
48
+ <ModalScreen.Header type="default" title="메모 추가" />
49
+ <div
50
+ style={{
51
+ padding: '16px 30px',
52
+ background: 'white',
53
+ minHeight: '200px',
54
+ color: contents200,
55
+ }}
56
+ >
57
+ 내용을 입력해주세요
58
+ </div>
59
+ <ModalScreen.Bottom
60
+ rightElement={
61
+ <Flex gap={8}>
62
+ <Button variant="outline" size="xLarge" layout="block">
63
+ 취소
64
+ </Button>
65
+ <Button variant="primary" size="xLarge" layout="block">
66
+ 저장
67
+ </Button>
68
+ </Flex>
69
+ }
70
+ />
71
+ </div>
72
+ ),
73
+ };
74
+
75
+ // Bottom Off 타입 모달 예시
76
+ export const BottomOffModal: Story = {
77
+ render: () => (
78
+ <div style={{ width: 650, margin: '0 auto' }}>
79
+ <ModalScreen.Header
80
+ title="미리보기"
81
+ rightElement={
82
+ <button type="button" style={{ all: 'unset' }}>
83
+ <IconCloseM />
84
+ </button>
85
+ }
86
+ />
87
+ <Flex
88
+ direction="column"
89
+ align="center"
90
+ style={{
91
+ backgroundColor: bgModal200,
92
+ padding: '24px 85px',
93
+ borderRadius: '0 0 8px 8px',
94
+ }}
95
+ >
96
+ {/* 상단 알림 박스 */}
97
+ <Flex
98
+ width="100%"
99
+ style={{
100
+ backgroundColor: bgModal100,
101
+ padding: '8px 24px',
102
+ marginBottom: '8px',
103
+ }}
104
+ >
105
+ <Typography variant="SubTitle1" color={contents000}>
106
+ {'{#회사명}'}에서 {'{#지원자명}'}님 서류 검토를{' '}
107
+ <span style={{ color: roleRed }}>시작했습니다</span>.
108
+ </Typography>
109
+ </Flex>
110
+
111
+ {/* 메인 콘텐츠 */}
112
+ <Flex
113
+ direction="column"
114
+ align="flex-start"
115
+ width="100%"
116
+ style={{
117
+ backgroundColor: bgModal100,
118
+ padding: '40px 24px 24px',
119
+ }}
120
+ >
121
+ {/* Remember 로고 */}
122
+ <div style={{ marginBottom: '24px' }}>
123
+ <RememberLogo color={contents000} height={18} />
124
+ </div>
125
+
126
+ {/* 제목 */}
127
+ <div style={{ marginBottom: '24px' }}>
128
+ <Typography variant="Title2" color={contents000}>
129
+ 지원하신 {'{#회사명}'} {'{#포지션명}'}의
130
+ <br />
131
+ 서류 검토가 시작되었습니다.
132
+ </Typography>
133
+ </div>
134
+
135
+ {/* 본문 텍스트 */}
136
+ <Typography
137
+ variant="UIBody2"
138
+ color={contents000}
139
+ style={{ marginBottom: '24px' }}
140
+ >
141
+ {'{#지원자명}'}님 안녕하세요.
142
+ <br />
143
+ {'{#회사명}'}에서 제출하신 서류 검토를{' '}
144
+ <span style={{ color: roleRed }}>시작했습니다</span>.<br />
145
+ 연락을 받지 못하시면 아래 정보로 문의해 주세요.
146
+ <br />
147
+ 부디 좋은 결과 있으시길 바랍니다!
148
+ <br />
149
+ 감사합니다.
150
+ </Typography>
151
+
152
+ {/* 담당자 정보 폼 */}
153
+ <Flex
154
+ direction="column"
155
+ width="100%"
156
+ style={{ marginBottom: '24px' }}
157
+ >
158
+ {/* 담당자명 */}
159
+ <Flex
160
+ direction="column"
161
+ align="flex-start"
162
+ width="100%"
163
+ gap={4}
164
+ style={{
165
+ borderBottom: `1px solid ${divider}`,
166
+ padding: '16px 0',
167
+ }}
168
+ >
169
+ <Flex align="center" gap={4}>
170
+ <IconProfileM />
171
+ <Typography variant="SubTitle1" color={contents000}>
172
+ 담당자명
173
+ </Typography>
174
+ </Flex>
175
+ <Typography variant="UIBody2" color={contents000}>
176
+ {'{#기업담당자} 이름}'}
177
+ </Typography>
178
+ </Flex>
179
+
180
+ {/* 담당자 이메일 */}
181
+ <Flex
182
+ direction="column"
183
+ align="flex-start"
184
+ width="100%"
185
+ gap={4}
186
+ style={{
187
+ borderBottom: `1px solid ${divider}`,
188
+ padding: '16px 0',
189
+ }}
190
+ >
191
+ <Flex align="center" gap={4}>
192
+ <IconMailM />
193
+ <Typography variant="SubTitle1" color={contents000}>
194
+ 담당자 이메일
195
+ </Typography>
196
+ </Flex>
197
+ <Typography variant="UIBody2" color={contents000}>
198
+ {'{#기업담당자} 이메일 주소}'}
199
+ </Typography>
200
+ </Flex>
201
+ </Flex>
202
+
203
+ {/* CTA 버튼 */}
204
+ <Button variant="primary" size="medium" layout="inline">
205
+ 지원 현황 바로가기
206
+ </Button>
207
+ </Flex>
208
+
209
+ {/* 하단 푸터 */}
210
+ <Flex direction="column" width={480} style={{ marginTop: '16px' }}>
211
+ <Divider />
212
+ <Flex
213
+ direction="column"
214
+ align="flex-start"
215
+ gap={16}
216
+ style={{ padding: '16px 0' }}
217
+ >
218
+ {/* 안내 문구 */}
219
+ <Typography variant="Caption1" color={contents150}>
220
+ 본 메일은 발신전용으로 회신할 수 없습니다. 서비스와 관련하여
221
+ 궁금하신 사항은 고객센터로 문의해 주세요.
222
+ </Typography>
223
+
224
+ {/* 메뉴 링크 */}
225
+ <Flex align="center" gap={8}>
226
+ <Typography variant="Caption1" color={contents150}>
227
+ 프리미엄 채용공고
228
+ </Typography>
229
+ <Divider
230
+ style={{
231
+ width: '1px',
232
+ height: '12px',
233
+ transform: 'rotate(90deg)',
234
+ }}
235
+ />
236
+ <Typography variant="Caption1" color={contents150}>
237
+ 스카웃 제안 받기
238
+ </Typography>
239
+ <Divider
240
+ style={{
241
+ width: '1px',
242
+ height: '12px',
243
+ transform: 'rotate(90deg)',
244
+ }}
245
+ />
246
+ <Typography variant="Caption1" color={contents150}>
247
+ 고객센터
248
+ </Typography>
249
+ </Flex>
250
+ </Flex>
251
+ </Flex>
252
+
253
+ {/* 최하단 푸터 */}
254
+ <Flex
255
+ direction="column"
256
+ align="center"
257
+ gap={12}
258
+ width={480}
259
+ style={{ padding: '24px 0' }}
260
+ >
261
+ <RememberLogo color={contents150} height={18} />
262
+ <Typography variant="Caption2_B" color={contents150}>
263
+ copyright © Drama&Company All rights reserved.
264
+ </Typography>
265
+ </Flex>
266
+ </Flex>
267
+ </div>
268
+ ),
269
+ };
@@ -0,0 +1,79 @@
1
+ import { contents000, contents200 } from '@remember-web/mixin';
2
+ import type { ReactNode } from 'react';
3
+ import { Divider, Typography } from '@/Common';
4
+ import {
5
+ StyledModalScreenBottom,
6
+ StyledModalScreenHeader,
7
+ StyledModalScreenLeftElement,
8
+ } from './styles';
9
+
10
+ type ModalScreenHeaderProps = {
11
+ title: ReactNode;
12
+ rightElement?: ReactNode;
13
+ divider?: boolean;
14
+ } & (
15
+ | { type?: 'default' }
16
+ | { type: 'vertical' | 'horizontal'; description: ReactNode }
17
+ );
18
+
19
+ const ModalScreenHeader = (props: ModalScreenHeaderProps) => {
20
+ const { divider = true } = props;
21
+
22
+ return (
23
+ <>
24
+ <StyledModalScreenHeader>
25
+ <StyledModalScreenLeftElement data-layout={props.type || 'default'}>
26
+ {typeof props.title === 'string' ? (
27
+ <Typography variant="Title1" color={contents000} ellipsis={1}>
28
+ {props.title}
29
+ </Typography>
30
+ ) : (
31
+ props.title
32
+ )}
33
+ {(props.type === 'vertical' || props.type === 'horizontal') &&
34
+ (typeof props.description === 'string' ? (
35
+ <Typography
36
+ variant="UIBody2"
37
+ color={contents200}
38
+ {...(props.type === 'horizontal' && { ellipsis: 1 })}
39
+ >
40
+ {props.description}
41
+ </Typography>
42
+ ) : (
43
+ props.description
44
+ ))}
45
+ </StyledModalScreenLeftElement>
46
+ {props.rightElement}
47
+ </StyledModalScreenHeader>
48
+ {divider && <Divider />}
49
+ </>
50
+ );
51
+ };
52
+
53
+ type ModalScreenBottomProps = {
54
+ rightElement: ReactNode;
55
+ leftElement?: ReactNode;
56
+ divider?: boolean;
57
+ };
58
+
59
+ const ModalScreenBottom = (props: ModalScreenBottomProps) => {
60
+ const { divider = true } = props;
61
+
62
+ return (
63
+ <>
64
+ {divider && <Divider />}
65
+ <StyledModalScreenBottom>
66
+ {props.leftElement}
67
+ {props.rightElement}
68
+ </StyledModalScreenBottom>
69
+ </>
70
+ );
71
+ };
72
+
73
+ const ModalScreen = {
74
+ Header: ModalScreenHeader,
75
+ Bottom: ModalScreenBottom,
76
+ };
77
+
78
+ export type { ModalScreenHeaderProps, ModalScreenBottomProps };
79
+ export { ModalScreen };
@@ -0,0 +1,77 @@
1
+ import {
2
+ Dialog,
3
+ DialogClose,
4
+ DialogContent,
5
+ DialogDescription,
6
+ DialogOverlay,
7
+ DialogPortal,
8
+ DialogTitle,
9
+ DialogTrigger,
10
+ } from '@radix-ui/react-dialog';
11
+ import type { ModalScreenBottomProps, ModalScreenHeaderProps } from './index';
12
+ import { ModalScreen as BaseModalScreen } from './index';
13
+
14
+ /**
15
+ * Radix UI Dialog를 사용하는 프로젝트를 위한 ModalScreen 컴포넌트
16
+ *
17
+ * @example
18
+ * ```tsx
19
+ * import { ModalScreen } from '@remember-web/primitive/radix';
20
+ *
21
+ * <Dialog.Root open={isOpen}>
22
+ * <Dialog.Portal>
23
+ * <Dialog.Content>
24
+ * <ModalScreen.Header
25
+ * title="제목"
26
+ * rightElement={<CloseButton />}
27
+ * />
28
+ * <ModalScreen.Bottom
29
+ * rightElement={<Button>확인</Button>}
30
+ * />
31
+ * </Dialog.Content>
32
+ * </Dialog.Portal>
33
+ * </Dialog.Root>
34
+ * ```
35
+ */
36
+
37
+ /**
38
+ * Radix UI DialogTitle로 래핑된 ModalScreen.Header
39
+ * 접근성을 위해 DialogTitle이 필수인 Radix UI Dialog에서 사용
40
+ */
41
+ const ModalScreenHeader = (props: ModalScreenHeaderProps) => {
42
+ return (
43
+ <>
44
+ <span
45
+ style={{
46
+ position: 'absolute',
47
+ border: 0,
48
+ width: 1,
49
+ height: 1,
50
+ padding: 0,
51
+ margin: -1,
52
+ overflow: 'hidden',
53
+ clip: 'rect(0, 0, 0, 0)',
54
+ whiteSpace: 'nowrap',
55
+ wordWrap: 'normal',
56
+ }}
57
+ >
58
+ <DialogTitle>{props.title?.toString()}</DialogTitle>
59
+ </span>
60
+ <BaseModalScreen.Header {...props} />
61
+ </>
62
+ );
63
+ };
64
+
65
+ export const ModalScreen = {
66
+ Dialog,
67
+ Overlay: DialogOverlay,
68
+ Trigger: DialogTrigger,
69
+ Portal: DialogPortal,
70
+ Header: ModalScreenHeader,
71
+ Description: DialogDescription,
72
+ Content: DialogContent,
73
+ Bottom: BaseModalScreen.Bottom,
74
+ Close: DialogClose,
75
+ };
76
+
77
+ export type { ModalScreenBottomProps, ModalScreenHeaderProps };
@@ -0,0 +1,54 @@
1
+ import { bgModal100, mobileOnly } from '@remember-web/mixin';
2
+ import styled from 'styled-components';
3
+
4
+ export const StyledModalScreenHeader = styled.header`
5
+ display: flex;
6
+ align-items: center;
7
+ justify-content: space-between;
8
+ gap: 24px;
9
+ background-color: ${bgModal100};
10
+ padding: 24px;
11
+ border-radius: 8px 8px 0 0;
12
+
13
+ &:not(:has(> :only-child)) {
14
+ align-items: flex-start;
15
+ }
16
+
17
+ &:has(> [data-layout='vertical']) {
18
+ padding-bottom: 16px;
19
+ }
20
+
21
+ ${mobileOnly`
22
+ border-radius: 0;
23
+ `}
24
+ `;
25
+
26
+ export const StyledModalScreenLeftElement = styled.div`
27
+ display: grid;
28
+ grid-auto-flow: column;
29
+ align-items: center;
30
+ gap: 8px;
31
+
32
+ &[data-layout='vertical'] {
33
+ grid-auto-flow: row;
34
+ align-items: flex-start;
35
+ }
36
+ `;
37
+
38
+ export const StyledModalScreenBottom = styled.footer`
39
+ display: flex;
40
+ align-items: center;
41
+ justify-content: space-between;
42
+ gap: 24px;
43
+ padding: 14px 24px;
44
+ border-radius: 0 0 8px 8px;
45
+ background-color: ${bgModal100};
46
+
47
+ &:has(> :only-child) {
48
+ justify-content: flex-end;
49
+ }
50
+
51
+ ${mobileOnly`
52
+ border-radius: 0;
53
+ `}
54
+ `;
package/src/index.ts CHANGED
@@ -1,12 +1,13 @@
1
1
  export * from './Avatars';
2
- export * from './Buttons';
3
2
  export * from './Badge';
3
+ export * from './Buttons';
4
+ export * from './Chips';
4
5
  export * from './Common';
5
- export * from './Control';
6
- export * from './Logos';
7
- export * from './Modals';
8
- export * from './Paginations';
6
+ export { _Checkbox, Checkbox } from './Control/Checkbox';
7
+ export { Radio } from './Control/Radio';
8
+ export { _Toggle, CircleBaseToggle, Toggle } from './Control/Toggle';
9
9
  export * from './Inputs';
10
- export * from './Floating';
11
- export * from './Chips';
12
10
  export * from './Labels';
11
+ export * from './Logos';
12
+ export * from './ModalScreen';
13
+ export * from './Paginations';
package/src/radix.ts ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @remember-web/primitive/radix
3
+ *
4
+ * Radix UI에 의존하는 모든 컴포넌트를 한 곳에서 export합니다.
5
+ * Radix UI를 사용하지 않는 프로젝트는 이 파일을 import하지 않으면 됩니다.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * // Radix 사용 프로젝트
10
+ * import { Dialog, ModalScreen, Popover, Tooltip, Divider } from '@remember-web/primitive/radix';
11
+ *
12
+ * // Native 사용 프로젝트
13
+ * import { ModalScreen } from '@remember-web/primitive';
14
+ * ```
15
+ */
16
+
17
+ // Control - Switch (Radix Switch 기반)
18
+ export * from './Control/Switch';
19
+ // Floating 컴포넌트들 (default export)
20
+ export * from './Floating';
21
+ // ModalScreen (Radix DialogTitle 래핑 버전)
22
+ export * from './ModalScreen/radix';
23
+ // Dialog 관련
24
+ export * from './Modals/Dialog';