Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using it's URL and the proper URL extention.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <script src="https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js"></script>

<header>
  <h1>Javascript Calculator</h1>
</header>
<div id="root">
</div>

              
            
!

CSS

              
                html {
  margin: 0;
  padding: 0;
  font-size: 15px;
}

body {
  background-color: #251719;
}

header {
  text-align: center;
  color: #A29E90;
}

#root {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
}

#calculator {
  padding: 2rem;
  border-radius: 9px;
  background-color: #A29E90;
}

#display {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 0.6rem;
  margin-bottom: 0.6rem;
  background-color: #202020;
  color: #8398AF;
  font-size: 1.2rem;
}

.button-container {
  display: flex;
}

button {
  background-color: #283535;
  border-radius: 6px;
  margin: 0.1rem;
  font-size: 1.2rem;
  text-align: center;
  color: #A8B8C8;
  font-size: 1rem;
}

#Numpad {
  display: grid;
  grid-template-columns: repeat(3, 45px);
  grid-template-rows: repeat(4, 45px);
  margin: 0.6rem 0.6rem 0 0;
}

#operators {
  margin-top: 0.6rem;
  display: grid;
  grid-template-columns: 45px 45px;
  grid-template-rows: 45px 45px;
  button {
    font-size: 1.5rem;
  }
}
              
            
!

JS

              
                // Key Outtakes:
// Numbers should not begin with 0
// Two decimals not accepted
// Click '-' twice to start with a negative number.

const OPERATOR = 'OPERATOR';
const CLEAR = 'CLEAR';
const EQUALS = 'EQUALS';
const UPDATE = 'UPDATE';

// Array of numbers. Used in Numpad class.
const numbers = [
  {
    numId: 'one',
    value: 1
  },
  {
    numId: 'two',
    value: 2
  },
  {
    numId: 'three',
    value: 3
  },
  {
    numId: 'four',
    value: 4
  },
  {
    numId: 'five',
    value: 5
  },
  {
    numId: 'six',
    value: 6
  },
  {
    numId: 'seven',
    value: 7
  },
  {
    numId: 'eight',
    value: 8
  },
  {
    numId: 'nine',
    value: 9
  },
  {
    numId: 'zero',
    value: 0
  },  // If local state is 0 ? Don't do anything : concat 0;
  {
    numId: 'thousand',
    value: '000'
  },
  {
    numId: 'decimal',
    value: '.'
  }  // Check if there's a decimal already ? Don't add decimal : Add decimal;
];

// Array of operators and functions, used in operator class.
const operators = [
  {
    opId: 'add',
    value: '+'
  },
  {
    opId: 'subtract',
    value: '-'
  },
  {
    opId: 'multiply',
    value: '*'
  },
  {
    opId: 'divide',
    value: '/'
  },
  {
    opId: 'equals',
    value: '='
  },
  {
    opId: 'clear',
    value: 'clr'
  }  // If there are non-zero numbers in local state, clear the local state. Else, clear redux state.
];

// Redux state should store a value, that will be the first operand and the operator, defining the operation. Eg Action: Enters number; Enters operator; Number and Operator stored in central state; Input is reset; Enters second number; Enters equals; Operation carried out on second operand by state stored number using state stored operator.

const defaultReduxState = {
  value: 0,
  operator: ''
}

// Redux:
// Operator "+,-,/,*" buttons are clicked
const operatorAction = (firstOperand, operator) => {
  return {
    type: OPERATOR,
    value: firstOperand,
    operator: operator
  }
}

// Equals "=" button is clicked
const equalsAction = (secondOperand) => {
  return {
    type: EQUALS,
    value: secondOperand
  }
}

// Clear is clicked
const clearAction = () => {
  return {
    type: CLEAR
  }
}

const operatorReducer = (state=defaultReduxState, action) => {
  var result;   // Stores calculated results.
  // New state, maintain state immutability.
  let newState = {
    ...state
  }
  switch(action.type) {
    // If operator, store current input and operator in state.
    case OPERATOR:
      console.log('OPERATOR.');
      let nextValue = Number(action.value);
      let operator = action.operator;
      if(nextValue)
      {
        newState = {
          value: nextValue,
          operator: operator
        }
      }
      else
      {
        newState = {
          value: state.value,
          operator: operator
        }
      }
      return newState;
    // If equals, use state(value and operator) to operate with provided second operand. Store result in state, with blank operator. 
    case EQUALS:
      let operation = state.operator;
      console.log('EQUALS');
      switch(operation) {
        case "+":
          result = parseFloat(state.value) + parseFloat(action.value);
          console.log(result);
          newState = {
            value: result,
            operator: ''
          }
          return newState;
          
        case "-":
          result = parseFloat(state.value) - parseFloat(action.value);
          console.log(result);
          newState = {
            value: result,
            operator: ''
          }
          return newState;
          
        case "*":
          result = parseFloat(state.value) * parseFloat(action.value);
          newState = {
            value: result,
            operator: ''
          }
          return newState;

        case "/":
          result = parseFloat(state.value) / parseFloat(action.value);
          newState = {
            value: result,
            operator: ''
          }
          return newState;
          
        default:
          return state;
      }
    // Clear state properties.
    case CLEAR:
      return {
        value: 0,
        operator: ''
      }
      
    default:
      return state;
  }
}
// Used thunk because I thought my system was acting asynchronous and state wasn't updating. Using "store.getState()" to get recent state now.  
const store = Redux.createStore(operatorReducer, Redux.applyMiddleware(ReduxThunk.default));

