tembro 3.1.7 → 3.1.9
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/showcase/package-meta.d.ts +2 -2
- package/dist/src/components/layout/app-sidebar.cjs +1 -1
- package/dist/src/components/layout/app-sidebar.js +10 -5
- package/dist/src/showcase/package-meta.cjs +1 -1
- package/dist/src/showcase/package-meta.js +1 -1
- package/dist/src/showcase/site-data.cjs +1 -1
- package/dist/src/showcase/site-data.js +1 -1
- package/dist/src/showcase/tembro-registry.json.cjs +1 -1
- package/dist/src/showcase/tembro-registry.json.js +1 -1
- package/package.json +1 -1
- package/packages/cli/dist/index.cjs +116 -116
- package/packages/cli/vendor/src/components/layout/app-sidebar.tsx +19 -14
- package/packages/cli/vendor/src/showcase/package-meta.ts +1 -1
- package/packages/cli/vendor/src/showcase/tembro-registry.json +1 -1
- package/packages/cli/vendor/templates/showcase/src/App.tsx +107 -0
- package/packages/cli/vendor/templates/showcase/src/showcase/data/registry.ts +273 -0
- package/packages/cli/vendor/templates/showcase/src/showcase/layout/HeroSection.tsx +41 -0
- package/packages/cli/vendor/templates/showcase/src/showcase/layout/WorkbenchSidebar.tsx +82 -0
- package/packages/cli/vendor/templates/showcase/src/showcase/sections/CalendarSection.tsx +35 -0
- package/packages/cli/vendor/templates/showcase/src/showcase/sections/CategorySection.tsx +48 -0
- package/packages/cli/vendor/templates/showcase/src/showcase/sections/ComponentDetailSection.tsx +46 -0
- package/packages/cli/vendor/templates/showcase/src/showcase/sections/ComponentLivePreview.tsx +64 -0
- package/packages/cli/vendor/templates/showcase/src/showcase/sections/CoreUiSection.tsx +100 -0
- package/packages/cli/vendor/templates/showcase/src/showcase/sections/DataTableSection.tsx +45 -0
- package/packages/cli/vendor/templates/showcase/src/showcase/sections/FormsSection.tsx +26 -0
- package/packages/cli/vendor/templates/showcase/src/showcase/sections/InventorySection.tsx +38 -0
- package/packages/cli/vendor/templates/showcase/src/showcase/sections/KanbanSection.tsx +42 -0
- package/packages/cli/vendor/templates/showcase/src/showcase/sections/OverlaySection.tsx +62 -0
- package/packages/cli/vendor/templates/showcase/src/showcase/sections/PatternsSection.tsx +36 -0
- package/packages/cli/vendor/templates/showcase/src/showcase/sections/VerificationSection.tsx +42 -0
- package/packages/cli/vendor/templates/showcase/src/showcase/sections/WizardSection.tsx +44 -0
- package/packages/cli/vendor/templates/showcase/src/showcase/shared/SectionTitle.tsx +13 -0
- package/registry.json +1 -1
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Button } from "@/components/ui/button"
|
|
2
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
|
3
|
+
import { Badge } from "@/components/ui/badge"
|
|
4
|
+
import { getModuleKey, getModuleLabel, getModuleSourcePath, type RegistryGroup } from "@/showcase/data/registry"
|
|
5
|
+
|
|
6
|
+
type CategorySectionProps = {
|
|
7
|
+
group: RegistryGroup
|
|
8
|
+
onSelect: (key: string) => void
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function CategorySection({ group, onSelect }: CategorySectionProps) {
|
|
12
|
+
return (
|
|
13
|
+
<section className="grid gap-4">
|
|
14
|
+
<Card>
|
|
15
|
+
<CardHeader>
|
|
16
|
+
<div className="flex flex-wrap items-start justify-between gap-3">
|
|
17
|
+
<div>
|
|
18
|
+
<CardTitle>{group.name}</CardTitle>
|
|
19
|
+
<CardDescription>{group.components.length} registry component. Sidebar ichidan bittasini tanlab alohida test qilasiz.</CardDescription>
|
|
20
|
+
</div>
|
|
21
|
+
<Badge label={`${group.components.length} components`} variant="secondary" />
|
|
22
|
+
</div>
|
|
23
|
+
</CardHeader>
|
|
24
|
+
<CardContent>
|
|
25
|
+
<div className="grid gap-3 md:grid-cols-2 xl:grid-cols-3">
|
|
26
|
+
{group.components.map((component) => (
|
|
27
|
+
<button
|
|
28
|
+
key={component.name}
|
|
29
|
+
type="button"
|
|
30
|
+
className="rounded-lg border bg-card p-4 text-left transition hover:border-primary/40 hover:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
31
|
+
onClick={() => onSelect(getModuleKey(group.name, component.name))}
|
|
32
|
+
>
|
|
33
|
+
<div className="flex items-center justify-between gap-3">
|
|
34
|
+
<div className="font-medium">{getModuleLabel(component.name)}</div>
|
|
35
|
+
<Badge label={component.status} status={component.status === "stable" ? "success" : "info"} variant="soft" />
|
|
36
|
+
</div>
|
|
37
|
+
<div className="mt-1 text-xs text-muted-foreground">{getModuleSourcePath(component)}</div>
|
|
38
|
+
</button>
|
|
39
|
+
))}
|
|
40
|
+
</div>
|
|
41
|
+
</CardContent>
|
|
42
|
+
</Card>
|
|
43
|
+
<div className="flex justify-end">
|
|
44
|
+
<Button variant="outline" onClick={() => onSelect("overview")}>Back to overview</Button>
|
|
45
|
+
</div>
|
|
46
|
+
</section>
|
|
47
|
+
)
|
|
48
|
+
}
|
package/packages/cli/vendor/templates/showcase/src/showcase/sections/ComponentDetailSection.tsx
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { FolderOpenIcon } from "lucide-react"
|
|
2
|
+
|
|
3
|
+
import { Badge } from "@/components/ui/badge"
|
|
4
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
|
5
|
+
import { getModuleLabel, getModuleSourcePath, type RegistryComponent, type RegistryGroup } from "@/showcase/data/registry"
|
|
6
|
+
import { ComponentLivePreview } from "@/showcase/sections/ComponentLivePreview"
|
|
7
|
+
|
|
8
|
+
type ComponentDetailSectionProps = {
|
|
9
|
+
group: RegistryGroup
|
|
10
|
+
component: RegistryComponent
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function ComponentDetailSection({ group, component }: ComponentDetailSectionProps) {
|
|
14
|
+
const sourcePath = getModuleSourcePath(component)
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<section className="grid gap-5">
|
|
18
|
+
<Card>
|
|
19
|
+
<CardHeader>
|
|
20
|
+
<div className="flex flex-wrap items-start justify-between gap-3">
|
|
21
|
+
<div>
|
|
22
|
+
<CardTitle>{getModuleLabel(component.name)}</CardTitle>
|
|
23
|
+
<CardDescription>Tanlangan registry component test paneli.</CardDescription>
|
|
24
|
+
</div>
|
|
25
|
+
<div className="flex flex-wrap gap-2">
|
|
26
|
+
<Badge label={group.name} status="info" />
|
|
27
|
+
<Badge label={component.status} status={component.status === "stable" ? "success" : "info"} />
|
|
28
|
+
<Badge label="local source" status="success" />
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</CardHeader>
|
|
32
|
+
<CardContent className="grid gap-3 text-sm">
|
|
33
|
+
<div className="flex items-center gap-2 rounded-lg border bg-muted/30 p-3">
|
|
34
|
+
<FolderOpenIcon className="size-4 text-muted-foreground" />
|
|
35
|
+
<code className="min-w-0 break-all">{sourcePath}</code>
|
|
36
|
+
</div>
|
|
37
|
+
<p className="text-muted-foreground">
|
|
38
|
+
Sidebar orqali har bir registry component alohida tanlanadi. Quyidagi preview shu component tegishli category test surfaceini ko‘rsatadi.
|
|
39
|
+
</p>
|
|
40
|
+
</CardContent>
|
|
41
|
+
</Card>
|
|
42
|
+
|
|
43
|
+
<ComponentLivePreview group={group} component={component} />
|
|
44
|
+
</section>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Badge } from "@/components/ui/badge"
|
|
2
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
|
3
|
+
import type { RegistryComponent, RegistryGroup } from "@/showcase/data/registry"
|
|
4
|
+
import { CalendarSection } from "@/showcase/sections/CalendarSection"
|
|
5
|
+
import { CoreUiSection } from "@/showcase/sections/CoreUiSection"
|
|
6
|
+
import { DataTableSection } from "@/showcase/sections/DataTableSection"
|
|
7
|
+
import { FormsSection } from "@/showcase/sections/FormsSection"
|
|
8
|
+
import { KanbanSection } from "@/showcase/sections/KanbanSection"
|
|
9
|
+
import { OverlaySection } from "@/showcase/sections/OverlaySection"
|
|
10
|
+
import { PatternsSection } from "@/showcase/sections/PatternsSection"
|
|
11
|
+
import { WizardSection } from "@/showcase/sections/WizardSection"
|
|
12
|
+
|
|
13
|
+
type ComponentLivePreviewProps = {
|
|
14
|
+
group: RegistryGroup
|
|
15
|
+
component: RegistryComponent
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function getPreviewLabel(groupName: string, componentName: string) {
|
|
19
|
+
if (componentName === "kanban") return "KanbanBoard"
|
|
20
|
+
if (componentName === "data-table") return "DataTable"
|
|
21
|
+
if (["calendar", "date-picker", "date-range-picker"].includes(componentName)) return "Calendar and date pickers"
|
|
22
|
+
if (["drawer", "confirm-dialog", "alert-dialog", "dialog"].includes(componentName)) return "Overlay controls"
|
|
23
|
+
if (["stepper", "wizard"].includes(componentName)) return "Stepper and Wizard"
|
|
24
|
+
if (["rating", "slider", "tag-input", "async-select", "color-picker", "signature-pad", "json-input", "time-picker"].includes(componentName)) return "Advanced input controls"
|
|
25
|
+
if (groupName === "ui") return "UI primitive controls"
|
|
26
|
+
if (groupName === "display") return "Display and status components"
|
|
27
|
+
if (groupName === "patterns") return "Pattern components"
|
|
28
|
+
return `${componentName} live surface`
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function RenderLiveSurface({ group, component }: ComponentLivePreviewProps) {
|
|
32
|
+
if (component.name === "kanban") return <KanbanSection />
|
|
33
|
+
if (component.name === "data-table") return <DataTableSection />
|
|
34
|
+
if (group.name === "calendar" || group.name === "navigation") return <CalendarSection />
|
|
35
|
+
if (group.name === "overlay" || ["dialog", "drawer", "confirm-dialog", "alert-dialog"].includes(component.name)) return <OverlaySection />
|
|
36
|
+
if (group.name === "wizard") return <WizardSection />
|
|
37
|
+
if (group.name === "form" || group.name === "inputs" || group.name === "feedback") return <FormsSection />
|
|
38
|
+
if (group.name === "ui" || group.name === "actions" || group.name === "command") return <CoreUiSection />
|
|
39
|
+
return <PatternsSection />
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function ComponentLivePreview({ group, component }: ComponentLivePreviewProps) {
|
|
43
|
+
return (
|
|
44
|
+
<section className="grid gap-4">
|
|
45
|
+
<Card>
|
|
46
|
+
<CardHeader>
|
|
47
|
+
<div className="flex flex-wrap items-start justify-between gap-3">
|
|
48
|
+
<div>
|
|
49
|
+
<CardTitle>Live preview: {component.name}</CardTitle>
|
|
50
|
+
<CardDescription>{getPreviewLabel(group.name, component.name)} rendered from copied local source.</CardDescription>
|
|
51
|
+
</div>
|
|
52
|
+
<div className="flex flex-wrap gap-2">
|
|
53
|
+
<Badge label={group.name} status="info" />
|
|
54
|
+
<Badge label={component.status} status={component.status === "stable" ? "success" : "info"} />
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
</CardHeader>
|
|
58
|
+
<CardContent>
|
|
59
|
+
<RenderLiveSurface group={group} component={component} />
|
|
60
|
+
</CardContent>
|
|
61
|
+
</Card>
|
|
62
|
+
</section>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { ChevronRightIcon, CommandIcon, SearchIcon, SparklesIcon } from "lucide-react"
|
|
2
|
+
|
|
3
|
+
import { AvatarGroup } from "@/components/display/avatar"
|
|
4
|
+
import { Progress } from "@/components/display/progress"
|
|
5
|
+
import { Tag, TagGroup } from "@/components/display/tag"
|
|
6
|
+
import { Alert } from "@/components/feedback/alert"
|
|
7
|
+
import { Badge } from "@/components/ui/badge"
|
|
8
|
+
import { Button } from "@/components/ui/button"
|
|
9
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
|
10
|
+
import { Checkbox } from "@/components/ui/checkbox"
|
|
11
|
+
import { Input } from "@/components/ui/input"
|
|
12
|
+
import { Select } from "@/components/ui/select"
|
|
13
|
+
import { Switch } from "@/components/ui/switch"
|
|
14
|
+
import { Textarea } from "@/components/ui/textarea"
|
|
15
|
+
import { registryGroups } from "@/showcase/data/registry"
|
|
16
|
+
|
|
17
|
+
export function CoreUiSection() {
|
|
18
|
+
return (
|
|
19
|
+
<div className="grid gap-4 xl:grid-cols-[1.35fr_0.65fr]">
|
|
20
|
+
<Card>
|
|
21
|
+
<CardHeader>
|
|
22
|
+
<CardTitle>UI controls and variants</CardTitle>
|
|
23
|
+
<CardDescription>Button, Badge, Card, Input, Select, Checkbox, Switch, Tabs, Textarea.</CardDescription>
|
|
24
|
+
</CardHeader>
|
|
25
|
+
<CardContent className="grid gap-5">
|
|
26
|
+
<div className="flex flex-wrap gap-2">
|
|
27
|
+
<Button leftIcon={<SparklesIcon className="size-4" />}>Default</Button>
|
|
28
|
+
<Button variant="secondary">Secondary</Button>
|
|
29
|
+
<Button variant="outline">Outline</Button>
|
|
30
|
+
<Button variant="ghost">Ghost</Button>
|
|
31
|
+
<Button variant="warning">Warning</Button>
|
|
32
|
+
<Button variant="destructive">Destructive</Button>
|
|
33
|
+
<Button iconOnly aria-label="Command"><CommandIcon className="size-4" /></Button>
|
|
34
|
+
</div>
|
|
35
|
+
<div className="flex flex-wrap gap-2">
|
|
36
|
+
<Badge label="default" />
|
|
37
|
+
<Badge label="info" status="info" />
|
|
38
|
+
<Badge label="success" status="success" />
|
|
39
|
+
<Badge label="warning" status="warning" />
|
|
40
|
+
<Badge label="danger" status="danger" removable />
|
|
41
|
+
</div>
|
|
42
|
+
<div className="grid gap-3 md:grid-cols-3">
|
|
43
|
+
<Input kind="search" placeholder="Search components" searchIcon={<SearchIcon className="size-4" />} />
|
|
44
|
+
<Input kind="password" placeholder="Password input" />
|
|
45
|
+
<Select
|
|
46
|
+
placeholder="Pick category"
|
|
47
|
+
searchable
|
|
48
|
+
clearable
|
|
49
|
+
options={registryGroups.slice(0, 8).map((group) => ({
|
|
50
|
+
label: group.name,
|
|
51
|
+
value: group.name,
|
|
52
|
+
description: `${group.components.length} components`,
|
|
53
|
+
}))}
|
|
54
|
+
/>
|
|
55
|
+
</div>
|
|
56
|
+
<div className="grid gap-4 md:grid-cols-2">
|
|
57
|
+
<Textarea defaultValue="Textarea with helper text, counter and Tembro theme." helperText="Textarea helper text" maxLength={140} showCharacterCount />
|
|
58
|
+
<div className="grid content-start gap-4 rounded-lg border bg-muted/25 p-4">
|
|
59
|
+
<Switch defaultChecked label="Enable preview" description="Switch with label and description" />
|
|
60
|
+
<div className="flex items-center gap-3">
|
|
61
|
+
<Checkbox defaultChecked />
|
|
62
|
+
<span className="text-sm">Checkbox selected state</span>
|
|
63
|
+
</div>
|
|
64
|
+
<AvatarGroup
|
|
65
|
+
items={[
|
|
66
|
+
{ key: "az", name: "Azamat Jurayev", status: "online" },
|
|
67
|
+
{ key: "qa", name: "QA Tester", status: "busy" },
|
|
68
|
+
{ key: "ux", name: "UX Review", status: "away" },
|
|
69
|
+
{ key: "tm", name: "Tembro Maintainer", status: "offline" },
|
|
70
|
+
]}
|
|
71
|
+
max={3}
|
|
72
|
+
/>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</CardContent>
|
|
76
|
+
</Card>
|
|
77
|
+
<Card
|
|
78
|
+
variant="elevated"
|
|
79
|
+
tone="info"
|
|
80
|
+
eyebrow="Card surface props"
|
|
81
|
+
title="Surface API"
|
|
82
|
+
description="title, description, badge, action, content, footer props."
|
|
83
|
+
badge={<Badge label="styled" status="info" />}
|
|
84
|
+
action={<Button size="sm" variant="outline" rightIcon={<ChevronRightIcon className="size-4" />}>Open</Button>}
|
|
85
|
+
content={
|
|
86
|
+
<div className="grid gap-4">
|
|
87
|
+
<Progress value={78} label="Style coverage" showValue tone="info" />
|
|
88
|
+
<TagGroup>
|
|
89
|
+
<Tag tone="success" selected>Selected</Tag>
|
|
90
|
+
<Tag tone="info">Info</Tag>
|
|
91
|
+
<Tag tone="warning" removable>Removable</Tag>
|
|
92
|
+
</TagGroup>
|
|
93
|
+
<Alert tone="success" title="Styled by Tembro tokens" description="This card uses copied local component code." />
|
|
94
|
+
</div>
|
|
95
|
+
}
|
|
96
|
+
footer={<span className="text-sm text-muted-foreground">No Vite starter component here.</span>}
|
|
97
|
+
/>
|
|
98
|
+
</div>
|
|
99
|
+
)
|
|
100
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { ColumnDef } from "@tanstack/react-table"
|
|
2
|
+
|
|
3
|
+
import { DataTable } from "@/components/data-table/data-table"
|
|
4
|
+
import { Badge } from "@/components/ui/badge"
|
|
5
|
+
import { componentRows, type ComponentRow } from "@/showcase/data/registry"
|
|
6
|
+
|
|
7
|
+
const columns: ColumnDef<ComponentRow>[] = [
|
|
8
|
+
{ accessorKey: "component", header: "Component" },
|
|
9
|
+
{ accessorKey: "category", header: "Category" },
|
|
10
|
+
{
|
|
11
|
+
accessorKey: "status",
|
|
12
|
+
header: "Status",
|
|
13
|
+
cell: ({ row }) => {
|
|
14
|
+
const status = row.original.status
|
|
15
|
+
return (
|
|
16
|
+
<Badge
|
|
17
|
+
status={status === "Ready" ? "success" : status === "Testing" ? "info" : "warning"}
|
|
18
|
+
label={status}
|
|
19
|
+
variant="soft"
|
|
20
|
+
/>
|
|
21
|
+
)
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
{ accessorKey: "props", header: "Props shown" },
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
export function DataTableSection() {
|
|
28
|
+
return (
|
|
29
|
+
<DataTable
|
|
30
|
+
columns={columns}
|
|
31
|
+
data={componentRows}
|
|
32
|
+
title="Component audit"
|
|
33
|
+
description="DataTable with search, column visibility, row actions, refresh, export and pagination features."
|
|
34
|
+
features={{ search: true, columnVisibility: true, rowActions: true, refresh: true, export: true }}
|
|
35
|
+
search={{ placeholder: "Search table", clearable: true }}
|
|
36
|
+
pagination={{ pageIndex: 0, pageSize: 5, rowCount: componentRows.length, showPageSize: true }}
|
|
37
|
+
rowActions={() => [
|
|
38
|
+
{ key: "inspect", label: "Inspect", onSelect: () => undefined },
|
|
39
|
+
{ key: "mark-ready", label: "Mark ready", onSelect: () => undefined },
|
|
40
|
+
]}
|
|
41
|
+
onRefresh={() => undefined}
|
|
42
|
+
onExport={() => undefined}
|
|
43
|
+
/>
|
|
44
|
+
)
|
|
45
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ProgressCard } from "@/components/display/progress"
|
|
2
|
+
import { LoadingState } from "@/components/feedback/loading-state"
|
|
3
|
+
import { Rating } from "@/components/inputs/rating"
|
|
4
|
+
import { RangeSlider, Slider } from "@/components/inputs/slider"
|
|
5
|
+
import { Badge } from "@/components/ui/badge"
|
|
6
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
|
7
|
+
|
|
8
|
+
export function FormsSection() {
|
|
9
|
+
return (
|
|
10
|
+
<div className="grid gap-4 lg:grid-cols-3">
|
|
11
|
+
<Card>
|
|
12
|
+
<CardHeader>
|
|
13
|
+
<CardTitle>Advanced inputs</CardTitle>
|
|
14
|
+
<CardDescription>Rating and sliders.</CardDescription>
|
|
15
|
+
</CardHeader>
|
|
16
|
+
<CardContent className="grid gap-5">
|
|
17
|
+
<Rating defaultValue={4} />
|
|
18
|
+
<Slider defaultValue={72} label="Quality" showValue formatValue={(value) => `${value}%`} />
|
|
19
|
+
<RangeSlider defaultValue={[20, 80]} label="Range" showValue />
|
|
20
|
+
</CardContent>
|
|
21
|
+
</Card>
|
|
22
|
+
<ProgressCard title="Form completeness" description="input, select, switch, textarea" value={86} tone="success" footer={<Badge label="forms" status="success" />} />
|
|
23
|
+
<LoadingState label="Loading state" description="Spinner/skeleton/progress state component." />
|
|
24
|
+
</div>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Badge } from "@/components/ui/badge"
|
|
2
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
|
3
|
+
import { Tag } from "@/components/display/tag"
|
|
4
|
+
import { registryGroups } from "@/showcase/data/registry"
|
|
5
|
+
import { SectionTitle } from "@/showcase/shared/SectionTitle"
|
|
6
|
+
|
|
7
|
+
export function InventorySection() {
|
|
8
|
+
return (
|
|
9
|
+
<section>
|
|
10
|
+
<SectionTitle
|
|
11
|
+
title="Complete Registry Component Inventory"
|
|
12
|
+
description="Bu yerda `tembro add <name>` bilan chaqiriladigan aniq component nomlari ko‘rinadi."
|
|
13
|
+
/>
|
|
14
|
+
<div className="grid gap-3 md:grid-cols-2 xl:grid-cols-3">
|
|
15
|
+
{registryGroups.map((group) => (
|
|
16
|
+
<Card key={group.name} variant="outline" className="min-w-0">
|
|
17
|
+
<CardHeader>
|
|
18
|
+
<div className="flex items-center justify-between gap-3">
|
|
19
|
+
<CardTitle>{group.name}</CardTitle>
|
|
20
|
+
<Badge label={group.components.length} variant="secondary" />
|
|
21
|
+
</div>
|
|
22
|
+
<CardDescription>src/components/{group.name}</CardDescription>
|
|
23
|
+
</CardHeader>
|
|
24
|
+
<CardContent>
|
|
25
|
+
<div className="flex flex-wrap gap-2">
|
|
26
|
+
{group.components.map((component) => (
|
|
27
|
+
<Tag key={component.name} size="sm" tone="neutral">
|
|
28
|
+
{component.name}
|
|
29
|
+
</Tag>
|
|
30
|
+
))}
|
|
31
|
+
</div>
|
|
32
|
+
</CardContent>
|
|
33
|
+
</Card>
|
|
34
|
+
))}
|
|
35
|
+
</div>
|
|
36
|
+
</section>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { useMemo } from "react"
|
|
2
|
+
|
|
3
|
+
import { KanbanBoard, type KanbanColumn } from "@/components/display/kanban"
|
|
4
|
+
import { Badge } from "@/components/ui/badge"
|
|
5
|
+
|
|
6
|
+
export function KanbanSection() {
|
|
7
|
+
const kanbanColumns = useMemo<KanbanColumn[]>(
|
|
8
|
+
() => [
|
|
9
|
+
{
|
|
10
|
+
key: "todo",
|
|
11
|
+
title: "Todo",
|
|
12
|
+
count: 2,
|
|
13
|
+
cards: [
|
|
14
|
+
{ key: "sidebar", title: "Sidebar", description: "Navigation, badges, collapsed state", meta: <Badge label="layout" /> },
|
|
15
|
+
{ key: "upload", title: "Upload", description: "file-upload and image-upload components", meta: <Badge label="upload" /> },
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
key: "doing",
|
|
20
|
+
title: "Doing",
|
|
21
|
+
count: 2,
|
|
22
|
+
cards: [
|
|
23
|
+
{ key: "kanban", title: "Kanban board", description: "Drag cards between columns", meta: <Badge status="info" label="dnd" /> },
|
|
24
|
+
{ key: "wizard", title: "Wizard flow", description: "Stepper and wizard controls", meta: <Badge status="warning" label="flow" /> },
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
key: "done",
|
|
29
|
+
title: "Done",
|
|
30
|
+
count: 3,
|
|
31
|
+
cards: [
|
|
32
|
+
{ key: "ui", title: "UI controls", description: "Button, input, select, tabs, card", meta: <Badge status="success" label="ready" /> },
|
|
33
|
+
{ key: "calendar", title: "Calendar", description: "Calendar and DatePicker", meta: <Badge status="success" label="ready" /> },
|
|
34
|
+
{ key: "table", title: "DataTable", description: "Search, actions, pagination", meta: <Badge status="success" label="ready" /> },
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
[]
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
return <KanbanBoard defaultColumns={kanbanColumns} />
|
|
42
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { useState } from "react"
|
|
2
|
+
|
|
3
|
+
import { StatusLegend } from "@/components/display/status-legend"
|
|
4
|
+
import { Alert } from "@/components/feedback/alert"
|
|
5
|
+
import { ConfirmDialog } from "@/components/overlay/confirm-dialog"
|
|
6
|
+
import { Drawer, DrawerCloseButton } from "@/components/overlay/drawer"
|
|
7
|
+
import { Button } from "@/components/ui/button"
|
|
8
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
|
9
|
+
|
|
10
|
+
export function OverlaySection() {
|
|
11
|
+
const [drawerOpen, setDrawerOpen] = useState(false)
|
|
12
|
+
const [confirmOpen, setConfirmOpen] = useState(false)
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<div className="grid gap-4 lg:grid-cols-3">
|
|
16
|
+
<Card>
|
|
17
|
+
<CardHeader>
|
|
18
|
+
<CardTitle>Drawer</CardTitle>
|
|
19
|
+
<CardDescription>Right-side overlay component.</CardDescription>
|
|
20
|
+
</CardHeader>
|
|
21
|
+
<CardContent>
|
|
22
|
+
<Drawer
|
|
23
|
+
open={drawerOpen}
|
|
24
|
+
onOpenChange={setDrawerOpen}
|
|
25
|
+
trigger={<Button onClick={() => setDrawerOpen(true)}>Open drawer</Button>}
|
|
26
|
+
title="Drawer preview"
|
|
27
|
+
description="Overlay/drawer module rendered from local Tembro code."
|
|
28
|
+
footer={<DrawerCloseButton>Close drawer</DrawerCloseButton>}
|
|
29
|
+
>
|
|
30
|
+
<StatusLegend
|
|
31
|
+
title="Drawer content"
|
|
32
|
+
items={[
|
|
33
|
+
{ key: "one", label: "Overlay API", count: 3, tone: "info" },
|
|
34
|
+
{ key: "two", label: "Dialog base", count: 1, tone: "success" },
|
|
35
|
+
]}
|
|
36
|
+
/>
|
|
37
|
+
</Drawer>
|
|
38
|
+
</CardContent>
|
|
39
|
+
</Card>
|
|
40
|
+
<Card>
|
|
41
|
+
<CardHeader>
|
|
42
|
+
<CardTitle>ConfirmDialog</CardTitle>
|
|
43
|
+
<CardDescription>Controlled confirm modal.</CardDescription>
|
|
44
|
+
</CardHeader>
|
|
45
|
+
<CardContent>
|
|
46
|
+
<ConfirmDialog
|
|
47
|
+
open={confirmOpen}
|
|
48
|
+
onOpenChange={setConfirmOpen}
|
|
49
|
+
trigger={<Button variant="outline" onClick={() => setConfirmOpen(true)}>Open confirm</Button>}
|
|
50
|
+
title="Confirm action"
|
|
51
|
+
description="This is a Tembro ConfirmDialog."
|
|
52
|
+
confirmText="Confirm"
|
|
53
|
+
cancelText="Cancel"
|
|
54
|
+
>
|
|
55
|
+
<p className="text-sm text-muted-foreground">Dialog content area is rendered and styled by the component.</p>
|
|
56
|
+
</ConfirmDialog>
|
|
57
|
+
</CardContent>
|
|
58
|
+
</Card>
|
|
59
|
+
<Alert tone="warning" title="Overlay components" description="alert-dialog, confirm-dialog and drawer are installed. Dialog primitives are under ui/dialog." />
|
|
60
|
+
</div>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { StatusLegend } from "@/components/display/status-legend"
|
|
2
|
+
import { Timeline } from "@/components/display/timeline"
|
|
3
|
+
import { EmptyState } from "@/components/patterns/empty-state"
|
|
4
|
+
|
|
5
|
+
export function PatternsSection() {
|
|
6
|
+
return (
|
|
7
|
+
<div className="grid gap-4 lg:grid-cols-3">
|
|
8
|
+
<EmptyState
|
|
9
|
+
title="EmptyState pattern"
|
|
10
|
+
description="Pattern components are installed: resource pages, settings pages, page toolbar, data view and form builder."
|
|
11
|
+
primaryAction={{ label: "Primary action" }}
|
|
12
|
+
secondaryAction={{ label: "Secondary action", variant: "outline" }}
|
|
13
|
+
/>
|
|
14
|
+
<StatusLegend
|
|
15
|
+
title="StatusLegend"
|
|
16
|
+
description="Display module with counts."
|
|
17
|
+
orientation="grid"
|
|
18
|
+
items={[
|
|
19
|
+
{ key: "ready", label: "Ready", description: "Can be tested", count: 62, tone: "success" },
|
|
20
|
+
{ key: "large", label: "Large components", description: "sidebar, kanban, data-table", count: 8, tone: "info" },
|
|
21
|
+
{ key: "forms", label: "Form components", description: "inputs and shells", count: 18, tone: "warning" },
|
|
22
|
+
{ key: "overlay", label: "Overlay components", description: "dialog, drawer", count: 3, tone: "default" },
|
|
23
|
+
]}
|
|
24
|
+
/>
|
|
25
|
+
<Timeline
|
|
26
|
+
items={[
|
|
27
|
+
{ key: "init", title: "tembro init", description: "Project initialized", time: "done", tone: "success" },
|
|
28
|
+
{ key: "add", title: "tembro add", description: "All registry entries copied", time: "done", tone: "success" },
|
|
29
|
+
{ key: "show", title: "Workbench", description: "Visible app surface", time: "now", tone: "info" },
|
|
30
|
+
]}
|
|
31
|
+
pending
|
|
32
|
+
pendingLabel="Manual component QA"
|
|
33
|
+
/>
|
|
34
|
+
</div>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { CheckCircle2Icon, Layers3Icon, ShieldCheckIcon } from "lucide-react"
|
|
2
|
+
|
|
3
|
+
import { Card, CardContent } from "@/components/ui/card"
|
|
4
|
+
import { moduleCount, registryGroups } from "@/showcase/data/registry"
|
|
5
|
+
import { SectionTitle } from "@/showcase/shared/SectionTitle"
|
|
6
|
+
|
|
7
|
+
export function VerificationSection() {
|
|
8
|
+
return (
|
|
9
|
+
<section>
|
|
10
|
+
<SectionTitle title="Verification" description="Package va project setup holati." />
|
|
11
|
+
<div className="grid gap-4 md:grid-cols-3">
|
|
12
|
+
<Card>
|
|
13
|
+
<CardContent className="flex items-start gap-3 pt-6">
|
|
14
|
+
<CheckCircle2Icon className="mt-1 size-5 text-emerald-600" />
|
|
15
|
+
<div>
|
|
16
|
+
<div className="font-medium">Doctor passed</div>
|
|
17
|
+
<p className="text-sm text-muted-foreground">Tembro theme tokens recognized.</p>
|
|
18
|
+
</div>
|
|
19
|
+
</CardContent>
|
|
20
|
+
</Card>
|
|
21
|
+
<Card>
|
|
22
|
+
<CardContent className="flex items-start gap-3 pt-6">
|
|
23
|
+
<Layers3Icon className="mt-1 size-5 text-blue-600" />
|
|
24
|
+
<div>
|
|
25
|
+
<div className="font-medium">All categories listed</div>
|
|
26
|
+
<p className="text-sm text-muted-foreground">{registryGroups.length} groups and {moduleCount} registry components.</p>
|
|
27
|
+
</div>
|
|
28
|
+
</CardContent>
|
|
29
|
+
</Card>
|
|
30
|
+
<Card>
|
|
31
|
+
<CardContent className="flex items-start gap-3 pt-6">
|
|
32
|
+
<ShieldCheckIcon className="mt-1 size-5 text-amber-600" />
|
|
33
|
+
<div>
|
|
34
|
+
<div className="font-medium">Build checked</div>
|
|
35
|
+
<p className="text-sm text-muted-foreground">Vite starter screen removed.</p>
|
|
36
|
+
</div>
|
|
37
|
+
</CardContent>
|
|
38
|
+
</Card>
|
|
39
|
+
</div>
|
|
40
|
+
</section>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { useState } from "react"
|
|
2
|
+
|
|
3
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
|
4
|
+
import { Stepper } from "@/components/wizard/stepper"
|
|
5
|
+
import { Wizard } from "@/components/wizard/wizard"
|
|
6
|
+
import { wizardSteps } from "@/showcase/data/registry"
|
|
7
|
+
|
|
8
|
+
export function WizardSection() {
|
|
9
|
+
const [step, setStep] = useState("test")
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<div className="grid gap-4 lg:grid-cols-[1fr_0.8fr]">
|
|
13
|
+
<Card>
|
|
14
|
+
<CardHeader>
|
|
15
|
+
<CardTitle>Stepper</CardTitle>
|
|
16
|
+
<CardDescription>Horizontal and clickable stepper.</CardDescription>
|
|
17
|
+
</CardHeader>
|
|
18
|
+
<CardContent>
|
|
19
|
+
<Stepper steps={wizardSteps} currentStep={step} onStepChange={setStep} />
|
|
20
|
+
</CardContent>
|
|
21
|
+
</Card>
|
|
22
|
+
<Card>
|
|
23
|
+
<CardHeader>
|
|
24
|
+
<CardTitle>Wizard</CardTitle>
|
|
25
|
+
<CardDescription>Wizard footer, previous/next and content slot.</CardDescription>
|
|
26
|
+
</CardHeader>
|
|
27
|
+
<CardContent>
|
|
28
|
+
<Wizard
|
|
29
|
+
steps={wizardSteps}
|
|
30
|
+
currentStep={step}
|
|
31
|
+
onStepChange={setStep}
|
|
32
|
+
onPrevious={() => setStep("add")}
|
|
33
|
+
onNext={() => setStep("test")}
|
|
34
|
+
onFinish={() => setStep("test")}
|
|
35
|
+
>
|
|
36
|
+
<div className="rounded-lg border bg-muted/25 p-4 text-sm">
|
|
37
|
+
Current step: <span className="font-semibold">{step}</span>
|
|
38
|
+
</div>
|
|
39
|
+
</Wizard>
|
|
40
|
+
</CardContent>
|
|
41
|
+
</Card>
|
|
42
|
+
</div>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
type SectionTitleProps = {
|
|
2
|
+
title: string
|
|
3
|
+
description: string
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function SectionTitle({ title, description }: SectionTitleProps) {
|
|
7
|
+
return (
|
|
8
|
+
<div className="mb-4">
|
|
9
|
+
<h2 className="text-xl font-semibold tracking-tight">{title}</h2>
|
|
10
|
+
<p className="mt-1 max-w-3xl text-sm leading-6 text-muted-foreground">{description}</p>
|
|
11
|
+
</div>
|
|
12
|
+
)
|
|
13
|
+
}
|