diff --git a/samples/calc-02/index.css b/samples/calc-02/index.css
index cf2038f..6993ee7 100644
--- a/samples/calc-02/index.css
+++ b/samples/calc-02/index.css
@@ -137,6 +137,10 @@ body {
justify-self: end;
font-size: 20px;
}
+
+.stack-value.is-editing {
+ letter-spacing: 0.02em;
+}
.display-buttons-panel {
padding: 8px;
min-height: 0;
@@ -379,6 +383,37 @@ button:active {
outline-offset: 2px;
}
+.stack-value.is-editing {
+ display: inline-flex;
+ align-items: center;
+ justify-content: flex-end;
+ gap: 0;
+}
+
+.edit-text {
+ display: inline-block;
+ white-space: pre;
+}
+
+.edit-caret {
+ display: inline-block;
+ width: 1px;
+ height: 1em;
+ margin: 0 0.12ch;
+ background: currentColor;
+ animation: caret-blink 1s steps(1, end) infinite;
+ transform: translateY(0.02em);
+}
+
+@keyframes caret-blink {
+ 0%, 49% {
+ opacity: 1;
+ }
+ 50%, 100% {
+ opacity: 0.15;
+ }
+}
+
.key-default {
background: linear-gradient(180deg, var(--btnTop), var(--btnBottom));
color: #eef2f7;
diff --git a/samples/calc-02/index.js b/samples/calc-02/index.js
index 06ae7ab..517fed7 100644
--- a/samples/calc-02/index.js
+++ b/samples/calc-02/index.js
@@ -94,6 +94,7 @@ function focusInput() {
}
let statusTimer = null;
+let editCursor = 0;
function setStatus(message, isError = false, timeoutMs = 1400) {
if (!statusLine) return;
@@ -128,9 +129,14 @@ function getStackLine(indexFromTop) {
return indexFromTop >= 0 && indexFromTop < calc.stack.length ? calc.stack[indexFromTop] : '';
}
+function getEditingDisplayValue() {
+ if (!calc.isEditing) return '';
+ return calc.inputValue;
+}
+
function getStackDisplayValue(label) {
if (label === 'X') {
- return calc.isEditing ? calc.inputValue : (calc.formatNumber(getStackLine(0)) || '');
+ return calc.isEditing ? getEditingDisplayValue() : (calc.formatNumber(getStackLine(0)) || '');
}
if (label === 'Y') {
return calc.isEditing ? (calc.formatNumber(getStackLine(0)) || '') : (calc.formatNumber(getStackLine(1)) || '');
@@ -157,14 +163,30 @@ function render() {
const isPortrait = window.matchMedia('(orientation: portrait)').matches || window.innerWidth <= 860;
calculatorEl?.classList.toggle('portrait', isPortrait);
calculatorEl?.classList.toggle('landscape', !isPortrait);
- stackEls.X.textContent = getStackDisplayValue('X');
+ const xValue = getStackDisplayValue('X');
+ if (calc.isEditing) {
+ const cursor = Math.max(0, Math.min(editCursor, calc.inputValue.length));
+ stackEls.X.innerHTML = `${calc.inputValue.slice(0, cursor)}${calc.inputValue.slice(cursor)}`;
+ } else {
+ stackEls.X.textContent = xValue;
+ }
stackEls.Y.textContent = getStackDisplayValue('Y');
stackEls.Z.textContent = getStackDisplayValue('Z');
stackEls.T.textContent = getStackDisplayValue('T');
+ stackEls.X.classList.toggle('is-editing', calc.isEditing);
+ stackEls.X.classList.toggle('is-caret-visible', calc.isEditing);
updateCopyButtons();
modeButton.textContent = calc.angleMode;
}
+function stopEditing(clearValue = false) {
+ if (clearValue) {
+ calc.inputValue = '';
+ }
+ calc.isEditing = false;
+ editCursor = 0;
+}
+
function pushEditingValueIfNeeded() {
if (!calc.isEditing) return;
if (calc.inputValue !== '') {
@@ -172,20 +194,27 @@ function pushEditingValueIfNeeded() {
}
calc.inputValue = '';
calc.isEditing = false;
+ editCursor = 0;
}
function inputToX(value) {
if (!calc.isEditing) {
calc.isEditing = true;
calc.inputValue = '';
+ editCursor = 0;
}
if (value === 'Backspace') {
- calc.inputValue = calc.inputValue.slice(0, -1);
+ if (editCursor > 0) {
+ calc.inputValue = `${calc.inputValue.slice(0, editCursor - 1)}${calc.inputValue.slice(editCursor)}`;
+ editCursor -= 1;
+ }
} else {
- calc.inputValue += value;
+ calc.inputValue = `${calc.inputValue.slice(0, editCursor)}${value}${calc.inputValue.slice(editCursor)}`;
+ editCursor += value.length;
}
if (calc.inputValue === '') {
calc.isEditing = false;
+ editCursor = 0;
}
}
@@ -217,11 +246,9 @@ function execute(name) {
}
} else if (name === 'clear') {
calc.clear();
- calc.inputValue = '';
- calc.isEditing = false;
+ stopEditing(true);
} else if (name === 'escape') {
- calc.inputValue = '';
- calc.isEditing = false;
+ stopEditing(true);
} else if (name === 'backspace') {
if (calc.isEditing) {
inputToX('Backspace');
@@ -235,7 +262,11 @@ function execute(name) {
}
} else if (name === 'neg') {
if (calc.isEditing) {
- calc.inputValue = calc.inputValue.startsWith('-') ? calc.inputValue.slice(1) : `-${calc.inputValue}`;
+ const hasSign = calc.inputValue.startsWith('-');
+ calc.inputValue = hasSign ? calc.inputValue.slice(1) : `-${calc.inputValue}`;
+ if (!hasSign) editCursor += 1;
+ if (hasSign) editCursor = Math.max(0, editCursor - 1);
+ editCursor = Math.max(0, Math.min(editCursor, calc.inputValue.length));
} else {
calc.command('neg');
}
@@ -243,6 +274,9 @@ function execute(name) {
pushEditingValueIfNeeded();
calc.command(name);
}
+ if (!calc.isEditing) {
+ editCursor = 0;
+ }
render();
} catch (error) {
setStatus(error?.message || 'Operation error', true);
@@ -306,15 +340,34 @@ function handleKeyboard(event) {
'%': 'mod',
'^': 'pow',
};
- const arrowMap = {
- ArrowUp: upButton,
- ArrowDown: downButton,
- ArrowLeft: leftButton,
- ArrowRight: rightButton,
- };
- if (arrowMap[key]) {
+ if (key === 'ArrowLeft') {
event.preventDefault();
- arrowMap[key].click();
+ if (calc.isEditing) {
+ editCursor = Math.max(0, editCursor - 1);
+ render();
+ return;
+ }
+ leftButton.click();
+ return;
+ }
+ if (key === 'ArrowRight') {
+ event.preventDefault();
+ if (calc.isEditing) {
+ editCursor = Math.min(calc.inputValue.length, editCursor + 1);
+ render();
+ return;
+ }
+ rightButton.click();
+ return;
+ }
+ if (key === 'ArrowUp') {
+ event.preventDefault();
+ upButton.click();
+ return;
+ }
+ if (key === 'ArrowDown') {
+ event.preventDefault();
+ downButton.click();
return;
}
if (map[key]) {
@@ -459,7 +512,13 @@ constButton.addEventListener('click', (event) => {
toggleConstMenu();
});
-leftButton.addEventListener('click', () => {});
+leftButton.addEventListener('click', () => {
+ if (calc.isEditing) {
+ editCursor = Math.max(0, editCursor - 1);
+ render();
+ focusInput();
+ }
+});
downButton.addEventListener('click', () => {
if (!calc.isEditing && calc.isValidIndex(0)) {
const value = calc.stack[0];
@@ -472,6 +531,12 @@ downButton.addEventListener('click', () => {
});
rightButton.addEventListener('click', () => {
+ if (calc.isEditing) {
+ editCursor = Math.min(calc.inputValue.length, editCursor + 1);
+ render();
+ focusInput();
+ return;
+ }
execute('swap');
});