@ceed/cds 1.22.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.
@@ -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.
@@ -4,8 +4,25 @@
4
4
 
5
5
  The Modal component is a dialog overlay that appears on top of the main content, demanding user attention and interaction. Built on Joy UI's Modal, it is used for displaying critical information, capturing user input, or requiring confirmation before proceeding. Modals block interaction with the underlying page until dismissed, making them ideal for important actions that require focused attention.
6
6
 
7
- ```
8
- <Canvas of={Modal.Playground} />
7
+ ```tsx
8
+ <>
9
+ <Button onClick={() => setOpen(true)}>Open Modal</Button>
10
+ <Modal open={open} onClose={() => setOpen(false)}>
11
+ <ModalDialog>
12
+ <ModalClose />
13
+ <DialogTitle>Modal Title</DialogTitle>
14
+ <DialogContent>This is the modal content. You can place any content here.</DialogContent>
15
+ <DialogActions>
16
+ <Button variant="solid" onClick={() => setOpen(false)}>
17
+ Confirm
18
+ </Button>
19
+ <Button variant="plain" color="neutral" onClick={() => setOpen(false)}>
20
+ Cancel
21
+ </Button>
22
+ </DialogActions>
23
+ </ModalDialog>
24
+ </Modal>
25
+ </>
9
26
  ```
10
27
 
11
28
  | Field | Description | Default |
@@ -24,15 +41,7 @@ The Modal component is a dialog overlay that appears on top of the main content,
24
41
  ## Usage
25
42
 
26
43
  ```tsx
27
- import {
28
- Modal,
29
- ModalDialog,
30
- ModalClose,
31
- DialogTitle,
32
- DialogContent,
33
- DialogActions,
34
- Button,
35
- } from '@ceed/cds';
44
+ import { Modal, ModalDialog, ModalClose, DialogTitle, DialogContent, DialogActions, Button } from '@ceed/cds';
36
45
 
