Skip to content

Commit 46362d5

Browse files
Copilothotlong
andcommitted
Complete AI development prompts for all ObjectUI components
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent 25d84af commit 46362d5

8 files changed

Lines changed: 3387 additions & 0 deletions

.github/prompts/components/data-display-components.md

Lines changed: 439 additions & 0 deletions
Large diffs are not rendered by default.

.github/prompts/components/disclosure-complex-components.md

Lines changed: 431 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 361 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,361 @@
1+
# AI Prompt: Feedback Components
2+
3+
## Overview
4+
5+
Feedback components provide **visual feedback** to users about system state, progress, and asynchronous operations. They inform users when things are loading, processing, or completing.
6+
7+
**Category**: `feedback`
8+
**Examples**: loading, progress, skeleton, toast, spinner
9+
**Complexity**: ⭐⭐ Moderate
10+
**Package**: `@object-ui/components/src/renderers/feedback/`
11+
12+
## Purpose
13+
14+
Feedback components:
15+
1. **Show loading states** (spinners, skeletons)
16+
2. **Display progress** (progress bars, percentages)
17+
3. **Notify users** (toasts, alerts)
18+
4. **Indicate activity** (pulsing, animations)
19+
20+
## Core Feedback Components
21+
22+
### Loading Component
23+
Spinning loader indicator.
24+
25+
**Schema**:
26+
```json
27+
{
28+
"type": "loading",
29+
"size": "sm" | "md" | "lg",
30+
"text": "Loading...",
31+
"fullscreen": false
32+
}
33+
```
34+
35+
**Implementation**:
36+
```tsx
37+
import { Loader2 } from 'lucide-react';
38+
import { cva } from 'class-variance-authority';
39+
40+
const loadingVariants = cva('animate-spin', {
41+
variants: {
42+
size: {
43+
sm: 'h-4 w-4',
44+
md: 'h-8 w-8',
45+
lg: 'h-12 w-12'
46+
}
47+
},
48+
defaultVariants: {
49+
size: 'md'
50+
}
51+
});
52+
53+
export function LoadingRenderer({ schema }: RendererProps<LoadingSchema>) {
54+
const content = (
55+
<div className={cn('flex flex-col items-center gap-2', schema.className)}>
56+
<Loader2 className={loadingVariants({ size: schema.size })} />
57+
{schema.text && (
58+
<p className="text-sm text-muted-foreground">{schema.text}</p>
59+
)}
60+
</div>
61+
);
62+
63+
if (schema.fullscreen) {
64+
return (
65+
<div className="fixed inset-0 flex items-center justify-center bg-background/80 backdrop-blur-sm z-50">
66+
{content}
67+
</div>
68+
);
69+
}
70+
71+
return content;
72+
}
73+
```
74+
75+
### Progress Component
76+
Progress bar showing completion percentage.
77+
78+
**Schema**:
79+
```json
80+
{
81+
"type": "progress",
82+
"value": 65,
83+
"max": 100,
84+
"showLabel": true,
85+
"variant": "default" | "success" | "warning" | "danger"
86+
}
87+
```
88+
89+
**Implementation**:
90+
```tsx
91+
import { Progress as ShadcnProgress } from '@/ui/progress';
92+
import { useExpression } from '@object-ui/react';
93+
94+
export function ProgressRenderer({ schema }: RendererProps<ProgressSchema>) {
95+
const value = useExpression(schema.value, {}, 0);
96+
const max = schema.max || 100;
97+
const percentage = Math.round((value / max) * 100);
98+
99+
return (
100+
<div className={cn('space-y-2', schema.className)}>
101+
{schema.showLabel && (
102+
<div className="flex justify-between text-sm">
103+
<span>{schema.label}</span>
104+
<span className="text-muted-foreground">{percentage}%</span>
105+
</div>
106+
)}
107+
108+
<ShadcnProgress
109+
value={percentage}
110+
className={cn(
111+
schema.variant === 'success' && '[&>div]:bg-green-500',
112+
schema.variant === 'warning' && '[&>div]:bg-yellow-500',
113+
schema.variant === 'danger' && '[&>div]:bg-red-500'
114+
)}
115+
/>
116+
</div>
117+
);
118+
}
119+
```
120+
121+
### Skeleton Component
122+
Placeholder for loading content.
123+
124+
**Schema**:
125+
```json
126+
{
127+
"type": "skeleton",
128+
"variant": "text" | "circular" | "rectangular",
129+
"width": "100%",
130+
"height": "20px",
131+
"count": 3
132+
}
133+
```
134+
135+
**Implementation**:
136+
```tsx
137+
import { Skeleton as ShadcnSkeleton } from '@/ui/skeleton';
138+
139+
export function SkeletonRenderer({ schema }: RendererProps<SkeletonSchema>) {
140+
const count = schema.count || 1;
141+
142+
const skeletonStyle = {
143+
width: schema.width,
144+
height: schema.height
145+
};
146+
147+
const skeletonClass = cn(
148+
schema.variant === 'circular' && 'rounded-full',
149+
schema.variant === 'rectangular' && 'rounded-md',
150+
schema.className
151+
);
152+
153+
return (
154+
<div className="space-y-2">
155+
{Array.from({ length: count }).map((_, index) => (
156+
<ShadcnSkeleton
157+
key={index}
158+
className={skeletonClass}
159+
style={skeletonStyle}
160+
/>
161+
))}
162+
</div>
163+
);
164+
}
165+
```
166+
167+
### Toast Component
168+
Temporary notification message.
169+
170+
**Schema**:
171+
```json
172+
{
173+
"type": "toast",
174+
"title": "Success",
175+
"description": "Your changes have been saved.",
176+
"variant": "default" | "destructive",
177+
"duration": 3000
178+
}
179+
```
180+
181+
**Implementation**:
182+
```tsx
183+
import { useToast } from '@/hooks/use-toast';
184+
import { useEffect } from 'react';
185+
186+
export function ToastRenderer({ schema }: RendererProps<ToastSchema>) {
187+
const { toast } = useToast();
188+
189+
useEffect(() => {
190+
toast({
191+
title: schema.title,
192+
description: schema.description,
193+
variant: schema.variant,
194+
duration: schema.duration || 3000
195+
});
196+
}, [schema.title, schema.description, schema.variant, schema.duration]);
197+
198+
return null; // Toast is rendered in portal
199+
}
200+
```
201+
202+
## Development Guidelines
203+
204+
### Loading States
205+
206+
Show loading during async operations:
207+
208+
```tsx
209+
// In button component
210+
{loading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
211+
212+
// Full page loading
213+
{isLoading && (
214+
<LoadingRenderer schema={{ type: 'loading', fullscreen: true }} />
215+
)}
216+
```
217+
218+
### Progress Updates
219+
220+
Support real-time progress updates:
221+
222+
```tsx
223+
const progress = useExpression(schema.value, data, 0);
224+
225+
useEffect(() => {
226+
// Update progress from data context
227+
}, [progress]);
228+
```
229+
230+
### Skeleton Patterns
231+
232+
Match skeleton to actual content structure:
233+
234+
```tsx
235+
// Card skeleton
236+
{
237+
"type": "stack",
238+
"spacing": 2,
239+
"children": [
240+
{ "type": "skeleton", "variant": "rectangular", "height": "200px" },
241+
{ "type": "skeleton", "variant": "text", "width": "60%" },
242+
{ "type": "skeleton", "variant": "text", "width": "40%" }
243+
]
244+
}
245+
```
246+
247+
### Accessibility
248+
249+
```tsx
250+
// Loading state
251+
<div role="status" aria-live="polite">
252+
<Loader2 className="animate-spin" />
253+
<span className="sr-only">Loading...</span>
254+
</div>
255+
256+
// Progress bar
257+
<div role="progressbar" aria-valuenow={value} aria-valuemin={0} aria-valuemax={max}>
258+
<Progress value={value} />
259+
</div>
260+
```
261+
262+
## Testing
263+
264+
```tsx
265+
describe('LoadingRenderer', () => {
266+
it('renders loading spinner', () => {
267+
const schema = { type: 'loading', text: 'Loading...' };
268+
render(<SchemaRenderer schema={schema} />);
269+
270+
expect(screen.getByText('Loading...')).toBeInTheDocument();
271+
});
272+
273+
it('renders fullscreen loading', () => {
274+
const schema = { type: 'loading', fullscreen: true };
275+
const { container } = render(<SchemaRenderer schema={schema} />);
276+
277+
expect(container.querySelector('.fixed')).toBeInTheDocument();
278+
});
279+
});
280+
```
281+
282+
## Common Patterns
283+
284+
### Button with Loading
285+
286+
```json
287+
{
288+
"type": "button",
289+
"label": "Submit",
290+
"loading": "${data.isSubmitting}",
291+
"onClick": { "type": "action", "name": "submit" }
292+
}
293+
```
294+
295+
### Skeleton Card
296+
297+
```json
298+
{
299+
"type": "card",
300+
"className": "p-6",
301+
"body": {
302+
"type": "stack",
303+
"spacing": 3,
304+
"children": [
305+
{ "type": "skeleton", "variant": "circular", "width": "40px", "height": "40px" },
306+
{ "type": "skeleton", "variant": "text", "width": "80%" },
307+
{ "type": "skeleton", "variant": "text", "width": "60%" }
308+
]
309+
}
310+
}
311+
```
312+
313+
### Upload Progress
314+
315+
```json
316+
{
317+
"type": "stack",
318+
"spacing": 2,
319+
"children": [
320+
{ "type": "text", "content": "Uploading file..." },
321+
{
322+
"type": "progress",
323+
"value": "${data.uploadProgress}",
324+
"showLabel": true,
325+
"variant": "default"
326+
}
327+
]
328+
}
329+
```
330+
331+
## Performance
332+
333+
### Avoid Excessive Animation
334+
335+
```tsx
336+
// ✅ Good: Single loader
337+
<Loader2 className="animate-spin" />
338+
339+
// ❌ Bad: Many loaders
340+
{items.map(() => <Loader2 className="animate-spin" />)}
341+
```
342+
343+
### Debounce Updates
344+
345+
```tsx
346+
// For frequent progress updates
347+
const debouncedProgress = useDebounce(progress, 100);
348+
```
349+
350+
## Checklist
351+
352+
- [ ] Loading states handled
353+
- [ ] Progress updates smoothly
354+
- [ ] Skeletons match content
355+
- [ ] Accessible ARIA attributes
356+
- [ ] Animations performant
357+
- [ ] Tests added
358+
359+
---
360+
361+
**Principle**: Provide **clear**, **timely** feedback about system state.

0 commit comments

Comments
 (0)