nihonput 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 hanab
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,318 @@
1
+ <p align="center">
2
+ <img src="https://img.shields.io/npm/v/nihonput?style=flat-square" alt="npm version" />
3
+ <img src="https://img.shields.io/npm/l/nihonput?style=flat-square" alt="license" />
4
+ <img src="https://img.shields.io/badge/React-18%2B-61dafb?style=flat-square&logo=react" alt="React 18+" />
5
+ <img src="https://img.shields.io/badge/TypeScript-5.0%2B-3178c6?style=flat-square&logo=typescript" alt="TypeScript" />
6
+ </p>
7
+
8
+ <h1 align="center">nihonput</h1>
9
+
10
+ <p align="center">
11
+ <strong>React用 日本語入力フォーム制御ライブラリ</strong>
12
+ </p>
13
+
14
+ <p align="center">
15
+ 日本語フォーム入力を扱うためのHeadless React Hooksライブラリ。<br />
16
+ カタカナ、郵便番号、電話番号などの自動変換・バリデーション・フォーマットを提供します。
17
+ </p>
18
+
19
+ <p align="center">
20
+ <a href="./README.md">🇺🇸 English</a>
21
+ </p>
22
+
23
+ ---
24
+
25
+ ## 特徴
26
+
27
+ - **カタカナ / ひらがな** — 自動変換とバリデーション
28
+ - **郵便番号** — `1234567` → `123-4567` へのフォーマットとバリデーション
29
+ - **都道府県** — バリデーションと自動補完(例: `東京` → `東京都`)
30
+ - **住所** — 全角/半角の文字幅制御
31
+ - **電話番号** — ハイフン自動挿入とバリデーション
32
+ - **IME対応** — 日本語IME変換中の適切なハンドリング
33
+ - **Headless** — UIなし、ロジックのみ。どんなデザインシステムとも組み合わせ可能
34
+ - **Tree-shaking対応** — 必要なものだけインポート可能
35
+
36
+ ---
37
+
38
+ ## インストール
39
+
40
+ ```bash
41
+ npm install nihonput
42
+ ```
43
+
44
+ ```bash
45
+ pnpm add nihonput
46
+ ```
47
+
48
+ ```bash
49
+ yarn add nihonput
50
+ ```
51
+
52
+ ---
53
+
54
+ ## クイックスタート
55
+
56
+ ```tsx
57
+ import { useNameField } from 'nihonput';
58
+
59
+ function NameInput() {
60
+ const field = useNameField({
61
+ kanaMode: 'auto-convert',
62
+ });
63
+
64
+ return (
65
+ <input
66
+ value={field.value}
67
+ onChange={field.onChange}
68
+ onBlur={field.onBlur}
69
+ onCompositionStart={field.onCompositionStart}
70
+ onCompositionEnd={field.onCompositionEnd}
71
+ placeholder="フリガナ"
72
+ />
73
+ );
74
+ }
75
+ ```
76
+
77
+ `やまだ たろう` と入力 → blur時に自動で `ヤマダ タロウ` に変換されます。
78
+
79
+ ---
80
+
81
+ ## Hooks
82
+
83
+ ### useNameField
84
+
85
+ フリガナなど、カタカナ入力用のフィールド。
86
+
87
+ ```tsx
88
+ const field = useNameField({
89
+ kanaMode: 'auto-convert', // または 'error-on-hiragana'
90
+ normalizeOn: 'blur', // 'blur' | 'change' | 'compositionEnd'
91
+ validateOn: 'blur', // 'blur' | 'change' | 'submit'
92
+ errorMessages: {
93
+ katakanaOnly: 'カタカナで入力してください',
94
+ },
95
+ });
96
+ ```
97
+
98
+ | オプション | 値 | 説明 |
99
+ |-----------|-----|------|
100
+ | `kanaMode` | `'auto-convert'` | ひらがな → カタカナ自動変換 |
101
+ | | `'error-on-hiragana'` | ひらがな入力時にエラー表示 |
102
+ | `normalizeOn` | `'blur'` `'change'` `'compositionEnd'` | 正規化のタイミング |
103
+ | `validateOn` | `'blur'` `'change'` `'submit'` | バリデーションのタイミング |
104
+
105
+ ---
106
+
107
+ ### usePostalCodeField
108
+
109
+ 郵便番号フィールド用。
110
+
111
+ ```tsx
112
+ const field = usePostalCodeField({
113
+ widthMode: 'half-width-only', // または 'full-width-only'
114
+ onInvalid: 'auto-convert', // または 'error'
115
+ autoHyphen: true,
116
+ });
117
+ ```
118
+
119
+ | オプション | 値 | 説明 |
120
+ |-----------|-----|------|
121
+ | `widthMode` | `'half-width-only'` | 半角のみ許可(`123-4567`) |
122
+ | | `'full-width-only'` | 全角のみ許可(`123−4567`) |
123
+ | `onInvalid` | `'auto-convert'` | 自動変換 |
124
+ | | `'error'` | エラー表示 |
125
+ | `autoHyphen` | `true` / `false` | ハイフン自動挿入 |
126
+
127
+ **変換例:** `1234567` → `123-4567`
128
+
129
+ ---
130
+
131
+ ### usePrefectureField
132
+
133
+ 都道府県フィールド用。
134
+
135
+ ```tsx
136
+ const field = usePrefectureField({
137
+ autoComplete: true,
138
+ });
139
+ ```
140
+
141
+ | オプション | 値 | 説明 |
142
+ |-----------|-----|------|
143
+ | `autoComplete` | `true` / `false` | 短縮名を自動補完 |
144
+
145
+ **変換例:** `東京` → `東京都`、`大阪` → `大阪府`
146
+
147
+ ---
148
+
149
+ ### useAddressField
150
+
151
+ 住所フィールド用。文字幅の制御が可能。
152
+
153
+ ```tsx
154
+ const field = useAddressField({
155
+ alphanumericMode: 'full-width-only', // または 'allow-half-width'
156
+ onInvalid: 'auto-convert',
157
+ });
158
+ ```
159
+
160
+ | オプション | 値 | 説明 |
161
+ |-----------|-----|------|
162
+ | `alphanumericMode` | `'full-width-only'` | 半角英数字を全角に変換 |
163
+ | | `'allow-half-width'` | 半角も許可 |
164
+ | `onInvalid` | `'auto-convert'` / `'error'` | 違反時の動作 |
165
+
166
+ **変換例:** `渋谷区1-2-3` → `渋谷区1−2−3`
167
+
168
+ ---
169
+
170
+ ### usePhoneNumberField
171
+
172
+ 電話番号フィールド用。
173
+
174
+ ```tsx
175
+ const field = usePhoneNumberField({
176
+ onInvalid: 'auto-convert',
177
+ autoHyphen: true,
178
+ });
179
+ ```
180
+
181
+ | オプション | 値 | 説明 |
182
+ |-----------|-----|------|
183
+ | `onInvalid` | `'auto-convert'` / `'error'` | 違反時の動作 |
184
+ | `autoHyphen` | `true` / `false` | ハイフン自動挿入 |
185
+
186
+ **変換例:** `09012345678` → `090-1234-5678`
187
+
188
+ ---
189
+
190
+ ## 戻り値
191
+
192
+ すべてのフックは同じインターフェースを返します:
193
+
194
+ ```tsx
195
+ interface FieldReturn {
196
+ value: string; // 現在の値
197
+ onChange: (e) => void; // changeイベントハンドラ
198
+ onBlur: (e) => void; // blurイベントハンドラ
199
+ onCompositionStart: (e) => void; // IME変換開始
200
+ onCompositionEnd: (e) => void; // IME変換確定
201
+ error: string | null; // エラーメッセージ
202
+ isValidating: boolean; // バリデーション中かどうか
203
+ isComposing: boolean; // IME変換中かどうか
204
+ }
205
+ ```
206
+
207
+ ---
208
+
209
+ ## 単体関数
210
+
211
+ フックを使わずに、バリデーション関数や正規化関数を単体で使用できます。
212
+
213
+ ### バリデーション関数
214
+
215
+ ```tsx
216
+ import {
217
+ validateKatakana,
218
+ validateHiragana,
219
+ validatePostalCode,
220
+ validatePrefecture,
221
+ validatePhoneNumber,
222
+ } from 'nihonput';
223
+
224
+ validateKatakana('カタカナ'); // true
225
+ validateKatakana('ひらがな'); // false
226
+
227
+ validatePostalCode('123-4567'); // true
228
+ validatePostalCode('1234567'); // true
229
+
230
+ validatePrefecture('東京都'); // true
231
+ validatePrefecture('東京'); // true(短縮形もOK)
232
+
233
+ validatePhoneNumber('090-1234-5678'); // true
234
+ ```
235
+
236
+ ### 正規化関数
237
+
238
+ ```tsx
239
+ import {
240
+ toKatakana,
241
+ toHiragana,
242
+ toFullWidth,
243
+ toHalfWidth,
244
+ addPostalCodeHyphen,
245
+ addPhoneNumberHyphen,
246
+ } from 'nihonput';
247
+
248
+ toKatakana('ひらがな'); // 'ヒラガナ'
249
+ toHiragana('カタカナ'); // 'かたかな'
250
+
251
+ toFullWidth('abc123'); // 'abc123'
252
+ toHalfWidth('123'); // '123'
253
+
254
+ addPostalCodeHyphen('1234567'); // '123-4567'
255
+ addPhoneNumberHyphen('09012345678'); // '090-1234-5678'
256
+ ```
257
+
258
+ ---
259
+
260
+ ## React Hook Form との連携
261
+
262
+ ```tsx
263
+ import { useForm } from 'react-hook-form';
264
+ import { toKatakana, validateKatakana } from 'nihonput';
265
+
266
+ function Form() {
267
+ const { register, handleSubmit, setValue, formState: { errors } } = useForm();
268
+
269
+ return (
270
+ <form onSubmit={handleSubmit(console.log)}>
271
+ <input
272
+ {...register('furigana', {
273
+ validate: (v) => validateKatakana(v) || 'カタカナで入力してください',
274
+ })}
275
+ onBlur={(e) => setValue('furigana', toKatakana(e.target.value))}
276
+ />
277
+ {errors.furigana && <span>{errors.furigana.message}</span>}
278
+
279
+ <button type="submit">送信</button>
280
+ </form>
281
+ );
282
+ }
283
+ ```
284
+
285
+ ---
286
+
287
+ ## エクスポート一覧
288
+
289
+ ### Hooks
290
+ - `useNameField`
291
+ - `usePostalCodeField`
292
+ - `usePrefectureField`
293
+ - `useAddressField`
294
+ - `usePhoneNumberField`
295
+
296
+ ### バリデーション関数
297
+ - `validateKatakana` / `containsHiragana`
298
+ - `validateHiragana` / `containsKatakana`
299
+ - `validatePostalCode` / `isHalfWidthPostalCode` / `isFullWidthPostalCode`
300
+ - `validatePrefecture` / `normalizePrefecture`
301
+ - `validatePhoneNumber` / `isHalfWidthPhoneNumber`
302
+
303
+ ### 正規化関数
304
+ - `toKatakana` / `toHiragana`
305
+ - `toFullWidth` / `toHalfWidth`
306
+ - `toFullWidthNumbers` / `toHalfWidthNumbers`
307
+ - `addPostalCodeHyphen` / `addPhoneNumberHyphen`
308
+
309
+ ### 定数
310
+ - `PREFECTURES` — 47都道府県の配列
311
+ - `PREFECTURE_SHORT_NAMES` — 短縮名から正式名へのマップ
312
+ - `defaultErrorMessages` — デフォルトエラーメッセージ
313
+
314
+ ---
315
+
316
+ ## ライセンス
317
+
318
+ MIT © [Hanab Labs](https://github.com/HanabLabs)
package/README.md ADDED
@@ -0,0 +1,318 @@
1
+ <p align="center">
2
+ <img src="https://img.shields.io/npm/v/nihonput?style=flat-square" alt="npm version" />
3
+ <img src="https://img.shields.io/npm/l/nihonput?style=flat-square" alt="license" />
4
+ <img src="https://img.shields.io/badge/React-18%2B-61dafb?style=flat-square&logo=react" alt="React 18+" />
5
+ <img src="https://img.shields.io/badge/TypeScript-5.0%2B-3178c6?style=flat-square&logo=typescript" alt="TypeScript" />
6
+ </p>
7
+
8
+ <h1 align="center">nihonput</h1>
9
+
10
+ <p align="center">
11
+ <strong>Japanese Input Form Control Library for React</strong>
12
+ </p>
13
+
14
+ <p align="center">
15
+ A headless React hooks library for handling Japanese form inputs.<br />
16
+ Automatic conversion, validation, and formatting for Katakana, postal codes, phone numbers, and more.
17
+ </p>
18
+
19
+ <p align="center">
20
+ <a href="./README.ja.md">🇯🇵 日本語</a>
21
+ </p>
22
+
23
+ ---
24
+
25
+ ## Features
26
+
27
+ - **Katakana / Hiragana** — Auto-convert and validate Japanese kana
28
+ - **Postal Code** — Format `1234567` → `123-4567` with validation
29
+ - **Prefecture** — Validate and auto-complete (e.g., `東京` → `東京都`)
30
+ - **Address** — Full-width / half-width character control
31
+ - **Phone Number** — Format with hyphens and validate
32
+ - **IME Aware** — Proper handling of Japanese IME composition
33
+ - **Headless** — No UI, just logic. Use with any design system
34
+ - **Tree-shakeable** — Import only what you need
35
+
36
+ ---
37
+
38
+ ## Installation
39
+
40
+ ```bash
41
+ npm install nihonput
42
+ ```
43
+
44
+ ```bash
45
+ pnpm add nihonput
46
+ ```
47
+
48
+ ```bash
49
+ yarn add nihonput
50
+ ```
51
+
52
+ ---
53
+
54
+ ## Quick Start
55
+
56
+ ```tsx
57
+ import { useNameField } from 'nihonput';
58
+
59
+ function NameInput() {
60
+ const field = useNameField({
61
+ kanaMode: 'auto-convert',
62
+ });
63
+
64
+ return (
65
+ <input
66
+ value={field.value}
67
+ onChange={field.onChange}
68
+ onBlur={field.onBlur}
69
+ onCompositionStart={field.onCompositionStart}
70
+ onCompositionEnd={field.onCompositionEnd}
71
+ placeholder="フリガナ"
72
+ />
73
+ );
74
+ }
75
+ ```
76
+
77
+ Type `やまだ たろう` → automatically converts to `ヤマダ タロウ` on blur.
78
+
79
+ ---
80
+
81
+ ## Hooks
82
+
83
+ ### useNameField
84
+
85
+ For name fields with Katakana input.
86
+
87
+ ```tsx
88
+ const field = useNameField({
89
+ kanaMode: 'auto-convert', // or 'error-on-hiragana'
90
+ normalizeOn: 'blur', // 'blur' | 'change' | 'compositionEnd'
91
+ validateOn: 'blur', // 'blur' | 'change' | 'submit'
92
+ errorMessages: {
93
+ katakanaOnly: 'カタカナで入力してください',
94
+ },
95
+ });
96
+ ```
97
+
98
+ | Option | Values | Description |
99
+ |--------|--------|-------------|
100
+ | `kanaMode` | `'auto-convert'` | Hiragana → Katakana auto-conversion |
101
+ | | `'error-on-hiragana'` | Show error if Hiragana is entered |
102
+ | `normalizeOn` | `'blur'` `'change'` `'compositionEnd'` | When to normalize |
103
+ | `validateOn` | `'blur'` `'change'` `'submit'` | When to validate |
104
+
105
+ ---
106
+
107
+ ### usePostalCodeField
108
+
109
+ For Japanese postal codes (郵便番号).
110
+
111
+ ```tsx
112
+ const field = usePostalCodeField({
113
+ widthMode: 'half-width-only', // or 'full-width-only'
114
+ onInvalid: 'auto-convert', // or 'error'
115
+ autoHyphen: true,
116
+ });
117
+ ```
118
+
119
+ | Option | Values | Description |
120
+ |--------|--------|-------------|
121
+ | `widthMode` | `'half-width-only'` | Only allow `123-4567` |
122
+ | | `'full-width-only'` | Only allow `123−4567` |
123
+ | `onInvalid` | `'auto-convert'` | Auto-convert to correct width |
124
+ | | `'error'` | Show validation error |
125
+ | `autoHyphen` | `true` / `false` | Auto-insert hyphen |
126
+
127
+ **Example:** `1234567` → `123-4567`
128
+
129
+ ---
130
+
131
+ ### usePrefectureField
132
+
133
+ For Japanese prefecture (都道府県) input.
134
+
135
+ ```tsx
136
+ const field = usePrefectureField({
137
+ autoComplete: true,
138
+ });
139
+ ```
140
+
141
+ | Option | Values | Description |
142
+ |--------|--------|-------------|
143
+ | `autoComplete` | `true` / `false` | Auto-complete short names |
144
+
145
+ **Example:** `東京` → `東京都`, `大阪` → `大阪府`
146
+
147
+ ---
148
+
149
+ ### useAddressField
150
+
151
+ For address fields with character width control.
152
+
153
+ ```tsx
154
+ const field = useAddressField({
155
+ alphanumericMode: 'full-width-only', // or 'allow-half-width'
156
+ onInvalid: 'auto-convert',
157
+ });
158
+ ```
159
+
160
+ | Option | Values | Description |
161
+ |--------|--------|-------------|
162
+ | `alphanumericMode` | `'full-width-only'` | Convert half-width to full-width |
163
+ | | `'allow-half-width'` | Allow both |
164
+ | `onInvalid` | `'auto-convert'` / `'error'` | Behavior on invalid input |
165
+
166
+ **Example:** `渋谷区1-2-3` → `渋谷区1−2−3`
167
+
168
+ ---
169
+
170
+ ### usePhoneNumberField
171
+
172
+ For Japanese phone numbers.
173
+
174
+ ```tsx
175
+ const field = usePhoneNumberField({
176
+ onInvalid: 'auto-convert',
177
+ autoHyphen: true,
178
+ });
179
+ ```
180
+
181
+ | Option | Values | Description |
182
+ |--------|--------|-------------|
183
+ | `onInvalid` | `'auto-convert'` / `'error'` | Behavior on invalid input |
184
+ | `autoHyphen` | `true` / `false` | Auto-insert hyphens |
185
+
186
+ **Example:** `09012345678` → `090-1234-5678`
187
+
188
+ ---
189
+
190
+ ## Return Value
191
+
192
+ All hooks return the same interface:
193
+
194
+ ```tsx
195
+ interface FieldReturn {
196
+ value: string; // Current value
197
+ onChange: (e) => void; // Change handler
198
+ onBlur: (e) => void; // Blur handler
199
+ onCompositionStart: (e) => void; // IME composition start
200
+ onCompositionEnd: (e) => void; // IME composition end
201
+ error: string | null; // Validation error message
202
+ isValidating: boolean; // Validation in progress
203
+ isComposing: boolean; // IME composing state
204
+ }
205
+ ```
206
+
207
+ ---
208
+
209
+ ## Standalone Functions
210
+
211
+ Use validators and normalizers without hooks:
212
+
213
+ ### Validators
214
+
215
+ ```tsx
216
+ import {
217
+ validateKatakana,
218
+ validateHiragana,
219
+ validatePostalCode,
220
+ validatePrefecture,
221
+ validatePhoneNumber,
222
+ } from 'nihonput';
223
+
224
+ validateKatakana('カタカナ'); // true
225
+ validateKatakana('ひらがな'); // false
226
+
227
+ validatePostalCode('123-4567'); // true
228
+ validatePostalCode('1234567'); // true
229
+
230
+ validatePrefecture('東京都'); // true
231
+ validatePrefecture('東京'); // true (short form)
232
+
233
+ validatePhoneNumber('090-1234-5678'); // true
234
+ ```
235
+
236
+ ### Normalizers
237
+
238
+ ```tsx
239
+ import {
240
+ toKatakana,
241
+ toHiragana,
242
+ toFullWidth,
243
+ toHalfWidth,
244
+ addPostalCodeHyphen,
245
+ addPhoneNumberHyphen,
246
+ } from 'nihonput';
247
+
248
+ toKatakana('ひらがな'); // 'ヒラガナ'
249
+ toHiragana('カタカナ'); // 'かたかな'
250
+
251
+ toFullWidth('abc123'); // 'abc123'
252
+ toHalfWidth('123'); // '123'
253
+
254
+ addPostalCodeHyphen('1234567'); // '123-4567'
255
+ addPhoneNumberHyphen('09012345678'); // '090-1234-5678'
256
+ ```
257
+
258
+ ---
259
+
260
+ ## React Hook Form Integration
261
+
262
+ ```tsx
263
+ import { useForm } from 'react-hook-form';
264
+ import { toKatakana, validateKatakana } from 'nihonput';
265
+
266
+ function Form() {
267
+ const { register, handleSubmit, setValue, formState: { errors } } = useForm();
268
+
269
+ return (
270
+ <form onSubmit={handleSubmit(console.log)}>
271
+ <input
272
+ {...register('furigana', {
273
+ validate: (v) => validateKatakana(v) || 'カタカナで入力してください',
274
+ })}
275
+ onBlur={(e) => setValue('furigana', toKatakana(e.target.value))}
276
+ />
277
+ {errors.furigana && <span>{errors.furigana.message}</span>}
278
+
279
+ <button type="submit">Submit</button>
280
+ </form>
281
+ );
282
+ }
283
+ ```
284
+
285
+ ---
286
+
287
+ ## All Exports
288
+
289
+ ### Hooks
290
+ - `useNameField`
291
+ - `usePostalCodeField`
292
+ - `usePrefectureField`
293
+ - `useAddressField`
294
+ - `usePhoneNumberField`
295
+
296
+ ### Validators
297
+ - `validateKatakana` / `containsHiragana`
298
+ - `validateHiragana` / `containsKatakana`
299
+ - `validatePostalCode` / `isHalfWidthPostalCode` / `isFullWidthPostalCode`
300
+ - `validatePrefecture` / `normalizePrefecture`
301
+ - `validatePhoneNumber` / `isHalfWidthPhoneNumber`
302
+
303
+ ### Normalizers
304
+ - `toKatakana` / `toHiragana`
305
+ - `toFullWidth` / `toHalfWidth`
306
+ - `toFullWidthNumbers` / `toHalfWidthNumbers`
307
+ - `addPostalCodeHyphen` / `addPhoneNumberHyphen`
308
+
309
+ ### Constants
310
+ - `PREFECTURES` — Array of all 47 prefectures
311
+ - `PREFECTURE_SHORT_NAMES` — Map of short names to full names
312
+ - `defaultErrorMessages` — Default error messages (Japanese)
313
+
314
+ ---
315
+
316
+ ## License
317
+
318
+ MIT © [Hanab Labs](https://github.com/HanabLabs)