@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,726 @@
|
|
|
1
|
+
# RenderStateView
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
A utility component that manages different rendering states for data-driven components. RenderStateView provides a clean way to handle loading, empty, and filled states with customizable components for each state, making it perfect for data fetching scenarios where you need to display different content based on the presence and loading state of data.
|
|
6
|
+
|
|
7
|
+
## Aliases
|
|
8
|
+
|
|
9
|
+
- RenderStateView
|
|
10
|
+
- State Renderer
|
|
11
|
+
- Data State Manager
|
|
12
|
+
- Conditional Renderer
|
|
13
|
+
|
|
14
|
+
## Props Breakdown
|
|
15
|
+
|
|
16
|
+
**Extends:** Standalone interface (no HTML element inheritance)
|
|
17
|
+
|
|
18
|
+
| Prop | Type | Default | Required | Description |
|
|
19
|
+
|------|------|---------|----------|-------------|
|
|
20
|
+
| `className` | `string` | - | No | Additional CSS class names |
|
|
21
|
+
| `data` | `T \| null` | - | No | The data to render or check for presence |
|
|
22
|
+
| `isLoading` | `boolean` | - | No | Whether data is currently loading |
|
|
23
|
+
| `filled` | `React.ComponentType<{ data: T } & P>` | - | No | Component to render when data is present |
|
|
24
|
+
| `empty` | `React.ComponentType<P>` | - | No | Component to render when no data |
|
|
25
|
+
| `loading` | `React.ComponentType<P>` | - | No | Component to render during loading state |
|
|
26
|
+
|
|
27
|
+
This component does not add standard HTML attributes as it renders different components based on state.
|
|
28
|
+
|
|
29
|
+
## Examples
|
|
30
|
+
|
|
31
|
+
### Basic Data Loading States
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
import { RenderStateView, Spinner, Text, Button } from '@delightui/components';
|
|
35
|
+
|
|
36
|
+
function BasicRenderStateExample() {
|
|
37
|
+
const [users, setUsers] = useState<any[]>([]);
|
|
38
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
39
|
+
|
|
40
|
+
const fetchUsers = async () => {
|
|
41
|
+
setIsLoading(true);
|
|
42
|
+
try {
|
|
43
|
+
// Simulate API call
|
|
44
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
45
|
+
setUsers([
|
|
46
|
+
{ id: 1, name: 'John Doe', email: 'john@example.com' },
|
|
47
|
+
{ id: 2, name: 'Jane Smith', email: 'jane@example.com' }
|
|
48
|
+
]);
|
|
49
|
+
} catch (error) {
|
|
50
|
+
setUsers([]);
|
|
51
|
+
} finally {
|
|
52
|
+
setIsLoading(false);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const LoadingComponent = () => (
|
|
57
|
+
<div style={{ textAlign: 'center', padding: '40px' }}>
|
|
58
|
+
<Spinner />
|
|
59
|
+
<Text>Loading users...</Text>
|
|
60
|
+
</div>
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const EmptyComponent = () => (
|
|
64
|
+
<div style={{ textAlign: 'center', padding: '40px' }}>
|
|
65
|
+
<Text>No users found</Text>
|
|
66
|
+
<Button onClick={fetchUsers} style={{ marginTop: '16px' }}>
|
|
67
|
+
Load Users
|
|
68
|
+
</Button>
|
|
69
|
+
</div>
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const FilledComponent = ({ data }: { data: any[] }) => (
|
|
73
|
+
<div>
|
|
74
|
+
<Text weight="Bold">Users ({data.length})</Text>
|
|
75
|
+
{data.map(user => (
|
|
76
|
+
<div key={user.id} style={{ padding: '8px', border: '1px solid #ccc', margin: '8px 0' }}>
|
|
77
|
+
<Text weight="Bold">{user.name}</Text>
|
|
78
|
+
<Text size="Small">{user.email}</Text>
|
|
79
|
+
</div>
|
|
80
|
+
))}
|
|
81
|
+
</div>
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<div style={{ padding: '20px' }}>
|
|
86
|
+
<Button onClick={fetchUsers} disabled={isLoading}>
|
|
87
|
+
Fetch Users
|
|
88
|
+
</Button>
|
|
89
|
+
|
|
90
|
+
<RenderStateView
|
|
91
|
+
data={users.length > 0 ? users : null}
|
|
92
|
+
isLoading={isLoading}
|
|
93
|
+
loading={LoadingComponent}
|
|
94
|
+
empty={EmptyComponent}
|
|
95
|
+
filled={FilledComponent}
|
|
96
|
+
/>
|
|
97
|
+
</div>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Product Catalog with Search
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
import { RenderStateView, Input, Text, Card, Button, Image } from '@delightui/components';
|
|
106
|
+
|
|
107
|
+
function ProductCatalogExample() {
|
|
108
|
+
const [products, setProducts] = useState<any[]>([]);
|
|
109
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
110
|
+
const [searchTerm, setSearchTerm] = useState('');
|
|
111
|
+
|
|
112
|
+
const searchProducts = async (term: string) => {
|
|
113
|
+
setIsLoading(true);
|
|
114
|
+
try {
|
|
115
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
116
|
+
|
|
117
|
+
const allProducts = [
|
|
118
|
+
{ id: 1, name: 'Wireless Headphones', price: 199.99, image: '/images/headphones.jpg' },
|
|
119
|
+
{ id: 2, name: 'Smart Watch', price: 299.99, image: '/images/watch.jpg' },
|
|
120
|
+
{ id: 3, name: 'Bluetooth Speaker', price: 89.99, image: '/images/speaker.jpg' }
|
|
121
|
+
];
|
|
122
|
+
|
|
123
|
+
const filtered = term
|
|
124
|
+
? allProducts.filter(p => p.name.toLowerCase().includes(term.toLowerCase()))
|
|
125
|
+
: [];
|
|
126
|
+
|
|
127
|
+
setProducts(filtered);
|
|
128
|
+
} finally {
|
|
129
|
+
setIsLoading(false);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const LoadingSpinner = () => (
|
|
134
|
+
<div style={{ textAlign: 'center', padding: '60px' }}>
|
|
135
|
+
<Text>🔍 Searching products...</Text>
|
|
136
|
+
</div>
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
const EmptyState = () => (
|
|
140
|
+
<div style={{ textAlign: 'center', padding: '60px' }}>
|
|
141
|
+
<Text size="large">No products found</Text>
|
|
142
|
+
<Text>Try searching for "headphones", "watch", or "speaker"</Text>
|
|
143
|
+
</div>
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
const ProductGrid = ({ data }: { data: any[] }) => (
|
|
147
|
+
<div style={{
|
|
148
|
+
display: 'grid',
|
|
149
|
+
gridTemplateColumns: 'repeat(auto-fit, minmax(250px, 1fr))',
|
|
150
|
+
gap: '20px',
|
|
151
|
+
marginTop: '20px'
|
|
152
|
+
}}>
|
|
153
|
+
{data.map(product => (
|
|
154
|
+
<Card key={product.id}>
|
|
155
|
+
<Image
|
|
156
|
+
src={product.image}
|
|
157
|
+
alt={product.name}
|
|
158
|
+
width="100%"
|
|
159
|
+
height={200}
|
|
160
|
+
fit="Cover"
|
|
161
|
+
/>
|
|
162
|
+
<div style={{ padding: '16px' }}>
|
|
163
|
+
<Text weight="Bold">{product.name}</Text>
|
|
164
|
+
<Text size="large" weight="Bold" style={{ color: '#2e7d32' }}>
|
|
165
|
+
${product.price}
|
|
166
|
+
</Text>
|
|
167
|
+
<Button style={{ marginTop: '8px', width: '100%' }}>
|
|
168
|
+
Add to Cart
|
|
169
|
+
</Button>
|
|
170
|
+
</div>
|
|
171
|
+
</Card>
|
|
172
|
+
))}
|
|
173
|
+
</div>
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<div style={{ padding: '20px' }}>
|
|
178
|
+
<Input
|
|
179
|
+
value={searchTerm}
|
|
180
|
+
onValueChange={setSearchTerm}
|
|
181
|
+
placeholder="Search products..."
|
|
182
|
+
trailingIcon={
|
|
183
|
+
<Button
|
|
184
|
+
size="Small"
|
|
185
|
+
onClick={() => searchProducts(searchTerm)}
|
|
186
|
+
disabled={isLoading}
|
|
187
|
+
>
|
|
188
|
+
Search
|
|
189
|
+
</Button>
|
|
190
|
+
}
|
|
191
|
+
/>
|
|
192
|
+
|
|
193
|
+
<RenderStateView
|
|
194
|
+
data={products.length > 0 ? products : null}
|
|
195
|
+
isLoading={isLoading}
|
|
196
|
+
loading={LoadingSpinner}
|
|
197
|
+
empty={EmptyState}
|
|
198
|
+
filled={ProductGrid}
|
|
199
|
+
/>
|
|
200
|
+
</div>
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Dashboard Analytics
|
|
206
|
+
|
|
207
|
+
```tsx
|
|
208
|
+
import { RenderStateView, Text, Card, ProgressBar, Button } from '@delightui/components';
|
|
209
|
+
|
|
210
|
+
function DashboardAnalyticsExample() {
|
|
211
|
+
const [analytics, setAnalytics] = useState<any>(null);
|
|
212
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
213
|
+
|
|
214
|
+
const loadAnalytics = async () => {
|
|
215
|
+
setIsLoading(true);
|
|
216
|
+
try {
|
|
217
|
+
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
218
|
+
setAnalytics({
|
|
219
|
+
pageViews: 125840,
|
|
220
|
+
uniqueVisitors: 45230,
|
|
221
|
+
bounceRate: 32.5,
|
|
222
|
+
conversionRate: 4.2,
|
|
223
|
+
revenueGrowth: 12.8,
|
|
224
|
+
topPages: [
|
|
225
|
+
{ page: '/home', views: 25000 },
|
|
226
|
+
{ page: '/products', views: 18500 },
|
|
227
|
+
{ page: '/about', views: 12300 }
|
|
228
|
+
]
|
|
229
|
+
});
|
|
230
|
+
} finally {
|
|
231
|
+
setIsLoading(false);
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const LoadingDashboard = () => (
|
|
236
|
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(250px, 1fr))', gap: '20px' }}>
|
|
237
|
+
{[1, 2, 3, 4].map(i => (
|
|
238
|
+
<Card key={i} style={{ padding: '20px', textAlign: 'center' }}>
|
|
239
|
+
<div style={{ width: '60px', height: '60px', backgroundColor: '#f0f0f0', borderRadius: '50%', margin: '0 auto 16px' }} />
|
|
240
|
+
<div style={{ width: '100px', height: '20px', backgroundColor: '#f0f0f0', margin: '0 auto 8px' }} />
|
|
241
|
+
<div style={{ width: '80px', height: '16px', backgroundColor: '#f0f0f0', margin: '0 auto' }} />
|
|
242
|
+
</Card>
|
|
243
|
+
))}
|
|
244
|
+
</div>
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
const EmptyDashboard = () => (
|
|
248
|
+
<Card style={{ textAlign: 'center', padding: '60px' }}>
|
|
249
|
+
<Text size="large">📊 No Analytics Data</Text>
|
|
250
|
+
<Text>Analytics data is not available at the moment.</Text>
|
|
251
|
+
<Button onClick={loadAnalytics} style={{ marginTop: '16px' }}>
|
|
252
|
+
Reload Analytics
|
|
253
|
+
</Button>
|
|
254
|
+
</Card>
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
const AnalyticsDashboard = ({ data }: { data: any }) => (
|
|
258
|
+
<div>
|
|
259
|
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: '20px', marginBottom: '30px' }}>
|
|
260
|
+
<Card style={{ padding: '20px', textAlign: 'center' }}>
|
|
261
|
+
<Text size="small" color="secondary">Page Views</Text>
|
|
262
|
+
<Text size="xl" weight="Bold">{data.pageViews.toLocaleString()}</Text>
|
|
263
|
+
</Card>
|
|
264
|
+
|
|
265
|
+
<Card style={{ padding: '20px', textAlign: 'center' }}>
|
|
266
|
+
<Text size="small" color="secondary">Unique Visitors</Text>
|
|
267
|
+
<Text size="xl" weight="Bold">{data.uniqueVisitors.toLocaleString()}</Text>
|
|
268
|
+
</Card>
|
|
269
|
+
|
|
270
|
+
<Card style={{ padding: '20px', textAlign: 'center' }}>
|
|
271
|
+
<Text size="small" color="secondary">Bounce Rate</Text>
|
|
272
|
+
<Text size="xl" weight="Bold">{data.bounceRate}%</Text>
|
|
273
|
+
</Card>
|
|
274
|
+
|
|
275
|
+
<Card style={{ padding: '20px', textAlign: 'center' }}>
|
|
276
|
+
<Text size="small" color="secondary">Conversion Rate</Text>
|
|
277
|
+
<Text size="xl" weight="Bold">{data.conversionRate}%</Text>
|
|
278
|
+
</Card>
|
|
279
|
+
</div>
|
|
280
|
+
|
|
281
|
+
<Card style={{ padding: '20px' }}>
|
|
282
|
+
<Text weight="Bold" style={{ marginBottom: '16px' }}>Top Pages</Text>
|
|
283
|
+
{data.topPages.map((page: any, index: number) => (
|
|
284
|
+
<div key={index} style={{ marginBottom: '12px' }}>
|
|
285
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '4px' }}>
|
|
286
|
+
<Text>{page.page}</Text>
|
|
287
|
+
<Text weight="Bold">{page.views.toLocaleString()}</Text>
|
|
288
|
+
</div>
|
|
289
|
+
<ProgressBar
|
|
290
|
+
value={(page.views / data.topPages[0].views) * 100}
|
|
291
|
+
max={100}
|
|
292
|
+
/>
|
|
293
|
+
</div>
|
|
294
|
+
))}
|
|
295
|
+
</Card>
|
|
296
|
+
</div>
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
return (
|
|
300
|
+
<div style={{ padding: '20px' }}>
|
|
301
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '20px' }}>
|
|
302
|
+
<Text size="xl" weight="Bold">Analytics Dashboard</Text>
|
|
303
|
+
<Button onClick={loadAnalytics} disabled={isLoading}>
|
|
304
|
+
Refresh Data
|
|
305
|
+
</Button>
|
|
306
|
+
</div>
|
|
307
|
+
|
|
308
|
+
<RenderStateView
|
|
309
|
+
data={analytics}
|
|
310
|
+
isLoading={isLoading}
|
|
311
|
+
loading={LoadingDashboard}
|
|
312
|
+
empty={EmptyDashboard}
|
|
313
|
+
filled={AnalyticsDashboard}
|
|
314
|
+
/>
|
|
315
|
+
</div>
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### User Profile Management
|
|
321
|
+
|
|
322
|
+
```tsx
|
|
323
|
+
import { RenderStateView, Text, Card, Button, Image, Form, FormField, Input } from '@delightui/components';
|
|
324
|
+
|
|
325
|
+
function UserProfileExample() {
|
|
326
|
+
const [profile, setProfile] = useState<any>(null);
|
|
327
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
328
|
+
const [isEditing, setIsEditing] = useState(false);
|
|
329
|
+
|
|
330
|
+
const loadProfile = async () => {
|
|
331
|
+
setIsLoading(true);
|
|
332
|
+
try {
|
|
333
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
334
|
+
setProfile({
|
|
335
|
+
name: 'John Doe',
|
|
336
|
+
email: 'john.doe@example.com',
|
|
337
|
+
avatar: '/images/avatar.jpg',
|
|
338
|
+
role: 'Senior Developer',
|
|
339
|
+
department: 'Engineering',
|
|
340
|
+
joinDate: '2022-03-15',
|
|
341
|
+
projects: 24
|
|
342
|
+
});
|
|
343
|
+
} finally {
|
|
344
|
+
setIsLoading(false);
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
const ProfileSkeleton = () => (
|
|
349
|
+
<Card style={{ padding: '30px' }}>
|
|
350
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: '20px', marginBottom: '20px' }}>
|
|
351
|
+
<div style={{ width: '80px', height: '80px', backgroundColor: '#f0f0f0', borderRadius: '50%' }} />
|
|
352
|
+
<div>
|
|
353
|
+
<div style={{ width: '150px', height: '24px', backgroundColor: '#f0f0f0', marginBottom: '8px' }} />
|
|
354
|
+
<div style={{ width: '200px', height: '16px', backgroundColor: '#f0f0f0' }} />
|
|
355
|
+
</div>
|
|
356
|
+
</div>
|
|
357
|
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '16px' }}>
|
|
358
|
+
{[1, 2, 3, 4].map(i => (
|
|
359
|
+
<div key={i}>
|
|
360
|
+
<div style={{ width: '80px', height: '16px', backgroundColor: '#f0f0f0', marginBottom: '4px' }} />
|
|
361
|
+
<div style={{ width: '120px', height: '20px', backgroundColor: '#f0f0f0' }} />
|
|
362
|
+
</div>
|
|
363
|
+
))}
|
|
364
|
+
</div>
|
|
365
|
+
</Card>
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
const EmptyProfile = () => (
|
|
369
|
+
<Card style={{ textAlign: 'center', padding: '60px' }}>
|
|
370
|
+
<Text size="large">👤 No Profile Found</Text>
|
|
371
|
+
<Text>Unable to load profile information.</Text>
|
|
372
|
+
<Button onClick={loadProfile} style={{ marginTop: '16px' }}>
|
|
373
|
+
Retry Loading
|
|
374
|
+
</Button>
|
|
375
|
+
</Card>
|
|
376
|
+
);
|
|
377
|
+
|
|
378
|
+
const ProfileDisplay = ({ data }: { data: any }) => (
|
|
379
|
+
<Card style={{ padding: '30px' }}>
|
|
380
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: '20px' }}>
|
|
381
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: '20px' }}>
|
|
382
|
+
<Image
|
|
383
|
+
src={data.avatar}
|
|
384
|
+
alt={data.name}
|
|
385
|
+
width={80}
|
|
386
|
+
height={80}
|
|
387
|
+
fit="Cover"
|
|
388
|
+
style={{ borderRadius: '50%' }}
|
|
389
|
+
/>
|
|
390
|
+
<div>
|
|
391
|
+
<Text size="xl" weight="Bold">{data.name}</Text>
|
|
392
|
+
<Text color="secondary">{data.email}</Text>
|
|
393
|
+
<Text>{data.role} • {data.department}</Text>
|
|
394
|
+
</div>
|
|
395
|
+
</div>
|
|
396
|
+
<Button onClick={() => setIsEditing(true)}>
|
|
397
|
+
Edit Profile
|
|
398
|
+
</Button>
|
|
399
|
+
</div>
|
|
400
|
+
|
|
401
|
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: '20px' }}>
|
|
402
|
+
<div>
|
|
403
|
+
<Text size="small" color="secondary">Join Date</Text>
|
|
404
|
+
<Text weight="Bold">{data.joinDate}</Text>
|
|
405
|
+
</div>
|
|
406
|
+
<div>
|
|
407
|
+
<Text size="small" color="secondary">Projects</Text>
|
|
408
|
+
<Text weight="Bold">{data.projects}</Text>
|
|
409
|
+
</div>
|
|
410
|
+
<div>
|
|
411
|
+
<Text size="small" color="secondary">Department</Text>
|
|
412
|
+
<Text weight="Bold">{data.department}</Text>
|
|
413
|
+
</div>
|
|
414
|
+
<div>
|
|
415
|
+
<Text size="small" color="secondary">Role</Text>
|
|
416
|
+
<Text weight="Bold">{data.role}</Text>
|
|
417
|
+
</div>
|
|
418
|
+
</div>
|
|
419
|
+
</Card>
|
|
420
|
+
);
|
|
421
|
+
|
|
422
|
+
return (
|
|
423
|
+
<div style={{ padding: '20px' }}>
|
|
424
|
+
<Text size="xl" weight="Bold" style={{ marginBottom: '20px' }}>
|
|
425
|
+
User Profile
|
|
426
|
+
</Text>
|
|
427
|
+
|
|
428
|
+
<RenderStateView
|
|
429
|
+
data={profile}
|
|
430
|
+
isLoading={isLoading}
|
|
431
|
+
loading={ProfileSkeleton}
|
|
432
|
+
empty={EmptyProfile}
|
|
433
|
+
filled={ProfileDisplay}
|
|
434
|
+
/>
|
|
435
|
+
|
|
436
|
+
{!profile && !isLoading && (
|
|
437
|
+
<Button onClick={loadProfile} style={{ marginTop: '20px' }}>
|
|
438
|
+
Load Profile
|
|
439
|
+
</Button>
|
|
440
|
+
)}
|
|
441
|
+
</div>
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
### Shopping Cart States
|
|
447
|
+
|
|
448
|
+
```tsx
|
|
449
|
+
import { RenderStateView, Text, Button, Card, Image } from '@delightui/components';
|
|
450
|
+
|
|
451
|
+
function ShoppingCartExample() {
|
|
452
|
+
const [cartItems, setCartItems] = useState<any[]>([]);
|
|
453
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
454
|
+
|
|
455
|
+
const loadCart = async () => {
|
|
456
|
+
setIsLoading(true);
|
|
457
|
+
try {
|
|
458
|
+
await new Promise(resolve => setTimeout(resolve, 800));
|
|
459
|
+
setCartItems([
|
|
460
|
+
{ id: 1, name: 'Wireless Mouse', price: 29.99, quantity: 2, image: '/images/mouse.jpg' },
|
|
461
|
+
{ id: 2, name: 'USB Cable', price: 12.99, quantity: 1, image: '/images/cable.jpg' }
|
|
462
|
+
]);
|
|
463
|
+
} finally {
|
|
464
|
+
setIsLoading(false);
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
const clearCart = () => {
|
|
469
|
+
setCartItems([]);
|
|
470
|
+
};
|
|
471
|
+
|
|
472
|
+
const CartLoading = () => (
|
|
473
|
+
<Card style={{ padding: '30px', textAlign: 'center' }}>
|
|
474
|
+
<Text>🛒 Loading your cart...</Text>
|
|
475
|
+
</Card>
|
|
476
|
+
);
|
|
477
|
+
|
|
478
|
+
const EmptyCart = () => (
|
|
479
|
+
<Card style={{ padding: '60px', textAlign: 'center' }}>
|
|
480
|
+
<Text size="xl">🛒</Text>
|
|
481
|
+
<Text size="large" weight="Bold">Your cart is empty</Text>
|
|
482
|
+
<Text>Add some items to get started!</Text>
|
|
483
|
+
<Button onClick={loadCart} style={{ marginTop: '20px' }}>
|
|
484
|
+
Load Sample Items
|
|
485
|
+
</Button>
|
|
486
|
+
</Card>
|
|
487
|
+
);
|
|
488
|
+
|
|
489
|
+
const CartWithItems = ({ data }: { data: any[] }) => {
|
|
490
|
+
const total = data.reduce((sum, item) => sum + (item.price * item.quantity), 0);
|
|
491
|
+
|
|
492
|
+
return (
|
|
493
|
+
<div>
|
|
494
|
+
<Card style={{ padding: '20px', marginBottom: '20px' }}>
|
|
495
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '20px' }}>
|
|
496
|
+
<Text size="large" weight="Bold">Shopping Cart ({data.length} items)</Text>
|
|
497
|
+
<Button type="Outlined" onClick={clearCart}>
|
|
498
|
+
Clear Cart
|
|
499
|
+
</Button>
|
|
500
|
+
</div>
|
|
501
|
+
|
|
502
|
+
{data.map(item => (
|
|
503
|
+
<div key={item.id} style={{
|
|
504
|
+
display: 'flex',
|
|
505
|
+
alignItems: 'center',
|
|
506
|
+
gap: '16px',
|
|
507
|
+
padding: '16px 0',
|
|
508
|
+
borderBottom: '1px solid #eee'
|
|
509
|
+
}}>
|
|
510
|
+
<Image
|
|
511
|
+
src={item.image}
|
|
512
|
+
alt={item.name}
|
|
513
|
+
width={60}
|
|
514
|
+
height={60}
|
|
515
|
+
fit="Cover"
|
|
516
|
+
style={{ borderRadius: '8px' }}
|
|
517
|
+
/>
|
|
518
|
+
<div style={{ flex: 1 }}>
|
|
519
|
+
<Text weight="Bold">{item.name}</Text>
|
|
520
|
+
<Text size="small">Quantity: {item.quantity}</Text>
|
|
521
|
+
</div>
|
|
522
|
+
<Text weight="Bold">${(item.price * item.quantity).toFixed(2)}</Text>
|
|
523
|
+
</div>
|
|
524
|
+
))}
|
|
525
|
+
</Card>
|
|
526
|
+
|
|
527
|
+
<Card style={{ padding: '20px' }}>
|
|
528
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '16px' }}>
|
|
529
|
+
<Text size="large" weight="Bold">Total: ${total.toFixed(2)}</Text>
|
|
530
|
+
</div>
|
|
531
|
+
<Button style={{ width: '100%' }}>
|
|
532
|
+
Proceed to Checkout
|
|
533
|
+
</Button>
|
|
534
|
+
</Card>
|
|
535
|
+
</div>
|
|
536
|
+
);
|
|
537
|
+
};
|
|
538
|
+
|
|
539
|
+
return (
|
|
540
|
+
<div style={{ padding: '20px', maxWidth: '600px' }}>
|
|
541
|
+
<RenderStateView
|
|
542
|
+
data={cartItems.length > 0 ? cartItems : null}
|
|
543
|
+
isLoading={isLoading}
|
|
544
|
+
loading={CartLoading}
|
|
545
|
+
empty={EmptyCart}
|
|
546
|
+
filled={CartWithItems}
|
|
547
|
+
/>
|
|
548
|
+
</div>
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
### File Upload Manager
|
|
554
|
+
|
|
555
|
+
```tsx
|
|
556
|
+
import { RenderStateView, Text, Button, Card, ProgressBar } from '@delightui/components';
|
|
557
|
+
|
|
558
|
+
function FileUploadManagerExample() {
|
|
559
|
+
const [files, setFiles] = useState<any[]>([]);
|
|
560
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
561
|
+
|
|
562
|
+
const uploadFiles = async () => {
|
|
563
|
+
setIsLoading(true);
|
|
564
|
+
try {
|
|
565
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
566
|
+
setFiles([
|
|
567
|
+
{ id: 1, name: 'document.pdf', size: '2.4 MB', status: 'completed', uploadDate: '2024-01-15' },
|
|
568
|
+
{ id: 2, name: 'image.jpg', size: '1.8 MB', status: 'completed', uploadDate: '2024-01-15' }
|
|
569
|
+
]);
|
|
570
|
+
} finally {
|
|
571
|
+
setIsLoading(false);
|
|
572
|
+
}
|
|
573
|
+
};
|
|
574
|
+
|
|
575
|
+
const UploadingState = () => (
|
|
576
|
+
<Card style={{ padding: '40px', textAlign: 'center' }}>
|
|
577
|
+
<Text>📤 Uploading files...</Text>
|
|
578
|
+
<ProgressBar value={65} max={100} style={{ marginTop: '16px' }} />
|
|
579
|
+
<Text size="small" style={{ marginTop: '8px' }}>65% complete</Text>
|
|
580
|
+
</Card>
|
|
581
|
+
);
|
|
582
|
+
|
|
583
|
+
const NoFilesState = () => (
|
|
584
|
+
<Card style={{ padding: '60px', textAlign: 'center', border: '2px dashed #ccc' }}>
|
|
585
|
+
<Text size="xl">📁</Text>
|
|
586
|
+
<Text size="large" weight="Bold">No files uploaded</Text>
|
|
587
|
+
<Text>Drag and drop files here or click to upload</Text>
|
|
588
|
+
<Button onClick={uploadFiles} style={{ marginTop: '20px' }}>
|
|
589
|
+
Upload Sample Files
|
|
590
|
+
</Button>
|
|
591
|
+
</Card>
|
|
592
|
+
);
|
|
593
|
+
|
|
594
|
+
const FilesList = ({ data }: { data: any[] }) => (
|
|
595
|
+
<div>
|
|
596
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '20px' }}>
|
|
597
|
+
<Text size="large" weight="Bold">Uploaded Files ({data.length})</Text>
|
|
598
|
+
<Button onClick={uploadFiles}>
|
|
599
|
+
Upload More
|
|
600
|
+
</Button>
|
|
601
|
+
</div>
|
|
602
|
+
|
|
603
|
+
{data.map(file => (
|
|
604
|
+
<Card key={file.id} style={{ padding: '16px', marginBottom: '12px' }}>
|
|
605
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
|
606
|
+
<div>
|
|
607
|
+
<Text weight="Bold">{file.name}</Text>
|
|
608
|
+
<Text size="small" color="secondary">
|
|
609
|
+
{file.size} • Uploaded {file.uploadDate}
|
|
610
|
+
</Text>
|
|
611
|
+
</div>
|
|
612
|
+
<div style={{ display: 'flex', gap: '8px' }}>
|
|
613
|
+
<Button size="Small" type="Outlined">Download</Button>
|
|
614
|
+
<Button size="Small" style="Destructive">Delete</Button>
|
|
615
|
+
</div>
|
|
616
|
+
</div>
|
|
617
|
+
</Card>
|
|
618
|
+
))}
|
|
619
|
+
</div>
|
|
620
|
+
);
|
|
621
|
+
|
|
622
|
+
return (
|
|
623
|
+
<div style={{ padding: '20px', maxWidth: '600px' }}>
|
|
624
|
+
<Text size="xl" weight="Bold" style={{ marginBottom: '20px' }}>
|
|
625
|
+
File Manager
|
|
626
|
+
</Text>
|
|
627
|
+
|
|
628
|
+
<RenderStateView
|
|
629
|
+
data={files.length > 0 ? files : null}
|
|
630
|
+
isLoading={isLoading}
|
|
631
|
+
loading={UploadingState}
|
|
632
|
+
empty={NoFilesState}
|
|
633
|
+
filled={FilesList}
|
|
634
|
+
/>
|
|
635
|
+
</div>
|
|
636
|
+
);
|
|
637
|
+
}
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
### Custom Error States
|
|
641
|
+
|
|
642
|
+
```tsx
|
|
643
|
+
import { RenderStateView, Text, Button, Card } from '@delightui/components';
|
|
644
|
+
|
|
645
|
+
function CustomErrorStatesExample() {
|
|
646
|
+
const [data, setData] = useState<any>(null);
|
|
647
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
648
|
+
const [error, setError] = useState<string>('');
|
|
649
|
+
|
|
650
|
+
const fetchData = async (shouldFail = false) => {
|
|
651
|
+
setIsLoading(true);
|
|
652
|
+
setError('');
|
|
653
|
+
try {
|
|
654
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
655
|
+
if (shouldFail) {
|
|
656
|
+
throw new Error('Network error occurred');
|
|
657
|
+
}
|
|
658
|
+
setData({ message: 'Data loaded successfully!' });
|
|
659
|
+
} catch (err) {
|
|
660
|
+
setError(err instanceof Error ? err.message : 'Unknown error');
|
|
661
|
+
setData(null);
|
|
662
|
+
} finally {
|
|
663
|
+
setIsLoading(false);
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
|
|
667
|
+
const LoadingComponent = () => (
|
|
668
|
+
<Card style={{ padding: '40px', textAlign: 'center' }}>
|
|
669
|
+
<Text>⏳ Loading data...</Text>
|
|
670
|
+
</Card>
|
|
671
|
+
);
|
|
672
|
+
|
|
673
|
+
const ErrorComponent = () => (
|
|
674
|
+
<Card style={{ padding: '40px', textAlign: 'center', backgroundColor: '#ffebee' }}>
|
|
675
|
+
<Text size="large">❌ Error Occurred</Text>
|
|
676
|
+
<Text>{error}</Text>
|
|
677
|
+
<div style={{ marginTop: '20px', display: 'flex', gap: '12px', justifyContent: 'center' }}>
|
|
678
|
+
<Button onClick={() => fetchData(false)}>Retry</Button>
|
|
679
|
+
<Button type="Outlined" onClick={() => setError('')}>Dismiss</Button>
|
|
680
|
+
</div>
|
|
681
|
+
</Card>
|
|
682
|
+
);
|
|
683
|
+
|
|
684
|
+
const SuccessComponent = ({ data }: { data: any }) => (
|
|
685
|
+
<Card style={{ padding: '40px', textAlign: 'center', backgroundColor: '#e8f5e8' }}>
|
|
686
|
+
<Text size="large">✅ Success!</Text>
|
|
687
|
+
<Text>{data.message}</Text>
|
|
688
|
+
</Card>
|
|
689
|
+
);
|
|
690
|
+
|
|
691
|
+
// Use error state to determine what to show
|
|
692
|
+
const currentData = error ? null : data;
|
|
693
|
+
|
|
694
|
+
return (
|
|
695
|
+
<div style={{ padding: '20px', maxWidth: '400px' }}>
|
|
696
|
+
<Text size="xl" weight="Bold" style={{ marginBottom: '20px' }}>
|
|
697
|
+
Error Handling Demo
|
|
698
|
+
</Text>
|
|
699
|
+
|
|
700
|
+
<div style={{ marginBottom: '20px', display: 'flex', gap: '12px' }}>
|
|
701
|
+
<Button onClick={() => fetchData(false)}>Load Success</Button>
|
|
702
|
+
<Button onClick={() => fetchData(true)}>Trigger Error</Button>
|
|
703
|
+
</div>
|
|
704
|
+
|
|
705
|
+
{error ? (
|
|
706
|
+
<ErrorComponent />
|
|
707
|
+
) : (
|
|
708
|
+
<RenderStateView
|
|
709
|
+
data={currentData}
|
|
710
|
+
isLoading={isLoading}
|
|
711
|
+
loading={LoadingComponent}
|
|
712
|
+
empty={() => (
|
|
713
|
+
<Card style={{ padding: '40px', textAlign: 'center' }}>
|
|
714
|
+
<Text>📭 No data available</Text>
|
|
715
|
+
<Button onClick={() => fetchData(false)} style={{ marginTop: '16px' }}>
|
|
716
|
+
Load Data
|
|
717
|
+
</Button>
|
|
718
|
+
</Card>
|
|
719
|
+
)}
|
|
720
|
+
filled={SuccessComponent}
|
|
721
|
+
/>
|
|
722
|
+
)}
|
|
723
|
+
</div>
|
|
724
|
+
);
|
|
725
|
+
}
|
|
726
|
+
```
|