@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/dist/index.js CHANGED
@@ -6381,7 +6381,7 @@ function NumericFormat(props) {
6381
6381
  }
6382
6382
 
6383
6383
  const CalculatorDisplay = ({ value, error, inputRef, editable, placeholder, onChange, numberFormatOptions = {} }) => {
6384
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [editable ? (jsxRuntime.jsx(NumericFormat, { className: "calculator-display-input", value: value, onValueChange: (vals) => onChange?.(vals.value), placeholder: placeholder ?? '数値を入力', inputMode: "decimal", ...numberFormatOptions })) : (jsxRuntime.jsx("div", { className: "calculator-display-input", children: jsxRuntime.jsx("div", { ref: inputRef, style: { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, children: value ? (jsxRuntime.jsx(NumericFormat, { value: value, displayType: "text", ...numberFormatOptions })) : '' }) })), error && jsxRuntime.jsx("div", { className: "calculator-error", children: error })] }));
6384
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [editable ? (jsxRuntime.jsx(NumericFormat, { className: "calculator-display-input", value: value, onValueChange: (vals) => onChange?.(vals.value), placeholder: placeholder ?? '数値を入力', inputMode: "decimal", ...numberFormatOptions })) : (jsxRuntime.jsx("div", { className: "calculator-display-input", children: jsxRuntime.jsx("div", { ref: inputRef, style: { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, children: value ? (jsxRuntime.jsx(NumericFormat, { value: value, displayType: "text", ...numberFormatOptions })) : (jsxRuntime.jsx("span", { className: "text-gray-400", children: placeholder ?? '0' })) }) })), error && jsxRuntime.jsx("div", { className: "calculator-error", children: error })] }));
6385
6385
  };
6386
6386
 
6387
6387
  const CalculatorKeypad = ({ onButtonClick, onEqual, onDecide, onTaxInclude, onTaxExclude, enableTaxCalculation = true, decimalPlaces = 6 }) => {
@@ -6448,11 +6448,11 @@ function calculateExpression(expr) {
6448
6448
  return '0';
6449
6449
  }
6450
6450
  }
