@app-studio/web 0.9.30 → 0.9.32
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/dist/components/Text/Text/Text.view.d.ts +1 -0
- package/dist/web.cjs.development.js +3 -3
- package/dist/web.cjs.development.js.map +1 -1
- package/dist/web.cjs.production.min.js +1 -1
- package/dist/web.cjs.production.min.js.map +1 -1
- package/dist/web.esm.js +3 -3
- package/dist/web.esm.js.map +1 -1
- package/dist/web.umd.development.js +3 -3
- package/dist/web.umd.development.js.map +1 -1
- package/dist/web.umd.production.min.js +1 -1
- package/dist/web.umd.production.min.js.map +1 -1
- package/docs/components/Accordion.mdx +158 -0
- package/docs/components/Alert.mdx +123 -0
- package/docs/components/AspectRatio.mdx +55 -0
- package/docs/components/Avatar.mdx +85 -0
- package/docs/components/Background.mdx +522 -0
- package/docs/components/Badge.mdx +220 -0
- package/docs/components/Button.mdx +272 -0
- package/docs/components/Calendar.mdx +274 -0
- package/docs/components/Card.mdx +341 -0
- package/docs/components/Carousel.mdx +411 -0
- package/docs/components/Center.mdx +474 -0
- package/docs/components/Chart.mdx +232 -0
- package/docs/components/ChatInput.mdx +373 -0
- package/docs/components/Checkbox.mdx +66 -0
- package/docs/components/ColorInput.mdx +209 -0
- package/docs/components/ComboBox.mdx +364 -0
- package/docs/components/Command.mdx +252 -0
- package/docs/components/ContextMenu.mdx +219 -0
- package/docs/components/CountryPicker.mdx +123 -0
- package/docs/components/DatePicker.mdx +77 -0
- package/docs/components/DragAndDrop.mdx +539 -0
- package/docs/components/DropdownMenu.mdx +205 -0
- package/docs/components/File.mdx +8 -0
- package/docs/components/Flow.mdx +257 -0
- package/docs/components/Form.mdx +681 -0
- package/docs/components/Formik.mdx +621 -0
- package/docs/components/Gradient.mdx +271 -0
- package/docs/components/Horizontal.mdx +40 -0
- package/docs/components/HoverCard.mdx +140 -0
- package/docs/components/Icon.mdx +438 -0
- package/docs/components/Label.mdx +438 -0
- package/docs/components/Link.mdx +83 -0
- package/docs/components/Loader.mdx +527 -0
- package/docs/components/Menubar.mdx +124 -0
- package/docs/components/Message.mdx +571 -0
- package/docs/components/Modal.mdx +533 -0
- package/docs/components/NavigationMenu.mdx +165 -0
- package/docs/components/Pagination.mdx +150 -0
- package/docs/components/Password.mdx +121 -0
- package/docs/components/Resizable.mdx +148 -0
- package/docs/components/Select.mdx +126 -0
- package/docs/components/Separator.mdx +121 -0
- package/docs/components/Sidebar.mdx +147 -0
- package/docs/components/Slider.mdx +232 -0
- package/docs/components/Switch.mdx +62 -0
- package/docs/components/Table.mdx +409 -0
- package/docs/components/Tabs.mdx +215 -0
- package/docs/components/TagInput.mdx +528 -0
- package/docs/components/Text.mdx +163 -0
- package/docs/components/TextArea.mdx +136 -0
- package/docs/components/TextField.mdx +225 -0
- package/docs/components/Title.mdx +535 -0
- package/docs/components/Toast.mdx +165 -0
- package/docs/components/Toggle.mdx +141 -0
- package/docs/components/ToggleGroup.mdx +165 -0
- package/docs/components/Tooltip.mdx +191 -0
- package/docs/components/Tree.mdx +340 -0
- package/docs/components/Uploader.mdx +426 -0
- package/docs/components/Vertical.mdx +566 -0
- package/package.json +1 -1
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
# Loader
|
|
2
|
+
|
|
3
|
+
A versatile loading indicator component with multiple animation types, customizable sizes, colors, and text positioning. Perfect for showing loading states, progress indicators, and providing visual feedback during asynchronous operations.
|
|
4
|
+
|
|
5
|
+
### **Import**
|
|
6
|
+
```tsx
|
|
7
|
+
import { Loader } from '@app-studio/web';
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
### **Basic Usage**
|
|
11
|
+
```tsx
|
|
12
|
+
import React from 'react';
|
|
13
|
+
import { Loader } from '@app-studio/web';
|
|
14
|
+
|
|
15
|
+
export const BasicLoader = () => (
|
|
16
|
+
<Loader>Loading...</Loader>
|
|
17
|
+
);
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### **Loader Types**
|
|
21
|
+
```tsx
|
|
22
|
+
import React from 'react';
|
|
23
|
+
import { Loader } from '@app-studio/web';
|
|
24
|
+
import { Vertical, Horizontal } from 'app-studio';
|
|
25
|
+
|
|
26
|
+
export const LoaderTypes = () => (
|
|
27
|
+
<Vertical gap={24}>
|
|
28
|
+
<Horizontal gap={32} alignItems="center">
|
|
29
|
+
<Loader type="default">Default Spinner</Loader>
|
|
30
|
+
<Loader type="dotted">Dotted Loader</Loader>
|
|
31
|
+
<Loader type="quarter">Quarter Loader</Loader>
|
|
32
|
+
</Horizontal>
|
|
33
|
+
</Vertical>
|
|
34
|
+
);
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### **Loader Sizes**
|
|
38
|
+
```tsx
|
|
39
|
+
import React from 'react';
|
|
40
|
+
import { Loader } from '@app-studio/web';
|
|
41
|
+
import { Horizontal } from 'app-studio';
|
|
42
|
+
|
|
43
|
+
export const LoaderSizes = () => (
|
|
44
|
+
<Horizontal gap={24} alignItems="center">
|
|
45
|
+
<Loader size="xs">XS</Loader>
|
|
46
|
+
<Loader size="sm">Small</Loader>
|
|
47
|
+
<Loader size="md">Medium</Loader>
|
|
48
|
+
<Loader size="lg">Large</Loader>
|
|
49
|
+
<Loader size="xl">XL</Loader>
|
|
50
|
+
</Horizontal>
|
|
51
|
+
);
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### **Custom Sizes**
|
|
55
|
+
```tsx
|
|
56
|
+
import React from 'react';
|
|
57
|
+
import { Loader } from '@app-studio/web';
|
|
58
|
+
import { Horizontal } from 'app-studio';
|
|
59
|
+
|
|
60
|
+
export const CustomSizes = () => (
|
|
61
|
+
<Horizontal gap={24} alignItems="center">
|
|
62
|
+
<Loader size={16}>16px</Loader>
|
|
63
|
+
<Loader size={32}>32px</Loader>
|
|
64
|
+
<Loader size={48}>48px</Loader>
|
|
65
|
+
<Loader size={64}>64px</Loader>
|
|
66
|
+
</Horizontal>
|
|
67
|
+
);
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### **Text Positioning**
|
|
71
|
+
```tsx
|
|
72
|
+
import React from 'react';
|
|
73
|
+
import { Loader } from '@app-studio/web';
|
|
74
|
+
import { Vertical } from 'app-studio';
|
|
75
|
+
|
|
76
|
+
export const TextPositioning = () => (
|
|
77
|
+
<Vertical gap={32}>
|
|
78
|
+
<Loader textPosition="right">Loading (Right)</Loader>
|
|
79
|
+
<Loader textPosition="left">Loading (Left)</Loader>
|
|
80
|
+
<Loader textPosition="top">Loading (Top)</Loader>
|
|
81
|
+
<Loader textPosition="bottom">Loading (Bottom)</Loader>
|
|
82
|
+
</Vertical>
|
|
83
|
+
);
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### **Colors and Theming**
|
|
87
|
+
```tsx
|
|
88
|
+
import React from 'react';
|
|
89
|
+
import { Loader } from '@app-studio/web';
|
|
90
|
+
import { Vertical } from 'app-studio';
|
|
91
|
+
|
|
92
|
+
export const ColoredLoaders = () => (
|
|
93
|
+
<Vertical gap={20}>
|
|
94
|
+
<Loader
|
|
95
|
+
loaderColor="color.blue.500"
|
|
96
|
+
textColor="color.blue.700"
|
|
97
|
+
>
|
|
98
|
+
Blue Loader
|
|
99
|
+
</Loader>
|
|
100
|
+
|
|
101
|
+
<Loader
|
|
102
|
+
loaderColor="color.green.500"
|
|
103
|
+
textColor="color.green.700"
|
|
104
|
+
>
|
|
105
|
+
Green Loader
|
|
106
|
+
</Loader>
|
|
107
|
+
|
|
108
|
+
<Loader
|
|
109
|
+
loaderColor="color.red.500"
|
|
110
|
+
textColor="color.red.700"
|
|
111
|
+
>
|
|
112
|
+
Red Loader
|
|
113
|
+
</Loader>
|
|
114
|
+
|
|
115
|
+
<Loader
|
|
116
|
+
loaderColor="theme.primary"
|
|
117
|
+
textColor="theme.primary"
|
|
118
|
+
>
|
|
119
|
+
Theme Primary
|
|
120
|
+
</Loader>
|
|
121
|
+
</Vertical>
|
|
122
|
+
);
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### **Animation Speeds**
|
|
126
|
+
```tsx
|
|
127
|
+
import React from 'react';
|
|
128
|
+
import { Loader } from '@app-studio/web';
|
|
129
|
+
import { Horizontal } from 'app-studio';
|
|
130
|
+
|
|
131
|
+
export const AnimationSpeeds = () => (
|
|
132
|
+
<Horizontal gap={32} alignItems="center">
|
|
133
|
+
<Loader speed="slow">Slow</Loader>
|
|
134
|
+
<Loader speed="normal">Normal</Loader>
|
|
135
|
+
<Loader speed="fast">Fast</Loader>
|
|
136
|
+
</Horizontal>
|
|
137
|
+
);
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### **Loading States in Components**
|
|
141
|
+
```tsx
|
|
142
|
+
import React, { useState } from 'react';
|
|
143
|
+
import { Loader } from '@app-studio/web';
|
|
144
|
+
import { Button } from '@app-studio/web';
|
|
145
|
+
import { View, Text, Vertical } from 'app-studio';
|
|
146
|
+
|
|
147
|
+
export const LoadingStates = () => {
|
|
148
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
149
|
+
const [data, setData] = useState(null);
|
|
150
|
+
|
|
151
|
+
const handleLoad = async () => {
|
|
152
|
+
setIsLoading(true);
|
|
153
|
+
// Simulate API call
|
|
154
|
+
setTimeout(() => {
|
|
155
|
+
setData('Data loaded successfully!');
|
|
156
|
+
setIsLoading(false);
|
|
157
|
+
}, 2000);
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<Vertical gap={20} alignItems="center">
|
|
162
|
+
<Button onClick={handleLoad} disabled={isLoading}>
|
|
163
|
+
Load Data
|
|
164
|
+
</Button>
|
|
165
|
+
|
|
166
|
+
<View
|
|
167
|
+
height={100}
|
|
168
|
+
width={300}
|
|
169
|
+
border="1px solid"
|
|
170
|
+
borderColor="color.gray.200"
|
|
171
|
+
borderRadius={8}
|
|
172
|
+
display="flex"
|
|
173
|
+
alignItems="center"
|
|
174
|
+
justifyContent="center"
|
|
175
|
+
>
|
|
176
|
+
{isLoading ? (
|
|
177
|
+
<Loader loaderColor="theme.primary">
|
|
178
|
+
Loading data...
|
|
179
|
+
</Loader>
|
|
180
|
+
) : data ? (
|
|
181
|
+
<Text color="color.green.600">{data}</Text>
|
|
182
|
+
) : (
|
|
183
|
+
<Text color="color.gray.500">No data loaded</Text>
|
|
184
|
+
)}
|
|
185
|
+
</View>
|
|
186
|
+
</Vertical>
|
|
187
|
+
);
|
|
188
|
+
};
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### **Props**
|
|
192
|
+
|
|
193
|
+
| Prop | Type | Default | Description |
|
|
194
|
+
| ---- | ---- | ------- | ----------- |
|
|
195
|
+
| children | React.ReactNode | undefined | Text to display alongside the loader |
|
|
196
|
+
| type | `'default' \| 'dotted' \| 'quarter'` | 'default' | Type of loading animation |
|
|
197
|
+
| size | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| number` | 'md' | Size of the loader |
|
|
198
|
+
| speed | `'slow' \| 'normal' \| 'fast'` | 'normal' | Animation speed |
|
|
199
|
+
| loaderColor | string | 'currentColor' | Color of the loader animation |
|
|
200
|
+
| textColor | string | 'currentColor' | Color of the text |
|
|
201
|
+
| textPosition | `'left' \| 'right' \| 'top' \| 'bottom'` | 'right' | Position of text relative to loader |
|
|
202
|
+
|
|
203
|
+
**Inherited ViewProps:**
|
|
204
|
+
- All standard view properties like `margin`, `padding`, `width`, `height`
|
|
205
|
+
- Layout properties like `display`, `position`, `flex`
|
|
206
|
+
- Styling properties like `backgroundColor`, `borderRadius`
|
|
207
|
+
|
|
208
|
+
### **Full Page Loading**
|
|
209
|
+
```tsx
|
|
210
|
+
import React from 'react';
|
|
211
|
+
import { Loader } from '@app-studio/web';
|
|
212
|
+
import { Center } from 'app-studio';
|
|
213
|
+
|
|
214
|
+
export const FullPageLoader = () => (
|
|
215
|
+
<Center
|
|
216
|
+
position="fixed"
|
|
217
|
+
top={0}
|
|
218
|
+
left={0}
|
|
219
|
+
right={0}
|
|
220
|
+
bottom={0}
|
|
221
|
+
backgroundColor="rgba(255, 255, 255, 0.9)"
|
|
222
|
+
zIndex={9999}
|
|
223
|
+
>
|
|
224
|
+
<Loader
|
|
225
|
+
size="lg"
|
|
226
|
+
loaderColor="theme.primary"
|
|
227
|
+
textColor="theme.primary"
|
|
228
|
+
>
|
|
229
|
+
Loading application...
|
|
230
|
+
</Loader>
|
|
231
|
+
</Center>
|
|
232
|
+
);
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### **Inline Loading**
|
|
236
|
+
```tsx
|
|
237
|
+
import React, { useState } from 'react';
|
|
238
|
+
import { Loader } from '@app-studio/web';
|
|
239
|
+
import { Button } from '@app-studio/web';
|
|
240
|
+
import { Horizontal, Text } from 'app-studio';
|
|
241
|
+
|
|
242
|
+
export const InlineLoader = () => {
|
|
243
|
+
const [saving, setSaving] = useState(false);
|
|
244
|
+
|
|
245
|
+
const handleSave = () => {
|
|
246
|
+
setSaving(true);
|
|
247
|
+
setTimeout(() => setSaving(false), 2000);
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
return (
|
|
251
|
+
<Horizontal gap={16} alignItems="center">
|
|
252
|
+
<Button onClick={handleSave} disabled={saving}>
|
|
253
|
+
Save Changes
|
|
254
|
+
</Button>
|
|
255
|
+
|
|
256
|
+
{saving && (
|
|
257
|
+
<Loader size="sm" loaderColor="color.blue.500">
|
|
258
|
+
Saving...
|
|
259
|
+
</Loader>
|
|
260
|
+
)}
|
|
261
|
+
|
|
262
|
+
{!saving && (
|
|
263
|
+
<Text color="color.green.600" fontSize={14}>
|
|
264
|
+
All changes saved
|
|
265
|
+
</Text>
|
|
266
|
+
)}
|
|
267
|
+
</Horizontal>
|
|
268
|
+
);
|
|
269
|
+
};
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### **Card Loading State**
|
|
273
|
+
```tsx
|
|
274
|
+
import React, { useState, useEffect } from 'react';
|
|
275
|
+
import { Loader } from '@app-studio/web';
|
|
276
|
+
import { View, Text, Vertical } from 'app-studio';
|
|
277
|
+
|
|
278
|
+
export const CardLoader = () => {
|
|
279
|
+
const [loading, setLoading] = useState(true);
|
|
280
|
+
const [content, setContent] = useState(null);
|
|
281
|
+
|
|
282
|
+
useEffect(() => {
|
|
283
|
+
// Simulate data loading
|
|
284
|
+
setTimeout(() => {
|
|
285
|
+
setContent({
|
|
286
|
+
title: 'Card Title',
|
|
287
|
+
description: 'This is the card content that was loaded.',
|
|
288
|
+
});
|
|
289
|
+
setLoading(false);
|
|
290
|
+
}, 3000);
|
|
291
|
+
}, []);
|
|
292
|
+
|
|
293
|
+
return (
|
|
294
|
+
<View
|
|
295
|
+
width={300}
|
|
296
|
+
height={200}
|
|
297
|
+
border="1px solid"
|
|
298
|
+
borderColor="color.gray.200"
|
|
299
|
+
borderRadius={12}
|
|
300
|
+
padding={20}
|
|
301
|
+
backgroundColor="color.white"
|
|
302
|
+
>
|
|
303
|
+
{loading ? (
|
|
304
|
+
<Center height="100%">
|
|
305
|
+
<Loader
|
|
306
|
+
type="dotted"
|
|
307
|
+
loaderColor="color.gray.400"
|
|
308
|
+
textColor="color.gray.600"
|
|
309
|
+
>
|
|
310
|
+
Loading content...
|
|
311
|
+
</Loader>
|
|
312
|
+
</Center>
|
|
313
|
+
) : (
|
|
314
|
+
<Vertical gap={12}>
|
|
315
|
+
<Text fontSize={18} fontWeight="bold">
|
|
316
|
+
{content.title}
|
|
317
|
+
</Text>
|
|
318
|
+
<Text color="color.gray.600">
|
|
319
|
+
{content.description}
|
|
320
|
+
</Text>
|
|
321
|
+
</Vertical>
|
|
322
|
+
)}
|
|
323
|
+
</View>
|
|
324
|
+
);
|
|
325
|
+
};
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### **Button Loading States**
|
|
329
|
+
```tsx
|
|
330
|
+
import React, { useState } from 'react';
|
|
331
|
+
import { Loader } from '@app-studio/web';
|
|
332
|
+
import { Button } from '@app-studio/web';
|
|
333
|
+
import { Horizontal } from 'app-studio';
|
|
334
|
+
|
|
335
|
+
export const ButtonLoaders = () => {
|
|
336
|
+
const [loading1, setLoading1] = useState(false);
|
|
337
|
+
const [loading2, setLoading2] = useState(false);
|
|
338
|
+
|
|
339
|
+
return (
|
|
340
|
+
<Horizontal gap={16}>
|
|
341
|
+
<Button
|
|
342
|
+
onClick={() => {
|
|
343
|
+
setLoading1(true);
|
|
344
|
+
setTimeout(() => setLoading1(false), 2000);
|
|
345
|
+
}}
|
|
346
|
+
disabled={loading1}
|
|
347
|
+
icon={loading1 ? <Loader size={16} loaderColor="currentColor" /> : undefined}
|
|
348
|
+
>
|
|
349
|
+
{loading1 ? 'Submitting...' : 'Submit'}
|
|
350
|
+
</Button>
|
|
351
|
+
|
|
352
|
+
<Button
|
|
353
|
+
variant="outline"
|
|
354
|
+
onClick={() => {
|
|
355
|
+
setLoading2(true);
|
|
356
|
+
setTimeout(() => setLoading2(false), 2000);
|
|
357
|
+
}}
|
|
358
|
+
disabled={loading2}
|
|
359
|
+
>
|
|
360
|
+
{loading2 ? (
|
|
361
|
+
<Loader size={16} loaderColor="currentColor">
|
|
362
|
+
Processing
|
|
363
|
+
</Loader>
|
|
364
|
+
) : (
|
|
365
|
+
'Process'
|
|
366
|
+
)}
|
|
367
|
+
</Button>
|
|
368
|
+
</Horizontal>
|
|
369
|
+
);
|
|
370
|
+
};
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### **Custom Loader Styling**
|
|
374
|
+
```tsx
|
|
375
|
+
import React from 'react';
|
|
376
|
+
import { Loader } from '@app-studio/web';
|
|
377
|
+
import { View } from 'app-studio';
|
|
378
|
+
|
|
379
|
+
export const CustomLoader = () => (
|
|
380
|
+
<View
|
|
381
|
+
padding={24}
|
|
382
|
+
backgroundColor="color.gray.900"
|
|
383
|
+
borderRadius={12}
|
|
384
|
+
>
|
|
385
|
+
<Loader
|
|
386
|
+
type="quarter"
|
|
387
|
+
size={48}
|
|
388
|
+
loaderColor="color.white"
|
|
389
|
+
textColor="color.white"
|
|
390
|
+
textPosition="bottom"
|
|
391
|
+
views={{
|
|
392
|
+
container: {
|
|
393
|
+
gap: 16,
|
|
394
|
+
},
|
|
395
|
+
text: {
|
|
396
|
+
fontWeight: 'medium',
|
|
397
|
+
fontSize: 16,
|
|
398
|
+
},
|
|
399
|
+
}}
|
|
400
|
+
>
|
|
401
|
+
Loading...
|
|
402
|
+
</Loader>
|
|
403
|
+
</View>
|
|
404
|
+
);
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
### **Best Practices**
|
|
408
|
+
|
|
409
|
+
**When to Use:**
|
|
410
|
+
- During API calls and data fetching
|
|
411
|
+
- File uploads and downloads
|
|
412
|
+
- Form submissions
|
|
413
|
+
- Page transitions
|
|
414
|
+
- Long-running operations
|
|
415
|
+
|
|
416
|
+
**User Experience:**
|
|
417
|
+
- Show loaders for operations taking longer than 200ms
|
|
418
|
+
- Provide meaningful loading text when possible
|
|
419
|
+
- Use appropriate sizes for the context
|
|
420
|
+
- Consider skeleton screens for content loading
|
|
421
|
+
- Ensure loaders are accessible to screen readers
|
|
422
|
+
|
|
423
|
+
**Performance:**
|
|
424
|
+
- Use CSS animations instead of JavaScript when possible
|
|
425
|
+
- Avoid showing loaders for very quick operations
|
|
426
|
+
- Consider lazy loading for better perceived performance
|
|
427
|
+
- Use appropriate animation speeds (not too fast or slow)
|
|
428
|
+
|
|
429
|
+
**Accessibility:**
|
|
430
|
+
- Include descriptive text for screen readers
|
|
431
|
+
- Use `aria-live` regions for dynamic loading states
|
|
432
|
+
- Ensure sufficient color contrast
|
|
433
|
+
- Provide alternative feedback for users who can't see animations
|
|
434
|
+
|
|
435
|
+
### **Integration Examples**
|
|
436
|
+
|
|
437
|
+
**With Forms:**
|
|
438
|
+
```tsx
|
|
439
|
+
import { FormikForm, FormikTextField } from '@app-studio/web';
|
|
440
|
+
|
|
441
|
+
const FormWithLoader = () => {
|
|
442
|
+
const [submitting, setSubmitting] = useState(false);
|
|
443
|
+
|
|
444
|
+
return (
|
|
445
|
+
<FormikForm>
|
|
446
|
+
<FormikTextField name="email" label="Email" />
|
|
447
|
+
<Button
|
|
448
|
+
type="submit"
|
|
449
|
+
disabled={submitting}
|
|
450
|
+
icon={submitting ? <Loader size={16} /> : undefined}
|
|
451
|
+
>
|
|
452
|
+
{submitting ? 'Submitting...' : 'Submit'}
|
|
453
|
+
</Button>
|
|
454
|
+
</FormikForm>
|
|
455
|
+
);
|
|
456
|
+
};
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
**With Data Tables:**
|
|
460
|
+
```tsx
|
|
461
|
+
const DataTable = ({ data, loading }) => (
|
|
462
|
+
<View>
|
|
463
|
+
{loading ? (
|
|
464
|
+
<Center height={200}>
|
|
465
|
+
<Loader>Loading table data...</Loader>
|
|
466
|
+
</Center>
|
|
467
|
+
) : (
|
|
468
|
+
<Table data={data} />
|
|
469
|
+
)}
|
|
470
|
+
</View>
|
|
471
|
+
);
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
**With Image Loading:**
|
|
475
|
+
```tsx
|
|
476
|
+
const ImageWithLoader = ({ src, alt }) => {
|
|
477
|
+
const [loading, setLoading] = useState(true);
|
|
478
|
+
|
|
479
|
+
return (
|
|
480
|
+
<View position="relative">
|
|
481
|
+
{loading && (
|
|
482
|
+
<Center
|
|
483
|
+
position="absolute"
|
|
484
|
+
top={0}
|
|
485
|
+
left={0}
|
|
486
|
+
right={0}
|
|
487
|
+
bottom={0}
|
|
488
|
+
backgroundColor="color.gray.100"
|
|
489
|
+
>
|
|
490
|
+
<Loader size="sm">Loading image...</Loader>
|
|
491
|
+
</Center>
|
|
492
|
+
)}
|
|
493
|
+
<img
|
|
494
|
+
src={src}
|
|
495
|
+
alt={alt}
|
|
496
|
+
onLoad={() => setLoading(false)}
|
|
497
|
+
style={{ opacity: loading ? 0 : 1 }}
|
|
498
|
+
/>
|
|
499
|
+
</View>
|
|
500
|
+
);
|
|
501
|
+
};
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
### **Advanced Patterns**
|
|
505
|
+
|
|
506
|
+
**Progress Loader:**
|
|
507
|
+
```tsx
|
|
508
|
+
const ProgressLoader = ({ progress }) => (
|
|
509
|
+
<Vertical gap={8} alignItems="center">
|
|
510
|
+
<Loader size="lg" />
|
|
511
|
+
<Text fontSize={14} color="color.gray.600">
|
|
512
|
+
{progress}% complete
|
|
513
|
+
</Text>
|
|
514
|
+
</Vertical>
|
|
515
|
+
);
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
**Conditional Loader:**
|
|
519
|
+
```tsx
|
|
520
|
+
const ConditionalLoader = ({ condition, children }) => (
|
|
521
|
+
condition ? (
|
|
522
|
+
<Loader>Loading...</Loader>
|
|
523
|
+
) : (
|
|
524
|
+
children
|
|
525
|
+
)
|
|
526
|
+
);
|
|
527
|
+
```
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Menubar
|
|
2
|
+
|
|
3
|
+
A flexible menubar component for creating application menus with dropdown submenus.
|
|
4
|
+
|
|
5
|
+
### **Import**
|
|
6
|
+
```tsx
|
|
7
|
+
import { Menubar } from '@app-studio/web';
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
### **Default**
|
|
11
|
+
```tsx
|
|
12
|
+
import React from 'react';
|
|
13
|
+
import { Menubar } from '../Menubar';
|
|
14
|
+
import { View } from 'app-studio';
|
|
15
|
+
import { Text } from '../../Text/Text';
|
|
16
|
+
|
|
17
|
+
export const DefaultMenubar = () => {
|
|
18
|
+
const items = [
|
|
19
|
+
{
|
|
20
|
+
id: 'file',
|
|
21
|
+
label: 'File',
|
|
22
|
+
items: [
|
|
23
|
+
{
|
|
24
|
+
id: 'new',
|
|
25
|
+
label: 'New',
|
|
26
|
+
onClick: () => console.log('New clicked'),
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: 'open',
|
|
30
|
+
label: 'Open',
|
|
31
|
+
onClick: () => console.log('Open clicked'),
|
|
32
|
+
},
|
|
33
|
+
{ separator: true },
|
|
34
|
+
{
|
|
35
|
+
id: 'save',
|
|
36
|
+
label: 'Save',
|
|
37
|
+
onClick: () => console.log('Save clicked'),
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
id: 'edit',
|
|
43
|
+
label: 'Edit',
|
|
44
|
+
items: [
|
|
45
|
+
{
|
|
46
|
+
id: 'undo',
|
|
47
|
+
label: 'Undo',
|
|
48
|
+
onClick: () => console.log('Undo clicked'),
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: 'redo',
|
|
52
|
+
label: 'Redo',
|
|
53
|
+
onClick: () => console.log('Redo clicked'),
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<Menubar items={items} />
|
|
61
|
+
);
|
|
62
|
+
};
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### **Variants**
|
|
66
|
+
The Menubar component supports different visual variants:
|
|
67
|
+
|
|
68
|
+
```tsx
|
|
69
|
+
<Menubar items={items} variant="default" />
|
|
70
|
+
<Menubar items={items} variant="filled" />
|
|
71
|
+
<Menubar items={items} variant="outline" />
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### **Orientation**
|
|
75
|
+
The Menubar can be displayed horizontally or vertically:
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
<Menubar items={items} orientation="horizontal" />
|
|
79
|
+
<Menubar items={items} orientation="vertical" />
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### **Sizes**
|
|
83
|
+
The Menubar supports different sizes:
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
<Menubar items={items} size="sm" />
|
|
87
|
+
<Menubar items={items} size="md" />
|
|
88
|
+
<Menubar items={items} size="lg" />
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### **Composite Pattern**
|
|
92
|
+
You can also use the Menubar's subcomponents for more control:
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
<Menubar.Root orientation="horizontal" variant="outline">
|
|
96
|
+
<Menubar.Menu id="file">
|
|
97
|
+
<Menubar.Trigger>File</Menubar.Trigger>
|
|
98
|
+
<Menubar.Content>
|
|
99
|
+
<Menubar.Item id="new" onClick={() => console.log('New clicked')}>
|
|
100
|
+
New
|
|
101
|
+
</Menubar.Item>
|
|
102
|
+
<Menubar.Item id="open" onClick={() => console.log('Open clicked')}>
|
|
103
|
+
Open
|
|
104
|
+
</Menubar.Item>
|
|
105
|
+
<Menubar.Separator />
|
|
106
|
+
<Menubar.Item id="save" onClick={() => console.log('Save clicked')}>
|
|
107
|
+
Save
|
|
108
|
+
</Menubar.Item>
|
|
109
|
+
</Menubar.Content>
|
|
110
|
+
</Menubar.Menu>
|
|
111
|
+
|
|
112
|
+
<Menubar.Menu id="edit">
|
|
113
|
+
<Menubar.Trigger>Edit</Menubar.Trigger>
|
|
114
|
+
<Menubar.Content>
|
|
115
|
+
<Menubar.Item id="undo" onClick={() => console.log('Undo clicked')}>
|
|
116
|
+
Undo
|
|
117
|
+
</Menubar.Item>
|
|
118
|
+
<Menubar.Item id="redo" onClick={() => console.log('Redo clicked')}>
|
|
119
|
+
Redo
|
|
120
|
+
</Menubar.Item>
|
|
121
|
+
</Menubar.Content>
|
|
122
|
+
</Menubar.Menu>
|
|
123
|
+
</Menubar.Root>
|
|
124
|
+
```
|