input-duration 3.0.0 → 3.0.2

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
@@ -49,7 +49,11 @@
49
49
 
50
50
  Use `input-duration` when users need to enter a duration rather than a time of day: for example, a countdown, elapsed-time limit, or media offset. Its displayed value uses `HH:mm:ss.SSS` on every locale, without a locale-specific AM/PM presentation, and supports durations greater than 24 hours (currently up to 99 hours) with millisecond values, such as `30:00:00.125`.
51
51
 
52
+ HTML's built-in `<input type="time">` is for choosing a time of day, not entering elapsed time. HTML does not provide a native duration input with a fixed `HH:mm:ss.SSS` format or support for values over 24 hours; this web component fills that gap.
53
+
52
54
  The component supplies its own segmented layout instead of the browser's native time-picker UI, so the displayed format does not change with browser or locale. It is built from native number inputs, however, so details such as the on-screen keyboard and interaction can still vary by device and browser; this is consistency of the value's presentation, not a guarantee of pixel-identical rendering. Milliseconds can be entered as three digits; the input's step controls increment in 10 ms intervals.
55
+
56
+ The control's styles are scoped inside its Shadow DOM, so global styles and resets from Tailwind or other CSS libraries do not leak into its internal layout. The control inherits its font and text color from its surrounding page.
53
57
  <br>
54
58
  <p>
55
59
  <div>
