JSFiddle - React, Tailwind, and code Playground

by lchau

HTML

<div>
  <label for="subtotal">Subtotal</label>
  <input type="number" id="subtotal" value="0" readonly="readonly" disabled="disabled">
</div>
<div>
  <label for="total">Sales Tax</label>
  <input type="number" id="salesTax" value="8.5" min="1.0">%
</div>
<div>
  <label for="total">SF Employer Mandate</label>
  <input type="number" id="mandatoryTax" value=4 min="0">%
</div>
<div>
  <label for="total">Total</label>
  <input type="number" id="total" value="40" min="0">
</div>
<div>
  <label for="total">Split</label>
  <input type="number" id="splitCount" value="2" min="1">
</div>

<button id="calculate">calculate</button>
<div id="output"></div>

CSS

#output {
  font-family: monospace;
}

table {
  margin-top: 10px;
  border: 1px solid silver;
  border-collapse: collapse;
}

table td,
table th {
  border: 1px solid #AAAAAA;
  padding: 2px 15px;
}

table td {
  text-align: right;
}

label {
  width: 150px;
  display: inline-block;
  text-align: right;
  padding-right: 5px;
}

input {
  width: 75px;
  text-align: right;
}

body,
input {
  font-family: monospace;
}

JavaScript

function getValue(id, defaultValue = 0) {
  const $element = document.getElementById(id);
  const value = Number($element.value) || defaultValue;
  return value >= 0 ? value : defaultValue;
}

const toFixed = (value, precision = 2) => Number(Number(value).toFixed(precision));

function calculate() {
  const total = getValue('total', 0);
  const salesTax = 1 + (getValue('salesTax', 1) / 100);
  const mandatoryTax = 1 + (getValue('mandatoryTax', 1) / 100);
  const splitCount = getValue('splitCount', 1);

  const subtotal = toFixed(total / salesTax / mandatoryTax);
	document.getElementById('subtotal').value = subtotal;
  return [1.15, 1.18, 1.20].map(v => {
    const tip = toFixed((subtotal * v) - subtotal);
    return {
      '%': Math.round((v - 1) * 100),
      tip,
      get adjusted_tip() {
//        return +(+Math.trunc(this.tip) + +(Math.ceil(this.total) - this.total)).toFixed(2);
				return toFixed((Math.ceil(total) - total) + Math.ceil(this.tip));
      },
      get adjusted_tip_person() {
      	return toFixed(this.adjusted_tip / splitCount);
      },
      get round() {
        return toFixed(this.adjusted_tip + total);
      },
      get split() {
        return toFixed(this.round / splitCount);
      }
    };
  });
}

function render() {
  const rows = calculate().map(data => {
    return `
      <tr>
        <td>${data['%']}%</td>
				<td>$${data.tip}</td>
				<td>$${data.adjusted_tip}</td>
        <td>$${data.round}</td>
				<td>$${data.adjusted_tip_person}</td> 
        <td>$${data.split}</td>
      </tr>
    `;
  });
  document.getElementById('output').innerHTML = `
    <table>
      <tr>
        <th>%</th>
        <th>Tip</th>
        <th>Adjusted Tip</th>
        <th>Rounded Total</th>
  			<th>Tip/Person</th>
        <th>Total/Person</th>
      </tr>
      ${rows.join('')}
    </table>
  `
}


document.getElementById('calculate').addEventListener('click', render);
document.addEventListener('keyup', render);
render();