JSFiddle - React, Tailwind, and code Playground

by Takuya Nakajima

HTML

<script src="https://cdn.jsdelivr.net/npm/stimulus@2.0.0/dist/stimulus.umd.min.js"></script>
<div class="Parent" data-controller="parent" data-action="child:change->parent#updateFormula">
  <p>Parent</p>
  <div class="Parent__children" data-parent-target="children">
    <!-- child -->
  </div>
  <button type="button" data-action="click->parent#add">
    add child
  </button>
  <div class="Parent__value" data-parent-target="value">-</div>

  <template data-parent-target="template">
    <div class="Child" data-controller="child">
      <p>Child</p>
      <input type="text" data-parent-target="input" data-action="input->child#change">
      <button data-action="click->child#remove">delete</button>
    </div>
  </template>
</div>

CSS

* {
  margin: 0;
  padding: 0;
}

body {
  margin: 20px;
}

.Parent {
  padding: 20px;
  border: 1px solid #000;
}

.Parent__value {
  margin-top: 20px;
  padding: 10px;
  background: #dedede;
}

.Child {
  padding: 10px;
  background: skyblue;
  border: 1px solid #000;
}

input,button {
  padding: 5px;
}

JavaScript

const {Application, Controller} = Stimulus;

const app = Application.start();

class Base extends Controller { // from hey.com
  dispatch(eventName, { target = this.element, detail = {}, bubbles = true, cancelable = true } = {}) {
    const type = `${this.identifier}:${eventName}`
    const event = new CustomEvent(type, { detail, bubbles, cancelable })
    target.dispatchEvent(event)
    return event
  }
}

app.register('parent', class extends Base {
	static targets = ['children', 'template', 'value', 'input'];
  
	initialize() {
    this.add();
  }
  
  add() {
  	let child = this.templateTarget.content.cloneNode(true);
    this.childrenTarget.append(child);
  }
  
  updateFormula() {
  	let formula = this.inputTargets.map(input => input.value).join('/');
    this.valueTarget.innerHTML = formula || '-';
  }
});

app.register('child', class extends Base {
	remove() {
    let parent = this.element.parentNode;
  	this.element.remove();
    this.dispatch('change', {target: parent});
  }
  
  change() {
    this.dispatch('change');
  }
})