@ceed/cds 1.21.0 → 1.22.1

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.
@@ -0,0 +1,56 @@
1
+ import React, { ReactNode } from 'react';
2
+ import type { SxProps } from '@mui/joy/styles/types';
3
+ export interface RadioTileOption<T = string> {
4
+ value: T;
5
+ label: ReactNode;
6
+ disabled?: boolean;
7
+ startDecorator?: ReactNode;
8
+ }
9
+ export interface RadioTileGroupProps<T = string> {
10
+ /**
11
+ * @default 'sm'
12
+ */
13
+ size?: 'sm' | 'md' | 'lg';
14
+ options: RadioTileOption<T>[];
15
+ value?: T;
16
+ defaultValue?: T;
17
+ name?: string;
18
+ onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
19
+ sx?: SxProps;
20
+ disabled?: boolean;
21
+ className?: string;
22
+ useIndicator?: boolean;
23
+ flex?: boolean;
24
+ /**
25
+ * 지정하지 않으면 한 row에 모든 옵션이 렌더링된다.
26
+ * - 특정 rows 이내로 렌더링 하고 싶으면 `columns: Math.ceil(options.length / 원하는 rows)`로 설정할 수 있다.
27
+ * - 수직으로 렌더링 하고 싶으면 `columns: 1`로 설정할 수 있다.
28
+ * - `flex` 옵션과 함께 사용할수 있다.
29
+ */
30
+ columns?: number;
31
+ /**
32
+ * @default 'center'
33
+ */
34
+ textAlign?: 'start' | 'center';
35
+ /**
36
+ * Label for the RadioTileGroup
37
+ */
38
+ label?: React.ReactNode;
39
+ /**
40
+ * Helper text for the RadioTileGroup
41
+ */
42
+ helperText?: React.ReactNode;
43
+ /**
44
+ * Whether the RadioTileGroup has an error
45
+ */
46
+ error?: boolean;
47
+ /**
48
+ * Whether the RadioTileGroup is required
49
+ */
50
+ required?: boolean;
51
+ }
52
+ declare function RadioTileGroup<T extends string | number = string>(props: RadioTileGroupProps<T>): React.JSX.Element;
53
+ declare namespace RadioTileGroup {
54
+ var displayName: string;
55
+ }
56
+ export { RadioTileGroup };
@@ -0,0 +1,3 @@
1
+ import { RadioTileGroup } from './RadioTileGroup';
2
+ export * from './RadioTileGroup';
3
+ export default RadioTileGroup;
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Introduction
4
4
 
5
- Typography 컴포넌트는 텍스트를 표시하기 위한 핵심 컴포넌트입니다. Joy UI Typography 기반으로 하며, 다양한 텍스트 레벨과 스타일을 제공합니다. 제목, 본문, 라벨 모든 텍스트 요소에 일관된 타이포그래피를 적용할 있습니다.
5
+ The Typography component is a core component for displaying text. It is based on Joy UI Typography and provides a variety of text levels and styles. You can apply consistent typography across all text elements such as headings, body text, and labels.
6
6
 
7
7
  ```tsx
8
8
  <Typography children="Typography" />
@@ -16,15 +16,15 @@ Typography 컴포넌트는 텍스트를 표시하기 위한 핵심 컴포넌트
16
16
  ## Usage
17
17
 
18
18
  ```tsx
19
- import { Typography } from '@ceed/cds';
19
+ import { Typography } from '@ceed/ads';
20
20
 
