bootstrap-time 0.1.6-beta → 0.1.7-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -33,8 +33,8 @@ Or load the published files from jsDelivr:
33
33
  ```html
34
34
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@6.0.0-alpha.1/dist/css/bootstrap.min.css">
35
35
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.13.2/font/bootstrap-icons.min.css">
36
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-time@0.1.6-beta/dist/css/bootstrap-time.min.css">
37
- <script src="https://cdn.jsdelivr.net/npm/bootstrap-time@0.1.6-beta/dist/js/bootstrap-time.min.js"></script>
36
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-time@0.1.7-beta/dist/css/bootstrap-time.min.css">
37
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap-time@0.1.7-beta/dist/js/bootstrap-time.min.js"></script>
38
38
  ```
39
39
 
40
40
  Local builds use the same paths under `dist/`:
@@ -44,10 +44,21 @@ Local builds use the same paths under `dist/`:
44
44
  <script src="dist/js/bootstrap-time.min.js"></script>
45
45
  ```
46
46
 
47
- The script initializes the clock when `.clock-picker`, `#clock`, `#time-input`, `#hour-arrow` and `#minute-arrow` are present. The widget overlays the form under the time input. Minute selection follows the input `step`, including when `step` changes: `60` allows every minute by clicking between the numbers, `900` enables only 00, 15, 30 and 45. See `examples/bootstrap-time.html`.
47
+ Add `bs-time` to a time field. The script builds the clock and places it under that field, so it works in an existing Bootstrap form.
48
+
49
+ ```html
50
+ <label class="form-label" for="time">Time</label>
51
+ <input type="time" class="form-control bs-time" id="time" step="60">
52
+ ```
53
+
54
+ Minute selection follows the input `step`, including when `step` changes: `60` allows every minute by clicking between the numbers, `900` enables only 00, 15, 30 and 45. See `examples/bootstrap-time.html`.
48
55
 
49
56
  ## Changelog
50
57
 
58
+ ### 0.1.7-beta
59
+
60
+ - Attach the clock to any field with class `bs-time`.
61
+
51
62
  ### 0.1.6-beta
52
63
 
53
64
  - The example starts on the current time.
@@ -1,6 +1,5 @@
1
1
  .clock-picker {
2
2
  position: relative;
3
- width: 20rem;
4
3
  }