package/docs/index.html CHANGED
@@ -1,196 +1,298 @@
1
- <!DOCTYPE html>
1
+ <!doctype html>
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="utf-8">
5
- <title>JSDoc: Home</title>
6
- <h1> See the input-duration in action</h1>
7
-
8
- <h2>Best HTML5 Option</h2>
9
- <input type="time" id="problemTime" step="0.01" value="00:00:00.000">
10
-
11
- <h2>Better Option</h2>
12
- <input-duration id="bob"></input-duration>
13
-
14
- <h1>More uses of input-duration</h1>
15
- <input-duration id="bobPrepop" value="99:09:09.999"></input-duration><br>
16
- <input-duration id="Jane"></input-duration><br>
17
- <input type="date" id="BobsDate"><br>
18
-
19
- <h2>Test the prototype</h2>
20
- <p id="what">This is for testing HTMLelement prototype update</p>
21
-
22
- <script type="module">
23
- import chronlyhms from 'https://cdn.jsdelivr.net/npm/input-duration/+esm';
24
- </script>
25
-
26
- <script src="scripts/prettify/prettify.js"> </script>
27
- <script src="scripts/prettify/lang-css.js"> </script>
28
- <!--[if lt IE 9]>
29
- <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
30
- <![endif]-->
31
- <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
32
- <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <meta name="description" content="A tiny demo of why a time-of-day input is not the same thing as a duration input.">
7
+ <title>Time is not a duration — input-duration</title>
8
+ <style>
9
+ :root {
10
+ color-scheme: light;
11
+ font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
12
+ color: #172b45;
13
+ background: #eef6ff;
14
+ font-synthesis: none;
15
+ text-rendering: optimizeLegibility;
16
+ --blue: #2563a6;
17
+ --muted: #536b84;
18
+ --line: #d2e2f2;
19
+ }
20
+
21
+ * {
22
+ box-sizing: border-box;
23
+ }
24
+
25
+ body {
26
+ min-width: 320px;
27
+ margin: 0;
28
+ }
29
+
30
+ main {
31
+ width: min(100% - 40px, 960px);
32
+ margin: 0 auto;
33
+ padding: 48px 0 32px;
34
+ }
35
+
36
+ header {
37
+ max-width: 720px;
38
+ margin: 0 auto 34px;
39
+ text-align: center;
40
+ }
41
+
42
+ .brand {
43
+ display: inline-flex;
44
+ align-items: center;
45
+ gap: 9px;
46
+ margin-bottom: 24px;
47
+ color: var(--blue);
48
+ }
49
+
50
+ .brand-icon {
51
+ width: 30px;
52
+ height: 34px;
53
+ flex: 0 0 auto;
54
+ }
55
+
56
+ .brand-tag {
57
+ padding: 6px 10px;
58
+ border: 1px solid #c8def4;
59
+ border-radius: 8px;
60
+ background: #fff;
61
+ color: #244e7a;
62
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
63
+ font-size: .95rem;
64
+ font-weight: 650;
65
+ letter-spacing: -.04em;
66
+ }
67
+
68
+ .eyebrow {
69
+ margin: 0 0 14px;
70
+ color: var(--blue);
71
+ font-size: .78rem;
72
+ font-weight: 750;
73
+ letter-spacing: .12em;
74
+ text-transform: uppercase;
75
+ }
76
+
77
+ h1 {
78
+ margin: 0;
79
+ font-size: clamp(2.35rem, 7vw, 4.5rem);
80
+ letter-spacing: -.065em;
81
+ line-height: 1.02;
82
+ }
83
+
84
+ .intro {
85
+ max-width: 610px;
86
+ margin: 20px auto 0;
87
+ color: var(--muted);
88
+ font-size: 1.08rem;
89
+ line-height: 1.6;
90
+ }
91
+
92
+ .scenario {
93
+ display: inline-block;
94
+ margin-top: 20px;
95
+ padding: 10px 15px;
96
+ border: 1px solid #c8def4;
97
+ border-radius: 999px;
98
+ background: #e1efff;
99
+ color: #244e7a;
100
+ font-size: .95rem;
101
+ font-weight: 650;
102
+ }
103
+
104
+ .comparison {
105
+ display: grid;
106
+ grid-template-columns: repeat(2, minmax(0, 1fr));
107
+ gap: 18px;
108
+ }
109
+
110
+ article {
111
+ display: flex;
112
+ min-height: 300px;
113
+ flex-direction: column;
114
+ align-items: flex-start;
115
+ padding: 26px;
116
+ border: 1px solid var(--line);
117
+ border-radius: 18px;
118
+ background: #fff;
119
+ box-shadow: 0 12px 32px rgb(35 79 125 / 7%);
120
+ }
121
+
122
+ article.duration {
123
+ border-color: #a9cbed;
124
+ box-shadow: 0 12px 32px rgb(37 99 166 / 12%);
125
+ }
126
+
127
+ .card-label {
128
+ margin: 0 0 8px;
129
+ color: var(--muted);
130
+ font-size: .76rem;
131
+ font-weight: 750;
132
+ letter-spacing: .1em;
133
+ text-transform: uppercase;
134
+ }
135
+
136
+ h2 {
137
+ margin: 0;
138
+ font-size: 1.45rem;
139
+ letter-spacing: -.03em;
140
+ }
141
+
142
+ .card-copy {
143
+ min-height: 52px;
144
+ margin: 10px 0 20px;
145
+ color: var(--muted);
146
+ line-height: 1.5;
147
+ }
148
+
149
+ .field {
150
+ display: flex;
151
+ min-height: 54px;
152
+ align-items: center;
153
+ margin: 0 0 18px;
154
+ padding: 8px 12px;
155
+ border: 1px solid #b8cde2;
156
+ border-radius: 10px;
157
+ background: #fff;
158
+ }
159
+
160
+ input[type="time"] {
161
+ width: 100%;
162
+ border: 0;
163
+ background: transparent;
164
+ color: inherit;
165
+ font: inherit;
166
+ font-size: 1.2rem;
167
+ }
168
+
169
+ input[type="time"]:focus-visible {
170
+ outline: 2px solid var(--blue);
171
+ outline-offset: 3px;
172
+ }
173
+
174
+ input-duration {
175
+ display: block;
176
+ max-width: 100%;
177
+ font-size: 1.2rem;
178
+ }
179
+
180
+ .result {
181
+ margin: auto 0 0;
182
+ color: #334c69;
183
+ font-size: .94rem;
184
+ line-height: 1.5;
185
+ }
186
+
187
+ .result strong {
188
+ color: #a23e2c;
189
+ }
190
+
191
+ .duration .result strong {
192
+ color: var(--blue);
193
+ }
194
+
195
+ footer {
196
+ margin-top: 28px;
197
+ color: var(--muted);
198
+ font-size: .92rem;
199
+ line-height: 1.6;
200
+ text-align: center;
201
+ }
202
+
203
+ a {
204
+ color: var(--blue);
205
+ font-weight: 650;
206
+ }
207
+
208
+ @media (max-width: 650px) {
209
+ main {
210
+ width: min(100% - 28px, 480px);
211
+ padding-top: 34px;
212
+ }
213
+
214
+ .comparison {
215
+ grid-template-columns: 1fr;
216
+ }
217
+
218
+ article {
219
+ min-height: 270px;
220
+ padding: 22px;
221
+ }
222
+ }
223
+ </style>
33
224
  </head>
34
-
35
225
  <body>
