Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions apps/docs/app/api/demo/repos/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const items = [
{ value: 'hareru-ui', label: 'hareru-ai/ui' },
{ value: 'next-js', label: 'vercel/next.js' },
{ value: 'react', label: 'facebook/react' },
{ value: 'typescript', label: 'microsoft/typescript' },
{ value: 'vite', label: 'vitejs/vite' },
{ value: 'tailwindcss', label: 'tailwindlabs/tailwindcss' },
{ value: 'biome', label: 'biomejs/biome' },
{ value: 'vitest', label: 'vitest-dev/vitest' },
];

export function GET(req: Request) {
const url = new URL(req.url);
const q = (url.searchParams.get('q') ?? '').toLowerCase();
const filtered = q ? items.filter((i) => i.label.toLowerCase().includes(q)) : items;
return Response.json({ items: filtered });
}
19 changes: 19 additions & 0 deletions apps/docs/app/api/demo/search/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const items = [
{ value: 'apple', label: 'Apple' },
{ value: 'banana', label: 'Banana' },
{ value: 'cherry', label: 'Cherry' },
{ value: 'grape', label: 'Grape' },
{ value: 'lemon', label: 'Lemon' },
{ value: 'mango', label: 'Mango' },
{ value: 'orange', label: 'Orange' },
{ value: 'peach', label: 'Peach' },
{ value: 'strawberry', label: 'Strawberry' },
{ value: 'watermelon', label: 'Watermelon' },
];

export function GET(req: Request) {
const url = new URL(req.url);
const q = (url.searchParams.get('q') ?? '').toLowerCase();
const filtered = q ? items.filter((i) => i.label.toLowerCase().includes(q)) : items;
return Response.json({ items: filtered });
}
10 changes: 7 additions & 3 deletions apps/docs/app/components/preview/ComponentPreview.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use client';

