AIエージェントにUIを作成してもらうときに、数値入力欄を数値形式の入力欄になってしまうことがあります。
毎回、後から修正してもらうのが面倒なので、プロンプトを用意しました。
数値形式の入力欄とは、HTMLの<input type="number">にあたります。
入力内容を同期していると、空欄や不正な文字が入力できず、入力内容が強制的に制限されることで、
- 例えば10→5に変更したくても、5を入力する前に10を消すことができなかったり、最小値、最大値の関係で1や105にもできなかったりと使いにくくなります。
- 少数を入力、あるいは消したいときも同様です。
そのため不正な入力自体は可能で、後から数値に変換したり、バリデーションしたものを反映する必要があります。
また、入力内容をプレビューなどに反映する機能があるアプリでは、入力中でも即時プレビューに反映された方が便利です。
次のプロンプトは普段使用しているReact向けのものです。技術スタックが異なる場合は調整してください。
プロンプト 指示文
### 数値入力欄の実装ルール
数値入力欄を実装するときは、以下のルールを必ず守ること。**`type="number"` の入力欄は使用しない**(ブラウザが空欄・"1."・"-"・範囲外などの入力をブロックしてしまい、例: 10→5 に直す際に一度消すこともできない)。
1. **入力欄はテキスト入力にする**: `type="text"`(MUI なら `<TextField>` のデフォルト)を使う。数値キーボードが必要なら `inputMode="decimal"` を付けてもよい。`type="number"` にしない。
2. **value は文字列で保持する**: state やフォーム変数は `number` ではなく `string` 型にし、実際のデータ(数値)とは分離する。初期値や対象データの変更時のみ `useEffect` 等で文字列を同期する。そのため、空欄・途中文字列・範囲外の値も入力途中はそのまま保持され、入力がブロックされない。
3. **後から数値に変換し、不正入力はユーザーに明示する**: 値を実際に使う時点で `parseFloat`(または `Number`/`parseInt`)で変換する。`Number.isNaN(parseFloat(value))` なら「無効」として実データには書き込まず、入力欄の `error` props を `true` にして赤字表示する(MUI の `<TextField error={isInvalid}>` など)。さらに `helperText` に理由(例: `isInvalid && "数値を入力してください"`)を表示し、実行ボタンは `disabled` にして確定できないようにする。不正入力を黙って無視・スルーしないこと。
4. **入力中にプレビューへ即時反映**: 変換が有効になるたびに即座に実データとプレビューを更新する(例: `triggerPreviewUpdate()`)。不正文字列の間は更新をスキップし、前の値のままに保つ。
5. **範囲制約は min/max 属性でなく数値に適用**: 最小値・最大値は `Math.max(0, val)` / `Math.min(...)` を変換後の数値へ適用する。input の `min`/`max`/`step` は設定しない(途中入力や一時的な範囲外入力を許すため)。
6. **確定時も最終的に `parseFloat` で変換した有効値のみ使う**: サーバー送信・保存の直前に再度変換し、無効値(`isNaN`)なら送信しない。
Skill.md
---
name: number-input
description: 数値入力欄、数値フィールド、数値テキストフィールド、スライダー値入力(単位付き入力、座標入力、長さ・角度・金額などの数値入力)を実装するとき、または type="number" の入力欄を見かけたときに使用。type="number" を禁止し、テキスト入力+文字列状態+parseFloat による後変換で、途中入力や範囲外入力をブロックしない実装にする
---
# 数値入力欄の実装ルール
数値入力欄を実装するときは、以下のルールを必ず守ること。**`type="number"` の入力欄は使用しない**(ブラウザが空欄・`1.`・`-`・範囲外などの入力をブロックしてしまい、例: 10→5 に直す際に一度消すこともできない)。
## ルール
1. **入力欄はテキスト入力にする**: `type="text"`(MUI なら `<TextField>`、React なら `<input type="text">` のデフォルト)を使う。数値キーボードが必要なら `inputMode="decimal"` を付けてもよい。`type="number"` にしない。
2. **value は文字列で保持する**: state やフォーム変数は `number` ではなく `string` 型にし、実際のデータ(数値)とは分離する。初期値や対象データの変更時のみ `useEffect` 等で文字列を同期する。これにより、空欄・途中文字列・範囲外の値も入力途中はそのまま保持され、入力がブロックされない。
3. **後から数値に変換し、不正入力はユーザーに明示する**: 値を実際に使う時点で `parseFloat`(または `Number`/`parseInt`)で変換する。`Number.isNaN(parseFloat(value))` なら「無効」として実データには書き込まず、**入力欄の `error` 属性を `true` にして一目でわかるようにする**(MUI の `<TextField error={isInvalid}>` などで赤字表示)。さらに `helperText` に理由(例: `isInvalid && "数値を入力してください"`)を表示し、実行ボタンは `disabled` にして確定できないようにする。表示色を暗い背景にしている場合は `error` 表示が十分見えることも確認する。**不正入力を黙って無視・スルーしないこと。**
4. **入力中にプレビューへ即時反映**: 変換が有効になるたびに即座に実データとプレビューを更新する(例: `triggerPreviewUpdate()`)。不正文字列の間は更新をスキップし、前の値のままに保つ。
5. **範囲制約は min/max 属性でなく数値に適用**: 最小値・最大値は `Math.max(0, val)` / `Math.min(...)` を変換後の数値へ適用する。input の `min`/`max`/`step` は設定しない(途中入力や一時的な範囲外入力を許すため)。
6. **確定時も最終的に `parseFloat` で変換した有効値のみ使う**: サーバー送信・保存の直前に再度変換し、無効値(`isNaN`)なら送信しない。
## 避けるべきパターン(旧実装の残存例)
- `type="number"` を使い、`onChange={e => model.value = Number(e.target.value)}` のように入力のたびに即時変換してモデルへ書き戻すもの。
- 実データ(number)をそのまま入力欄の `value` に直接バインドするもの。
これらのパターンは、途中状態(空、`1.`、`-`、範囲外)が表現できないため、10→5 の変更などが不可能になる。
正解となるアプリについて調査、説明してもらった:
このアプリの数値入力欄のデータの扱い方について説明してください。AIエージェントにUIを作成してもらうときに、毎回、数値入力欄を数値形式の入力欄にしてしまい、空欄や不正な文字が入力できず、入力内容が強制的に制限されることで、例えば10→5に変更したくても、5を入力する前に10を消すことができなかったり、最小値、最大値の関係で1や105にもできなかったりと使いにくくなります。また、少数を入力、あるいは消したいときも同様です。そのため不正な入力自体は可能で、後から数値に変換する必要があります。また、入力内容をプレビューなどに反映する機能があるアプリでは、入力中でも即時プレビューに反映された方が便利です。このアプリではそういった問題が解決されているのですが、AIエージェントに指示するための文章を作成してください。
Q. この問題は入力欄の値を同期するときだけ発生しますか?
A. いいえ、同期だけが原因ではありません。同期の有無で症状が分かれます。
- 「10→5 にできない」「1・105 にできない」→ 同期が原因。入力欄の表示値を数値のモデルと結び、
onChangeで毎回Number()/parseFloat()して書き戻す実装では、途中の状態(空、範囲外、小数の途中)がモデルと一致せず、モデル値に表示が引き戻されます。これが最初の症状の主因です。 - 「小数や検索文字が入力できない」→
type="number"自体が原因。ブラウザは数値でない中間入力(1.、-、英字)に対しe.target.valueを''にしてしまい、同期していない(ローカル文字列 state のみ)場合でも、キー入力のたびに変換→反映する実装だと途中状態が失われます。type="number"の入力欄は「どのみち文字列としての中間状態を正しく渡せない」ことが根本原因です。
つまり、入力のたびの即時変換・同期と、type="number" による文字列中間状態の破棄の2つが独立に問題を引き起こします。そのため指示文のルール(テキスト入力 + 文字列 state + 変換は使用時のみ)は、同期タイミングだけ直しても不十分で、両方必要、という意味です。