tembro 2.1.12 → 2.1.13

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.
Files changed (49) hide show
  1. package/dist/components/charts/charts.cjs +1 -1
  2. package/dist/components/charts/charts.d.ts +8 -3
  3. package/dist/components/charts/charts.js +79 -51
  4. package/dist/components/charts/horizontal-bar-chart.cjs +1 -1
  5. package/dist/components/charts/horizontal-bar-chart.d.ts +3 -1
  6. package/dist/components/charts/horizontal-bar-chart.js +12 -7
  7. package/dist/components/charts/index.cjs +1 -1
  8. package/dist/components/charts/index.js +5 -5
  9. package/dist/components/charts/public.cjs +1 -1
  10. package/dist/components/charts/public.js +2 -2
  11. package/dist/components/data-table/demo.cjs +1 -1
  12. package/dist/components/data-table/demo.d.ts +2 -6
  13. package/dist/components/data-table/demo.js +27 -3
  14. package/dist/index.cjs +1 -1
  15. package/dist/index.js +41 -41
  16. package/dist/showcase/create-demo.cjs +2 -138
  17. package/dist/showcase/create-demo.js +37 -52
  18. package/dist/showcase/demo-snippets/actions/index.cjs +64 -0
  19. package/dist/showcase/demo-snippets/actions/index.d.ts +1 -0
  20. package/dist/showcase/demo-snippets/actions/index.js +10 -0
  21. package/dist/showcase/demo-snippets/forms/index.cjs +26 -0
  22. package/dist/showcase/demo-snippets/forms/index.d.ts +1 -0
  23. package/dist/showcase/demo-snippets/forms/index.js +9 -0
  24. package/dist/showcase/demo-snippets/index.cjs +1 -0
  25. package/dist/showcase/demo-snippets/index.d.ts +1 -0
  26. package/dist/showcase/demo-snippets/index.js +13 -0
  27. package/dist/showcase/demo-snippets/layout/index.cjs +45 -0
  28. package/dist/showcase/demo-snippets/layout/index.d.ts +1 -0
  29. package/dist/showcase/demo-snippets/layout/index.js +9 -0
  30. package/dist/showcase/demo-snippets/upload/index.cjs +5 -0
  31. package/dist/showcase/demo-snippets/upload/index.d.ts +1 -0
  32. package/dist/showcase/demo-snippets/upload/index.js +4 -0
  33. package/dist/showcase/premium/index.cjs +1 -1
  34. package/dist/showcase/premium/index.js +278 -278
  35. package/dist/showcase/tembro-registry.json.cjs +1 -1
  36. package/dist/showcase/tembro-registry.json.js +1 -1
  37. package/package.json +1 -1
  38. package/packages/cli/dist/index.cjs +36 -36
  39. package/packages/cli/vendor/src/components/charts/charts.tsx +59 -22
  40. package/packages/cli/vendor/src/components/charts/horizontal-bar-chart.tsx +34 -20
  41. package/packages/cli/vendor/src/components/data-table/demo.tsx +31 -2
  42. package/packages/cli/vendor/src/showcase/create-demo.tsx +8 -160
  43. package/packages/cli/vendor/src/showcase/demo-snippets/actions/index.ts +70 -0
  44. package/packages/cli/vendor/src/showcase/demo-snippets/forms/index.ts +31 -0
  45. package/packages/cli/vendor/src/showcase/demo-snippets/index.ts +11 -0
  46. package/packages/cli/vendor/src/showcase/demo-snippets/layout/index.ts +50 -0
  47. package/packages/cli/vendor/src/showcase/demo-snippets/upload/index.ts +7 -0
  48. package/packages/cli/vendor/src/showcase/tembro-registry.json +1 -1
  49. package/registry.json +1 -1