6451
- const Calculator = ({ isOpen, onClose, onCalculate, initialValue = '', title, description, enableTaxCalculation = false, decimalPlaces = 6, numberFormatOptions = {},
6452
- // placeholder removed - not used
6453
- }) => {
6454
- const [input, setInput] = React.useState(initialValue || '');
6451
+ const Calculator = ({ isOpen, onClose, onCalculate, initialValue = '', title, description, enableTaxCalculation = false, decimalPlaces = 6, numberFormatOptions = {}, placeholder, }) => {
6452
+ const [expression, setExpression] = React.useState(initialValue || '');
6453
+ const [displayValue, setDisplayValue] = React.useState(initialValue || '');
6455
6454
  const [error, setError] = React.useState('');
6455
+ const [isWaitingForOperand, setIsWaitingForOperand] = React.useState(false);
6456
6456
  const [mounted, setMounted] = React.useState(false);
6457
6457
  const inputRef = React.useRef(null);
6458
6458
  // DOM要素の存在確認
@@ -6462,7 +6462,9 @@ const Calculator = ({ isOpen, onClose, onCalculate, initialValue = '', title, de
6462
6462
  }, []);
6463
6463
  React.useEffect(() => {
6464
6464
  if (isOpen) {
6465
- setInput(initialValue || '');
6465
+ setExpression(initialValue || '');
6466
+ setDisplayValue(initialValue || '');
6467
+ setIsWaitingForOperand(false);
6466
6468
  setError('');
6467
6469
  }
6468
6470
  }, [isOpen, initialValue]);
@@ -6506,84 +6508,136 @@ const Calculator = ({ isOpen, onClose, onCalculate, initialValue = '', title, de
6506
6508
  };
6507
6509
  window.addEventListener('keydown', handleKeyDown);
6508
6510
  return () => window.removeEventListener('keydown', handleKeyDown);
6509
- }, [isOpen, input]);
6511
+ }, [isOpen, expression, displayValue]);
6512
+ const handleDisplayChange = (newValue) => {
6513
+ setError('');
6514
+ setDisplayValue(newValue);
6515
+ if (isWaitingForOperand) {
6516
+ // 演算子入力後の直接入力の場合、式を更新しない
6517
+ // If an operator was just entered, and user types directly,
6518
+ // the new value is the start of the next operand.
6519
+ // Append it to the expression.
6520
+ setExpression(expression + newValue);
6521
+ setIsWaitingForOperand(false); // No longer waiting for operand
6522
+ }
6523
+ else {
6524
+ // 既存の式の最後の数値を置き換える
6525
+ const newExpression = expression.replace(/[\d.]+$/, newValue);
6526
+ setExpression(newExpression);
6527
+ }
6528
+ };
6510
6529
  const handleButtonClick = (val) => {
6511
6530
  setError('');
6512
6531
  if (val === 'C') {
6513
- setInput('');
6532
+ setExpression('');
6533
+ setDisplayValue('');
6534
+ setIsWaitingForOperand(false);
6514
6535
  }
6515
6536
  else if (val === '←') {
6516
- setInput((prev) => prev.slice(0, -1));
6537
+ if (displayValue.length > 0) {
6538
+ const newDisplayValue = displayValue.slice(0, -1);
6539
+ setDisplayValue(newDisplayValue);
6540
+ setExpression((prev) => prev.slice(0, -1));
6541
+ }
6517
6542
  }
6518
6543
  else if (val === '.') {
6519
- setInput((prev) => {
6520
- if (!prev)
6521
- return '0.';
6522
- const lastSegment = prev.split(/[+\-×÷]/).pop() || '';
6523
- if (lastSegment.includes('.'))
6524
- return prev;
6525
- return prev + '.';
6526
- });
6544
+ if (!displayValue.includes('.')) {
6545
+ const newDisplayValue = displayValue ? displayValue + '.' : '0.';
6546
+ setDisplayValue(newDisplayValue);
6547
+ if (isWaitingForOperand) {
6548
+ setExpression(expression + newDisplayValue);
6549
+ setIsWaitingForOperand(false);
6550
+ }
6551
+ else {
6552
+ setExpression(expression + (displayValue ? '.' : '0.'));
6553
+ // Append decimal to the last number in the expression
6554
+ const lastNumberRegex = /([\d.]+)$/;
6555
+ setExpression(expression.replace(lastNumberRegex, (match) => match + '.'));
6556
+ }
6557
+ }
6527
6558
  }
6528
6559
  else if (['+', '-', '×', '÷'].includes(val)) {
6529
- if (!input || /[+\-×÷]$/.test(input))
6530
- return;
6531
- setInput((prev) => prev + val);
6560
+ if (expression) {
6561
+ if (/[+\-×÷]$/.test(expression)) {
6562
+ setExpression(expression.slice(0, -1) + val);
6563
+ }
6564
+ else {
6565
+ const result = calculateExpression(expression);
6566
+ setExpression(result + val);
6567
+ setDisplayValue(result);
6568
+ }
6569
+ setIsWaitingForOperand(true);
6570
+ }
6532
6571
  }
6533
6572
  else {
6534
- setInput((prev) => (prev === '0' ? val : prev + val));
6573
+ if (isWaitingForOperand) {
6574
+ setDisplayValue(val);
6575
+ setExpression(expression + val);
6576
+ setIsWaitingForOperand(false);
6577
+ }
6578
+ else {
6579
+ const newDisplayValue = displayValue === '0' ? val : displayValue + val;
6580
+ setDisplayValue(newDisplayValue);
6581
+ setExpression(expression === '0' ? val : expression + val);
6582
+ }
6535
6583
  }
6536
6584
  };
6537
6585
  const handleEqual = () => {
6538
- if (!input)
6586
+ if (!expression)
6539
6587
  return;
6540
- const result = calculateExpression(input);
6541
- setInput(result);
6588
+ const result = calculateExpression(expression);
6589
+ setExpression(result);
6590
+ setDisplayValue(result);
6591
+ setIsWaitingForOperand(false);
6542
6592
  };
