map() Insert

map() Insert demo built with Expressive MVC - the complete source below runs as an editable sandbox when JavaScript is enabled.

App.tsx

import './App.css';

import { Component, map } from '@expressive/react';

// `map<K, V>()` is the insert mode: a reactive Map of values you place by
// key with `set(key, value)` - no factory, no spawning. Bumping one key
// notifies only that entry; adding or removing a key notifies shape.
export default class Inventory extends Component {
  stock = map<string, number>();
  item = '';

  protected new() {
    this.stock.set('apples', 3);
    this.stock.set('bread', 1);
    this.stock.set('milk', 2);
  }

  add(name = this.item) {
    name = name.trim().toLowerCase();
    if (!name) return;
    this.stock.set(name, (this.stock.get(name) ?? 0) + 1);
    this.item = '';
  }

  render() {
    const { stock, item } = this;
    const total = [...stock.values()].reduce((sum, n) => sum + n, 0);

    return (
      <div className="container inv">
        <h1>Reactive Map</h1>
        <p>
          <code>map&lt;string, number&gt;()</code> keys values you insert with{' '}
          <code>set(key, value)</code>. Re-adding a name bumps its count in
          place.
        </p>

        <ul className="stock">
          {[...stock].map(([name, qty]) => (
            <li key={name}>
              <span className="name">{name}</span>
              <span className="qty">
                <button onClick={() => stock.set(name, Math.max(0, qty - 1))}>−</button>
                <output>{qty}</output>
                <button onClick={() => stock.set(name, qty + 1)}>+</button>
              </span>
            </li>
          ))}
        </ul>

        <form
          onSubmit={(e) => {
            e.preventDefault();
            this.add();
          }}>
          <input
            value={item}
            placeholder="Add or bump an item…"
            onChange={(e) => (this.item = e.target.value)}
          />
          <button type="submit">Add</button>
        </form>

        <footer>
          <small>{stock.size} items · {total} in stock</small>
        </footer>
      </div>
    );
  }
}

App.css

.inv {
  max-width: 26rem;
}

.stock {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  width: 100%;
  margin: 0;
  padding: 0;
  list-style: none;
}

.stock li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  padding: var(--s2) var(--s3);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  cursor: default;
}

.stock .name {
  font-weight: 500;
  text-transform: capitalize;
}

.qty {
  display: flex;
  align-items: center;
  gap: var(--s2);
}

.qty button {
  width: 1.75rem;
  height: 1.75rem;
  padding: 0;
  font-family: var(--font-mono);
  line-height: 1;
}

.qty output {
  min-width: 1.5ch;
  font-family: var(--font-mono);
  font-size: var(--t-lg);
  text-align: center;
  color: var(--accent);
}

.inv form {
  display: flex;
  gap: var(--s2);
  width: 100%;
}

.inv form button {
  flex: 0 0 auto;
}