velloo 0.0.0 → 0.2.0
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/BUN-LICENSE.md +91 -0
- package/LICENSE +202 -0
- package/NOTICE +9 -0
- package/README.md +288 -2
- package/THIRD-PARTY-NOTICES.md +467 -0
- package/assets/elsewhere-alps-9fqhv029.jpg +0 -0
- package/assets/elsewhere-bali-vp60g115.jpg +0 -0
- package/assets/elsewhere-forest-xgn23zrj.jpg +0 -0
- package/assets/elsewhere-japan-qs7qkbr2.jpg +0 -0
- package/assets/elsewhere-kyoto-map-n2bvkrya.svg +1 -0
- package/assets/elsewhere-kyoto-qemtn6nk.jpg +0 -0
- package/assets/elsewhere-room-g1gaw7cs.jpg +0 -0
- package/canvas/apple-touch-icon.png +0 -0
- package/canvas/assets/index-BCvDZiBo.css +2 -0
- package/canvas/assets/index-ClaBqcaS.js +28 -0
- package/canvas/assets/lucide-all-iIF-pmzN.js +1 -0
- package/canvas/assets/radix-C3HaOB-d.js +41 -0
- package/canvas/assets/react-D0Ec-185.js +9 -0
- package/canvas/assets/rolldown-runtime-hePW80VL.js +1 -0
- package/canvas/assets/vendor-CKmQ-Zst.js +53 -0
- package/canvas/favicon.ico +0 -0
- package/canvas/favicon.svg +12 -0
- package/canvas/fonts/OFL-1.1.txt +93 -0
- package/canvas/fonts/poppins-700.woff2 +0 -0
- package/canvas/icon-192.png +0 -0
- package/canvas/icon-512.png +0 -0
- package/canvas/index.html +22 -0
- package/canvas/site.webmanifest +11 -0
- package/chunk-08sv3k41.js +4 -0
- package/chunk-0fe83yzt.js +2 -0
- package/chunk-0t68szgr.js +3 -0
- package/chunk-1h55s5he.js +2 -0
- package/chunk-2c7jbx46.js +2 -0
- package/chunk-2smsg2ct.js +7 -0
- package/chunk-308ynsxd.js +2 -0
- package/chunk-3e8vez4d.js +1229 -0
- package/chunk-40xgcth8.js +63 -0
- package/chunk-4dn75m6t.js +3 -0
- package/chunk-4fj5tahv.js +3 -0
- package/chunk-6nqghjda.js +3 -0
- package/chunk-6tx68d90.js +3 -0
- package/chunk-6w6ch4ce.js +2 -0
- package/chunk-735ezz23.js +86 -0
- package/chunk-7eq7nx37.js +7 -0
- package/chunk-7eyb8yrf.js +6 -0
- package/chunk-80fjfyha.js +86 -0
- package/chunk-80r93t2e.js +4 -0
- package/chunk-94vx1yb0.js +3 -0
- package/chunk-9vg3yafz.js +3 -0
- package/chunk-a84n72m2.js +4 -0
- package/chunk-ampmdzz4.js +10 -0
- package/chunk-avns4dbf.js +2 -0
- package/chunk-ayrb3qnm.js +3 -0
- package/chunk-bwnvcncx.js +3 -0
- package/chunk-ccszg02q.js +5 -0
- package/chunk-cvn689d5.js +3 -0
- package/chunk-cxe6jjwj.js +3 -0
- package/chunk-dg6z4vdd.js +3 -0
- package/chunk-dgwjpbp9.js +3 -0
- package/chunk-dy41n60j.js +52 -0
- package/chunk-ek71z2fv.js +226 -0
- package/chunk-er1fxb7d.js +2 -0
- package/chunk-eyr8hrzf.js +2 -0
- package/chunk-fjbzk4sw.js +3 -0
- package/chunk-fsbsksga.js +3 -0
- package/chunk-h6gchare.js +3 -0
- package/chunk-hcb68zt7.js +85 -0
- package/chunk-hggap8fq.js +3 -0
- package/chunk-j11fyeqh.js +2 -0
- package/chunk-jb21jkp1.js +8 -0
- package/chunk-jfx9xjmr.js +18 -0
- package/chunk-jr0ss0mm.js +303 -0
- package/chunk-jrsknzjt.js +3 -0
- package/chunk-jv0xggh0.js +2 -0
- package/chunk-k17gfwq3.js +2 -0
- package/chunk-mh2pr7cq.js +5 -0
- package/chunk-mnapdp1y.js +2 -0
- package/chunk-mzhdrawd.js +3 -0
- package/chunk-n9gs6yx9.js +2 -0
- package/chunk-ngnymg2q.js +3 -0
- package/chunk-nwx5700b.js +3 -0
- package/chunk-pjtg3wnm.js +4 -0
- package/chunk-pp86v6m6.js +3 -0
- package/chunk-r22epxz2.js +3 -0
- package/chunk-rgasy862.js +5 -0
- package/chunk-rpwpeph5.js +3 -0
- package/chunk-s23gtejf.js +3 -0
- package/chunk-sjgqnykd.js +6 -0
- package/chunk-sn3x2m0m.js +6 -0
- package/chunk-t448rrfr.js +2 -0
- package/chunk-tdsk5wxa.js +451 -0
- package/chunk-v40fn08f.js +442 -0
- package/chunk-vbk1qhdz.js +3 -0
- package/chunk-vdkrbzh1.js +2 -0
- package/chunk-vgmr98cv.js +40 -0
- package/chunk-vsv8z729.js +6 -0
- package/chunk-x4f4k9pp.js +26 -0
- package/chunk-xt5ayg7g.js +4 -0
- package/chunk-ydhwv29b.js +244 -0
- package/chunk-zb6hc1y0.js +2 -0
- package/cli.js +3 -0
- package/launcher.cjs +97 -0
- package/package.json +58 -5
- package/pkgs/helpers/src/box.tsx +21 -0
- package/pkgs/helpers/src/cn.ts +32 -0
- package/pkgs/helpers/src/descriptors.ts +270 -0
- package/pkgs/helpers/src/divider.tsx +123 -0
- package/pkgs/helpers/src/gradient.tsx +90 -0
- package/pkgs/helpers/src/heading.tsx +20 -0
- package/pkgs/helpers/src/icon-data.ts +8009 -0
- package/pkgs/helpers/src/icon.tsx +53 -0
- package/pkgs/helpers/src/image.tsx +111 -0
- package/pkgs/helpers/src/index.ts +29 -0
- package/pkgs/helpers/src/layer.tsx +61 -0
- package/pkgs/helpers/src/lowering.ts +53 -0
- package/pkgs/helpers/src/paths.ts +47 -0
- package/pkgs/helpers/src/placeholder.tsx +88 -0
- package/pkgs/helpers/src/prose.tsx +42 -0
- package/pkgs/helpers/src/registry.ts +41 -0
- package/pkgs/helpers/src/svg.tsx +62 -0
- package/pkgs/helpers/src/text.tsx +16 -0
- package/pkgs/provider-antd/src/index.ts +78 -0
- package/pkgs/provider-antd/src/intro.ts +14 -0
- package/pkgs/provider-antd/src/manifest.ts +439 -0
- package/pkgs/provider-antd/src/overlays.ts +176 -0
- package/pkgs/provider-antd/src/registry.ts +104 -0
- package/pkgs/provider-antd/src/render-pass.ts +43 -0
- package/pkgs/provider-antd/src/tailwind-entry.css +21 -0
- package/pkgs/provider-antd/src/theme.ts +132 -0
- package/pkgs/provider-antd/src/version.ts +8 -0
- package/pkgs/provider-chakra/src/index.ts +80 -0
- package/pkgs/provider-chakra/src/intro.ts +14 -0
- package/pkgs/provider-chakra/src/manifest.ts +421 -0
- package/pkgs/provider-chakra/src/overlays.ts +246 -0
- package/pkgs/provider-chakra/src/registry.ts +227 -0
- package/pkgs/provider-chakra/src/render-pass.ts +55 -0
- package/pkgs/provider-chakra/src/tailwind-entry.css +21 -0
- package/pkgs/provider-chakra/src/theme.ts +180 -0
- package/pkgs/provider-chakra/src/version.ts +8 -0
- package/pkgs/provider-mui/src/index.ts +117 -0
- package/pkgs/provider-mui/src/intro.ts +12 -0
- package/pkgs/provider-mui/src/manifest.ts +263 -0
- package/pkgs/provider-mui/src/overlays.ts +124 -0
- package/pkgs/provider-mui/src/registry.ts +120 -0
- package/pkgs/provider-mui/src/render-pass.ts +36 -0
- package/pkgs/provider-mui/src/tailwind-entry.css +21 -0
- package/pkgs/provider-mui/src/theme.ts +129 -0
- package/pkgs/provider-mui/src/version.ts +8 -0
- package/pkgs/provider-none/src/components-inline.tsx +204 -0
- package/pkgs/provider-none/src/components.tsx +155 -0
- package/pkgs/provider-none/src/index.ts +57 -0
- package/pkgs/provider-none/src/intro.ts +18 -0
- package/pkgs/provider-none/src/manifest.ts +163 -0
- package/pkgs/provider-none/src/registry-inline.ts +36 -0
- package/pkgs/provider-none/src/registry.ts +39 -0
- package/pkgs/provider-none/src/tailwind-entry.css +57 -0
- package/pkgs/provider-none/src/version.ts +10 -0
- package/pkgs/schema/src/annotation.ts +120 -0
- package/pkgs/schema/src/asset.ts +66 -0
- package/pkgs/schema/src/board.ts +71 -0
- package/pkgs/schema/src/comment.ts +107 -0
- package/pkgs/schema/src/config.ts +185 -0
- package/pkgs/schema/src/css-sanitize.ts +56 -0
- package/pkgs/schema/src/extension.ts +105 -0
- package/pkgs/schema/src/fonts.ts +690 -0
- package/pkgs/schema/src/frame.ts +40 -0
- package/pkgs/schema/src/icon-name.ts +21 -0
- package/pkgs/schema/src/ids.ts +14 -0
- package/pkgs/schema/src/index.ts +169 -0
- package/pkgs/schema/src/migrate.ts +297 -0
- package/pkgs/schema/src/node.ts +229 -0
- package/pkgs/schema/src/paths.ts +28 -0
- package/pkgs/schema/src/repo.ts +134 -0
- package/pkgs/schema/src/screen.ts +30 -0
- package/pkgs/schema/src/snippet-resolve.ts +193 -0
- package/pkgs/schema/src/snippet.ts +104 -0
- package/pkgs/schema/src/svg-sanitize.ts +487 -0
- package/pkgs/schema/src/theme.ts +218 -0
- package/pkgs/schema/src/typeset.ts +693 -0
- package/pkgs/schema/src/validate-ids.ts +46 -0
- package/pkgs/schema/src/viewport.ts +8 -0
- package/pkgs/shadcn-snapshot/dist/manifest.json +8109 -0
- package/pkgs/shadcn-snapshot/src/components/canvas-portal.tsx +55 -0
- package/pkgs/shadcn-snapshot/src/components/ui/accordion.tsx +122 -0
- package/pkgs/shadcn-snapshot/src/components/ui/alert-dialog.tsx +164 -0
- package/pkgs/shadcn-snapshot/src/components/ui/alert.tsx +74 -0
- package/pkgs/shadcn-snapshot/src/components/ui/aspect-ratio.tsx +13 -0
- package/pkgs/shadcn-snapshot/src/components/ui/attachment.tsx +197 -0
- package/pkgs/shadcn-snapshot/src/components/ui/avatar.tsx +100 -0
- package/pkgs/shadcn-snapshot/src/components/ui/badge.tsx +50 -0
- package/pkgs/shadcn-snapshot/src/components/ui/breadcrumb.tsx +109 -0
- package/pkgs/shadcn-snapshot/src/components/ui/bubble.tsx +130 -0
- package/pkgs/shadcn-snapshot/src/components/ui/button-group.tsx +82 -0
- package/pkgs/shadcn-snapshot/src/components/ui/button.tsx +72 -0
- package/pkgs/shadcn-snapshot/src/components/ui/calendar.tsx +229 -0
- package/pkgs/shadcn-snapshot/src/components/ui/card.tsx +92 -0
- package/pkgs/shadcn-snapshot/src/components/ui/carousel.tsx +235 -0
- package/pkgs/shadcn-snapshot/src/components/ui/chart-option.ts +250 -0
- package/pkgs/shadcn-snapshot/src/components/ui/chart.tsx +177 -0
- package/pkgs/shadcn-snapshot/src/components/ui/checkbox.tsx +41 -0
- package/pkgs/shadcn-snapshot/src/components/ui/collapsible.tsx +32 -0
- package/pkgs/shadcn-snapshot/src/components/ui/combobox.tsx +196 -0
- package/pkgs/shadcn-snapshot/src/components/ui/context-menu.tsx +227 -0
- package/pkgs/shadcn-snapshot/src/components/ui/dialog.tsx +131 -0
- package/pkgs/shadcn-snapshot/src/components/ui/direction.tsx +24 -0
- package/pkgs/shadcn-snapshot/src/components/ui/drawer.tsx +112 -0
- package/pkgs/shadcn-snapshot/src/components/ui/dropdown-menu.tsx +205 -0
- package/pkgs/shadcn-snapshot/src/components/ui/empty.tsx +98 -0
- package/pkgs/shadcn-snapshot/src/components/ui/field.tsx +228 -0
- package/pkgs/shadcn-snapshot/src/components/ui/hover-card.tsx +51 -0
- package/pkgs/shadcn-snapshot/src/components/ui/input-group.tsx +148 -0
- package/pkgs/shadcn-snapshot/src/components/ui/input.tsx +43 -0
- package/pkgs/shadcn-snapshot/src/components/ui/item.tsx +187 -0
- package/pkgs/shadcn-snapshot/src/components/ui/kbd.tsx +30 -0
- package/pkgs/shadcn-snapshot/src/components/ui/label.tsx +25 -0
- package/pkgs/shadcn-snapshot/src/components/ui/marker.tsx +71 -0
- package/pkgs/shadcn-snapshot/src/components/ui/menubar.tsx +243 -0
- package/pkgs/shadcn-snapshot/src/components/ui/message.tsx +89 -0
- package/pkgs/shadcn-snapshot/src/components/ui/native-select.tsx +60 -0
- package/pkgs/shadcn-snapshot/src/components/ui/navigation-menu.tsx +165 -0
- package/pkgs/shadcn-snapshot/src/components/ui/pagination.tsx +117 -0
- package/pkgs/shadcn-snapshot/src/components/ui/popover.tsx +66 -0
- package/pkgs/shadcn-snapshot/src/components/ui/progress.tsx +35 -0
- package/pkgs/shadcn-snapshot/src/components/ui/radio-group.tsx +48 -0
- package/pkgs/shadcn-snapshot/src/components/ui/scroll-area.tsx +59 -0
- package/pkgs/shadcn-snapshot/src/components/ui/select.tsx +180 -0
- package/pkgs/shadcn-snapshot/src/components/ui/separator.tsx +32 -0
- package/pkgs/shadcn-snapshot/src/components/ui/sheet.tsx +107 -0
- package/pkgs/shadcn-snapshot/src/components/ui/skeleton.tsx +17 -0
- package/pkgs/shadcn-snapshot/src/components/ui/slider.tsx +68 -0
- package/pkgs/shadcn-snapshot/src/components/ui/sonner.tsx +91 -0
- package/pkgs/shadcn-snapshot/src/components/ui/spinner.tsx +21 -0
- package/pkgs/shadcn-snapshot/src/components/ui/switch.tsx +41 -0
- package/pkgs/shadcn-snapshot/src/components/ui/table.tsx +93 -0
- package/pkgs/shadcn-snapshot/src/components/ui/tabs.tsx +84 -0
- package/pkgs/shadcn-snapshot/src/components/ui/textarea.tsx +24 -0
- package/pkgs/shadcn-snapshot/src/components/ui/toggle-group.tsx +94 -0
- package/pkgs/shadcn-snapshot/src/components/ui/toggle.tsx +50 -0
- package/pkgs/shadcn-snapshot/src/components/ui/tooltip.tsx +47 -0
- package/pkgs/shadcn-snapshot/src/examples.ts +36 -0
- package/pkgs/shadcn-snapshot/src/groups.ts +87 -0
- package/pkgs/shadcn-snapshot/src/index.ts +43 -0
- package/pkgs/shadcn-snapshot/src/lib/utils.ts +4 -0
- package/pkgs/shadcn-snapshot/src/manifest.ts +12 -0
- package/pkgs/shadcn-snapshot/src/notes.ts +97 -0
- package/pkgs/shadcn-snapshot/src/paths.ts +47 -0
- package/pkgs/shadcn-snapshot/src/registry-files.ts +293 -0
- package/pkgs/shadcn-snapshot/src/registry.ts +631 -0
- package/pkgs/shadcn-snapshot/src/shadcn-tailwind.css +632 -0
- package/pkgs/shadcn-snapshot/src/tailwind-entry.css +159 -0
- package/pkgs/shadcn-snapshot/src/version.ts +8 -0
- package/plugins/claude/velloo/.claude-plugin/plugin.json +6 -0
- package/plugins/claude/velloo/agents/velloo-design-reviewer.md +33 -0
- package/plugins/claude/velloo/agents/velloo-designer.md +39 -0
- package/plugins/claude/velloo/commands/design.md +11 -0
- package/plugins/claude/velloo/commands/implement.md +10 -0
- package/plugins/claude/velloo/commands/review.md +8 -0
- package/plugins/gemini/GEMINI.md +31 -0
- package/plugins/gemini/commands/velloo/design.toml +10 -0
- package/plugins/gemini/commands/velloo/implement.toml +9 -0
- package/plugins/gemini/gemini-extension.json +9 -0
- package/skills/velloo-brand/SKILL.md +124 -0
- package/skills/velloo-design/SKILL.md +144 -0
- package/skills/velloo-design-system/SKILL.md +90 -0
- package/skills/velloo-implement/SKILL.md +101 -0
- package/skills/velloo-logo/SKILL.md +93 -0
- package/skills/velloo-setup/SKILL.md +156 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
// echarts → SVG string for the design-mode Chart preview.
|
|
2
|
+
//
|
|
3
|
+
// Why echarts (and not recharts): the renderer SSRs components with
|
|
4
|
+
// `renderToString` — static HTML, no client JS to hydrate or measure. A
|
|
5
|
+
// measurement-based lib (recharts' ResponsiveContainer) renders an empty
|
|
6
|
+
// shell under SSR; echarts' headless SSR mode produces a complete SVG
|
|
7
|
+
// synchronously from one declarative options object, across every chart type.
|
|
8
|
+
//
|
|
9
|
+
// Colors are CSS variables (`var(--color-*)`) — the same tokens themeToCss
|
|
10
|
+
// emits — so the SVG theme-flips under `.dark` with no re-render (Chromium
|
|
11
|
+
// resolves var() in SVG presentation attributes). The tree-shakeable `core`
|
|
12
|
+
// API keeps only the chart types we register.
|
|
13
|
+
import { BarChart, LineChart, PieChart, ScatterChart } from "echarts/charts";
|
|
14
|
+
import { GridComponent, LegendComponent } from "echarts/components";
|
|
15
|
+
import type { EChartsCoreOption } from "echarts/core";
|
|
16
|
+
import * as echarts from "echarts/core";
|
|
17
|
+
import { SVGRenderer } from "echarts/renderers";
|
|
18
|
+
|
|
19
|
+
echarts.use([
|
|
20
|
+
BarChart,
|
|
21
|
+
LineChart,
|
|
22
|
+
PieChart,
|
|
23
|
+
ScatterChart,
|
|
24
|
+
GridComponent,
|
|
25
|
+
LegendComponent,
|
|
26
|
+
SVGRenderer,
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
export type ChartKind = "bar" | "line" | "area" | "pie" | "scatter";
|
|
30
|
+
export type ChartColor = "primary" | "accent" | "muted";
|
|
31
|
+
export type TickFormat = "number" | "compact" | "currency" | "percent";
|
|
32
|
+
|
|
33
|
+
export interface ChartSpec {
|
|
34
|
+
kind?: ChartKind | undefined;
|
|
35
|
+
/** Single-series convenience: a list of `{ x label, y value }` points. */
|
|
36
|
+
data?: { x: string | number; y: number }[] | undefined;
|
|
37
|
+
/** Multi-series: shared categories + named series. Takes precedence over `data`. */
|
|
38
|
+
categories?: (string | number)[] | undefined;
|
|
39
|
+
series?: { name?: string | undefined; data: number[] }[] | undefined;
|
|
40
|
+
/** Axis titles. */
|
|
41
|
+
xLabel?: string | undefined;
|
|
42
|
+
yLabel?: string | undefined;
|
|
43
|
+
/** Y-axis tick formatting. */
|
|
44
|
+
tickFormat?: TickFormat | undefined;
|
|
45
|
+
/** Accent for single-series charts; multi-series cycles a fixed palette. */
|
|
46
|
+
color?: ChartColor | undefined;
|
|
47
|
+
/** Force the legend on/off; defaults on for multi-series, off otherwise. */
|
|
48
|
+
legend?: boolean | undefined;
|
|
49
|
+
/** Stack multiple bar/area series into cumulative totals; default off. */
|
|
50
|
+
stacked?: boolean | undefined;
|
|
51
|
+
/** Show axes, tick labels, and gridlines; set false for sparkline mode. */
|
|
52
|
+
axes?: boolean | undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const AXIS = "var(--color-muted-foreground)";
|
|
56
|
+
const GRID = "var(--color-border)";
|
|
57
|
+
const SINGLE: Record<ChartColor, string> = {
|
|
58
|
+
primary: "var(--color-primary)",
|
|
59
|
+
accent: "var(--color-accent)",
|
|
60
|
+
muted: "var(--color-muted-foreground)",
|
|
61
|
+
};
|
|
62
|
+
// Distinct, theme-flipping hues for multi-series — all tokens themeToCss emits.
|
|
63
|
+
// Deliberately excludes destructive: an error hue must never be auto-assigned
|
|
64
|
+
// to ordinary data (a 3-series chart would paint one series alarm-red).
|
|
65
|
+
const PALETTE = [
|
|
66
|
+
"var(--color-primary)",
|
|
67
|
+
"var(--color-accent)",
|
|
68
|
+
"var(--color-secondary)",
|
|
69
|
+
"var(--color-muted-foreground)",
|
|
70
|
+
"var(--color-foreground)",
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
const FORMATTERS: Record<TickFormat, ((v: number) => string) | undefined> = {
|
|
74
|
+
number: undefined,
|
|
75
|
+
compact: (v) => new Intl.NumberFormat("en", { notation: "compact" }).format(v),
|
|
76
|
+
currency: (v) =>
|
|
77
|
+
new Intl.NumberFormat("en", {
|
|
78
|
+
style: "currency",
|
|
79
|
+
currency: "USD",
|
|
80
|
+
notation: "compact",
|
|
81
|
+
}).format(v),
|
|
82
|
+
percent: (v) => `${v}%`,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
function normalize(spec: ChartSpec): {
|
|
86
|
+
categories: (string | number)[];
|
|
87
|
+
series: { name?: string | undefined; data: number[] }[];
|
|
88
|
+
} {
|
|
89
|
+
if (spec.series && spec.series.length > 0) {
|
|
90
|
+
const first = spec.series[0];
|
|
91
|
+
const categories = spec.categories ?? (first ? first.data.map((_, i) => i + 1) : []);
|
|
92
|
+
return { categories, series: spec.series };
|
|
93
|
+
}
|
|
94
|
+
const data = spec.data ?? [];
|
|
95
|
+
return { categories: data.map((d) => d.x), series: [{ data: data.map((d) => d.y) }] };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function buildChartOption(spec: ChartSpec): EChartsCoreOption {
|
|
99
|
+
const kind = spec.kind ?? "bar";
|
|
100
|
+
const { categories, series } = normalize(spec);
|
|
101
|
+
const multi = series.length > 1;
|
|
102
|
+
const showLegend = spec.legend ?? multi;
|
|
103
|
+
const showAxes = spec.axes ?? true;
|
|
104
|
+
const stacked = (spec.stacked ?? false) && multi && (kind === "bar" || kind === "area");
|
|
105
|
+
const formatter = spec.tickFormat ? FORMATTERS[spec.tickFormat] : undefined;
|
|
106
|
+
|
|
107
|
+
const base: Record<string, unknown> = {
|
|
108
|
+
animation: false,
|
|
109
|
+
color: PALETTE,
|
|
110
|
+
...(showLegend
|
|
111
|
+
? {
|
|
112
|
+
legend: {
|
|
113
|
+
top: 0,
|
|
114
|
+
icon: "roundRect",
|
|
115
|
+
itemHeight: 8,
|
|
116
|
+
itemWidth: 8,
|
|
117
|
+
textStyle: { color: AXIS, fontSize: 10 },
|
|
118
|
+
},
|
|
119
|
+
}
|
|
120
|
+
: {}),
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
if (kind === "pie") {
|
|
124
|
+
const points = spec.data ?? categories.map((c, i) => ({ x: c, y: series[0]?.data[i] ?? 0 }));
|
|
125
|
+
return {
|
|
126
|
+
...base,
|
|
127
|
+
series: [
|
|
128
|
+
{
|
|
129
|
+
type: "pie",
|
|
130
|
+
radius: ["40%", "70%"],
|
|
131
|
+
data: points.map((p) => ({ name: String(p.x), value: p.y })),
|
|
132
|
+
label: { color: AXIS, fontSize: 10 },
|
|
133
|
+
itemStyle: { borderColor: "var(--color-background)", borderWidth: 2 },
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
} as EChartsCoreOption;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const isScatter = kind === "scatter";
|
|
140
|
+
const cartesianSeries = series.map((s, i) => {
|
|
141
|
+
const tint = multi ? PALETTE[i % PALETTE.length] : SINGLE[spec.color ?? "primary"];
|
|
142
|
+
if (kind === "line" || kind === "area") {
|
|
143
|
+
return {
|
|
144
|
+
type: "line",
|
|
145
|
+
name: s.name,
|
|
146
|
+
data: s.data,
|
|
147
|
+
smooth: true,
|
|
148
|
+
showSymbol: false,
|
|
149
|
+
lineStyle: { color: tint, width: 2 },
|
|
150
|
+
itemStyle: { color: tint },
|
|
151
|
+
...(kind === "area" ? { areaStyle: { color: tint, opacity: 0.18 } } : {}),
|
|
152
|
+
...(stacked ? { stack: "total" } : {}),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
if (isScatter) {
|
|
156
|
+
return {
|
|
157
|
+
type: "scatter",
|
|
158
|
+
name: s.name,
|
|
159
|
+
symbolSize: 8,
|
|
160
|
+
itemStyle: { color: tint },
|
|
161
|
+
data: s.data.map((y, j) => [categories[j] ?? j, y]),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
type: "bar",
|
|
166
|
+
name: s.name,
|
|
167
|
+
data: s.data,
|
|
168
|
+
// No rounded tops when stacked — every segment would get one, notching the seams.
|
|
169
|
+
itemStyle: { color: tint, ...(stacked ? {} : { borderRadius: [3, 3, 0, 0] }) },
|
|
170
|
+
...(stacked ? { stack: "total" } : {}),
|
|
171
|
+
};
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
...base,
|
|
176
|
+
grid: showAxes
|
|
177
|
+
? {
|
|
178
|
+
left: spec.yLabel ? 52 : 44,
|
|
179
|
+
right: 14,
|
|
180
|
+
top: showLegend ? 30 : 16,
|
|
181
|
+
bottom: spec.xLabel ? 38 : 24,
|
|
182
|
+
}
|
|
183
|
+
: // Sparkline mode: marks fill the box edge-to-edge.
|
|
184
|
+
{ left: 2, right: 2, top: showLegend ? 30 : 2, bottom: 2 },
|
|
185
|
+
xAxis: {
|
|
186
|
+
type: isScatter ? "value" : "category",
|
|
187
|
+
...(isScatter ? {} : { data: categories }),
|
|
188
|
+
...(showAxes
|
|
189
|
+
? {
|
|
190
|
+
...(spec.xLabel ? { name: spec.xLabel, nameLocation: "middle", nameGap: 22 } : {}),
|
|
191
|
+
nameTextStyle: { color: AXIS, fontSize: 10 },
|
|
192
|
+
axisLabel: { color: AXIS, fontSize: 10 },
|
|
193
|
+
axisLine: { lineStyle: { color: GRID } },
|
|
194
|
+
axisTick: { show: false },
|
|
195
|
+
}
|
|
196
|
+
: { show: false }),
|
|
197
|
+
},
|
|
198
|
+
yAxis: {
|
|
199
|
+
type: "value",
|
|
200
|
+
...(showAxes
|
|
201
|
+
? {
|
|
202
|
+
...(spec.yLabel ? { name: spec.yLabel } : {}),
|
|
203
|
+
nameTextStyle: { color: AXIS, fontSize: 10 },
|
|
204
|
+
axisLabel: { color: AXIS, fontSize: 10, ...(formatter ? { formatter } : {}) },
|
|
205
|
+
splitLine: { lineStyle: { color: GRID, opacity: 0.5 } },
|
|
206
|
+
}
|
|
207
|
+
: // `show: false` alone leaves gridlines on — splitLine needs its own off switch.
|
|
208
|
+
{ show: false, splitLine: { show: false } }),
|
|
209
|
+
},
|
|
210
|
+
series: cartesianSeries,
|
|
211
|
+
} as EChartsCoreOption;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const DEFAULT_W = 480;
|
|
215
|
+
const DEFAULT_H = 270;
|
|
216
|
+
const MIN_ASPECT = 0.25;
|
|
217
|
+
const MAX_ASPECT = 20;
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Render size for a width÷height `aspect` ratio, clamped to 0.25–20 so a bad
|
|
221
|
+
* value can't produce a degenerate SVG. No aspect keeps the 16:9 default.
|
|
222
|
+
*/
|
|
223
|
+
export function chartSize(aspect?: number): { width: number; height: number } {
|
|
224
|
+
if (aspect === undefined || !Number.isFinite(aspect)) {
|
|
225
|
+
return { width: DEFAULT_W, height: DEFAULT_H };
|
|
226
|
+
}
|
|
227
|
+
const clamped = Math.min(MAX_ASPECT, Math.max(MIN_ASPECT, aspect));
|
|
228
|
+
return { width: DEFAULT_W, height: Math.round(DEFAULT_W / clamped) };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** Render a chart spec to a self-contained, responsive SVG string. */
|
|
232
|
+
export function renderChartSvg(
|
|
233
|
+
spec: ChartSpec,
|
|
234
|
+
size?: { width?: number; height?: number },
|
|
235
|
+
): string {
|
|
236
|
+
const chart = echarts.init(null, null, {
|
|
237
|
+
renderer: "svg",
|
|
238
|
+
ssr: true,
|
|
239
|
+
width: size?.width ?? DEFAULT_W,
|
|
240
|
+
height: size?.height ?? DEFAULT_H,
|
|
241
|
+
});
|
|
242
|
+
chart.setOption(buildChartOption(spec));
|
|
243
|
+
const svg = chart.renderToSVGString();
|
|
244
|
+
chart.dispose();
|
|
245
|
+
// Drop the fixed pixel size so the SVG scales to its container (viewBox kept).
|
|
246
|
+
return svg.replace(
|
|
247
|
+
/<svg([^>]*?)\swidth="\d+"\sheight="\d+"/,
|
|
248
|
+
'<svg$1 width="100%" height="100%" preserveAspectRatio="xMidYMid meet"',
|
|
249
|
+
);
|
|
250
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// Velloo-flavored Chart. shadcn's `chart` is a recharts wrapper that needs a
|
|
2
|
+
// measured DOM (ResponsiveContainer), so it renders nothing under the
|
|
3
|
+
// renderer's static SSR. We render a realistic preview server-side via
|
|
4
|
+
// echarts' headless SSR-to-SVG mode (see chart-option.ts) — bar/line/area/
|
|
5
|
+
// pie/scatter, multi-series, axis labels + tick formatting — from one
|
|
6
|
+
// normalized schema. Colors are theme tokens, so previews theme-flip.
|
|
7
|
+
//
|
|
8
|
+
// This built-in `Chart` always previews via echarts, and `emit_code` emits it
|
|
9
|
+
// as a `Chart` component (the preview engine — NOT the app's chart lib). To
|
|
10
|
+
// get the app's *own* chart (its exact recharts) — pixel-faithful in the
|
|
11
|
+
// canvas AND emitted as a real import — register that component as a
|
|
12
|
+
// `render:"live"` extension instead (live islands):
|
|
13
|
+
// extensions emit a real `import` from their declared `importPath`.
|
|
14
|
+
import type * as React from "react";
|
|
15
|
+
import { cn } from "../../lib/utils.ts";
|
|
16
|
+
import { chartSize, renderChartSvg } from "./chart-option.ts";
|
|
17
|
+
|
|
18
|
+
export type { ChartColor, ChartKind, ChartSpec, TickFormat } from "./chart-option.ts";
|
|
19
|
+
|
|
20
|
+
export interface ChartContainerProps extends React.ComponentProps<"div"> {
|
|
21
|
+
config?: Record<string, { label?: string; color?: string }>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function ChartContainer({
|
|
25
|
+
className,
|
|
26
|
+
config: _config,
|
|
27
|
+
children,
|
|
28
|
+
...props
|
|
29
|
+
}: ChartContainerProps) {
|
|
30
|
+
return (
|
|
31
|
+
<div
|
|
32
|
+
data-slot="chart-container"
|
|
33
|
+
className={cn(
|
|
34
|
+
"flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground",
|
|
35
|
+
className,
|
|
36
|
+
)}
|
|
37
|
+
{...props}
|
|
38
|
+
>
|
|
39
|
+
{children}
|
|
40
|
+
</div>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Declared inline (not `extends ChartSpec`) so build.ts's manifest extraction
|
|
45
|
+
// — which reads an interface's *own* members — surfaces every prop to agents.
|
|
46
|
+
export interface ChartProps {
|
|
47
|
+
className?: string;
|
|
48
|
+
kind?: "bar" | "line" | "area" | "pie" | "scatter";
|
|
49
|
+
/** Single-series convenience: a list of `{ x label, y value }` points. */
|
|
50
|
+
data?: { x: string | number; y: number }[];
|
|
51
|
+
/** Multi-series: shared categories + named series. Takes precedence over `data`. */
|
|
52
|
+
categories?: (string | number)[];
|
|
53
|
+
series?: { name?: string; data: number[] }[];
|
|
54
|
+
/** Axis titles. */
|
|
55
|
+
xLabel?: string;
|
|
56
|
+
yLabel?: string;
|
|
57
|
+
/** Y-axis tick formatting. */
|
|
58
|
+
tickFormat?: "number" | "compact" | "currency" | "percent";
|
|
59
|
+
/** Accent for single-series charts; multi-series cycles a fixed palette. */
|
|
60
|
+
color?: "primary" | "accent" | "muted";
|
|
61
|
+
/** Force the legend on/off; defaults on for multi-series, off otherwise. */
|
|
62
|
+
legend?: boolean;
|
|
63
|
+
/** Stack multiple bar/area series into cumulative totals; default off. */
|
|
64
|
+
stacked?: boolean;
|
|
65
|
+
/** Width÷height ratio, e.g. 4 for a wide sparkline or 1 for square; defaults to 16:9. */
|
|
66
|
+
aspect?: number;
|
|
67
|
+
/** Show axes, tick labels, and gridlines; set false for sparkline mode. */
|
|
68
|
+
axes?: boolean;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Dashboard-grade chart preview. Renders server-side via echarts SSR→SVG
|
|
73
|
+
* (chart-option.ts) so it shows real geometry + axes in the static render
|
|
74
|
+
* path, and the SVG is injected as-is (no scripts/foreignObject). Empty
|
|
75
|
+
* `data`/`series` degrades to a sized blank slot.
|
|
76
|
+
*/
|
|
77
|
+
export function Chart({
|
|
78
|
+
className,
|
|
79
|
+
style,
|
|
80
|
+
kind,
|
|
81
|
+
data,
|
|
82
|
+
series,
|
|
83
|
+
categories,
|
|
84
|
+
xLabel,
|
|
85
|
+
yLabel,
|
|
86
|
+
tickFormat,
|
|
87
|
+
color,
|
|
88
|
+
legend,
|
|
89
|
+
stacked,
|
|
90
|
+
aspect,
|
|
91
|
+
axes,
|
|
92
|
+
...rest
|
|
93
|
+
}: ChartProps & React.ComponentProps<"div">) {
|
|
94
|
+
const size = chartSize(aspect);
|
|
95
|
+
// Derive the box ratio from the clamped render size so the container and
|
|
96
|
+
// the SVG geometry always agree; inline style beats the aspect-video class.
|
|
97
|
+
const boxStyle =
|
|
98
|
+
aspect === undefined ? style : { aspectRatio: size.width / size.height, ...style };
|
|
99
|
+
const hasData =
|
|
100
|
+
(Array.isArray(series) && series.length > 0) || (Array.isArray(data) && data.length > 0);
|
|
101
|
+
if (!hasData) {
|
|
102
|
+
return (
|
|
103
|
+
<div
|
|
104
|
+
data-slot="chart"
|
|
105
|
+
className={cn("aspect-video w-full", className)}
|
|
106
|
+
style={boxStyle}
|
|
107
|
+
{...rest}
|
|
108
|
+
/>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
const svg = renderChartSvg(
|
|
112
|
+
{
|
|
113
|
+
kind,
|
|
114
|
+
data,
|
|
115
|
+
series,
|
|
116
|
+
categories,
|
|
117
|
+
xLabel,
|
|
118
|
+
yLabel,
|
|
119
|
+
tickFormat,
|
|
120
|
+
color,
|
|
121
|
+
legend,
|
|
122
|
+
stacked,
|
|
123
|
+
axes,
|
|
124
|
+
},
|
|
125
|
+
size,
|
|
126
|
+
);
|
|
127
|
+
return (
|
|
128
|
+
<div
|
|
129
|
+
data-slot="chart"
|
|
130
|
+
className={cn("aspect-video w-full", className)}
|
|
131
|
+
style={boxStyle}
|
|
132
|
+
{...rest}
|
|
133
|
+
// biome-ignore lint/security/noDangerouslySetInnerHtml: server-generated static echarts SVG (no scripts/foreignObject); see chart-option.ts
|
|
134
|
+
dangerouslySetInnerHTML={{ __html: svg }}
|
|
135
|
+
/>
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function ChartLegend({ className, ...props }: React.ComponentProps<"div">) {
|
|
140
|
+
return (
|
|
141
|
+
<div
|
|
142
|
+
data-slot="chart-legend"
|
|
143
|
+
className={cn("flex items-center gap-4 text-xs text-muted-foreground", className)}
|
|
144
|
+
{...props}
|
|
145
|
+
/>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function ChartLegendContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
150
|
+
return (
|
|
151
|
+
<div
|
|
152
|
+
data-slot="chart-legend-content"
|
|
153
|
+
className={cn("flex flex-wrap items-center gap-2", className)}
|
|
154
|
+
{...props}
|
|
155
|
+
/>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function ChartTooltip({ className, ...props }: React.ComponentProps<"div">) {
|
|
160
|
+
return (
|
|
161
|
+
<div
|
|
162
|
+
data-slot="chart-tooltip"
|
|
163
|
+
className={cn("rounded-md border bg-popover px-2 py-1 text-xs shadow-md", className)}
|
|
164
|
+
{...props}
|
|
165
|
+
/>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function ChartTooltipContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
170
|
+
return (
|
|
171
|
+
<div
|
|
172
|
+
data-slot="chart-tooltip-content"
|
|
173
|
+
className={cn("flex flex-col gap-1", className)}
|
|
174
|
+
{...props}
|
|
175
|
+
/>
|
|
176
|
+
);
|
|
177
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Vendored from shadcn-ui (https://ui.shadcn.com/docs/components/radix/checkbox).
|
|
2
|
+
// Snapshot version: see packages/shadcn-snapshot/package.json#snapshotVersion.
|
|
3
|
+
// Canvas-safe: a design ships `checked` to show a ticked state, with no handler
|
|
4
|
+
// because nothing on the canvas is interactive. Radix would then hold the box
|
|
5
|
+
// at that value and React would warn about the missing handler, so a handlerless
|
|
6
|
+
// `checked` becomes `defaultChecked`.
|
|
7
|
+
"use client";
|
|
8
|
+
|
|
9
|
+
import { CheckIcon } from "lucide-react";
|
|
10
|
+
import { Checkbox as CheckboxPrimitive } from "radix-ui";
|
|
11
|
+
import type * as React from "react";
|
|
12
|
+
|
|
13
|
+
import { cn } from "../../lib/utils.ts";
|
|
14
|
+
|
|
15
|
+
export function Checkbox({
|
|
16
|
+
className,
|
|
17
|
+
...props
|
|
18
|
+
}: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
|
|
19
|
+
const { checked, ...rest } = props;
|
|
20
|
+
const isStaticControlled = checked !== undefined && props.onCheckedChange === undefined;
|
|
21
|
+
// Drop `checked` by omitting it, not by setting it to undefined: Radix reads
|
|
22
|
+
// "present but undefined" as a controlled component with no value.
|
|
23
|
+
const finalProps = isStaticControlled ? { ...rest, defaultChecked: checked } : props;
|
|
24
|
+
return (
|
|
25
|
+
<CheckboxPrimitive.Root
|
|
26
|
+
data-slot="checkbox"
|
|
27
|
+
className={cn(
|
|
28
|
+
"peer relative flex size-4 shrink-0 items-center justify-center rounded-[4px] border border-input transition-colors outline-none group-has-disabled/field:opacity-50 group-has-[:focus-visible]/field-label:ring-0 group-has-[:focus-visible]/field-label:not-data-checked:border-input after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground group-has-[:focus-visible]/field-label:data-checked:border-primary dark:data-checked:bg-primary",
|
|
29
|
+
className,
|
|
30
|
+
)}
|
|
31
|
+
{...finalProps}
|
|
32
|
+
>
|
|
33
|
+
<CheckboxPrimitive.Indicator
|
|
34
|
+
data-slot="checkbox-indicator"
|
|
35
|
+
className="grid place-content-center text-current transition-none [&>svg]:size-3.5"
|
|
36
|
+
>
|
|
37
|
+
<CheckIcon />
|
|
38
|
+
</CheckboxPrimitive.Indicator>
|
|
39
|
+
</CheckboxPrimitive.Root>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Vendored from shadcn-ui (https://ui.shadcn.com/docs/components/radix/collapsible).
|
|
2
|
+
// Snapshot version: see packages/shadcn-snapshot/package.json#snapshotVersion.
|
|
3
|
+
// Canvas-safe: nothing on the canvas is clickable, so a collapsible left at its
|
|
4
|
+
// default would render as an unopenable header. With neither `open` nor
|
|
5
|
+
// `defaultOpen` set, the content starts open so the design is previewable.
|
|
6
|
+
"use client";
|
|
7
|
+
|
|
8
|
+
import { Collapsible as CollapsiblePrimitive } from "radix-ui";
|
|
9
|
+
import type * as React from "react";
|
|
10
|
+
|
|
11
|
+
export function Collapsible({ ...props }: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
|
|
12
|
+
const designOpen = props.open === undefined && props.defaultOpen === undefined;
|
|
13
|
+
return (
|
|
14
|
+
<CollapsiblePrimitive.Root
|
|
15
|
+
data-slot="collapsible"
|
|
16
|
+
{...(designOpen ? { defaultOpen: true } : {})}
|
|
17
|
+
{...props}
|
|
18
|
+
/>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function CollapsibleTrigger({
|
|
23
|
+
...props
|
|
24
|
+
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {
|
|
25
|
+
return <CollapsiblePrimitive.CollapsibleTrigger data-slot="collapsible-trigger" {...props} />;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function CollapsibleContent({
|
|
29
|
+
...props
|
|
30
|
+
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {
|
|
31
|
+
return <CollapsiblePrimitive.CollapsibleContent data-slot="collapsible-content" {...props} />;
|
|
32
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
// Vendored from shadcn-ui (https://ui.shadcn.com/docs/components/radix/combobox).
|
|
2
|
+
// Snapshot version: see packages/shadcn-snapshot/package.json#snapshotVersion.
|
|
3
|
+
//
|
|
4
|
+
// Canvas-safe: see ../canvas-portal.tsx. Upstream's Combobox is the one shadcn
|
|
5
|
+
// component built on Base UI rather than Radix, and all of it — filtering, the
|
|
6
|
+
// highlighted item, the anchored popup — is runtime behaviour that a static
|
|
7
|
+
// render has nothing to show for. Rendering the family as plain elements keeps
|
|
8
|
+
// a second component library out of the dependency tree for a component whose
|
|
9
|
+
// design surface is just "an input with a list under it". `emit_code` still
|
|
10
|
+
// emits `Combobox`, so the app gets the real Base UI one from its own
|
|
11
|
+
// components/ui/combobox.
|
|
12
|
+
//
|
|
13
|
+
// Sizing classes that read Base UI's anchor vars (`--anchor-width`,
|
|
14
|
+
// `--available-height`) are dropped; nothing sets them without the positioner.
|
|
15
|
+
"use client";
|
|
16
|
+
|
|
17
|
+
import { ChevronDownIcon, XIcon } from "lucide-react";
|
|
18
|
+
import type * as React from "react";
|
|
19
|
+
import { cn } from "../../lib/utils.ts";
|
|
20
|
+
import { inlineOpenAttrs } from "../canvas-portal.tsx";
|
|
21
|
+
import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput } from "./input-group.tsx";
|
|
22
|
+
|
|
23
|
+
export function Combobox({ ...props }: React.ComponentProps<"div">) {
|
|
24
|
+
return <div data-slot="combobox" {...props} />;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function ComboboxValue({ ...props }: React.ComponentProps<"span">) {
|
|
28
|
+
return <span data-slot="combobox-value" {...props} />;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function ComboboxTrigger({ className, children, ...props }: React.ComponentProps<"button">) {
|
|
32
|
+
return (
|
|
33
|
+
<button
|
|
34
|
+
type="button"
|
|
35
|
+
data-slot="combobox-trigger"
|
|
36
|
+
className={cn("[&_svg:not([class*='size-'])]:size-4", className)}
|
|
37
|
+
{...props}
|
|
38
|
+
>
|
|
39
|
+
{children}
|
|
40
|
+
<ChevronDownIcon className="pointer-events-none size-4 text-muted-foreground" />
|
|
41
|
+
</button>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function ComboboxClear({ className, ...props }: React.ComponentProps<"button">) {
|
|
46
|
+
return (
|
|
47
|
+
<InputGroupButton
|
|
48
|
+
data-slot="combobox-clear"
|
|
49
|
+
variant="ghost"
|
|
50
|
+
size="icon-xs"
|
|
51
|
+
className={className}
|
|
52
|
+
{...props}
|
|
53
|
+
>
|
|
54
|
+
<XIcon className="pointer-events-none" />
|
|
55
|
+
</InputGroupButton>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function ComboboxInput({
|
|
60
|
+
className,
|
|
61
|
+
disabled = false,
|
|
62
|
+
showTrigger = true,
|
|
63
|
+
showClear = false,
|
|
64
|
+
...props
|
|
65
|
+
}: React.ComponentProps<"input"> & { showTrigger?: boolean; showClear?: boolean }) {
|
|
66
|
+
return (
|
|
67
|
+
<InputGroup className={cn("w-auto", className)}>
|
|
68
|
+
<InputGroupInput disabled={disabled} {...props} />
|
|
69
|
+
<InputGroupAddon align="inline-end">
|
|
70
|
+
{showClear ? <ComboboxClear disabled={disabled} /> : null}
|
|
71
|
+
{showTrigger ? (
|
|
72
|
+
<InputGroupButton size="icon-xs" variant="ghost" disabled={disabled}>
|
|
73
|
+
<ChevronDownIcon className="pointer-events-none size-4 text-muted-foreground" />
|
|
74
|
+
</InputGroupButton>
|
|
75
|
+
) : null}
|
|
76
|
+
</InputGroupAddon>
|
|
77
|
+
</InputGroup>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function ComboboxContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
82
|
+
return (
|
|
83
|
+
<div
|
|
84
|
+
data-slot="combobox-content"
|
|
85
|
+
{...inlineOpenAttrs()}
|
|
86
|
+
className={cn(
|
|
87
|
+
"cn-menu-target cn-menu-translucent group/combobox-content relative overflow-hidden rounded-lg bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10",
|
|
88
|
+
className,
|
|
89
|
+
)}
|
|
90
|
+
{...props}
|
|
91
|
+
/>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function ComboboxList({ className, ...props }: React.ComponentProps<"div">) {
|
|
96
|
+
return (
|
|
97
|
+
<div
|
|
98
|
+
data-slot="combobox-list"
|
|
99
|
+
className={cn(
|
|
100
|
+
"no-scrollbar max-h-72 scroll-py-1 overflow-y-auto overscroll-contain p-1 data-empty:p-0",
|
|
101
|
+
className,
|
|
102
|
+
)}
|
|
103
|
+
{...props}
|
|
104
|
+
/>
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function ComboboxItem({ className, ...props }: React.ComponentProps<"div">) {
|
|
109
|
+
return (
|
|
110
|
+
<div
|
|
111
|
+
data-slot="combobox-item"
|
|
112
|
+
className={cn(
|
|
113
|
+
"relative flex w-full cursor-default items-center gap-2 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none data-highlighted:bg-accent data-highlighted:text-accent-foreground not-data-[variant=destructive]:data-highlighted:**:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
114
|
+
className,
|
|
115
|
+
)}
|
|
116
|
+
{...props}
|
|
117
|
+
/>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function ComboboxGroup({ ...props }: React.ComponentProps<"div">) {
|
|
122
|
+
return <div data-slot="combobox-group" {...props} />;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function ComboboxLabel({ className, ...props }: React.ComponentProps<"div">) {
|
|
126
|
+
return (
|
|
127
|
+
<div
|
|
128
|
+
data-slot="combobox-label"
|
|
129
|
+
className={cn("px-2 py-1.5 text-xs text-muted-foreground", className)}
|
|
130
|
+
{...props}
|
|
131
|
+
/>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function ComboboxCollection({ children }: React.ComponentProps<"div">) {
|
|
136
|
+
return <>{children}</>;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function ComboboxEmpty({ className, ...props }: React.ComponentProps<"div">) {
|
|
140
|
+
return (
|
|
141
|
+
<div
|
|
142
|
+
data-slot="combobox-empty"
|
|
143
|
+
className={cn(
|
|
144
|
+
"hidden w-full justify-center py-2 text-center text-sm text-muted-foreground group-data-empty/combobox-content:flex",
|
|
145
|
+
className,
|
|
146
|
+
)}
|
|
147
|
+
{...props}
|
|
148
|
+
/>
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function ComboboxSeparator({ className, ...props }: React.ComponentProps<"div">) {
|
|
153
|
+
return (
|
|
154
|
+
<div
|
|
155
|
+
data-slot="combobox-separator"
|
|
156
|
+
className={cn("-mx-1 my-1 h-px bg-border", className)}
|
|
157
|
+
{...props}
|
|
158
|
+
/>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function ComboboxChips({ className, ...props }: React.ComponentProps<"div">) {
|
|
163
|
+
return (
|
|
164
|
+
<div
|
|
165
|
+
data-slot="combobox-chips"
|
|
166
|
+
className={cn(
|
|
167
|
+
"flex min-h-8 flex-wrap items-center gap-1 rounded-lg border border-input bg-transparent bg-clip-padding px-2.5 py-1 text-sm transition-colors focus-within:border-ring focus-within:ring-3 focus-within:ring-ring/50 has-aria-invalid:border-destructive has-aria-invalid:ring-3 has-aria-invalid:ring-destructive/20 has-data-[slot=combobox-chip]:px-1 dark:bg-input/30",
|
|
168
|
+
className,
|
|
169
|
+
)}
|
|
170
|
+
{...props}
|
|
171
|
+
/>
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function ComboboxChip({ className, ...props }: React.ComponentProps<"div">) {
|
|
176
|
+
return (
|
|
177
|
+
<div
|
|
178
|
+
data-slot="combobox-chip"
|
|
179
|
+
className={cn(
|
|
180
|
+
"flex h-[calc(--spacing(5.25))] w-fit items-center justify-center gap-1 rounded-sm bg-muted px-1.5 text-xs font-medium whitespace-nowrap text-foreground has-disabled:pointer-events-none has-disabled:opacity-50 has-data-[slot=combobox-chip-remove]:pr-0",
|
|
181
|
+
className,
|
|
182
|
+
)}
|
|
183
|
+
{...props}
|
|
184
|
+
/>
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function ComboboxChipsInput({ className, ...props }: React.ComponentProps<"input">) {
|
|
189
|
+
return (
|
|
190
|
+
<input
|
|
191
|
+
data-slot="combobox-chip-input"
|
|
192
|
+
className={cn("min-w-16 flex-1 outline-none", className)}
|
|
193
|
+
{...props}
|
|
194
|
+
/>
|
|
195
|
+
);
|
|
196
|
+
}
|