calculator source

App.tsx

import { useState } from 'react';
import { StatusBar, useWindowDimensions } from 'react-native';
import { SafeAreaProvider } from 'react-native-safe-area-context';

import { Block, Button, PlocksProvider, SafeArea, Text } from '@plocks/ui';

const COLORS = {
  background: '#15191E',
  surface: '#252B32',
  ink: '#F7F9FA',
  muted: '#A6B0BA',
  accent: '#E9A33C',
} as const;

const KEYS = [
  ['AC', '±', '%', '÷'],
  ['7', '8', '9', '×'],
  ['4', '5', '6', '−'],
  ['1', '2', '3', '+'],
  ['0', '.', '='],
] as const;
const KEY_INSET = 5;

type Operator = '+' | '−' | '×' | '÷';

const isOperator = (key: string): key is Operator => ['+', '−', '×', '÷'].includes(key);
const format = (value: number) => Number.isFinite(value) ? String(Number(value.toPrecision(11))) : 'Error';
const formatDisplay = (value: string) =>
  value.replace(/^-?\d+/, integer => integer.replace(/\B(?=(\d{3})+(?!\d))/g, ','));
const evaluate = (left: number, right: number, operator: Operator) => {
  switch (operator) {
    case '+': return left + right;
    case '−': return left - right;
    case '×': return left * right;
    case '÷': return left / right;
  }
};

export default function App() {
  const { width, height } = useWindowDimensions();
  const [display, setDisplay] = useState('0');
  const [stored, setStored] = useState<number | null>(null);
  const [operator, setOperator] = useState<Operator | null>(null);
  const [fresh, setFresh] = useState(false);
  const [keypadHeight, setKeypadHeight] = useState(0);
  const fontScale = Math.sqrt((width * height) / (390 * 844));
  const keyHeight = keypadHeight ? keypadHeight / KEYS.length - KEY_INSET * 2 : 60;
  const keyFontSize = Math.max(18, Math.min(56, 24 * fontScale, keyHeight * 0.45));
  const displayFontSize = Math.max(44, Math.min(112, 52 * fontScale));
  const historyFontSize = Math.max(16, Math.min(34, 18 * fontScale));

  function press(key: string) {
    if (key === 'AC') {
      setDisplay('0');
      setStored(null);
      setOperator(null);
      setFresh(false);
      return;
    }

    if (/^\d$/.test(key) || key === '.') {
      const starting = fresh || display === 'Error';
      if (key === '.' && !starting && display.includes('.')) return;
      setDisplay(starting ? (key === '.' ? '0.' : key) : display === '0' && key !== '.' ? key : display + key);
      setFresh(false);
      return;
    }

    if (display === 'Error') return;
    if (key === '±' || key === '%') {
      setDisplay(format(key === '±' ? -Number(display) : Number(display) / 100));
      return;
    }

    if (key === '=') {
      if (operator && stored !== null) {
        setDisplay(format(evaluate(stored, Number(display), operator)));
        setStored(null);
        setOperator(null);
        setFresh(true);
      }
      return;
    }

    if (isOperator(key)) {
      const next = operator && stored !== null && !fresh
        ? evaluate(stored, Number(display), operator)
        : Number(display);
      setStored(next);
      setDisplay(format(next));
      setOperator(key);
      setFresh(true);
    }
  }

  return (
    <SafeAreaProvider>
      <PlocksProvider theme={{ colorScheme: 'dark' }}>
        <SafeArea flex={1} gap={0} bg={COLORS.background}>
          <StatusBar barStyle="light-content" />
          <Block flex={1} gap={0} p={12}>
            <Block flex={1} gap={4} px={5} pb={12} align="flex-end" justify="flex-end">
              <Text c={COLORS.muted} size={historyFontSize} ta="right">
                {stored !== null && operator ? `${formatDisplay(format(stored))} ${operator}` : ' '}
              </Text>
              <Text c={COLORS.ink} size={displayFontSize} ta="right" numberOfLines={1}>
                {formatDisplay(display)}
              </Text>
            </Block>

            <Block flex={3} gap={0} onLayout={({ nativeEvent: { layout } }) => setKeypadHeight(layout.height)}>
              {KEYS.map((row, rowIndex) => (
                <Block key={rowIndex} flex={1} direction="row" gap={0}>
                  {row.map((key) => (
                    <Block key={key} flex={key === '0' ? 2 : 1} gap={0} p={KEY_INSET}>
                      <Button
                        title={key}
                        onPress={() => press(key)}
                        variant="filled"
                        color={isOperator(key) || key === '=' ? COLORS.accent : COLORS.surface}
                        textColor={COLORS.ink}
                        labelProps={{ size: keyFontSize, fw: '600' }}
                        radius={18}
                        fullWidth
                        h={keyHeight}
                      />
                    </Block>
                  ))}
                </Block>
              ))}
            </Block>
          </Block>
        </SafeArea>
      </PlocksProvider>
    </SafeAreaProvider>
  );
}

index.ts

import { registerRootComponent } from 'expo';

import App from './App';

registerRootComponent(App);