Clocklet

GitHub

An opinionated clock-style vanilla-js timepicker.
See README on GitHub for details.

1. Basic Usage

<input data-clocklet>
<input data-clocklet value="12:34">
<input data-clocklet="format: h:mm a" value="1:23 pm">
CodePen

2. Inline (no popup)

without input element

with input element

<div style="display: flex">
  <div>
    <h3>without input element</h3>
    <div id="clocklet-inline-container"></div>
    <script>
      const element = document.getElementById("clocklet-inline-container");
      clocklet.inline(element).value("14:45");
      element.addEventListener("input", function (event) {
        const time = event.detail.time;
        console.log(time.format("hh:mm a"), time);
      });
    </script>
  </div>

  <div>
    <h3>with input element</h3>
    <div id="clocklet-inline-with-input-container"></div>
    <input id="clocklet-inline-with-input-input">
  </div>
  <script>
    clocklet.inline(
      document.getElementById("clocklet-inline-with-input-container"),
      {
        input: document.getElementById("clocklet-inline-with-input-input"),
        format: "_H:mm",
      }
    );
  </script>
</div>

3. Options

<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Changa|Frank+Ruhl+Libre:500">

<style>
  .clocklet-options-1 {
    font-family: Changa, sans-serif;
    border-radius: 50%;
  }
</style>
<input
  data-clocklet="class-name: clocklet-options-1; format: h:mm AA; alignment: center; placement: top;"
  placeholder="h:mm AA"
>

<style>
  .clocklet-options-2 {
    font-family: "Frank Ruhl Libre", serif;
    font-weight: 500;
  }
</style>
<input
  data-clocklet="class-name: clocklet-options-2; format: hh:mm a; alignment: right; append-to: parent;"
  placeholder="hh:mm a"
>

4. Events

<input class="clocklet-events" data-clocklet="format: _H:_m;" maxlength="5" value=" 1:23">

<label><input class="clocklet-cancel-opening" type="checkbox">Cancel opening</label>
<label><input class="clocklet-cancel-closing" type="checkbox">Cancel closing</label>

<script>
  (function () {
    var inputElement = document.querySelector('.clocklet-events');

    inputElement.addEventListener('clocklet.opening', function (event) {
      console.log(event.type, event.target.value, event.detail.options);
      if (document.querySelector('.clocklet-cancel-opening').checked) {
        event.preventDefault();
      }
    });

    inputElement.addEventListener('clocklet.opened', function (event) {
      console.log(event.type, event.target.value, event.detail.options);
    });

    inputElement.addEventListener('clocklet.closing', function (event) {
      console.log(event.type, event.target.value);
      if (document.querySelector('.clocklet-cancel-closing').checked) {
        event.preventDefault();
      }
    });

    inputElement.addEventListener('clocklet.closed', function (event) {
      console.log(event.type, event.target.value);
    });

    inputElement.addEventListener('input', function (event) {
      console.log(event.type, event.target.value, event.target.value);
    });
  })();
</script>

5. Colors

<style>
  .clocklet-color-example { background-color: #dce1e4; border: none; }

  /* minute - dial, hand, selected tick, hovered tick */
  .clocklet-color-example .clocklet-dial--minute { background-color: #fcf0e8; }
  .clocklet-color-example .clocklet-hand--minute { background-color: #f5bb95; }
  .clocklet-color-example .clocklet-tick--minute.clocklet-tick--selected { background-color: #f2a470; }
  .clocklet-color-example.clocklet--hoverable:not(.clocklet--dragging) .clocklet-tick--minute:hover { background-color: #f5bb95; }

  /* hour - dial, hand, selected tick, hovered tick */
  .clocklet-color-example .clocklet-dial--hour { background-color: #e9fdf1; }
  .clocklet-color-example .clocklet-hand--hour { background-color: #98f5bd; }
  .clocklet-color-example .clocklet-tick--hour.clocklet-tick--selected { background-color: #44ee88; }
  .clocklet-color-example.clocklet--hoverable:not(.clocklet--dragging) .clocklet-tick--hour:hover { background-color: #98f5bd; }

  /* hand origin */
  .clocklet-color-example .clocklet-hand-origin { background-color: #f1e369; }

  /* ampm */
  .clocklet-color-example .clocklet-ampm::before { background-color: #44eedd; }
  .clocklet-color-example .clocklet-ampm:hover::before { background-color: #97f5ec; }
  .clocklet-color-example .clocklet-ampm[data-clocklet-ampm="pm"]::before { background-color: #dd44ee; }
  .clocklet-color-example .clocklet-ampm[data-clocklet-ampm="pm"]:hover::before { background-color: #eda1f6; }
</style>

<input data-clocklet="class-name: clocklet-color-example; format: hh:mm a" value="02:55 am">

6. Scroll to clocklet on open

<input class="clocklet-scroll-into-view" data-clocklet maxlength="5" value="01:23">

<script>
  document
    .querySelector('.clocklet-scroll-into-view')
    .addEventListener('clocklet.opened', function (event) {
      requestAnimationFrame(function () {
        var inputRect = event.target.getBoundingClientRect();
        var clockletRect = clocklet.root.getBoundingClientRect();
        scroll({
          top:  document.documentElement.scrollTop
              + document.body.scrollTop
              + (event.detail.options.placement === 'top' ? clockletRect.top : inputRect.top)
              - 10,
          left: document.documentElement.scrollLeft
              + document.body.scrollLeft
              + clockletRect.left
              - 10,
          behavior: 'smooth',
        });
      });
    });
</script>