5
4
  .clock-picker input[type=time]::-webkit-calendar-picker-indicator {
6
5
  display: none;
@@ -1 +1 @@
1
- .clock-picker{position:relative;width:20rem}.clock-picker input[type=time]::-webkit-calendar-picker-indicator{display:none;appearance:none}.clock-toggle{pointer-events:auto;width:2.25rem;height:2.25rem;margin:-0.25rem;border:0;border-radius:50%;background:rgba(0,0,0,0);color:var(--bs-form-adorn-icon-color, var(--bs-secondary-color))}.clock-toggle .bi{font-size:1rem;line-height:1}.clock-toggle:hover,.clock-toggle:focus-visible{background-color:var(--bs-bg-3);color:var(--bs-body-color)}.clock-widget{position:absolute;top:100%;left:0;z-index:5;width:20rem;margin-top:.25rem;padding:.25rem .5rem 1rem;background-color:var(--bs-secondary-bg-subtle);color:var(--bs-secondary-fg)}.clock-arrows{display:flex;justify-content:flex-end;gap:.25rem}.btn.clock-arrow{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;min-height:0;padding:0;border-radius:50%;background-color:rgba(0,0,0,0);color:var(--bs-body-color);text-decoration:none}.btn.clock-arrow svg{width:1.5rem;height:1.5rem;fill:currentColor}.btn.clock-arrow:hover,.btn.clock-arrow:focus-visible{background-color:var(--bs-bg-3);color:var(--bs-body-color)}.btn.clock-arrow:disabled,.btn.clock-arrow:disabled:hover{background-color:rgba(0,0,0,0);color:var(--bs-secondary-color);opacity:.45}.clock{width:16rem;height:16rem;border-radius:50%;touch-action:none;cursor:pointer}.clock-hand{position:absolute;left:50%;bottom:50%;width:2px;height:var(--hand);transform-origin:center bottom;transform:translateX(-50%) rotate(var(--angle));transition:transform .3s ease,height .3s ease;border-radius:1px;pointer-events:none}.clock-knob{position:absolute;left:50%;top:0;width:2.25rem;height:2.25rem;border-radius:50%;transform:translate(-50%, -50%)}.clock-number{position:absolute;left:50%;top:50%;z-index:1;width:2.25rem;height:2.25rem;margin:-1.125rem;border:0;border-radius:50%;background:rgba(0,0,0,0);color:inherit;transform:rotate(var(--angle)) translateY(calc(var(--radius) * -1)) rotate(calc(var(--angle) * -1))}.clock-number[aria-pressed=true]{color:var(--bs-white, #fff)}.clock-number:disabled{opacity:.35;cursor:default}
1
+ .clock-picker{position:relative}.clock-picker input[type=time]::-webkit-calendar-picker-indicator{display:none;appearance:none}.clock-toggle{pointer-events:auto;width:2.25rem;height:2.25rem;margin:-0.25rem;border:0;border-radius:50%;background:rgba(0,0,0,0);color:var(--bs-form-adorn-icon-color, var(--bs-secondary-color))}.clock-toggle .bi{font-size:1rem;line-height:1}.clock-toggle:hover,.clock-toggle:focus-visible{background-color:var(--bs-bg-3);color:var(--bs-body-color)}.clock-widget{position:absolute;top:100%;left:0;z-index:5;width:20rem;margin-top:.25rem;padding:.25rem .5rem 1rem;background-color:var(--bs-secondary-bg-subtle);color:var(--bs-secondary-fg)}.clock-arrows{display:flex;justify-content:flex-end;gap:.25rem}.btn.clock-arrow{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;min-height:0;padding:0;border-radius:50%;background-color:rgba(0,0,0,0);color:var(--bs-body-color);text-decoration:none}.btn.clock-arrow svg{width:1.5rem;height:1.5rem;fill:currentColor}.btn.clock-arrow:hover,.btn.clock-arrow:focus-visible{background-color:var(--bs-bg-3);color:var(--bs-body-color)}.btn.clock-arrow:disabled,.btn.clock-arrow:disabled:hover{background-color:rgba(0,0,0,0);color:var(--bs-secondary-color);opacity:.45}.clock{width:16rem;height:16rem;border-radius:50%;touch-action:none;cursor:pointer}.clock-hand{position:absolute;left:50%;bottom:50%;width:2px;height:var(--hand);transform-origin:center bottom;transform:translateX(-50%) rotate(var(--angle));transition:transform .3s ease,height .3s ease;border-radius:1px;pointer-events:none}.clock-knob{position:absolute;left:50%;top:0;width:2.25rem;height:2.25rem;border-radius:50%;transform:translate(-50%, -50%)}.clock-number{position:absolute;left:50%;top:50%;z-index:1;width:2.25rem;height:2.25rem;margin:-1.125rem;border:0;border-radius:50%;background:rgba(0,0,0,0);color:inherit;transform:rotate(var(--angle)) translateY(calc(var(--radius) * -1)) rotate(calc(var(--angle) * -1))}.clock-number[aria-pressed=true]{color:var(--bs-white, #fff)}.clock-number:disabled{opacity:.35;cursor:default}
@@ -7,20 +7,69 @@
7
7
 
8
8
  var INNER_RADIUS = "4.6rem";
9
9
  var OUTER_RADIUS = "6.4rem";
10
+ var LEFT = "M15.41 16.59 10.83 12l4.58-4.59L14 6l-6 6 6 6z";
11
+ var RIGHT = "M8.59 16.59 13.17 12 8.59 7.41 10 6l6 6-6 6z";
10
12
 
11
- function init() {
12
- var picker = document.querySelector(".clock-picker");
13
- var clock = document.querySelector("#clock");
14
- var widget = document.querySelector("#clock-widget");
15
- var timeInput = document.querySelector("#time-input");
16
- var hourArrow = document.querySelector("#hour-arrow");
17
- var minuteArrow = document.querySelector("#minute-arrow");
18
- var clockToggle = document.querySelector("#clock-toggle");
19
- if (!clock || !timeInput || !hourArrow || !minuteArrow) return;
13
+ function svg(path) {
14
+ return '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="' + path + '"></path></svg>';
15
+ }
16
+
17
+ function mount(field) {
18
+ if (field.dataset.bsTime === "ready") return;
19
+ field.dataset.bsTime = "ready";
20
+
21
+ var host = field.closest(".form-control") || field;
22
+ var picker = host.closest(".clock-picker");
23
+ if (!picker) {
24
+ picker = document.createElement("div");
25
+ picker.className = "clock-picker";
26
+ host.parentNode.insertBefore(picker, host);
27
+ picker.append(host);
28
+ }
29
+
30
+ var adorn = field.closest(".form-adorn");
31
+ if (!adorn && field.classList.contains("form-control")) {
32
+ adorn = document.createElement("div");
33
+ adorn.className = "form-control form-adorn form-adorn-end";
34
+ field.parentNode.insertBefore(adorn, field);
35
+ adorn.append(field);
36
+ field.classList.remove("form-control");
37
+ field.classList.add("form-ghost");
38
+ }
39
+
40
+ var toggle = picker.querySelector(".clock-toggle");
41
+ if (!toggle && adorn) {
42
+ toggle = document.createElement("button");
43
+ toggle.type = "button";
44
+ toggle.className = "form-adorn-icon clock-toggle";
45
+ toggle.setAttribute("aria-label", "Klok");
46
+ toggle.setAttribute("aria-pressed", "false");
47
+ toggle.innerHTML = '<i class="bi bi-clock" aria-hidden="true"></i>';
48
+ adorn.insertBefore(toggle, adorn.firstChild);
49
+ }
50
+
51
+ var widget = picker.querySelector(".clock-widget");
52
+ if (!widget) {
53
+ widget = document.createElement("div");
54
+ widget.className = "clock-widget card theme-secondary theme-subtle";
55
+ widget.hidden = true;
56
+ widget.innerHTML =
57
+ '<div class="clock-arrows">' +
58
+ '<button class="btn btn-link clock-arrow clock-hour" type="button" aria-label="Uren">' + svg(LEFT) + "</button>" +
59
+ '<button class="btn btn-link clock-arrow clock-minute" type="button" aria-label="Minuten">' + svg(RIGHT) + "</button>" +
60
+ "</div>" +
61
+ '<div class="clock position-relative mx-auto theme-inverse theme-subtle" role="group" aria-label="Kies een uur"></div>';
62
+ picker.append(widget);
63
+ }
64
+
65
+ var clock = widget.querySelector(".clock");
66
+ var hourArrow = widget.querySelector(".clock-hour");
67
+ var minuteArrow = widget.querySelector(".clock-minute");
68
+ if (!clock || !hourArrow || !minuteArrow) return;
20
69
 
21
70
  var now = new Date();
22
- var initial = timeInput.value
23
- ? timeInput.value.split(":")
71
+ var initial = field.value
72
+ ? field.value.split(":")
24
73
  : [now.getHours(), now.getMinutes()];
25
74
  var hour = Number(initial[0]) || 0;
26
75
  var minute = snapMinute(Number(initial[1]) || 0);
@@ -40,7 +89,7 @@
40
89
  }
41
90
 
42
91
  function minuteStep() {
43
- var step = Number(timeInput.getAttribute("step") || timeInput.step);
92
+ var step = Number(field.getAttribute("step") || field.step);
44
93
  if (!step || step < 60) step = 60;
45
94
  return Math.max(1, Math.round(step / 60));
46
95
  }
@@ -78,7 +127,7 @@
78
127
  }
79
128
 
80
129
  function writeInput() {
81
- timeInput.value = pad(hour) + ":" + pad(minute);
130
+ field.value = pad(hour) + ":" + pad(minute);
82
131
  }
83
132
 
84
133
  function showMode(nextMode) {
@@ -131,19 +180,17 @@
131
180
  }
132
181
 
133
182
  function showWidget() {
134
- if (!widget) return;
135
183
  widget.hidden = false;
136
- if (clockToggle) clockToggle.setAttribute("aria-pressed", "true");
184
+ if (toggle) toggle.setAttribute("aria-pressed", "true");
137
185
  }
138
186
 
139
187
  function hideWidget() {
140
- if (!widget) return;
141
188
  widget.hidden = true;
142
- if (clockToggle) clockToggle.setAttribute("aria-pressed", "false");
189
+ if (toggle) toggle.setAttribute("aria-pressed", "false");
143
190
  }
144
191
 
145
192
  function toggleWidget() {
146
- if (widget && widget.hidden) showWidget();
193
+ if (widget.hidden) showWidget();
147
194
  else hideWidget();
148
195
  }
149
196
 
@@ -178,13 +225,11 @@
178
225
  minuteArrow.addEventListener("click", function () {
179
226
  showMode("minute");
180
227
  });
181
- if (clockToggle) {
182
- clockToggle.addEventListener("click", toggleWidget);
183
- }
184
- timeInput.addEventListener("focus", showWidget);
185
- timeInput.addEventListener("input", function () {
186
- if (!timeInput.value) return;
187
- var parts = timeInput.value.split(":");
228
+ if (toggle) toggle.addEventListener("click", toggleWidget);
229
+ field.addEventListener("focus", showWidget);
230
+ field.addEventListener("input", function () {
231
+ if (!field.value) return;
232
+ var parts = field.value.split(":");
188
233
  var nextHour = Number(parts[0]);
189
234
  var nextMinute = Number(parts[1]);
190
235
  if (nextMinute !== minute) mode = "minute";
@@ -194,48 +239,50 @@
194
239
  updateArrows();
195
240
  renderClock();
196
241
  });
197
- if (picker) {
198
- picker.addEventListener("mousedown", function (event) {
199
- if (event.target === timeInput) return;
200
- if (clockToggle && clockToggle.contains(event.target)) return;
201
- event.preventDefault();
202
- });
203
- }
242
+ picker.addEventListener("mousedown", function (event) {
243
+ if (event.target === field) return;
244
+ if (toggle && toggle.contains(event.target)) return;
245
+ event.preventDefault();
246
+ });
204
247
  document.addEventListener("mousedown", function (event) {
205
- if (!picker || picker.contains(event.target)) return;
248
+ if (picker.contains(event.target)) return;
206
249
  hideWidget();
207
250
  });
208
251
 
209
252
  function applyStep() {
210
253
  minute = snapMinute(minute);
211
- writeInput();
254
+ field.value = pad(hour) + ":" + pad(minute);
212
255
  renderClock();
213
256
  }
214
257
 
215
258
  var stepDescriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "step");
216
259
  if (stepDescriptor && stepDescriptor.set) {
217
- Object.defineProperty(timeInput, "step", {
260
+ Object.defineProperty(field, "step", {
218
261
  configurable: true,
219
262
  get: function () {
220
- return stepDescriptor.get.call(timeInput);
263
+ return stepDescriptor.get.call(field);
221
264
  },
222
265
  set: function (value) {
223
- stepDescriptor.set.call(timeInput, value);
266
+ stepDescriptor.set.call(field, value);
224
267
  applyStep();
225
268
  }
226
269
  });
227
270
  }
228
- new MutationObserver(applyStep).observe(timeInput, {
271
+ new MutationObserver(applyStep).observe(field, {
229
272
  attributes: true,
230
273
  attributeFilter: ["step"]
231
274
  });
232
275
 
233
276
  updateArrows();
234
- writeInput();
277
+ if (!field.value) field.value = pad(hour) + ":" + pad(minute);
235
278
  renderClock();
236
279
  hideWidget();
237
280
  }
238
281
 
282
+ function init() {
283
+ document.querySelectorAll(".bs-time").forEach(mount);
284
+ }
285
+
239
286
  if (document.readyState === "loading") {
240
287
  document.addEventListener("DOMContentLoaded", init);
241
288
  } else {
@@ -1 +1 @@
1
- !function(){"use strict";var e="4.6rem",t="6.4rem";function n(){var n=document.querySelector(".clock-picker"),r=document.querySelector("#clock"),u=document.querySelector("#clock-widget"),o=document.querySelector("#time-input"),i=document.querySelector("#hour-arrow"),a=document.querySelector("#minute-arrow"),c=document.querySelector("#clock-toggle");if(r&&o&&i&&a){var d=new Date,s=o.value?o.value.split(":"):[d.getHours(),d.getMinutes()],l=Number(s[0])||0,m=k(Number(s[1])||0),f="hour",p=150,h=document.createElement("div"),v=document.createElement("span");h.className="clock-hand bg-primary",v.className="clock-knob bg-primary",v.setAttribute("aria-hidden","true"),h.append(v),r.append(h),r.addEventListener("click",function(e){if(!e.target.closest(".clock-number")){var t=function(e){var t=r.getBoundingClientRect(),n=e.clientX-(t.left+t.width/2),u=e.clientY-(t.top+t.height/2),o=(180*Math.atan2(n,-u)/Math.PI+360)%360;if("minute"===f)return k(Math.round(o/6)%60);var i=Math.round(o/30)%12;return Math.hypot(n,u)<.32*t.width?0===i?0:i+12:0===i?12:i}(e);"minute"===f?m=t:(l=t,f="minute"),L(),S(),w()}}),i.addEventListener("click",function(){M("hour")}),a.addEventListener("click",function(){M("minute")}),c&&c.addEventListener("click",function(){u&&u.hidden?q():N()}),o.addEventListener("focus",q),o.addEventListener("input",function(){if(o.value){var e=o.value.split(":"),t=Number(e[0]),n=Number(e[1]);n!==m?f="minute":t!==l&&(f="hour"),l=t,m=n,S(),w()}}),n&&n.addEventListener("mousedown",function(e){e.target!==o&&(c&&c.contains(e.target)||e.preventDefault())}),document.addEventListener("mousedown",function(e){n&&!n.contains(e.target)&&N()});var b=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,"step");b&&b.set&&Object.defineProperty(o,"step",{configurable:!0,get:function(){return b.get.call(o)},set:function(e){b.set.call(o,e),A()}}),new MutationObserver(A).observe(o,{attributes:!0,attributeFilter:["step"]}),S(),L(),w(),N()}function g(e){return String(e).padStart(2,"0")}function y(){var e=Number(o.getAttribute("step")||o.step);return(!e||e<60)&&(e=60),Math.max(1,Math.round(e/60))}function k(e){var t=y();return(Math.round(e/t)*t%60+60)%60}function E(){p+=((("hour"===f?l%12*30:6*m)-p)%360+540)%360-180,h.style.setProperty("--angle",p+"deg"),h.style.setProperty("--hand","hour"===f&&(0===l||l>12)?e:t)}function S(){i.disabled="hour"===f,a.disabled="minute"===f}function L(){o.value=g(l)+":"+g(m)}function M(e){f!==e&&(f=e,S(),w())}function w(){r.querySelectorAll(".clock-number").forEach(function(e){e.remove()}),r.setAttribute("aria-label","hour"===f?"Kies een uur":"Kies de minuten");var n="hour"===f?[12,1,2,3,4,5,6,7,8,9,10,11,0,13,14,15,16,17,18,19,20,21,22,23]:[0,5,10,15,20,25,30,35,40,45,50,55],u="hour"===f?l:m;E(),n.forEach(function(n,o){var i=document.createElement("button"),a="hour"===f&&o>=12,c=30*("hour"===f?o%12:o),d="minute"===f&&!function(e){return e%y()===0}(n);i.type="button",i.className="clock-number",i.textContent=0===n?"00":String(n),i.dataset.value=String(n),i.disabled=d,i.setAttribute("aria-pressed",d||n!==u?"false":"true"),i.style.setProperty("--angle",c+"deg"),i.style.setProperty("--radius",a?e:t),i.addEventListener("click",function(){i.disabled||("hour"===f?(l=n,f="minute"):m=n,L(),S(),w())}),r.append(i)})}function q(){u&&(u.hidden=!1,c&&c.setAttribute("aria-pressed","true"))}function N(){u&&(u.hidden=!0,c&&c.setAttribute("aria-pressed","false"))}function A(){m=k(m),L(),w()}}"loading"===document.readyState?document.addEventListener("DOMContentLoaded",n):n()}();
1
+ !function(){"use strict";var e="4.6rem",t="6.4rem";function n(e){return'<svg viewBox="0 0 24 24" aria-hidden="true"><path d="'+e+'"></path></svg>'}function r(r){if("ready"!==r.dataset.bsTime){r.dataset.bsTime="ready";var o=r.closest(".form-control")||r,a=o.closest(".clock-picker");a||((a=document.createElement("div")).className="clock-picker",o.parentNode.insertBefore(a,o),a.append(o));var i=r.closest(".form-adorn");!i&&r.classList.contains("form-control")&&((i=document.createElement("div")).className="form-control form-adorn form-adorn-end",r.parentNode.insertBefore(i,r),i.append(r),r.classList.remove("form-control"),r.classList.add("form-ghost"));var c=a.querySelector(".clock-toggle");!c&&i&&((c=document.createElement("button")).type="button",c.className="form-adorn-icon clock-toggle",c.setAttribute("aria-label","Klok"),c.setAttribute("aria-pressed","false"),c.innerHTML='<i class="bi bi-clock" aria-hidden="true"></i>',i.insertBefore(c,i.firstChild));var u=a.querySelector(".clock-widget");u||((u=document.createElement("div")).className="clock-widget card theme-secondary theme-subtle",u.hidden=!0,u.innerHTML='<div class="clock-arrows"><button class="btn btn-link clock-arrow clock-hour" type="button" aria-label="Uren">'+n("M15.41 16.59 10.83 12l4.58-4.59L14 6l-6 6 6 6z")+'</button><button class="btn btn-link clock-arrow clock-minute" type="button" aria-label="Minuten">'+n("M8.59 16.59 13.17 12 8.59 7.41 10 6l6 6-6 6z")+'</button></div><div class="clock position-relative mx-auto theme-inverse theme-subtle" role="group" aria-label="Kies een uur"></div>',a.append(u));var s=u.querySelector(".clock"),l=u.querySelector(".clock-hour"),d=u.querySelector(".clock-minute");if(s&&l&&d){var m=new Date,f=r.value?r.value.split(":"):[m.getHours(),m.getMinutes()],b=Number(f[0])||0,p=M(Number(f[1])||0),v="hour",h=150,g=document.createElement("div"),k=document.createElement("span");g.className="clock-hand bg-primary",k.className="clock-knob bg-primary",k.setAttribute("aria-hidden","true"),g.append(k),s.append(g),s.addEventListener("click",function(e){if(!e.target.closest(".clock-number")){var t=function(e){var t=s.getBoundingClientRect(),n=e.clientX-(t.left+t.width/2),r=e.clientY-(t.top+t.height/2),o=(180*Math.atan2(n,-r)/Math.PI+360)%360;if("minute"===v)return M(Math.round(o/6)%60);var a=Math.round(o/30)%12;return Math.hypot(n,r)<.32*t.width?0===a?0:a+12:0===a?12:a}(e);"minute"===v?p=t:(b=t,v="minute"),S(),w(),q()}}),l.addEventListener("click",function(){A("hour")}),d.addEventListener("click",function(){A("minute")}),c&&c.addEventListener("click",function(){u.hidden?P():B()}),r.addEventListener("focus",P),r.addEventListener("input",function(){if(r.value){var e=r.value.split(":"),t=Number(e[0]),n=Number(e[1]);n!==p?v="minute":t!==b&&(v="hour"),b=t,p=n,w(),q()}}),a.addEventListener("mousedown",function(e){e.target!==r&&(c&&c.contains(e.target)||e.preventDefault())}),document.addEventListener("mousedown",function(e){a.contains(e.target)||B()});var y=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,"step");y&&y.set&&Object.defineProperty(r,"step",{configurable:!0,get:function(){return y.get.call(r)},set:function(e){y.set.call(r,e),O()}}),new MutationObserver(O).observe(r,{attributes:!0,attributeFilter:["step"]}),w(),r.value||(r.value=E(b)+":"+E(p)),q(),B()}}function E(e){return String(e).padStart(2,"0")}function L(){var e=Number(r.getAttribute("step")||r.step);return(!e||e<60)&&(e=60),Math.max(1,Math.round(e/60))}function M(e){var t=L();return(Math.round(e/t)*t%60+60)%60}function N(){h+=((("hour"===v?b%12*30:6*p)-h)%360+540)%360-180,g.style.setProperty("--angle",h+"deg"),g.style.setProperty("--hand","hour"===v&&(0===b||b>12)?e:t)}function w(){l.disabled="hour"===v,d.disabled="minute"===v}function S(){r.value=E(b)+":"+E(p)}function A(e){v!==e&&(v=e,w(),q())}function q(){s.querySelectorAll(".clock-number").forEach(function(e){e.remove()}),s.setAttribute("aria-label","hour"===v?"Kies een uur":"Kies de minuten");var n="hour"===v?[12,1,2,3,4,5,6,7,8,9,10,11,0,13,14,15,16,17,18,19,20,21,22,23]:[0,5,10,15,20,25,30,35,40,45,50,55],r="hour"===v?b:p;N(),n.forEach(function(n,o){var a=document.createElement("button"),i="hour"===v&&o>=12,c=30*("hour"===v?o%12:o),u="minute"===v&&!function(e){return e%L()===0}(n);a.type="button",a.className="clock-number",a.textContent=0===n?"00":String(n),a.dataset.value=String(n),a.disabled=u,a.setAttribute("aria-pressed",u||n!==r?"false":"true"),a.style.setProperty("--angle",c+"deg"),a.style.setProperty("--radius",i?e:t),a.addEventListener("click",function(){a.disabled||("hour"===v?(b=n,v="minute"):p=n,S(),w(),q())}),s.append(a)})}function P(){u.hidden=!1,c&&c.setAttribute("aria-pressed","true")}function B(){u.hidden=!0,c&&c.setAttribute("aria-pressed","false")}function O(){p=M(p),r.value=E(b)+":"+E(p),q()}}function o(){document.querySelectorAll(".bs-time").forEach(r)}"loading"===document.readyState?document.addEventListener("DOMContentLoaded",o):o()}();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bootstrap-time",
3
- "version": "0.1.6-beta",
3
+ "version": "0.1.7-beta",
4
4
  "description": "Time picker based on Bootstrap.",
5
5
  "author": "M. de Ridder",
6
6
  "license": "MIT",
@@ -7,20 +7,69 @@
7
7
 
8
8
  var INNER_RADIUS = "4.6rem";
9
9
  var OUTER_RADIUS = "6.4rem";
10
+ var LEFT = "M15.41 16.59 10.83 12l4.58-4.59L14 6l-6 6 6 6z";
11
+ var RIGHT = "M8.59 16.59 13.17 12 8.59 7.41 10 6l6 6-6 6z";
10
12
 
11
- function init() {
12
- var picker = document.querySelector(".clock-picker");
13
- var clock = document.querySelector("#clock");
14
- var widget = document.querySelector("#clock-widget");
15
- var timeInput = document.querySelector("#time-input");
16
- var hourArrow = document.querySelector("#hour-arrow");
17
- var minuteArrow = document.querySelector("#minute-arrow");
18
- var clockToggle = document.querySelector("#clock-toggle");
19
- if (!clock || !timeInput || !hourArrow || !minuteArrow) return;
13
+ function svg(path) {
14
+ return '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="' + path + '"></path></svg>';
15
+ }
16
+
17
+ function mount(field) {
18
+ if (field.dataset.bsTime === "ready") return;
19
+ field.dataset.bsTime = "ready";
20
+
21
+ var host = field.closest(".form-control") || field;
22
+ var picker = host.closest(".clock-picker");
23
+ if (!picker) {
24
+ picker = document.createElement("div");
25
+ picker.className = "clock-picker";
26
+ host.parentNode.insertBefore(picker, host);
27
+ picker.append(host);
28
+ }
29
+
30
+ var adorn = field.closest(".form-adorn");
31
+ if (!adorn && field.classList.contains("form-control")) {
32
+ adorn = document.createElement("div");
33
+ adorn.className = "form-control form-adorn form-adorn-end";
34
+ field.parentNode.insertBefore(adorn, field);
35
+ adorn.append(field);
36
+ field.classList.remove("form-control");
37
+ field.classList.add("form-ghost");
38
+ }
39
+
40
+ var toggle = picker.querySelector(".clock-toggle");
41
+ if (!toggle && adorn) {
42
+ toggle = document.createElement("button");
43
+ toggle.type = "button";
44
+ toggle.className = "form-adorn-icon clock-toggle";
45
+ toggle.setAttribute("aria-label", "Klok");
46
+ toggle.setAttribute("aria-pressed", "false");
47
+ toggle.innerHTML = '<i class="bi bi-clock" aria-hidden="true"></i>';
48
+ adorn.insertBefore(toggle, adorn.firstChild);
49
+ }
50
+
51
+ var widget = picker.querySelector(".clock-widget");
52
+ if (!widget) {
53
+ widget = document.createElement("div");
54
+ widget.className = "clock-widget card theme-secondary theme-subtle";
55
+ widget.hidden = true;
56
+ widget.innerHTML =
57
+ '<div class="clock-arrows">' +
58
+ '<button class="btn btn-link clock-arrow clock-hour" type="button" aria-label="Uren">' + svg(LEFT) + "</button>" +
59
+ '<button class="btn btn-link clock-arrow clock-minute" type="button" aria-label="Minuten">' + svg(RIGHT) + "</button>" +
60
+ "</div>" +
61
+ '<div class="clock position-relative mx-auto theme-inverse theme-subtle" role="group" aria-label="Kies een uur"></div>';
62
+ picker.append(widget);
63
+ }
64
+
65
+ var clock = widget.querySelector(".clock");
66
+ var hourArrow = widget.querySelector(".clock-hour");
67
+ var minuteArrow = widget.querySelector(".clock-minute");
68
+ if (!clock || !hourArrow || !minuteArrow) return;
20
69
 
21
70
  var now = new Date();
22
- var initial = timeInput.value
23
- ? timeInput.value.split(":")
71
+ var initial = field.value
72
+ ? field.value.split(":")
24
73
  : [now.getHours(), now.getMinutes()];
25
74
  var hour = Number(initial[0]) || 0;
26
75
  var minute = snapMinute(Number(initial[1]) || 0);
@@ -40,7 +89,7 @@
40
89
  }
41
90
 
42
91
  function minuteStep() {
43
- var step = Number(timeInput.getAttribute("step") || timeInput.step);
92
+ var step = Number(field.getAttribute("step") || field.step);
44
93
  if (!step || step < 60) step = 60;
45
94
  return Math.max(1, Math.round(step / 60));
46
95
  }
@@ -78,7 +127,7 @@
78
127
  }
79
128
 
80
129
  function writeInput() {
81
- timeInput.value = pad(hour) + ":" + pad(minute);
130
+ field.value = pad(hour) + ":" + pad(minute);
82
131
  }
83
132
 
84
133
  function showMode(nextMode) {
@@ -131,19 +180,17 @@
131
180
  }
132
181
 
133
182
  function showWidget() {
134
- if (!widget) return;
135
183
  widget.hidden = false;
136
- if (clockToggle) clockToggle.setAttribute("aria-pressed", "true");
184
+ if (toggle) toggle.setAttribute("aria-pressed", "true");
137
185
  }
138
186
 
139
187
  function hideWidget() {
140
- if (!widget) return;
141
188
  widget.hidden = true;
142
- if (clockToggle) clockToggle.setAttribute("aria-pressed", "false");
189
+ if (toggle) toggle.setAttribute("aria-pressed", "false");
143
190
  }
144
191
 
145
192
  function toggleWidget() {
146
- if (widget && widget.hidden) showWidget();
193
+ if (widget.hidden) showWidget();
147
194
  else hideWidget();
148
195
  }
149
196
 
@@ -178,13 +225,11 @@
178
225
  minuteArrow.addEventListener("click", function () {
179
226
  showMode("minute");
180
227
  });
181
- if (clockToggle) {
182
- clockToggle.addEventListener("click", toggleWidget);
183
- }
184
- timeInput.addEventListener("focus", showWidget);
185
- timeInput.addEventListener("input", function () {
186
- if (!timeInput.value) return;
187
- var parts = timeInput.value.split(":");
228
+ if (toggle) toggle.addEventListener("click", toggleWidget);
229
+ field.addEventListener("focus", showWidget);
230
+ field.addEventListener("input", function () {
231
+ if (!field.value) return;
232
+ var parts = field.value.split(":");
188
233
  var nextHour = Number(parts[0]);
189
234
  var nextMinute = Number(parts[1]);
190
235
  if (nextMinute !== minute) mode = "minute";
@@ -194,48 +239,50 @@
194
239
  updateArrows();
195
240
  renderClock();
196
241
  });
197
- if (picker) {
198
- picker.addEventListener("mousedown", function (event) {
199
- if (event.target === timeInput) return;
200
- if (clockToggle && clockToggle.contains(event.target)) return;
201
- event.preventDefault();
202
- });
203
- }
242
+ picker.addEventListener("mousedown", function (event) {
243
+ if (event.target === field) return;
244
+ if (toggle && toggle.contains(event.target)) return;
245
+ event.preventDefault();
246
+ });
204
247
  document.addEventListener("mousedown", function (event) {
205
- if (!picker || picker.contains(event.target)) return;
248
+ if (picker.contains(event.target)) return;
206
249
  hideWidget();
207
250
  });
208
251
 
209
252
  function applyStep() {
210
253
  minute = snapMinute(minute);
211
- writeInput();
254
+ field.value = pad(hour) + ":" + pad(minute);
212
255
  renderClock();
213
256
  }
214
257
 
215
258
  var stepDescriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "step");
216
259
  if (stepDescriptor && stepDescriptor.set) {
217
- Object.defineProperty(timeInput, "step", {
260
+ Object.defineProperty(field, "step", {
218
261
  configurable: true,
219
262
  get: function () {
220
- return stepDescriptor.get.call(timeInput);
263
+ return stepDescriptor.get.call(field);
221
264
  },
222
265
  set: function (value) {
223
- stepDescriptor.set.call(timeInput, value);
266
+ stepDescriptor.set.call(field, value);
224
267
  applyStep();
225
268
  }
226
269
  });
227
270
  }
228
- new MutationObserver(applyStep).observe(timeInput, {
271
+ new MutationObserver(applyStep).observe(field, {
229
272
  attributes: true,
230
273
  attributeFilter: ["step"]
231
274
  });
232
275
 
233
276
  updateArrows();
234
- writeInput();
277
+ if (!field.value) field.value = pad(hour) + ":" + pad(minute);
235
278
  renderClock();
236
279
  hideWidget();
237
280
  }
238
281
 
282
+ function init() {
283
+ document.querySelectorAll(".bs-time").forEach(mount);
284
+ }
285
+
239
286
  if (document.readyState === "loading") {
240
287
  document.addEventListener("DOMContentLoaded", init);
241
288
  } else {
@@ -4,7 +4,6 @@
4
4
 
5
5
  .clock-picker {
6
6
  position: relative;
7
- width: 20rem;
8
7
 
9
8
  input[type="time"]::-webkit-calendar-picker-indicator {
10
9
  display: none;