@delightui/components 0.1.105 → 0.1.107
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.
- package/README.md +104 -1
- package/dist/cjs/components/molecules/Modal/DemoModal.d.ts +8 -0
- package/dist/cjs/components/molecules/Modal/ModalContext/ModalContext.d.ts +41 -0
- package/dist/cjs/components/molecules/Modal/ModalContext/ModalContext.types.d.ts +87 -0
- package/dist/cjs/components/molecules/Modal/ModalContext/index.d.ts +3 -0
- package/dist/cjs/components/molecules/Modal/ModalContext/useModal.d.ts +34 -0
- package/dist/cjs/components/molecules/Modal/index.d.ts +2 -0
- package/dist/cjs/components/molecules/index.d.ts +2 -0
- package/dist/cjs/library.css +19 -6
- package/dist/cjs/library.js +3 -3
- package/dist/cjs/library.js.map +1 -1
- package/dist/esm/components/molecules/Modal/DemoModal.d.ts +8 -0
- package/dist/esm/components/molecules/Modal/ModalContext/ModalContext.d.ts +41 -0
- package/dist/esm/components/molecules/Modal/ModalContext/ModalContext.types.d.ts +87 -0
- package/dist/esm/components/molecules/Modal/ModalContext/index.d.ts +3 -0
- package/dist/esm/components/molecules/Modal/ModalContext/useModal.d.ts +34 -0
- package/dist/esm/components/molecules/Modal/index.d.ts +2 -0
- package/dist/esm/components/molecules/index.d.ts +2 -0
- package/dist/esm/library.css +19 -6
- package/dist/esm/library.js +3 -3
- package/dist/esm/library.js.map +1 -1
- package/dist/index.d.ts +108 -2
- package/docs/README.md +264 -0
- package/docs/components/atoms/ActionImage.md +119 -0
- package/docs/components/atoms/Button.md +197 -0
- package/docs/components/atoms/Checkbox.md +299 -0
- package/docs/components/atoms/CheckboxItem.md +314 -0
- package/docs/components/atoms/Chip.md +380 -0
- package/docs/components/atoms/CustomToggle.md +270 -0
- package/docs/components/atoms/Icon.md +365 -0
- package/docs/components/atoms/IconButton.md +407 -0
- package/docs/components/atoms/Image.md +448 -0
- package/docs/components/atoms/Input.md +430 -0
- package/docs/components/atoms/ListItem.md +502 -0
- package/docs/components/atoms/Password.md +472 -0
- package/docs/components/atoms/RadioButton.md +614 -0
- package/docs/components/atoms/RadioButtonItem.md +588 -0
- package/docs/components/atoms/ResponsiveComponent.md +612 -0
- package/docs/components/atoms/SelectListItem.md +609 -0
- package/docs/components/atoms/Slider.md +605 -0
- package/docs/components/atoms/Spinner.md +605 -0
- package/docs/components/atoms/Text.md +463 -0
- package/docs/components/atoms/TextArea.md +670 -0
- package/docs/components/atoms/ToastNotification.md +668 -0
- package/docs/components/atoms/Toggle.md +737 -0
- package/docs/components/atoms/ToggleButton.md +751 -0
- package/docs/components/atoms/Tooltip.md +391 -0
- package/docs/components/molecules/Accordion.md +440 -0
- package/docs/components/molecules/AccordionGroup.md +547 -0
- package/docs/components/molecules/ActionCard.md +546 -0
- package/docs/components/molecules/Breadcrumb.md +403 -0
- package/docs/components/molecules/Breadcrumbs.md +485 -0
- package/docs/components/molecules/ButtonGroup.md +383 -0
- package/docs/components/molecules/Card.md +298 -0
- package/docs/components/molecules/ChipInput.md +646 -0
- package/docs/components/molecules/ContextMenu.md +768 -0
- package/docs/components/molecules/CustomTimeSelector.md +116 -0
- package/docs/components/molecules/DatePicker.md +516 -0
- package/docs/components/molecules/DateTimeSelector.md +166 -0
- package/docs/components/molecules/FormField.md +312 -0
- package/docs/components/molecules/Grid.md +577 -0
- package/docs/components/molecules/GridItem.md +834 -0
- package/docs/components/molecules/GridList.md +244 -0
- package/docs/components/molecules/List.md +485 -0
- package/docs/components/molecules/Modal.md +470 -0
- package/docs/components/molecules/ModalFooter.md +702 -0
- package/docs/components/molecules/ModalHeader.md +756 -0
- package/docs/components/molecules/ModalProvider.md +205 -0
- package/docs/components/molecules/Nav.md +530 -0
- package/docs/components/molecules/NavItem.md +572 -0
- package/docs/components/molecules/NavLink.md +499 -0
- package/docs/components/molecules/Option.md +521 -0
- package/docs/components/molecules/Pagination.md +592 -0
- package/docs/components/molecules/PaginationNumberField.md +722 -0
- package/docs/components/molecules/Popover.md +516 -0
- package/docs/components/molecules/ProgressBar.md +624 -0
- package/docs/components/molecules/RadioGroup.md +831 -0
- package/docs/components/molecules/RepeaterList.md +185 -0
- package/docs/components/molecules/Select.md +402 -0
- package/docs/components/molecules/SortableTrigger.md +82 -0
- package/docs/components/molecules/useModal.md +379 -0
- package/docs/components/organisms/Dropzone.md +346 -0
- package/docs/components/organisms/DropzoneClear.md +135 -0
- package/docs/components/organisms/DropzoneContent.md +216 -0
- package/docs/components/organisms/DropzoneFilename.md +191 -0
- package/docs/components/organisms/DropzoneSupportedFormats.md +184 -0
- package/docs/components/organisms/DropzoneTrigger.md +209 -0
- package/docs/components/organisms/Form.md +533 -0
- package/docs/components/organisms/SlideOutPanel.md +662 -0
- package/docs/components/organisms/TabContent.md +902 -0
- package/docs/components/organisms/TabItem.md +1091 -0
- package/docs/components/organisms/Table.md +611 -0
- package/docs/components/organisms/TableBody.md +679 -0
- package/docs/components/organisms/TableCell.md +482 -0
- package/docs/components/organisms/TableHeader.md +513 -0
- package/docs/components/organisms/TableHeaderCell.md +661 -0
- package/docs/components/organisms/TableRow.md +715 -0
- package/docs/components/organisms/Tabs.md +1330 -0
- package/docs/components/utils/ConditionalView.md +568 -0
- package/docs/components/utils/RenderStateView.md +726 -0
- package/docs/components/utils/WrapTextNodes.md +614 -0
- package/package.json +3 -2
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
# useModal
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
A React hook that provides programmatic modal management with automatic ID generation and simplified modal state handling. The useModal hook takes a modal component as a parameter and returns functions to open and close that specific modal, along with automatic unique identifier generation for each modal instance.
|
|
6
|
+
|
|
7
|
+
## Aliases
|
|
8
|
+
|
|
9
|
+
- useModal
|
|
10
|
+
- Modal Hook
|
|
11
|
+
- Modal Manager
|
|
12
|
+
|
|
13
|
+
## Hook Signature
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
function useModal<T extends ModalComponentProps>(
|
|
17
|
+
Component: React.ComponentType<T>
|
|
18
|
+
): UseModalReturn<T>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Parameters
|
|
22
|
+
|
|
23
|
+
| Parameter | Type | Required | Description |
|
|
24
|
+
|-----------|------|----------|-------------|
|
|
25
|
+
| `Component` | `React.ComponentType<T>` | Yes | Modal component that extends ModalComponentProps |
|
|
26
|
+
|
|
27
|
+
## Return Value
|
|
28
|
+
|
|
29
|
+
Returns an object with the following properties:
|
|
30
|
+
|
|
31
|
+
| Property | Type | Description |
|
|
32
|
+
|----------|------|-------------|
|
|
33
|
+
| `openModal` | `(props: T) => void` | Function to open the modal with specified props |
|
|
34
|
+
| `closeModal` | `() => void` | Function to close the modal instance |
|
|
35
|
+
| `modalId` | `string` | Unique identifier for this modal instance |
|
|
36
|
+
|
|
37
|
+
## Requirements
|
|
38
|
+
|
|
39
|
+
### ModalComponentProps Interface
|
|
40
|
+
|
|
41
|
+
Modal components used with `useModal` must extend the `ModalComponentProps` interface:
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
interface ModalComponentProps {
|
|
45
|
+
show?: boolean;
|
|
46
|
+
onCancel?: () => void;
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The hook automatically provides these props:
|
|
51
|
+
- `show`: Set to `true` when modal is opened
|
|
52
|
+
- `onCancel`: Function that closes the modal and calls the original `onCancel` if provided
|
|
53
|
+
|
|
54
|
+
## Examples
|
|
55
|
+
|
|
56
|
+
### Basic Usage
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
import React from 'react';
|
|
60
|
+
import { Modal, Button, Text, useModal } from '@delightui/components';
|
|
61
|
+
|
|
62
|
+
// Define your modal component
|
|
63
|
+
const AlertModal = ({ show, onCancel, title, message }) => (
|
|
64
|
+
<Modal show={show} onHide={onCancel}>
|
|
65
|
+
<Text type="Heading4">{title}</Text>
|
|
66
|
+
<Text>{message}</Text>
|
|
67
|
+
</Modal>
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
function MyComponent() {
|
|
71
|
+
const alertModal = useModal(AlertModal);
|
|
72
|
+
|
|
73
|
+
const showAlert = () => {
|
|
74
|
+
alertModal.openModal({
|
|
75
|
+
title: 'Success!',
|
|
76
|
+
message: 'Your action was completed successfully.'
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<Button onClick={showAlert}>
|
|
82
|
+
Show Alert
|
|
83
|
+
</Button>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Confirmation Modal
|
|
89
|
+
|
|
90
|
+
```tsx
|
|
91
|
+
import { Modal, Button, Text, useModal } from '@delightui/components';
|
|
92
|
+
|
|
93
|
+
const ConfirmationModal = ({ show, onCancel, title, message, onConfirm }) => (
|
|
94
|
+
<Modal
|
|
95
|
+
show={show}
|
|
96
|
+
onHide={onCancel}
|
|
97
|
+
footer={
|
|
98
|
+
<div style={{ display: 'flex', gap: '8px' }}>
|
|
99
|
+
<Button type="Outlined" onClick={onCancel}>Cancel</Button>
|
|
100
|
+
<Button style="Primary" onClick={onConfirm}>Confirm</Button>
|
|
101
|
+
</div>
|
|
102
|
+
}
|
|
103
|
+
>
|
|
104
|
+
<Text type="Heading4">{title}</Text>
|
|
105
|
+
<Text>{message}</Text>
|
|
106
|
+
</Modal>
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
function DeleteButton() {
|
|
110
|
+
const confirmModal = useModal(ConfirmationModal);
|
|
111
|
+
|
|
112
|
+
const handleDelete = () => {
|
|
113
|
+
confirmModal.openModal({
|
|
114
|
+
title: 'Delete Item',
|
|
115
|
+
message: 'Are you sure you want to delete this item? This action cannot be undone.',
|
|
116
|
+
onConfirm: () => {
|
|
117
|
+
// Perform delete action
|
|
118
|
+
console.log('Item deleted');
|
|
119
|
+
confirmModal.closeModal();
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<Button style="Destructive" onClick={handleDelete}>
|
|
126
|
+
Delete Item
|
|
127
|
+
</Button>
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Form Modal
|
|
133
|
+
|
|
134
|
+
```tsx
|
|
135
|
+
import { Modal, Form, FormField, Input, Button, useModal } from '@delightui/components';
|
|
136
|
+
|
|
137
|
+
const UserFormModal = ({ show, onCancel, onSubmit, initialData }) => {
|
|
138
|
+
const handleSubmit = (formData) => {
|
|
139
|
+
onSubmit(formData);
|
|
140
|
+
onCancel(); // Close modal after submission
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
<Modal
|
|
145
|
+
show={show}
|
|
146
|
+
onHide={onCancel}
|
|
147
|
+
size="Medium"
|
|
148
|
+
header={<Text type="Heading4">Edit User</Text>}
|
|
149
|
+
>
|
|
150
|
+
<Form onSubmit={handleSubmit} initialValues={initialData}>
|
|
151
|
+
<FormField name="name" label="Name" required>
|
|
152
|
+
<Input placeholder="Enter name" />
|
|
153
|
+
</FormField>
|
|
154
|
+
|
|
155
|
+
<FormField name="email" label="Email" required>
|
|
156
|
+
<Input type="email" placeholder="Enter email" />
|
|
157
|
+
</FormField>
|
|
158
|
+
|
|
159
|
+
<div style={{ display: 'flex', gap: '8px', marginTop: '16px' }}>
|
|
160
|
+
<Button type="Outlined" onClick={onCancel}>
|
|
161
|
+
Cancel
|
|
162
|
+
</Button>
|
|
163
|
+
<Button actionType="submit">
|
|
164
|
+
Save Changes
|
|
165
|
+
</Button>
|
|
166
|
+
</div>
|
|
167
|
+
</Form>
|
|
168
|
+
</Modal>
|
|
169
|
+
);
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
function UserList() {
|
|
173
|
+
const userModal = useModal(UserFormModal);
|
|
174
|
+
|
|
175
|
+
const editUser = (user) => {
|
|
176
|
+
userModal.openModal({
|
|
177
|
+
initialData: user,
|
|
178
|
+
onSubmit: (formData) => {
|
|
179
|
+
console.log('User updated:', formData);
|
|
180
|
+
// Handle form submission
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
return (
|
|
186
|
+
<Button onClick={() => editUser({ name: 'John', email: 'john@example.com' })}>
|
|
187
|
+
Edit User
|
|
188
|
+
</Button>
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Multiple Modal Types
|
|
194
|
+
|
|
195
|
+
```tsx
|
|
196
|
+
import { useModal } from '@delightui/components';
|
|
197
|
+
|
|
198
|
+
function Dashboard() {
|
|
199
|
+
const alertModal = useModal(AlertModal);
|
|
200
|
+
const confirmModal = useModal(ConfirmationModal);
|
|
201
|
+
const formModal = useModal(UserFormModal);
|
|
202
|
+
|
|
203
|
+
return (
|
|
204
|
+
<div>
|
|
205
|
+
<Button onClick={() => alertModal.openModal({
|
|
206
|
+
title: 'Info',
|
|
207
|
+
message: 'This is an information message.'
|
|
208
|
+
})}>
|
|
209
|
+
Show Info
|
|
210
|
+
</Button>
|
|
211
|
+
|
|
212
|
+
<Button onClick={() => confirmModal.openModal({
|
|
213
|
+
title: 'Confirm Action',
|
|
214
|
+
message: 'Are you sure?',
|
|
215
|
+
onConfirm: () => console.log('Confirmed')
|
|
216
|
+
})}>
|
|
217
|
+
Confirm Action
|
|
218
|
+
</Button>
|
|
219
|
+
|
|
220
|
+
<Button onClick={() => formModal.openModal({
|
|
221
|
+
onSubmit: (data) => console.log('Form data:', data)
|
|
222
|
+
})}>
|
|
223
|
+
Open Form
|
|
224
|
+
</Button>
|
|
225
|
+
</div>
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Custom Modal with Complex State
|
|
231
|
+
|
|
232
|
+
```tsx
|
|
233
|
+
const SettingsModal = ({ show, onCancel, settings, onSave }) => {
|
|
234
|
+
const [localSettings, setLocalSettings] = useState(settings);
|
|
235
|
+
|
|
236
|
+
useEffect(() => {
|
|
237
|
+
setLocalSettings(settings);
|
|
238
|
+
}, [settings]);
|
|
239
|
+
|
|
240
|
+
const handleSave = () => {
|
|
241
|
+
onSave(localSettings);
|
|
242
|
+
onCancel();
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
return (
|
|
246
|
+
<Modal
|
|
247
|
+
show={show}
|
|
248
|
+
onHide={onCancel}
|
|
249
|
+
size="Large"
|
|
250
|
+
header={<Text type="Heading3">Settings</Text>}
|
|
251
|
+
footer={
|
|
252
|
+
<div>
|
|
253
|
+
<Button type="Outlined" onClick={onCancel}>Cancel</Button>
|
|
254
|
+
<Button onClick={handleSave}>Save Settings</Button>
|
|
255
|
+
</div>
|
|
256
|
+
}
|
|
257
|
+
>
|
|
258
|
+
{/* Settings form content */}
|
|
259
|
+
<div>
|
|
260
|
+
<h4>Notification Settings</h4>
|
|
261
|
+
<label>
|
|
262
|
+
<input
|
|
263
|
+
type="checkbox"
|
|
264
|
+
checked={localSettings.notifications}
|
|
265
|
+
onChange={(e) => setLocalSettings(prev => ({
|
|
266
|
+
...prev,
|
|
267
|
+
notifications: e.target.checked
|
|
268
|
+
}))}
|
|
269
|
+
/>
|
|
270
|
+
Enable notifications
|
|
271
|
+
</label>
|
|
272
|
+
</div>
|
|
273
|
+
</Modal>
|
|
274
|
+
);
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
function SettingsPage() {
|
|
278
|
+
const settingsModal = useModal(SettingsModal);
|
|
279
|
+
const [userSettings, setUserSettings] = useState({
|
|
280
|
+
notifications: true,
|
|
281
|
+
theme: 'light'
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
const openSettings = () => {
|
|
285
|
+
settingsModal.openModal({
|
|
286
|
+
settings: userSettings,
|
|
287
|
+
onSave: (newSettings) => {
|
|
288
|
+
setUserSettings(newSettings);
|
|
289
|
+
console.log('Settings saved:', newSettings);
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
return (
|
|
295
|
+
<Button onClick={openSettings}>
|
|
296
|
+
Open Settings
|
|
297
|
+
</Button>
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## TypeScript Support
|
|
303
|
+
|
|
304
|
+
The hook is fully typed and provides excellent TypeScript support:
|
|
305
|
+
|
|
306
|
+
```tsx
|
|
307
|
+
interface MyModalProps extends ModalComponentProps {
|
|
308
|
+
title: string;
|
|
309
|
+
count: number;
|
|
310
|
+
onAction: (value: string) => void;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const MyModal: React.FC<MyModalProps> = ({ show, onCancel, title, count, onAction }) => {
|
|
314
|
+
// Modal implementation
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
function MyComponent() {
|
|
318
|
+
const modal = useModal(MyModal);
|
|
319
|
+
|
|
320
|
+
// TypeScript will enforce correct prop types
|
|
321
|
+
modal.openModal({
|
|
322
|
+
title: "Required string", // ✅ Required
|
|
323
|
+
count: 42, // ✅ Required number
|
|
324
|
+
onAction: (val) => {...} // ✅ Required function
|
|
325
|
+
// Missing any required prop will cause TypeScript error
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
## Best Practices
|
|
331
|
+
|
|
332
|
+
1. **Create reusable modal components** that extend `ModalComponentProps`
|
|
333
|
+
2. **Use one hook per modal type** for better organization
|
|
334
|
+
3. **Handle async operations** within modal components or callbacks
|
|
335
|
+
4. **Always provide meaningful prop types** for better TypeScript support
|
|
336
|
+
5. **Keep modal logic separate** from business logic for better testability
|
|
337
|
+
|
|
338
|
+
## Common Patterns
|
|
339
|
+
|
|
340
|
+
### Loading States
|
|
341
|
+
|
|
342
|
+
```tsx
|
|
343
|
+
const LoadingModal = ({ show, onCancel, isLoading, message }) => (
|
|
344
|
+
<Modal show={show} onHide={!isLoading ? onCancel : undefined}>
|
|
345
|
+
{isLoading ? (
|
|
346
|
+
<div>
|
|
347
|
+
<Spinner />
|
|
348
|
+
<Text>Loading...</Text>
|
|
349
|
+
</div>
|
|
350
|
+
) : (
|
|
351
|
+
<Text>{message}</Text>
|
|
352
|
+
)}
|
|
353
|
+
</Modal>
|
|
354
|
+
);
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Conditional Modal Content
|
|
358
|
+
|
|
359
|
+
```tsx
|
|
360
|
+
const DynamicModal = ({ show, onCancel, mode, data }) => (
|
|
361
|
+
<Modal show={show} onHide={onCancel}>
|
|
362
|
+
{mode === 'view' && <ViewContent data={data} />}
|
|
363
|
+
{mode === 'edit' && <EditContent data={data} />}
|
|
364
|
+
{mode === 'delete' && <DeleteConfirmation data={data} />}
|
|
365
|
+
</Modal>
|
|
366
|
+
);
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
## Requirements
|
|
370
|
+
|
|
371
|
+
- Must be used within a `ModalProvider`
|
|
372
|
+
- Modal components must extend `ModalComponentProps`
|
|
373
|
+
- Requires React 18+ for `useId` hook support
|
|
374
|
+
|
|
375
|
+
## Related
|
|
376
|
+
|
|
377
|
+
- **[ModalProvider](./ModalProvider.md)** - Context provider for modal state
|
|
378
|
+
- **[Modal](./Modal.md)** - Base modal component
|
|
379
|
+
- **[ModalComponentProps](./Modal.md#modalcomponentprops)** - Required interface for modal components
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
# Dropzone
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
A comprehensive file upload component with drag-and-drop functionality. The Dropzone component provides an intuitive interface for users to upload files either by dragging and dropping them onto the designated area or by clicking to open a file dialog. It supports file type restrictions, size limits, and multiple file uploads with customizable states for empty, loading, and uploaded scenarios.
|
|
6
|
+
|
|
7
|
+
## Aliases
|
|
8
|
+
|
|
9
|
+
- Dropzone
|
|
10
|
+
- File Upload
|
|
11
|
+
- File Drop
|
|
12
|
+
- Upload Area
|
|
13
|
+
|
|
14
|
+
## Props Breakdown
|
|
15
|
+
|
|
16
|
+
**Extends:** `ControlledFormComponentProps<File[]>`
|
|
17
|
+
|
|
18
|
+
| Prop | Type | Default | Required | Description |
|
|
19
|
+
|------|------|---------|----------|-------------|
|
|
20
|
+
| `empty` | `ReactNode` | - | No | Content to display when the dropzone is empty |
|
|
21
|
+
| `loading` | `ReactNode` | - | No | Content to display during file upload/processing |
|
|
22
|
+
| `uploaded` | `ReactNode` | - | No | Content to display when files are uploaded |
|
|
23
|
+
| `accept` | `{ [key: string]: readonly string[] }` | - | No | Accepted file types based on mime type |
|
|
24
|
+
| `maxSize` | `number` | - | No | Maximum file size in bytes |
|
|
25
|
+
| `maxFiles` | `number` | `1` | No | Maximum number of files to upload |
|
|
26
|
+
| `onFilesReset` | `() => void` | - | No | Callback when files are reset |
|
|
27
|
+
| `className` | `string` | - | No | Additional CSS class names |
|
|
28
|
+
| `initialValue` | `File[]` | - | No | Initial files value |
|
|
29
|
+
| `value` | `File[]` | - | No | Current files value |
|
|
30
|
+
| `onValueChange` | `(files: File[]) => void` | - | No | Callback when files change |
|
|
31
|
+
| `disabled` | `boolean` | `false` | No | Whether the dropzone is disabled |
|
|
32
|
+
| `required` | `boolean` | `false` | No | Whether file selection is required |
|
|
33
|
+
| `invalid` | `boolean` | `false` | No | Whether the current state is invalid |
|
|
34
|
+
| `id` | `string` | - | No | ID for the dropzone |
|
|
35
|
+
|
|
36
|
+
Plus all standard HTML div attributes (aria-*, data-*, etc.).
|
|
37
|
+
|
|
38
|
+
## Examples
|
|
39
|
+
|
|
40
|
+
### Basic Dropzone
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
import { Dropzone } from '@delightui/components';
|
|
44
|
+
|
|
45
|
+
function BasicDropzoneExample() {
|
|
46
|
+
const [files, setFiles] = useState<File[]>([]);
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<Dropzone
|
|
50
|
+
value={files}
|
|
51
|
+
onValueChange={setFiles}
|
|
52
|
+
maxFiles={1}
|
|
53
|
+
accept={{
|
|
54
|
+
'image/*': ['.jpg', '.jpeg', '.png', '.gif']
|
|
55
|
+
}}
|
|
56
|
+
/>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Custom States
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
import { Dropzone, Text, Spinner, Button } from '@delightui/components';
|
|
65
|
+
|
|
66
|
+
function CustomStatesExample() {
|
|
67
|
+
const [files, setFiles] = useState<File[]>([]);
|
|
68
|
+
const [isUploading, setIsUploading] = useState(false);
|
|
69
|
+
|
|
70
|
+
const handleUpload = async () => {
|
|
71
|
+
setIsUploading(true);
|
|
72
|
+
// Simulate upload
|
|
73
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
74
|
+
setIsUploading(false);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<Dropzone
|
|
79
|
+
value={files}
|
|
80
|
+
onValueChange={setFiles}
|
|
81
|
+
empty={
|
|
82
|
+
<div style={{ textAlign: 'center', padding: '40px' }}>
|
|
83
|
+
<Text size="large">Drop files here or click to upload</Text>
|
|
84
|
+
<Text size="small" color="secondary">
|
|
85
|
+
Supports JPG, PNG, GIF up to 10MB
|
|
86
|
+
</Text>
|
|
87
|
+
</div>
|
|
88
|
+
}
|
|
89
|
+
loading={
|
|
90
|
+
<div style={{ textAlign: 'center', padding: '40px' }}>
|
|
91
|
+
<Spinner />
|
|
92
|
+
<Text>Uploading files...</Text>
|
|
93
|
+
</div>
|
|
94
|
+
}
|
|
95
|
+
uploaded={
|
|
96
|
+
<div style={{ textAlign: 'center', padding: '40px' }}>
|
|
97
|
+
<Text color="success">✓ Files uploaded successfully</Text>
|
|
98
|
+
<Button onClick={() => setFiles([])}>Upload More</Button>
|
|
99
|
+
</div>
|
|
100
|
+
}
|
|
101
|
+
accept={{
|
|
102
|
+
'image/*': ['.jpg', '.jpeg', '.png', '.gif']
|
|
103
|
+
}}
|
|
104
|
+
maxSize={10 * 1024 * 1024} // 10MB
|
|
105
|
+
maxFiles={5}
|
|
106
|
+
/>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Multiple File Upload
|
|
112
|
+
|
|
113
|
+
```tsx
|
|
114
|
+
import { Dropzone, Text } from '@delightui/components';
|
|
115
|
+
|
|
116
|
+
function MultipleFileExample() {
|
|
117
|
+
const [files, setFiles] = useState<File[]>([]);
|
|
118
|
+
|
|
119
|
+
const handleFilesReset = () => {
|
|
120
|
+
setFiles([]);
|
|
121
|
+
console.log('Files reset');
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<div>
|
|
126
|
+
<Dropzone
|
|
127
|
+
value={files}
|
|
128
|
+
onValueChange={setFiles}
|
|
129
|
+
onFilesReset={handleFilesReset}
|
|
130
|
+
maxFiles={10}
|
|
131
|
+
accept={{
|
|
132
|
+
'image/*': ['.jpg', '.jpeg', '.png'],
|
|
133
|
+
'application/pdf': ['.pdf'],
|
|
134
|
+
'text/*': ['.txt', '.csv']
|
|
135
|
+
}}
|
|
136
|
+
maxSize={5 * 1024 * 1024} // 5MB per file
|
|
137
|
+
/>
|
|
138
|
+
|
|
139
|
+
{files.length > 0 && (
|
|
140
|
+
<div style={{ marginTop: '16px' }}>
|
|
141
|
+
<Text>Selected files: {files.length}</Text>
|
|
142
|
+
{files.map((file, index) => (
|
|
143
|
+
<Text key={index} size="small">
|
|
144
|
+
{file.name} ({(file.size / 1024).toFixed(1)} KB)
|
|
145
|
+
</Text>
|
|
146
|
+
))}
|
|
147
|
+
</div>
|
|
148
|
+
)}
|
|
149
|
+
</div>
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Form Integration
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
import { Form, FormField, Dropzone, Button } from '@delightui/components';
|
|
158
|
+
|
|
159
|
+
function FormDropzoneExample() {
|
|
160
|
+
const handleSubmit = (data: any) => {
|
|
161
|
+
console.log('Form submitted with files:', data.documents);
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
return (
|
|
165
|
+
<Form onSubmit={handleSubmit}>
|
|
166
|
+
<FormField
|
|
167
|
+
name="documents"
|
|
168
|
+
label="Upload Documents"
|
|
169
|
+
required
|
|
170
|
+
message="Please upload at least one document"
|
|
171
|
+
>
|
|
172
|
+
<Dropzone
|
|
173
|
+
accept={{
|
|
174
|
+
'application/pdf': ['.pdf'],
|
|
175
|
+
'application/msword': ['.doc'],
|
|
176
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': ['.docx']
|
|
177
|
+
}}
|
|
178
|
+
maxSize={20 * 1024 * 1024} // 20MB
|
|
179
|
+
maxFiles={3}
|
|
180
|
+
/>
|
|
181
|
+
</FormField>
|
|
182
|
+
|
|
183
|
+
<Button actionType="submit">Submit Documents</Button>
|
|
184
|
+
</Form>
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Image Upload with Preview
|
|
190
|
+
|
|
191
|
+
```tsx
|
|
192
|
+
import { Dropzone, Text, Image } from '@delightui/components';
|
|
193
|
+
|
|
194
|
+
function ImageUploadExample() {
|
|
195
|
+
const [files, setFiles] = useState<File[]>([]);
|
|
196
|
+
const [previews, setPreviews] = useState<string[]>([]);
|
|
197
|
+
|
|
198
|
+
useEffect(() => {
|
|
199
|
+
// Create preview URLs for images
|
|
200
|
+
const urls = files.map(file => {
|
|
201
|
+
if (file.type.startsWith('image/')) {
|
|
202
|
+
return URL.createObjectURL(file);
|
|
203
|
+
}
|
|
204
|
+
return null;
|
|
205
|
+
}).filter(Boolean) as string[];
|
|
206
|
+
|
|
207
|
+
setPreviews(urls);
|
|
208
|
+
|
|
209
|
+
// Cleanup URLs on unmount
|
|
210
|
+
return () => {
|
|
211
|
+
urls.forEach(url => URL.revokeObjectURL(url));
|
|
212
|
+
};
|
|
213
|
+
}, [files]);
|
|
214
|
+
|
|
215
|
+
return (
|
|
216
|
+
<div>
|
|
217
|
+
<Dropzone
|
|
218
|
+
value={files}
|
|
219
|
+
onValueChange={setFiles}
|
|
220
|
+
accept={{
|
|
221
|
+
'image/*': ['.jpg', '.jpeg', '.png', '.gif', '.webp']
|
|
222
|
+
}}
|
|
223
|
+
maxSize={5 * 1024 * 1024}
|
|
224
|
+
maxFiles={1}
|
|
225
|
+
uploaded={
|
|
226
|
+
previews.length > 0 ? (
|
|
227
|
+
<div style={{ textAlign: 'center', padding: '20px' }}>
|
|
228
|
+
<Image
|
|
229
|
+
src={previews[0]}
|
|
230
|
+
alt="Preview"
|
|
231
|
+
width={200}
|
|
232
|
+
height={200}
|
|
233
|
+
fit="Crop"
|
|
234
|
+
style={{ borderRadius: '8px' }}
|
|
235
|
+
/>
|
|
236
|
+
<Text>Image uploaded successfully</Text>
|
|
237
|
+
</div>
|
|
238
|
+
) : undefined
|
|
239
|
+
}
|
|
240
|
+
/>
|
|
241
|
+
</div>
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Disabled State
|
|
247
|
+
|
|
248
|
+
```tsx
|
|
249
|
+
import { Dropzone, Text } from '@delightui/components';
|
|
250
|
+
|
|
251
|
+
function DisabledDropzoneExample() {
|
|
252
|
+
return (
|
|
253
|
+
<Dropzone
|
|
254
|
+
disabled
|
|
255
|
+
empty={
|
|
256
|
+
<div style={{ textAlign: 'center', padding: '40px' }}>
|
|
257
|
+
<Text color="disabled">File upload is currently disabled</Text>
|
|
258
|
+
</div>
|
|
259
|
+
}
|
|
260
|
+
/>
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Custom Styling
|
|
266
|
+
|
|
267
|
+
```tsx
|
|
268
|
+
import { Dropzone, Text } from '@delightui/components';
|
|
269
|
+
|
|
270
|
+
function CustomStyledDropzoneExample() {
|
|
271
|
+
const [files, setFiles] = useState<File[]>([]);
|
|
272
|
+
|
|
273
|
+
return (
|
|
274
|
+
<Dropzone
|
|
275
|
+
value={files}
|
|
276
|
+
onValueChange={setFiles}
|
|
277
|
+
className="custom-dropzone"
|
|
278
|
+
style={{
|
|
279
|
+
border: '2px dashed #007bff',
|
|
280
|
+
borderRadius: '12px',
|
|
281
|
+
backgroundColor: '#f8f9fa'
|
|
282
|
+
}}
|
|
283
|
+
empty={
|
|
284
|
+
<div style={{
|
|
285
|
+
textAlign: 'center',
|
|
286
|
+
padding: '60px 20px',
|
|
287
|
+
color: '#007bff'
|
|
288
|
+
}}>
|
|
289
|
+
<Text size="large">📁 Drop your files here</Text>
|
|
290
|
+
<Text size="small">or click to browse</Text>
|
|
291
|
+
</div>
|
|
292
|
+
}
|
|
293
|
+
/>
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Error Handling
|
|
299
|
+
|
|
300
|
+
```tsx
|
|
301
|
+
import { Dropzone, Text } from '@delightui/components';
|
|
302
|
+
|
|
303
|
+
function ErrorHandlingExample() {
|
|
304
|
+
const [files, setFiles] = useState<File[]>([]);
|
|
305
|
+
const [error, setError] = useState<string>('');
|
|
306
|
+
|
|
307
|
+
const handleFileChange = (newFiles: File[]) => {
|
|
308
|
+
setError('');
|
|
309
|
+
|
|
310
|
+
// Validate files
|
|
311
|
+
const invalidFiles = newFiles.filter(file => {
|
|
312
|
+
if (file.size > 10 * 1024 * 1024) {
|
|
313
|
+
setError(`File "${file.name}" is too large. Maximum size is 10MB.`);
|
|
314
|
+
return true;
|
|
315
|
+
}
|
|
316
|
+
return false;
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
if (invalidFiles.length === 0) {
|
|
320
|
+
setFiles(newFiles);
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
return (
|
|
325
|
+
<div>
|
|
326
|
+
<Dropzone
|
|
327
|
+
value={files}
|
|
328
|
+
onValueChange={handleFileChange}
|
|
329
|
+
invalid={!!error}
|
|
330
|
+
accept={{
|
|
331
|
+
'image/*': ['.jpg', '.jpeg', '.png'],
|
|
332
|
+
'application/pdf': ['.pdf']
|
|
333
|
+
}}
|
|
334
|
+
maxSize={10 * 1024 * 1024}
|
|
335
|
+
maxFiles={3}
|
|
336
|
+
/>
|
|
337
|
+
|
|
338
|
+
{error && (
|
|
339
|
+
<Text color="error" size="small" style={{ marginTop: '8px' }}>
|
|
340
|
+
{error}
|
|
341
|
+
</Text>
|
|
342
|
+
)}
|
|
343
|
+
</div>
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
```
|