kaze-design-system 0.6.1 → 0.7.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 +54 -9
- package/components.css +476 -0
- package/dist/components/CategoryBar/CategoryBar.js +88 -0
- package/dist/components/ChartLegend/ChartLegend.js +43 -0
- package/dist/components/ColumnChart/ColumnChart.js +290 -0
- package/dist/components/Heatmap/Heatmap.js +255 -0
- package/dist/components/LineChart/LineChart.js +314 -0
- package/dist/components/SegmentedControl/SegmentedControl.js +120 -0
- package/dist/index.js +12 -0
- package/dist/lib/chartScale.js +38 -0
- package/dist/types/components/CategoryBar/CategoryBar.d.ts +28 -0
- package/dist/types/components/CategoryBar/index.d.ts +2 -0
- package/dist/types/components/ChartLegend/ChartLegend.d.ts +17 -0
- package/dist/types/components/ChartLegend/index.d.ts +2 -0
- package/dist/types/components/ColumnChart/ColumnChart.d.ts +33 -0
- package/dist/types/components/ColumnChart/index.d.ts +2 -0
- package/dist/types/components/Heatmap/Heatmap.d.ts +49 -0
- package/dist/types/components/Heatmap/index.d.ts +2 -0
- package/dist/types/components/LineChart/LineChart.d.ts +40 -0
- package/dist/types/components/LineChart/index.d.ts +2 -0
- package/dist/types/components/SegmentedControl/SegmentedControl.d.ts +21 -0
- package/dist/types/components/SegmentedControl/index.d.ts +2 -0
- package/dist/types/components/index.d.ts +12 -0
- package/dist/types/lib/chartScale.d.ts +19 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/kaze-design-system)
|
|
4
4
|
[](https://github.com/yuki930/kaze-design-system/blob/main/LICENSE)
|
|
5
5
|
|
|
6
|
-
**Kaze (風)** は、管理画面・ダッシュボード・金融系アプリのための React デザインシステムです。**白と温かみのあるグレーを基調にした静かな配色**と、日本語組版への丁寧な最適化(PALT・和欧混植・読みやすい行間)、そしてコピペで完結する
|
|
6
|
+
**Kaze (風)** は、管理画面・ダッシュボード・金融系アプリのための React デザインシステムです。**白と温かみのあるグレーを基調にした静かな配色**と、日本語組版への丁寧な最適化(PALT・和欧混植・読みやすい行間)、そしてコピペで完結する 69 のコンポーネントを 1 パッケージにまとめています。派手な色や装飾ではなく「読みやすさ」「情報密度」「長時間見続けても疲れないこと」を設計の中心に置いています。
|
|
7
7
|
|
|
8
|
-
- **
|
|
8
|
+
- **69 コンポーネント**: フォーム、テーブル、依存ゼロの SVG チャート(折れ線・縦棒・ヒートマップ等)、ダッシュボード UI、ランディングページ、マーケティングパーツまで
|
|
9
9
|
- **日本語組版に最適化**: PALT グローバル適用、和欧混植、括弧ツメ、適切な line-height
|
|
10
10
|
- **ライト / ダーク両対応**: `data-theme` 属性で切替、`prefers-color-scheme` 連動
|
|
11
11
|
- **素の CSS + カスケードレイヤー**: `@layer tokens, reset, components, utilities` でカスケード管理、Tailwind/CSS-in-JS 不要
|
|
@@ -57,7 +57,7 @@ AI コーディングアシスタントは以下のファイルからライブ
|
|
|
57
57
|
- **Short index**: https://kaze-design-system.vercel.app/llms.txt
|
|
58
58
|
- **Full API reference**: https://kaze-design-system.vercel.app/llms-full.txt
|
|
59
59
|
|
|
60
|
-
`llms-full.txt` には
|
|
60
|
+
`llms-full.txt` には 69 コンポーネントの `Props` / `Usage` / カテゴリが機械可読形式で含まれており、Storybook を参照しなくても正確なコード提案が可能です。[llmstxt.org](https://llmstxt.org) 準拠。
|
|
61
61
|
|
|
62
62
|
## インストール
|
|
63
63
|
|
|
@@ -122,7 +122,7 @@ function App() {
|
|
|
122
122
|
kaze は1パッケージ・3レイヤーで構成されています。
|
|
123
123
|
|
|
124
124
|
- **Foundation**: デザイントークン(カラー・タイポグラフィ・スペーシング)と CSS。両レイヤー共通の土台
|
|
125
|
-
- **Application UI**: 管理画面・アプリ向けの
|
|
125
|
+
- **Application UI**: 管理画面・アプリ向けの58コンポーネント。`kaze-design-system` から import
|
|
126
126
|
- **Marketing**: LP・キャンペーンページ向けの11コンポーネント。`kaze-design-system/marketing` から import
|
|
127
127
|
|
|
128
128
|
```tsx
|
|
@@ -133,15 +133,15 @@ import { Table, Sidebar, CommandPalette } from "kaze-design-system";
|
|
|
133
133
|
import { Hero, PricingGrid, CTABanner } from "kaze-design-system/marketing";
|
|
134
134
|
```
|
|
135
135
|
|
|
136
|
-
## コンポーネント一覧(
|
|
136
|
+
## コンポーネント一覧(69 コンポーネント)
|
|
137
137
|
|
|
138
|
-
### Application UI(
|
|
138
|
+
### Application UI(58)
|
|
139
139
|
|
|
140
140
|
| カテゴリ | コンポーネント |
|
|
141
141
|
|---|---|
|
|
142
|
-
| **フォーム** | Button, FAB, Input, NumberField, Select, Checkbox, Radio, Textarea, Switch, FormField, FilterPill |
|
|
142
|
+
| **フォーム** | Button, FAB, Input, NumberField, Select, Checkbox, Radio, Textarea, Switch, FormField, FilterPill, SegmentedControl |
|
|
143
143
|
| **データ表示** | Card, Badge, StatusBadge, Table, Metric, Avatar, BarList, Tracker, Watermark |
|
|
144
|
-
| **チャート** | BarChart, DonutChart, Sparkline |
|
|
144
|
+
| **チャート** | BarChart, DonutChart, Sparkline, LineChart, ColumnChart, Heatmap, CategoryBar, ChartLegend |
|
|
145
145
|
| **フィードバック** | Alert, Progress, Meter, Skeleton, EmptyState, Toast |
|
|
146
146
|
| **ナビゲーション** | Tabs, Sidebar, TopBar, Breadcrumb, Pagination, Stepper, Disclosure |
|
|
147
147
|
| **オーバーレイ** | Dialog, Tooltip, HelpButton, Dropdown, CommandPalette |
|
|
@@ -156,6 +156,51 @@ import { Hero, PricingGrid, CTABanner } from "kaze-design-system/marketing";
|
|
|
156
156
|
|
|
157
157
|
詳細は [kaze-design-system.vercel.app/docs/components](https://kaze-design-system.vercel.app/docs/components) を参照してください。
|
|
158
158
|
|
|
159
|
+
## チャート
|
|
160
|
+
|
|
161
|
+
チャートはすべて SVG の手書き実装で、追加の依存はありません(recharts 等は不要)。色は `--chart-*` トークンを使うので、ダークモードとパレット切り替えに自動で追従します。
|
|
162
|
+
|
|
163
|
+
| コンポーネント | 用途 |
|
|
164
|
+
|---|---|
|
|
165
|
+
| `LineChart` | 時系列の折れ線 / 面グラフ。複数系列、欠損値、ホバーとキーボードのツールチップ |
|
|
166
|
+
| `ColumnChart` | 縦棒グラフ。グループ / 積み上げ、負の値 |
|
|
167
|
+
| `BarChart` | 横棒のランキング |
|
|
168
|
+
| `DonutChart` | 構成比のドーナツ |
|
|
169
|
+
| `CategoryBar` | 構成比を 1 本の帯で。目標線マーカー付き |
|
|
170
|
+
| `Heatmap` | 曜日×時間などの行列、日別カレンダー |
|
|
171
|
+
| `Sparkline` | 表やカードに埋め込む小さな推移 |
|
|
172
|
+
| `ChartLegend` | 系列の表示 / 非表示を切り替える凡例 |
|
|
173
|
+
| `SegmentedControl` | 期間(1W / 1M / 1Y)や表示の切り替え |
|
|
174
|
+
|
|
175
|
+
```tsx
|
|
176
|
+
import { LineChart, SegmentedControl } from "kaze-design-system";
|
|
177
|
+
|
|
178
|
+
<SegmentedControl
|
|
179
|
+
aria-label="期間"
|
|
180
|
+
options={[
|
|
181
|
+
{ value: "1m", label: "1M" },
|
|
182
|
+
{ value: "3m", label: "3M" },
|
|
183
|
+
{ value: "1y", label: "1Y" },
|
|
184
|
+
]}
|
|
185
|
+
defaultValue="3m"
|
|
186
|
+
/>
|
|
187
|
+
<LineChart
|
|
188
|
+
aria-label="月別の売上と利用者数"
|
|
189
|
+
index="month"
|
|
190
|
+
data={[
|
|
191
|
+
{ month: "4月", sales: 320, users: 210 },
|
|
192
|
+
{ month: "5月", sales: 340, users: 230 },
|
|
193
|
+
{ month: "6月", sales: 310, users: 245 },
|
|
194
|
+
]}
|
|
195
|
+
series={[
|
|
196
|
+
{ key: "sales", label: "売上(万円)" },
|
|
197
|
+
{ key: "users", label: "利用者数(人)" },
|
|
198
|
+
]}
|
|
199
|
+
curve="monotone"
|
|
200
|
+
area
|
|
201
|
+
/>
|
|
202
|
+
```
|
|
203
|
+
|
|
159
204
|
## Hooks
|
|
160
205
|
|
|
161
206
|
```tsx
|
|
@@ -169,7 +214,7 @@ import { useFocusTrap, useLegendToggle } from "kaze-design-system/hooks";
|
|
|
169
214
|
|
|
170
215
|
## バンドルサイズ
|
|
171
216
|
|
|
172
|
-
63
|
|
217
|
+
63 コンポーネント時点の計測で **JS 38 kB gzip + CSS 23 kB gzip ≒ 61 kB gz**。ただし、個別 import すれば使った分だけが bundle に入ります。
|
|
173
218
|
|
|
174
219
|
| 区分 | 生サイズ | gzip |
|
|
175
220
|
|---|---:|---:|
|
package/components.css
CHANGED
|
@@ -2891,6 +2891,482 @@
|
|
|
2891
2891
|
font-variant-numeric: tabular-nums;
|
|
2892
2892
|
}
|
|
2893
2893
|
|
|
2894
|
+
/* ── ChartLegend ─────────────────────────────────────────── */
|
|
2895
|
+
.chart-legend {
|
|
2896
|
+
display: flex;
|
|
2897
|
+
flex-wrap: wrap;
|
|
2898
|
+
gap: var(--space-1) var(--space-4);
|
|
2899
|
+
font-size: var(--font-size-xs);
|
|
2900
|
+
}
|
|
2901
|
+
|
|
2902
|
+
.chart-legend__item {
|
|
2903
|
+
display: inline-flex;
|
|
2904
|
+
align-items: center;
|
|
2905
|
+
gap: var(--space-1-5);
|
|
2906
|
+
padding: 0;
|
|
2907
|
+
background: none;
|
|
2908
|
+
border: none;
|
|
2909
|
+
color: var(--color-fg-secondary);
|
|
2910
|
+
font: inherit;
|
|
2911
|
+
transition: opacity var(--duration-normal) var(--ease-default);
|
|
2912
|
+
}
|
|
2913
|
+
|
|
2914
|
+
button.chart-legend__item {
|
|
2915
|
+
cursor: pointer;
|
|
2916
|
+
border-radius: var(--radius-sm);
|
|
2917
|
+
}
|
|
2918
|
+
|
|
2919
|
+
button.chart-legend__item:hover {
|
|
2920
|
+
color: var(--color-fg);
|
|
2921
|
+
}
|
|
2922
|
+
|
|
2923
|
+
button.chart-legend__item:focus-visible {
|
|
2924
|
+
outline: none;
|
|
2925
|
+
box-shadow: 0 0 0 var(--ring-width) var(--ring-color);
|
|
2926
|
+
}
|
|
2927
|
+
|
|
2928
|
+
.chart-legend__item--hidden {
|
|
2929
|
+
opacity: 0.4;
|
|
2930
|
+
}
|
|
2931
|
+
|
|
2932
|
+
.chart-legend__swatch {
|
|
2933
|
+
width: 8px;
|
|
2934
|
+
height: 8px;
|
|
2935
|
+
border-radius: var(--radius-full);
|
|
2936
|
+
flex-shrink: 0;
|
|
2937
|
+
}
|
|
2938
|
+
|
|
2939
|
+
/* ── Chart primitives (shared by LineChart / ColumnChart / Heatmap) ── */
|
|
2940
|
+
.chart {
|
|
2941
|
+
position: relative;
|
|
2942
|
+
width: 100%;
|
|
2943
|
+
}
|
|
2944
|
+
|
|
2945
|
+
.chart__svg {
|
|
2946
|
+
display: block;
|
|
2947
|
+
width: 100%;
|
|
2948
|
+
overflow: visible;
|
|
2949
|
+
}
|
|
2950
|
+
|
|
2951
|
+
.chart-grid line {
|
|
2952
|
+
stroke: var(--color-border-subtle);
|
|
2953
|
+
stroke-width: 1;
|
|
2954
|
+
shape-rendering: crispEdges;
|
|
2955
|
+
}
|
|
2956
|
+
|
|
2957
|
+
.chart-axis text {
|
|
2958
|
+
fill: var(--color-fg-tertiary);
|
|
2959
|
+
font-size: var(--font-size-xs);
|
|
2960
|
+
font-variant-numeric: tabular-nums;
|
|
2961
|
+
}
|
|
2962
|
+
|
|
2963
|
+
.chart-axis line {
|
|
2964
|
+
stroke: var(--color-border);
|
|
2965
|
+
shape-rendering: crispEdges;
|
|
2966
|
+
}
|
|
2967
|
+
|
|
2968
|
+
.chart-crosshair {
|
|
2969
|
+
stroke: var(--color-border-strong);
|
|
2970
|
+
stroke-width: 1;
|
|
2971
|
+
stroke-dasharray: 3 3;
|
|
2972
|
+
pointer-events: none;
|
|
2973
|
+
}
|
|
2974
|
+
|
|
2975
|
+
.chart-tooltip {
|
|
2976
|
+
position: absolute;
|
|
2977
|
+
z-index: var(--z-tooltip);
|
|
2978
|
+
min-width: 8rem;
|
|
2979
|
+
padding: var(--space-2) var(--space-3);
|
|
2980
|
+
background: var(--color-surface);
|
|
2981
|
+
color: var(--color-fg);
|
|
2982
|
+
border: 1px solid var(--color-border);
|
|
2983
|
+
border-radius: var(--radius-md);
|
|
2984
|
+
box-shadow: var(--shadow-md);
|
|
2985
|
+
font-size: var(--font-size-xs);
|
|
2986
|
+
pointer-events: none;
|
|
2987
|
+
white-space: nowrap;
|
|
2988
|
+
}
|
|
2989
|
+
|
|
2990
|
+
.chart-tooltip__title {
|
|
2991
|
+
margin-bottom: var(--space-1);
|
|
2992
|
+
font-weight: var(--font-weight-medium);
|
|
2993
|
+
color: var(--color-fg-secondary);
|
|
2994
|
+
}
|
|
2995
|
+
|
|
2996
|
+
.chart-tooltip__row {
|
|
2997
|
+
display: flex;
|
|
2998
|
+
align-items: center;
|
|
2999
|
+
gap: var(--space-2);
|
|
3000
|
+
}
|
|
3001
|
+
|
|
3002
|
+
.chart-tooltip__swatch {
|
|
3003
|
+
width: 8px;
|
|
3004
|
+
height: 8px;
|
|
3005
|
+
border-radius: var(--radius-full);
|
|
3006
|
+
flex-shrink: 0;
|
|
3007
|
+
}
|
|
3008
|
+
|
|
3009
|
+
.chart-tooltip__label {
|
|
3010
|
+
flex: 1;
|
|
3011
|
+
color: var(--color-fg-secondary);
|
|
3012
|
+
}
|
|
3013
|
+
|
|
3014
|
+
.chart-tooltip__value {
|
|
3015
|
+
font-weight: var(--font-weight-medium);
|
|
3016
|
+
font-variant-numeric: tabular-nums;
|
|
3017
|
+
}
|
|
3018
|
+
|
|
3019
|
+
/* ================================================================
|
|
3020
|
+
Chart: LineChart
|
|
3021
|
+
Uses the shared "Chart primitives" (.chart, .chart-grid, .chart-axis,
|
|
3022
|
+
.chart-crosshair, .chart-tooltip). Series color is set via `color`
|
|
3023
|
+
on .line-chart__series and consumed with currentColor.
|
|
3024
|
+
================================================================ */
|
|
3025
|
+
|
|
3026
|
+
.line-chart {
|
|
3027
|
+
display: flex;
|
|
3028
|
+
flex-direction: column;
|
|
3029
|
+
gap: var(--space-3);
|
|
3030
|
+
width: 100%;
|
|
3031
|
+
}
|
|
3032
|
+
|
|
3033
|
+
.line-chart__svg {
|
|
3034
|
+
touch-action: pan-y;
|
|
3035
|
+
}
|
|
3036
|
+
|
|
3037
|
+
.line-chart__svg:focus-visible {
|
|
3038
|
+
outline: var(--ring-width) solid var(--ring-color);
|
|
3039
|
+
outline-offset: var(--ring-offset);
|
|
3040
|
+
border-radius: var(--radius-sm);
|
|
3041
|
+
}
|
|
3042
|
+
|
|
3043
|
+
.line-chart__line {
|
|
3044
|
+
fill: none;
|
|
3045
|
+
stroke: currentColor;
|
|
3046
|
+
stroke-width: 2;
|
|
3047
|
+
stroke-linecap: round;
|
|
3048
|
+
stroke-linejoin: round;
|
|
3049
|
+
}
|
|
3050
|
+
|
|
3051
|
+
.line-chart__area {
|
|
3052
|
+
fill: currentColor;
|
|
3053
|
+
fill-opacity: 0.12;
|
|
3054
|
+
stroke: none;
|
|
3055
|
+
}
|
|
3056
|
+
|
|
3057
|
+
.line-chart__dot {
|
|
3058
|
+
fill: var(--color-surface);
|
|
3059
|
+
stroke: currentColor;
|
|
3060
|
+
stroke-width: 2;
|
|
3061
|
+
}
|
|
3062
|
+
|
|
3063
|
+
.line-chart__dot--active {
|
|
3064
|
+
fill: currentColor;
|
|
3065
|
+
stroke: var(--color-surface);
|
|
3066
|
+
}
|
|
3067
|
+
|
|
3068
|
+
.line-chart__tooltip {
|
|
3069
|
+
/* position / transform are set inline from the active point */
|
|
3070
|
+
pointer-events: none;
|
|
3071
|
+
}
|
|
3072
|
+
|
|
3073
|
+
/* ── ColumnChart ─────────────────────────────────────────── */
|
|
3074
|
+
.column-chart {
|
|
3075
|
+
display: flex;
|
|
3076
|
+
flex-direction: column;
|
|
3077
|
+
gap: var(--space-3);
|
|
3078
|
+
border-radius: var(--radius-sm);
|
|
3079
|
+
}
|
|
3080
|
+
|
|
3081
|
+
.column-chart__figure:focus-visible {
|
|
3082
|
+
outline: none;
|
|
3083
|
+
box-shadow: 0 0 0 var(--ring-width) var(--ring-color);
|
|
3084
|
+
}
|
|
3085
|
+
|
|
3086
|
+
.column-chart__plot {
|
|
3087
|
+
position: relative;
|
|
3088
|
+
width: 100%;
|
|
3089
|
+
}
|
|
3090
|
+
|
|
3091
|
+
.column-chart__hit {
|
|
3092
|
+
fill: transparent;
|
|
3093
|
+
}
|
|
3094
|
+
|
|
3095
|
+
.column-chart__band {
|
|
3096
|
+
fill: var(--color-surface-active);
|
|
3097
|
+
opacity: 0.5;
|
|
3098
|
+
pointer-events: none;
|
|
3099
|
+
}
|
|
3100
|
+
|
|
3101
|
+
.column-chart__bar {
|
|
3102
|
+
transition: opacity var(--duration-normal) var(--ease-default);
|
|
3103
|
+
}
|
|
3104
|
+
|
|
3105
|
+
.column-chart__total {
|
|
3106
|
+
margin-top: var(--space-1);
|
|
3107
|
+
padding-top: var(--space-1);
|
|
3108
|
+
border-top: 1px solid var(--color-border-subtle);
|
|
3109
|
+
font-weight: var(--font-weight-medium);
|
|
3110
|
+
}
|
|
3111
|
+
|
|
3112
|
+
/* ── Heatmap ── */
|
|
3113
|
+
.heatmap {
|
|
3114
|
+
--heatmap-color: var(--chart-1);
|
|
3115
|
+
--heatmap-cell: 16px;
|
|
3116
|
+
--heatmap-gap: 3px;
|
|
3117
|
+
width: fit-content;
|
|
3118
|
+
max-width: 100%;
|
|
3119
|
+
}
|
|
3120
|
+
|
|
3121
|
+
.heatmap__scroll {
|
|
3122
|
+
overflow-x: auto;
|
|
3123
|
+
padding-block: var(--space-1);
|
|
3124
|
+
}
|
|
3125
|
+
|
|
3126
|
+
.heatmap__grid {
|
|
3127
|
+
display: inline-grid;
|
|
3128
|
+
gap: var(--heatmap-gap);
|
|
3129
|
+
align-items: center;
|
|
3130
|
+
}
|
|
3131
|
+
|
|
3132
|
+
.heatmap__label {
|
|
3133
|
+
font-size: var(--font-size-xs);
|
|
3134
|
+
color: var(--color-fg-tertiary);
|
|
3135
|
+
font-variant-numeric: tabular-nums;
|
|
3136
|
+
line-height: 1;
|
|
3137
|
+
white-space: nowrap;
|
|
3138
|
+
}
|
|
3139
|
+
|
|
3140
|
+
.heatmap__label--row {
|
|
3141
|
+
padding-right: var(--space-2);
|
|
3142
|
+
text-align: right;
|
|
3143
|
+
}
|
|
3144
|
+
|
|
3145
|
+
.heatmap__label--column {
|
|
3146
|
+
text-align: center;
|
|
3147
|
+
overflow: visible;
|
|
3148
|
+
}
|
|
3149
|
+
|
|
3150
|
+
.heatmap__label[hidden] {
|
|
3151
|
+
display: block;
|
|
3152
|
+
visibility: hidden;
|
|
3153
|
+
}
|
|
3154
|
+
|
|
3155
|
+
/* Spans several week columns so "10月" is not clipped by one narrow cell. */
|
|
3156
|
+
.heatmap__label--month {
|
|
3157
|
+
text-align: left;
|
|
3158
|
+
}
|
|
3159
|
+
|
|
3160
|
+
.heatmap__cell {
|
|
3161
|
+
width: var(--heatmap-cell);
|
|
3162
|
+
height: var(--heatmap-cell);
|
|
3163
|
+
border-radius: calc(var(--heatmap-cell) * 0.2);
|
|
3164
|
+
background: var(--color-bg-tertiary);
|
|
3165
|
+
outline: 1px solid transparent;
|
|
3166
|
+
outline-offset: 1px;
|
|
3167
|
+
}
|
|
3168
|
+
|
|
3169
|
+
.heatmap__cell:hover {
|
|
3170
|
+
outline-color: var(--color-fg-secondary);
|
|
3171
|
+
}
|
|
3172
|
+
|
|
3173
|
+
.heatmap__legend {
|
|
3174
|
+
display: flex;
|
|
3175
|
+
align-items: center;
|
|
3176
|
+
justify-content: flex-end;
|
|
3177
|
+
gap: var(--space-1);
|
|
3178
|
+
margin-top: var(--space-2);
|
|
3179
|
+
font-size: var(--font-size-xs);
|
|
3180
|
+
color: var(--color-fg-tertiary);
|
|
3181
|
+
}
|
|
3182
|
+
|
|
3183
|
+
.heatmap__legend > span:first-child {
|
|
3184
|
+
margin-right: var(--space-1);
|
|
3185
|
+
}
|
|
3186
|
+
|
|
3187
|
+
.heatmap__legend > span:last-child {
|
|
3188
|
+
margin-left: var(--space-1);
|
|
3189
|
+
}
|
|
3190
|
+
|
|
3191
|
+
.heatmap__swatch {
|
|
3192
|
+
width: 12px;
|
|
3193
|
+
height: 12px;
|
|
3194
|
+
border-radius: calc(12px * 0.2);
|
|
3195
|
+
}
|
|
3196
|
+
|
|
3197
|
+
.heatmap__tooltip {
|
|
3198
|
+
transform: translate(-50%, calc(-100% - 6px));
|
|
3199
|
+
}
|
|
3200
|
+
|
|
3201
|
+
/* ── CategoryBar ─────────────────────────────────────────── */
|
|
3202
|
+
.category-bar {
|
|
3203
|
+
display: flex;
|
|
3204
|
+
flex-direction: column;
|
|
3205
|
+
gap: var(--space-2);
|
|
3206
|
+
width: 100%;
|
|
3207
|
+
}
|
|
3208
|
+
|
|
3209
|
+
.category-bar__track {
|
|
3210
|
+
position: relative;
|
|
3211
|
+
display: flex;
|
|
3212
|
+
gap: 2px;
|
|
3213
|
+
align-items: center;
|
|
3214
|
+
width: 100%;
|
|
3215
|
+
background: var(--color-bg-tertiary);
|
|
3216
|
+
border-radius: var(--radius-full);
|
|
3217
|
+
}
|
|
3218
|
+
|
|
3219
|
+
.category-bar__segment {
|
|
3220
|
+
flex-basis: 0;
|
|
3221
|
+
min-width: 2px;
|
|
3222
|
+
height: 100%;
|
|
3223
|
+
}
|
|
3224
|
+
|
|
3225
|
+
.category-bar__segment:first-child {
|
|
3226
|
+
border-radius: var(--radius-full) 0 0 var(--radius-full);
|
|
3227
|
+
}
|
|
3228
|
+
|
|
3229
|
+
.category-bar__segment:last-of-type {
|
|
3230
|
+
border-radius: 0 var(--radius-full) var(--radius-full) 0;
|
|
3231
|
+
}
|
|
3232
|
+
|
|
3233
|
+
.category-bar__segment:only-of-type {
|
|
3234
|
+
border-radius: var(--radius-full);
|
|
3235
|
+
}
|
|
3236
|
+
|
|
3237
|
+
.category-bar__marker {
|
|
3238
|
+
position: absolute;
|
|
3239
|
+
top: 50%;
|
|
3240
|
+
width: 2px;
|
|
3241
|
+
background: var(--color-fg);
|
|
3242
|
+
border-radius: var(--radius-full);
|
|
3243
|
+
box-shadow: 0 0 0 1px var(--color-surface);
|
|
3244
|
+
transform: translate(-50%, -50%);
|
|
3245
|
+
}
|
|
3246
|
+
|
|
3247
|
+
.category-bar__legend {
|
|
3248
|
+
display: flex;
|
|
3249
|
+
flex-wrap: wrap;
|
|
3250
|
+
gap: var(--space-1) var(--space-4);
|
|
3251
|
+
margin: 0;
|
|
3252
|
+
padding: 0;
|
|
3253
|
+
list-style: none;
|
|
3254
|
+
font-size: var(--font-size-xs);
|
|
3255
|
+
}
|
|
3256
|
+
|
|
3257
|
+
.category-bar__legend-item {
|
|
3258
|
+
display: inline-flex;
|
|
3259
|
+
align-items: center;
|
|
3260
|
+
gap: var(--space-2);
|
|
3261
|
+
}
|
|
3262
|
+
|
|
3263
|
+
.category-bar__swatch {
|
|
3264
|
+
flex: none;
|
|
3265
|
+
width: 8px;
|
|
3266
|
+
height: 8px;
|
|
3267
|
+
border-radius: var(--radius-full);
|
|
3268
|
+
}
|
|
3269
|
+
|
|
3270
|
+
.category-bar__swatch--marker {
|
|
3271
|
+
width: 2px;
|
|
3272
|
+
height: 12px;
|
|
3273
|
+
background: var(--color-fg);
|
|
3274
|
+
}
|
|
3275
|
+
|
|
3276
|
+
.category-bar__label {
|
|
3277
|
+
color: var(--color-fg-secondary);
|
|
3278
|
+
}
|
|
3279
|
+
|
|
3280
|
+
.category-bar__value {
|
|
3281
|
+
font-weight: var(--font-weight-medium);
|
|
3282
|
+
color: var(--color-fg);
|
|
3283
|
+
font-variant-numeric: tabular-nums;
|
|
3284
|
+
}
|
|
3285
|
+
|
|
3286
|
+
.category-bar__percent {
|
|
3287
|
+
font-weight: var(--font-weight-normal);
|
|
3288
|
+
color: var(--color-fg-secondary);
|
|
3289
|
+
}
|
|
3290
|
+
|
|
3291
|
+
/* ── SegmentedControl ────────────────────────────────────── */
|
|
3292
|
+
.segmented-control {
|
|
3293
|
+
--segmented-control-padding: 2px;
|
|
3294
|
+
position: relative;
|
|
3295
|
+
display: inline-grid;
|
|
3296
|
+
grid-auto-flow: column;
|
|
3297
|
+
grid-auto-columns: 1fr;
|
|
3298
|
+
padding: var(--segmented-control-padding);
|
|
3299
|
+
background: var(--color-bg-tertiary);
|
|
3300
|
+
border-radius: var(--radius-field);
|
|
3301
|
+
}
|
|
3302
|
+
|
|
3303
|
+
.segmented-control--full {
|
|
3304
|
+
display: grid;
|
|
3305
|
+
width: 100%;
|
|
3306
|
+
}
|
|
3307
|
+
|
|
3308
|
+
.segmented-control__indicator {
|
|
3309
|
+
position: absolute;
|
|
3310
|
+
top: var(--segmented-control-padding);
|
|
3311
|
+
bottom: var(--segmented-control-padding);
|
|
3312
|
+
left: var(--segmented-control-padding);
|
|
3313
|
+
background: var(--color-surface);
|
|
3314
|
+
border-radius: calc(var(--radius-field) - 2px);
|
|
3315
|
+
box-shadow: var(--shadow-xs);
|
|
3316
|
+
transition: transform var(--duration-normal) var(--ease-out);
|
|
3317
|
+
}
|
|
3318
|
+
|
|
3319
|
+
.segmented-control__option {
|
|
3320
|
+
position: relative;
|
|
3321
|
+
z-index: 1;
|
|
3322
|
+
display: inline-flex;
|
|
3323
|
+
align-items: center;
|
|
3324
|
+
justify-content: center;
|
|
3325
|
+
height: calc(var(--density-control-height) - var(--segmented-control-padding) * 2);
|
|
3326
|
+
padding: 0 var(--space-3);
|
|
3327
|
+
font: inherit;
|
|
3328
|
+
font-size: var(--density-font-size-ui);
|
|
3329
|
+
font-weight: var(--font-weight-medium);
|
|
3330
|
+
color: var(--color-fg-secondary);
|
|
3331
|
+
white-space: nowrap;
|
|
3332
|
+
background: transparent;
|
|
3333
|
+
border: 0;
|
|
3334
|
+
border-radius: calc(var(--radius-field) - 2px);
|
|
3335
|
+
cursor: pointer;
|
|
3336
|
+
transition: color var(--duration-normal) var(--ease-out);
|
|
3337
|
+
}
|
|
3338
|
+
|
|
3339
|
+
.segmented-control__option:hover:not(:disabled) {
|
|
3340
|
+
color: var(--color-fg);
|
|
3341
|
+
}
|
|
3342
|
+
|
|
3343
|
+
.segmented-control__option[aria-checked="true"] {
|
|
3344
|
+
color: var(--color-fg);
|
|
3345
|
+
}
|
|
3346
|
+
|
|
3347
|
+
.segmented-control__option:focus-visible {
|
|
3348
|
+
outline: var(--ring-width) solid var(--ring-color);
|
|
3349
|
+
outline-offset: var(--ring-offset);
|
|
3350
|
+
}
|
|
3351
|
+
|
|
3352
|
+
.segmented-control__option:disabled {
|
|
3353
|
+
opacity: 0.5;
|
|
3354
|
+
cursor: not-allowed;
|
|
3355
|
+
}
|
|
3356
|
+
|
|
3357
|
+
.segmented-control--sm .segmented-control__option {
|
|
3358
|
+
height: calc(var(--density-control-height) - 0.5rem - var(--segmented-control-padding) * 2);
|
|
3359
|
+
padding: 0 var(--space-2);
|
|
3360
|
+
font-size: var(--font-size-xs);
|
|
3361
|
+
}
|
|
3362
|
+
|
|
3363
|
+
@media (prefers-reduced-motion: reduce) {
|
|
3364
|
+
.segmented-control__indicator,
|
|
3365
|
+
.segmented-control__option {
|
|
3366
|
+
transition: none;
|
|
3367
|
+
}
|
|
3368
|
+
}
|
|
3369
|
+
|
|
2894
3370
|
/* ================================================================
|
|
2895
3371
|
Chart — DonutChart
|
|
2896
3372
|
================================================================ */
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from "react";
|
|
3
|
+
import { cn } from "../../lib/utils.js";
|
|
4
|
+
const PALETTE_SIZE = 8;
|
|
5
|
+
const defaultFormat = (v) => v.toLocaleString("ja-JP");
|
|
6
|
+
const formatPercent = (ratio) => `${Math.round(ratio * 1e3) / 10}%`;
|
|
7
|
+
const CategoryBar = forwardRef(
|
|
8
|
+
({
|
|
9
|
+
data,
|
|
10
|
+
marker,
|
|
11
|
+
showLabels = true,
|
|
12
|
+
showPercent = false,
|
|
13
|
+
height = 8,
|
|
14
|
+
formatValue = defaultFormat,
|
|
15
|
+
className,
|
|
16
|
+
"aria-label": ariaLabel,
|
|
17
|
+
...rest
|
|
18
|
+
}, ref) => {
|
|
19
|
+
const items = data.map((d, i) => ({
|
|
20
|
+
...d,
|
|
21
|
+
value: Math.max(0, Number.isFinite(d.value) ? d.value : 0),
|
|
22
|
+
color: d.color ?? `var(--chart-${i % PALETTE_SIZE + 1})`
|
|
23
|
+
}));
|
|
24
|
+
const total = items.reduce((sum, d) => sum + d.value, 0);
|
|
25
|
+
const ratioOf = (v) => total > 0 ? v / total : 0;
|
|
26
|
+
const markerPos = marker && total > 0 ? Math.min(1, Math.max(0, ratioOf(marker.value))) : null;
|
|
27
|
+
const describe = (d) => `${d.label} ${formatValue(d.value)}${showPercent ? ` (${formatPercent(ratioOf(d.value))})` : ""}`;
|
|
28
|
+
return /* @__PURE__ */ jsxs(
|
|
29
|
+
"div",
|
|
30
|
+
{
|
|
31
|
+
ref,
|
|
32
|
+
role: "img",
|
|
33
|
+
"aria-label": ariaLabel,
|
|
34
|
+
className: cn("category-bar", className),
|
|
35
|
+
...rest,
|
|
36
|
+
children: [
|
|
37
|
+
/* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
|
|
38
|
+
items.map(describe).join("、"),
|
|
39
|
+
marker ? `。${marker.label ?? "目標"} ${formatValue(marker.value)}` : ""
|
|
40
|
+
] }),
|
|
41
|
+
/* @__PURE__ */ jsxs("div", { className: "category-bar__track", style: { height }, "aria-hidden": "true", children: [
|
|
42
|
+
items.map(
|
|
43
|
+
(d) => d.value > 0 && /* @__PURE__ */ jsx(
|
|
44
|
+
"div",
|
|
45
|
+
{
|
|
46
|
+
className: "category-bar__segment",
|
|
47
|
+
style: { flexGrow: d.value, background: d.color },
|
|
48
|
+
title: describe(d)
|
|
49
|
+
},
|
|
50
|
+
d.label
|
|
51
|
+
)
|
|
52
|
+
),
|
|
53
|
+
markerPos !== null && marker && /* @__PURE__ */ jsx(
|
|
54
|
+
"div",
|
|
55
|
+
{
|
|
56
|
+
className: "category-bar__marker",
|
|
57
|
+
style: { left: `${markerPos * 100}%`, height: height + 8 },
|
|
58
|
+
title: `${marker.label ?? "目標"} ${formatValue(marker.value)}`
|
|
59
|
+
}
|
|
60
|
+
)
|
|
61
|
+
] }),
|
|
62
|
+
showLabels && /* @__PURE__ */ jsxs("ul", { className: "category-bar__legend", "aria-hidden": "true", children: [
|
|
63
|
+
items.map((d) => /* @__PURE__ */ jsxs("li", { className: "category-bar__legend-item", children: [
|
|
64
|
+
/* @__PURE__ */ jsx("span", { className: "category-bar__swatch", style: { background: d.color } }),
|
|
65
|
+
/* @__PURE__ */ jsx("span", { className: "category-bar__label", children: d.label }),
|
|
66
|
+
/* @__PURE__ */ jsxs("span", { className: "category-bar__value", children: [
|
|
67
|
+
formatValue(d.value),
|
|
68
|
+
showPercent && /* @__PURE__ */ jsxs("span", { className: "category-bar__percent", children: [
|
|
69
|
+
" ",
|
|
70
|
+
formatPercent(ratioOf(d.value))
|
|
71
|
+
] })
|
|
72
|
+
] })
|
|
73
|
+
] }, d.label)),
|
|
74
|
+
marker?.label && /* @__PURE__ */ jsxs("li", { className: "category-bar__legend-item", children: [
|
|
75
|
+
/* @__PURE__ */ jsx("span", { className: "category-bar__swatch category-bar__swatch--marker" }),
|
|
76
|
+
/* @__PURE__ */ jsx("span", { className: "category-bar__label", children: marker.label }),
|
|
77
|
+
/* @__PURE__ */ jsx("span", { className: "category-bar__value", children: formatValue(marker.value) })
|
|
78
|
+
] })
|
|
79
|
+
] })
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
CategoryBar.displayName = "CategoryBar";
|
|
86
|
+
export {
|
|
87
|
+
CategoryBar
|
|
88
|
+
};
|