6543
6593
  const handleDecide = () => {
6544
- if (!input) {
6594
+ if (!expression) {
6545
6595
  setError('金額を入力してください');
6546
6596
  return;
6547
6597
  }
6548
- const result = calculateExpression(input);
6598
+ const result = calculateExpression(expression);
6549
6599
  onCalculate(result);
6550
6600
  onClose();
6551
6601
  };
6552
6602
  const handleTaxInclude = (rate) => {
6553
6603
  if (!enableTaxCalculation)
6554
6604
  return;
6555
- if (!input) {
6605
+ if (!expression) {
6556
6606
  setError('金額を入力してください');
6557
6607
  return;
6558
6608
  }
6559
- const currentValue = parseFloat(calculateExpression(input));
6609
+ const currentValue = parseFloat(calculateExpression(expression));
6560
6610
  if (isNaN(currentValue)) {
6561
6611
  setError('有効な金額を入力してください');
6562
6612
  return;
6563
6613
  }
6564
6614
  const taxIncluded = currentValue * (1 + rate);
6565
- setInput(normalizeNumberString(taxIncluded, decimalPlaces));
6615
+ const result = normalizeNumberString(taxIncluded, decimalPlaces);
6616
+ setExpression(result);
6617
+ setDisplayValue(result);
6566
6618
  setError('');
6567
6619
  };
6568
6620
  const handleTaxExclude = (rate) => {
6569
6621
  if (!enableTaxCalculation)
6570
6622
  return;
6571
- if (!input) {
6623
+ if (!expression) {
6572
6624
  setError('金額を入力してください');
6573
6625
  return;
6574
6626
  }
6575
- const currentValue = parseFloat(calculateExpression(input));
6627
+ const currentValue = parseFloat(calculateExpression(expression));
6576
6628
  if (isNaN(currentValue)) {
6577
6629
  setError('有効な金額を入力してください');
6578
6630
  return;
6579
6631
  }
6580
6632
  const taxExcluded = currentValue / (1 + rate);
6581
- setInput(normalizeNumberString(taxExcluded, decimalPlaces));
6633
+ const result = normalizeNumberString(taxExcluded, decimalPlaces);
6634
+ setExpression(result);
6635
+ setDisplayValue(result);
6582
6636
  setError('');
6583
6637
  };
6584
6638
  if (!isOpen || !mounted)
6585
6639
  return null;
6586
- const modal = (jsxRuntime.jsx("div", { className: "calculator-overlay", children: jsxRuntime.jsxs("div", { className: "calculator-modal", children: [title || description ? (jsxRuntime.jsxs("div", { className: "calculator-header", children: [jsxRuntime.jsxs("div", { children: [title && jsxRuntime.jsx("h2", { className: "calculator-title", children: title }), description && jsxRuntime.jsx("p", { className: "calculator-subtitle", children: description })] }), jsxRuntime.jsx("button", { onClick: onClose, className: "calculator-close-button", "aria-label": "\u9589\u3058\u308B", children: jsxRuntime.jsx(Icon, { icon: faTimes, className: "w-5 h-5" }) })] })) : (jsxRuntime.jsxs("div", { className: "calculator-header", children: [jsxRuntime.jsx("div", {}), jsxRuntime.jsx("button", { onClick: onClose, className: "calculator-close-button", "aria-label": "\u9589\u3058\u308B", children: jsxRuntime.jsx(Icon, { icon: faTimes, className: "w-5 h-5" }) })] })), jsxRuntime.jsx("div", { className: "calculator-display", children: jsxRuntime.jsx(CalculatorDisplay, { value: input, error: error, inputRef: inputRef, editable: true, onChange: (v) => setInput(v), numberFormatOptions: numberFormatOptions }) }), jsxRuntime.jsx(CalculatorKeypad, { onButtonClick: handleButtonClick, onEqual: handleEqual, onDecide: handleDecide, onTaxInclude: handleTaxInclude, onTaxExclude: handleTaxExclude, enableTaxCalculation: enableTaxCalculation, decimalPlaces: decimalPlaces })] }) }));
6640
+ const modal = (jsxRuntime.jsx("div", { className: "calculator-overlay", children: jsxRuntime.jsxs("div", { className: "calculator-modal", children: [title || description ? (jsxRuntime.jsxs("div", { className: "calculator-header", children: [jsxRuntime.jsxs("div", { children: [title && jsxRuntime.jsx("h2", { className: "calculator-title", children: title }), description && jsxRuntime.jsx("p", { className: "calculator-subtitle", children: description })] }), jsxRuntime.jsx("button", { onClick: onClose, className: "calculator-close-button", "aria-label": "\u9589\u3058\u308B", children: jsxRuntime.jsx(Icon, { icon: faTimes, className: "w-5 h-5" }) })] })) : (jsxRuntime.jsxs("div", { className: "calculator-header", children: [jsxRuntime.jsx("div", {}), jsxRuntime.jsx("button", { onClick: onClose, className: "calculator-close-button", "aria-label": "\u9589\u3058\u308B", children: jsxRuntime.jsx(Icon, { icon: faTimes, className: "w-5 h-5" }) })] })), jsxRuntime.jsx("div", { className: "calculator-display", children: jsxRuntime.jsx(CalculatorDisplay, { value: displayValue, error: error, inputRef: inputRef, editable: true, onChange: handleDisplayChange, numberFormatOptions: numberFormatOptions, placeholder: placeholder }) }), jsxRuntime.jsx(CalculatorKeypad, { onButtonClick: handleButtonClick, onEqual: handleEqual, onDecide: handleDecide, onTaxInclude: handleTaxInclude, onTaxExclude: handleTaxExclude, enableTaxCalculation: enableTaxCalculation, decimalPlaces: decimalPlaces })] }) }));
6587
6641
  // DOM要素の存在確認を行ってからポータルを作成
6588
6642
  if (typeof document !== 'undefined' && document.body) {
6589
6643
  return reactDom.createPortal(modal, document.body);