@@ -40,6 +40,14 @@ function normalizeValue(value: number, max: number) {
40
40
  function getChartColor(index: number, custom?: string) {
41
41
  return custom ?? `var(--color-chart-${(index % 5) + 1}, var(--primary))`
42
42
  }
43
+
44
+ function formatChartValue(value: number, formatter?: (value: number) => React.ReactNode) {
45
+ return formatter ? formatter(value) : value.toLocaleString()
46
+ }
47
+
48
+ function chartLabelToTitle(label: React.ReactNode) {
49
+ return typeof label === "string" || typeof label === "number" ? String(label) : undefined
50
+ }
43
51
 
44
52
  function polarToCartesian(cx: number, cy: number, radius: number, angle: number) {
45
53
  const radians = ((angle - 90) * Math.PI) / 180
@@ -117,18 +125,19 @@ function ChartFrame({ title, description, action, state = "ready", emptyLabel =
117
125
  )
118
126
  }
119
127
 
120
- export type BarChartProps = React.ComponentProps<"div"> & {
121
- data: ChartDatum[]
128
+ export type BarChartProps = React.ComponentProps<"div"> & {
129
+ data: ChartDatum[]
122
130
  size?: ChartSize
123
131
  max?: number
124
132
  showLabels?: boolean
125
133
  showValues?: boolean
134
+ valueFormatter?: (value: number) => React.ReactNode
126
135
  barClassName?: string
127
136
  state?: ChartState
128
137
  emptyLabel?: React.ReactNode
129
138
  }
130
139
 
131
- function BarChart({ data, size = "md", max, showLabels = true, showValues = true, state = "ready", emptyLabel = "No chart data.", className, barClassName, ...props }: BarChartProps) {
140
+ function BarChart({ data, size = "md", max, showLabels = true, showValues = true, valueFormatter, state = "ready", emptyLabel = "No chart data.", className, barClassName, ...props }: BarChartProps) {
132
141
  const values = data.map((item) => item.value)
133
142
  const absoluteMax = max ?? safeMax(values.map((value) => Math.abs(value)))
134
143
  const height = chartHeightBySize[size]
@@ -147,9 +156,10 @@ function BarChart({ data, size = "md", max, showLabels = true, showValues = true
147
156
  {data.map((item, index) => {
148
157
  const ratio = normalizeValue(Math.abs(item.value), absoluteMax)
149
158
  const negative = item.value < 0
159
+ const title = chartLabelToTitle(item.label)
150
160
  return (
151
- <div key={index} className="flex min-w-0 flex-1 flex-col items-center gap-2">
152
- {showValues && <div className="text-xs text-muted-foreground">{item.value}</div>}
161
+ <div key={index} className="flex min-w-0 flex-1 flex-col items-center gap-2" title={title ? `${title}: ${item.value}` : undefined}>
162
+ {showValues && <div className="text-xs font-medium text-muted-foreground">{formatChartValue(item.value, valueFormatter)}</div>}
153
163
  <div className="flex w-full flex-1 items-end rounded-[min(var(--radius-xl),16px)] border border-border/60 bg-muted/38 p-1">
154
164
  <div
155
165
  className={cn("w-full rounded-[min(var(--radius-lg),12px)] bg-primary transition-all", barClassName)}
@@ -170,15 +180,19 @@ export type LineChartProps = Omit<React.ComponentProps<"svg">, "values"> & {
170
180
  size?: ChartSize
171
181
  width?: number
172
182
  showArea?: boolean
183
+ showGrid?: boolean
184
+ labels?: ChartAxisLabel[]
185
+ valueFormatter?: (value: number) => React.ReactNode
173
186
  stroke?: string
174
187
  state?: ChartState
175
188
  emptyLabel?: React.ReactNode
176
189
  }
177
190
 
178
- function LineChart({ values, size = "md", width = 560, showArea = false, stroke = "var(--primary)", state = "ready", emptyLabel = "No line data.", className, ...props }: LineChartProps) {
191
+ function LineChart({ values, size = "md", width = 560, showArea = false, showGrid = true, labels, valueFormatter, stroke = "var(--primary)", state = "ready", emptyLabel = "No line data.", className, ...props }: LineChartProps) {
179
192
  const height = chartHeightBySize[size]
180
193
  const linePath = buildLinePath(values, width, height)
181
194
  const areaPath = buildAreaPath(values, width, height)
195
+ const max = safeMax(values)
182
196
 
183
197
  if (state === "loading") {
184
198
  return <div className={cn("h-36 animate-pulse rounded-[min(var(--radius-xl),16px)] bg-muted/70", className)} />
@@ -189,18 +203,39 @@ function LineChart({ values, size = "md", width = 560, showArea = false, stroke
189
203
  }
190
204
 
191
205
  return (
192
- <svg data-slot="line-chart" viewBox={`0 0 ${width} ${height}`} className={cn("h-auto w-full overflow-visible", className)} role="img" {...props}>
193
- {showArea && <path d={areaPath} fill="var(--primary)" opacity="0.12" />}
194
- <path d={linePath} fill="none" stroke={stroke} strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" />
195
- {values.map((value, index) => {
196
- const max = safeMax(values)
197
- const x = 12 + (values.length === 1 ? (width - 24) / 2 : (index / (values.length - 1)) * (width - 24))
198
- const y = 12 + (1 - normalizeValue(value, max)) * (height - 24)
199
- return <circle key={index} cx={x} cy={y} r="3" fill={stroke} />
200
- })}
201
- </svg>
202
- )
203
- }
206
+ <svg data-slot="line-chart" viewBox={`0 0 ${width} ${height}`} className={cn("h-auto w-full overflow-visible", className)} role="img" {...props}>
207
+ {showGrid && [0.25, 0.5, 0.75].map((position) => (
208
+ <line
209
+ key={position}
210
+ x1="12"
211
+ x2={width - 12}
212
+ y1={12 + position * (height - 24)}
213
+ y2={12 + position * (height - 24)}
214
+ stroke="var(--border)"
215
+ strokeDasharray="4 6"
216
+ opacity="0.7"
217
+ />
218
+ ))}
219
+ {showArea && <path d={areaPath} fill="var(--primary)" opacity="0.12" />}
220
+ <path d={linePath} fill="none" stroke={stroke} strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" />
221
+ {values.map((value, index) => {
222
+ const x = 12 + (values.length === 1 ? (width - 24) / 2 : (index / (values.length - 1)) * (width - 24))
223
+ const y = 12 + (1 - normalizeValue(value, max)) * (height - 24)
224
+ const label = labels?.[index]
225
+ const title = label != null ? `${chartLabelToTitle(label) ?? "Value"}: ${value}` : `${value}`
226
+ return (
227
+ <circle key={index} cx={x} cy={y} r="3.5" fill={stroke}>
228
+ <title>{valueFormatter ? `${label ?? "Value"}: ${formatChartValue(value, valueFormatter)}` : title}</title>
229
+ </circle>
230
+ )
231
+ })}
232
+ </svg>
233
+ )
234
+ }
235
+
236
+ function AreaChart(props: Omit<LineChartProps, "showArea">) {
237
+ return <LineChart showArea {...props} />
238
+ }
204
239
 
205
240
  export type SparklineProps = Omit<LineChartProps, "size" | "showArea"> & {
206
241
  values: number[]
@@ -262,9 +297,11 @@ function DonutChart({ data, size = 180, strokeWidth = 18, centerLabel, centerVal
262
297
  stroke={getChartColor(index, item.color)}
263
298
  strokeWidth={strokeWidth}
264
299
  strokeLinecap="round"
265
- />
266
- )
267
- })}
300
+ >
301
+ <title>{`${chartLabelToTitle(item.label) ?? "Segment"}: ${item.value}`}</title>
302
+ </path>
303
+ )
304
+ })}
268
305
  {(centerValue || centerLabel) && (
269
306
  <text x="50%" y="50%" textAnchor="middle" dominantBaseline="middle" fill="var(--foreground)">
270
307
  {centerValue && <tspan x="50%" dy={centerLabel ? "-0.2em" : "0"} className="text-lg font-semibold">{centerValue}</tspan>}
@@ -326,4 +363,4 @@ function MetricTrend({ label, value, change, positive = true, values, className,
326
363
  )
327
364
  }
328
365
 
329
- export { BarChart, ChartFrame, ChartLegend, DonutChart, LineChart, MetricTrend, Sparkline }
366
+ export { AreaChart, BarChart, ChartFrame, ChartLegend, DonutChart, LineChart, MetricTrend, Sparkline }
@@ -10,31 +10,45 @@ export type HorizontalBarDatum = {
10
10
  color?: string
11
11
  }
12
12
 
13
- export type HorizontalBarChartProps = React.ComponentProps<"div"> & {
14
- data: HorizontalBarDatum[]
15
- max?: number
16
- showValue?: boolean
17
- }
18
-
19
- function HorizontalBarChart({ data, max, showValue = true, className, ...props }: HorizontalBarChartProps) {
20
- const resolvedMax = max ?? Math.max(...data.map((item) => item.value), 1)
21
-
22
- return (
23
- <div data-slot="horizontal-bar-chart" className={cn("grid gap-3", className)} {...props}>
24
- {data.map((item) => {
25
- const width = resolvedMax > 0 ? Math.max(0, Math.min((item.value / resolvedMax) * 100, 100)) : 0
13
+ export type HorizontalBarChartProps = React.ComponentProps<"div"> & {
14
+ data: HorizontalBarDatum[]
15
+ max?: number
16
+ showValue?: boolean
17
+ emptyLabel?: React.ReactNode
18
+ valueFormatter?: (value: number) => React.ReactNode
19
+ }
20
+
21
+ function HorizontalBarChart({ data, max, showValue = true, emptyLabel = "No ranking data.", valueFormatter, className, ...props }: HorizontalBarChartProps) {
22
+ const resolvedMax = max ?? Math.max(...data.map((item) => item.value), 1)
23
+
24
+ if (data.length === 0) {
25
+ return (
26
+ <div
27
+ data-slot="horizontal-bar-chart"
28
+ className={cn("rounded-[min(var(--radius-xl),16px)] border border-dashed border-border/70 px-4 py-10 text-center text-sm text-muted-foreground", className)}
29
+ {...props}
30
+ >
31
+ {emptyLabel}
32
+ </div>
33
+ )
34
+ }
35
+
36
+ return (
37
+ <div data-slot="horizontal-bar-chart" className={cn("grid gap-3", className)} {...props}>
38
+ {data.map((item) => {
39
+ const width = resolvedMax > 0 ? Math.max(0, Math.min((item.value / resolvedMax) * 100, 100)) : 0
26
40
 
27
41
  return (
28
42
  <div key={item.key} className="grid gap-1.5">
29
43
  <div className="flex items-center justify-between gap-3 text-sm">
30
44
  <div className="min-w-0">
31
- <div className="truncate font-medium text-foreground">{item.label}</div>
32
- {item.description && <div className="truncate text-xs text-muted-foreground">{item.description}</div>}
33
- </div>
34
- {showValue && <div className="text-xs font-medium text-muted-foreground">{item.value}</div>}
35
- </div>
36
- <div className="h-2 overflow-hidden rounded-full bg-muted">
37
- <div className="h-full rounded-full bg-primary" style={{ width: `${width}%`, background: item.color }} />
45
+ <div className="truncate font-medium text-foreground">{item.label}</div>
46
+ {item.description && <div className="truncate text-xs text-muted-foreground">{item.description}</div>}
47
+ </div>
48
+ {showValue && <div className="text-xs font-medium text-muted-foreground">{valueFormatter ? valueFormatter(item.value) : item.value.toLocaleString()}</div>}
49
+ </div>
50
+ <div className="h-2 overflow-hidden rounded-full bg-muted">
51
+ <div className="h-full rounded-full bg-primary" style={{ width: `${width}%`, background: item.color }} />
38
52
  </div>
39
53
  </div>
40
54
  )
@@ -1,3 +1,32 @@
1
- import { createShowcaseDemoRegistry } from "@/showcase/create-demo"
1
+ import { DataTableShowcase, dataTableMock } from "@/showcase/premium/data-table"
2
+ import {
3
+ DataTablePartShowcase,
4
+ dataTableActionsColumnMock,
5
+ dataTablePaginationMock,
6
+ dataTableRowActionsMock,
7
+ dataTableSavedFiltersMock,
8
+ dataTableSelectColumnMock,
9
+ dataTableToolbarMock,
10
+ } from "@/showcase/premium/data-table-parts"
2
11
 
3
- export const dataTableShowcaseDemoRegistry = createShowcaseDemoRegistry([])
12
+ import type { ComponentDemoBundle } from "@/showcase/premium/types"
13
+
14
+ function createPartBundle(
15
+ slug: "data-table-pagination" | "data-table-toolbar" | "data-table-row-actions" | "data-table-actions-column" | "data-table-select-column" | "data-table-saved-filters",
16
+ mock: ComponentDemoBundle["mock"]
17
+ ): ComponentDemoBundle {
18
+ return {
19
+ mock,
20
+ Showcase: (props) => <DataTablePartShowcase {...props} slug={slug} />,
21
+ }
22
+ }
23
+
24
+ export const dataTableShowcaseDemoRegistry: Record<string, ComponentDemoBundle> = {
25
+ "data-table": { mock: dataTableMock, Showcase: DataTableShowcase },
26
+ "data-table-pagination": createPartBundle("data-table-pagination", dataTablePaginationMock),
27
+ "data-table-toolbar": createPartBundle("data-table-toolbar", dataTableToolbarMock),
28
+ "data-table-row-actions": createPartBundle("data-table-row-actions", dataTableRowActionsMock),
29
+ "data-table-actions-column": createPartBundle("data-table-actions-column", dataTableActionsColumnMock),
30
+ "data-table-select-column": createPartBundle("data-table-select-column", dataTableSelectColumnMock),
31
+ "data-table-saved-filters": createPartBundle("data-table-saved-filters", dataTableSavedFiltersMock),
32
+ }
@@ -1,5 +1,6 @@
1
- import type { ShowcaseDemoBundle, ShowcaseDemoDefinition, ShowcaseDemoMock, ShowcaseDemoProps } from "./types"
2
- import { renderShowcasePreview } from "./render-registry-preview"
1
+ import type { ShowcaseDemoBundle, ShowcaseDemoDefinition, ShowcaseDemoMock, ShowcaseDemoProps } from "./types"
2
+ import { renderShowcasePreview } from "./render-registry-preview"
3
+ import { demoCodeSnippets } from "./demo-snippets"
3
4
 
4
5
  export function component(
5
6
  slug: string,
@@ -57,165 +58,12 @@ function createMock(definition: ShowcaseDemoDefinition): ShowcaseDemoMock {
57
58
  }
58
59
  }
59
60
 
60
- function createCodeSnippet(definition: ShowcaseDemoDefinition) {
61
- return exactCodeSnippets[definition.slug]
62
- ?? createKindSnippet(definition)
63
- }
64
-
65
- const exactCodeSnippets: Record<string, string> = {
66
- "search-input": `import { Input } from "tembro"
67
-
68
- export function Demo() {
69
- return (
70
- <Input
71
- type="search"
72
- value="invoice"
73
- onValueChange={(value) => console.log(value)}
74
- placeholder="Search invoices..."
75
- resultCount={12}
76
- shortcut="Ctrl K"
77
- />
78
- )
79
- }`,
80
- "password-input": `import { PasswordInput } from "tembro"
81
-
82
- export function Demo() {
83
- return <PasswordInput placeholder="Enter secure token" autoComplete="current-password" />
84
- }`,
85
- "clearable-input": `import { Input } from "tembro"
86
-
87
- export function Demo() {
88
- return <Input defaultValue="Azamat UI" placeholder="Search customer" clearable />
89
- }`,
90
- "tag-input": `import { TagInput } from "tembro"
91
-
92
- export function Demo() {
93
- return <TagInput defaultValue={["dashboard", "billing"]} placeholder="Add tag" />
94
- }`,
95
- "action-menu": `import { ActionMenu, Button } from "tembro"
96
-
97
- export function Demo() {
98
- return (
99
- <ActionMenu
100
- label="Row actions"
101
- actions={[
102
- { key: "open", label: "Open" },
103
- { key: "duplicate", label: "Duplicate" },
104
- { key: "archive", label: "Archive", destructive: true },
105
- ]}
106
- trigger={<Button variant="outline">Actions</Button>}
107
- />
108
- )
109
- }`,
110
- "button-group": `import { ButtonGroup } from "tembro"
111
-
112
- export function Demo() {
113
- return (
114
- <ButtonGroup
115
- items={[
116
- { key: "day", label: "Day" },
117
- { key: "week", label: "Week" },
118
- { key: "month", label: "Month" },
119
- ]}
120
- />
121
- )
122
- }`,
123
- "quick-action-grid": `import { QuickActionGrid } from "tembro"
124
-
125
- export function Demo() {
126
- return (
127
- <QuickActionGrid
128
- columns={3}
129
- items={[
130
- { key: "new", label: "New invoice", description: "Create a billing row." },
131
- { key: "import", label: "Import CSV", description: "Upload operational data." },
132
- { key: "share", label: "Share", description: "Invite a teammate." },
133
- ]}
134
- />
135
- )
136
- }`,
137
- "filter-bar": `import { Button, FilterBar, Input } from "tembro"
138
-
139
- export function Demo() {
140
- return (
141
- <FilterBar
142
- search={<Input type="search" value="" placeholder="Search rows..." readOnly />}
143
- chips={[
144
- { key: "status", label: "Status", value: "Active", tone: "success" },
145
- { key: "owner", label: "Owner", value: "Azamat" },
146
- ]}
147
- filters={<Button variant="outline">Status</Button>}
148
- actions={<Button>Export</Button>}
149
- onReset={() => undefined}
150
- />
151
- )
152
- }`,
153
- "description-list": `import { DescriptionList } from "tembro"
154
-
155
- export function Demo() {
156
- return (
157
- <DescriptionList
158
- title="Invoice details"
159
- items={[
160
- { key: "id", label: "Invoice", value: "#4821" },
161
- { key: "amount", label: "Amount", value: "$12,420" },
162
- { key: "status", label: "Status", value: "Paid" },
163
- ]}
164
- />
165
- )
166
- }`,
167
- "stat-card": `import { StatCard } from "tembro"
168
-
169
- export function Demo() {
170
- return (
171
- <StatCard
172
- title="Revenue"
173
- value="$84.2k"
174
- description="Compared with last month"
175
- trend={{ value: "+12.4%", tone: "success" }}
176
- helperText="Updated just now"
177
- />
178
- )
179
- }`,
180
- "pagination": `import { Pagination } from "tembro"
181
-
182
- export function Demo() {
183
- return <Pagination page={3} pageCount={9} onPageChange={(page) => console.log(page)} />
184
- }`,
185
- "dialog-actions": `import { DialogActionButton, DialogActions } from "tembro"
186
-
187
- export function Demo() {
188
- return (
189
- <DialogActions align="end">
190
- <DialogActionButton variant="ghost">Cancel</DialogActionButton>
191
- <DialogActionButton variant="outline">Save draft</DialogActionButton>
192
- <DialogActionButton>Publish</DialogActionButton>
193
- </DialogActions>
194
- )
195
- }`,
196
- "file-dropzone": `import { FileDropzone } from "tembro"
197
-
198
- export function Demo() {
199
- return <FileDropzone label="Drop contract files" description="PDF, PNG or CSV up to 10MB." />
200
- }`,
201
- "stepper": `import { Stepper } from "tembro"
202
-
203
- export function Demo() {
204
- return (
205
- <Stepper
206
- currentStep="billing"
207
- steps={[
208
- { id: "profile", title: "Profile", completed: true },
209
- { id: "billing", title: "Billing" },
210
- { id: "review", title: "Review" },
211
- ]}
212
- onStepChange={() => undefined}
213
- />
214
- )
215
- }`,
216
- }
61
+ function createCodeSnippet(definition: ShowcaseDemoDefinition) {
62
+ return demoCodeSnippets[definition.slug]
63
+ ?? createKindSnippet(definition)
64
+ }
217
65
 
218
- function createKindSnippet(definition: ShowcaseDemoDefinition) {
66
+ function createKindSnippet(definition: ShowcaseDemoDefinition) {
219
67
  const importName = definition.importName ?? definition.component
220
68
  const importPath = "tembro"
221
69
 
@@ -0,0 +1,70 @@
1
+ export const actionDemoCodeSnippets: Record<string, string> = {
2
+ "action-menu": `import { ActionMenu, Button } from "tembro"
3
+
4
+ export function Demo() {
5
+ return (
6
+ <ActionMenu
7
+ label="Row actions"
8
+ actions={[
9
+ { key: "open", label: "Open" },
10
+ { key: "duplicate", label: "Duplicate" },
11
+ { key: "archive", label: "Archive", destructive: true },
12
+ ]}
13
+ trigger={<Button variant="outline">Actions</Button>}
14
+ />
15
+ )
16
+ }`,
17
+ "button-group": `import { ButtonGroup } from "tembro"
18
+
19
+ export function Demo() {
20
+ return (
21
+ <ButtonGroup
22
+ items={[
23
+ { key: "day", label: "Day" },
24
+ { key: "week", label: "Week" },
25
+ { key: "month", label: "Month" },
26
+ ]}
27
+ />
28
+ )
29
+ }`,
30
+ "quick-action-grid": `import { QuickActionGrid } from "tembro"
31
+
32
+ export function Demo() {
33
+ return (
34
+ <QuickActionGrid
35
+ columns={3}
36
+ items={[
37
+ { key: "new", label: "New invoice", description: "Create a billing row." },
38
+ { key: "import", label: "Import CSV", description: "Upload operational data." },
39
+ { key: "share", label: "Share", description: "Invite a teammate." },
40
+ ]}
41
+ />
42
+ )
43
+ }`,
44
+ "dialog-actions": `import { DialogActionButton, DialogActions } from "tembro"
45
+
46
+ export function Demo() {
47
+ return (
48
+ <DialogActions align="end">
49
+ <DialogActionButton variant="ghost">Cancel</DialogActionButton>
50
+ <DialogActionButton variant="outline">Save draft</DialogActionButton>
51
+ <DialogActionButton>Publish</DialogActionButton>
52
+ </DialogActions>
53
+ )
54
+ }`,
55
+ stepper: `import { Stepper } from "tembro"
56
+
57
+ export function Demo() {
58
+ return (
59
+ <Stepper
60
+ currentStep="billing"
61
+ steps={[
62
+ { id: "profile", title: "Profile", completed: true },
63
+ { id: "billing", title: "Billing" },
64
+ { id: "review", title: "Review" },
65
+ ]}
66
+ onStepChange={() => undefined}
67
+ />
68
+ )
69
+ }`,
70
+ }
@@ -0,0 +1,31 @@
1
+ export const formDemoCodeSnippets: Record<string, string> = {
2
+ "search-input": `import { Input } from "tembro"
3
+
4
+ export function Demo() {
5
+ return (
6
+ <Input
7
+ type="search"
8
+ value="invoice"
9
+ onValueChange={(value) => console.log(value)}
10
+ placeholder="Search invoices..."
11
+ resultCount={12}
12
+ shortcut="Ctrl K"
13
+ />
14
+ )
15
+ }`,
16
+ "password-input": `import { PasswordInput } from "tembro"
17
+
18
+ export function Demo() {
19
+ return <PasswordInput placeholder="Enter secure token" autoComplete="current-password" />
20
+ }`,
21
+ "clearable-input": `import { Input } from "tembro"
22
+
23
+ export function Demo() {
24
+ return <Input defaultValue="Azamat UI" placeholder="Search customer" clearable />
25
+ }`,
26
+ "tag-input": `import { TagInput } from "tembro"
27
+
28
+ export function Demo() {
29
+ return <TagInput defaultValue={["dashboard", "billing"]} placeholder="Add tag" />
30
+ }`,
31
+ }
@@ -0,0 +1,11 @@
1
+ import { actionDemoCodeSnippets } from "./actions"
2
+ import { formDemoCodeSnippets } from "./forms"
3
+ import { layoutDemoCodeSnippets } from "./layout"
4
+ import { uploadDemoCodeSnippets } from "./upload"
5
+
6
+ export const demoCodeSnippets: Record<string, string> = {
7
+ ...actionDemoCodeSnippets,
8
+ ...formDemoCodeSnippets,
9
+ ...layoutDemoCodeSnippets,
10
+ ...uploadDemoCodeSnippets,
11
+ }
@@ -0,0 +1,50 @@
1
+ export const layoutDemoCodeSnippets: Record<string, string> = {
2
+ "filter-bar": `import { Button, FilterBar, Input } from "tembro"
3
+
4
+ export function Demo() {
5
+ return (
6
+ <FilterBar
7
+ search={<Input type="search" value="" placeholder="Search rows..." readOnly />}
8
+ chips={[
9
+ { key: "status", label: "Status", value: "Active", tone: "success" },
10
+ { key: "owner", label: "Owner", value: "Azamat" },
11
+ ]}
12
+ filters={<Button variant="outline">Status</Button>}
13
+ actions={<Button>Export</Button>}
14
+ onReset={() => undefined}
15
+ />
16
+ )
17
+ }`,
18
+ "description-list": `import { DescriptionList } from "tembro"
19
+
20
+ export function Demo() {
21
+ return (
22
+ <DescriptionList
23
+ title="Invoice details"
24
+ items={[
25
+ { key: "id", label: "Invoice", value: "#4821" },
26
+ { key: "amount", label: "Amount", value: "$12,420" },
27
+ { key: "status", label: "Status", value: "Paid" },
28
+ ]}
29
+ />
30
+ )
31
+ }`,
32
+ "stat-card": `import { StatCard } from "tembro"
33
+
34
+ export function Demo() {
35
+ return (
36
+ <StatCard
37
+ title="Revenue"
38
+ value="$84.2k"
39
+ description="Compared with last month"
40
+ trend={{ value: "+12.4%", tone: "success" }}
41
+ helperText="Updated just now"
42
+ />
43
+ )
44
+ }`,
45
+ pagination: `import { Pagination } from "tembro"
46
+
47
+ export function Demo() {
48
+ return <Pagination page={3} pageCount={9} onPageChange={(page) => console.log(page)} />
49
+ }`,
50
+ }
@@ -0,0 +1,7 @@
1
+ export const uploadDemoCodeSnippets: Record<string, string> = {
2
+ "file-dropzone": `import { FileDropzone } from "tembro"
3
+
4
+ export function Demo() {
5
+ return <FileDropzone label="Drop contract files" description="PDF, PNG or CSV up to 10MB." />
6
+ }`,
7
+ }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://tembro.dev/registry.schema.json",
3
3
  "name": "tembro",
4
- "version": "2.1.12",
4
+ "version": "2.1.13",
5
5
  "description": "Reusable React + TypeScript UI kit registry",
6
6
  "groups": {
7
7
  "ui": [
package/registry.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://tembro.dev/registry.schema.json",
3
3
  "name": "tembro",
4
- "version": "2.1.12",
4
+ "version": "2.1.13",
5
5
  "description": "Reusable React + TypeScript UI kit registry",
6
6
  "groups": {
7
7
  "ui": [