{"project":{"id":"uDF7Y9D","userId":"davidyarham@gmail.com","username":null,"userPicture":null,"name":"iOS Calculator","visible":true,"contributors":"","githubRepo":null,"forkedFrom":null,"isTemplate":false,"tags":"","files":{"folder":"","files":[{"name":"index.html","content":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Untitled</title>\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n<body>\n<div class=\"calc\">\n\t<div class=\"calc__display\">\n\t\t<span class=\"calc__display-text\" id=\"display\">0</span>\n\t</div>\n\t<div class=\"calc__grid\">\n\t\t<button class=\"calc__btn calc__btn--util\" data-action=\"clear\">AC</button>\n\t\t<button class=\"calc__btn calc__btn--util\" data-action=\"toggle\">+/−</button>\n\t\t<button class=\"calc__btn calc__btn--util\" data-action=\"percent\">%</button>\n\t\t<button class=\"calc__btn calc__btn--op\" data-action=\"op\" data-op=\"/\">÷</button>\n\n\t\t<button class=\"calc__btn calc__btn--num\" data-action=\"num\" data-val=\"7\">7</button>\n\t\t<button class=\"calc__btn calc__btn--num\" data-action=\"num\" data-val=\"8\">8</button>\n\t\t<button class=\"calc__btn calc__btn--num\" data-action=\"num\" data-val=\"9\">9</button>\n\t\t<button class=\"calc__btn calc__btn--op\" data-action=\"op\" data-op=\"*\">×</button>\n\n\t\t<button class=\"calc__btn calc__btn--num\" data-action=\"num\" data-val=\"4\">4</button>\n\t\t<button class=\"calc__btn calc__btn--num\" data-action=\"num\" data-val=\"5\">5</button>\n\t\t<button class=\"calc__btn calc__btn--num\" data-action=\"num\" data-val=\"6\">6</button>\n\t\t<button class=\"calc__btn calc__btn--op\" data-action=\"op\" data-op=\"-\">−</button>\n\n\t\t<button class=\"calc__btn calc__btn--num\" data-action=\"num\" data-val=\"1\">1</button>\n\t\t<button class=\"calc__btn calc__btn--num\" data-action=\"num\" data-val=\"2\">2</button>\n\t\t<button class=\"calc__btn calc__btn--num\" data-action=\"num\" data-val=\"3\">3</button>\n\t\t<button class=\"calc__btn calc__btn--op\" data-action=\"op\" data-op=\"+\">+</button>\n\n\t\t<button class=\"calc__btn calc__btn--num calc__btn--zero\" data-action=\"num\" data-val=\"0\">0</button>\n\t\t<button class=\"calc__btn calc__btn--num\" data-action=\"decimal\">.</button>\n\t\t<button class=\"calc__btn calc__btn--op\" data-action=\"equals\">=</button>\n\t</div>\n</div>\n  <script type=\"module\" src=\"main.js\"></script>\n</body>\n</html>"},{"name":"main.js","content":"(function() {\n\tconst display = document.getElementById('display');\n\tconst grid = document.querySelector('.calc__grid');\n\tconst clearBtn = grid.querySelector('[data-action=\"clear\"]');\n\n\tlet currentVal = '0';\n\tlet previousVal = '';\n\tlet operator = null;\n\tlet shouldResetDisplay = false;\n\tlet lastEquals = false;\n\tlet lastOp = null;\n\tlet lastNum = null;\n\n\tfunction formatDisplay(val) {\n\t\tif (val === 'Error') return 'Error';\n\t\tconst num = parseFloat(val);\n\t\tif (isNaN(num)) return '0';\n\n\t\t// If it's being typed and has a trailing decimal or trailing zeros after decimal\n\t\tif (val.includes('.') && (val.endsWith('.') || /\\.\\d*0+$/.test(val))) {\n\t\t\tconst parts = val.split('.');\n\t\t\tconst intPart = parseInt(parts[0]);\n\t\t\tconst formatted = Math.abs(intPart) >= 1000 ?\n\t\t\t\tintPart.toLocaleString('en-US') :\n\t\t\t\tintPart.toString();\n\t\t\treturn (num < 0 && intPart === 0 ? '-' : '') + formatted + '.' + parts[1];\n\t\t}\n\n\t\tif (Number.isInteger(num) && Math.abs(num) < 1e16) {\n\t\t\treturn num.toLocaleString('en-US');\n\t\t}\n\n\t\tif (Math.abs(num) < 1e-7 && num !== 0) {\n\t\t\treturn num.toExponential(5);\n\t\t}\n\n\t\tif (Math.abs(num) >= 1e16) {\n\t\t\treturn num.toExponential(5);\n\t\t}\n\n\t\tconst str = num.toLocaleString('en-US', {\n\t\t\tmaximumFractionDigits: 9\n\t\t});\n\t\treturn str;\n\t}\n\n\tfunction updateDisplay() {\n\t\tconst formatted = formatDisplay(currentVal);\n\t\tdisplay.textContent = formatted;\n\n\t\t// Adjust font size based on length\n\t\tconst len = formatted.length;\n\t\tdisplay.className = 'calc__display-text';\n\t\tif (len > 11) display.classList.add('calc__display-text--xxs');\n\t\telse if (len > 9) display.classList.add('calc__display-text--xs');\n\t\telse if (len > 6) display.classList.add('calc__display-text--sm');\n\n\t\t// Update AC / C\n\t\tclearBtn.textContent = currentVal === '0' && !operator ? 'AC' : 'C';\n\t}\n\n\tfunction highlightOp(op) {\n\t\tgrid.querySelectorAll('.calc__btn--op').forEach(btn => {\n\t\t\tbtn.classList.toggle('is-active', btn.dataset.op === op && !shouldResetDisplay === false || btn.dataset.op === op && shouldResetDisplay);\n\t\t});\n\t}\n\n\tfunction clearHighlights() {\n\t\tgrid.querySelectorAll('.calc__btn--op').forEach(btn => btn.classList.remove('is-active'));\n\t}\n\n\tfunction calculate(a, b, op) {\n\t\tconst numA = parseFloat(a);\n\t\tconst numB = parseFloat(b);\n\t\tswitch (op) {\n\t\t\tcase '+':\n\t\t\t\treturn numA + numB;\n\t\t\tcase '-':\n\t\t\t\treturn numA - numB;\n\t\t\tcase '*':\n\t\t\t\treturn numA * numB;\n\t\t\tcase '/':\n\t\t\t\treturn numB === 0 ? 'Error' : numA / numB;\n\t\t\tdefault:\n\t\t\t\treturn numB;\n\t\t}\n\t}\n\n\tfunction handleNumber(val) {\n\t\tlastEquals = false;\n\t\tif (shouldResetDisplay || currentVal === '0') {\n\t\t\tif (shouldResetDisplay) {\n\t\t\t\tcurrentVal = val;\n\t\t\t\tshouldResetDisplay = false;\n\t\t\t} else {\n\t\t\t\tcurrentVal = val;\n\t\t\t}\n\t\t} else {\n\t\t\t// Limit to 9 digits\n\t\t\tconst rawDigits = currentVal.replace(/[^0-9]/g, '');\n\t\t\tif (rawDigits.length >= 9) return;\n\t\t\tcurrentVal += val;\n\t\t}\n\t\tupdateDisplay();\n\t\tif (operator) highlightOp(operator);\n\t\telse clearHighlights();\n\t}\n\n\tfunction handleDecimal() {\n\t\tlastEquals = false;\n\t\tif (shouldResetDisplay) {\n\t\t\tcurrentVal = '0.';\n\t\t\tshouldResetDisplay = false;\n\t\t} else if (!currentVal.includes('.')) {\n\t\t\tcurrentVal += '.';\n\t\t}\n\t\tupdateDisplay();\n\t}\n\n\tfunction handleOperator(op) {\n\t\tlastEquals = false;\n\t\tif (operator && !shouldResetDisplay) {\n\t\t\tconst result = calculate(previousVal, currentVal, operator);\n\t\t\tcurrentVal = result === 'Error' ? 'Error' : String(result);\n\t\t\tpreviousVal = currentVal;\n\t\t} else {\n\t\t\tpreviousVal = currentVal;\n\t\t}\n\t\toperator = op;\n\t\tshouldResetDisplay = true;\n\t\tupdateDisplay();\n\t\thighlightOp(op);\n\t}\n\n\tfunction handleEquals() {\n\t\tif (lastEquals && lastOp && lastNum !== null) {\n\t\t\tconst result = calculate(currentVal, lastNum, lastOp);\n\t\t\tcurrentVal = result === 'Error' ? 'Error' : String(result);\n\t\t\tupdateDisplay();\n\t\t\tclearHighlights();\n\t\t\treturn;\n\t\t}\n\n\t\tif (operator) {\n\t\t\tlastOp = operator;\n\t\t\tlastNum = currentVal;\n\t\t\tconst result = calculate(previousVal, currentVal, operator);\n\t\t\tcurrentVal = result === 'Error' ? 'Error' : String(result);\n\t\t\tpreviousVal = '';\n\t\t\toperator = null;\n\t\t\tshouldResetDisplay = true;\n\t\t\tlastEquals = true;\n\t\t}\n\t\tupdateDisplay();\n\t\tclearHighlights();\n\t}\n\n\tfunction handleClear() {\n\t\tif (clearBtn.textContent === 'AC') {\n\t\t\tcurrentVal = '0';\n\t\t\tpreviousVal = '';\n\t\t\toperator = null;\n\t\t\tshouldResetDisplay = false;\n\t\t\tlastEquals = false;\n\t\t\tlastOp = null;\n\t\t\tlastNum = null;\n\t\t} else {\n\t\t\tcurrentVal = '0';\n\t\t\tshouldResetDisplay = false;\n\t\t}\n\t\tupdateDisplay();\n\t\tif (operator) highlightOp(operator);\n\t\telse clearHighlights();\n\t}\n\n\tfunction handleToggle() {\n\t\tif (currentVal === '0' || currentVal === 'Error') return;\n\t\tif (currentVal.startsWith('-')) {\n\t\t\tcurrentVal = currentVal.slice(1);\n\t\t} else {\n\t\t\tcurrentVal = '-' + currentVal;\n\t\t}\n\t\tupdateDisplay();\n\t}\n\n\tfunction handlePercent() {\n\t\tconst num = parseFloat(currentVal);\n\t\tif (isNaN(num)) return;\n\t\tcurrentVal = String(num / 100);\n\t\tupdateDisplay();\n\t}\n\n\t// Event delegation\n\tgrid.addEventListener('click', (e) => {\n\t\tconst btn = e.target.closest('.calc__btn');\n\t\tif (!btn) return;\n\n\t\tconst action = btn.dataset.action;\n\n\t\tswitch (action) {\n\t\t\tcase 'num':\n\t\t\t\thandleNumber(btn.dataset.val);\n\t\t\t\tbreak;\n\t\t\tcase 'decimal':\n\t\t\t\thandleDecimal();\n\t\t\t\tbreak;\n\t\t\tcase 'op':\n\t\t\t\thandleOperator(btn.dataset.op);\n\t\t\t\tbreak;\n\t\t\tcase 'equals':\n\t\t\t\thandleEquals();\n\t\t\t\tbreak;\n\t\t\tcase 'clear':\n\t\t\t\thandleClear();\n\t\t\t\tbreak;\n\t\t\tcase 'toggle':\n\t\t\t\thandleToggle();\n\t\t\t\tbreak;\n\t\t\tcase 'percent':\n\t\t\t\thandlePercent();\n\t\t\t\tbreak;\n\t\t}\n\t});\n\n\t// Keyboard support\n\tdocument.addEventListener('keydown', (e) => {\n\t\tif (e.key >= '0' && e.key <= '9') handleNumber(e.key);\n\t\telse if (e.key === '.') handleDecimal();\n\t\telse if (e.key === '+') handleOperator('+');\n\t\telse if (e.key === '-') handleOperator('-');\n\t\telse if (e.key === '*') handleOperator('*');\n\t\telse if (e.key === '/') {\n\t\t\te.preventDefault();\n\t\t\thandleOperator('/');\n\t\t} else if (e.key === 'Enter' || e.key === '=') handleEquals();\n\t\telse if (e.key === 'Escape' || e.key === 'c' || e.key === 'C') handleClear();\n\t\telse if (e.key === '%') handlePercent();\n\t\telse if (e.key === 'Backspace') {\n\t\t\tif (currentVal.length > 1) {\n\t\t\t\tcurrentVal = currentVal.slice(0, -1);\n\t\t\t} else {\n\t\t\t\tcurrentVal = '0';\n\t\t\t}\n\t\t\tupdateDisplay();\n\t\t}\n\t});\n\n\tupdateDisplay();\n})();"},{"name":"style.css","content":":root {\n\tcolor-scheme: dark;\n\tfont-family: -apple-system, 'Helvetica Neue', 'SF Pro Display', system-ui, sans-serif;\n}\n\n* {\n\tbox-sizing: border-box;\n\tmargin: 0;\n\tpadding: 0;\n}\n\nbody {\n\tbackground: #000;\n\tmin-height: 100dvh;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\t-webkit-user-select: none;\n\tuser-select: none;\n}\n\n.calc {\n\twidth: 100%;\n\tmax-width: 360px;\n\tpadding: 0 12px 20px;\n}\n\n/* Display */\n.calc__display {\n\twidth: 100%;\n\tpadding: 20px 8px 16px;\n\tdisplay: flex;\n\talign-items: flex-end;\n\tjustify-content: flex-end;\n\tmin-height: 120px;\n}\n\n.calc__display-text {\n\tcolor: #fff;\n\tfont-size: 88px;\n\tfont-weight: 200;\n\tline-height: 1;\n\tletter-spacing: -1.5px;\n\ttext-align: right;\n\ttransition: font-size 0.15s ease;\n}\n\n.calc__display-text--sm {\n\tfont-size: 64px;\n}\n\n.calc__display-text--xs {\n\tfont-size: 48px;\n}\n\n.calc__display-text--xxs {\n\tfont-size: 36px;\n}\n\n/* Button Grid */\n.calc__grid {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(4, 1fr);\n\tgap: 14px;\n}\n\n/* Buttons */\n.calc__btn {\n\taspect-ratio: 1;\n\tborder-radius: 50%;\n\tborder: none;\n\tfont-size: 34px;\n\tfont-weight: 400;\n\tcursor: pointer;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\ttransition: filter 0.1s ease;\n\t-webkit-tap-highlight-color: transparent;\n\tfont-family: inherit;\n\tline-height: 1;\n}\n\n.calc__btn:active {\n\tfilter: brightness(1.4);\n}\n\n/* Number buttons */\n.calc__btn--num {\n\tbackground: #333;\n\tcolor: #fff;\n}\n\n.calc__btn--num:active {\n\tbackground: #737373;\n}\n\n/* Zero button */\n.calc__btn--zero {\n\tgrid-column: span 2;\n\tborder-radius: 999px;\n\taspect-ratio: auto;\n\tjustify-content: flex-start;\n\tpadding-left: 30px;\n}\n\n/* Utility buttons (AC, +/-, %) */\n.calc__btn--util {\n\tbackground: #a5a5a5;\n\tcolor: #000;\n\tfont-size: 28px;\n\tfont-weight: 500;\n}\n\n.calc__btn--util:active {\n\tbackground: #d9d9d9;\n}\n\n/* Operator buttons */\n.calc__btn--op {\n\tbackground: #ff9f0a;\n\tcolor: #fff;\n\tfont-size: 40px;\n\tfont-weight: 400;\n}\n\n.calc__btn--op:active {\n\tbackground: #fcc580;\n}\n\n/* Active operator state */\n.calc__btn--op.is-active {\n\tbackground: #fff;\n\tcolor: #ff9f0a;\n}\n\n.calc__btn--op.is-active:active {\n\tbackground: #fcc580;\n\tcolor: #fff;\n}\n\n/* Responsive */\n@media (max-height: 600px) {\n\t.calc__display {\n\t\tmin-height: 80px;\n\t\tpadding: 10px 8px 10px;\n\t}\n\n\t.calc__display-text {\n\t\tfont-size: 56px;\n\t}\n\n\t.calc__grid {\n\t\tgap: 10px;\n\t}\n\n\t.calc__btn {\n\t\tfont-size: 28px;\n\t}\n\n\t.calc__btn--op {\n\t\tfont-size: 32px;\n\t}\n}"}],"folders":[]},"variants":null,"createdAt":"2026-03-02T01:51:59.114Z","updatedAt":"2026-03-02T01:52:04.343Z","hasThumbnail":true}}