Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 126 additions & 8 deletions src/assets/Styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ header {
max-width: 100%;
width: 538px;
margin-bottom: 5rem;
position: relative;
}
.calculator * {
transition: background-color 1s, color 0.1s ease-in-out;
Expand All @@ -100,27 +101,40 @@ header {
font-size: 32px;
font-weight: 700;
cursor: pointer;
font-family: var(--main-font);
}

.screen {
font-size: 3.3rem;
padding: 2.5rem 1.9rem;
padding: 1.5rem 1.9rem;
text-align: right;
height: 2.9rem;
min-height: 4.5rem;
letter-spacing: 1px;
border-radius: 5px;
margin-top: 0.9rem;
text-align: right;
display: flex;
flex-direction: row;
flex-direction: column;
justify-content: flex-end;
align-items: flex-end;
gap: 0.3rem;
}

.screen-expression {
font-size: 1.2rem;
opacity: 0.7;
word-break: break-all;
font-family: var(--main-font);
}

.screen-result {
font-size: 3.3rem;
font-family: var(--main-font);
}

.keypad {
display: grid;
row-gap: 1.9rem;
column-gap: 1.3rem;
grid-template: repeat(5, 1fr) / repeat(4, 1fr);
grid-template: repeat(6, 1fr) / repeat(4, 1fr);
grid-auto-flow: row;
padding: 2rem;
border-radius: 5px;
Expand All @@ -137,14 +151,116 @@ header {
}
#del,
#reset-key,
#equals-key {
#equals-key,
.history-toggle {
font-size: 1.5rem;
}
#reset-key {
grid-column: 1 / span 2;
}
.history-toggle {
grid-column: 3 / span 1;
}
#equals-key {
grid-column: 3 / span 2;
grid-column: 4 / span 1;
}

/* HISTORY PANEL */
.history-panel {
margin-top: 1.6rem;
padding: 1.5rem;
max-height: 400px;
overflow-y: auto;
animation: slideDown 0.3s ease-out;
}

@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}

.history-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
padding-bottom: 1rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}

.history-header h3 {
font-size: 1.2rem;
font-family: var(--main-font);
}

.clear-history-btn {
font-size: 0.85rem !important;
padding: 0.4rem 1rem;
border-radius: 5px;
font-family: var(--main-font);
}

.history-list {
display: flex;
flex-direction: column;
gap: 0.8rem;
}

.history-item {
padding: 1rem 1.2rem;
cursor: pointer;
transition: all 0.2s ease;
display: flex;
flex-direction: column;
gap: 0.3rem;
}

.history-item:hover {
transform: translateX(5px);
}

.history-expression {
font-size: 0.95rem;
word-break: break-all;
font-family: var(--main-font);
}

.history-result {
font-size: 1.4rem;
font-weight: 700;
font-family: var(--main-font);
}

.empty-history {
text-align: center;
padding: 2rem;
font-size: 1rem;
font-family: var(--main-font);
}

/* Scrollbar styling for history panel */
.history-panel::-webkit-scrollbar {
width: 6px;
}

.history-panel::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.1);
border-radius: 3px;
}

.history-panel::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.2);
border-radius: 3px;
}

.history-panel::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.3);
}

