@novi-ui/tactile 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 +195 -0
- package/dist/accordion/accordion.d.mts +31 -0
- package/dist/accordion/accordion.d.mts.map +1 -0
- package/dist/accordion/accordion.mjs +88 -0
- package/dist/accordion/accordion.mjs.map +1 -0
- package/dist/accordion/accordion.styles.d.mts +61 -0
- package/dist/accordion/accordion.styles.d.mts.map +1 -0
- package/dist/accordion/accordion.styles.mjs +83 -0
- package/dist/accordion/accordion.styles.mjs.map +1 -0
- package/dist/accordion/index.d.mts +3 -0
- package/dist/avatar/avatar.d.mts +19 -0
- package/dist/avatar/avatar.d.mts.map +1 -0
- package/dist/avatar/avatar.mjs +57 -0
- package/dist/avatar/avatar.mjs.map +1 -0
- package/dist/avatar/avatar.styles.d.mts +49 -0
- package/dist/avatar/avatar.styles.d.mts.map +1 -0
- package/dist/avatar/avatar.styles.mjs +45 -0
- package/dist/avatar/avatar.styles.mjs.map +1 -0
- package/dist/avatar/index.d.mts +3 -0
- package/dist/badge/badge.d.mts +15 -0
- package/dist/badge/badge.d.mts.map +1 -0
- package/dist/badge/badge.mjs +39 -0
- package/dist/badge/badge.mjs.map +1 -0
- package/dist/badge/badge.styles.d.mts +67 -0
- package/dist/badge/badge.styles.d.mts.map +1 -0
- package/dist/badge/badge.styles.mjs +99 -0
- package/dist/badge/badge.styles.mjs.map +1 -0
- package/dist/badge/index.d.mts +3 -0
- package/dist/breadcrumbs/breadcrumbs.d.mts +33 -0
- package/dist/breadcrumbs/breadcrumbs.d.mts.map +1 -0
- package/dist/breadcrumbs/breadcrumbs.mjs +79 -0
- package/dist/breadcrumbs/breadcrumbs.mjs.map +1 -0
- package/dist/breadcrumbs/breadcrumbs.styles.d.mts +46 -0
- package/dist/breadcrumbs/breadcrumbs.styles.d.mts.map +1 -0
- package/dist/breadcrumbs/breadcrumbs.styles.mjs +38 -0
- package/dist/breadcrumbs/breadcrumbs.styles.mjs.map +1 -0
- package/dist/breadcrumbs/index.d.mts +3 -0
- package/dist/button/button.d.mts +17 -0
- package/dist/button/button.d.mts.map +1 -0
- package/dist/button/button.mjs +78 -0
- package/dist/button/button.mjs.map +1 -0
- package/dist/button/button.styles.d.mts +82 -0
- package/dist/button/button.styles.d.mts.map +1 -0
- package/dist/button/button.styles.mjs +136 -0
- package/dist/button/button.styles.mjs.map +1 -0
- package/dist/button/index.d.mts +3 -0
- package/dist/card/card.d.mts +51 -0
- package/dist/card/card.d.mts.map +1 -0
- package/dist/card/card.mjs +96 -0
- package/dist/card/card.mjs.map +1 -0
- package/dist/card/card.styles.d.mts +70 -0
- package/dist/card/card.styles.d.mts.map +1 -0
- package/dist/card/card.styles.mjs +61 -0
- package/dist/card/card.styles.mjs.map +1 -0
- package/dist/card/index.d.mts +3 -0
- package/dist/checkbox/checkbox.d.mts +24 -0
- package/dist/checkbox/checkbox.d.mts.map +1 -0
- package/dist/checkbox/checkbox.mjs +124 -0
- package/dist/checkbox/checkbox.mjs.map +1 -0
- package/dist/checkbox/checkbox.styles.d.mts +111 -0
- package/dist/checkbox/checkbox.styles.d.mts.map +1 -0
- package/dist/checkbox/checkbox.styles.mjs +99 -0
- package/dist/checkbox/checkbox.styles.mjs.map +1 -0
- package/dist/checkbox/index.d.mts +3 -0
- package/dist/index.d.mts +61 -0
- package/dist/index.mjs +42 -0
- package/dist/input/index.d.mts +3 -0
- package/dist/input/input.d.mts +20 -0
- package/dist/input/input.d.mts.map +1 -0
- package/dist/input/input.mjs +86 -0
- package/dist/input/input.mjs.map +1 -0
- package/dist/input/input.styles.d.mts +81 -0
- package/dist/input/input.styles.d.mts.map +1 -0
- package/dist/input/input.styles.mjs +88 -0
- package/dist/input/input.styles.mjs.map +1 -0
- package/dist/menu/index.d.mts +3 -0
- package/dist/menu/menu.d.mts +62 -0
- package/dist/menu/menu.d.mts.map +1 -0
- package/dist/menu/menu.mjs +115 -0
- package/dist/menu/menu.mjs.map +1 -0
- package/dist/menu/menu.styles.d.mts +90 -0
- package/dist/menu/menu.styles.d.mts.map +1 -0
- package/dist/menu/menu.styles.mjs +42 -0
- package/dist/menu/menu.styles.mjs.map +1 -0
- package/dist/modal/index.d.mts +3 -0
- package/dist/modal/modal.d.mts +55 -0
- package/dist/modal/modal.d.mts.map +1 -0
- package/dist/modal/modal.mjs +122 -0
- package/dist/modal/modal.mjs.map +1 -0
- package/dist/modal/modal.styles.d.mts +58 -0
- package/dist/modal/modal.styles.d.mts.map +1 -0
- package/dist/modal/modal.styles.mjs +91 -0
- package/dist/modal/modal.styles.mjs.map +1 -0
- package/dist/popover/index.d.mts +3 -0
- package/dist/popover/popover.d.mts +40 -0
- package/dist/popover/popover.d.mts.map +1 -0
- package/dist/popover/popover.mjs +86 -0
- package/dist/popover/popover.mjs.map +1 -0
- package/dist/popover/popover.styles.d.mts +81 -0
- package/dist/popover/popover.styles.d.mts.map +1 -0
- package/dist/popover/popover.styles.mjs +53 -0
- package/dist/popover/popover.styles.mjs.map +1 -0
- package/dist/progress/index.d.mts +3 -0
- package/dist/progress/progress.d.mts +14 -0
- package/dist/progress/progress.d.mts.map +1 -0
- package/dist/progress/progress.mjs +54 -0
- package/dist/progress/progress.mjs.map +1 -0
- package/dist/progress/progress.styles.d.mts +70 -0
- package/dist/progress/progress.styles.d.mts.map +1 -0
- package/dist/progress/progress.styles.mjs +54 -0
- package/dist/progress/progress.styles.mjs.map +1 -0
- package/dist/radio/index.d.mts +3 -0
- package/dist/radio/radio.d.mts +22 -0
- package/dist/radio/radio.d.mts.map +1 -0
- package/dist/radio/radio.mjs +95 -0
- package/dist/radio/radio.mjs.map +1 -0
- package/dist/radio/radio.styles.d.mts +111 -0
- package/dist/radio/radio.styles.d.mts.map +1 -0
- package/dist/radio/radio.styles.mjs +95 -0
- package/dist/radio/radio.styles.mjs.map +1 -0
- package/dist/select/index.d.mts +3 -0
- package/dist/select/select.d.mts +34 -0
- package/dist/select/select.d.mts.map +1 -0
- package/dist/select/select.mjs +136 -0
- package/dist/select/select.mjs.map +1 -0
- package/dist/select/select.styles.d.mts +85 -0
- package/dist/select/select.styles.d.mts.map +1 -0
- package/dist/select/select.styles.mjs +109 -0
- package/dist/select/select.styles.mjs.map +1 -0
- package/dist/skeleton/index.d.mts +3 -0
- package/dist/skeleton/skeleton.d.mts +15 -0
- package/dist/skeleton/skeleton.d.mts.map +1 -0
- package/dist/skeleton/skeleton.mjs +26 -0
- package/dist/skeleton/skeleton.mjs.map +1 -0
- package/dist/skeleton/skeleton.styles.d.mts +31 -0
- package/dist/skeleton/skeleton.styles.d.mts.map +1 -0
- package/dist/skeleton/skeleton.styles.mjs +22 -0
- package/dist/skeleton/skeleton.styles.mjs.map +1 -0
- package/dist/spinner/index.d.mts +3 -0
- package/dist/spinner/spinner.d.mts +15 -0
- package/dist/spinner/spinner.d.mts.map +1 -0
- package/dist/spinner/spinner.mjs +55 -0
- package/dist/spinner/spinner.mjs.map +1 -0
- package/dist/spinner/spinner.styles.d.mts +46 -0
- package/dist/spinner/spinner.styles.d.mts.map +1 -0
- package/dist/spinner/spinner.styles.mjs +37 -0
- package/dist/spinner/spinner.styles.mjs.map +1 -0
- package/dist/styles/focus-ring.mjs +23 -0
- package/dist/styles/focus-ring.mjs.map +1 -0
- package/dist/styles/tap-target.mjs +25 -0
- package/dist/styles/tap-target.mjs.map +1 -0
- package/dist/switch/index.d.mts +3 -0
- package/dist/switch/switch.d.mts +17 -0
- package/dist/switch/switch.d.mts.map +1 -0
- package/dist/switch/switch.mjs +57 -0
- package/dist/switch/switch.mjs.map +1 -0
- package/dist/switch/switch.styles.d.mts +58 -0
- package/dist/switch/switch.styles.d.mts.map +1 -0
- package/dist/switch/switch.styles.mjs +79 -0
- package/dist/switch/switch.styles.mjs.map +1 -0
- package/dist/tabs/index.d.mts +3 -0
- package/dist/tabs/tabs.d.mts +57 -0
- package/dist/tabs/tabs.d.mts.map +1 -0
- package/dist/tabs/tabs.mjs +91 -0
- package/dist/tabs/tabs.mjs.map +1 -0
- package/dist/tabs/tabs.styles.d.mts +58 -0
- package/dist/tabs/tabs.styles.d.mts.map +1 -0
- package/dist/tabs/tabs.styles.mjs +92 -0
- package/dist/tabs/tabs.styles.mjs.map +1 -0
- package/dist/tactile.css +528 -0
- package/dist/tactile.scoped.css +528 -0
- package/dist/textarea/index.d.mts +3 -0
- package/dist/textarea/textarea.d.mts +15 -0
- package/dist/textarea/textarea.d.mts.map +1 -0
- package/dist/textarea/textarea.mjs +70 -0
- package/dist/textarea/textarea.mjs.map +1 -0
- package/dist/textarea/textarea.styles.d.mts +73 -0
- package/dist/textarea/textarea.styles.d.mts.map +1 -0
- package/dist/textarea/textarea.styles.mjs +83 -0
- package/dist/textarea/textarea.styles.mjs.map +1 -0
- package/dist/toast/index.d.mts +3 -0
- package/dist/toast/toast.d.mts +38 -0
- package/dist/toast/toast.d.mts.map +1 -0
- package/dist/toast/toast.mjs +86 -0
- package/dist/toast/toast.mjs.map +1 -0
- package/dist/toast/toast.styles.d.mts +61 -0
- package/dist/toast/toast.styles.d.mts.map +1 -0
- package/dist/toast/toast.styles.mjs +59 -0
- package/dist/toast/toast.styles.mjs.map +1 -0
- package/package.json +68 -0
package/README.md
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# @novi-ui/tactile
|
|
2
|
+
|
|
3
|
+
Novi UI のテーマ2。**タッチファースト。**
|
|
4
|
+
|
|
5
|
+
指で触る前提で寸法を決め、面を持ち上げて階層を作る。
|
|
6
|
+
下から出るシート、大きなタップ領域、押した手応え。
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
pnpm add @novi-ui/tactile @novi-ui/core react-aria-components
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
```tsx
|
|
13
|
+
import '@novi-ui/core/base.css'
|
|
14
|
+
import '@novi-ui/tactile/tactile.css'
|
|
15
|
+
import { Button, Input } from '@novi-ui/tactile'
|
|
16
|
+
|
|
17
|
+
export function Example() {
|
|
18
|
+
return (
|
|
19
|
+
<form>
|
|
20
|
+
<Input label="メールアドレス" type="email" isRequired />
|
|
21
|
+
<Button type="submit" color="primary">送信</Button>
|
|
22
|
+
</form>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Provider は要りません。テーマは CSS 変数だけで効きます。
|
|
28
|
+
|
|
29
|
+
**`@novi-ui/raster` と公開 API は完全に同一です。** import 元を差し替えるだけで
|
|
30
|
+
構造ごと切り替わります。上のコードは1文字も変えずに両方で動きます。
|
|
31
|
+
|
|
32
|
+
## Raster とどう違うか
|
|
33
|
+
|
|
34
|
+
| | Raster | **Tactile** |
|
|
35
|
+
|---|---|---|
|
|
36
|
+
| 前提の入力 | ポインタ | **指** |
|
|
37
|
+
| 階層の作り方 | 線で切る | **面を持ち上げる** |
|
|
38
|
+
| Modal | 中央のダイアログ・閉じるは右上の ✕ | **下から出るシート・閉じるはフッターのフルワイド** |
|
|
39
|
+
| Select / Menu | トリガーの隣に出る | **画面下端のシート** |
|
|
40
|
+
| Tabs | 下線1本 | **セグメンテッドコントロール** |
|
|
41
|
+
| Toast | 右下 | **上端**(下端はシートとキーボードが占有する) |
|
|
42
|
+
| 高さ | 32 / 40 / 48px | **40 / 48 / 56px** |
|
|
43
|
+
| 本文 | 16px | **17px** |
|
|
44
|
+
|
|
45
|
+
**デスクトップで情報密度が要るなら Raster を選んでください。** Tactile は
|
|
46
|
+
腕を伸ばした距離で指を使う前提で、画面あたりの情報量は意図的に少なくなっています。
|
|
47
|
+
|
|
48
|
+
## デザイン言語(数値定義)
|
|
49
|
+
|
|
50
|
+
「タッチファースト」を主観で運用すると必ずブレるので、すべて数値で固定しています。
|
|
51
|
+
違反は CI(`scripts/check-design-rules.mjs` と実ブラウザでの実測)が検出します。
|
|
52
|
+
|
|
53
|
+
| 項目 | 規則 |
|
|
54
|
+
|---|---|
|
|
55
|
+
| タップターゲット | 対話要素の**実効領域が最小 44×44px**(WCAG 2.2 AAA / Apple HIG) |
|
|
56
|
+
| 一覧の行高 | 選択可能な行は**最小 48px**(誤タップは一覧で最も起きる) |
|
|
57
|
+
| 入力の文字 | **16px 以上**(iOS Safari は下回るとフォーカス時にページごと拡大する) |
|
|
58
|
+
| コンポーネント高さ | `sm` 40px / `md` 48px / `lg` 56px |
|
|
59
|
+
| タイポスケール | 13 / 15 / **17** / 21 / 26 / 32 / 40px |
|
|
60
|
+
| 角丸 | `sm` 8px / `md` 14px / `lg` 20px。**`none` 以外は 8px 以上** |
|
|
61
|
+
| 影 | **面にも使う。** 不透明度は α ≤ 0.24。`lg` だけ上向き(シートは下から来る) |
|
|
62
|
+
| 境界線 | **補助的にのみ。** 既定は影と背景色差で分ける |
|
|
63
|
+
| モーション | 出現 260ms / 消失 200ms / 状態変化 120ms |
|
|
64
|
+
| 押下の表現 | `scale` を **押下状態にのみ**許可(0.96〜1.0)。装飾目的では使わない |
|
|
65
|
+
| 画面端 | `env(safe-area-inset-*)` を加算する |
|
|
66
|
+
| 彩度(中立色) | chroma 0.004〜0.02。**選んだ色の色相に染まる** |
|
|
67
|
+
|
|
68
|
+
## 色を選ぶ
|
|
69
|
+
|
|
70
|
+
Tactile は **Textile Dyes** という8色のカラーセットを持ちます。全色が布を染めてきた
|
|
71
|
+
染料の実名です。`data-novi-color` 属性ひとつで切り替わります。
|
|
72
|
+
|
|
73
|
+
```html
|
|
74
|
+
<html data-novi-color="madder">
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
| 色 | hue | 出自 | 差し色(バイカラー) |
|
|
78
|
+
|---|---|---|---|
|
|
79
|
+
| `indigo` **(既定)** | 255 | 藍。デニムを染めた染料 | `saffron` |
|
|
80
|
+
| `peacock` | 200 | 孔雀の青緑 | `madder` |
|
|
81
|
+
| `sage` | 148 | セージの葉 | `cochineal` |
|
|
82
|
+
| `saffron` | 78 | サフラン | `indigo` |
|
|
83
|
+
| `madder` | 28 | 茜。人類最古級の赤い染料 | `peacock` |
|
|
84
|
+
| `cochineal` | 5 | コチニール。紅を生む染料 | `sage` |
|
|
85
|
+
| `mauve` | 315 | モーブ。1856年、史上初の合成染料 | `sage` |
|
|
86
|
+
| `greige` | 無彩 | 生機。染める前の布の色 | `indigo` |
|
|
87
|
+
|
|
88
|
+
- **トーンはテーマが持ちます。** どの色を選んでも明度と彩度は L50% / C0.080(dark は L76%)
|
|
89
|
+
- **Tactile は染まる生地です。** 選んだ染料の色相に**中立色まで追従**します。
|
|
90
|
+
Raster(染まらない紙)との最も分かりやすい違いがここに出ます
|
|
91
|
+
- **差し色が組で付いてきます。** `--novi-color-secondary` に表の相方が入ります
|
|
92
|
+
- **意味を持つ色は変わりません。** `success` / `warning` / `danger` は色選択の影響を受けません
|
|
93
|
+
- **知らない名前を書いても壊れません。** 該当する色が無ければ既定の `indigo` で描画されます
|
|
94
|
+
|
|
95
|
+
Raster の色名(`ink` / `brick` など)とは**1つも重なりません**。モデルごとに
|
|
96
|
+
コンセプトから色を選んでいるためで、テーマを切り替えると既定色に戻ります。
|
|
97
|
+
|
|
98
|
+
> **`data-novi-color` はテーマを宣言している要素に置いてください。**
|
|
99
|
+
> `tactile.scoped.css` を使っていて `data-novi-theme` を入れ子にしている場合、
|
|
100
|
+
> 内側の要素が既定色を宣言し直すため、外側に置いた色指定はそこで止まります。
|
|
101
|
+
|
|
102
|
+
## 知っておくべき仕様
|
|
103
|
+
|
|
104
|
+
### `size` は幅ではなく最大高です(Modal)
|
|
105
|
+
|
|
106
|
+
全幅のシートに幅の段階は存在しないので、Modal の `size` は**シートの最大高**として
|
|
107
|
+
解釈します(`sm` 40dvh / `md` 60dvh / `lg` 80dvh / `full` 100dvh)。
|
|
108
|
+
|
|
109
|
+
語彙は core が共通で固定し、解釈はテーマの自由、という設計です。同じコードが
|
|
110
|
+
テーマを跨いでそのまま動くのはこのためで、不具合ではありません。
|
|
111
|
+
|
|
112
|
+
Select / Menu の `size` はトリガーの寸法と行の密度になります。
|
|
113
|
+
|
|
114
|
+
### シートの位置は `classNames` で変えられません
|
|
115
|
+
|
|
116
|
+
Select と Menu のシートは `!important` で位置を固定しています。React Aria が
|
|
117
|
+
トリガー基準の座標を**インラインスタイルで書き込む**ため、通常のクラスでは勝てません。
|
|
118
|
+
|
|
119
|
+
位置を変えたい場合は `tv({ extend })` を使ってください。
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
const mySelect = tv({
|
|
123
|
+
extend: selectStyles,
|
|
124
|
+
slots: { popover: '!top-0 !bottom-auto' }, // 上から出す
|
|
125
|
+
})
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 掴み手はドラッグできません
|
|
129
|
+
|
|
130
|
+
シート上端の短い線は「下から出た面である」ことを示す装飾で、`data-slot` を持たず
|
|
131
|
+
`aria-hidden` です。ドラッグでの開閉は未実装です(キーボード利用者への代替経路を
|
|
132
|
+
含めて設計する必要があるため、別途対応します)。
|
|
133
|
+
|
|
134
|
+
閉じる手段は**フッターのフルワイドボタン / 背景タップ / Escape** の3つです。
|
|
135
|
+
|
|
136
|
+
### 押すと沈みます
|
|
137
|
+
|
|
138
|
+
対話要素は押下中に 0.97 倍に縮み、`solid` では影が消えます。持ち上がっていた面が
|
|
139
|
+
沈むという一連の動きで手応えを返します。`prefers-reduced-motion` では起きません。
|
|
140
|
+
|
|
141
|
+
`scale` を使うのはここだけです。装飾目的の `scale` は CI が拒否します。
|
|
142
|
+
|
|
143
|
+
## スタイルの拡張
|
|
144
|
+
|
|
145
|
+
すべての `tv()` 定義を named export しています。
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
import { buttonStyles } from '@novi-ui/tactile'
|
|
149
|
+
import { tv } from 'tailwind-variants'
|
|
150
|
+
|
|
151
|
+
// slot ベースの定義なので `base` ではなく `slots` で足す(`base` は無視されます)
|
|
152
|
+
const myButton = tv({
|
|
153
|
+
extend: buttonStyles,
|
|
154
|
+
slots: { root: 'uppercase tracking-widest' },
|
|
155
|
+
})
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
size variant のクラスに負ける場合は、呼び出し時に渡してください。
|
|
159
|
+
|
|
160
|
+
```tsx
|
|
161
|
+
// ✗ size の px-5 に負ける
|
|
162
|
+
tv({ extend: buttonStyles, slots: { root: 'px-10' } })
|
|
163
|
+
|
|
164
|
+
// ✓ 呼び出し時なら勝つ
|
|
165
|
+
<Button classNames={{ root: 'px-10' }} />
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
ブランド色を変えるだけなら CSS 変数の上書きで足ります。
|
|
169
|
+
|
|
170
|
+
```css
|
|
171
|
+
:root {
|
|
172
|
+
--novi-color-primary: oklch(50% 0.08 200);
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## slot
|
|
177
|
+
|
|
178
|
+
全コンポーネントは構成部位に `data-slot="<名前>"` を出力します。
|
|
179
|
+
テスト・CSS の上書き・視覚回帰がすべてこれに乗ります。
|
|
180
|
+
|
|
181
|
+
```tsx
|
|
182
|
+
<Modal classNames={{ panel: 'max-h-[50dvh]' }} />
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Raster 向けに書いた `classNames` をそのまま渡しても壊れません。** Tactile が
|
|
186
|
+
描画しない slot(Modal の `header` など)のキーは黙って無視されます。
|
|
187
|
+
|
|
188
|
+
## 日本語入力
|
|
189
|
+
|
|
190
|
+
テキスト入力と一覧選択は、IME の変換中に Enter を押しても送信・選択が起きません。
|
|
191
|
+
`useImeSafeKeys` を経由しています。
|
|
192
|
+
|
|
193
|
+
## ライセンス
|
|
194
|
+
|
|
195
|
+
MIT
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import { AccordionProps } from "@novi-ui/core";
|
|
3
|
+
//#region src/accordion/accordion.d.ts
|
|
4
|
+
interface AccordionItemProps {
|
|
5
|
+
id: string;
|
|
6
|
+
title: ReactNode;
|
|
7
|
+
isDisabled?: boolean;
|
|
8
|
+
children?: ReactNode;
|
|
9
|
+
className?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Accordion の項目。
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* <AccordionItem id="shipping" title="配送について">…</AccordionItem>
|
|
16
|
+
*/
|
|
17
|
+
declare function AccordionItem({ id, title, isDisabled, children, className }: AccordionItemProps): import("react").JSX.Element;
|
|
18
|
+
/**
|
|
19
|
+
* 折りたたみできる項目の集合。
|
|
20
|
+
*
|
|
21
|
+
* 開閉の記号は `+` / `−` の線で示す。三角矢印の回転は使わない。
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* <Accordion allowsMultipleExpanded>
|
|
25
|
+
* <AccordionItem id="shipping" title="配送について">…</AccordionItem>
|
|
26
|
+
* </Accordion>
|
|
27
|
+
*/
|
|
28
|
+
declare function Accordion({ variant, size, allowsMultipleExpanded, expandedKeys, defaultExpandedKeys, onExpandedChange, isDisabled, children, className, classNames, id }: AccordionProps): import("react").JSX.Element;
|
|
29
|
+
//#endregion
|
|
30
|
+
export { Accordion, AccordionItem, AccordionItemProps };
|
|
31
|
+
//# sourceMappingURL=accordion.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accordion.d.mts","names":[],"sources":["../../src/accordion/accordion.tsx"],"mappings":";;;UAuBiB;EACf;EACA,OAAO;EACP;EACA,WAAW;EACX;;;;;;;;iBASc,gBAAgB,IAAI,OAAO,YAAY,UAAU,aAAa,qCAAkB,IAAA;;;;;;;;;;;iBAuChF,YACd,SACA,MACA,wBACA,cACA,qBACA,kBACA,YACA,UACA,WACA,YACA,MACC,iCAAc,IAAA"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { accordionStyles } from "./accordion.styles.mjs";
|
|
3
|
+
import { Button, Disclosure, DisclosureGroup, DisclosurePanel, Heading } from "react-aria-components";
|
|
4
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
//#region src/accordion/accordion.tsx
|
|
6
|
+
/** 開閉を示す記号。回転させず `+` と `−` の線で描く。 */
|
|
7
|
+
function Indicator({ isExpanded }) {
|
|
8
|
+
return /* @__PURE__ */ jsxs("svg", {
|
|
9
|
+
viewBox: "0 0 16 16",
|
|
10
|
+
width: "0.875em",
|
|
11
|
+
height: "0.875em",
|
|
12
|
+
fill: "none",
|
|
13
|
+
"aria-hidden": "true",
|
|
14
|
+
children: [/* @__PURE__ */ jsx("path", {
|
|
15
|
+
d: "M3 8h10",
|
|
16
|
+
stroke: "currentColor",
|
|
17
|
+
strokeWidth: "1.5"
|
|
18
|
+
}), !isExpanded && /* @__PURE__ */ jsx("path", {
|
|
19
|
+
d: "M8 3v10",
|
|
20
|
+
stroke: "currentColor",
|
|
21
|
+
strokeWidth: "1.5"
|
|
22
|
+
})]
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Accordion の項目。
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* <AccordionItem id="shipping" title="配送について">…</AccordionItem>
|
|
30
|
+
*/
|
|
31
|
+
function AccordionItem({ id, title, isDisabled, children, className }) {
|
|
32
|
+
const s = accordionStyles();
|
|
33
|
+
return /* @__PURE__ */ jsx(Disclosure, {
|
|
34
|
+
id,
|
|
35
|
+
isDisabled,
|
|
36
|
+
"data-slot": "item",
|
|
37
|
+
className: s.item({ class: className }),
|
|
38
|
+
children: ({ isExpanded }) => /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Heading, {
|
|
39
|
+
"data-slot": "heading",
|
|
40
|
+
className: s.heading(),
|
|
41
|
+
children: /* @__PURE__ */ jsxs(Button, {
|
|
42
|
+
slot: "trigger",
|
|
43
|
+
"data-slot": "trigger",
|
|
44
|
+
className: s.trigger(),
|
|
45
|
+
children: [/* @__PURE__ */ jsx("span", { children: title }), /* @__PURE__ */ jsx("span", {
|
|
46
|
+
"data-slot": "indicator",
|
|
47
|
+
className: s.indicator(),
|
|
48
|
+
children: /* @__PURE__ */ jsx(Indicator, { isExpanded })
|
|
49
|
+
})]
|
|
50
|
+
})
|
|
51
|
+
}), /* @__PURE__ */ jsx(DisclosurePanel, {
|
|
52
|
+
"data-slot": "panel",
|
|
53
|
+
className: s.panel(),
|
|
54
|
+
children
|
|
55
|
+
})] })
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 折りたたみできる項目の集合。
|
|
60
|
+
*
|
|
61
|
+
* 開閉の記号は `+` / `−` の線で示す。三角矢印の回転は使わない。
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* <Accordion allowsMultipleExpanded>
|
|
65
|
+
* <AccordionItem id="shipping" title="配送について">…</AccordionItem>
|
|
66
|
+
* </Accordion>
|
|
67
|
+
*/
|
|
68
|
+
function Accordion({ variant, size, allowsMultipleExpanded, expandedKeys, defaultExpandedKeys, onExpandedChange, isDisabled, children, className, classNames, id }) {
|
|
69
|
+
const s = accordionStyles({
|
|
70
|
+
variant,
|
|
71
|
+
size
|
|
72
|
+
});
|
|
73
|
+
return /* @__PURE__ */ jsx(DisclosureGroup, {
|
|
74
|
+
id,
|
|
75
|
+
allowsMultipleExpanded,
|
|
76
|
+
expandedKeys,
|
|
77
|
+
defaultExpandedKeys,
|
|
78
|
+
onExpandedChange: (keys) => onExpandedChange?.([...keys].map(String)),
|
|
79
|
+
isDisabled,
|
|
80
|
+
"data-slot": "root",
|
|
81
|
+
className: s.root({ class: [className, classNames?.root] }),
|
|
82
|
+
children
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
//#endregion
|
|
86
|
+
export { Accordion, AccordionItem };
|
|
87
|
+
|
|
88
|
+
//# sourceMappingURL=accordion.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accordion.mjs","names":[],"sources":["../../src/accordion/accordion.tsx"],"sourcesContent":["'use client'\n\nimport type { AccordionProps } from '@novi-ui/core'\nimport type { ReactNode } from 'react'\nimport {\n Button,\n Disclosure,\n DisclosureGroup,\n DisclosurePanel,\n Heading,\n} from 'react-aria-components'\nimport { accordionStyles } from './accordion.styles'\n\n/** 開閉を示す記号。回転させず `+` と `−` の線で描く。 */\nfunction Indicator({ isExpanded }: { isExpanded: boolean }) {\n return (\n <svg viewBox=\"0 0 16 16\" width=\"0.875em\" height=\"0.875em\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M3 8h10\" stroke=\"currentColor\" strokeWidth=\"1.5\" />\n {!isExpanded && <path d=\"M8 3v10\" stroke=\"currentColor\" strokeWidth=\"1.5\" />}\n </svg>\n )\n}\n\nexport interface AccordionItemProps {\n id: string\n title: ReactNode\n isDisabled?: boolean\n children?: ReactNode\n className?: string\n}\n\n/**\n * Accordion の項目。\n *\n * @example\n * <AccordionItem id=\"shipping\" title=\"配送について\">…</AccordionItem>\n */\nexport function AccordionItem({ id, title, isDisabled, children, className }: AccordionItemProps) {\n const s = accordionStyles()\n\n return (\n <Disclosure\n id={id}\n isDisabled={isDisabled}\n data-slot=\"item\"\n className={s.item({ class: className })}\n >\n {({ isExpanded }) => (\n <>\n <Heading data-slot=\"heading\" className={s.heading()}>\n <Button slot=\"trigger\" data-slot=\"trigger\" className={s.trigger()}>\n <span>{title}</span>\n <span data-slot=\"indicator\" className={s.indicator()}>\n <Indicator isExpanded={isExpanded} />\n </span>\n </Button>\n </Heading>\n <DisclosurePanel data-slot=\"panel\" className={s.panel()}>\n {children}\n </DisclosurePanel>\n </>\n )}\n </Disclosure>\n )\n}\n\n/**\n * 折りたたみできる項目の集合。\n *\n * 開閉の記号は `+` / `−` の線で示す。三角矢印の回転は使わない。\n *\n * @example\n * <Accordion allowsMultipleExpanded>\n * <AccordionItem id=\"shipping\" title=\"配送について\">…</AccordionItem>\n * </Accordion>\n */\nexport function Accordion({\n variant,\n size,\n allowsMultipleExpanded,\n expandedKeys,\n defaultExpandedKeys,\n onExpandedChange,\n isDisabled,\n children,\n className,\n classNames,\n id,\n}: AccordionProps) {\n const s = accordionStyles({ variant, size })\n\n return (\n <DisclosureGroup\n id={id}\n allowsMultipleExpanded={allowsMultipleExpanded}\n expandedKeys={expandedKeys}\n defaultExpandedKeys={defaultExpandedKeys}\n onExpandedChange={(keys) => onExpandedChange?.([...keys].map(String))}\n isDisabled={isDisabled}\n data-slot=\"root\"\n className={s.root({ class: [className, classNames?.root] })}\n >\n {children}\n </DisclosureGroup>\n )\n}\n"],"mappings":";;;;;;AAcA,SAAS,UAAU,EAAE,cAAuC;CAC1D,OACE,qBAAC,OAAD;EAAK,SAAQ;EAAY,OAAM;EAAU,QAAO;EAAU,MAAK;EAAO,eAAY;EAAlF,UAAA,CACE,oBAAC,QAAD;GAAM,GAAE;GAAU,QAAO;GAAe,aAAY;EAAO,CAAA,GAC1D,CAAC,cAAc,oBAAC,QAAD;GAAM,GAAE;GAAU,QAAO;GAAe,aAAY;EAAO,CAAA,CACxE;;AAET;;;;;;;AAgBA,SAAgB,cAAc,EAAE,IAAI,OAAO,YAAY,UAAU,aAAiC;CAChG,MAAM,IAAI,gBAAgB;CAE1B,OACE,oBAAC,YAAD;EACM;EACQ;EACZ,aAAU;EACV,WAAW,EAAE,KAAK,EAAE,OAAO,UAAU,CAAC;EAEpC,WAAA,EAAE,iBACF,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,SAAD;GAAS,aAAU;GAAU,WAAW,EAAE,QAAQ;GAChD,UAAA,qBAAC,QAAD;IAAQ,MAAK;IAAU,aAAU;IAAU,WAAW,EAAE,QAAQ;IAAhE,UAAA,CACE,oBAAC,QAAD,EAAA,UAAO,MAAY,CAAA,GACnB,oBAAC,QAAD;KAAM,aAAU;KAAY,WAAW,EAAE,UAAU;KACjD,UAAA,oBAAC,WAAD,EAAuB,WAAa,CAAA;IAChC,CAAA,CACA;;EACD,CAAA,GACT,oBAAC,iBAAD;GAAiB,aAAU;GAAQ,WAAW,EAAE,MAAM;GACnD;EACc,CAAA,CACjB,EAAA,CAAA;CAEM,CAAA;AAEhB;;;;;;;;;;;AAYA,SAAgB,UAAU,EACxB,SACA,MACA,wBACA,cACA,qBACA,kBACA,YACA,UACA,WACA,YACA,MACiB;CACjB,MAAM,IAAI,gBAAgB;EAAE;EAAS;CAAK,CAAC;CAE3C,OACE,oBAAC,iBAAD;EACM;EACoB;EACV;EACO;EACrB,mBAAmB,SAAS,mBAAmB,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,MAAM,CAAC;EACxD;EACZ,aAAU;EACV,WAAW,EAAE,KAAK,EAAE,OAAO,CAAC,WAAW,YAAY,IAAI,EAAE,CAAC;EAEzD;CACc,CAAA;AAErB"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { VariantMap } from "@novi-ui/core";
|
|
2
|
+
//#region src/accordion/accordion.styles.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Accordion のスタイル定義。
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* const myAccordion = tv({ extend: accordionStyles, slots: { panel: 'pt-2' } })
|
|
8
|
+
*/
|
|
9
|
+
declare const accordionStyles: import("tailwind-variants").TVReturnType<{
|
|
10
|
+
size: VariantMap<"sm" | "md" | "lg", {
|
|
11
|
+
trigger: string;
|
|
12
|
+
panel: string;
|
|
13
|
+
}>;
|
|
14
|
+
variant: VariantMap<"solid" | "outline" | "soft" | "ghost" | "plain", {
|
|
15
|
+
root: string;
|
|
16
|
+
item: string;
|
|
17
|
+
}>;
|
|
18
|
+
}, {
|
|
19
|
+
root: string;
|
|
20
|
+
item: string;
|
|
21
|
+
heading: string;
|
|
22
|
+
trigger: string;
|
|
23
|
+
indicator: string;
|
|
24
|
+
panel: string;
|
|
25
|
+
}, undefined, {
|
|
26
|
+
size: VariantMap<"sm" | "md" | "lg", {
|
|
27
|
+
trigger: string;
|
|
28
|
+
panel: string;
|
|
29
|
+
}>;
|
|
30
|
+
variant: VariantMap<"solid" | "outline" | "soft" | "ghost" | "plain", {
|
|
31
|
+
root: string;
|
|
32
|
+
item: string;
|
|
33
|
+
}>;
|
|
34
|
+
}, {
|
|
35
|
+
root: string;
|
|
36
|
+
item: string;
|
|
37
|
+
heading: string;
|
|
38
|
+
trigger: string;
|
|
39
|
+
indicator: string;
|
|
40
|
+
panel: string;
|
|
41
|
+
}, import("tailwind-variants").TVReturnTypeLike<{
|
|
42
|
+
size: VariantMap<"sm" | "md" | "lg", {
|
|
43
|
+
trigger: string;
|
|
44
|
+
panel: string;
|
|
45
|
+
}>;
|
|
46
|
+
variant: VariantMap<"solid" | "outline" | "soft" | "ghost" | "plain", {
|
|
47
|
+
root: string;
|
|
48
|
+
item: string;
|
|
49
|
+
}>;
|
|
50
|
+
}, {
|
|
51
|
+
root: string;
|
|
52
|
+
item: string;
|
|
53
|
+
heading: string;
|
|
54
|
+
trigger: string;
|
|
55
|
+
indicator: string;
|
|
56
|
+
panel: string;
|
|
57
|
+
}>>;
|
|
58
|
+
type AccordionStyleProps = Parameters<typeof accordionStyles>[0];
|
|
59
|
+
//#endregion
|
|
60
|
+
export { AccordionStyleProps, accordionStyles };
|
|
61
|
+
//# sourceMappingURL=accordion.styles.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accordion.styles.d.mts","names":[],"sources":["../../src/accordion/accordion.styles.ts"],"mappings":";;;;;;;;cA6Da,6CAAe;;IAZgB;IAAe;;;IATZ;IAAc;;;;;;;;;;;IASjB;IAAe;;;IATZ;IAAc;;;;;;;;;;;IASjB;IAAe;;;IATZ;IAAc;;;;;;;;;;KA2BjD,sBAAsB,kBAAkB"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { focusRing } from "../styles/focus-ring.mjs";
|
|
2
|
+
import { tv } from "tailwind-variants";
|
|
3
|
+
//#region src/accordion/accordion.styles.ts
|
|
4
|
+
/**
|
|
5
|
+
* インジケータは `+` と `−` の線で示す。
|
|
6
|
+
* 三角矢印を回転させる表現は Raster の「動きで飾らない」原則に反するため使わない。
|
|
7
|
+
*/
|
|
8
|
+
const slots = {
|
|
9
|
+
root: "flex flex-col border-t border-[var(--novi-color-border)]",
|
|
10
|
+
item: "border-b border-[var(--novi-color-border)]",
|
|
11
|
+
heading: "m-0",
|
|
12
|
+
trigger: [
|
|
13
|
+
"flex items-center justify-between gap-4 w-full text-left",
|
|
14
|
+
"cursor-pointer outline-none",
|
|
15
|
+
"text-[var(--novi-color-fg)]",
|
|
16
|
+
"transition-colors duration-[var(--novi-duration-fast)] ease-[var(--novi-ease-standard)]",
|
|
17
|
+
"hover:bg-[var(--novi-color-subtle)]",
|
|
18
|
+
"data-[disabled]:opacity-40 data-[disabled]:cursor-default",
|
|
19
|
+
focusRing
|
|
20
|
+
].join(" "),
|
|
21
|
+
indicator: [
|
|
22
|
+
"shrink-0 text-[var(--novi-color-muted)]",
|
|
23
|
+
"transition-transform duration-[var(--novi-duration-fast)] ease-[var(--novi-ease-standard)]",
|
|
24
|
+
"group-data-[expanded]:rotate-180",
|
|
25
|
+
"motion-reduce:transition-none"
|
|
26
|
+
].join(" "),
|
|
27
|
+
panel: "text-[var(--novi-color-fg)] leading-[var(--novi-leading-body)]"
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Accordion のスタイル定義。
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* const myAccordion = tv({ extend: accordionStyles, slots: { panel: 'pt-2' } })
|
|
34
|
+
*/
|
|
35
|
+
const accordionStyles = tv({
|
|
36
|
+
slots,
|
|
37
|
+
variants: {
|
|
38
|
+
size: {
|
|
39
|
+
sm: {
|
|
40
|
+
trigger: "min-h-12 px-4 py-3 text-[length:var(--novi-text-sm)]",
|
|
41
|
+
panel: "px-4 pb-3"
|
|
42
|
+
},
|
|
43
|
+
md: {
|
|
44
|
+
trigger: "min-h-14 px-5 py-4 text-[length:var(--novi-text-base)]",
|
|
45
|
+
panel: "px-5 pb-4"
|
|
46
|
+
},
|
|
47
|
+
lg: {
|
|
48
|
+
trigger: "min-h-16 px-6 py-5 text-[length:var(--novi-text-base)]",
|
|
49
|
+
panel: "px-6 pb-5"
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
variant: {
|
|
53
|
+
solid: {
|
|
54
|
+
root: "border-t-0 gap-1",
|
|
55
|
+
item: "bg-[var(--novi-color-subtle)] border-b-0"
|
|
56
|
+
},
|
|
57
|
+
outline: {
|
|
58
|
+
root: "border-t border-[var(--novi-color-border)]",
|
|
59
|
+
item: "border-b"
|
|
60
|
+
},
|
|
61
|
+
soft: {
|
|
62
|
+
root: "border-t-0 gap-1",
|
|
63
|
+
item: "bg-[var(--novi-color-subtle)] border-b-0"
|
|
64
|
+
},
|
|
65
|
+
ghost: {
|
|
66
|
+
root: "border-t-0",
|
|
67
|
+
item: "border-b border-[var(--novi-color-border)]"
|
|
68
|
+
},
|
|
69
|
+
plain: {
|
|
70
|
+
root: "border-t-0 gap-2",
|
|
71
|
+
item: "border-b-0"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
defaultVariants: {
|
|
76
|
+
variant: "outline",
|
|
77
|
+
size: "md"
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
//#endregion
|
|
81
|
+
export { accordionStyles };
|
|
82
|
+
|
|
83
|
+
//# sourceMappingURL=accordion.styles.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accordion.styles.mjs","names":[],"sources":["../../src/accordion/accordion.styles.ts"],"sourcesContent":["import type {\n accordionRequiredSlots,\n accordionSlots,\n NoviSize,\n NoviVariant,\n SlotMap,\n VariantMap,\n} from '@novi-ui/core'\nimport { tv } from 'tailwind-variants'\nimport { focusRing } from '../styles/focus-ring'\n\n/**\n * インジケータは `+` と `−` の線で示す。\n * 三角矢印を回転させる表現は Raster の「動きで飾らない」原則に反するため使わない。\n */\nconst slots = {\n root: 'flex flex-col border-t border-[var(--novi-color-border)]',\n item: 'border-b border-[var(--novi-color-border)]',\n // 見出し要素とその中のボタンを分ける。支援技術のために必要\n heading: 'm-0',\n trigger: [\n 'flex items-center justify-between gap-4 w-full text-left',\n 'cursor-pointer outline-none',\n 'text-[var(--novi-color-fg)]',\n 'transition-colors duration-[var(--novi-duration-fast)] ease-[var(--novi-ease-standard)]',\n 'hover:bg-[var(--novi-color-subtle)]',\n 'data-[disabled]:opacity-40 data-[disabled]:cursor-default',\n focusRing,\n ].join(' '),\n // シェブロンは開閉の「方向」そのものを示すので回転させる。\n // +/− に置き換えると向きの情報が消える(ADR-T4 の rotate 例外)\n indicator: [\n 'shrink-0 text-[var(--novi-color-muted)]',\n 'transition-transform duration-[var(--novi-duration-fast)] ease-[var(--novi-ease-standard)]',\n 'group-data-[expanded]:rotate-180',\n 'motion-reduce:transition-none',\n ].join(' '),\n panel: 'text-[var(--novi-color-fg)] leading-[var(--novi-leading-body)]',\n} satisfies SlotMap<typeof accordionSlots, (typeof accordionRequiredSlots)[number]>\n\nconst variant: VariantMap<NoviVariant, { root: string; item: string }> = {\n solid: { root: 'border-t-0 gap-1', item: 'bg-[var(--novi-color-subtle)] border-b-0' },\n outline: { root: 'border-t border-[var(--novi-color-border)]', item: 'border-b' },\n soft: { root: 'border-t-0 gap-1', item: 'bg-[var(--novi-color-subtle)] border-b-0' },\n ghost: { root: 'border-t-0', item: 'border-b border-[var(--novi-color-border)]' },\n plain: { root: 'border-t-0 gap-2', item: 'border-b-0' },\n}\n\n/** trigger は行全体が押せる。どの段でも 48px を下回らせない。 */\nconst size: VariantMap<NoviSize, { trigger: string; panel: string }> = {\n sm: { trigger: 'min-h-12 px-4 py-3 text-[length:var(--novi-text-sm)]', panel: 'px-4 pb-3' },\n md: { trigger: 'min-h-14 px-5 py-4 text-[length:var(--novi-text-base)]', panel: 'px-5 pb-4' },\n lg: { trigger: 'min-h-16 px-6 py-5 text-[length:var(--novi-text-base)]', panel: 'px-6 pb-5' },\n}\n\n/**\n * Accordion のスタイル定義。\n *\n * @example\n * const myAccordion = tv({ extend: accordionStyles, slots: { panel: 'pt-2' } })\n */\nexport const accordionStyles = tv({\n slots,\n variants: { size, variant },\n defaultVariants: { variant: 'outline', size: 'md' },\n})\n\nexport type AccordionStyleProps = Parameters<typeof accordionStyles>[0]\n"],"mappings":";;;;;;;AAeA,MAAM,QAAQ;CACZ,MAAM;CACN,MAAM;CAEN,SAAS;CACT,SAAS;EACP;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC,CAAC,KAAK,GAAG;CAGV,WAAW;EACT;EACA;EACA;EACA;CACF,CAAC,CAAC,KAAK,GAAG;CACV,OAAO;AACT;;;;;;;AAuBA,MAAa,kBAAkB,GAAG;CAChC;CACA,UAAU;EAAE;GAbZ,IAAI;IAAE,SAAS;IAAwD,OAAO;GAAY;GAC1F,IAAI;IAAE,SAAS;IAA0D,OAAO;GAAY;GAC5F,IAAI;IAAE,SAAS;IAA0D,OAAO;GAAY;EAWhF;EAAM;GAtBlB,OAAO;IAAE,MAAM;IAAoB,MAAM;GAA2C;GACpF,SAAS;IAAE,MAAM;IAA8C,MAAM;GAAW;GAChF,MAAM;IAAE,MAAM;IAAoB,MAAM;GAA2C;GACnF,OAAO;IAAE,MAAM;IAAc,MAAM;GAA6C;GAChF,OAAO;IAAE,MAAM;IAAoB,MAAM;GAAa;EAkBpC;CAAQ;CAC1B,iBAAiB;EAAE,SAAS;EAAW,MAAM;CAAK;AACpD,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { AvatarProps } from "@novi-ui/core";
|
|
2
|
+
//#region src/avatar/avatar.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* 表示名からイニシャルを作る。
|
|
5
|
+
*
|
|
6
|
+
* 日本語の氏名は空白で区切られないことが多いので、
|
|
7
|
+
* 空白があれば各語の先頭、なければ先頭1文字を使う。
|
|
8
|
+
*/
|
|
9
|
+
declare function initialsOf(name: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* 人や組織を表す画像。読み込みに失敗したら fallback を表示する。
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* <Avatar src="/me.jpg" name="小島 佑翔" />
|
|
15
|
+
*/
|
|
16
|
+
declare function Avatar({ size, radius, src, name, fallback, badge, className, classNames, id }: AvatarProps): import("react").JSX.Element;
|
|
17
|
+
//#endregion
|
|
18
|
+
export { Avatar, initialsOf };
|
|
19
|
+
//# sourceMappingURL=avatar.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"avatar.d.mts","names":[],"sources":["../../src/avatar/avatar.tsx"],"mappings":";;;;;;;;iBAYgB,WAAW;;;;;;;iBAgBX,SACd,MACA,QACA,KACA,MACA,UACA,OACA,WACA,YACA,MACC,8BAAW,IAAA"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { avatarStyles } from "./avatar.styles.mjs";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
//#region src/avatar/avatar.tsx
|
|
6
|
+
/**
|
|
7
|
+
* 表示名からイニシャルを作る。
|
|
8
|
+
*
|
|
9
|
+
* 日本語の氏名は空白で区切られないことが多いので、
|
|
10
|
+
* 空白があれば各語の先頭、なければ先頭1文字を使う。
|
|
11
|
+
*/
|
|
12
|
+
function initialsOf(name) {
|
|
13
|
+
const parts = name.trim().split(/\s+/).filter(Boolean);
|
|
14
|
+
if (parts.length === 0) return "";
|
|
15
|
+
if (parts.length === 1) return [...parts[0]][0] ?? "";
|
|
16
|
+
return parts.slice(0, 2).map((part) => [...part][0] ?? "").join("");
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 人や組織を表す画像。読み込みに失敗したら fallback を表示する。
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* <Avatar src="/me.jpg" name="小島 佑翔" />
|
|
23
|
+
*/
|
|
24
|
+
function Avatar({ size, radius, src, name, fallback, badge, className, classNames, id }) {
|
|
25
|
+
const s = avatarStyles({
|
|
26
|
+
size,
|
|
27
|
+
radius
|
|
28
|
+
});
|
|
29
|
+
const [failed, setFailed] = useState(false);
|
|
30
|
+
const showImage = src !== void 0 && src !== "" && !failed;
|
|
31
|
+
return /* @__PURE__ */ jsxs("span", {
|
|
32
|
+
id,
|
|
33
|
+
"data-slot": "root",
|
|
34
|
+
className: s.root({ class: [className, classNames?.root] }),
|
|
35
|
+
children: [showImage ? /* @__PURE__ */ jsx("img", {
|
|
36
|
+
"data-slot": "image",
|
|
37
|
+
className: s.image({ class: classNames?.image }),
|
|
38
|
+
src,
|
|
39
|
+
alt: name ?? "",
|
|
40
|
+
onError: () => setFailed(true)
|
|
41
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
42
|
+
"data-slot": "fallback",
|
|
43
|
+
className: s.fallback({ class: classNames?.fallback }),
|
|
44
|
+
role: "img",
|
|
45
|
+
"aria-label": name,
|
|
46
|
+
children: fallback ?? (name !== void 0 ? initialsOf(name) : null)
|
|
47
|
+
}), badge !== void 0 && /* @__PURE__ */ jsx("span", {
|
|
48
|
+
"data-slot": "badge",
|
|
49
|
+
className: s.badge({ class: classNames?.badge }),
|
|
50
|
+
children: badge
|
|
51
|
+
})]
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
//#endregion
|
|
55
|
+
export { Avatar, initialsOf };
|
|
56
|
+
|
|
57
|
+
//# sourceMappingURL=avatar.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"avatar.mjs","names":[],"sources":["../../src/avatar/avatar.tsx"],"sourcesContent":["'use client'\n\nimport type { AvatarProps } from '@novi-ui/core'\nimport { useState } from 'react'\nimport { avatarStyles } from './avatar.styles'\n\n/**\n * 表示名からイニシャルを作る。\n *\n * 日本語の氏名は空白で区切られないことが多いので、\n * 空白があれば各語の先頭、なければ先頭1文字を使う。\n */\nexport function initialsOf(name: string): string {\n const parts = name.trim().split(/\\s+/).filter(Boolean)\n if (parts.length === 0) return ''\n if (parts.length === 1) return [...(parts[0] as string)][0] ?? ''\n return parts\n .slice(0, 2)\n .map((part) => [...part][0] ?? '')\n .join('')\n}\n\n/**\n * 人や組織を表す画像。読み込みに失敗したら fallback を表示する。\n *\n * @example\n * <Avatar src=\"/me.jpg\" name=\"小島 佑翔\" />\n */\nexport function Avatar({\n size,\n radius,\n src,\n name,\n fallback,\n badge,\n className,\n classNames,\n id,\n}: AvatarProps) {\n const s = avatarStyles({ size, radius })\n const [failed, setFailed] = useState(false)\n const showImage = src !== undefined && src !== '' && !failed\n\n return (\n <span id={id} data-slot=\"root\" className={s.root({ class: [className, classNames?.root] })}>\n {showImage ? (\n <img\n data-slot=\"image\"\n className={s.image({ class: classNames?.image })}\n src={src}\n alt={name ?? ''}\n onError={() => setFailed(true)}\n />\n ) : (\n // 画像の代わりなので role=\"img\" を与える。\n // これがないと aria-label が効かず、読み上げが「小佑」のような\n // イニシャルだけになって氏名が伝わらない\n <span\n data-slot=\"fallback\"\n className={s.fallback({ class: classNames?.fallback })}\n role=\"img\"\n aria-label={name}\n >\n {fallback ?? (name !== undefined ? initialsOf(name) : null)}\n </span>\n )}\n\n {badge !== undefined && (\n <span data-slot=\"badge\" className={s.badge({ class: classNames?.badge })}>\n {badge}\n </span>\n )}\n </span>\n )\n}\n"],"mappings":";;;;;;;;;;;AAYA,SAAgB,WAAW,MAAsB;CAC/C,MAAM,QAAQ,KAAK,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,OAAO,OAAO;CACrD,IAAI,MAAM,WAAW,GAAG,OAAO;CAC/B,IAAI,MAAM,WAAW,GAAG,OAAO,CAAC,GAAI,MAAM,EAAa,CAAC,CAAC,MAAM;CAC/D,OAAO,MACJ,MAAM,GAAG,CAAC,CAAC,CACX,KAAK,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,CACjC,KAAK,EAAE;AACZ;;;;;;;AAQA,SAAgB,OAAO,EACrB,MACA,QACA,KACA,MACA,UACA,OACA,WACA,YACA,MACc;CACd,MAAM,IAAI,aAAa;EAAE;EAAM;CAAO,CAAC;CACvC,MAAM,CAAC,QAAQ,aAAa,SAAS,KAAK;CAC1C,MAAM,YAAY,QAAQ,KAAA,KAAa,QAAQ,MAAM,CAAC;CAEtD,OACE,qBAAC,QAAD;EAAU;EAAI,aAAU;EAAO,WAAW,EAAE,KAAK,EAAE,OAAO,CAAC,WAAW,YAAY,IAAI,EAAE,CAAC;EAAzF,UAAA,CACG,YACC,oBAAC,OAAD;GACE,aAAU;GACV,WAAW,EAAE,MAAM,EAAE,OAAO,YAAY,MAAM,CAAC;GAC1C;GACL,KAAK,QAAQ;GACb,eAAe,UAAU,IAAI;EAC9B,CAAA,IAKD,oBAAC,QAAD;GACE,aAAU;GACV,WAAW,EAAE,SAAS,EAAE,OAAO,YAAY,SAAS,CAAC;GACrD,MAAK;GACL,cAAY;GAEX,UAAA,aAAa,SAAS,KAAA,IAAY,WAAW,IAAI,IAAI;EAClD,CAAA,GAGP,UAAU,KAAA,KACT,oBAAC,QAAD;GAAM,aAAU;GAAQ,WAAW,EAAE,MAAM,EAAE,OAAO,YAAY,MAAM,CAAC;GACpE,UAAA;EACG,CAAA,CAEJ;;AAEV"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { VariantMap } from "@novi-ui/core";
|
|
2
|
+
//#region src/avatar/avatar.styles.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Avatar のスタイル定義。
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* const myAvatar = tv({ extend: avatarStyles, slots: { root: 'border-2' } })
|
|
8
|
+
*/
|
|
9
|
+
declare const avatarStyles: import("tailwind-variants").TVReturnType<{
|
|
10
|
+
size: VariantMap<"sm" | "md" | "lg", {
|
|
11
|
+
root: string;
|
|
12
|
+
}>;
|
|
13
|
+
radius: VariantMap<"sm" | "md" | "lg" | "none" | "full", {
|
|
14
|
+
root: string;
|
|
15
|
+
}>;
|
|
16
|
+
}, {
|
|
17
|
+
root: string;
|
|
18
|
+
image: string;
|
|
19
|
+
fallback: string;
|
|
20
|
+
badge: string;
|
|
21
|
+
}, undefined, {
|
|
22
|
+
size: VariantMap<"sm" | "md" | "lg", {
|
|
23
|
+
root: string;
|
|
24
|
+
}>;
|
|
25
|
+
radius: VariantMap<"sm" | "md" | "lg" | "none" | "full", {
|
|
26
|
+
root: string;
|
|
27
|
+
}>;
|
|
28
|
+
}, {
|
|
29
|
+
root: string;
|
|
30
|
+
image: string;
|
|
31
|
+
fallback: string;
|
|
32
|
+
badge: string;
|
|
33
|
+
}, import("tailwind-variants").TVReturnTypeLike<{
|
|
34
|
+
size: VariantMap<"sm" | "md" | "lg", {
|
|
35
|
+
root: string;
|
|
36
|
+
}>;
|
|
37
|
+
radius: VariantMap<"sm" | "md" | "lg" | "none" | "full", {
|
|
38
|
+
root: string;
|
|
39
|
+
}>;
|
|
40
|
+
}, {
|
|
41
|
+
root: string;
|
|
42
|
+
image: string;
|
|
43
|
+
fallback: string;
|
|
44
|
+
badge: string;
|
|
45
|
+
}>>;
|
|
46
|
+
type AvatarStyleProps = Parameters<typeof avatarStyles>[0];
|
|
47
|
+
//#endregion
|
|
48
|
+
export { AvatarStyleProps, avatarStyles };
|
|
49
|
+
//# sourceMappingURL=avatar.styles.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"avatar.styles.d.mts","names":[],"sources":["../../src/avatar/avatar.styles.ts"],"mappings":";;;;;;;;cA6Ca,0CAAY;;IArBgB;;;IAOI;;;;;;;;;IAPJ;;;IAOI;;;;;;;;;IAPJ;;;IAOI;;;;;;;;KAoBjC,mBAAmB,kBAAkB"}
|