36
-
37
- <div id="main">
38
-
39
- <h1 class="page-title">Plugin Doccumentation</h1>
40
-
41
-
42
-
43
-
44
-
45
-
46
-
47
-
48
- <h3> </h3>
49
-
50
-
51
-
52
-
53
-
54
-
55
-
56
-
57
-
58
-
59
-
60
-
61
-
62
-
63
-
64
-
65
-
66
-
67
-
68
-
69
-
70
-
71
-
72
- <section>
73
-
74
- <header>
75
-
76
- <h2>index.js</h2>
77
-
78
-
79
- </header>
80
-
81
- <article>
82
- <div class="container-overview">
83
-
84
-
85
- <div class="description">INPUT-DURATION</div>
86
-
87
-
88
-
89
-
90
-
91
- <dl class="details">
92
-
93
-
94
-
95
-
96
-
97
-
98
-
99
-
100
-
101
-
102
-
103
-
104
-
105
-
106
-
107
-
108
-
109
-
110
- <dt class="tag-author">Author:</dt>
111
- <dd class="tag-author">
112
- <ul>
113
- <li>Stephan Luis</li>
114
- </ul>
115
- </dd>
116
-
117
-
118
-
119
- <dt class="tag-copyright">Copyright:</dt>
120
- <dd class="tag-copyright"><ul class="dummy"><li>MIT License, Copyright (c) 2022</li></ul></dd>
121
-
122
-
123
-
124
-
125
-
126
-
127
-
128
- <dt class="tag-source">Source:</dt>
129
- <dd class="tag-source"><ul class="dummy"><li>
130
- <a href="index.js.html">index.js</a>, <a href="index.js.html#line1">line 1</a>
131
- </li></ul></dd>
132
-
133
-
134
-
135
-
136
-
137
- <dt class="tag-see">See:</dt>
138
- <dd class="tag-see">
139
- <ul>
140
- <li><a href="https://www.npmjs.com/package/input-duration">NPM input-duration</a></li>
141
-
142
- <li><a href="https://github.com/StephanLuis/input-duration">GitHub input-duration</a></li>
143
- </ul>
144
- </dd>
145
-
146
-
147
-
148
- </dl>
149
-
150
-
151
-
152
-
153
- </div>
154
-
155
-
156
-
157
-
158
-
159
-
160
-
161
-
162
-
163
-
164
-
165
-
166
-
167
-
168
-
169
-
170
-
171
-
172
-
173
-
174
- </article>
175
-
176
- </section>
177
-
178
-
179
-
180
-
181
- </div>
182
-
183
- <nav>
184
- <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="InputDuration.html">InputDuration</a></li></ul>
185
- </nav>
186
-
187
- <br class="clear">
188
-
189
- <footer>
190
- Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.2</a> on Fri Sep 29 2023 11:02:59 GMT+0000 (GMT)
191
- </footer>
192
-
193
- <script> prettyPrint(); </script>
194
- <script src="scripts/linenumber.js"> </script>
226
+ <main>
227
+ <header>
228
+ <div class="brand" role="img" aria-label="input-duration">
229
+ <svg class="brand-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" aria-hidden="true">
230
+ <path fill="currentColor" d="M393.9 184l22.6-22.6c4.7-4.7 4.7-12.3 0-17l-17-17c-4.7-4.7-12.3-4.7-17 0l-20.7 20.7c-31.1-27.5-70.4-45.9-113.8-50.8V48h28c6.6 0 12-5.4 12-12V12c0-6.6-5.4-12-12-12H172c-6.6 0-12 5.4-12 12v24c0 6.6 5.4 12 12 12h28v49.4C96.4 109.3 16 197.2 16 304c0 114.9 93.1 208 208 208s208-93.1 208-208c0-44.7-14.1-86.1-38.1-120zM224 464c-88.4 0-160-71.6-160-160s71.6-160 160-160 160 71.6 160 160-71.6 160-160 160zm12-112h-24c-6.6 0-12-5.4-12-12V204c0-6.6 5.4-12 12-12h24c6.6 0 12 5.4 12 12v136c0 6.6-5.4 12-12 12z"/>
231
+ </svg>
232
+ <span class="brand-tag">&lt;input-duration&gt;</span>
233
+ </div>
234
+ <p class="eyebrow">A small input, solves a very big problem</p>
235
+ <h1>Time is not a duration.</h1>
236
+ <p class="intro">You need to set a maintenance window. It’ll take 30 hours and 42 milliseconds. Surely a time picker can handle that?</p>
237
+ <p class="scenario">The task: run X for 30 hours and 42 milliseconds</p>
238
+ </header>
239
+
240
+ <section class="comparison" aria-label="Compare a time-of-day input with a duration input">
241
+ <article>
242
+ <p class="card-label">The problem</p>
243
+ <h2>&lt;input type="time"&gt;</h2>
244
+ <p class="card-copy">Native HTML inputs are for times of day . Try setting this to 30 hours and 42 milliseconds. A time picker only accepts a time of day.</p>
245
+ <label class="field" for="clock-time">
246
+ <input id="clock-time" type="time" value="06:00" step="60">
247
+ </label>
248
+ <p class="result" id="clock-result" aria-live="polite">06:00 is a time of day—not how long the task takes.</p>
249
+ </article>
250
+
251
+ <article class="duration">
252
+ <p class="card-label">The solution</p>
253
+ <h2>&lt;input-duration&gt;</h2>
254
+ <p class="card-copy">HTML has no built-in duration input. This web component handles 30 hours, plus those cheeky 42 milliseconds.</p>
255
+ <input-duration id="duration" role="group" aria-label="Duration in hours, minutes, seconds, and milliseconds" value="30:00:00.042"></input-duration>
256
+ <p class="result" id="duration-result" aria-live="polite"><strong>30:00:00.042</strong> — 30 hours, 42 milliseconds. No clock math required.</p>
257
+ </article>
258
+ </section>
259
+
260
+ <footer>
261
+ Native HTML time inputs are for times of day; HTML has no built-in control for entering a duration like 30:00:00.042.
262
+ That's the gap <a href="InputDuration.html">input-duration</a> fills. Read the
263
+ <a href="https://github.com/StephanLuis/input-duration">source on GitHub</a>.
264
+ </footer>
265
+ </main>
266
+ <script type="module">
267
+ import 'https://cdn.jsdelivr.net/npm/input-duration@3.0.1/+esm';
268
+
269
+ const clockInput = document.querySelector('#clock-time');
270
+ const clockResult = document.querySelector('#clock-result');
271
+ const durationInput = document.querySelector('#duration');
272
+ const durationResult = document.querySelector('#duration-result');
273
+
274
+ clockInput.addEventListener('input', () => {
275
+ clockResult.textContent = clockInput.value
276
+ ? `${clockInput.value} is a time of day—not how long the task takes.`
277
+ : 'No time picked. Still not a duration.';
278
+ });
279
+
280
+ const updateDurationResult = () => {
281
+ const value = durationInput.value;
282
+ if (/^[0-9]{2}:[0-5][0-9]:[0-5][0-9]\.[0-9]{3}$/.test(value)) {
283
+ const formattedValue = document.createElement('strong');
284
+ formattedValue.textContent = value;
285
+ durationResult.replaceChildren(
286
+ formattedValue,
287
+ document.createTextNode(' — duration, not time of day.')
288
+ );
289
+ } else {
290
+ durationResult.textContent = 'Keep going—finish entering the duration.';
291
+ }
292
+ };
293
+
294
+ durationInput.addEventListener('input', updateDurationResult);
295
+ durationInput.addEventListener('change', updateDurationResult);
296
+ </script>
195
297
  </body>