@media screen and (max-width: 600px) {
Expand All @@ -156,5 +272,7 @@ header {
@media screen and (max-width: 440px) {
.keypad {
padding: 1rem;
row-gap: 1rem;
column-gap: 0.8rem;
}
}
103 changes: 87 additions & 16 deletions src/components/calculator/Calculator.jsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,49 @@
import { useState } from "react";
import { useState, useEffect, useCallback } from "react";
import React from "react";
import { useEffect } from "react";
import Screen from "@/components/calculator/Screen";
import Keypad from "@/components/calculator/Keypad";
import History from "@/components/calculator/History";

export default function Calculator() {
const [calc, setCalc] = useState("");
const [calculated, setCalculated] = useState(false);
const [history, setHistory] = useState([]);
const [showHistory, setShowHistory] = useState(false);
const [currentExpression, setCurrentExpression] = useState("");
const operations = ["/", "*", "+", "-", "."];
const numbers = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"];
const ref = React.createRef();

// 从 localStorage 加载历史记录
useEffect(() => {
const savedHistory = localStorage.getItem("calculator-history");
if (savedHistory) {
try {
setHistory(JSON.parse(savedHistory));
} catch (e) {
console.error("Failed to parse history:", e);
}
}
}, []);

// 保存历史记录到 localStorage
useEffect(() => {
localStorage.setItem("calculator-history", JSON.stringify(history));
}, [history]);

const addToHistory = useCallback((expression, result) => {
const newHistoryItem = {
expression: expression,
result: result,
timestamp: new Date().toISOString(),
};
setHistory((prevHistory) => [newHistoryItem, ...prevHistory].slice(0, 50)); // 最多保留50条记录
}, []);

const updateCalc = (value) => {
// 当开始新的输入时,清除之前的表达式显示
setCurrentExpression("");

const displayValue = ref.current;
//************* BEFORE CALCULATION *************//
if (value === "0" && calc === "0") {
Expand All @@ -21,20 +53,14 @@ export default function Calculator() {
(operations.includes(value) && operations.includes(calc.slice(-1)))
) {
return;
} else if (!operations.includes(value)) {
setCalc((+calc + +value).toString());

} else if (!operations.includes(value) && !calculated) {
if (calc.length > 7) {
displayValue.style.fontSize = "2.5rem";
}
if (calc.length === 11 && !operations.includes(value)) {
displayValue.style.width = "fit-content";
displayValue.style.overflow = "hidden";
return setCalc(calc.toString());
} else if (calc.length === 11 && operations.includes(value)) {
console.log("hi");
} else {
console.error();
}
}

Expand All @@ -50,59 +76,104 @@ export default function Calculator() {
} else if (calculated === true && value === "0") {
setCalculated(false);
return setCalc("");
} else {
console.error();
}
setCalc(calc + value);
};

const calculate = () => {
if (eval(calc) === undefined) {
if (!calc || calc === "") {
return;
}
setCalculated(true);
setCalc(eval(calc).toString());
try {
const result = eval(calc).toString();
setCalculated(true);
addToHistory(calc, result);
// 计算完成后,设置表达式以便显示
setCurrentExpression(calc);
setCalc(result);
} catch (e) {
console.error("Calculation error:", e);
}
};

const clear = () => {
setCalculated(false);
setCalc("");
setCurrentExpression(""); // 清除表达式显示
const displayValue = ref.current;
if (displayValue) {
displayValue.style.fontSize = "3.3rem";
displayValue.style.width = "auto";
displayValue.style.overflow = "visible";
}
};

const del = () => {
if (calc === "") {
return;
}
setCurrentExpression(""); // 删除时清除表达式显示
const displayValue = ref.current;
if (calc.length < 7) {
if (calc.length < 8) {
displayValue.style.fontSize = "3.3rem";
}

const value = calc.slice(0, -1);
setCalc(value);
};

// 处理历史记录点击 - 同时显示表达式和结果
const handleHistoryClick = (item) => {
setCurrentExpression(item.expression);
setCalc(item.result);
setCalculated(true);
};

// 清除历史记录
const handleClearHistory = () => {
setHistory([]);
localStorage.removeItem("calculator-history");
};

// 切换历史记录显示
const toggleHistory = () => {
setShowHistory(!showHistory);
};

const onKeyDown = (e) => {
if (operations.includes(e.key) || numbers.includes(e.key)) {
updateCalc(e.key);
} else if (e.key === "Enter" || e.key === "=") {
calculate();
} else if (e.key === "Backspace") del();
else if (e.key === "Escape") clear();
};

useEffect(() => {
window.addEventListener("keydown", onKeyDown);
return () => {
window.removeEventListener("keydown", onKeyDown);
};
});

return (
<main>
<article className="calculator">
<Screen ref={ref} calc={calc} />
<Screen ref={ref} calc={calc} expression={currentExpression} />
<Keypad
updateCalc={updateCalc}
calculate={calculate}
del={del}
clear={clear}
onKeyDown={onKeyDown}
toggleHistory={toggleHistory}
showHistory={showHistory}
/>
<History
history={history}
onHistoryClick={handleHistoryClick}
onClearHistory={handleClearHistory}
isVisible={showHistory}
/>
</article>
</main>
Expand Down
Loading