Examples

      <div class="SmarkForm">
        <section>
          <div class='form-group h1'>Company</div>
          <div class='input-group'>
            <label>Corporate Name</label>
            <input data-smark name='company' type='text' placeholder='Company Name'>
          </div>
          <div class='input-group'>
            <label>Address</label>
            <textarea data-smark name='address' placeholder='Address'></textarea>
          </div>
          <div class='form-group'>
            <label>City,State,Zip</label>
            <input data-smark name='city' placeholder='City' style='flex: 6'>
            <input data-smark name='state' placeholder='State' style='flex:1'>
            <input data-smark name='postCode' placeholder='Postal Code' style='flex:2'>
          </div>
        </section>
        <section>
          <div class='form-group h2'><span class='foldButton' data-smark='{"action":"fold","for":"employees","foldedClass":"folded"}'></span><span><span>Employees (</span><span data-smark='{"action":"count","for":"employees"}'></span><span>)</span></span>
            <div class='form-group'>
              <div class='spacer'></div>
              <button data-smark='{"action":"addItem","for":"employees","autoscroll":"self"}' title='Add employee'></button>
              <button data-smark='{"action":"removeItem","for":"employees","keep_non_empty":true}' title='Remove employees from bottom priorizing empties'></button>
              <button data-smark='{"action":"removeItem","for":"employees","to":"*","keep_non_empty":true}' title='Clear all empty employees'>🧹</button>
            </div>
          </div>
          <div class='form-group'>
            <div class='form-group' data-smark='{"type":"list","exportEmpties":true,"sortable":true,"name":"employees","min_items":0}'>
              <fieldset class='full-width form-group aside reverse' data-smark='{"exportEmpties":false}'>
                <button class='inline' data-smark='{"action":"removeItem"}' title='Remove this employee'></button>
                <div class='form-group spacer'>
                  <div class='form-group'>
                    <div class='input-group'>
                      <label>First Name</label>
                      <input data-smark name='name' placeholder='Name'>
                    </div>
                    <div class='input-group'>
                      <label>Last Name</label>
                      <input data-smark name='lastName' placeholder='Surnme'>
                    </div>
                  </div>
                  <div class='form-group'>
                    <div class='input-group'>
                      <label>Date of Birth</label>
                      <input data-smark type='date' name='birth' placeholder='dd/mm/yyyy'>
                    </div>
                    <div class='input-group'>
                      <label>Salary</label>
                      <input data-smark type='number' name='salary' step='0.01' placeholder='Euros'>
                    </div>
                  </div>
                  <div class='form-group'>
                    <div class='input-group'>
                      <label>Telephones</label>
                      <div data-smark='{"name":"phones","type":"list","of":"input","max_items":4}'>
                        <div class='singleton'>
                          <button data-smark='{"action":"addItem"}' title='Add new item below'></button>
                          <input data-smark='data-smark' type='tel' placeholder='Telephone'/>
                          <button data-smark='{"action":"removeItem","failback":"clear"}' title='Remove this item'></button>
                        </div>
                      </div>
                    </div>
                    <div class='input-group'>
                      <label>Emails</label>
                      <div data-smark='{"name":"emails","type":"list","of":"input","max_items":4}'>
                        <div class='singleton'>
                          <button data-smark='{"action":"addItem"}' title='Add new item below'></button>
                          <input data-smark='data-smark' type='email' placeholder='Email'/>
                          <button data-smark='{"action":"removeItem","failback":"clear"}' title='Remove this item'></button>
                        </div>
                      </div>
                    </div>
                  </div>
                </div>
              </fieldset>
            </div>
          </div>
          <div class='form-group f2'>
            <div class='spacer'></div>
            <button data-smark='{"action":"addItem","for":"employees","autoscroll":"elegant"}' title='Add employee'></button>
            <button data-smark='{"action":"removeItem","for":"employees","keep_non_empty":true,"autoscroll":"elegant"}' title='Remove employees from bottom priorizing empties'></button>
            <button data-smark='{"action":"removeItem","for":"employees","to":"*","autoscroll":"elegant","keep_non_empty":true}' title='Clear all empty employees'>🧹</button>
          </div>
        </section>
        <section>
          <div class='form-group'>
            <div class='spacer'></div>
            <button data-smark='{"action":"empty"}' title='Clear form data'>❌ Cancel</button>
            <button data-smark='{"action":"export"}' title='Submit form data'>💾 Submit</button>
          </div>
        </section>
      </div>
      <div class="SmarkForm">
        <section>
          <div class='form-group h1 nowrap'>
            <div class='spacer'></div>
            <button data-smark='{"action":"import","for":"tasklist"}'>📂 Import (JSON)</button>
            <button data-smark='{"action":"export","for":"tasklist"}'>💾 Export (JSON)</button>
          </div>
          <div class='form-group' data-smark='{"type":"list","name":"tasklist","exportEmpties":true,"min_items":0}'>
            <fieldset class='form-group aside reverse' data-smark='{"exportEmpties":false}'>
              <button class='inline' data-smark='{"action":"removeItem"}' title='Remove task'></button>
              <div class='form-group spacer'>
                <div class='form-group'>
                  <input data-smark name='title' type='text' placeholder='Task title'>
                </div>
                <div class='form-group'>
                  <div class='input-group'>
                    <label>Goals</label>
                    <div data-smark='{"name":"goals","type":"list","of":"input","max_items":100}'>
                      <div class='singleton'>
                        <button data-smark='{"action":"addItem"}' title='Add new item below'></button>
                        <input data-smark='data-smark' type='text' placeholder='New goal...'/>
                        <button data-smark='{"action":"removeItem","failback":"clear"}' title='Remove this item'></button>
                      </div>
                    </div>
                  </div>
                </div>
              </div>
            </fieldset>
          </div>
          <div class='form-group f1 nowrap' style='text-align: right'>
            <div class='spacer'></div>
            <button data-smark='{"action":"empty","for":"tasklist","autoscroll":"elegant"}' title='Clear form data'>❌ Clear all</button>
            <button data-smark='{"action":"removeItem","for":"tasklist","to":"*","autoscroll":"elegant","keep_non_empty":true}' title='Clear all empty tasks'>🧹 Clear empty</button>
            <button data-smark='{"action":"addItem","for":"tasklist"}'>➕ Add new task</button>
          </div>
        </section>
      </div>
      <div class="SmarkForm">
        <section>
          <h2>Activity</h2>
          <div class='form-group'>
            <label>Title</label>
            <input data-smark name='activity_name' type='text' placeholder='Name your planned activity...'>
          </div>
          <div class='input-group'>
            <label>Description</label>
            <textarea data-smark name='activity_description' placeholder='Brief description of your activity'></textarea>
          </div>
          <h2>Planning</h2>
          <h3>Origin</h3>
          <div class='form-group' data-smark='{"type":"form","name":"origin"}'>
            <input data-smark name='place' placeholder='Place'>
            <input data-smark name='city' placeholder='City'>
            <input data-smark name='date' type='date' placeholder='dd/mm/yyyy'>
            <input data-smark name='time' type='time' placeholder='hh:mm'>
          </div>
          <h3>Stops</h3>
          <div class='form-group' data-smark='{"type":"list","name":"stops","min_items":0,"exportEmpties":false}'>
            <div class='full-width form-group aside reverse'>
              <button class='inline' data-smark='{"action":"removeItem"}'>-</button>
              <div class='form-group'>
                <input data-smark name='place' placeholder='Place'>
                <input data-smark name='city' placeholder='City'>
                <input data-smark name='date' type='date' placeholder='dd/mm/yyyy'>
                <input data-smark name='time' type='time' placeholder='hh:mm'>
              </div>
            </div>
          </div>
          <div class='form-group'>
            <div class='spacer'></div>
            <button class='inline' data-smark='{"action":"addItem","for":"stops"}'>Add Stop Place</button>
          </div>
          <h3>Destination</h3>
          <div class='form-group' data-smark='{"type":"form","name":"destination"}'>
            <input data-smark name='place' placeholder='Place'>
            <input data-smark name='city' placeholder='City'>
            <input data-smark name='date' type='date' placeholder='dd/mm/yyyy'>
            <input data-smark name='time' type='time' placeholder='hh:mm'>
          </div>
        </section>
        <section>
          <h2>Participants</h2>
          <h3>Organizers</h3>
          <div class='form-group' data-smark='{"type":"list","name":"organizers","exportEmpties":false}'>
            <div class='full-width form-group aside reverse'>
              <button class='inline' data-smark='{"action":"removeItem"}'>-</button>
              <div class='form-group spacer'>
                <div class='form-group'>
                  <input style='flex: 6' data-smark name='name' placeholder='Name'>
                  <input style='flex: 2' data-smark name='phone' type='tel' placeholder='Phone number'>
                </div>
              </div>
            </div>
          </div>
          <div class='form-group'>
            <div class='spacer'></div>
            <button class='inline' data-smark='{"action":"addItem","for":"organizers"}'>Add Organizer</button>
          </div>
          <h3>Participants:</h3>
          <div class='form-group' data-smark='{"type":"list","name":"participants","min_items":0,"exportEmpties":false}'>
            <div class='full-width form-group aside reverse'>
              <button class='inline' data-smark='{"action":"removeItem"}'>-</button>
              <div class='form-group spacer'>
                <div class='form-group'>
                  <input style='flex: 6' data-smark name='name' placeholder='Name'>
                  <input style='flex: 2' data-smark name='phone' type='tel' placeholder='Phone number'>
                </div>
              </div>
            </div>
          </div>
          <div class='form-group'>
            <div class='spacer'></div>
            <button class='inline' data-smark='{"action":"addItem","for":"participants"}'>Add Participant</button>
          </div>
        </section>
        <section>
          <div class='form-group'>
            <div class='spacer'></div>
            <button data-smark='{"action":"empty"}' title='Clear form data'>❌ Cancel</button>
            <button data-smark='{"action":"export"}' title='Submit form data'>💾 Submit</button>
          </div>
        </section>
      </div>
          // Form instantiation:
          // -------------------
          window.form = new SmarkForm(
              document.querySelector("#main-form")
              , {
                  // Do Something on export action:
                  onAfterAction_export({data}) {
                      alert (JSON.stringify(data, null, 4));
                  },
                  // Ask for confirm on empty action
                  // ...but only if form is not already empty:
                  async onBeforeAction_empty({context, preventDefault}) {
                      if (
                          ! await context.isEmpty()
                          && ! confirm("Are you sure?")
                      ) preventDefault();
                  },
                  // Fetching data on import action (example):
                  onBeforeAction_import(options) {
                      let data = prompt('Provide JSON data');
                      try {
                          options.data = JSON.parse(data);
                      } catch (err) {
                          if (data.length) {
                              alert ('⚠️  Invalid JSON!!');
                              data = null;
                          } else {
                              data = {}; // Drop form contents
                          };
                      };
                      if (data === null) options.preventDefault();
                  },
              }
          );
      
          // List items addition/removal animation:
          // --------------------------------------
          form
              .onAll("addItem", function({
                  newItemTarget,
                  onRendered,
              }) {
                  newItemTarget.classList.add("ingoing");
                  onRendered(()=>{
                      newItemTarget.classList.remove("ingoing")
                      newItemTarget.classList.add("ongoing");
                  });
              })
              .onAll("removeItem", async function({
                  oldItemTarget,
                  onRemmoved,
              }) {
                  oldItemTarget.classList.remove("ongoing");
                  oldItemTarget.classList.add("outgoing");
      
                  // Await for transition to be finished before item removal:
                  const [duration, multiplier = 1000] = window.getComputedStyle(oldItemTarget)
                      .getPropertyValue('transition-duration')
                      .slice(0,-1).replace("m","/1")
                      .split("/")
                      .map(Number)
                  ;
                  await new Promise(resolve=>setTimeout(
                      resolve
                      , duration * multiplier
                  ));
              })
          ;
          // Form instantiation:
          // -------------------
          window.form = new SmarkForm(
              document.querySelector("#main-form")
              , {
                  // Do Something on export action:
                  onAfterAction_export({data}) {
                      alert (JSON.stringify(data, null, 4));
                  },
                  // Ask for confirm on empty action
                  // ...but only if form is not already empty:
                  async onBeforeAction_empty({context, preventDefault}) {
                      if (
                          ! await context.isEmpty()
                          && ! confirm("Are you sure?")
                      ) preventDefault();
                  },
                  // Fetching data on import action (example):
                  onBeforeAction_import(options) {
                      let data = prompt('Provide JSON data');
                      try {
                          options.data = JSON.parse(data);
                      } catch (err) {
                          if (data.length) {
                              alert ('⚠️  Invalid JSON!!');
                              data = null;
                          } else {
                              data = {}; // Drop form contents
                          };
                      };
                      if (data === null) options.preventDefault();
                  },
              }
          );
      
          // List items addition/removal animation:
          // --------------------------------------
          form
              .onAll("addItem", function({
                  newItemTarget,
                  onRendered,
              }) {
                  newItemTarget.classList.add("ingoing");
                  onRendered(()=>{
                      newItemTarget.classList.remove("ingoing")
                      newItemTarget.classList.add("ongoing");
                  });
              })
              .onAll("removeItem", async function({
                  oldItemTarget,
                  onRemmoved,
              }) {
                  oldItemTarget.classList.remove("ongoing");
                  oldItemTarget.classList.add("outgoing");
      
                  // Await for transition to be finished before item removal:
                  const [duration, multiplier = 1000] = window.getComputedStyle(oldItemTarget)
                      .getPropertyValue('transition-duration')
                      .slice(0,-1).replace("m","/1")
                      .split("/")
                      .map(Number)
                  ;
                  await new Promise(resolve=>setTimeout(
                      resolve
                      , duration * multiplier
                  ));
              })
          ;
          // Form instantiation:
          // -------------------
          window.form = new SmarkForm(
              document.querySelector("#main-form")
              , {
                  // Do Something on export action:
                  onAfterAction_export({data}) {
                      alert (JSON.stringify(data, null, 4));
                  },
                  // Ask for confirm on empty action
                  // ...but only if form is not already empty:
                  async onBeforeAction_empty({context, preventDefault}) {
                      if (
                          ! await context.isEmpty()
                          && ! confirm("Are you sure?")
                      ) preventDefault();
                  },
                  // Fetching data on import action (example):
                  onBeforeAction_import(options) {
                      let data = prompt('Provide JSON data');
                      try {
                          options.data = JSON.parse(data);
                      } catch (err) {
                          if (data.length) {
                              alert ('⚠️  Invalid JSON!!');
                              data = null;
                          } else {
                              data = {}; // Drop form contents
                          };
                      };
                      if (data === null) options.preventDefault();
                  },
              }
          );
      
          // List items addition/removal animation:
          // --------------------------------------
          form
              .onAll("addItem", function({
                  newItemTarget,
                  onRendered,
              }) {
                  newItemTarget.classList.add("ingoing");
                  onRendered(()=>{
                      newItemTarget.classList.remove("ingoing")
                      newItemTarget.classList.add("ongoing");
                  });
              })
              .onAll("removeItem", async function({
                  oldItemTarget,
                  onRemmoved,
              }) {
                  oldItemTarget.classList.remove("ongoing");
                  oldItemTarget.classList.add("outgoing");
      
                  // Await for transition to be finished before item removal:
                  const [duration, multiplier = 1000] = window.getComputedStyle(oldItemTarget)
                      .getPropertyValue('transition-duration')
                      .slice(0,-1).replace("m","/1")
                      .split("/")
                      .map(Number)
                  ;
                  await new Promise(resolve=>setTimeout(
                      resolve
                      , duration * multiplier
                  ));
              })
          ;