@gumigumih/react-calculator-input-form 1.1.2 → 1.1.4
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 +0 -40
- package/dist/components/organisms/Calculator.d.ts +1 -1
- package/dist/index.esm.js +87 -33
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +87 -33
- package/dist/index.js.map +1 -1
- package/dist/styles/index.d.ts +6 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -118,29 +118,6 @@ function App() {
|
|
|
118
118
|
/>
|
|
119
119
|
```
|
|
120
120
|
|
|
121
|
-
### Calculator オプション
|
|
122
|
-
|
|
123
|
-
```tsx
|
|
124
|
-
<Calculator
|
|
125
|
-
isOpen={isOpen}
|
|
126
|
-
onClose={() => setIsOpen(false)}
|
|
127
|
-
onCalculate={handleCalculate}
|
|
128
|
-
// 税計算の有効/無効
|
|
129
|
-
enableTaxCalculation={false}
|
|
130
|
-
// 小数点以下の桁数
|
|
131
|
-
decimalPlaces={2}
|
|
132
|
-
// 数値フォーマットオプション
|
|
133
|
-
numberFormatOptions={{
|
|
134
|
-
thousandSeparator: true,
|
|
135
|
-
allowNegative: false,
|
|
136
|
-
allowLeadingZeros: false,
|
|
137
|
-
decimalScale: 2,
|
|
138
|
-
prefix: "¥",
|
|
139
|
-
suffix: ""
|
|
140
|
-
}}
|
|
141
|
-
/>
|
|
142
|
-
```
|
|
143
|
-
|
|
144
121
|
## 🔗 react-number-format対応
|
|
145
122
|
|
|
146
123
|
このプラグインは**react-number-format**ライブラリと完全に統合されており、豊富な数値フォーマット機能を提供します。
|
|
@@ -200,8 +177,6 @@ react-number-formatの**すべてのプロパティ**が利用可能です。詳
|
|
|
200
177
|
|
|
201
178
|
## 📋 Props
|
|
202
179
|
|
|
203
|
-
### CalculatorInputForm Props
|
|
204
|
-
|
|
205
180
|
| プロパティ | 型 | 必須 | デフォルト | 説明 |
|
|
206
181
|
|------------|----|------|------------|------|
|
|
207
182
|
| `value` | string | ✓ | - | 入力値 |
|
|
@@ -214,23 +189,8 @@ react-number-formatの**すべてのプロパティ**が利用可能です。詳
|
|
|
214
189
|
| `decimalPlaces` | number | | 6 | 小数点以下の最大桁数 |
|
|
215
190
|
| `numberFormatOptions` | object | | {} | 数値フォーマットの詳細設定 |
|
|
216
191
|
|
|
217
|
-
### Calculator Props
|
|
218
|
-
|
|
219
|
-
| プロパティ | 型 | 必須 | デフォルト | 説明 |
|
|
220
|
-
|------------|----|------|------------|------|
|
|
221
|
-
| `isOpen` | boolean | ✓ | - | 電卓の表示/非表示 |
|
|
222
|
-
| `onClose` | () => void | ✓ | - | 電卓を閉じる時のコールバック |
|
|
223
|
-
| `onCalculate` | (value: string) => void | ✓ | - | 計算完了時のコールバック |
|
|
224
|
-
| `initialValue` | string | | "" | 初期値 |
|
|
225
|
-
| `title` | string | | "金額入力" | モーダルのタイトル |
|
|
226
|
-
| `description` | string | | "税込・税抜や小数計算に対応" | モーダルの説明文 |
|
|
227
|
-
| `enableTaxCalculation` | boolean | | true | 税計算機能の有効/無効 |
|
|
228
|
-
| `decimalPlaces` | number | | 6 | 小数点以下の最大桁数 |
|
|
229
|
-
| `numberFormatOptions` | object | | {} | 数値フォーマットの詳細設定(react-number-formatの全オプションが利用可能) |
|
|
230
|
-
|
|
231
192
|
## ⌨️ キーボードショートカット
|
|
232
193
|
|
|
233
|
-
- `0-9`: 数字入力
|
|
234
194
|
- `+`, `-`, `*`, `/`: 演算子
|
|
235
195
|
- `Enter`, `=`: 計算実行
|
|
236
196
|
- `Escape`: 電卓を閉じる
|
|
@@ -10,4 +10,4 @@ export interface CalculatorProps {
|
|
|
10
10
|
numberFormatOptions?: any;
|
|
11
11
|
placeholder?: string;
|
|
12
12
|
}
|
|
13
|
-
export declare const Calculator: ({ isOpen, onClose, onCalculate, initialValue, title, description, enableTaxCalculation, decimalPlaces, numberFormatOptions, }: CalculatorProps) => import("react").ReactPortal | null;
|
|
13
|
+
export declare const Calculator: ({ isOpen, onClose, onCalculate, initialValue, title, description, enableTaxCalculation, decimalPlaces, numberFormatOptions, placeholder, }: CalculatorProps) => import("react").ReactPortal | null;
|
package/dist/index.esm.js
CHANGED
|
@@ -6379,7 +6379,7 @@ function NumericFormat(props) {
|
|
|
6379
6379
|
}
|
|
6380
6380
|
|
|
6381
6381
|
const CalculatorDisplay = ({ value, error, inputRef, editable, placeholder, onChange, numberFormatOptions = {} }) => {
|
|
6382
|
-
return (jsxs(Fragment, { children: [editable ? (jsx(NumericFormat, { className: "calculator-display-input", value: value, onValueChange: (vals) => onChange?.(vals.value), placeholder: placeholder ?? '数値を入力', inputMode: "decimal", ...numberFormatOptions })) : (jsx("div", { className: "calculator-display-input", children: jsx("div", { ref: inputRef, style: { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, children: value ? (jsx(NumericFormat, { value: value, displayType: "text", ...numberFormatOptions })) : '' }) })), error && jsx("div", { className: "calculator-error", children: error })] }));
|
|
6382
|
+
return (jsxs(Fragment, { children: [editable ? (jsx(NumericFormat, { className: "calculator-display-input", value: value, onValueChange: (vals) => onChange?.(vals.value), placeholder: placeholder ?? '数値を入力', inputMode: "decimal", ...numberFormatOptions })) : (jsx("div", { className: "calculator-display-input", children: jsx("div", { ref: inputRef, style: { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, children: value ? (jsx(NumericFormat, { value: value, displayType: "text", ...numberFormatOptions })) : (jsx("span", { className: "text-gray-400", children: placeholder ?? '0' })) }) })), error && jsx("div", { className: "calculator-error", children: error })] }));
|
|
6383
6383
|
};
|
|
6384
6384
|
|
|
6385
6385
|
const CalculatorKeypad = ({ onButtonClick, onEqual, onDecide, onTaxInclude, onTaxExclude, enableTaxCalculation = true, decimalPlaces = 6 }) => {
|
|
@@ -6446,11 +6446,11 @@ function calculateExpression(expr) {
|
|
|
6446
6446
|
return '0';
|
|
6447
6447
|
}
|
|
6448
6448
|
}
|
|
6449
|
-
const Calculator = ({ isOpen, onClose, onCalculate, initialValue = '', title, description, enableTaxCalculation = false, decimalPlaces = 6, numberFormatOptions = {},
|
|
6450
|
-
|
|
6451
|
-
|
|
6452
|
-
const [input, setInput] = useState(initialValue || '');
|
|
6449
|
+
const Calculator = ({ isOpen, onClose, onCalculate, initialValue = '', title, description, enableTaxCalculation = false, decimalPlaces = 6, numberFormatOptions = {}, placeholder, }) => {
|
|
6450
|
+
const [expression, setExpression] = useState(initialValue || '');
|
|
6451
|
+
const [displayValue, setDisplayValue] = useState(initialValue || '');
|
|
6453
6452
|
const [error, setError] = useState('');
|
|
6453
|
+
const [isWaitingForOperand, setIsWaitingForOperand] = useState(false);
|
|
6454
6454
|
const [mounted, setMounted] = useState(false);
|
|
6455
6455
|
const inputRef = useRef(null);
|
|
6456
6456
|
// DOM要素の存在確認
|
|
@@ -6460,7 +6460,9 @@ const Calculator = ({ isOpen, onClose, onCalculate, initialValue = '', title, de
|
|
|
6460
6460
|
}, []);
|
|
6461
6461
|
useEffect(() => {
|
|
6462
6462
|
if (isOpen) {
|
|
6463
|
-
|
|
6463
|
+
setExpression(initialValue || '');
|
|
6464
|
+
setDisplayValue(initialValue || '');
|
|
6465
|
+
setIsWaitingForOperand(false);
|
|
6464
6466
|
setError('');
|
|
6465
6467
|
}
|
|
6466
6468
|
}, [isOpen, initialValue]);
|
|
@@ -6504,84 +6506,136 @@ const Calculator = ({ isOpen, onClose, onCalculate, initialValue = '', title, de
|
|
|
6504
6506
|
};
|
|
6505
6507
|
window.addEventListener('keydown', handleKeyDown);
|
|
6506
6508
|
return () => window.removeEventListener('keydown', handleKeyDown);
|
|
6507
|
-
}, [isOpen,
|
|
6509
|
+
}, [isOpen, expression, displayValue]);
|
|
6510
|
+
const handleDisplayChange = (newValue) => {
|
|
6511
|
+
setError('');
|
|
6512
|
+
setDisplayValue(newValue);
|
|
6513
|
+
if (isWaitingForOperand) {
|
|
6514
|
+
// 演算子入力後の直接入力の場合、式を更新しない
|
|
6515
|
+
// If an operator was just entered, and user types directly,
|
|
6516
|
+
// the new value is the start of the next operand.
|
|
6517
|
+
// Append it to the expression.
|
|
6518
|
+
setExpression(expression + newValue);
|
|
6519
|
+
setIsWaitingForOperand(false); // No longer waiting for operand
|
|
6520
|
+
}
|
|
6521
|
+
else {
|
|
6522
|
+
// 既存の式の最後の数値を置き換える
|
|
6523
|
+
const newExpression = expression.replace(/[\d.]+$/, newValue);
|
|
6524
|
+
setExpression(newExpression);
|
|
6525
|
+
}
|
|
6526
|
+
};
|
|
6508
6527
|
const handleButtonClick = (val) => {
|
|
6509
6528
|
setError('');
|
|
6510
6529
|
if (val === 'C') {
|
|
6511
|
-
|
|
6530
|
+
setExpression('');
|
|
6531
|
+
setDisplayValue('');
|
|
6532
|
+
setIsWaitingForOperand(false);
|
|
6512
6533
|
}
|
|
6513
6534
|
else if (val === '←') {
|
|
6514
|
-
|
|
6535
|
+
if (displayValue.length > 0) {
|
|
6536
|
+
const newDisplayValue = displayValue.slice(0, -1);
|
|
6537
|
+
setDisplayValue(newDisplayValue);
|
|
6538
|
+
setExpression((prev) => prev.slice(0, -1));
|
|
6539
|
+
}
|
|
6515
6540
|
}
|
|
6516
6541
|
else if (val === '.') {
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6542
|
+
if (!displayValue.includes('.')) {
|
|
6543
|
+
const newDisplayValue = displayValue ? displayValue + '.' : '0.';
|
|
6544
|
+
setDisplayValue(newDisplayValue);
|
|
6545
|
+
if (isWaitingForOperand) {
|
|
6546
|
+
setExpression(expression + newDisplayValue);
|
|
6547
|
+
setIsWaitingForOperand(false);
|
|
6548
|
+
}
|
|
6549
|
+
else {
|
|
6550
|
+
setExpression(expression + (displayValue ? '.' : '0.'));
|
|
6551
|
+
// Append decimal to the last number in the expression
|
|
6552
|
+
const lastNumberRegex = /([\d.]+)$/;
|
|
6553
|
+
setExpression(expression.replace(lastNumberRegex, (match) => match + '.'));
|
|
6554
|
+
}
|
|
6555
|
+
}
|
|
6525
6556
|
}
|
|
6526
6557
|
else if (['+', '-', '×', '÷'].includes(val)) {
|
|
6527
|
-
if (
|
|
6528
|
-
|
|
6529
|
-
|
|
6558
|
+
if (expression) {
|
|
6559
|
+
if (/[+\-×÷]$/.test(expression)) {
|
|
6560
|
+
setExpression(expression.slice(0, -1) + val);
|
|
6561
|
+
}
|
|
6562
|
+
else {
|
|
6563
|
+
const result = calculateExpression(expression);
|
|
6564
|
+
setExpression(result + val);
|
|
6565
|
+
setDisplayValue(result);
|
|
6566
|
+
}
|
|
6567
|
+
setIsWaitingForOperand(true);
|
|
6568
|
+
}
|
|
6530
6569
|
}
|
|
6531
6570
|
else {
|
|
6532
|
-
|
|
6571
|
+
if (isWaitingForOperand) {
|
|
6572
|
+
setDisplayValue(val);
|
|
6573
|
+
setExpression(expression + val);
|
|
6574
|
+
setIsWaitingForOperand(false);
|
|
6575
|
+
}
|
|
6576
|
+
else {
|
|
6577
|
+
const newDisplayValue = displayValue === '0' ? val : displayValue + val;
|
|
6578
|
+
setDisplayValue(newDisplayValue);
|
|
6579
|
+
setExpression(expression === '0' ? val : expression + val);
|
|
6580
|
+
}
|
|
6533
6581
|
}
|
|
6534
6582
|
};
|
|
6535
6583
|
const handleEqual = () => {
|
|
6536
|
-
if (!
|
|
6584
|
+
if (!expression)
|
|
6537
6585
|
return;
|
|
6538
|
-
const result = calculateExpression(
|
|
6539
|
-
|
|
6586
|
+
const result = calculateExpression(expression);
|
|
6587
|
+
setExpression(result);
|
|
6588
|
+
setDisplayValue(result);
|
|
6589
|
+
setIsWaitingForOperand(false);
|
|
6540
6590
|
};
|
|
6541
6591
|
const handleDecide = () => {
|
|
6542
|
-
if (!
|
|
6592
|
+
if (!expression) {
|
|
6543
6593
|
setError('金額を入力してください');
|
|
6544
6594
|
return;
|
|
6545
6595
|
}
|
|
6546
|
-
const result = calculateExpression(
|
|
6596
|
+
const result = calculateExpression(expression);
|
|
6547
6597
|
onCalculate(result);
|
|
6548
6598
|
onClose();
|
|
6549
6599
|
};
|
|
6550
6600
|
const handleTaxInclude = (rate) => {
|
|
6551
6601
|
if (!enableTaxCalculation)
|
|
6552
6602
|
return;
|
|
6553
|
-
if (!
|
|
6603
|
+
if (!expression) {
|
|
6554
6604
|
setError('金額を入力してください');
|
|
6555
6605
|
return;
|
|
6556
6606
|
}
|
|
6557
|
-
const currentValue = parseFloat(calculateExpression(
|
|
6607
|
+
const currentValue = parseFloat(calculateExpression(expression));
|
|
6558
6608
|
if (isNaN(currentValue)) {
|
|
6559
6609
|
setError('有効な金額を入力してください');
|
|
6560
6610
|
return;
|
|
6561
6611
|
}
|
|
6562
6612
|
const taxIncluded = currentValue * (1 + rate);
|
|
6563
|
-
|
|
6613
|
+
const result = normalizeNumberString(taxIncluded, decimalPlaces);
|
|
6614
|
+
setExpression(result);
|
|
6615
|
+
setDisplayValue(result);
|
|
6564
6616
|
setError('');
|
|
6565
6617
|
};
|
|
6566
6618
|
const handleTaxExclude = (rate) => {
|
|
6567
6619
|
if (!enableTaxCalculation)
|
|
6568
6620
|
return;
|
|
6569
|
-
if (!
|
|
6621
|
+
if (!expression) {
|
|
6570
6622
|
setError('金額を入力してください');
|
|
6571
6623
|
return;
|
|
6572
6624
|
}
|
|
6573
|
-
const currentValue = parseFloat(calculateExpression(
|
|
6625
|
+
const currentValue = parseFloat(calculateExpression(expression));
|
|
6574
6626
|
if (isNaN(currentValue)) {
|
|
6575
6627
|
setError('有効な金額を入力してください');
|
|
6576
6628
|
return;
|
|
6577
6629
|
}
|
|
6578
6630
|
const taxExcluded = currentValue / (1 + rate);
|
|
6579
|
-
|
|
6631
|
+
const result = normalizeNumberString(taxExcluded, decimalPlaces);
|
|
6632
|
+
setExpression(result);
|
|
6633
|
+
setDisplayValue(result);
|
|
6580
6634
|
setError('');
|
|
6581
6635
|
};
|
|
6582
6636
|
if (!isOpen || !mounted)
|
|
6583
6637
|
return null;
|
|
6584
|
-
const modal = (jsx("div", { className: "calculator-overlay", children: jsxs("div", { className: "calculator-modal", children: [title || description ? (jsxs("div", { className: "calculator-header", children: [jsxs("div", { children: [title && jsx("h2", { className: "calculator-title", children: title }), description && jsx("p", { className: "calculator-subtitle", children: description })] }), jsx("button", { onClick: onClose, className: "calculator-close-button", "aria-label": "\u9589\u3058\u308B", children: jsx(Icon, { icon: faTimes, className: "w-5 h-5" }) })] })) : (jsxs("div", { className: "calculator-header", children: [jsx("div", {}), jsx("button", { onClick: onClose, className: "calculator-close-button", "aria-label": "\u9589\u3058\u308B", children: jsx(Icon, { icon: faTimes, className: "w-5 h-5" }) })] })), jsx("div", { className: "calculator-display", children: jsx(CalculatorDisplay, { value:
|
|
6638
|
+
const modal = (jsx("div", { className: "calculator-overlay", children: jsxs("div", { className: "calculator-modal", children: [title || description ? (jsxs("div", { className: "calculator-header", children: [jsxs("div", { children: [title && jsx("h2", { className: "calculator-title", children: title }), description && jsx("p", { className: "calculator-subtitle", children: description })] }), jsx("button", { onClick: onClose, className: "calculator-close-button", "aria-label": "\u9589\u3058\u308B", children: jsx(Icon, { icon: faTimes, className: "w-5 h-5" }) })] })) : (jsxs("div", { className: "calculator-header", children: [jsx("div", {}), jsx("button", { onClick: onClose, className: "calculator-close-button", "aria-label": "\u9589\u3058\u308B", children: jsx(Icon, { icon: faTimes, className: "w-5 h-5" }) })] })), jsx("div", { className: "calculator-display", children: jsx(CalculatorDisplay, { value: displayValue, error: error, inputRef: inputRef, editable: true, onChange: handleDisplayChange, numberFormatOptions: numberFormatOptions, placeholder: placeholder }) }), jsx(CalculatorKeypad, { onButtonClick: handleButtonClick, onEqual: handleEqual, onDecide: handleDecide, onTaxInclude: handleTaxInclude, onTaxExclude: handleTaxExclude, enableTaxCalculation: enableTaxCalculation, decimalPlaces: decimalPlaces })] }) }));
|
|
6585
6639
|
// DOM要素の存在確認を行ってからポータルを作成
|
|
6586
6640
|
if (typeof document !== 'undefined' && document.body) {
|
|
6587
6641
|
return createPortal(modal, document.body);
|