37
46
  function MyComponent() {
38
47
  const [open, setOpen] = useState(false);
@@ -44,9 +53,7 @@ function MyComponent() {
44
53
  <ModalDialog>
45
54
  <ModalClose />
46
55
  <DialogTitle>Modal Title</DialogTitle>
47
- <DialogContent>
48
- Place your content here.
49
- </DialogContent>
56
+ <DialogContent>Place your content here.</DialogContent>
50
57
  <DialogActions>
51
58
  <Button onClick={() => setOpen(false)}>Close</Button>
52
59
  </DialogActions>
@@ -57,6 +64,28 @@ function MyComponent() {
57
64
  }
58
65
  ```
59
66
 
67
+ ### ModalFrame Usage
68
+
69
+ `ModalFrame` is a convenience component that combines `ModalDialog` + `ModalClose` + `DialogTitle` + `DialogContent` into a single composable unit.
70
+ It provides a concise way to build modals with a title, close button, and content area.
71
+
72
+ ```tsx
73
+ import { Modal, ModalFrame } from '@ceed/cds';
74
+
75
+ function DetailModal({ open, onClose }) {
76
+ return (
77
+ <Modal open={open} onClose={onClose}>
78
+ <ModalFrame title="Detail" onClose={onClose}>
79
+ Content goes here.
80
+ </ModalFrame>
81
+ </Modal>
82
+ );
83
+ }
84
+ ```
85
+
86
+ > **Note**: Connect the same handler to both `Modal`'s `onClose` and `ModalFrame`'s `onClose`.
87
+ > `Modal` handles backdrop click and ESC key, while `ModalFrame` handles the X button click.
88
+
60
89
  ## Examples
61
90
 
62
91
  ### Basic Modal
@@ -64,29 +93,32 @@ function MyComponent() {
64
93
  The basic modal with a simple Sheet for custom layouts.
65
94
 
66
95
  ```tsx
67
- <Modal aria-labelledby="modal-title" aria-describedby="modal-desc" open sx={{
68
- display: 'flex',
69
- justifyContent: 'center',
70
- alignItems: 'center'
71
- }}>
72
- <Sheet variant="outlined" sx={{
73
- maxWidth: 500,
74
- borderRadius: 'md',
75
- p: 3,
76
- boxShadow: 'lg'
77
- }}>
78
- <ModalClose variant="plain" sx={{
79
- m: 1
80
- }} />
81
- <Typography component="h2" id="modal-title" level="h4" textColor="inherit" fontWeight="lg" mb={2}>
82
- This is the modal title
83
- </Typography>
84
- <Typography id="modal-desc" textColor="text.tertiary">
85
- Make sure to use <code>aria-labelledby</code> on the modal dialog with an optional{' '}
86
- <code>aria-describedby</code> attribute.
87
- </Typography>
96
+ <>
97
+ <Button onClick={() => setOpen(true)}>Open Basic Modal</Button>
98
+ <Modal aria-labelledby="modal-title" aria-describedby="modal-desc" open={open} onClose={() => setOpen(false)} sx={{
99
+ display: 'flex',
100
+ justifyContent: 'center',
101
+ alignItems: 'center'
102
+ }}>
103
+ <Sheet variant="outlined" sx={{
104
+ maxWidth: 500,
105
+ borderRadius: 'md',
106
+ p: 3,
107
+ boxShadow: 'lg'
108
+ }}>
109
+ <ModalClose variant="plain" sx={{
110
+ m: 1
111
+ }} />
112
+ <Typography component="h2" id="modal-title" level="h4" textColor="inherit" fontWeight="lg" mb={2}>
113
+ This is the modal title
114
+ </Typography>
115
+ <Typography id="modal-desc" textColor="text.tertiary">
116
+ Make sure to use <code>aria-labelledby</code> on the modal dialog with an optional{' '}
117
+ <code>aria-describedby</code> attribute.
118
+ </Typography>
88
119
  </Sheet>
89
120
  </Modal>
121
+ </>
90
122
  ```
91
123
 
92
124
  ### Modal Dialog
@@ -94,29 +126,34 @@ The basic modal with a simple Sheet for custom layouts.
94
126
  Use ModalDialog for structured dialogs with title, content, and actions.
95
127
 
96
128
  ```tsx
97
- <Modal open>
98
- <ModalDialog>
99
- <DialogTitle>Create new project</DialogTitle>
100
- <DialogContent>
101
- Fill in the information of the project.
102
- <form onSubmit={(event: React.FormEvent<HTMLFormElement>) => {
103
- event.preventDefault();
104
- }}>
105
- <Stack spacing={4}>
106
- <FormControl>
107
- <FormLabel>Name</FormLabel>
108
- <Input required />
109
- </FormControl>
110
- <FormControl>
111
- <FormLabel>Description</FormLabel>
112
- <Input required />
113
- </FormControl>
114
- <Button type="submit">Submit</Button>
115
- </Stack>
116
- </form>
117
- </DialogContent>
118
- </ModalDialog>
129
+ <>
130
+ <Button onClick={() => setOpen(true)}>Open Form Modal</Button>
131
+ <Modal open={open} onClose={() => setOpen(false)}>
132
+ <ModalDialog>
133
+ <ModalClose />
134
+ <DialogTitle>Create new project</DialogTitle>
135
+ <DialogContent>
136
+ Fill in the information of the project.
137
+ <form onSubmit={(event: React.FormEvent<HTMLFormElement>) => {
138
+ event.preventDefault();
139
+ setOpen(false);
140
+ }}>
141
+ <Stack spacing={4}>
142
+ <FormControl>
143
+ <FormLabel>Name</FormLabel>
144
+ <Input required />
145
+ </FormControl>
146
+ <FormControl>
147
+ <FormLabel>Description</FormLabel>
148
+ <Input required />
149
+ </FormControl>
150
+ <Button type="submit">Submit</Button>
151
+ </Stack>
152
+ </form>
153
+ </DialogContent>
154
+ </ModalDialog>
119
155
  </Modal>
156
+ </>
120
157
  ```
121
158
 
122
159
  ### Variants
@@ -126,49 +163,61 @@ Modal dialogs support different visual variants.
126
163
  #### Plain Variant
127
164
 
128
165
  ```tsx
129
- <Modal open>
130
- <ModalDialog variant="plain">
131
- <ModalClose />
132
- <DialogTitle>Modal Dialog</DialogTitle>
133
- <DialogContent>This is a `plain` modal dialog.</DialogContent>
134
- </ModalDialog>
135
- </Modal>
166
+ <>
167
+ <Button onClick={() => setOpen(true)}>Plain Variant</Button>
168
+ <Modal open={open} onClose={() => setOpen(false)}>
169
+ <ModalDialog variant="plain">
170
+ <ModalClose />
171
+ <DialogTitle>Modal Dialog</DialogTitle>
172
+ <DialogContent>This is a `plain` modal dialog.</DialogContent>
173
+ </ModalDialog>
174
+ </Modal>
175
+ </>
136
176
  ```
137
177
 
138
178
  #### Outlined Variant
139
179
 
140
180
  ```tsx
141
- <Modal open>
142
- <ModalDialog variant="outlined">
143
- <ModalClose />
144
- <DialogTitle>Modal Dialog</DialogTitle>
145
- <DialogContent>This is an `outlined` modal dialog.</DialogContent>
146
- </ModalDialog>
147
- </Modal>
181
+ <>
182
+ <Button onClick={() => setOpen(true)}>Outlined Variant</Button>
183
+ <Modal open={open} onClose={() => setOpen(false)}>
184
+ <ModalDialog variant="outlined">
185
+ <ModalClose />
186
+ <DialogTitle>Modal Dialog</DialogTitle>
187
+ <DialogContent>This is an `outlined` modal dialog.</DialogContent>
188
+ </ModalDialog>
189
+ </Modal>
190
+ </>
148
191
  ```
149
192
 
150
193
  #### Soft Variant
151
194
 
152
195
  ```tsx
153
- <Modal open>
154
- <ModalDialog variant="soft">
155
- <ModalClose />
156
- <DialogTitle>Modal Dialog</DialogTitle>
157
- <DialogContent>This is a `soft` modal dialog.</DialogContent>
158
- </ModalDialog>
159
- </Modal>
196
+ <>
197
+ <Button onClick={() => setOpen(true)}>Soft Variant</Button>
198
+ <Modal open={open} onClose={() => setOpen(false)}>
199
+ <ModalDialog variant="soft">
200
+ <ModalClose />
201
+ <DialogTitle>Modal Dialog</DialogTitle>
202
+ <DialogContent>This is a `soft` modal dialog.</DialogContent>
203
+ </ModalDialog>
204
+ </Modal>
205
+ </>
160
206
  ```
161
207
 
162
208
  #### Solid Variant
163
209
 
164
210
  ```tsx
165
- <Modal open>
166
- <ModalDialog variant="solid">
167
- <ModalClose />
168
- <DialogTitle>Modal Dialog</DialogTitle>
169
- <DialogContent>This is a `solid` modal dialog.</DialogContent>
170
- </ModalDialog>
171
- </Modal>
211
+ <>
212
+ <Button onClick={() => setOpen(true)}>Solid Variant</Button>
213
+ <Modal open={open} onClose={() => setOpen(false)}>
214
+ <ModalDialog variant="solid">
215
+ <ModalClose />
216
+ <DialogTitle>Modal Dialog</DialogTitle>
217
+ <DialogContent>This is a `solid` modal dialog.</DialogContent>
218
+ </ModalDialog>
219
+ </Modal>
220
+ </>
172
221
  ```
173
222
 
174
223
  ### Alert Dialog
@@ -176,24 +225,29 @@ Modal dialogs support different visual variants.
176
225
  For critical confirmations that require explicit user decision.
177
226
 
178
227
  ```tsx
179
- <Modal open>
180
- <ModalDialog variant="outlined" role="alertdialog">
181
- <DialogTitle>
182
- <WarningRoundedIcon />
183
- Confirmation
184
- </DialogTitle>
185
- <Divider />
186
- <DialogContent>Are you sure you want to discard all of your notes?</DialogContent>
187
- <DialogActions>
188
- <Button variant="solid" color="danger">
189
- Discard notes
190
- </Button>
191
- <Button variant="plain" color="neutral">
192
- Cancel
193
- </Button>
194
- </DialogActions>
195
- </ModalDialog>
196
- </Modal>
228
+ <>
229
+ <Button color="danger" onClick={() => setOpen(true)}>
230
+ Delete Item
231
+ </Button>
232
+ <Modal open={open} onClose={() => setOpen(false)}>
233
+ <ModalDialog variant="outlined" role="alertdialog">
234
+ <DialogTitle>
235
+ <WarningRoundedIcon />
236
+ Confirmation
237
+ </DialogTitle>
238
+ <Divider />
239
+ <DialogContent>Are you sure you want to discard all of your notes?</DialogContent>
240
+ <DialogActions>
241
+ <Button variant="solid" color="danger" onClick={() => setOpen(false)}>
242
+ Discard notes
243
+ </Button>
244
+ <Button variant="plain" color="neutral" onClick={() => setOpen(false)}>
245
+ Cancel
246
+ </Button>
247
+ </DialogActions>
248
+ </ModalDialog>
249
+ </Modal>
250
+ </>
197
251
  ```
198
252
 
199
253
  ### Layouts
@@ -202,16 +256,207 @@ For critical confirmations that require explicit user decision.
202
256
 
203
257
  For complex content that requires maximum screen space.
204
258
 
205
- ```
206
- <Canvas of={Modal.FullscreenLayout} />
259
+ ```tsx
260
+ <>
261
+ <Button onClick={() => setOpen(true)}>Open Fullscreen Modal</Button>
262
+ <Modal open={open} onClose={() => setOpen(false)}>
263
+ <ModalDialog layout="fullscreen">
264
+ <ModalClose />
265
+ <DialogTitle>Fullscreen Modal</DialogTitle>
266
+ <DialogContent>This modal takes up the entire screen.</DialogContent>
267
+ <DialogActions>
268
+ <Button variant="solid" onClick={() => setOpen(false)}>
269
+ Save
270
+ </Button>
271
+ </DialogActions>
272
+ </ModalDialog>
273
+ </Modal>
274
+ </>
207
275
  ```
208
276
 
209
277
  ### Nested Modals
210
278
 
211
279
  Modals can be stacked on top of each other when necessary.
212
280
 
281
+ ```tsx
282
+ <>
283
+ <Button onClick={() => setFirstOpen(true)}>Open First Modal</Button>
284
+ <Modal open={firstOpen} onClose={() => setFirstOpen(false)}>
285
+ <ModalDialog>
286
+ <ModalClose />
287
+ <DialogTitle>First Modal</DialogTitle>
288
+ <DialogContent>This is the first modal. Click the button below to open a nested modal.</DialogContent>
289
+ <DialogActions>
290
+ <Button onClick={() => setSecondOpen(true)}>Open Nested Modal</Button>
291
+ </DialogActions>
292
+ </ModalDialog>
293
+ </Modal>
294
+ <Modal open={secondOpen} onClose={() => setSecondOpen(false)}>
295
+ <ModalDialog>
296
+ <ModalClose />
297
+ <DialogTitle>Nested Modal</DialogTitle>
298
+ <DialogContent>This is a nested modal on top of the first one.</DialogContent>
299
+ <DialogActions>
300
+ <Button onClick={() => setSecondOpen(false)}>Close</Button>
301
+ </DialogActions>
302
+ </ModalDialog>
303
+ </Modal>
304
+ </>
305
+ ```
306
+
307
+ ### ModalFrame
308
+
309
+ ModalFrame is a convenience component that automatically provides a title, close button, and content area.
310
+
311
+ #### ModalFrame Playground
312
+
313
+ ```tsx
314
+ <>
315
+ <Button onClick={() => setOpen(true)}>Open ModalFrame</Button>
316
+ <Modal open={open} onClose={() => setOpen(false)}>
317
+ <ModalFrame title="ModalFrame Title" onClose={() => setOpen(false)}>
318
+ <Typography>
319
+ ModalFrame automatically composes ModalDialog, ModalClose, DialogTitle, and DialogContent. You only need
320
+ to provide a title, onClose handler, and children.
321
+ </Typography>
322
+ </ModalFrame>
323
+ </Modal>
324
+ </>
213
325
  ```
214
- <Canvas of={Modal.NestedModals} />
326
+
327
+ #### titleStartDecorator
328
+
329
+ Display an icon or decorative element before the title.
330
+
331
+ ```tsx
332
+ <>
333
+ <Button onClick={() => setOpen(true)}>With Decorator</Button>
334
+ <Modal open={open} onClose={() => setOpen(false)}>
335
+ <ModalFrame title="Details" titleStartDecorator={<InfoOutlinedIcon />} onClose={() => setOpen(false)}>
336
+ <Typography>
337
+ Use the <code>titleStartDecorator</code> prop to display an icon or element before the title.
338
+ </Typography>
339
+ </ModalFrame>
340
+ </Modal>
341
+ </>
342
+ ```
343
+
344
+ #### Form Content
345
+
346
+ An inline form pattern where the submit button lives inside the content area.
347
+
348
+ ```tsx
349
+ <>
350
+ <Button onClick={() => setOpen(true)}>Form in ModalFrame</Button>
351
+ <Modal open={open} onClose={() => setOpen(false)}>
352
+ <ModalFrame title="Create Project" onClose={() => setOpen(false)}>
353
+ <form onSubmit={(event: React.FormEvent<HTMLFormElement>) => {
354
+ event.preventDefault();
355
+ setOpen(false);
356
+ }}>
357
+ <Stack spacing={2}>
358
+ <FormControl>
359
+ <FormLabel>Name</FormLabel>
360
+ <Input required />
361
+ </FormControl>
362
+ <FormControl>
363
+ <FormLabel>Description</FormLabel>
364
+ <Input required />
365
+ </FormControl>
366
+ <Button type="submit">Submit</Button>
367
+ </Stack>
368
+ </form>
369
+ </ModalFrame>
370
+ </Modal>
371
+ </>
372
+ ```
373
+
374
+ #### Sizes
375
+
376
+ Compare sm / md / lg sizes side by side.
377
+
378
+ ```tsx
379
+ <Stack direction="row" spacing={2}>
380
+ <Button size="sm" onClick={() => setOpenSm(true)}>
381
+ Small
382
+ </Button>
383
+ <Button size="md" onClick={() => setOpenMd(true)}>
384
+ Medium
385
+ </Button>
386
+ <Button size="lg" onClick={() => setOpenLg(true)}>
387
+ Large
388
+ </Button>
389
+ <Modal open={openSm} onClose={() => setOpenSm(false)}>
390
+ <ModalFrame title="Small ModalFrame" size="sm" onClose={() => setOpenSm(false)}>
391
+ <Typography>This is a small ModalFrame.</Typography>
392
+ </ModalFrame>
393
+ </Modal>
394
+ <Modal open={openMd} onClose={() => setOpenMd(false)}>
395
+ <ModalFrame title="Medium ModalFrame" size="md" onClose={() => setOpenMd(false)}>
396
+ <Typography>This is a medium ModalFrame.</Typography>
397
+ </ModalFrame>
398
+ </Modal>
399
+ <Modal open={openLg} onClose={() => setOpenLg(false)}>
400
+ <ModalFrame title="Large ModalFrame" size="lg" onClose={() => setOpenLg(false)}>
401
+ <Typography>This is a large ModalFrame.</Typography>
402
+ </ModalFrame>
403
+ </Modal>
404
+ </Stack>
405
+ ```
406
+
407
+ #### Custom Content
408
+
409
+ A layout example suited for displaying detailed information.
410
+
411
+ ```tsx
412
+ <>
413
+ <Button onClick={() => setOpen(true)}>Custom Content</Button>
414
+ <Modal open={open} onClose={() => setOpen(false)}>
415
+ <ModalFrame title="Order Details" onClose={() => setOpen(false)}>
416
+ <Stack spacing={2}>
417
+ <Box>
418
+ <Typography level="title-sm">Order ID</Typography>
419
+ <Typography level="body-sm">ORD-2024-00123</Typography>
420
+ </Box>
421
+ <Divider />
422
+ <Box>
423
+ <Typography level="title-sm">Customer</Typography>
424
+ <Typography level="body-sm">John Doe</Typography>
425
+ </Box>
426
+ <Divider />
427
+ <Box>
428
+ <Typography level="title-sm">Status</Typography>
429
+ <Typography level="body-sm" color="success">
430
+ Completed
431
+ </Typography>
432
+ </Box>
433
+ </Stack>
434
+ </ModalFrame>
435
+ </Modal>
436
+ </>
437
+ ```
438
+
439
+ #### Standalone
440
+
441
+ ModalFrame can be used without a Modal wrapper for embedding dialog-style layouts directly within a page.
442
+
443
+ > ⚠️ **Important** ⚠️
444
+ >
445
+ > When using ModalFrame without Modal, the parent container **must** provide explicit `width` and `height` values.
446
+ > ModalFrame inherits its dimensions from `ModalDialog`, which normally receives sizing from the Modal overlay.
447
+ > Without these constraints, the component will not render with correct dimensions.
448
+
449
+ ```tsx
450
+ <Box sx={{
451
+ width: 480,
452
+ height: 300
453
+ }}>
454
+ <ModalFrame title="Standalone ModalFrame" onClose={() => console.log('close')}>
455
+ <Typography>
456
+ ModalFrame used without Modal. The parent container must provide explicit width and height.
457
+ </Typography>
458
+ </ModalFrame>
459
+ </Box>
215
460
  ```
216
461
 
217
462
  ## When to Use
@@ -249,8 +494,7 @@ function DeleteConfirmation({ item, onDelete, onCancel }) {
249
494
  </DialogTitle>
250
495
  <Divider />
251
496
  <DialogContent>
252
- This action cannot be undone. All data associated with this item
253
- will be permanently removed.
497
+ This action cannot be undone. All data associated with this item will be permanently removed.
254
498
  </DialogContent>
255
499
  <DialogActions>
256
500
  <Button variant="solid" color="danger" onClick={onDelete}>
@@ -326,8 +570,7 @@ function TermsModal({ open, onAccept, onDecline }) {
326
570
  <DialogTitle>Terms of Service</DialogTitle>
327
571
  <DialogContent>
328
572
  <Typography level="body-sm">
329
- Please read and accept the following terms and conditions before
330
- proceeding...
573
+ Please read and accept the following terms and conditions before proceeding...
331
574
  </Typography>
332
575
  {/* Terms content */}
333
576
  </DialogContent>
@@ -355,11 +598,7 @@ function ImagePreviewModal({ image, open, onClose }) {
355
598
  <Modal open={open} onClose={onClose}>
356
599
  <ModalDialog layout="center" sx={{ p: 0, overflow: 'hidden' }}>
357
600
  <ModalClose sx={{ top: 8, right: 8, zIndex: 1 }} />
358
- <img
359
- src={image.src}
360
- alt={image.alt}
361
- style={{ maxWidth: '90vw', maxHeight: '90vh', objectFit: 'contain' }}
362
- />
601
+ <img src={image.src} alt={image.alt} style={{ maxWidth: '90vw', maxHeight: '90vh', objectFit: 'contain' }} />
363
602
  </ModalDialog>
364
603
  </Modal>
365
604
  );
@@ -390,22 +629,69 @@ function LoadingModal({ open, message }) {
390
629
  Modal uses a composition pattern with multiple sub-components:
391
630
 
392
631
  ```tsx
393
- <Modal> {/* Overlay and backdrop */}
394
- <ModalDialog> {/* Dialog container */}
395
- <ModalClose /> {/* Close button (optional) */}
396
- <DialogTitle> {/* Header */}
632
+ <Modal>
633
+ {/* Overlay and backdrop */}
634
+ <ModalDialog>
635
+ {/* Dialog container */}
636
+ <ModalClose /> {/* Close button (optional) */}
637
+ <DialogTitle>
638
+ {/* Header */}
397
639
  Title
398
640
  </DialogTitle>
399
- <DialogContent> {/* Body */}
641
+ <DialogContent>
642
+ {/* Body */}
400
643
  Content goes here
401
644
  </DialogContent>
402
- <DialogActions> {/* Footer */}
645
+ <DialogActions>
646
+ {/* Footer */}
403
647
  <Button>Action</Button>
404
648
  </DialogActions>
405
649
  </ModalDialog>
406
650
  </Modal>
407
651
  ```
408
652
 
653
+ ## Component Roles
654
+
655
+ | Component | Role | When to Use |
656
+ | ----------------- | --------------------------------------------------------------- | ---------------------------------------------------------------- |
657
+ | **Modal** | Overlay backdrop, open/close state management | Always required as the outermost wrapper |
658
+ | **ModalDialog** | Dialog container (variant/size/layout) | When you need direct control over layout |
659
+ | **ModalClose** | Close (X) button in the top-right corner | When users should be able to close via a button |
660
+ | **ModalOverflow** | Scrollable area | When content exceeds the viewport |
661
+ | **ModalFrame** | Combines ModalDialog + ModalClose + DialogTitle + DialogContent | When you only need a title + close + content (no action buttons) |
662
+ | **DialogTitle** | Header area (styled padding) | When composing manually |
663
+ | **DialogContent** | Body area (styled padding) | When composing manually |
664
+ | **DialogActions** | Footer action button area | When confirm/cancel buttons are needed |
665
+
666
+ ## Choosing the Right Component
667
+
668
+ ### ModalFrame vs DialogFrame
669
+
670
+ | | ModalFrame | DialogFrame |
671
+ | ------------------ | ----------------------------------------------- | --------------------------------------- |
672
+ | Close (X) button | Built-in | None |
673
+ | Title decorator | `titleStartDecorator` | None |
674
+ | Action button area | None | `actions` prop (required) |
675
+ | Fullscreen | `layout="fullscreen"` | `fullscreen` prop |
676
+ | Best for | Information display, detail views, inline forms | Confirm/cancel dialogs, decision-making |
677
+
678
+ ### Use ModalFrame when
679
+
680
+ - You need an informational modal with a close button (detail views, previews)
681
+ - The form's submit button lives inside the content area
682
+ - You need an icon next to the title
683
+
684
+ ### Use DialogFrame when
685
+
686
+ - Explicit action buttons (confirm/cancel) must be pinned to the bottom
687
+ - User decisions are required (delete confirmation, save confirmation)
688
+ - Only explicit choices should be allowed without a close (X) button
689
+
690
+ ### Use manual composition when
691
+
692
+ - You need a custom layout that doesn't fit the ModalFrame/DialogFrame pattern
693
+ - You want to use both ModalClose and DialogActions together
694
+
409
695
  ## Props and Customization
410
696
 
411
697
  ### Modal Props
@@ -427,6 +713,17 @@ Modal uses a composition pattern with multiple sub-components:
427
713
  | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Dialog size |
428
714
  | `layout` | `'center' \| 'fullscreen'` | `'center'` | Layout mode |
429
715
 
716
+ ### ModalFrame Props
717
+
718
+ | Prop | Type | Default | Description |
719
+ | --------------------- | ------------ | ------- | ------------------------------------------ |
720
+ | `title` | `ReactNode` | - | Title displayed in the header |
721
+ | `children` | `ReactNode` | - | Body content |
722
+ | `titleStartDecorator` | `ReactNode` | - | Icon or element displayed before the title |
723
+ | `onClose` | `() => void` | - | Callback when the close button is clicked |
724
+
725
+ ModalFrame accepts all ModalDialog props (`variant`, `color`, `size`, `layout`, `sx`, etc.).
726
+
430
727
  ### Custom Styling
431
728
 
432
729
  ```tsx
@@ -465,17 +762,10 @@ Modal components include comprehensive accessibility features:
465
762
  - `aria-describedby` connects to DialogContent
466
763
 
467
764
  ```tsx
468
- <Modal
469
- open={open}
470
- onClose={onClose}
471
- aria-labelledby="modal-title"
472
- aria-describedby="modal-description"
473
- >
765
+ <Modal open={open} onClose={onClose} aria-labelledby="modal-title" aria-describedby="modal-description">
474
766
  <ModalDialog>
475
767
  <DialogTitle id="modal-title">Accessible Title</DialogTitle>
476
- <DialogContent id="modal-description">
477
- This content is read by screen readers.
478
- </DialogContent>
768
+ <DialogContent id="modal-description">This content is read by screen readers.</DialogContent>
479
769
  </ModalDialog>
480
770
  </Modal>
481
771
  ```
@@ -504,7 +794,9 @@ Modal components include comprehensive accessibility features:
504
794
  ```tsx
505
795
  // ✅ Good: Clear action buttons
506
796
  <DialogActions>
507
- <Button variant="solid" color="danger">Delete</Button>
797
+ <Button variant="solid" color="danger">
798
+ Delete
799
+ </Button>
508
800
  <Button variant="plain">Cancel</Button>
509
801
  </DialogActions>
510
802
  ```
@@ -579,15 +871,11 @@ Use `keepMounted` only when the modal needs to preserve state between openings:
579
871
  Memoize modal content when it depends on complex data:
580
872
 
581
873
  ```tsx
582
- const modalContent = useMemo(() => (
583
- <ComplexContent data={data} />
584
- ), [data]);
874
+ const modalContent = useMemo(() => <ComplexContent data={data} />, [data]);
585
875
 
586
876
  <Modal open={open} onClose={onClose}>
587
- <ModalDialog>
588
- {modalContent}
589
- </ModalDialog>
590
- </Modal>
877
+ <ModalDialog>{modalContent}</ModalDialog>
878
+ </Modal>;
591
879
  ```
592
880
 
593
881
  Modal is a powerful component for focused user interactions. Use it thoughtfully to maintain a smooth user experience while capturing important decisions and inputs.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ceed/cds",
3
- "version": "1.22.0",
3
+ "version": "1.22.1",
4
4
  "main": "dist/index.cjs",
5
5
  "module": "dist/index.js",
6
6
  "types": "dist/index.d.ts",