@squadbase/vantage 0.0.1 → 0.1.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/README.md +21 -4
- package/dist/chunk-A2UUGASH.js +12 -0
- package/dist/chunk-A2UUGASH.js.map +1 -0
- package/dist/{chunk-ZGDU5YLH.js → chunk-PRFBSQA4.js} +9 -5
- package/dist/chunk-PRFBSQA4.js.map +1 -0
- package/dist/chunk-WQZYXXQW.js +2249 -0
- package/dist/chunk-WQZYXXQW.js.map +1 -0
- package/dist/{chunk-73J5ZD4C.js → chunk-YLAB6UQS.js} +17 -3
- package/dist/chunk-YLAB6UQS.js.map +1 -0
- package/dist/cli.js +607 -36
- package/dist/cli.js.map +1 -1
- package/dist/components/index.d.ts +554 -0
- package/dist/components/index.js +1983 -0
- package/dist/components/index.js.map +1 -0
- package/dist/markdown/index.d.ts +9 -0
- package/dist/markdown/index.js +26 -0
- package/dist/markdown/index.js.map +1 -0
- package/dist/query/index.d.ts +27 -1
- package/dist/query/index.js +18 -0
- package/dist/query/index.js.map +1 -1
- package/dist/server/node.js +6 -1
- package/dist/server/node.js.map +1 -1
- package/dist/ui/index.d.ts +316 -200
- package/dist/ui/index.js +566 -511
- package/dist/ui/index.js.map +1 -1
- package/dist/vite/index.js +2 -2
- package/docs/en/agent-skills.md +89 -0
- package/docs/en/api-and-server.md +84 -0
- package/docs/en/build-and-deploy.md +280 -0
- package/docs/en/cli-reference.md +311 -0
- package/docs/en/components.md +233 -0
- package/docs/en/data-fetching.md +78 -0
- package/docs/en/environment-variables.md +69 -0
- package/docs/en/getting-started.md +95 -0
- package/docs/en/index.md +65 -0
- package/docs/en/markdown/markdown-renderer.md +44 -0
- package/docs/en/pages-and-metadata.md +56 -0
- package/docs/en/parts/app-shell.md +54 -0
- package/docs/en/parts/dashboard-card.md +64 -0
- package/docs/en/parts/data-table.md +86 -0
- package/docs/en/parts/date-range-picker.md +41 -0
- package/docs/en/parts/echart.md +53 -0
- package/docs/en/parts/filter-bar.md +66 -0
- package/docs/en/parts/funnel-steps.md +33 -0
- package/docs/en/parts/metric-value.md +23 -0
- package/docs/en/parts/multi-select.md +27 -0
- package/docs/en/parts/page-shell.md +45 -0
- package/docs/en/parts/searchable-select.md +47 -0
- package/docs/en/parts/section-header.md +20 -0
- package/docs/en/parts/segmented-control.md +25 -0
- package/docs/en/parts/status-badge.md +44 -0
- package/docs/en/parts/trend-indicator.md +26 -0
- package/docs/en/routing.md +95 -0
- package/docs/en/ui/accordion.md +36 -0
- package/docs/en/ui/alert.md +31 -0
- package/docs/en/ui/badge.md +23 -0
- package/docs/en/ui/breadcrumb.md +38 -0
- package/docs/en/ui/button.md +39 -0
- package/docs/en/ui/calendar.md +28 -0
- package/docs/en/ui/card.md +35 -0
- package/docs/en/ui/checkbox.md +34 -0
- package/docs/en/ui/cn.md +34 -0
- package/docs/en/ui/collapsible.md +21 -0
- package/docs/en/ui/command.md +37 -0
- package/docs/en/ui/dialog.md +52 -0
- package/docs/en/ui/dropdown-menu.md +50 -0
- package/docs/en/ui/empty.md +22 -0
- package/docs/en/ui/error-state.md +50 -0
- package/docs/en/ui/input-group.md +30 -0
- package/docs/en/ui/input.md +28 -0
- package/docs/en/ui/label.md +19 -0
- package/docs/en/ui/loading.md +19 -0
- package/docs/en/ui/popover.md +34 -0
- package/docs/en/ui/progress.md +28 -0
- package/docs/en/ui/scroll-area.md +23 -0
- package/docs/en/ui/select.md +63 -0
- package/docs/en/ui/separator.md +15 -0
- package/docs/en/ui/sheet.md +28 -0
- package/docs/en/ui/sidebar.md +92 -0
- package/docs/en/ui/skeleton.md +16 -0
- package/docs/en/ui/slider.md +21 -0
- package/docs/en/ui/spinner.md +14 -0
- package/docs/en/ui/switch.md +19 -0
- package/docs/en/ui/table.md +24 -0
- package/docs/en/ui/tabs.md +23 -0
- package/docs/en/ui/textarea.md +15 -0
- package/docs/en/ui/toggle-group.md +26 -0
- package/docs/en/ui/toggle.md +19 -0
- package/docs/en/ui/tooltip.md +22 -0
- package/docs/en/ui/use-is-mobile.md +18 -0
- package/docs/en/ui-and-theming.md +95 -0
- package/docs/index.json +1306 -0
- package/docs/ja/agent-skills.md +87 -0
- package/docs/ja/api-and-server.md +84 -0
- package/docs/ja/build-and-deploy.md +279 -0
- package/docs/ja/cli-reference.md +305 -0
- package/docs/ja/components.md +229 -0
- package/docs/ja/data-fetching.md +78 -0
- package/docs/ja/environment-variables.md +68 -0
- package/docs/ja/getting-started.md +95 -0
- package/docs/ja/index.md +65 -0
- package/docs/ja/markdown/markdown-renderer.md +43 -0
- package/docs/ja/pages-and-metadata.md +56 -0
- package/docs/ja/parts/app-shell.md +54 -0
- package/docs/ja/parts/dashboard-card.md +64 -0
- package/docs/ja/parts/data-table.md +85 -0
- package/docs/ja/parts/date-range-picker.md +41 -0
- package/docs/ja/parts/echart.md +52 -0
- package/docs/ja/parts/filter-bar.md +64 -0
- package/docs/ja/parts/funnel-steps.md +33 -0
- package/docs/ja/parts/metric-value.md +25 -0
- package/docs/ja/parts/multi-select.md +27 -0
- package/docs/ja/parts/page-shell.md +45 -0
- package/docs/ja/parts/searchable-select.md +47 -0
- package/docs/ja/parts/section-header.md +20 -0
- package/docs/ja/parts/segmented-control.md +26 -0
- package/docs/ja/parts/status-badge.md +44 -0
- package/docs/ja/parts/trend-indicator.md +26 -0
- package/docs/ja/routing.md +95 -0
- package/docs/ja/ui/accordion.md +37 -0
- package/docs/ja/ui/alert.md +31 -0
- package/docs/ja/ui/badge.md +23 -0
- package/docs/ja/ui/breadcrumb.md +38 -0
- package/docs/ja/ui/button.md +39 -0
- package/docs/ja/ui/calendar.md +28 -0
- package/docs/ja/ui/card.md +35 -0
- package/docs/ja/ui/checkbox.md +34 -0
- package/docs/ja/ui/cn.md +34 -0
- package/docs/ja/ui/collapsible.md +21 -0
- package/docs/ja/ui/command.md +36 -0
- package/docs/ja/ui/dialog.md +51 -0
- package/docs/ja/ui/dropdown-menu.md +50 -0
- package/docs/ja/ui/empty.md +22 -0
- package/docs/ja/ui/error-state.md +50 -0
- package/docs/ja/ui/input-group.md +30 -0
- package/docs/ja/ui/input.md +28 -0
- package/docs/ja/ui/label.md +19 -0
- package/docs/ja/ui/loading.md +19 -0
- package/docs/ja/ui/popover.md +34 -0
- package/docs/ja/ui/progress.md +28 -0
- package/docs/ja/ui/scroll-area.md +24 -0
- package/docs/ja/ui/select.md +64 -0
- package/docs/ja/ui/separator.md +15 -0
- package/docs/ja/ui/sheet.md +28 -0
- package/docs/ja/ui/sidebar.md +92 -0
- package/docs/ja/ui/skeleton.md +16 -0
- package/docs/ja/ui/slider.md +21 -0
- package/docs/ja/ui/spinner.md +14 -0
- package/docs/ja/ui/switch.md +19 -0
- package/docs/ja/ui/table.md +24 -0
- package/docs/ja/ui/tabs.md +24 -0
- package/docs/ja/ui/textarea.md +15 -0
- package/docs/ja/ui/toggle-group.md +27 -0
- package/docs/ja/ui/toggle.md +19 -0
- package/docs/ja/ui/tooltip.md +22 -0
- package/docs/ja/ui/use-is-mobile.md +18 -0
- package/docs/ja/ui-and-theming.md +95 -0
- package/package.json +29 -4
- package/registry/blocks/sales-overview.tsx +43 -19
- package/registry/ui/data-table.tsx +702 -102
- package/skills/vantage-add-feature/SKILL.md +150 -0
- package/skills/vantage-app/SKILL.md +258 -0
- package/skills/vantage-pitfalls/SKILL.md +115 -0
- package/templates/AGENTS.md +268 -0
- package/theme.css +178 -40
- package/dist/chunk-73J5ZD4C.js.map +0 -1
- package/dist/chunk-ZGDU5YLH.js.map +0 -1
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# MetricValue
|
|
2
|
+
|
|
3
|
+
> KPI の数値。単位は MetricUnit で。
|
|
4
|
+
|
|
5
|
+
KPI の数値を大きく見せるための、字送りを詰めた表示です。単位や補助的な文字は `MetricUnit` で
|
|
6
|
+
添えます。
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import { MetricValue, MetricUnit, metricValueVariants } from "@squadbase/vantage/components";
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
| Prop | Type | Default | 説明 |
|
|
13
|
+
| --- | --- | --- | --- |
|
|
14
|
+
| `size` | `"sm" \| "md" \| "lg" \| "xl"` | `"md"` | 文字サイズ。 |
|
|
15
|
+
| `className` | `string` | | 足すクラス。 |
|
|
16
|
+
|
|
17
|
+
既定で上下に余白(`my-4`)が付きます。カードの中でぴったり詰めたいときは `className="my-0"` を
|
|
18
|
+
渡してください。
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
<DashboardCardPreset title="売上">
|
|
22
|
+
<MetricValue className="my-0">¥1,240,000</MetricValue>
|
|
23
|
+
<TrendIndicator value={12.4} direction="up" />
|
|
24
|
+
</DashboardCardPreset>
|
|
25
|
+
```
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# MultiSelect
|
|
2
|
+
|
|
3
|
+
> 検索できる複数選択。選択はチップで表示。
|
|
4
|
+
|
|
5
|
+
検索欄付きの複数選択です。選んだものはトリガーにチップとして並び、`maxCount` を超えた分は
|
|
6
|
+
「+N」にまとめられます。
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import { MultiSelect } from "@squadbase/vantage/components";
|
|
10
|
+
import type { SelectOption, SelectOptionGroup } from "@squadbase/vantage/components";
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
| Prop | Type | 説明 |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| `options` | `SelectOption[] \| SelectOptionGroup[]` | 候補。グループ配列を渡すと見出し付きになる。 |
|
|
16
|
+
| `value` | `string[]` | 選択中の値。 |
|
|
17
|
+
| `onChange` | `(value: string[]) => void` | 変更時。 |
|
|
18
|
+
| `maxCount` | `number` | チップとして並べる上限。超えた分は「+N」になる。 |
|
|
19
|
+
| `onSearch` | `(query: string) => void` | 入力のたびに呼ばれる。サーバー側検索に使う。 |
|
|
20
|
+
| `isLoading` | `boolean` | 候補を取得中。 |
|
|
21
|
+
| `placeholder` | `string` | 未選択時のトリガー表示。 |
|
|
22
|
+
| `searchPlaceholder` | `string` | 検索欄のプレースホルダ。 |
|
|
23
|
+
| `emptyText` | `string` | 0 件のときの表示。 |
|
|
24
|
+
| `disabled` | `boolean` | 操作を無効にする。 |
|
|
25
|
+
|
|
26
|
+
フィルタ行の中で使うなら、[`FilterBar`](parts/filter-bar) の `FilterBarMultiSelect`
|
|
27
|
+
が値の受け渡しまで面倒を見てくれます。
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# PageShell
|
|
2
|
+
|
|
3
|
+
> ヘッダー帯・サマリー・本文からなるページ枠。
|
|
4
|
+
|
|
5
|
+
1 ページぶんの枠です。全幅のヘッダー帯、その中に置けるサマリーカードの行、そして本文の 3 段で
|
|
6
|
+
できています。各パーツは薄い `<div>` なので、必要なものだけ使えます。
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import {
|
|
10
|
+
PageShell,
|
|
11
|
+
PageShellHeader,
|
|
12
|
+
PageShellHeading,
|
|
13
|
+
PageShellTitle,
|
|
14
|
+
PageShellDescription,
|
|
15
|
+
PageShellHeaderEnd,
|
|
16
|
+
PageShellMeta,
|
|
17
|
+
PageShellActions,
|
|
18
|
+
PageShellSummary,
|
|
19
|
+
PageShellSummaryCard,
|
|
20
|
+
PageShellContent,
|
|
21
|
+
pageShellSummaryCardVariants,
|
|
22
|
+
} from "@squadbase/vantage/components";
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## パーツ
|
|
26
|
+
|
|
27
|
+
| パーツ | 役割 |
|
|
28
|
+
| --- | --- |
|
|
29
|
+
| `PageShell` | 全体の縦積み |
|
|
30
|
+
| `PageShellHeader` | 下線付きのヘッダー帯。左に見出し、右に `HeaderEnd` の 2 列 |
|
|
31
|
+
| `PageShellHeading` / `PageShellTitle` / `PageShellDescription` | 見出しブロック |
|
|
32
|
+
| `PageShellHeaderEnd` | 右端の縦積み |
|
|
33
|
+
| `PageShellMeta` | 「最終更新 10 分前」のような補足 |
|
|
34
|
+
| `PageShellActions` | ボタン行 |
|
|
35
|
+
| `PageShellSummary` | サマリーカードのグリッド(ヘッダー内で全幅) |
|
|
36
|
+
| `PageShellSummaryCard` | サマリー 1 枚。`accent` で色が付く |
|
|
37
|
+
| `PageShellContent` | 本文 |
|
|
38
|
+
|
|
39
|
+
`PageShellSummaryCard` の `accent` は `default` / `accent` / `amber` / `blue` / `emerald` / `red`
|
|
40
|
+
です。`accent` はテーマの `--chart-1` を使うので、テーマトークンを変えると一緒に変わります。
|
|
41
|
+
|
|
42
|
+
## レイアウトと組み合わせる
|
|
43
|
+
|
|
44
|
+
ページごとに書くより、`_layout.tsx` に置いて全ページで共有する方が実際的です
|
|
45
|
+
([ルーティング](routing) を参照)。
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# SearchableSelect
|
|
2
|
+
|
|
3
|
+
> 検索できる単一選択。候補が多いときに。
|
|
4
|
+
|
|
5
|
+
検索欄の付いた単一選択です。候補が多くて [`Select`](ui/select) では探しづらいときに
|
|
6
|
+
使います。`onSearch` を渡せばサーバー側検索にもできます。
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import { SearchableSelect } from "@squadbase/vantage/components";
|
|
10
|
+
import type { SelectOption, SelectOptionGroup } from "@squadbase/vantage/components";
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
| Prop | Type | 説明 |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| `options` | `SelectOption[] \| SelectOptionGroup[]` | 候補。グループ配列を渡すと見出し付きになる。 |
|
|
16
|
+
| `value` | `string` | 選択中の値。 |
|
|
17
|
+
| `onChange` | `(value: string \| undefined) => void` | 変更時。 |
|
|
18
|
+
| `onSearch` | `(query: string) => void` | 入力のたびに呼ばれる。サーバー側検索に使う。 |
|
|
19
|
+
| `clearable` | `boolean` | 選択解除できるようにする。 |
|
|
20
|
+
| `isLoading` | `boolean` | 候補を取得中。 |
|
|
21
|
+
| `placeholder` | `string` | 未選択時のトリガー表示。 |
|
|
22
|
+
| `searchPlaceholder` | `string` | 検索欄のプレースホルダ。 |
|
|
23
|
+
| `emptyText` | `string` | 0 件のときの表示。 |
|
|
24
|
+
| `disabled` | `boolean` | 操作を無効にする。 |
|
|
25
|
+
|
|
26
|
+
## サーバー側で検索する
|
|
27
|
+
|
|
28
|
+
`onSearch` を渡して `options` を差し替えます。絞り込みはサーバーの仕事になるので、返ってきた
|
|
29
|
+
候補をそのまま渡してください。
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
const [query, setQuery] = useState("");
|
|
33
|
+
const { data, isFetching } = useQuery({
|
|
34
|
+
queryKey: ["customers", query],
|
|
35
|
+
queryFn: () => apiFetch(`/api/customers?q=${query}`).then((r) => r.json()),
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
<SearchableSelect
|
|
39
|
+
options={data ?? []}
|
|
40
|
+
onSearch={setQuery}
|
|
41
|
+
isLoading={isFetching}
|
|
42
|
+
value={value}
|
|
43
|
+
onChange={setValue}
|
|
44
|
+
/>;
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
複数選べるようにしたいときは [`MultiSelect`](parts/multi-select) を使います。
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# SectionHeader
|
|
2
|
+
|
|
3
|
+
> セクションの見出しとアクション。
|
|
4
|
+
|
|
5
|
+
ページの中でセクションを区切る見出しです。見出しレベルは `as` で選べます。
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { SectionHeader } from "@squadbase/vantage/components";
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
| Prop | Type | Default | 説明 |
|
|
12
|
+
| --- | --- | --- | --- |
|
|
13
|
+
| `title` | `string` | | 見出し。 |
|
|
14
|
+
| `description` | `string` | | 見出しの下の補足文。 |
|
|
15
|
+
| `actions` | `ReactNode` | | 右端に置く要素。 |
|
|
16
|
+
| `as` | `"h1" \| "h2" \| "h3" \| "h4"` | `"h2"` | 見出しレベル。文字サイズも一緒に変わる。 |
|
|
17
|
+
| `className` | `string` | | 足すクラス。 |
|
|
18
|
+
|
|
19
|
+
ページ全体の見出しには [`PageShell`](parts/page-shell) のヘッダー帯を使ってください。
|
|
20
|
+
`SectionHeader` はその中のセクション向けです。
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# SegmentedControl
|
|
2
|
+
|
|
3
|
+
> 単一選択のセグメント。ToggleGroup の配列を隠す。
|
|
4
|
+
|
|
5
|
+
2〜4 個の選択肢を横一列で切り替えます。中身は [`ToggleGroup`](ui/toggle-group) ですが、
|
|
6
|
+
**単一選択なので `value` は文字列 1 つ**です — Base UI の配列 API を扱う手間はここで吸収されて
|
|
7
|
+
います。
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { SegmentedControl } from "@squadbase/vantage/components";
|
|
11
|
+
import type { SegmentOption } from "@squadbase/vantage/components";
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
| Prop | Type | Default | 説明 |
|
|
15
|
+
| --- | --- | --- | --- |
|
|
16
|
+
| `options` | `SegmentOption[]` | | `{ label, value, icon?, disabled? }` の配列。 |
|
|
17
|
+
| `value` | `string` | | 選択中の値(**文字列 1 つ**)。 |
|
|
18
|
+
| `onChange` | `(value: string) => void` | | 変更時。 |
|
|
19
|
+
| `size` | `"sm" \| "default" \| "lg"` | `"default"` | アイコンの大きさ。 |
|
|
20
|
+
| `fullWidth` | `boolean` | | 親の幅いっぱいに広げる。 |
|
|
21
|
+
| `ariaLabel` | `string` | | グループ全体の説明。 |
|
|
22
|
+
|
|
23
|
+
`icon` には `lucide-react` のコンポーネントをそのまま渡せます。
|
|
24
|
+
|
|
25
|
+
選択肢が 5 個を超えるなら [`Select`](ui/select)、切り替える対象がページ相当なら
|
|
26
|
+
[`Tabs`](ui/tabs) の方が向いています。
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# StatusBadge
|
|
2
|
+
|
|
3
|
+
> ドット付きのステータスバッジ。色は名前で決まる。
|
|
4
|
+
|
|
5
|
+
行やレコードの状態を、色付きのドットとラベルで示します。状態の文字列から色への対応は
|
|
6
|
+
組み込みの表で決まり、`colorMap` で足したり上書きしたりできます。
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import { StatusBadge } from "@squadbase/vantage/components";
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
| Prop | Type | 説明 |
|
|
13
|
+
| --- | --- | --- |
|
|
14
|
+
| `status` | `string` | 状態の文字列。色はこれで決まる。 |
|
|
15
|
+
| `label` | `string` | 表示するテキスト。省くと `status` がそのまま出る。 |
|
|
16
|
+
| `colorMap` | `Record<string, string>` | 状態 → 色名の対応。既定の表に上書きされる。 |
|
|
17
|
+
| `className` | `string` | 足すクラス。 |
|
|
18
|
+
|
|
19
|
+
## 既定の対応
|
|
20
|
+
|
|
21
|
+
| `status` | 色 |
|
|
22
|
+
| --- | --- |
|
|
23
|
+
| `active` | green |
|
|
24
|
+
| `inactive` | gray |
|
|
25
|
+
| `warning` | yellow |
|
|
26
|
+
| `error` | red |
|
|
27
|
+
| `pending` | blue |
|
|
28
|
+
|
|
29
|
+
未知の状態は gray になります。独自の状態を足すときは `colorMap` に
|
|
30
|
+
`green` / `gray` / `yellow` / `red` / `blue` のいずれかを割り当ててください。
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
<StatusBadge status="reviewing" label="レビュー中" colorMap={{ reviewing: "blue" }} />
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
テーブルのセルにそのまま置けます。
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
{
|
|
40
|
+
accessorKey: "status",
|
|
41
|
+
header: "状態",
|
|
42
|
+
cell: ({ row }) => <StatusBadge status={row.original.status} />,
|
|
43
|
+
}
|
|
44
|
+
```
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# TrendIndicator
|
|
2
|
+
|
|
3
|
+
> 増減の矢印と変化率。良し悪しは positiveIsGood で決まる。
|
|
4
|
+
|
|
5
|
+
前期比などの変化を、矢印と変化率で示します。**「上がった=良い」とは限らない**ので、色は
|
|
6
|
+
`direction` と `positiveIsGood` の組み合わせで決まります — 解約率のように下がる方が良い指標では
|
|
7
|
+
`positiveIsGood={false}` を渡してください。
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { TrendIndicator } from "@squadbase/vantage/components";
|
|
11
|
+
import type { TrendDirection } from "@squadbase/vantage/components";
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
| Prop | Type | Default | 説明 |
|
|
15
|
+
| --- | --- | --- | --- |
|
|
16
|
+
| `value` | `number` | | 変化率(`12.4` で 12.4%)。 |
|
|
17
|
+
| `direction` | `"up" \| "down" \| "neutral"` | | 矢印の向き。**必須**。 |
|
|
18
|
+
| `positiveIsGood` | `boolean` | `true` | 上がることが良いか。`false` にすると色が反転する。 |
|
|
19
|
+
| `className` | `string` | | 足すクラス。 |
|
|
20
|
+
|
|
21
|
+
> [!WARNING]
|
|
22
|
+
> `direction` は必須です。`value` の符号からは推測しません — 「値は減ったが、それは良いこと」と
|
|
23
|
+
> いう場合を表現できるようにするためです。
|
|
24
|
+
|
|
25
|
+
色は 3 通りです。良い方向なら `--chart-1`、悪い方向なら `--primary`、`neutral` なら
|
|
26
|
+
`--muted-foreground`。テーマトークンを変えればここも一緒に変わります。
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# ファイルベースルーティング
|
|
2
|
+
|
|
3
|
+
> ファイルを置くだけでルートが生まれる。ページ・レイアウト・動的パラメータ・404/error の規約。
|
|
4
|
+
|
|
5
|
+
Vantage のルーティングは **ファイルの配置がそのまま URL** になります。ルーターの設定は書きません。
|
|
6
|
+
|
|
7
|
+
## ページの規約
|
|
8
|
+
|
|
9
|
+
| ファイル | ルート |
|
|
10
|
+
|---|---|
|
|
11
|
+
| `index.tsx` | `/` |
|
|
12
|
+
| `monthly-analysis.tsx` | `/monthly-analysis` |
|
|
13
|
+
| `sales/index.tsx` | `/sales` |
|
|
14
|
+
| `sales/[customerId].tsx` | `/sales/:customerId` |
|
|
15
|
+
| `sales/[...path].tsx` | `/sales/*`(catchall) |
|
|
16
|
+
| `_layout.tsx` | そのディレクトリ配下のレイアウト(入れ子可) |
|
|
17
|
+
| `_404.tsx` | not-found の UI(**ルートのみ**) |
|
|
18
|
+
| `_error.tsx` | error の UI(**ルートのみ**) |
|
|
19
|
+
|
|
20
|
+
- `[name]` → 動的パラメータ、`[...name]` → catchall。
|
|
21
|
+
- `index` は自身のディレクトリにマップ(名前は URL から落ちる)。
|
|
22
|
+
- `_` で始まるファイルは予約(`_layout`/`_404`/`_error`)またはプライベート(ルーティングされない)。
|
|
23
|
+
|
|
24
|
+
## ルーティングされないディレクトリ
|
|
25
|
+
|
|
26
|
+
次のディレクトリは決して走査されません。ここには自由にコードを置けます。
|
|
27
|
+
|
|
28
|
+
```text
|
|
29
|
+
components/ 再利用する React コンポーネント
|
|
30
|
+
hooks/ カスタムフック
|
|
31
|
+
lib/ ユーティリティ
|
|
32
|
+
server/ API(存在するとサーバーが有効になる)
|
|
33
|
+
public/ 静的アセット
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
> [!NOTE]
|
|
37
|
+
> ファイル間の import は **ランタイムの `.js` 指定子**を使います(例: `./components/revenue-chart.js`)。
|
|
38
|
+
> ESM の Node 解決に必要です。
|
|
39
|
+
|
|
40
|
+
## 動的パラメータの 3 つの綴り
|
|
41
|
+
|
|
42
|
+
同じパラメータが、場所によって 3 つの綴りで現れます。**必ず一致させます。**
|
|
43
|
+
|
|
44
|
+
| 場所 | 綴り | 例 |
|
|
45
|
+
|---|---|---|
|
|
46
|
+
| ファイル名 | `[id]` | `sales/[customerId].tsx` |
|
|
47
|
+
| 表示ルート | `:id` | `/sales/:customerId` |
|
|
48
|
+
| リンク | `$id` + `params` | `to="/sales/$customerId"` `params={{ customerId }}` |
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
import { Link, useParams } from "@squadbase/vantage/router";
|
|
52
|
+
|
|
53
|
+
function Row({ customerId }: { customerId: string }) {
|
|
54
|
+
return (
|
|
55
|
+
<Link to="/sales/$customerId" params={{ customerId }}>
|
|
56
|
+
顧客を開く
|
|
57
|
+
</Link>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// sales/[customerId].tsx
|
|
62
|
+
export default function Customer() {
|
|
63
|
+
const { customerId } = useParams({ strict: false });
|
|
64
|
+
return <div>顧客 {customerId}</div>;
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## レイアウトと入れ子
|
|
69
|
+
|
|
70
|
+
`_layout.tsx` は `<Outlet />` を描画して子ルートを囲みます。ディレクトリごとに置けば入れ子になります。
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
// _layout.tsx
|
|
74
|
+
import { Outlet } from "@squadbase/vantage/router";
|
|
75
|
+
|
|
76
|
+
export default function RootLayout() {
|
|
77
|
+
return (
|
|
78
|
+
<div className="min-h-screen">
|
|
79
|
+
<nav className="border-b p-4">My Dashboard</nav>
|
|
80
|
+
<Outlet />
|
|
81
|
+
</div>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`_404.tsx` と `_error.tsx` は **ルート(プロジェクト直下)でのみ**認識されます。
|
|
87
|
+
|
|
88
|
+
## HMR とルート再生成
|
|
89
|
+
|
|
90
|
+
`.tsx` ページや `server/api` ファイルを追加・削除すると、開発サーバーがルートを再生成して
|
|
91
|
+
フルリロードします。ページ本体を編集した場合は React Fast Refresh がその場で反映します。
|
|
92
|
+
|
|
93
|
+
## 次に読む
|
|
94
|
+
|
|
95
|
+
- [ページと definePage](pages-and-metadata) — ページの契約とメタデータ
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Accordion
|
|
2
|
+
|
|
3
|
+
> 開閉する見出しの縦積み。既定では 1 つだけ開く。
|
|
4
|
+
|
|
5
|
+
見出しをクリックして中身を開閉するリストです。既定では一度に 1 つだけ開きます。複数を同時に開き
|
|
6
|
+
たいときは `openMultiple` を付けます。
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import {
|
|
10
|
+
Accordion,
|
|
11
|
+
AccordionItem,
|
|
12
|
+
AccordionTrigger,
|
|
13
|
+
AccordionContent,
|
|
14
|
+
} from "@squadbase/vantage/ui";
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## パーツ
|
|
18
|
+
|
|
19
|
+
| パーツ | 役割 |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| `Accordion` | 全体。`value` / `defaultValue` は**開いている項目の配列** |
|
|
22
|
+
| `AccordionItem` | 1 項目。`value` で識別する |
|
|
23
|
+
| `AccordionTrigger` | 見出しボタン。シェブロンは自動で付く |
|
|
24
|
+
| `AccordionContent` | 開閉する中身 |
|
|
25
|
+
|
|
26
|
+
## 複数同時に開く
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
<Accordion openMultiple defaultValue={["a", "b"]}>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
> [!NOTE]
|
|
33
|
+
> `value` は Radix 版のような文字列ではなく**配列**です(Base UI の仕様)。1 つだけ開く場合も
|
|
34
|
+
> `defaultValue={["freshness"]}` のように配列で渡します。
|
|
35
|
+
|
|
36
|
+
常に開いている単独の折りたたみが欲しいだけなら
|
|
37
|
+
[`Collapsible`](ui/collapsible) の方が軽量です。
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Alert
|
|
2
|
+
|
|
3
|
+
> ページ内に置く通知。中身は表示したまま注意を促す。
|
|
4
|
+
|
|
5
|
+
ページの中に置く通知です。「この画面は失敗した」を表す
|
|
6
|
+
[`ErrorState`](ui/error-state) と違い、中身が表示できている上での注意喚起に使います。
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import { Alert, AlertTitle, AlertDescription, AlertAction } from "@squadbase/vantage/ui";
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## パーツと variant
|
|
13
|
+
|
|
14
|
+
| パーツ | 役割 |
|
|
15
|
+
| --- | --- |
|
|
16
|
+
| `Alert` | 枠。`variant`: `default` \| `destructive` |
|
|
17
|
+
| `AlertTitle` | 見出し |
|
|
18
|
+
| `AlertDescription` | 本文 |
|
|
19
|
+
| `AlertAction` | 右端に寄せるアクション |
|
|
20
|
+
|
|
21
|
+
アイコンは `Alert` の直下に置くだけで、グリッドが自動で 2 列になります。`lucide-react` から直接
|
|
22
|
+
import して構いません。
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
import { Info } from "lucide-react";
|
|
26
|
+
|
|
27
|
+
<Alert>
|
|
28
|
+
<Info />
|
|
29
|
+
<AlertTitle>読み取り専用です</AlertTitle>
|
|
30
|
+
</Alert>;
|
|
31
|
+
```
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Badge
|
|
2
|
+
|
|
3
|
+
> 小さなラベル。件数・状態・タグに。
|
|
4
|
+
|
|
5
|
+
テキストの横に添える小さなラベルです。行の状態を色付きのドットで示したいときは
|
|
6
|
+
[`StatusBadge`](parts/status-badge) を使ってください。
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import { Badge, badgeVariants } from "@squadbase/vantage/ui";
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Variant
|
|
13
|
+
|
|
14
|
+
`default` / `secondary` / `destructive` / `outline` / `ghost` / `link`。
|
|
15
|
+
|
|
16
|
+
`badgeVariants` はクラス文字列を返す関数で、`<a>` など別の要素にバッジの見た目を移したいときに
|
|
17
|
+
使います。
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
<a href="/docs" className={badgeVariants({ variant: "outline" })}>
|
|
21
|
+
ドキュメント
|
|
22
|
+
</a>
|
|
23
|
+
```
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Breadcrumb
|
|
2
|
+
|
|
3
|
+
> 階層を示すパンくずリスト。
|
|
4
|
+
|
|
5
|
+
いま見ているページが階層のどこにあるかを示します。ドリルダウンする詳細ページ
|
|
6
|
+
(`/sales/$customerId` など)で特に効きます。
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import {
|
|
10
|
+
Breadcrumb,
|
|
11
|
+
BreadcrumbList,
|
|
12
|
+
BreadcrumbItem,
|
|
13
|
+
BreadcrumbLink,
|
|
14
|
+
BreadcrumbPage,
|
|
15
|
+
BreadcrumbSeparator,
|
|
16
|
+
BreadcrumbEllipsis,
|
|
17
|
+
} from "@squadbase/vantage/ui";
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## パーツ
|
|
21
|
+
|
|
22
|
+
| パーツ | 役割 |
|
|
23
|
+
| --- | --- |
|
|
24
|
+
| `BreadcrumbLink` | 途中の階層(リンク) |
|
|
25
|
+
| `BreadcrumbPage` | 現在地。リンクにしない |
|
|
26
|
+
| `BreadcrumbSeparator` | 区切り。中身を渡せば記号を差し替えられる |
|
|
27
|
+
| `BreadcrumbEllipsis` | 省略した中間階層 |
|
|
28
|
+
|
|
29
|
+
## ルーターのリンクと組み合わせる
|
|
30
|
+
|
|
31
|
+
`BreadcrumbLink` は `render` プロップで別のコンポーネントに置き換えられます。Vantage の
|
|
32
|
+
[`Link`](routing) を渡せばクライアントサイド遷移になります。
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
import { Link } from "@squadbase/vantage/router";
|
|
36
|
+
|
|
37
|
+
<BreadcrumbLink render={<Link to="/sales" />}>売上</BreadcrumbLink>;
|
|
38
|
+
```
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Button
|
|
2
|
+
|
|
3
|
+
> 6 つの variant と 8 つの size を持つボタン。
|
|
4
|
+
|
|
5
|
+
いちばんよく使うコンポーネントです。`variant` で意味づけを、`size` で大きさを決めます。
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Button, buttonVariants } from "@squadbase/vantage/ui";
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
| Prop | Type | Default | 説明 |
|
|
12
|
+
| --- | --- | --- | --- |
|
|
13
|
+
| `variant` | `"default" \| "secondary" \| "outline" \| "ghost" \| "destructive" \| "link"` | `"default"` | 見た目と意味づけ。 |
|
|
14
|
+
| `size` | `"default" \| "xs" \| "sm" \| "lg" \| "icon" \| "icon-xs" \| "icon-sm" \| "icon-lg"` | `"default"` | 高さと余白。`icon` 系は正方形になる。 |
|
|
15
|
+
| `render` | `ReactElement` | | 別の要素として描画する(Radix の `asChild` に相当)。 |
|
|
16
|
+
|
|
17
|
+
アイコンは子要素として置くだけでサイズと余白が揃います。アイコンだけのボタンには
|
|
18
|
+
`size="icon"` と `aria-label` を必ず付けてください。
|
|
19
|
+
|
|
20
|
+
## リンクをボタンに見せる
|
|
21
|
+
|
|
22
|
+
`render` に要素を渡します。`asChild` は Base UI にはありません。
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
import { Link } from "@squadbase/vantage/router";
|
|
26
|
+
|
|
27
|
+
<Button render={<Link to="/sales" />}>売上を見る</Button>;
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
クラス文字列だけが欲しいときは `buttonVariants({ variant, size })` を使います。
|
|
31
|
+
|
|
32
|
+
## 処理中の表示
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
<Button disabled={isPending}>
|
|
36
|
+
{isPending && <Spinner />}
|
|
37
|
+
保存
|
|
38
|
+
</Button>
|
|
39
|
+
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Calendar
|
|
2
|
+
|
|
3
|
+
> react-day-picker ベースの日付カレンダー。
|
|
4
|
+
|
|
5
|
+
単日・複数日・期間を選べるカレンダーです。ダッシュボードで期間を選ばせたい場合は、これを直接
|
|
6
|
+
使うより [`DateRangePicker`](parts/date-range-picker) の方が手早く済みます。
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import { Calendar, CalendarDayButton } from "@squadbase/vantage/ui";
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
props は [react-day-picker](https://daypicker.dev) の `DayPicker` そのままで、Vantage 側で足して
|
|
13
|
+
いるのは次の 3 つだけです。
|
|
14
|
+
|
|
15
|
+
| Prop | Type | Default | 説明 |
|
|
16
|
+
| --- | --- | --- | --- |
|
|
17
|
+
| `captionLayout` | `"label" \| "dropdown" \| "dropdown-months" \| "dropdown-years"` | `"label"` | 月・年の見出しをテキストにするかセレクトにするか。 |
|
|
18
|
+
| `buttonVariant` | `Button の variant` | `"ghost"` | 前後の月へ送るボタンの見た目。 |
|
|
19
|
+
| `showOutsideDays` | `boolean` | `true` | 前後の月の日を薄く表示するか。 |
|
|
20
|
+
|
|
21
|
+
`mode` は `"single"` / `"multiple"` / `"range"` で、`selected` と `onSelect` の型がそれに追従
|
|
22
|
+
します。`CalendarDayButton` は 1 日分のセルで、日付の下にドットやラベルを描きたいときに差し
|
|
23
|
+
替えます。
|
|
24
|
+
|
|
25
|
+
> [!NOTE]
|
|
26
|
+
> ロケールは `locale` プロップに `date-fns` のロケールを渡して切り替えます。`date-fns` は
|
|
27
|
+
> Vantage の依存に含まれていますが、アプリからの直接 import は避け、必要なら
|
|
28
|
+
> [`DateRangePicker`](parts/date-range-picker) 側で完結させるのが安全です。
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Card
|
|
2
|
+
|
|
3
|
+
> 汎用のカード。ダッシュボードのタイルには DashboardCard を。
|
|
4
|
+
|
|
5
|
+
汎用のカードです。ダッシュボードのタイルには、余白とスケルトンまで組んである
|
|
6
|
+
[`DashboardCard`](parts/dashboard-card) の方が向いています。
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import {
|
|
10
|
+
Card,
|
|
11
|
+
CardHeader,
|
|
12
|
+
CardTitle,
|
|
13
|
+
CardDescription,
|
|
14
|
+
CardAction,
|
|
15
|
+
CardContent,
|
|
16
|
+
CardFooter,
|
|
17
|
+
} from "@squadbase/vantage/ui";
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## パーツ
|
|
21
|
+
|
|
22
|
+
| パーツ | 役割 |
|
|
23
|
+
| --- | --- |
|
|
24
|
+
| `Card` | 枠。`size`: `default` \| `sm`(内側の余白が変わる) |
|
|
25
|
+
| `CardHeader` | 見出し行 |
|
|
26
|
+
| `CardTitle` / `CardDescription` | 見出しと補足 |
|
|
27
|
+
| `CardAction` | 見出し行の右端。置くとヘッダーが自動で 2 列になる |
|
|
28
|
+
| `CardContent` | 本文 |
|
|
29
|
+
| `CardFooter` | 下端。置くとカード下部の余白が自動で詰まる |
|
|
30
|
+
|
|
31
|
+
余白は `--card-spacing` という CSS 変数 1 つで決まっているので、個別に上書きできます。
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
<Card className="[--card-spacing:--spacing(6)]">…</Card>
|
|
35
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Checkbox
|
|
2
|
+
|
|
3
|
+
> チェックボックス。中間状態は indeterminate プロップで。
|
|
4
|
+
|
|
5
|
+
オン/オフの入力です。Base UI 版では `checked` が `boolean` のみを取り、中間状態は別プロップに
|
|
6
|
+
分かれています — Radix 版から移ってきた場合の最大の違いです。
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import { Checkbox } from "@squadbase/vantage/ui";
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
| Prop | Type | 説明 |
|
|
13
|
+
| --- | --- | --- |
|
|
14
|
+
| `checked` | `boolean` | チェック状態。`"indeterminate"` は受け付けない。 |
|
|
15
|
+
| `indeterminate` | `boolean` | 中間状態(一部だけ選択)。`checked` とは独立。 |
|
|
16
|
+
| `onCheckedChange` | `(checked: boolean) => void` | 変更時。値は必ず boolean。 |
|
|
17
|
+
| `disabled` | `boolean` | 操作を無効にする。 |
|
|
18
|
+
|
|
19
|
+
> [!WARNING]
|
|
20
|
+
> Radix 版の `checked={"indeterminate"}` は型エラーになります。Base UI では `indeterminate` を
|
|
21
|
+
> 独立したプロップとして渡してください。
|
|
22
|
+
|
|
23
|
+
## テーブルの全選択
|
|
24
|
+
|
|
25
|
+
[`DataTable`](parts/data-table) の選択列がこの形です。
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
<Checkbox
|
|
29
|
+
checked={table.getIsAllPageRowsSelected()}
|
|
30
|
+
indeterminate={table.getIsSomePageRowsSelected()}
|
|
31
|
+
onCheckedChange={(checked) => table.toggleAllPageRowsSelected(checked)}
|
|
32
|
+
aria-label="全て選択"
|
|
33
|
+
/>
|
|
34
|
+
```
|
package/docs/ja/ui/cn.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# cn()
|
|
2
|
+
|
|
3
|
+
> 条件付きでクラスを組み立て、競合を解決するヘルパー。
|
|
4
|
+
|
|
5
|
+
`className` を条件付きで組み立てるヘルパーです。`clsx` で条件を解決し、`tailwind-merge` で競合
|
|
6
|
+
するユーティリティを解決します。
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import { cn } from "@squadbase/vantage/ui";
|
|
10
|
+
import type { ClassValue } from "@squadbase/vantage/ui";
|
|
11
|
+
|
|
12
|
+
<div className={cn("p-4", isActive && "bg-accent", className)} />;
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## 後から渡したクラスが勝つ
|
|
16
|
+
|
|
17
|
+
`tailwind-merge` を通しているので、競合するユーティリティは**後ろが勝ちます**。
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
cn("p-2", "p-4"); // → "p-4"
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
これが効くおかげで、コンポーネントに渡した `className` が既定のスタイルを確実に上書きできます。
|
|
24
|
+
組み込みコンポーネントはすべて内部で `cn()` を使っているので、`className` は常に上書きとして
|
|
25
|
+
機能します。
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
// 既定の bg-primary を確実に打ち消せる
|
|
29
|
+
<Button className="bg-emerald-600 hover:bg-emerald-700">実行</Button>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
> [!NOTE]
|
|
33
|
+
> `clsx` や `tailwind-merge` を直接 import する必要はありません(アプリからの直接 import は
|
|
34
|
+
> 避けてください)。両方まとめてこの `cn()` が公開の入り口です。
|