21
21
  function MyComponent() {
22
22
  return (
23
23
  <div>
24
- <Typography level="h1">메인 제목</Typography>
25
- <Typography level="body-md">본문 텍스트입니다.</Typography>
24
+ <Typography level="h1">Main Heading</Typography>
25
+ <Typography level="body-md">This is body text.</Typography>
26
26
  <Typography level="body-sm" color="neutral">
27
- 보조 설명 텍스트입니다.
27
+ This is supporting description text.
28
28
  </Typography>
29
29
  </div>
30
30
  );
@@ -35,18 +35,18 @@ function MyComponent() {
35
35
 
36
36
  ### Headings
37
37
 
38
- 제목을 위한 레벨들입니다. 페이지 구조를 명확하게 나타내는 사용됩니다.
38
+ Levels for headings. Use them to clearly define page structure.
39
39
 
40
40
  ```tsx
41
- <Typography level="h1">H1 - 페이지 메인 제목</Typography>
42
- <Typography level="h2">H2 - 섹션 제목</Typography>
43
- <Typography level="h3">H3 - 하위 섹션 제목</Typography>
44
- <Typography level="h4">H4 - 세부 제목</Typography>
41
+ <Typography level="h1">H1 - Main page heading</Typography>
42
+ <Typography level="h2">H2 - Section heading</Typography>
43
+ <Typography level="h3">H3 - Subsection heading</Typography>
44
+ <Typography level="h4">H4 - Detailed heading</Typography>
45
45
  ```
46
46
 
47
47
  ### Titles
48
48
 
49
- 중요한 제목이나 강조할 텍스트에 사용됩니다.
49
+ Used for important titles or emphasized text.
50
50
 
51
51
  ```tsx
52
52
  <Typography level="title-lg">Large Title</Typography>
@@ -56,13 +56,13 @@ function MyComponent() {
56
56
 
57
57
  ### Body Text
58
58
 
59
- 본문 텍스트를 위한 레벨들입니다. 대부분의 읽기 내용에 사용됩니다.
59
+ Levels for body text. Use these for most readable content.
60
60
 
61
61
  ```tsx
62
- <Typography level="body-lg">큰 본문 텍스트</Typography>
63
- <Typography level="body-md">일반 본문 텍스트</Typography>
64
- <Typography level="body-sm">작은 본문 텍스트</Typography>
65
- <Typography level="body-xs">매우 작은 텍스트</Typography>
62
+ <Typography level="body-lg">Large body text</Typography>
63
+ <Typography level="body-md">Regular body text</Typography>
64
+ <Typography level="body-sm">Small body text</Typography>
65
+ <Typography level="body-xs">Extra small text</Typography>
66
66
  ```
67
67
 
68
68
  ## Common Use Cases
@@ -74,22 +74,22 @@ function ArticlePage() {
74
74
  return (
75
75
  <article>
76
76
  <Typography level="h1" sx={{ mb: 2 }}>
77
- 기사 제목
77
+ Article Title
78
78
  </Typography>
79
79
 
80
80
  <Typography level="body-sm" color="neutral" sx={{ mb: 3 }}>
81
- 2024년 1월 15 · 김철수 작성
81
+ January 15, 2024 · Written by John Doe
82
82
  </Typography>
83
83
 
84
84
  <Typography level="body-md" sx={{ mb: 2 }}>
85
- 기사의 본문 내용이 여기에 들어갑니다. 텍스트는 읽기 쉬운 크기와 간격을 가지고 있습니다.
85
+ The main content of the article goes here. This text uses a readable size and line spacing.
86
86
  </Typography>
87
87
 
88
88
  <Typography level="h2" sx={{ mt: 4, mb: 2 }}>
89
- 섹션 제목
89
+ Section Title
90
90
  </Typography>
91
91
 
92
- <Typography level="body-md">섹션의 내용이 계속됩니다.</Typography>
92
+ <Typography level="body-md">The section content continues here.</Typography>
93
93
  </article>
94
94
  );
95
95
  }
@@ -101,15 +101,15 @@ function ArticlePage() {
101
101
  <Card>
102
102
  <CardContent>
103
103
  <Typography level="title-md" sx={{ mb: 1 }}>
104
- 제품명
104
+ Product Name
105
105
  </Typography>
106
106
 
107
107
  <Typography level="body-sm" color="neutral" sx={{ mb: 2 }}>
108
- 카테고리: 전자제품
108
+ Category: Electronics
109
109
  </Typography>
110
110
 
111
111
  <Typography level="body-md" sx={{ mb: 2 }}>
112
- 제품에 대한 상세 설명이 여기에 들어갑니다.
112
+ A detailed description of the product goes here.
113
113
  </Typography>
114
114
 
115
115
  <Typography level="title-lg" color="primary">
@@ -125,21 +125,21 @@ function ArticlePage() {
125
125
  <Stack spacing={2}>
126
126
  <FormControl>
127
127
  <Typography level="title-sm" component="label">
128
- 사용자 이름
128
+ Username
129
129
  </Typography>
130
- <Input placeholder="이름을 입력하세요" />
130
+ <Input placeholder="Enter your name" />
131
131
  <Typography level="body-xs" color="neutral">
132
- 실명으로 입력해 주세요.
132
+ Please enter your real name.
133
133
  </Typography>
134
134
  </FormControl>
135
135
 
136
136
  <FormControl error>
137
137
  <Typography level="title-sm" component="label">
138
- 이메일 주소
138
+ Email Address
139
139
  </Typography>
140
140
  <Input placeholder="email@example.com" />
141
141
  <Typography level="body-xs" color="danger">
142
- 올바른 이메일 형식이 아닙니다.
142
+ The email format is invalid.
143
143
  </Typography>
144
144
  </FormControl>
145
145
  </Stack>
@@ -150,16 +150,16 @@ function ArticlePage() {
150
150
  ```tsx
151
151
  <Stack spacing={2}>
152
152
  <Box>
153
- <Typography level="body-md">서버 상태:</Typography>
153
+ <Typography level="body-md">Server Status:</Typography>
154
154
  <Typography level="body-md" color="success">
155
- 정상 운영 중
155
+ Operating Normally
156
156
  </Typography>
157
157
  </Box>
158
158
 
159
159
  <Box>
160
- <Typography level="body-md">마지막 업데이트:</Typography>
160
+ <Typography level="body-md">Last Updated:</Typography>
161
161
  <Typography level="body-sm" color="neutral">
162
- 2
162
+ 2 minutes ago
163
163
  </Typography>
164
164
  </Box>
165
165
  </Stack>
@@ -169,7 +169,7 @@ function ArticlePage() {
169
169
 
170
170
  ```tsx
171
171
  <Stack spacing={1}>
172
- <Typography level="title-md">할 일 목록</Typography>
172
+ <Typography level="title-md">Todo List</Typography>
173
173
 
174
174
  {todoItems.map((item) => (
175
175
  <Box key={item.id} sx={{ pl: 2 }}>
@@ -192,39 +192,39 @@ function ArticlePage() {
192
192
 
193
193
  ## Colors
194
194
 
195
- Typography 다양한 색상을 지원합니다:
195
+ Typography supports various colors:
196
196
 
197
197
  ```tsx
198
198
  <Stack spacing={1}>
199
- <Typography color="primary">Primary 색상</Typography>
200
- <Typography color="neutral">Neutral 색상</Typography>
201
- <Typography color="danger">Danger 색상</Typography>
202
- <Typography color="success">Success 색상</Typography>
203
- <Typography color="warning">Warning 색상</Typography>
199
+ <Typography color="primary">Primary color</Typography>
200
+ <Typography color="neutral">Neutral color</Typography>
201
+ <Typography color="danger">Danger color</Typography>
202
+ <Typography color="success">Success color</Typography>
203
+ <Typography color="warning">Warning color</Typography>
204
204
  </Stack>
205
205
  ```
206
206
 
207
207
  ## Component Prop
208
208
 
209
- 다른 HTML 요소나 React 컴포넌트로 렌더링할 있습니다:
209
+ You can render it as a different HTML element or React component:
210
210
 
211
211
  ```tsx
212
212
  <Typography level="h1" component="h2">
213
- h2 태그로 렌더링되는 h1 스타일
213
+ h1 style rendered as an h2 tag
214
214
  </Typography>
215
215
 
216
216
  <Typography level="body-md" component="span">
217
- 인라인 텍스트
217
+ Inline text
218
218
  </Typography>
219
219
 
220
220
  <Typography level="title-md" component={Link} href="/page">
221
- 링크 컴포넌트로 렌더링
221
+ Rendered as a Link component
222
222
  </Typography>
223
223
  ```
224
224
 
225
225
  ## Responsive Typography
226
226
 
227
- 반응형 레벨을 사용할 있습니다:
227
+ You can use responsive levels:
228
228
 
229
229
  ```tsx
230
230
  <Typography
@@ -233,36 +233,36 @@ Typography는 다양한 색상을 지원합니다:
233
233
  fontSize: { xs: '1.5rem', sm: '2rem', md: '2.5rem' },
234
234
  }}
235
235
  >
236
- 반응형 제목
236
+ Responsive Heading
237
237
  </Typography>
238
238
  ```
239
239
 
240
240
  ## Best Practices
241
241
 
242
- 1. **의미적 구조**: 제목 레벨을 순서대로 사용하여 명확한 문서 구조를 만드세요.
242
+ 1. **Semantic Structure**: Use heading levels in order to create a clear document structure.
243
243
 
244
244
  ```tsx
245
- // ✅ 올바른 순서
246
- <Typography level="h1">메인 제목</Typography>
247
- <Typography level="h2">섹션 제목</Typography>
248
- <Typography level="h3">하위 섹션</Typography>
249
-
250
- // ❌ 잘못된 순서
251
- <Typography level="h1">메인 제목</Typography>
252
- <Typography level="h3">섹션 제목</Typography>
245
+ // ✅ Correct order
246
+ <Typography level="h1">Main heading</Typography>
247
+ <Typography level="h2">Section heading</Typography>
248
+ <Typography level="h3">Subsection</Typography>
249
+
250
+ // ❌ Incorrect order
251
+ <Typography level="h1">Main heading</Typography>
252
+ <Typography level="h3">Section heading</Typography>
253
253
  ```
254
254
 
255
- 2. **일관성**: 같은 용도의 텍스트에는 같은 레벨을 사용하세요.
255
+ 2. **Consistency**: Use the same level for text serving the same purpose.
256
256
 
257
- 3. **가독성**: 본문 텍스트에는 적절한 간격과 문단 구분을 제공하세요.
257
+ 3. **Readability**: Provide proper line spacing and paragraph separation for body text.
258
258
 
259
- 4. **색상 대비**: 충분한 색상 대비를 유지하여 접근성을 보장하세요.
259
+ 4. **Color Contrast**: Maintain sufficient color contrast to ensure accessibility.
260
260
 
261
261
  ## Accessibility
262
262
 
263
- - 적절한 HTML 시맨틱 태그 사용
264
- - 스크린 리더 지원
265
- - 키보드 탐색 가능 (링크나 버튼으로 사용될 )
266
- - 충분한 색상 대비
263
+ - Use appropriate semantic HTML tags
264
+ - Support screen readers
265
+ - Ensure keyboard navigation (when used as a link or button)
266
+ - Maintain sufficient color contrast
267
267
 
268
- Typography 사용자 인터페이스에서 정보를 효과적으로 전달하고 시각적 계층 구조를 만드는 핵심적인 역할을 합니다. 적절한 레벨과 스타일을 선택하여 읽기 쉽고 접근 가능한 콘텐츠를 만들 있습니다.
268
+ Typography plays a key role in effectively conveying information and creating visual hierarchy in user interfaces. By choosing appropriate levels and styles, you can create content that is easy to read and accessible.
@@ -43,9 +43,7 @@ function ConfirmationDialog({ open, onClose, onConfirm }) {
43
43
  <Button variant="plain" color="neutral" onClick={onClose}>
44
44
  Cancel
45
45
  </Button>
46
- <Button onClick={onConfirm}>
47
- Confirm
48
- </Button>
46
+ <Button onClick={onConfirm}>Confirm</Button>
49
47
  </>
50
48
  }
51
49
  >
@@ -94,6 +92,57 @@ Dialog Content
94
92
  </DialogFrame>
95
93
  ```
96
94
 
95
+ ### Standalone Usage
96
+
97
+ DialogFrame can be used without a Modal wrapper for embedding dialog-style layouts directly within a page.
98
+
99
+ > ⚠️ **Important** ⚠️
100
+ >
101
+ > When using DialogFrame without Modal, the parent container **must** provide explicit `width` and `height` values.
102
+ > DialogFrame inherits its dimensions from `ModalDialog`, which normally receives sizing from the Modal overlay.
103
+ > Without these constraints, the component will not render with correct dimensions.
104
+
105
+ ```tsx
106
+ <Box sx={{
107
+ width: 480,
108
+ height: 300
109
+ }}>
110
+ <DialogFrame {...args} title="Standalone Dialog" actions={<>
111
+ <Button variant="plain" color="neutral">
112
+ Cancel
113
+ </Button>
114
+ <Button variant="plain">Confirm</Button>
115
+ </>}>
116
+ DialogFrame used without Modal. The parent container must provide explicit width and height.
117
+ </DialogFrame>
118
+ </Box>
119
+ ```
120
+
121
+ ```tsx
122
+ import { DialogFrame, Button, Box } from '@ceed/cds';
123
+
124
+ // Standalone usage requires explicit container dimensions
125
+ function EmbeddedDialog() {
126
+ return (
127
+ <Box sx={{ width: 480, height: 300 }}>
128
+ <DialogFrame
129
+ title="Settings"
130
+ actions={
131
+ <>
132
+ <Button variant="plain" color="neutral">
133
+ Cancel
134
+ </Button>
135
+ <Button>Save</Button>
136
+ </>
137
+ }
138
+ >
139
+ This dialog is embedded directly in the page layout.
140
+ </DialogFrame>
141
+ </Box>
142
+ );
143
+ }
144
+ ```
145
+
97
146
  ## When to Use
98
147
 
99
148
  ### ✅ Good Use Cases
@@ -177,21 +226,11 @@ function QuickAddDialog({ open, onClose, onSubmit }) {
177
226
  <Stack gap={2}>
178
227
  <FormControl>
179
228
  <FormLabel>Name</FormLabel>
180
- <Input
181
- value={name}
182
- onChange={(e) => setName(e.target.value)}
183
- placeholder="Enter name"
184
- autoFocus
185
- />
229
+ <Input value={name} onChange={(e) => setName(e.target.value)} placeholder="Enter name" autoFocus />
186
230
  </FormControl>
187
231
  <FormControl>
188
232
  <FormLabel>Email</FormLabel>
189
- <Input
190
- type="email"
191
- value={email}
192
- onChange={(e) => setEmail(e.target.value)}
193
- placeholder="Enter email"
194
- />
233
+ <Input type="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="Enter email" />
195
234
  </FormControl>
196
235
  </Stack>
197
236
  </DialogFrame>
@@ -216,15 +255,11 @@ function UnsavedChangesDialog({ open, onClose, onDiscard, onSave }) {
216
255
  <Button variant="outlined" color="danger" onClick={onDiscard}>
217
256
  Discard
218
257
  </Button>
219
- <Button onClick={onSave}>
220
- Save Changes
221
- </Button>
258
+ <Button onClick={onSave}>Save Changes</Button>
222
259
  </>
223
260
  }
224
261
  >
225
- <Typography>
226
- You have unsaved changes. Would you like to save them before leaving?
227
- </Typography>
262
+ <Typography>You have unsaved changes. Would you like to save them before leaving?</Typography>
228
263
  </DialogFrame>
229
264
  </Modal>
230
265
  );
@@ -237,14 +272,7 @@ function UnsavedChangesDialog({ open, onClose, onDiscard, onSave }) {
237
272
  function InfoDialog({ open, onClose, title, message }) {
238
273
  return (
239
274
  <Modal open={open} onClose={onClose}>
240
- <DialogFrame
241
- title={title}
242
- actions={
243
- <Button onClick={onClose}>
244
- Got it
245
- </Button>
246
- }
247
- >
275
+ <DialogFrame title={title} actions={<Button onClick={onClose}>Got it</Button>}>
248
276
  <Typography>{message}</Typography>
249
277
  </DialogFrame>
250
278
  </Modal>
@@ -301,18 +329,8 @@ function ProcessingDialog({ open, status, onClose }) {
301
329
  return (
302
330
  <Modal open={open} onClose={isProcessing ? undefined : onClose}>
303
331
  <DialogFrame
304
- title={
305
- isProcessing ? 'Processing...' :
306
- isSuccess ? 'Success!' :
307
- 'Error'
308
- }
309
- actions={
310
- !isProcessing && (
311
- <Button onClick={onClose}>
312
- {isSuccess ? 'Done' : 'Try Again'}
313
- </Button>
314
- )
315
- }
332
+ title={isProcessing ? 'Processing...' : isSuccess ? 'Success!' : 'Error'}
333
+ actions={!isProcessing && <Button onClick={onClose}>{isSuccess ? 'Done' : 'Try Again'}</Button>}
316
334
  >
317
335
  <Box sx={{ textAlign: 'center', py: 2 }}>
318
336
  {isProcessing && <CircularProgress />}
@@ -457,9 +475,7 @@ DialogFrame should be wrapped in Modal for proper behavior:
457
475
  // Fullscreen for complex content or mobile
458
476
  <Modal open={open} onClose={onClose}>
459
477
  <DialogFrame fullscreen title="Edit Profile">
460
- <Box sx={{ p: 2 }}>
461
- {/* Large form or content */}
462
- </Box>
478
+ <Box sx={{ p: 2 }}>{/* Large form or content */}</Box>
463
479
  </DialogFrame>
464
480
  </Modal>
465
481
  ```
@@ -500,9 +516,7 @@ DialogFrame inherits accessibility features from Modal:
500
516
 
501
517
  ```tsx
502
518
  // Title provides context
503
- <DialogFrame title="Confirm Deletion">
504
- {/* Content is read after title */}
505
- </DialogFrame>
519
+ <DialogFrame title="Confirm Deletion">{/* Content is read after title */}</DialogFrame>
506
520
  ```
507
521
 
508
522
  ## Best Practices
@@ -530,9 +544,7 @@ DialogFrame inherits accessibility features from Modal:
530
544
  ```tsx
531
545
  // ✅ Good: Brief, scannable content
532
546
  <DialogFrame title="Delete Project">
533
- <Typography>
534
- This will permanently delete the project and all its data.
535
- </Typography>
547
+ <Typography>This will permanently delete the project and all its data.</Typography>
536
548
  </DialogFrame>
537
549
  ```
538
550
 
@@ -560,7 +572,7 @@ DialogFrame inherits accessibility features from Modal:
560
572
  // ❌ Bad: Dialog for simple feedback
561
573
  <DialogFrame title="Success">
562
574
  <Typography>Item saved!</Typography>
563
- </DialogFrame>
575
+ </DialogFrame>;
564
576
 
565
577
  // ✅ Good: Use Toast
566
578
  showToast({ message: 'Item saved!' });
@@ -570,9 +582,7 @@ showToast({ message: 'Item saved!' });
570
582
 
571
583
  ```tsx
572
584
  // ❌ Bad: Complex form in dialog
573
- <DialogFrame title="Create Account">
574
- {/* 20+ form fields */}
575
- </DialogFrame>
585
+ <DialogFrame title="Create Account">{/* 20+ form fields */}</DialogFrame>
576
586
  ```
577
587
 
578
588
  3. **Don't use vague button labels**: Be specific about actions
@@ -599,9 +609,7 @@ For dialogs with heavy content:
599
609
  function HeavyDialog({ open, onClose }) {
600
610
  return (
601
611
  <Modal open={open} onClose={onClose}>
602
- <DialogFrame title="Data Preview">
603
- {open && <HeavyDataComponent />}
604
- </DialogFrame>
612
+ <DialogFrame title="Data Preview">{open && <HeavyDataComponent />}</DialogFrame>
605
613
  </Modal>
606
614
  );
607
615
  }
@@ -625,11 +633,13 @@ const handleCancel = useCallback(() => {
625
633
  Unmount dialog completely when not needed:
626
634
 
627
635
  ```tsx
628
- {open && (
629
- <Modal open={open} onClose={onClose}>
630
- <DialogFrame>...</DialogFrame>
631
- </Modal>
632
- )}
636
+ {
637
+ open && (
638
+ <Modal open={open} onClose={onClose}>
639
+ <DialogFrame>...</DialogFrame>
640
+ </Modal>
641
+ );
642
+ }
633
643
  ```
634
644
 
635
645
  DialogFrame provides a consistent structure for dialog content. Combine it with Modal for proper overlay behavior, keep content concise and actionable, and always provide clear options for users to proceed or cancel.