import { Toaster, TooltipProvider } from '@hareru/ui';
import { Suspense } from 'react';
import { demos } from './demo-registry';
import './ComponentPreview.css';
Expand All @@ -21,9 +22,12 @@ export function ComponentPreview({ name }: ComponentPreviewProps) {
return (
<div className="hui-preview-container">
<div className="hui-root hui-preview-content">
<Suspense fallback={<div style={{ padding: '1rem' }}>Loading preview...</div>}>
<Demo />
</Suspense>
<TooltipProvider>
<Suspense fallback={<div style={{ padding: '1rem' }}>Loading preview...</div>}>
<Demo />
</Suspense>
<Toaster />
</TooltipProvider>
</div>
</div>
);
Expand Down
56 changes: 55 additions & 1 deletion apps/docs/app/components/preview/demo-registry.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,64 @@
import { type ComponentType, lazy } from 'react';

export const demos: Record<string, ComponentType> = {
'accordion-demo': lazy(() => import('./demos/accordion-demo')),
'alert-demo': lazy(() => import('./demos/alert-demo')),
'alert-dialog-demo': lazy(() => import('./demos/alert-dialog-demo')),
'approval-card-demo': lazy(() => import('./demos/approval-card-demo')),
'aspect-ratio-demo': lazy(() => import('./demos/aspect-ratio-demo')),
'async-combobox-field-demo': lazy(() => import('./demos/async-combobox-field-demo')),
'avatar-demo': lazy(() => import('./demos/avatar-demo')),
'badge-demo': lazy(() => import('./demos/badge-demo')),
'bento-grid-demo': lazy(() => import('./demos/bento-grid-demo')),
'breadcrumb-demo': lazy(() => import('./demos/breadcrumb-demo')),
'button-variants': lazy(() => import('./demos/button-variants')),
'card-demo': lazy(() => import('./demos/card-demo')),
'chat-composer-demo': lazy(() => import('./demos/chat-composer-demo')),
'chat-container-demo': lazy(() => import('./demos/chat-container-demo')),
'chat-message-demo': lazy(() => import('./demos/chat-message-demo')),
'checkbox-demo': lazy(() => import('./demos/checkbox-demo')),
'checkbox-group-demo': lazy(() => import('./demos/checkbox-group-demo')),
'collapsible-demo': lazy(() => import('./demos/collapsible-demo')),
'combobox-demo': lazy(() => import('./demos/combobox-demo')),
'command-demo': lazy(() => import('./demos/command-demo')),
'confidence-badge-demo': lazy(() => import('./demos/confidence-badge-demo')),
'context-menu-demo': lazy(() => import('./demos/context-menu-demo')),
'data-quality-indicator-demo': lazy(() => import('./demos/data-quality-indicator-demo')),
'definition-browser-demo': lazy(() => import('./demos/definition-browser-demo')),
'dialog-demo': lazy(() => import('./demos/dialog-demo')),
'dropdown-menu-demo': lazy(() => import('./demos/dropdown-menu-demo')),
'empty-state-demo': lazy(() => import('./demos/empty-state-demo')),
'field-diff-demo': lazy(() => import('./demos/field-diff-demo')),
'form-field-demo': lazy(() => import('./demos/form-field-demo')),
'input-demo': lazy(() => import('./demos/input-demo')),
'badge-demo': lazy(() => import('./demos/badge-demo')),
'key-value-list-demo': lazy(() => import('./demos/key-value-list-demo')),
'label-demo': lazy(() => import('./demos/label-demo')),
'metric-card-demo': lazy(() => import('./demos/metric-card-demo')),
'navigation-menu-demo': lazy(() => import('./demos/navigation-menu-demo')),
'pagination-demo': lazy(() => import('./demos/pagination-demo')),
'popover-demo': lazy(() => import('./demos/popover-demo')),
'preview-card-demo': lazy(() => import('./demos/preview-card-demo')),
'progress-demo': lazy(() => import('./demos/progress-demo')),
'query-feedback-demo': lazy(() => import('./demos/query-feedback-demo')),
'radio-group-demo': lazy(() => import('./demos/radio-group-demo')),
'readonly-field-demo': lazy(() => import('./demos/readonly-field-demo')),
'reasoning-panel-demo': lazy(() => import('./demos/reasoning-panel-demo')),
'scroll-area-demo': lazy(() => import('./demos/scroll-area-demo')),
'select-demo': lazy(() => import('./demos/select-demo')),
'semantic-suggest-demo': lazy(() => import('./demos/semantic-suggest-demo')),
'separator-demo': lazy(() => import('./demos/separator-demo')),
'sheet-demo': lazy(() => import('./demos/sheet-demo')),
'skeleton-demo': lazy(() => import('./demos/skeleton-demo')),
'slider-demo': lazy(() => import('./demos/slider-demo')),
'streaming-text-demo': lazy(() => import('./demos/streaming-text-demo')),
'switch-demo': lazy(() => import('./demos/switch-demo')),
'table-demo': lazy(() => import('./demos/table-demo')),
'tabs-demo': lazy(() => import('./demos/tabs-demo')),
'textarea-demo': lazy(() => import('./demos/textarea-demo')),
'toast-demo': lazy(() => import('./demos/toast-demo')),
'toggle-demo': lazy(() => import('./demos/toggle-demo')),
'toggle-group-demo': lazy(() => import('./demos/toggle-group-demo')),
'tool-call-card-demo': lazy(() => import('./demos/tool-call-card-demo')),
'toolbar-demo': lazy(() => import('./demos/toolbar-demo')),
'tooltip-demo': lazy(() => import('./demos/tooltip-demo')),
};
43 changes: 43 additions & 0 deletions apps/docs/app/components/preview/demos/accordion-demo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
'use client';

import {
Accordion,
AccordionContent,
AccordionHeader,
AccordionItem,
AccordionTrigger,
} from '@hareru/ui';

export default function AccordionDemo() {
return (
<Accordion defaultValue={['item-1']} style={{ width: '100%', maxWidth: '28rem' }}>
<AccordionItem value="item-1">
<AccordionHeader>
<AccordionTrigger>What is Hareru UI?</AccordionTrigger>
</AccordionHeader>
<AccordionContent>
Hareru UI is a semantic CSS design system with accessible React components built on Base
UI.
</AccordionContent>
</AccordionItem>
<AccordionItem value="item-2">
<AccordionHeader>
<AccordionTrigger>Is it accessible?</AccordionTrigger>
</AccordionHeader>
<AccordionContent>
Yes. All interactive components follow WAI-ARIA guidelines and support keyboard
navigation.
</AccordionContent>
</AccordionItem>
<AccordionItem value="item-3">
<AccordionHeader>
<AccordionTrigger>Can I customize the styles?</AccordionTrigger>
</AccordionHeader>
<AccordionContent>
Yes. Components expose CSS custom properties under the <code>--hui-</code> prefix for full
theming control.
</AccordionContent>
</AccordionItem>
</Accordion>
);
}
30 changes: 30 additions & 0 deletions apps/docs/app/components/preview/demos/alert-demo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
'use client';

import { Alert, AlertDescription, AlertTitle } from '@hareru/ui';

export default function AlertDemo() {
return (
<div
style={{
display: 'flex',
flexDirection: 'column',
gap: '0.75rem',
width: '100%',
maxWidth: '28rem',
}}
>
<Alert>
<AlertTitle>Heads up!</AlertTitle>
<AlertDescription>
You can add components and dependencies to your app using the CLI.
</AlertDescription>
</Alert>
<Alert variant="destructive">
<AlertTitle>Error</AlertTitle>
<AlertDescription>
Your session has expired. Please log in again to continue.
</AlertDescription>
</Alert>
</div>
);
}
34 changes: 34 additions & 0 deletions apps/docs/app/components/preview/demos/alert-dialog-demo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
'use client';

import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from '@hareru/ui';

export default function AlertDialogDemo() {
return (
<AlertDialog>
<AlertDialogTrigger>Delete account</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete your account and remove your
data from our servers.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction>Continue</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
}
30 changes: 30 additions & 0 deletions apps/docs/app/components/preview/demos/approval-card-demo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
'use client';

import { ApprovalCard } from '@hareru/ui';

export default function ApprovalCardDemo() {
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem', maxWidth: '400px' }}>
<ApprovalCard
title="Deploy to Production"
description="This will deploy version 2.4.1 to the production environment."
status="pending"
risk="low"
onApprove={() => {}}
onReject={() => {}}
/>
<ApprovalCard
title="Delete Customer Records"
description="Permanently delete 1,200 inactive customer records."
status="approved"
risk="high"
/>
<ApprovalCard
title="Send Email Campaign"
description="Send promotional email to 50,000 subscribers."
status="rejected"
risk="medium"
/>
</div>
);
}
26 changes: 26 additions & 0 deletions apps/docs/app/components/preview/demos/aspect-ratio-demo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
'use client';

import { AspectRatio } from '@hareru/ui';

export default function AspectRatioDemo() {
return (
<div style={{ width: '100%', maxWidth: '24rem' }}>
<AspectRatio ratio={16 / 9} style={{ borderRadius: '0.5rem', overflow: 'hidden' }}>
<div
style={{
width: '100%',
height: '100%',
background: 'var(--hui-color-muted)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'var(--hui-color-muted-foreground)',
fontSize: '0.875rem',
}}
>
16 / 9
</div>
</AspectRatio>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use client';

import { AsyncComboboxField } from '@hareru/ui';

export default function AsyncComboboxFieldDemo() {
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem', maxWidth: '20rem' }}>
<AsyncComboboxField
label="Search users"
placeholder="Type to search..."
fetchUrl="/api/demo/search"
/>

<AsyncComboboxField
label="Repository"
placeholder="Search repositories..."
fetchUrl="/api/demo/repos"
required
/>
</div>
);
}
21 changes: 21 additions & 0 deletions apps/docs/app/components/preview/demos/avatar-demo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use client';

import { Avatar, AvatarFallback, AvatarImage } from '@hareru/ui';

export default function AvatarDemo() {
return (
<div style={{ display: 'flex', gap: '1rem', alignItems: 'center' }}>
<Avatar>
<AvatarImage src="https://github.com/shadcn.png" alt="shadcn" />
<AvatarFallback>SC</AvatarFallback>
</Avatar>
<Avatar>
<AvatarImage src="https://github.com/vercel.png" alt="Vercel" />
<AvatarFallback>VC</AvatarFallback>
</Avatar>
<Avatar>
<AvatarFallback>JD</AvatarFallback>
</Avatar>
</div>
);
}
Loading
Loading