// ReactRedux
const Provider = ReactRedux.Provider;
const connect = ReactRedux.connect;

const mapStateToProps = (state) => {
  return {
    stored: state
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    operate: (value, operator) => dispatch(operatorAction(value, operator)),
    equals: (value) => dispatch(equalsAction(value)),
    clearState: () => dispatch(clearAction())
  }
}

class Operators extends React.Component {
  constructor(props) {
    super(props);
    
    this.operationManager = this.operationManager.bind(this);
    this.checkStorageAndInput = this.checkStorageAndInput.bind(this);
  }
  // Method to check if there are already operator and value in store, unprocessed, and if current input is valid, non-zero and non-hiphon(used for negative operations). Used primarily for chaining operations.
  checkStorageAndInput(value) {
    let previousValue = store.getState().value;
    let previousOperator = store.getState().operator;
    
    if(previousValue != 0 && previousOperator != '' && (value != '0' && value != '-'))
      return true;
    
    else
      return false;
  }
  // Method when operator clicked. Self explanatory. Carry out actions accordingly.
  operationManager(event) {
    let action = event.target.value;
    let operand = this.props.currentSequence;
    let result;
    
    switch(action) {
      case '+':
        if(this.checkStorageAndInput(operand)) {
          this.props.equals(operand);
          this.props.operate('', action);
        }
        else {
          this.props.operate(operand, action);
        }
        this.props.clearSequence();
        break;
      // After checking for operation checking. Then, if there is an operator in state currently, turn the next number into a negative instead of subtraction operation.
      case '-':
        if(this.checkStorageAndInput(operand)) {
          this.props.equals(operand);
          this.props.operate('', action);
        }
        else if(store.getState().operator) {
          this.props.displayResult('-');
          break;
        }
        else {
          this.props.operate(operand, action);
        }
        this.props.clearSequence();
        break;
        
      case '*':
        if(this.checkStorageAndInput(operand)) {
          this.props.equals(operand);
          this.props.operate('', action);
        }
        else {
          this.props.operate(operand, action);
        }
        this.props.clearSequence();
        break;
        
      case '/':
        if(this.checkStorageAndInput(operand)) {
          this.props.equals(operand);
          this.props.operate('', action);
        }
        else {
          this.props.operate(operand, action);
        }
        this.props.clearSequence();
        break;
        
      case '=':
        this.props.equals(operand);
        result = store.getState().value;
        console.log(result);
        this.props.displayResult(result); 
        break;
        
      case 'clr':
        //Clear function: If input is already clear, clear redux state value. Else, clear input.
        operand == 0 ? this.props.clearState() : this.props.clearSequence();
        result = 0;
        break;
        
      default: 
        break;
    }
  }

  render() {
    let funcArray = operators.map((opObj, i, opArray) => {
      return (
        <button id={opArray[i].opId}
          value={opArray[i].value}
          onClick={this.operationManager}
          className="operations">
          {opArray[i].value}
        </button>
      )
    });
    
    return (
      <div id="operators">
        {funcArray}
      </div>
    );
  }
}

const Container = connect(mapStateToProps, mapDispatchToProps)(Operators);

// UI class. Zero and decimal checks within.
class Numpad extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      input: "00"
    }
    
    this.updateSequence = this.updateSequence.bind(this);
    this.displayResult = this.displayResult.bind(this);
    this.clearSequence = this.clearSequence.bind(this);
  }
  
  componentDidMount() {
    // Keyboard functionality if desired.
    console.log("Numpad Mounted.");
  }
  
  updateSequence(value) {
    var newValue = value;
    var inputState = this.state.input;
    const digit = /\d+/;
    const zeroes = /^0+$/;
    const decimal = /[.]/;
    
    // Check if new incoming is a digit, then check if local state is zero(es), then replace with state else concatenate. 
    if(digit.test(newValue)) {
      zeroes.test(inputState) ? this.setState({
          input: newValue
        }) : this.setState({
          input: inputState + newValue
        });
    }
    // Else if a decimal point is entered, check if there isn't already a decimal in the local state then concatenate.
    else if(decimal.test(newValue)) {
      if(!decimal.test(inputState)) {
        this.setState({
          input: inputState + newValue
        });
      }
    }
  }
  // Useful in operators class.
  displayResult(value) {
    this.setState({
      input: value
    });
  }
  
  clearSequence() {
    this.setState({
      input: '0'
    });
  }

  render() {
    var numpad = numbers.map((obj, i, numArray) => {
      return (
        <button id={numArray[i].numId}
          value={numArray[i].value}
          onClick={() => this.updateSequence(event.target.value)}
          className='number'>
          {numArray[i].value}
        </button>
      )  
    });
    
    return (
      <div id="calculator" className="calc-container">
        <div id="display">
          {this.state.input}
        </div>
        <div className="button-container">
          <div id="Numpad">
            {numpad}
          </div>
          <Provider store={store}>
            <Container clearSequence={this.clearSequence}
              currentSequence={this.state.input}
              displayResult={this.displayResult} />
          </Provider>
        </div>
      </div>
    );
  }
};

ReactDOM.render(<Numpad />, document.getElementById('root'));
              
            
!
999px

Console