@gunjo/ui 0.0.1-alpha.0 → 0.0.1-alpha.2
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/LICENSE +21 -0
- package/README.ja.md +90 -0
- package/README.md +52 -91
- package/package.json +47 -6
- package/src/components/display/Accordion.tsx +185 -0
- package/src/components/display/AccordionGroup.tsx +155 -0
- package/src/components/display/ActionDataTable.tsx +413 -0
- package/src/components/display/ActivityTimelineCard.tsx +483 -0
- package/src/components/display/AnalyticsCard.tsx +167 -0
- package/src/components/display/AssetCard.tsx +242 -0
- package/src/components/display/AssetGrid.tsx +164 -0
- package/src/components/display/Avatar.tsx +127 -0
- package/src/components/display/AvatarGroup.tsx +131 -0
- package/src/components/{atoms → display}/Badge.tsx +3 -3
- package/src/components/display/BarChart.tsx +247 -0
- package/src/components/{molecules → display}/Card.tsx +1 -1
- package/src/components/display/Carousel.tsx +593 -0
- package/src/components/display/ChartLegend.tsx +124 -0
- package/src/components/display/ChatMessage.tsx +382 -0
- package/src/components/display/ChoroplethMap.tsx +613 -0
- package/src/components/display/Code.tsx +42 -0
- package/src/components/display/CodeBlock.tsx +338 -0
- package/src/components/display/ColorSwatch.tsx +71 -0
- package/src/components/display/ConcentricProgressCard.tsx +545 -0
- package/src/components/display/DataTable.tsx +522 -0
- package/src/components/display/DistributionBar.tsx +102 -0
- package/src/components/display/DocNote.tsx +36 -0
- package/src/components/display/DonutChart.tsx +257 -0
- package/src/components/display/EmptyState.tsx +44 -0
- package/src/components/display/FileTree.tsx +180 -0
- package/src/components/display/GaugeChart.tsx +219 -0
- package/src/components/display/HeatmapChart.tsx +266 -0
- package/src/components/display/Icon.tsx +66 -0
- package/src/components/display/ImagePreview.tsx +140 -0
- package/src/components/{atoms → display}/Img.tsx +46 -12
- package/src/components/display/LabeledDonutCard.tsx +475 -0
- package/src/components/display/LineChart.tsx +464 -0
- package/src/components/{molecules → display}/List.tsx +20 -13
- package/src/components/display/MarkdownRenderer.tsx +157 -0
- package/src/components/display/MetadataList.tsx +81 -0
- package/src/components/display/MiniDistributionBarCard.tsx +314 -0
- package/src/components/display/PieChart.tsx +234 -0
- package/src/components/display/QuadrantMatrix.tsx +330 -0
- package/src/components/display/RadarChart.tsx +335 -0
- package/src/components/display/RadialBarChart.tsx +264 -0
- package/src/components/display/RetentionCohortCard.tsx +350 -0
- package/src/components/display/RibbonChart.tsx +618 -0
- package/src/components/display/SearchableAccordion.tsx +270 -0
- package/src/components/display/SegmentTimelineCard.tsx +452 -0
- package/src/components/display/SegmentedGaugeCard.tsx +607 -0
- package/src/components/display/Spacer.tsx +51 -0
- package/src/components/display/SparklineChart.tsx +394 -0
- package/src/components/display/StackedBarChart.tsx +393 -0
- package/src/components/display/Statistic.tsx +70 -0
- package/src/components/{molecules → display}/Table.tsx +22 -7
- package/src/components/display/Tag.tsx +80 -0
- package/src/components/display/TagEditor.tsx +141 -0
- package/src/components/display/Timeline.tsx +121 -0
- package/src/components/{atoms → display}/ToolPill.tsx +42 -18
- package/src/components/display/TreeView.tsx +226 -0
- package/src/components/display/chart-tooltip.tsx +423 -0
- package/src/components/display/chart-utils.ts +71 -0
- package/src/components/display/circular-chart-utils.ts +147 -0
- package/src/components/display/generated/default-variant-keys.ts +90 -0
- package/src/components/display/generated/variant-keys.ts +169 -0
- package/src/components/{atoms → feedback}/Alert.tsx +12 -5
- package/src/components/feedback/Banner.tsx +90 -0
- package/src/components/{molecules → feedback}/NotificationCenter.tsx +64 -31
- package/src/components/feedback/ProgressWidget.tsx +44 -0
- package/src/components/{atoms → feedback}/Spinner.tsx +2 -2
- package/src/components/{molecules → feedback}/StatusBar.tsx +4 -4
- package/src/components/feedback/StatusScreen.tsx +148 -0
- package/src/components/{molecules → feedback}/Stepper.tsx +10 -5
- package/src/components/feedback/Toast.tsx +108 -0
- package/src/components/feedback/ToastProvider.tsx +78 -0
- package/src/components/feedback/generated/default-variant-keys.ts +16 -0
- package/src/components/feedback/generated/variant-keys.ts +21 -0
- package/src/components/generated/component-manifest.ts +1568 -454
- package/src/components/generated/component-style-hints.ts +1958 -718
- package/src/components/{atoms → inputs}/ButtonVariants.ts +13 -3
- package/src/components/inputs/Calendar.tsx +212 -0
- package/src/components/inputs/ChatComposer.tsx +75 -0
- package/src/components/inputs/ChatInput.tsx +528 -0
- package/src/components/{atoms → inputs}/Checkbox.tsx +2 -2
- package/src/components/inputs/Combobox.tsx +175 -0
- package/src/components/inputs/CopyButton.tsx +187 -0
- package/src/components/inputs/DatePicker.tsx +519 -0
- package/src/components/inputs/DateRangePicker.tsx +878 -0
- package/src/components/inputs/EditableField.tsx +182 -0
- package/src/components/{organisms → inputs}/FileUploader.tsx +24 -9
- package/src/components/inputs/FilterButton.tsx +163 -0
- package/src/components/{molecules → inputs}/Form.tsx +20 -3
- package/src/components/{atoms → inputs}/Input.tsx +2 -0
- package/src/components/inputs/InputOTP.tsx +75 -0
- package/src/components/inputs/Mention.tsx +279 -0
- package/src/components/inputs/NumberInput.tsx +109 -0
- package/src/components/inputs/PasswordGroup.tsx +138 -0
- package/src/components/inputs/PasswordInput.tsx +74 -0
- package/src/components/inputs/PasswordRequirementList.tsx +96 -0
- package/src/components/inputs/PasswordStrengthMeter.tsx +93 -0
- package/src/components/inputs/PhoneInput.tsx +99 -0
- package/src/components/inputs/PostalCodeInput.tsx +98 -0
- package/src/components/inputs/RangeSlider.tsx +129 -0
- package/src/components/inputs/SearchInput.tsx +76 -0
- package/src/components/inputs/Select.tsx +39 -0
- package/src/components/{atoms → inputs}/Slider.tsx +18 -5
- package/src/components/{molecules → inputs}/SortButton.tsx +5 -2
- package/src/components/{atoms → inputs}/Switch.tsx +15 -4
- package/src/components/inputs/TagInput.tsx +114 -0
- package/src/components/{atoms → inputs}/Textarea.tsx +1 -0
- package/src/components/inputs/TimePicker.tsx +150 -0
- package/src/components/inputs/Toggle.tsx +48 -0
- package/src/components/{atoms → inputs}/ToggleGroup.tsx +2 -2
- package/src/components/inputs/TooltipButton.tsx +148 -0
- package/src/components/inputs/VoiceInputButton.tsx +317 -0
- package/src/components/inputs/calendar-holidays.ts +56 -0
- package/src/components/inputs/generated/default-variant-keys.ts +32 -0
- package/src/components/{atoms → inputs}/generated/variant-keys.ts +19 -27
- package/src/components/layout/AspectRatio.tsx +12 -0
- package/src/components/layout/AssetInspectorPanel.tsx +416 -0
- package/src/components/layout/Cluster.tsx +56 -0
- package/src/components/layout/CollapsiblePanelToggle.tsx +94 -0
- package/src/components/layout/Container.tsx +43 -0
- package/src/components/layout/DeviceFrame.tsx +227 -0
- package/src/components/layout/Grid.tsx +65 -0
- package/src/components/layout/HStack.tsx +73 -0
- package/src/components/{organisms → layout}/InspectorPanel.tsx +6 -5
- package/src/components/layout/MarqueeFrame.tsx +158 -0
- package/src/components/layout/Resizable.tsx +94 -0
- package/src/components/layout/ScrollArea.tsx +71 -0
- package/src/components/{organisms → layout}/SpatialCanvas.tsx +12 -7
- package/src/components/layout/VStack.tsx +69 -0
- package/src/components/layout/generated/default-variant-keys.ts +16 -0
- package/src/components/layout/generated/variant-keys.ts +21 -0
- package/src/components/{molecules → navigation}/Breadcrumb.tsx +5 -4
- package/src/components/navigation/Command.tsx +266 -0
- package/src/components/navigation/CommandPalette.tsx +83 -0
- package/src/components/navigation/DocumentPager.tsx +171 -0
- package/src/components/navigation/Footer.tsx +88 -0
- package/src/components/navigation/Header.tsx +80 -0
- package/src/components/{molecules → navigation}/Menubar.tsx +45 -12
- package/src/components/navigation/NavigationMenu.tsx +128 -0
- package/src/components/navigation/PageAside.tsx +84 -0
- package/src/components/{molecules → navigation}/Pagination.tsx +60 -7
- package/src/components/{organisms → navigation}/RightRail.tsx +1 -1
- package/src/components/navigation/Sidebar.tsx +223 -0
- package/src/components/navigation/SidebarItem.tsx +160 -0
- package/src/components/{molecules → navigation}/Tabs.tsx +2 -2
- package/src/components/navigation/TextLink.tsx +71 -0
- package/src/components/navigation/generated/default-variant-keys.ts +12 -0
- package/src/components/navigation/generated/variant-keys.ts +13 -0
- package/src/components/overlay/AIChatInput.tsx +5 -0
- package/src/components/overlay/AIChatMessage.tsx +6 -0
- package/src/components/overlay/AlertDialog.tsx +145 -0
- package/src/components/overlay/ChatPanel.tsx +180 -0
- package/src/components/{molecules → overlay}/ContextMenu.tsx +65 -29
- package/src/components/{molecules → overlay}/Dialog.tsx +21 -13
- package/src/components/overlay/Drawer.tsx +131 -0
- package/src/components/{molecules → overlay}/DropdownMenu.tsx +52 -17
- package/src/components/overlay/FloatingPanel.tsx +90 -0
- package/src/components/overlay/HoverCard.tsx +36 -0
- package/src/components/overlay/MediaLightbox.tsx +403 -0
- package/src/components/overlay/MediaPickerDialog.tsx +198 -0
- package/src/components/overlay/Modal.tsx +103 -0
- package/src/components/overlay/OnboardingFlow.tsx +172 -0
- package/src/components/overlay/Popover.tsx +36 -0
- package/src/components/overlay/ShareModal.tsx +324 -0
- package/src/components/{molecules → overlay}/Sheet.tsx +76 -19
- package/src/components/overlay/Tooltip.tsx +130 -0
- package/src/components/overlay/generated/default-variant-keys.ts +14 -0
- package/src/components/overlay/generated/variant-keys.ts +17 -0
- package/src/components/patterns/BlogTemplate.tsx +46 -0
- package/src/components/{templates → patterns}/DashboardTemplate.tsx +2 -2
- package/src/components/patterns/DocsTemplate.tsx +41 -0
- package/src/components/{templates → patterns}/MediaLibraryTemplate.tsx +1 -1
- package/src/components/patterns/OnboardingTemplate.tsx +32 -0
- package/src/components/patterns/PricingTemplate.tsx +106 -0
- package/src/globals.css +173 -22
- package/src/index.ts +177 -76
- package/tailwind-theme-extend.cjs +48 -3
- package/design/atoms-metadata.json +0 -82
- package/design/molecules-metadata.json +0 -130
- package/design/organisms-metadata.json +0 -38
- package/design/templates-metadata.json +0 -38
- package/src/components/atoms/Avatar.tsx +0 -57
- package/src/components/atoms/Select.tsx +0 -28
- package/src/components/atoms/generated/default-variant-keys.ts +0 -36
- package/src/components/molecules/AIChatInput.tsx +0 -140
- package/src/components/molecules/AIChatMessage.tsx +0 -109
- package/src/components/molecules/Accordion.tsx +0 -99
- package/src/components/molecules/Calendar.tsx +0 -60
- package/src/components/molecules/Carousel.tsx +0 -261
- package/src/components/molecules/Command.tsx +0 -152
- package/src/components/molecules/FilterButton.tsx +0 -133
- package/src/components/molecules/HoverCard.tsx +0 -29
- package/src/components/molecules/Modal.tsx +0 -66
- package/src/components/molecules/Popover.tsx +0 -31
- package/src/components/molecules/ProgressWidget.tsx +0 -40
- package/src/components/molecules/Resizable.tsx +0 -47
- package/src/components/molecules/ScrollArea.tsx +0 -48
- package/src/components/molecules/SidebarItem.tsx +0 -134
- package/src/components/molecules/Toast.tsx +0 -57
- package/src/components/molecules/Tooltip.tsx +0 -30
- package/src/components/molecules/generated/default-variant-keys.ts +0 -22
- package/src/components/molecules/generated/variant-keys.ts +0 -33
- package/src/components/organisms/CommandPalette.tsx +0 -58
- package/src/components/organisms/FloatingPanel.tsx +0 -46
- package/src/components/organisms/ShareModal.tsx +0 -182
- package/src/components/organisms/ToastProvider.tsx +0 -49
- /package/src/components/{atoms → display}/Kbd.tsx +0 -0
- /package/src/components/{atoms → display}/Separator.tsx +0 -0
- /package/src/components/{atoms → display}/Skeleton.tsx +0 -0
- /package/src/components/{atoms → feedback}/Progress.tsx +0 -0
- /package/src/components/{atoms → inputs}/Button.tsx +0 -0
- /package/src/components/{atoms → inputs}/Label.tsx +0 -0
- /package/src/components/{atoms → inputs}/RadioGroup.tsx +0 -0
- /package/src/components/{organisms → navigation}/AppRail.tsx +0 -0
- /package/src/components/{templates → patterns}/AuthTemplate.tsx +0 -0
- /package/src/components/{templates → patterns}/BannalyzeTemplate.tsx +0 -0
- /package/src/components/{templates → patterns}/ChatTemplate.tsx +0 -0
- /package/src/components/{templates → patterns}/EditorTemplate.tsx +0 -0
- /package/src/components/{templates → patterns}/KanbanTemplate.tsx +0 -0
- /package/src/components/{templates → patterns}/LandingTemplate.tsx +0 -0
- /package/src/components/{templates → patterns}/SettingsTemplate.tsx +0 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 4px LLC
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.ja.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# @gunjo/ui
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@gunjo/ui)
|
|
4
|
+
[](./LICENSE)
|
|
5
|
+
|
|
6
|
+
[English](./README.md) · **日本語**
|
|
7
|
+
|
|
8
|
+
*[UIXHERO](https://www.uixhero.com) が手がけるデザインシステム — ライブの [コンポーネントギャラリー](https://www.uixhero.com/resources/ui-components) もどうぞ。*
|
|
9
|
+
|
|
10
|
+
> ⚠️ **1.0 未満。** 現在の `0.0.1-alpha.x` は名前確保とリリース工程の試運転です。`1.0.0` stable まで public API は変わる可能性があります——バージョンを固定し、アップグレード前に [CHANGELOG](./CHANGELOG.md) を確認してください。
|
|
11
|
+
|
|
12
|
+
**Gunjo(群青)** は SSOT 駆動の React + Tailwind デザインシステムです。**pen デザインソース・React ソース・ドキュメントの 3 軸が常に同期している**ことを検証された 150+ コンポーネントを提供し、人だけでなく AI エージェントからも快適に使えるよう設計しています。
|
|
13
|
+
|
|
14
|
+
## 「becoming」── 名前について
|
|
15
|
+
|
|
16
|
+
**群青(ぐんじょう)** は、伝統的な日本の色名に連なる深い青。けれどこの名が指すのは、**まだ青ならず、青になりつつある色**です。夜明け前の空、まだ乾かない墨。完成された青ではなく、これから青になる色──成長と、開かれた可能性の象徴。この *becoming* が、プロジェクトそのものの姿勢でもあります。AI 時代に作られ、完成を急がず、いまも到着し続けているデザインシステム。主役の **群青** を、温かい **媚茶(こびちゃ)** のアクセントが「土」として支えます。
|
|
17
|
+
|
|
18
|
+
## なぜ Gunjo か
|
|
19
|
+
|
|
20
|
+
- **単一の正(SSOT)を強制する。** すべての公開コンポーネントは、デザインソース(`.pen`)・実装(`src/`)・ドキュメントの 3 軸で、自動ドリフト検査(`npm run design:verify`)により突合されます。トークン・バリアント・見た目が黙ってズレることがありません。
|
|
21
|
+
- **「何を作るか」で整理。** コンポーネントは機能別に分類されています──Inputs · Display · Charts · Feedback · Navigation · Overlay · Layout。抽象的な階層ではなく、用途で引けます。すべて Radix ベースで、アクセシビリティ(ARIA・キーボード)とダークモードを内包。
|
|
22
|
+
|
|
23
|
+
## AI から使える設計
|
|
24
|
+
|
|
25
|
+
Gunjo は「エージェントがこれを読んで使う」を後付けではなく第一級の要件として扱います:
|
|
26
|
+
|
|
27
|
+
- **単一の型付きエントリ** — すべての公開 API を `src/index.ts` から re-export。`import { Button, Card } from '@gunjo/ui'` だけで、エージェント(や人)が API 全体を発見できます。
|
|
28
|
+
- **SSOT パイプライン** — 機械可読のコンポーネントメタデータ(`design/*-metadata.json`)が各コンポーネントのバリアントとトークンを記述。
|
|
29
|
+
- **AI ハンドオフ面** — 専用のハンドオフ面(docs サイトの `/ai-handoff`、MCP・Figma ガイドを含む)でエージェントがシステムを把握できます。*(公開 Figma ライブラリと MCP サーバは roadmap)*
|
|
30
|
+
|
|
31
|
+
## クイックスタート
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install @gunjo/ui
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
// コンポーネント
|
|
39
|
+
import { Button, Input, Card } from '@gunjo/ui';
|
|
40
|
+
// スタイル
|
|
41
|
+
import '@gunjo/ui/styles';
|
|
42
|
+
// Tailwind プリセット
|
|
43
|
+
import gunjoPreset from '@gunjo/ui/tailwind-preset';
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
> **Next.js:** `next.config.ts` に `transpilePackages: ["@gunjo/ui"]` を追加してください(本パッケージは TypeScript ソースを直接配布します)。詳細は [docs/adoption.md](./docs/adoption.md)。
|
|
47
|
+
|
|
48
|
+
**別プロジェクトへ導入する**
|
|
49
|
+
|
|
50
|
+
- [docs/adoption.md](./docs/adoption.md) — 5 分インストール(ここから)
|
|
51
|
+
- [docs/adoption-strategy.md](./docs/adoption-strategy.md) — 配布形態の決定書
|
|
52
|
+
- [docs/migration-playbook.md](./docs/migration-playbook.md) — 既存アプリの段階移行
|
|
53
|
+
- [docs/dependencies.md](./docs/dependencies.md) — peer dependency 範囲とテスト済みの組み合わせ
|
|
54
|
+
- [docs/versioning.md](./docs/versioning.md) — semver 運用と破壊的変更ポリシー
|
|
55
|
+
|
|
56
|
+
**Gunjo を拡張する**
|
|
57
|
+
|
|
58
|
+
- [docs/component-addition.md](./docs/component-addition.md) — SSOT 三軸(pen + source + docs)でコンポーネントを追加する手順
|
|
59
|
+
|
|
60
|
+
## ドキュメントサイト
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
npm install
|
|
64
|
+
npm run dev # http://localhost:13030
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
ドキュメントサイトは `@gunjo/ui` を自ら使って(ドッグフーディング)構築されており、全コンポーネント・トークン・パターンの正式リファレンスです。
|
|
68
|
+
|
|
69
|
+
## Gunjo の作り方
|
|
70
|
+
|
|
71
|
+
Gunjo は、近い将来あたりまえになっていくと考える、シンプルな役割分担の上に作られています:
|
|
72
|
+
|
|
73
|
+
- **人が思想と方向を定める** — 何を作るか、何を大事にするか、美意識、すべての判断。
|
|
74
|
+
- **Claude と協働して作る** — 実装、網羅的な監査(アクセシビリティ・デザイントークン・全コンポーネントの視覚ドリフト)、そして速い反復。
|
|
75
|
+
- **そして AI から使えるよう設計する** — 単一の型付きエントリ、SSOT パイプライン、AI ハンドオフ面。エージェントが人と同じくらい簡単に読んで使えるように。
|
|
76
|
+
|
|
77
|
+
これは「AI がコンポーネントライブラリを作った」ではありません。一人の人間がビジョンを定め、AI をパートナーに、短いループで試行錯誤を重ねたものです。そしてそれを隠さず明示します——それが誠実で、そして次第に当たり前になっていく、ソフトウェアの作られ方だと考えるからです。
|
|
78
|
+
|
|
79
|
+
## 貢献・セキュリティ
|
|
80
|
+
|
|
81
|
+
- [CONTRIBUTING.md](./CONTRIBUTING.md) — 開発参加の手引き、PR、SSOT 三軸ワークフロー
|
|
82
|
+
- [SECURITY.md](./SECURITY.md) — 脆弱性報告手順、サポート対象バージョン
|
|
83
|
+
- [CHANGELOG.md](./CHANGELOG.md) — リリース履歴
|
|
84
|
+
- [CLAUDE.md](./CLAUDE.md) — issue 駆動の working agreement(非自明な作業は issue を切ってから)
|
|
85
|
+
|
|
86
|
+
タスクは [GitHub Issues](https://github.com/uixhero/gunjo/issues) に集約しています。
|
|
87
|
+
|
|
88
|
+
## ライセンス
|
|
89
|
+
|
|
90
|
+
[MIT](./LICENSE) © 2026 4px LLC · built by [UIXHERO](https://www.uixhero.com)
|
package/README.md
CHANGED
|
@@ -1,129 +1,90 @@
|
|
|
1
1
|
# @gunjo/ui
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@gunjo/ui)
|
|
4
|
+
[](./LICENSE)
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
**English** · [日本語](./README.ja.md)
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
*A design system from [UIXHERO](https://www.uixhero.com) — see the live [component gallery](https://www.uixhero.com/resources/ui-components).*
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
> ⚠️ **Pre-1.0.** The current `0.0.1-alpha.x` publish reserves the name and exercises the release pipeline. Until the `1.0.0` stable release the public API may change — pin your version and check the [CHANGELOG](./CHANGELOG.md) before upgrading.
|
|
10
11
|
|
|
11
|
-
-
|
|
12
|
-
- SSOT 運用・同期手順は [design/README.md](./design/README.md) を参照してください。
|
|
13
|
-
- 今回対応の概要と運用 RUNBOOK は [SSOT_RUNBOOK.md](./SSOT_RUNBOOK.md) を参照してください。
|
|
14
|
-
- コンポーネント突合表(Source / Docs / Pen)は [COMPONENT_COVERAGE_MATRIX.md](./COMPONENT_COVERAGE_MATRIX.md) を参照してください。
|
|
15
|
-
- 視覚監査台帳(docs vs pen)は [COMPONENT_VISUAL_AUDIT.md](./COMPONENT_VISUAL_AUDIT.md) を参照してください。
|
|
12
|
+
**Gunjo (群青)** is an SSOT-driven React + Tailwind design system — 150+ components whose **pen design source, React source, and docs are verified to stay in sync**, and which are built to be used as comfortably by AI agents as by people.
|
|
16
13
|
|
|
17
|
-
##
|
|
14
|
+
## "Becoming" — about the name
|
|
18
15
|
|
|
19
|
-
|
|
16
|
+
*Gunjo (群青)* is a deep ultramarine from the traditional palette of Japanese color names. But the name points at a color **not yet blue — becoming blue**: the sky before dawn, ink before it dries. Not a finished blue, but one on its way there — a symbol of growth and open possibility. That *becoming* is the stance of the whole project: built in the age of AI, never quite finished, always arriving. The primary **群青 (ultramarine)** is grounded by a warm **媚茶 (kobicha)** accent — the earth that supports the becoming.
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
- [docs/adoption-strategy.md](./docs/adoption-strategy.md) — 配布形態の決定書
|
|
23
|
-
- [docs/migration-playbook.md](./docs/migration-playbook.md) — 既存アプリの段階移行
|
|
24
|
-
- [docs/dependencies.md](./docs/dependencies.md) — peer dependency 範囲とテスト済み組み合わせ
|
|
25
|
-
- [docs/versioning.md](./docs/versioning.md) — semver 運用と破壊的変更ポリシー
|
|
18
|
+
## Why Gunjo
|
|
26
19
|
|
|
27
|
-
|
|
20
|
+
- **One source of truth, enforced.** Every shipped component is checked across three axes — design source (`.pen`), implementation (`src/`), and documentation — by an automated drift pipeline (`npm run design:verify`). Tokens, variants, and visuals can't silently diverge.
|
|
21
|
+
- **Organized by what you build.** Components are grouped by function — Inputs · Display · Charts · Feedback · Navigation · Overlay · Layout — not by abstract tiers. All Radix-based, with accessibility (ARIA, keyboard) and dark mode built in.
|
|
28
22
|
|
|
29
|
-
|
|
23
|
+
## AI-consumable by design
|
|
30
24
|
|
|
31
|
-
|
|
25
|
+
Gunjo treats "an agent will read and use this" as a first-class requirement, not an afterthought:
|
|
32
26
|
|
|
33
|
-
|
|
27
|
+
- **Single typed entry point** — every public API is re-exported from `src/index.ts`, so `import { Button, Card } from '@gunjo/ui'` is all an agent (or a person) needs to discover the surface.
|
|
28
|
+
- **SSOT pipeline** — machine-readable component metadata (`design/*-metadata.json`) describes every component's variants and tokens.
|
|
29
|
+
- **AI-handoff surface** — a dedicated handoff section (`/ai-handoff` in the docs, with MCP and Figma guides) for agents to pick up the system. *(A published Figma library and an MCP server are on the roadmap.)*
|
|
34
30
|
|
|
35
|
-
|
|
36
|
-
cd gunjo
|
|
37
|
-
npm install
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
### 型チェック
|
|
31
|
+
## Quick start
|
|
41
32
|
|
|
42
33
|
```bash
|
|
43
|
-
npm
|
|
34
|
+
npm install @gunjo/ui
|
|
44
35
|
```
|
|
45
36
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
37
|
+
```typescript
|
|
38
|
+
// components
|
|
39
|
+
import { Button, Input, Card } from '@gunjo/ui';
|
|
40
|
+
// styles
|
|
41
|
+
import '@gunjo/ui/styles';
|
|
42
|
+
// tailwind preset
|
|
43
|
+
import gunjoPreset from '@gunjo/ui/tailwind-preset';
|
|
50
44
|
```
|
|
51
45
|
|
|
52
|
-
|
|
46
|
+
> **Next.js:** add `transpilePackages: ["@gunjo/ui"]` to `next.config.ts` — the package ships TypeScript source directly. See [docs/adoption.md](./docs/adoption.md).
|
|
53
47
|
|
|
54
|
-
|
|
48
|
+
**Adopting Gunjo in another project**
|
|
55
49
|
|
|
56
|
-
|
|
50
|
+
- [docs/adoption.md](./docs/adoption.md) — 5-minute install (start here)
|
|
51
|
+
- [docs/adoption-strategy.md](./docs/adoption-strategy.md) — distribution-format decision record
|
|
52
|
+
- [docs/migration-playbook.md](./docs/migration-playbook.md) — staged migration for existing apps
|
|
53
|
+
- [docs/dependencies.md](./docs/dependencies.md) — peer-dependency ranges and tested combinations
|
|
54
|
+
- [docs/versioning.md](./docs/versioning.md) — semver policy and breaking-change rules
|
|
57
55
|
|
|
58
|
-
|
|
59
|
-
# npm から(推奨)
|
|
60
|
-
npm install @gunjo/ui
|
|
56
|
+
**Extending Gunjo**
|
|
61
57
|
|
|
62
|
-
|
|
63
|
-
npm install file:../gunjo
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
> **重要**:採用先 Next.js では `next.config.ts` に `transpilePackages: ["@gunjo/ui"]` を追加してください(本パッケージは TypeScript ソースを直接配布するため)。詳細は [docs/adoption.md §2](./docs/adoption.md#2-nextjs-設定必須)。
|
|
67
|
-
|
|
68
|
-
### インポート
|
|
69
|
-
|
|
70
|
-
```typescript
|
|
71
|
-
// コンポーネント
|
|
72
|
-
import { Button, Input, Card } from '@gunjo/ui';
|
|
58
|
+
- [docs/component-addition.md](./docs/component-addition.md) — add a component across the SSOT three axes (pen + source + docs)
|
|
73
59
|
|
|
74
|
-
|
|
75
|
-
import '@gunjo/ui/styles';
|
|
60
|
+
## Documentation site
|
|
76
61
|
|
|
77
|
-
|
|
78
|
-
|
|
62
|
+
```bash
|
|
63
|
+
npm install
|
|
64
|
+
npm run dev # http://localhost:13030
|
|
79
65
|
```
|
|
80
66
|
|
|
81
|
-
|
|
67
|
+
The docs site dogfoods `@gunjo/ui` and is the canonical reference for every component, token, and pattern.
|
|
82
68
|
|
|
83
|
-
|
|
84
|
-
- ✅ 相対パスはすべて `gunjo/` ディレクトリ内
|
|
85
|
-
- ✅ どこに移動しても動作します
|
|
86
|
-
- ✅ `npm install` で依存関係をインストール可能
|
|
87
|
-
- ✅ ドキュメントサイトも含まれています
|
|
69
|
+
## How Gunjo was built
|
|
88
70
|
|
|
89
|
-
|
|
71
|
+
Gunjo is built on a simple division of labor — the kind we expect to become commonplace before long:
|
|
90
72
|
|
|
91
|
-
|
|
73
|
+
- **A human sets the philosophy and direction** — what to build, what matters, the aesthetic, every judgment call.
|
|
74
|
+
- **The system is built in collaboration with Claude** — implementation, exhaustive audits (accessibility, design tokens, visual drift across every component), and fast iteration.
|
|
75
|
+
- **And it is designed to be consumed by AI** — a single typed entry point, an SSOT pipeline, and an AI-handoff surface, so agents can read and use it as easily as people can.
|
|
92
76
|
|
|
93
|
-
|
|
94
|
-
- **TypeScript**: 各コンポーネントの props が型付けされており、補完・推論がしやすい
|
|
95
|
-
- **Atomic Design**: Atoms / Molecules / Organisms / Templates で整理され、役割ごとに検索しやすい
|
|
96
|
-
- **Radix UI ベース**: アクセシビリティ(ARIA・キーボード操作)を考慮した構造で、意味の読み取りがしやすい
|
|
77
|
+
This is not "an AI made a component library." It is one person setting the vision and iterating, in tight loops, with an AI partner — and being transparent about it, because we think that's the honest (and increasingly normal) way software gets made.
|
|
97
78
|
|
|
98
|
-
|
|
79
|
+
## Contributing & security
|
|
99
80
|
|
|
100
|
-
|
|
81
|
+
- [CONTRIBUTING.md](./CONTRIBUTING.md) — workflow, PRs, the SSOT three-axis flow
|
|
82
|
+
- [SECURITY.md](./SECURITY.md) — vulnerability reporting and supported versions
|
|
83
|
+
- [CHANGELOG.md](./CHANGELOG.md) — release history
|
|
84
|
+
- [CLAUDE.md](./CLAUDE.md) — issue-driven working agreement (open an issue before non-trivial work)
|
|
101
85
|
|
|
102
|
-
|
|
103
|
-
gunjo/
|
|
104
|
-
├── src/ # コンポーネントライブラリ
|
|
105
|
-
│ ├── components/
|
|
106
|
-
│ │ ├── atoms/ # 原子コンポーネント
|
|
107
|
-
│ │ ├── molecules/ # 分子コンポーネント
|
|
108
|
-
│ │ ├── organisms/ # 有機体コンポーネント
|
|
109
|
-
│ │ └── templates/ # テンプレート
|
|
110
|
-
│ ├── lib/ # ユーティリティ
|
|
111
|
-
│ ├── globals.css # グローバルスタイル
|
|
112
|
-
│ └── index.ts # エクスポート
|
|
113
|
-
├── docs/ # ドキュメントサイト(Next.js)
|
|
114
|
-
│ ├── app/ # Next.jsアプリ
|
|
115
|
-
│ ├── components/ # ドキュメント用コンポーネント
|
|
116
|
-
│ └── lib/ # ドキュメント用ユーティリティ
|
|
117
|
-
├── public/ # 静的ファイル
|
|
118
|
-
├── tailwind-preset.js # Tailwindプリセット
|
|
119
|
-
├── next.config.ts # Next.js設定
|
|
120
|
-
├── postcss.config.mjs # PostCSS設定
|
|
121
|
-
├── package.json
|
|
122
|
-
└── tsconfig.json
|
|
123
|
-
```
|
|
86
|
+
Work is tracked on [GitHub Issues](https://github.com/uixhero/gunjo/issues).
|
|
124
87
|
|
|
125
|
-
##
|
|
88
|
+
## License
|
|
126
89
|
|
|
127
|
-
|
|
128
|
-
- ドキュメントサイトを起動する場合: `npm run dev` で起動
|
|
129
|
-
- すべての依存関係は `package.json` に含まれています
|
|
90
|
+
[MIT](./LICENSE) © 2026 4px LLC · built by [UIXHERO](https://www.uixhero.com)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gunjo/ui",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.2",
|
|
4
4
|
"description": "GunjoUI — SSOT-driven React + Tailwind design system. Pen / source / docs three-axis verified. Early alpha: API may change. See docs/adoption.md to consume from another project.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"design-system",
|
|
@@ -12,6 +12,13 @@
|
|
|
12
12
|
],
|
|
13
13
|
"main": "src/index.ts",
|
|
14
14
|
"types": "src/index.ts",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/uixhero/gunjo.git"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://github.com/uixhero/gunjo#readme",
|
|
20
|
+
"bugs": "https://github.com/uixhero/gunjo/issues",
|
|
21
|
+
"author": "4px LLC (https://www.uixhero.com)",
|
|
15
22
|
"license": "MIT",
|
|
16
23
|
"private": false,
|
|
17
24
|
"publishConfig": {
|
|
@@ -38,6 +45,9 @@
|
|
|
38
45
|
"build": "next build",
|
|
39
46
|
"start": "next start",
|
|
40
47
|
"lint": "eslint",
|
|
48
|
+
"showcase:thumbs": "node scripts/showcase-capture-thumbs.mjs && node scripts/sync-thumb-manifest.mjs",
|
|
49
|
+
"patterns:thumbs": "node scripts/patterns-capture-thumbs.mjs && node scripts/sync-thumb-manifest.mjs",
|
|
50
|
+
"thumbs:manifest": "node scripts/sync-thumb-manifest.mjs",
|
|
41
51
|
"prepare": "npm run hooks:install",
|
|
42
52
|
"hooks:install": "node scripts/install-git-hooks.mjs",
|
|
43
53
|
"design:sync": "node scripts/design-sync.mjs",
|
|
@@ -45,6 +55,7 @@
|
|
|
45
55
|
"design:sync:metadata": "node scripts/design-sync.mjs --metadata",
|
|
46
56
|
"design:sync:components": "node scripts/design-sync.mjs --components",
|
|
47
57
|
"design:sync:docs-navigation": "node scripts/design-sync.mjs --docs-navigation",
|
|
58
|
+
"design:sync:docs-embeds": "node scripts/sync-docs-embeds.mjs",
|
|
48
59
|
"design:sync:docs-pages": "node scripts/design-sync.mjs --docs-pages",
|
|
49
60
|
"design:sync:docs-pages:refresh": "node scripts/design-sync.mjs --docs-pages --refresh-generated",
|
|
50
61
|
"design:sync:stubs": "node scripts/design-sync.mjs --stubs",
|
|
@@ -63,10 +74,8 @@
|
|
|
63
74
|
"design:verify:node-snapshots": "node scripts/design-verify-node-snapshot-coverage.mjs",
|
|
64
75
|
"design:verify:slot-nodes": "node scripts/design-verify-slot-node-coverage.mjs",
|
|
65
76
|
"design:verify:atom-variant-keys": "node scripts/design-verify-atom-variant-key-coverage.mjs",
|
|
66
|
-
"design:verify:
|
|
67
|
-
"design:verify:
|
|
68
|
-
"design:verify:molecule-variant-keys": "node scripts/design-verify-molecule-variant-key-coverage.mjs",
|
|
69
|
-
"design:verify:molecule-generated-variant-key-usage": "node scripts/design-verify-molecule-generated-variant-key-usage.mjs",
|
|
77
|
+
"design:verify:generated-variant-keys": "node scripts/design-verify-generated-variant-key-coverage.mjs",
|
|
78
|
+
"design:verify:generated-variant-key-usage": "node scripts/design-verify-generated-variant-key-usage.mjs",
|
|
70
79
|
"design:verify:default-variant-keys": "node scripts/design-verify-default-variant-key-coverage.mjs",
|
|
71
80
|
"design:verify:default-variant-key-usage": "node scripts/design-verify-default-variant-key-usage.mjs",
|
|
72
81
|
"design:verify:component-style-hints": "node scripts/design-verify-component-style-hints-coverage.mjs",
|
|
@@ -79,6 +88,7 @@
|
|
|
79
88
|
"design:verify:tailwind-theme-ssot": "node scripts/design-verify-tailwind-theme-ssot.mjs",
|
|
80
89
|
"design:verify:tailwind-preset-plugins": "node scripts/design-verify-tailwind-preset-plugins.mjs",
|
|
81
90
|
"design:verify:css-vars": "node scripts/design-verify-css-variable-coverage.mjs",
|
|
91
|
+
"design:verify:color-contrast": "node scripts/design-verify-color-contrast.mjs",
|
|
82
92
|
"design:verify:app-globals": "node scripts/design-verify-app-globals-sync.mjs",
|
|
83
93
|
"design:verify:generated-stubs": "node scripts/design-verify-generated-stub-coverage.mjs",
|
|
84
94
|
"design:verify:generated-stubs:report": "node scripts/design-verify-generated-stub-coverage.mjs --report",
|
|
@@ -95,9 +105,30 @@
|
|
|
95
105
|
"design:verify:ssot-progress-thresholds": "node scripts/design-verify-ssot-progress-thresholds.mjs",
|
|
96
106
|
"design:verify:ssot-pr-comment-docs": "node scripts/design-verify-ssot-pr-comment-docs.mjs",
|
|
97
107
|
"design:report:component-coverage": "node scripts/design-generate-component-coverage-matrix.mjs",
|
|
108
|
+
"design:report:figma-library": "node scripts/generate-figma-library-discovery.mjs",
|
|
109
|
+
"design:report:figma-foundations": "node scripts/generate-figma-foundations-payload.mjs",
|
|
98
110
|
"design:audit:docs-capture": "node scripts/design-capture-doc-component-screenshots.mjs",
|
|
99
111
|
"design:audit:checklist": "node scripts/design-generate-visual-audit-checklist.mjs",
|
|
100
112
|
"design:audit:refresh": "npm run -s design:audit:docs-capture && npm run -s design:audit:checklist",
|
|
113
|
+
"docs:audit:components": "node scripts/audit-component-docs.mjs",
|
|
114
|
+
"docs:crawl:components": "node scripts/crawl-component-doc-pages.mjs",
|
|
115
|
+
"docs:audit:foundation-tokens": "npm run docs:audit:component-foundation-tokens && npm run docs:audit:runtime-foundation-tokens && npm run docs:audit:site-foundation-tokens",
|
|
116
|
+
"docs:audit:component-foundation-tokens": "node scripts/audit-component-foundation-tokens.mjs",
|
|
117
|
+
"docs:audit:runtime-foundation-tokens": "node scripts/audit-runtime-foundation-tokens.mjs",
|
|
118
|
+
"docs:audit:site-foundation-tokens": "node scripts/audit-site-foundation-tokens.mjs",
|
|
119
|
+
"docs:audit:extended-tokens": "npm run docs:audit:component-extended-tokens && npm run docs:audit:runtime-extended-tokens && npm run docs:audit:site-extended-tokens",
|
|
120
|
+
"docs:audit:component-extended-tokens": "node scripts/audit-component-extended-tokens.mjs",
|
|
121
|
+
"docs:audit:runtime-extended-tokens": "node scripts/audit-runtime-extended-tokens.mjs",
|
|
122
|
+
"docs:audit:site-extended-tokens": "node scripts/audit-site-extended-tokens.mjs",
|
|
123
|
+
"docs:audit:colors": "npm run docs:audit:component-colors && npm run docs:audit:runtime-colors && npm run docs:audit:site-colors",
|
|
124
|
+
"docs:audit:component-colors": "node scripts/audit-component-colors.mjs",
|
|
125
|
+
"docs:audit:runtime-colors": "node scripts/audit-runtime-colors.mjs",
|
|
126
|
+
"docs:audit:site-colors": "node scripts/audit-site-colors.mjs",
|
|
127
|
+
"docs:audit:mobile-disabled-feedback": "node scripts/audit-mobile-disabled-feedback.mjs",
|
|
128
|
+
"docs:audit:public-contrast": "node scripts/audit-public-contrast.mjs",
|
|
129
|
+
"docs:audit:en-locale-sweep": "node scripts/audit-en-locale-sweep.mjs",
|
|
130
|
+
"docs:audit:command-palette": "node scripts/audit-command-palette.mjs",
|
|
131
|
+
"docs:audit:lighthouse-public": "node scripts/audit-public-lighthouse.mjs",
|
|
101
132
|
"design:verify:components": "node scripts/design-verify-components.mjs",
|
|
102
133
|
"design:verify:molecules": "node scripts/design-verify-molecule-drift.mjs",
|
|
103
134
|
"design:verify:organisms": "node scripts/design-verify-organism-drift.mjs",
|
|
@@ -109,13 +140,17 @@
|
|
|
109
140
|
"tailwindcss": "^3.0.0 || ^4.0.0"
|
|
110
141
|
},
|
|
111
142
|
"dependencies": {
|
|
143
|
+
"@next/third-parties": "^16.2.9",
|
|
112
144
|
"@radix-ui/react-accordion": "^1.2.12",
|
|
145
|
+
"@radix-ui/react-alert-dialog": "^1.1.15",
|
|
146
|
+
"@radix-ui/react-aspect-ratio": "^1.1.8",
|
|
113
147
|
"@radix-ui/react-avatar": "^1.1.11",
|
|
114
148
|
"@radix-ui/react-context-menu": "^2.2.16",
|
|
115
149
|
"@radix-ui/react-dialog": "^1.1.15",
|
|
116
150
|
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
117
151
|
"@radix-ui/react-hover-card": "^1.1.15",
|
|
118
152
|
"@radix-ui/react-menubar": "^1.1.16",
|
|
153
|
+
"@radix-ui/react-navigation-menu": "^1.2.14",
|
|
119
154
|
"@radix-ui/react-popover": "^1.1.15",
|
|
120
155
|
"@radix-ui/react-scroll-area": "^1.2.10",
|
|
121
156
|
"@radix-ui/react-slot": "^1.2.4",
|
|
@@ -123,12 +158,16 @@
|
|
|
123
158
|
"@radix-ui/react-toggle-group": "^1.1.11",
|
|
124
159
|
"@radix-ui/react-tooltip": "^1.2.8",
|
|
125
160
|
"@radix-ui/react-visually-hidden": "^1.2.4",
|
|
161
|
+
"@tabler/icons-react": "^3.44.0",
|
|
162
|
+
"@tanstack/react-table": "^8.21.3",
|
|
163
|
+
"@vercel/analytics": "^2.0.1",
|
|
126
164
|
"class-variance-authority": "^0.7.1",
|
|
127
165
|
"clsx": "^2.1.1",
|
|
128
166
|
"cmdk": "^1.1.1",
|
|
129
167
|
"date-fns": "^4.1.0",
|
|
130
168
|
"embla-carousel-react": "^8.6.0",
|
|
131
169
|
"framer-motion": "^12.0.0",
|
|
170
|
+
"input-otp": "^1.4.2",
|
|
132
171
|
"lucide-react": "^0.562.0",
|
|
133
172
|
"next": "16.1.0",
|
|
134
173
|
"next-themes": "^0.4.6",
|
|
@@ -140,7 +179,8 @@
|
|
|
140
179
|
"remark-gfm": "^4.0.1",
|
|
141
180
|
"shiki": "^3.20.0",
|
|
142
181
|
"tailwind-merge": "^3.4.0",
|
|
143
|
-
"tailwindcss-animate": "^1.0.7"
|
|
182
|
+
"tailwindcss-animate": "^1.0.7",
|
|
183
|
+
"vaul": "^1.1.2"
|
|
144
184
|
},
|
|
145
185
|
"devDependencies": {
|
|
146
186
|
"@tailwindcss/postcss": "^4.1.18",
|
|
@@ -150,6 +190,7 @@
|
|
|
150
190
|
"@types/react-syntax-highlighter": "^15.5.13",
|
|
151
191
|
"eslint": "^9",
|
|
152
192
|
"eslint-config-next": "16.1.0",
|
|
193
|
+
"lighthouse": "^12.8.2",
|
|
153
194
|
"postcss": "^8.5.6",
|
|
154
195
|
"puppeteer": "^24.41.0",
|
|
155
196
|
"tailwindcss": "^4",
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import * as AccordionPrimitive from "@radix-ui/react-accordion"
|
|
5
|
+
import {
|
|
6
|
+
IconChevronDown as ChevronDown,
|
|
7
|
+
IconPlus as Plus,
|
|
8
|
+
} from "@tabler/icons-react";
|
|
9
|
+
|
|
10
|
+
import { cn } from "../../lib/utils"
|
|
11
|
+
import { Icon } from "./Icon"
|
|
12
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from "../overlay/Tooltip"
|
|
13
|
+
import type { AccordionVariantKey } from "./generated/variant-keys"
|
|
14
|
+
import { accordionDefaultVariantKey } from "./generated/default-variant-keys"
|
|
15
|
+
|
|
16
|
+
function buildVariantStateMap(
|
|
17
|
+
defaultVariantKey: AccordionVariantKey,
|
|
18
|
+
defaultStateClass: string,
|
|
19
|
+
alternateStateClass: string
|
|
20
|
+
): Record<AccordionVariantKey, string> {
|
|
21
|
+
return defaultVariantKey === "collapsed"
|
|
22
|
+
? { collapsed: defaultStateClass, expanded: alternateStateClass }
|
|
23
|
+
: { collapsed: alternateStateClass, expanded: defaultStateClass }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const expandedAccordionVariantKey: AccordionVariantKey =
|
|
27
|
+
accordionDefaultVariantKey === "collapsed" ? "expanded" : "collapsed"
|
|
28
|
+
|
|
29
|
+
const contentStateClasses = buildVariantStateMap(
|
|
30
|
+
accordionDefaultVariantKey,
|
|
31
|
+
"data-[state=closed]:animate-accordion-up",
|
|
32
|
+
"data-[state=open]:animate-accordion-down"
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
const Accordion = React.forwardRef<
|
|
36
|
+
React.ElementRef<typeof AccordionPrimitive.Root>,
|
|
37
|
+
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Root>
|
|
38
|
+
>(({ className, ...props }, ref) => (
|
|
39
|
+
<AccordionPrimitive.Root
|
|
40
|
+
ref={ref}
|
|
41
|
+
className={cn("flex flex-col w-[400px] border", className)}
|
|
42
|
+
{...props}
|
|
43
|
+
/>
|
|
44
|
+
))
|
|
45
|
+
Accordion.displayName = AccordionPrimitive.Root.displayName
|
|
46
|
+
|
|
47
|
+
const AccordionItem = React.forwardRef<
|
|
48
|
+
React.ElementRef<typeof AccordionPrimitive.Item>,
|
|
49
|
+
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
|
|
50
|
+
>(({ className, ...props }, ref) => (
|
|
51
|
+
<AccordionPrimitive.Item
|
|
52
|
+
ref={ref}
|
|
53
|
+
className={cn("border-b", className)}
|
|
54
|
+
{...props}
|
|
55
|
+
/>
|
|
56
|
+
))
|
|
57
|
+
AccordionItem.displayName = "AccordionItem"
|
|
58
|
+
|
|
59
|
+
type AccordionTriggerIndicator = "chevron" | "plus" | "none"
|
|
60
|
+
|
|
61
|
+
interface AccordionTriggerProps
|
|
62
|
+
extends React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger> {
|
|
63
|
+
/**
|
|
64
|
+
* Visual indicator shown at the trailing edge of the trigger.
|
|
65
|
+
* `chevron` is the default navigation-style affordance. `plus` is useful
|
|
66
|
+
* for FAQ and disclosure lists where open/close reads as add/remove.
|
|
67
|
+
*/
|
|
68
|
+
indicator?: AccordionTriggerIndicator
|
|
69
|
+
/** Tooltip label shown when the item is closed. */
|
|
70
|
+
openLabel?: string
|
|
71
|
+
/** Tooltip label shown when the item is open. */
|
|
72
|
+
closeLabel?: string
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function AccordionTriggerIndicator({
|
|
76
|
+
indicator,
|
|
77
|
+
openLabel,
|
|
78
|
+
closeLabel,
|
|
79
|
+
}: {
|
|
80
|
+
indicator: AccordionTriggerIndicator
|
|
81
|
+
openLabel: string
|
|
82
|
+
closeLabel: string
|
|
83
|
+
}) {
|
|
84
|
+
const indicatorRef = React.useRef<HTMLSpanElement | null>(null)
|
|
85
|
+
const [tooltipLabel, setTooltipLabel] = React.useState(openLabel)
|
|
86
|
+
|
|
87
|
+
const updateTooltipLabel = React.useCallback(() => {
|
|
88
|
+
const trigger = indicatorRef.current?.closest("button[data-state]")
|
|
89
|
+
const isOpen = trigger?.getAttribute("data-state") === "open"
|
|
90
|
+
setTooltipLabel(isOpen ? closeLabel : openLabel)
|
|
91
|
+
}, [closeLabel, openLabel])
|
|
92
|
+
|
|
93
|
+
React.useEffect(() => {
|
|
94
|
+
const trigger = indicatorRef.current?.closest("button[data-state]")
|
|
95
|
+
if (!trigger) return
|
|
96
|
+
|
|
97
|
+
updateTooltipLabel()
|
|
98
|
+
|
|
99
|
+
const observer = new MutationObserver(updateTooltipLabel)
|
|
100
|
+
observer.observe(trigger, {
|
|
101
|
+
attributes: true,
|
|
102
|
+
attributeFilter: ["data-state"],
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
return () => observer.disconnect()
|
|
106
|
+
}, [updateTooltipLabel])
|
|
107
|
+
|
|
108
|
+
if (indicator === "none") return null
|
|
109
|
+
|
|
110
|
+
const indicatorNode = indicator === "plus"
|
|
111
|
+
? (
|
|
112
|
+
<Icon
|
|
113
|
+
icon={Plus}
|
|
114
|
+
size="sm"
|
|
115
|
+
className="h-4 w-4 shrink-0 transition-transform duration-200 group-data-[state=open]:rotate-45"
|
|
116
|
+
/>
|
|
117
|
+
)
|
|
118
|
+
: (
|
|
119
|
+
<Icon
|
|
120
|
+
icon={ChevronDown}
|
|
121
|
+
size="sm"
|
|
122
|
+
className="h-4 w-4 shrink-0 transition-transform duration-200 group-data-[state=open]:rotate-180"
|
|
123
|
+
/>
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
return (
|
|
127
|
+
<Tooltip>
|
|
128
|
+
<TooltipTrigger asChild>
|
|
129
|
+
<span
|
|
130
|
+
ref={indicatorRef}
|
|
131
|
+
className="inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-sm"
|
|
132
|
+
onFocus={updateTooltipLabel}
|
|
133
|
+
onMouseEnter={updateTooltipLabel}
|
|
134
|
+
>
|
|
135
|
+
{indicatorNode}
|
|
136
|
+
</span>
|
|
137
|
+
</TooltipTrigger>
|
|
138
|
+
<TooltipContent>{tooltipLabel}</TooltipContent>
|
|
139
|
+
</Tooltip>
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const AccordionTrigger = React.forwardRef<
|
|
144
|
+
React.ElementRef<typeof AccordionPrimitive.Trigger>,
|
|
145
|
+
AccordionTriggerProps
|
|
146
|
+
>(({ className, children, indicator = "chevron", openLabel = "Open", closeLabel = "Close", ...props }, ref) => (
|
|
147
|
+
<AccordionPrimitive.Header className="flex">
|
|
148
|
+
<AccordionPrimitive.Trigger
|
|
149
|
+
ref={ref}
|
|
150
|
+
className={cn(
|
|
151
|
+
"group flex flex-1 items-center justify-between px-4 py-4 text-sm font-medium transition-all hover:underline disabled:cursor-not-allowed disabled:opacity-55 disabled:hover:no-underline [&[data-state=open]>svg]:rotate-180",
|
|
152
|
+
className
|
|
153
|
+
)}
|
|
154
|
+
{...props}
|
|
155
|
+
>
|
|
156
|
+
{children}
|
|
157
|
+
<AccordionTriggerIndicator
|
|
158
|
+
indicator={indicator}
|
|
159
|
+
openLabel={openLabel}
|
|
160
|
+
closeLabel={closeLabel}
|
|
161
|
+
/>
|
|
162
|
+
</AccordionPrimitive.Trigger>
|
|
163
|
+
</AccordionPrimitive.Header>
|
|
164
|
+
))
|
|
165
|
+
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
|
|
166
|
+
|
|
167
|
+
const AccordionContent = React.forwardRef<
|
|
168
|
+
React.ElementRef<typeof AccordionPrimitive.Content>,
|
|
169
|
+
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
|
|
170
|
+
>(({ className, children, ...props }, ref) => (
|
|
171
|
+
<AccordionPrimitive.Content
|
|
172
|
+
ref={ref}
|
|
173
|
+
className={cn(
|
|
174
|
+
"overflow-hidden text-sm text-muted-foreground transition-all",
|
|
175
|
+
contentStateClasses[accordionDefaultVariantKey],
|
|
176
|
+
contentStateClasses[expandedAccordionVariantKey]
|
|
177
|
+
)}
|
|
178
|
+
{...props}
|
|
179
|
+
>
|
|
180
|
+
<div className={cn("px-4 pb-4 pt-0", className)}>{children}</div>
|
|
181
|
+
</AccordionPrimitive.Content>
|
|
182
|
+
))
|
|
183
|
+
AccordionContent.displayName = AccordionPrimitive.Content.displayName
|
|
184
|
+
|
|
185
|
+
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
|