196
298
  </html>
package/index.js CHANGED
@@ -256,82 +256,89 @@ class InputDuration extends HTMLElement {
256
256
  </svg>
257
257
  </div>
258
258
  <style>
259
- *:focus {
260
- outline: none;
259
+ :host {
260
+ display: inline-block;
261
+ color: inherit;
262
+ font: inherit;
263
+ vertical-align: middle;
261
264
  }
262
265
 
263
-
264
- div.timeCase{
265
- display: inline-flex;
266
- flex-direction: row;
267
- max-width: 300px;
268
- padding: 3px;
269
- border-style: solid;
270
- border-width: 1px;
271
- border-radius: 2px;
266
+ .timeCase {
267
+ display: inline-flex;
268
+ align-items: center;
269
+ gap: 0.08em;
270
+ max-width: 100%;
271
+ padding: 0.4em 0.55em;
272
+ border: 1px solid #9aa6b2;
273
+ border-radius: 0.4em;
274
+ background: transparent;
275
+ color: inherit;
276
+ font-variant-numeric: tabular-nums;
277
+ line-height: 1.4;
278
+ white-space: nowrap;
272
279
  }
273
280
 
274
-
275
- /* Each input width set and blinking cursor 'caret' removed */
276
- div.timeCase input{
277
- caret-color: transparent;
278
- width: 15px;
279
- padding-top:1px;
281
+ .timeCase:focus-within {
282
+ border-color: #176b4a;
283
+ outline: 3px solid #176b4a;
284
+ outline-offset: 2px;
280
285
  }
281
286
 
282
-
283
- div.timeCase .D3{
284
- width: 25px;
287
+ .timeCase input {
288
+ box-sizing: content-box;
289
+ width: 2ch;
290
+ min-width: 2ch;
291
+ margin: 0;
292
+ padding: 0;
293
+ border: 0;
294
+ outline: 0;
295
+ background: transparent;
296
+ color: inherit;
297
+ font: inherit;
298
+ font-variant-numeric: tabular-nums;
299
+ text-align: center;
300
+ caret-color: transparent;
301
+ appearance: textfield;
285
302
  }
286
303
 
287
-
288
- /* Hiding the up and down arrow */
289
- /*https://www.w3schools.com/howto/howto_css_hide_arrow_number.asp*/
290
- /* Chrome, Safari, Edge, Opera */
291
-
292
- input::-webkit-outer-spin-button,
293
- input::-webkit-inner-spin-button {
294
- -webkit-appearance: none;
295
- margin: 0;
304
+ .timeCase input.D3 {
305
+ width: 3ch;
306
+ min-width: 3ch;
296
307
  }
297
308
 
298
-
299
- /* Firefox */
300
-
301
- input[type=number] {
302
- /* -moz-appearance: textfield; */
303
- -moz-appearance: textarea;
304
- -webkit-appearance: textarea;
309
+ .timeCase input::-webkit-outer-spin-button,
310
+ .timeCase input::-webkit-inner-spin-button {
311
+ margin: 0;
312
+ appearance: none;
305
313
  }
306
314
 
307
-
308
- div.timeCase:focus-within {
309
- border: 2px solid black;
310
- padding: 2px;
315
+ .timeCase span {
316
+ color: currentColor;
317
+ opacity: 0.7;
311
318
  }
312
319
 
320
+ #svgContainer {
321
+ display: inline-flex;
322
+ width: 1em;
323
+ height: 1em;
324
+ flex: 0 0 auto;
325
+ align-items: center;
326
+ justify-content: center;
327
+ margin-left: 0.2em;
328
+ color: inherit;
329
+ }
313
330
 
314
- .ts_digit {
315
- border: none;
316
- }
317
-
318
- /* between digit span */
319
- .bds{
320
- padding-bottom: 1px;
331
+ #svgContainer svg {
332
+ display: block;
333
+ width: 100%;
334
+ height: 100%;
321
335
  }
322
336
 
323
- #svgContainer {
324
- width: 12px;
325
- height: 12px;
326
- padding-top: 1px;
327
- padding-right: 2px;
328
- color: black;
329
- }
330
-
331
- *:disabled {
332
- background-color: grey;
333
- color: lightgrey;
334
- opacity: .6;
337
+ .timeCase input:disabled {
338
+ background: transparent;
339
+ color: GrayText;
340
+ cursor: not-allowed;
341
+ opacity: 0.65;
335
342
  }
336
343
  </style> `
337
344
 
@@ -410,7 +417,7 @@ class InputDuration extends HTMLElement {
410
417
 
411
418
 
412
419
  /**
413
- * eventlistener that adds number input for keyed values triggered by the keyup event
420
+ * eventlistener that filters number input and advances between fields
414
421
  * @method
415
422
  * @see boot()
416
423
  */
@@ -432,12 +439,6 @@ class InputDuration extends HTMLElement {
432
439
  this.value = this.oldValue;
433
440
  }
434
441
  });
435
-
436
- inputElement.addEventListener('keyup', function (event) {
437
- if (/^[0-9]$/.test(event.key)) {
438
- inputFilter(this.value);
439
- }
440
- });
441
442
  }
442
443
 
443
444
  // three filters, hours or minutes and seconds (are the same) but milliseconds is different
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "input-duration",
3
- "version": "3.0.0",
3
+ "version": "3.0.2",
4
4
  "author": "Stephan Luis <stephan@learnsense.org>",
5
5
  "description": "A JavaScript web component for entering durations up to 99 hours in a fixed HH:mm:ss.SSS format, with millisecond precision for countdown timers and elapsed time.",
6
6
  "browser": "./index.js",