@delightui/components 0.1.104 → 0.1.106
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/Popover/Popover.presenter.d.ts +26 -0
- package/dist/cjs/components/molecules/Select/Option/Option.types.d.ts +6 -0
- package/dist/cjs/components/molecules/Select/Select.Context.d.ts +1 -1
- package/dist/cjs/components/molecules/Select/Select.d.ts +5 -5
- package/dist/cjs/components/molecules/Select/Select.presenter.d.ts +1 -0
- package/dist/cjs/components/molecules/Select/Select.types.d.ts +5 -0
- package/dist/cjs/components/molecules/Select/index.d.ts +2 -9
- package/dist/cjs/components/molecules/index.d.ts +2 -0
- package/dist/cjs/components/utils/accessibilityUtils.d.ts +41 -0
- package/dist/cjs/components/utils/index.d.ts +2 -0
- package/dist/cjs/library.css +13 -0
- package/dist/cjs/library.js +2 -2
- 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/Popover/Popover.presenter.d.ts +26 -0
- package/dist/esm/components/molecules/Select/Option/Option.types.d.ts +6 -0
- package/dist/esm/components/molecules/Select/Select.Context.d.ts +1 -1
- package/dist/esm/components/molecules/Select/Select.d.ts +5 -5
- package/dist/esm/components/molecules/Select/Select.presenter.d.ts +1 -0
- package/dist/esm/components/molecules/Select/Select.types.d.ts +5 -0
- package/dist/esm/components/molecules/Select/index.d.ts +2 -9
- package/dist/esm/components/molecules/index.d.ts +2 -0
- package/dist/esm/components/utils/accessibilityUtils.d.ts +41 -0
- package/dist/esm/components/utils/index.d.ts +2 -0
- package/dist/esm/library.css +13 -0
- package/dist/esm/library.js +3 -3
- package/dist/esm/library.js.map +1 -1
- package/dist/index.d.ts +156 -12
- 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,605 @@
|
|
|
1
|
+
# Spinner
|
|
2
|
+
|
|
3
|
+
A loading indicator component that displays an animated spinner to indicate processing, loading, or waiting states. This component provides visual feedback to users during asynchronous operations and helps maintain engagement during loading periods.
|
|
4
|
+
|
|
5
|
+
## Aliases
|
|
6
|
+
|
|
7
|
+
- Spinner
|
|
8
|
+
- LoadingSpinner
|
|
9
|
+
- Loader
|
|
10
|
+
|
|
11
|
+
## Props Breakdown
|
|
12
|
+
|
|
13
|
+
**Extends:** Standalone interface (no HTML element inheritance)
|
|
14
|
+
|
|
15
|
+
| Prop | Type | Default | Required | Description |
|
|
16
|
+
|------|------|---------|----------|-------------|
|
|
17
|
+
| `className` | `string` | `undefined` | No | Additional class for styling |
|
|
18
|
+
|
|
19
|
+
*Note: This component has a standalone interface and does not inherit from any HTML element attributes. It only accepts the props explicitly defined above.*
|
|
20
|
+
|
|
21
|
+
## Examples
|
|
22
|
+
|
|
23
|
+
### Basic Spinner
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
import { Spinner } from '@delightui/components';
|
|
27
|
+
|
|
28
|
+
function BasicSpinnerExample() {
|
|
29
|
+
return (
|
|
30
|
+
<div style={{ display: 'flex', justifyContent: 'center', padding: '20px' }}>
|
|
31
|
+
<Spinner />
|
|
32
|
+
</div>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Loading State in Button
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
import { Button, Spinner } from '@delightui/components';
|
|
41
|
+
|
|
42
|
+
function LoadingButtonExample() {
|
|
43
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
44
|
+
|
|
45
|
+
const handleClick = async () => {
|
|
46
|
+
setIsLoading(true);
|
|
47
|
+
// Simulate API call
|
|
48
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
49
|
+
setIsLoading(false);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<Button
|
|
54
|
+
onClick={handleClick}
|
|
55
|
+
disabled={isLoading}
|
|
56
|
+
leadingIcon={isLoading ? <Spinner /> : undefined}
|
|
57
|
+
>
|
|
58
|
+
{isLoading ? 'Loading...' : 'Submit'}
|
|
59
|
+
</Button>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Form Loading State
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
import { Form, FormField, Input, Button, Spinner, Text } from '@delightui/components';
|
|
68
|
+
|
|
69
|
+
function FormLoadingExample() {
|
|
70
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
71
|
+
|
|
72
|
+
const handleSubmit = async (data: any) => {
|
|
73
|
+
setIsSubmitting(true);
|
|
74
|
+
try {
|
|
75
|
+
// Simulate form submission
|
|
76
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
77
|
+
console.log('Form submitted:', data);
|
|
78
|
+
} finally {
|
|
79
|
+
setIsSubmitting(false);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<Form onSubmit={handleSubmit}>
|
|
85
|
+
<FormField name="name" label="Name" required>
|
|
86
|
+
<Input placeholder="Enter your name" disabled={isSubmitting} />
|
|
87
|
+
</FormField>
|
|
88
|
+
|
|
89
|
+
<FormField name="email" label="Email" required>
|
|
90
|
+
<Input
|
|
91
|
+
inputType="Email"
|
|
92
|
+
placeholder="Enter your email"
|
|
93
|
+
disabled={isSubmitting}
|
|
94
|
+
/>
|
|
95
|
+
</FormField>
|
|
96
|
+
|
|
97
|
+
{isSubmitting ? (
|
|
98
|
+
<div style={{
|
|
99
|
+
display: 'flex',
|
|
100
|
+
alignItems: 'center',
|
|
101
|
+
gap: '12px',
|
|
102
|
+
justifyContent: 'center',
|
|
103
|
+
padding: '20px'
|
|
104
|
+
}}>
|
|
105
|
+
<Spinner />
|
|
106
|
+
<Text>Submitting your information...</Text>
|
|
107
|
+
</div>
|
|
108
|
+
) : (
|
|
109
|
+
<Button type="submit">
|
|
110
|
+
Submit Form
|
|
111
|
+
</Button>
|
|
112
|
+
)}
|
|
113
|
+
</Form>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Data Loading State
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
import { Spinner, Text, Button } from '@delightui/components';
|
|
122
|
+
|
|
123
|
+
function DataLoadingExample() {
|
|
124
|
+
const [data, setData] = useState<string[] | null>(null);
|
|
125
|
+
const [loading, setLoading] = useState(false);
|
|
126
|
+
const [error, setError] = useState<string | null>(null);
|
|
127
|
+
|
|
128
|
+
const loadData = async () => {
|
|
129
|
+
setLoading(true);
|
|
130
|
+
setError(null);
|
|
131
|
+
try {
|
|
132
|
+
// Simulate data fetching
|
|
133
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
134
|
+
setData(['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5']);
|
|
135
|
+
} catch (err) {
|
|
136
|
+
setError('Failed to load data');
|
|
137
|
+
} finally {
|
|
138
|
+
setLoading(false);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
return (
|
|
143
|
+
<div style={{ padding: '20px', border: '1px solid #ccc', borderRadius: '8px' }}>
|
|
144
|
+
<div style={{ marginBottom: '16px' }}>
|
|
145
|
+
<Button onClick={loadData} disabled={loading}>
|
|
146
|
+
Load Data
|
|
147
|
+
</Button>
|
|
148
|
+
</div>
|
|
149
|
+
|
|
150
|
+
{loading && (
|
|
151
|
+
<div style={{
|
|
152
|
+
display: 'flex',
|
|
153
|
+
flexDirection: 'column',
|
|
154
|
+
alignItems: 'center',
|
|
155
|
+
gap: '12px',
|
|
156
|
+
padding: '40px'
|
|
157
|
+
}}>
|
|
158
|
+
<Spinner />
|
|
159
|
+
<Text color="secondary">Loading data...</Text>
|
|
160
|
+
</div>
|
|
161
|
+
)}
|
|
162
|
+
|
|
163
|
+
{error && (
|
|
164
|
+
<Text color="error">{error}</Text>
|
|
165
|
+
)}
|
|
166
|
+
|
|
167
|
+
{data && !loading && (
|
|
168
|
+
<div>
|
|
169
|
+
<Text weight="bold" style={{ marginBottom: '8px' }}>
|
|
170
|
+
Loaded Data:
|
|
171
|
+
</Text>
|
|
172
|
+
<ul>
|
|
173
|
+
{data.map((item, index) => (
|
|
174
|
+
<li key={index}>
|
|
175
|
+
<Text>{item}</Text>
|
|
176
|
+
</li>
|
|
177
|
+
))}
|
|
178
|
+
</ul>
|
|
179
|
+
</div>
|
|
180
|
+
)}
|
|
181
|
+
</div>
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Page Loading Overlay
|
|
187
|
+
|
|
188
|
+
```tsx
|
|
189
|
+
import { Spinner, Text } from '@delightui/components';
|
|
190
|
+
|
|
191
|
+
function PageLoadingOverlayExample() {
|
|
192
|
+
const [isPageLoading, setIsPageLoading] = useState(false);
|
|
193
|
+
|
|
194
|
+
const simulatePageLoad = () => {
|
|
195
|
+
setIsPageLoading(true);
|
|
196
|
+
setTimeout(() => setIsPageLoading(false), 3000);
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
return (
|
|
200
|
+
<div style={{ position: 'relative', minHeight: '300px' }}>
|
|
201
|
+
<div>
|
|
202
|
+
<Text weight="bold" style={{ marginBottom: '16px' }}>
|
|
203
|
+
Page Content
|
|
204
|
+
</Text>
|
|
205
|
+
<Text style={{ marginBottom: '16px' }}>
|
|
206
|
+
This is some page content that would be covered by the loading overlay.
|
|
207
|
+
</Text>
|
|
208
|
+
<Button onClick={simulatePageLoad}>
|
|
209
|
+
Simulate Page Loading
|
|
210
|
+
</Button>
|
|
211
|
+
</div>
|
|
212
|
+
|
|
213
|
+
{isPageLoading && (
|
|
214
|
+
<div style={{
|
|
215
|
+
position: 'absolute',
|
|
216
|
+
top: 0,
|
|
217
|
+
left: 0,
|
|
218
|
+
right: 0,
|
|
219
|
+
bottom: 0,
|
|
220
|
+
backgroundColor: 'rgba(255, 255, 255, 0.8)',
|
|
221
|
+
display: 'flex',
|
|
222
|
+
flexDirection: 'column',
|
|
223
|
+
alignItems: 'center',
|
|
224
|
+
justifyContent: 'center',
|
|
225
|
+
gap: '16px',
|
|
226
|
+
zIndex: 1000
|
|
227
|
+
}}>
|
|
228
|
+
<Spinner />
|
|
229
|
+
<Text weight="medium">Loading page...</Text>
|
|
230
|
+
</div>
|
|
231
|
+
)}
|
|
232
|
+
</div>
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Card Loading State
|
|
238
|
+
|
|
239
|
+
```tsx
|
|
240
|
+
import { Spinner, Text, Button } from '@delightui/components';
|
|
241
|
+
|
|
242
|
+
function CardLoadingExample() {
|
|
243
|
+
const [cards, setCards] = useState([
|
|
244
|
+
{ id: 1, loading: false, content: 'Card 1 content loaded' },
|
|
245
|
+
{ id: 2, loading: false, content: 'Card 2 content loaded' },
|
|
246
|
+
{ id: 3, loading: false, content: 'Card 3 content loaded' }
|
|
247
|
+
]);
|
|
248
|
+
|
|
249
|
+
const loadCard = async (cardId: number) => {
|
|
250
|
+
setCards(prev => prev.map(card =>
|
|
251
|
+
card.id === cardId ? { ...card, loading: true } : card
|
|
252
|
+
));
|
|
253
|
+
|
|
254
|
+
// Simulate loading
|
|
255
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
256
|
+
|
|
257
|
+
setCards(prev => prev.map(card =>
|
|
258
|
+
card.id === cardId ? {
|
|
259
|
+
...card,
|
|
260
|
+
loading: false,
|
|
261
|
+
content: `Updated content for Card ${cardId} - ${new Date().toLocaleTimeString()}`
|
|
262
|
+
} : card
|
|
263
|
+
));
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
return (
|
|
267
|
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(250px, 1fr))', gap: '16px' }}>
|
|
268
|
+
{cards.map(card => (
|
|
269
|
+
<div
|
|
270
|
+
key={card.id}
|
|
271
|
+
style={{
|
|
272
|
+
border: '1px solid #ccc',
|
|
273
|
+
borderRadius: '8px',
|
|
274
|
+
padding: '16px',
|
|
275
|
+
minHeight: '150px',
|
|
276
|
+
display: 'flex',
|
|
277
|
+
flexDirection: 'column'
|
|
278
|
+
}}
|
|
279
|
+
>
|
|
280
|
+
<Text weight="bold" style={{ marginBottom: '12px' }}>
|
|
281
|
+
Card {card.id}
|
|
282
|
+
</Text>
|
|
283
|
+
|
|
284
|
+
{card.loading ? (
|
|
285
|
+
<div style={{
|
|
286
|
+
flex: 1,
|
|
287
|
+
display: 'flex',
|
|
288
|
+
flexDirection: 'column',
|
|
289
|
+
alignItems: 'center',
|
|
290
|
+
justifyContent: 'center',
|
|
291
|
+
gap: '8px'
|
|
292
|
+
}}>
|
|
293
|
+
<Spinner />
|
|
294
|
+
<Text size="small" color="secondary">Loading...</Text>
|
|
295
|
+
</div>
|
|
296
|
+
) : (
|
|
297
|
+
<>
|
|
298
|
+
<Text style={{ flex: 1, marginBottom: '12px' }}>
|
|
299
|
+
{card.content}
|
|
300
|
+
</Text>
|
|
301
|
+
<Button
|
|
302
|
+
size="Small"
|
|
303
|
+
type="Outlined"
|
|
304
|
+
onClick={() => loadCard(card.id)}
|
|
305
|
+
>
|
|
306
|
+
Refresh
|
|
307
|
+
</Button>
|
|
308
|
+
</>
|
|
309
|
+
)}
|
|
310
|
+
</div>
|
|
311
|
+
))}
|
|
312
|
+
</div>
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### List Loading with Placeholder
|
|
318
|
+
|
|
319
|
+
```tsx
|
|
320
|
+
import { Spinner, Text, Button } from '@delightui/components';
|
|
321
|
+
|
|
322
|
+
function ListLoadingExample() {
|
|
323
|
+
const [items, setItems] = useState<string[]>([]);
|
|
324
|
+
const [loading, setLoading] = useState(false);
|
|
325
|
+
const [hasMore, setHasMore] = useState(true);
|
|
326
|
+
|
|
327
|
+
const loadItems = async () => {
|
|
328
|
+
setLoading(true);
|
|
329
|
+
// Simulate loading more items
|
|
330
|
+
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
331
|
+
|
|
332
|
+
const newItems = Array.from({ length: 5 }, (_, i) =>
|
|
333
|
+
`Item ${items.length + i + 1}`
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
setItems(prev => [...prev, ...newItems]);
|
|
337
|
+
setHasMore(items.length + newItems.length < 20);
|
|
338
|
+
setLoading(false);
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
return (
|
|
342
|
+
<div style={{
|
|
343
|
+
border: '1px solid #ccc',
|
|
344
|
+
borderRadius: '8px',
|
|
345
|
+
padding: '16px',
|
|
346
|
+
maxWidth: '300px'
|
|
347
|
+
}}>
|
|
348
|
+
<Text weight="bold" style={{ marginBottom: '16px' }}>
|
|
349
|
+
Item List
|
|
350
|
+
</Text>
|
|
351
|
+
|
|
352
|
+
<div style={{ marginBottom: '16px' }}>
|
|
353
|
+
{items.map((item, index) => (
|
|
354
|
+
<div
|
|
355
|
+
key={index}
|
|
356
|
+
style={{
|
|
357
|
+
padding: '8px 0',
|
|
358
|
+
borderBottom: index < items.length - 1 ? '1px solid #eee' : 'none'
|
|
359
|
+
}}
|
|
360
|
+
>
|
|
361
|
+
<Text>{item}</Text>
|
|
362
|
+
</div>
|
|
363
|
+
))}
|
|
364
|
+
</div>
|
|
365
|
+
|
|
366
|
+
{loading && (
|
|
367
|
+
<div style={{
|
|
368
|
+
display: 'flex',
|
|
369
|
+
alignItems: 'center',
|
|
370
|
+
justifyContent: 'center',
|
|
371
|
+
gap: '8px',
|
|
372
|
+
padding: '16px'
|
|
373
|
+
}}>
|
|
374
|
+
<Spinner />
|
|
375
|
+
<Text size="small" color="secondary">Loading more items...</Text>
|
|
376
|
+
</div>
|
|
377
|
+
)}
|
|
378
|
+
|
|
379
|
+
{!loading && hasMore && (
|
|
380
|
+
<Button
|
|
381
|
+
onClick={loadItems}
|
|
382
|
+
size="Small"
|
|
383
|
+
style={{ width: '100%' }}
|
|
384
|
+
>
|
|
385
|
+
Load More
|
|
386
|
+
</Button>
|
|
387
|
+
)}
|
|
388
|
+
|
|
389
|
+
{!hasMore && items.length > 0 && (
|
|
390
|
+
<Text size="small" color="secondary" style={{ textAlign: 'center' }}>
|
|
391
|
+
All items loaded
|
|
392
|
+
</Text>
|
|
393
|
+
)}
|
|
394
|
+
</div>
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
### Search with Loading
|
|
400
|
+
|
|
401
|
+
```tsx
|
|
402
|
+
import { Input, Spinner, Text } from '@delightui/components';
|
|
403
|
+
|
|
404
|
+
function SearchLoadingExample() {
|
|
405
|
+
const [searchTerm, setSearchTerm] = useState('');
|
|
406
|
+
const [loading, setLoading] = useState(false);
|
|
407
|
+
const [results, setResults] = useState<string[]>([]);
|
|
408
|
+
|
|
409
|
+
const mockData = [
|
|
410
|
+
'Apple', 'Banana', 'Cherry', 'Date', 'Elderberry',
|
|
411
|
+
'Fig', 'Grape', 'Honeydew', 'Kiwi', 'Lemon'
|
|
412
|
+
];
|
|
413
|
+
|
|
414
|
+
useEffect(() => {
|
|
415
|
+
if (searchTerm.trim() === '') {
|
|
416
|
+
setResults([]);
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
setLoading(true);
|
|
421
|
+
const timeoutId = setTimeout(() => {
|
|
422
|
+
const filteredResults = mockData.filter(item =>
|
|
423
|
+
item.toLowerCase().includes(searchTerm.toLowerCase())
|
|
424
|
+
);
|
|
425
|
+
setResults(filteredResults);
|
|
426
|
+
setLoading(false);
|
|
427
|
+
}, 800);
|
|
428
|
+
|
|
429
|
+
return () => clearTimeout(timeoutId);
|
|
430
|
+
}, [searchTerm]);
|
|
431
|
+
|
|
432
|
+
return (
|
|
433
|
+
<div style={{ maxWidth: '300px' }}>
|
|
434
|
+
<Input
|
|
435
|
+
value={searchTerm}
|
|
436
|
+
onValueChange={setSearchTerm}
|
|
437
|
+
placeholder="Search items..."
|
|
438
|
+
leadingIcon={loading ? <Spinner /> : <Icon name="SearchFilled" />}
|
|
439
|
+
/>
|
|
440
|
+
|
|
441
|
+
<div style={{ marginTop: '16px', minHeight: '100px' }}>
|
|
442
|
+
{loading ? (
|
|
443
|
+
<div style={{
|
|
444
|
+
display: 'flex',
|
|
445
|
+
alignItems: 'center',
|
|
446
|
+
justifyContent: 'center',
|
|
447
|
+
padding: '20px'
|
|
448
|
+
}}>
|
|
449
|
+
<Text color="secondary">Searching...</Text>
|
|
450
|
+
</div>
|
|
451
|
+
) : results.length > 0 ? (
|
|
452
|
+
<div>
|
|
453
|
+
{results.map((result, index) => (
|
|
454
|
+
<div
|
|
455
|
+
key={index}
|
|
456
|
+
style={{
|
|
457
|
+
padding: '8px',
|
|
458
|
+
borderBottom: '1px solid #eee'
|
|
459
|
+
}}
|
|
460
|
+
>
|
|
461
|
+
<Text>{result}</Text>
|
|
462
|
+
</div>
|
|
463
|
+
))}
|
|
464
|
+
</div>
|
|
465
|
+
) : searchTerm.trim() !== '' ? (
|
|
466
|
+
<div style={{ padding: '20px', textAlign: 'center' }}>
|
|
467
|
+
<Text color="secondary">No results found</Text>
|
|
468
|
+
</div>
|
|
469
|
+
) : null}
|
|
470
|
+
</div>
|
|
471
|
+
</div>
|
|
472
|
+
);
|
|
473
|
+
}
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
### Custom Styled Spinner
|
|
477
|
+
|
|
478
|
+
```tsx
|
|
479
|
+
import { Spinner } from '@delightui/components';
|
|
480
|
+
|
|
481
|
+
function CustomStyledSpinnerExample() {
|
|
482
|
+
return (
|
|
483
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '24px', alignItems: 'center' }}>
|
|
484
|
+
<div>
|
|
485
|
+
<Text style={{ marginBottom: '8px' }}>Default Spinner</Text>
|
|
486
|
+
<Spinner />
|
|
487
|
+
</div>
|
|
488
|
+
|
|
489
|
+
<div>
|
|
490
|
+
<Text style={{ marginBottom: '8px' }}>Large Custom Spinner</Text>
|
|
491
|
+
<Spinner
|
|
492
|
+
className="large-spinner"
|
|
493
|
+
style={{
|
|
494
|
+
width: '48px',
|
|
495
|
+
height: '48px',
|
|
496
|
+
borderWidth: '4px'
|
|
497
|
+
}}
|
|
498
|
+
/>
|
|
499
|
+
</div>
|
|
500
|
+
|
|
501
|
+
<div>
|
|
502
|
+
<Text style={{ marginBottom: '8px' }}>Colored Spinner</Text>
|
|
503
|
+
<Spinner
|
|
504
|
+
className="colored-spinner"
|
|
505
|
+
style={{
|
|
506
|
+
borderTopColor: '#007bff',
|
|
507
|
+
borderRightColor: '#007bff'
|
|
508
|
+
}}
|
|
509
|
+
/>
|
|
510
|
+
</div>
|
|
511
|
+
|
|
512
|
+
<div>
|
|
513
|
+
<Text style={{ marginBottom: '8px' }}>Small Spinner</Text>
|
|
514
|
+
<Spinner
|
|
515
|
+
className="small-spinner"
|
|
516
|
+
style={{
|
|
517
|
+
width: '16px',
|
|
518
|
+
height: '16px',
|
|
519
|
+
borderWidth: '2px'
|
|
520
|
+
}}
|
|
521
|
+
/>
|
|
522
|
+
</div>
|
|
523
|
+
</div>
|
|
524
|
+
);
|
|
525
|
+
}
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
### Multi-step Loading
|
|
529
|
+
|
|
530
|
+
```tsx
|
|
531
|
+
import { Spinner, Text, Button } from '@delightui/components';
|
|
532
|
+
|
|
533
|
+
function MultiStepLoadingExample() {
|
|
534
|
+
const [currentStep, setCurrentStep] = useState(0);
|
|
535
|
+
const [loading, setLoading] = useState(false);
|
|
536
|
+
|
|
537
|
+
const steps = [
|
|
538
|
+
'Initializing...',
|
|
539
|
+
'Processing data...',
|
|
540
|
+
'Validating results...',
|
|
541
|
+
'Finalizing...',
|
|
542
|
+
'Complete!'
|
|
543
|
+
];
|
|
544
|
+
|
|
545
|
+
const startProcess = async () => {
|
|
546
|
+
setLoading(true);
|
|
547
|
+
setCurrentStep(0);
|
|
548
|
+
|
|
549
|
+
for (let i = 0; i < steps.length - 1; i++) {
|
|
550
|
+
setCurrentStep(i);
|
|
551
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
setCurrentStep(steps.length - 1);
|
|
555
|
+
setLoading(false);
|
|
556
|
+
};
|
|
557
|
+
|
|
558
|
+
return (
|
|
559
|
+
<div style={{
|
|
560
|
+
padding: '24px',
|
|
561
|
+
border: '1px solid #ccc',
|
|
562
|
+
borderRadius: '8px',
|
|
563
|
+
textAlign: 'center',
|
|
564
|
+
maxWidth: '300px'
|
|
565
|
+
}}>
|
|
566
|
+
<Text weight="bold" style={{ marginBottom: '24px' }}>
|
|
567
|
+
Multi-step Process
|
|
568
|
+
</Text>
|
|
569
|
+
|
|
570
|
+
{!loading && currentStep < steps.length - 1 && (
|
|
571
|
+
<Button onClick={startProcess}>
|
|
572
|
+
Start Process
|
|
573
|
+
</Button>
|
|
574
|
+
)}
|
|
575
|
+
|
|
576
|
+
{(loading || currentStep === steps.length - 1) && (
|
|
577
|
+
<div style={{
|
|
578
|
+
display: 'flex',
|
|
579
|
+
flexDirection: 'column',
|
|
580
|
+
alignItems: 'center',
|
|
581
|
+
gap: '16px'
|
|
582
|
+
}}>
|
|
583
|
+
{loading && <Spinner />}
|
|
584
|
+
<Text
|
|
585
|
+
color={currentStep === steps.length - 1 ? 'success' : 'default'}
|
|
586
|
+
weight="medium"
|
|
587
|
+
>
|
|
588
|
+
{steps[currentStep]}
|
|
589
|
+
</Text>
|
|
590
|
+
|
|
591
|
+
{currentStep === steps.length - 1 && (
|
|
592
|
+
<Button
|
|
593
|
+
size="Small"
|
|
594
|
+
type="Outlined"
|
|
595
|
+
onClick={() => setCurrentStep(0)}
|
|
596
|
+
>
|
|
597
|
+
Reset
|
|
598
|
+
</Button>
|
|
599
|
+
)}
|
|
600
|
+
</div>
|
|
601
|
+
)}
|
|
602
|
+
</div>
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
```
|