input-duration 2.2.27 → 3.0.1

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.
@@ -1,7 +1,7 @@
1
1
  # manually triggered
2
2
  # triggers specific workflow in curl enpoint (that workflow needs workflow_disptach)
3
3
 
4
- name: ManualTrigger Echo0
4
+ name: ManualTrigger0
5
5
 
6
6
  # Controls when the action will run. Workflow runs when manually triggered using the UI
7
7
  # or API.
package/README.md CHANGED
@@ -4,6 +4,8 @@
4
4
  *** Thanks for checking out the Best-README-Template. If you have a suggestion
5
5
  *** that would make this better, please fork the repo and create a pull request
6
6
  *** or simply open an issue with the tag "enhancement".
7
+ *** Don't forget to give the project a star!
8
+ *** Thanks again! Now go create something AMAZING! :D
7
9
  -->
8
10
 
9
11
 
@@ -17,16 +19,6 @@
17
19
  *** https://www.markdownguide.org/basic-syntax/#reference-style-links
18
20
  -->
19
21
 
20
- Live BrowserStack Test Results:
21
-
22
- ![example workflow](https://github.com/StephanLuis/BrowserStackSelenium/workflows/curlEcho1/badge.svg)
23
-
24
- ![ContinuousIntegration](https://github.com/StephanLuis/BrowserStackSelenium/workflows/InputDurationTests/badge.svg)
25
-
26
- - this can be updated with https://shields.io/
27
-
28
- Remove these:
29
-
30
22
  [![npm bundle size][npm-bundle-size]][npm-bundle-size]
31
23
  [![Contributors][contributors-shield]][contributors-url]
32
24
  [![Forks][forks-shield]][forks-url]
@@ -47,15 +39,22 @@ Remove these:
47
39
 
48
40
 
49
41
  <a href="https://github.com/StephanLuis/input-duration">
50
- <h3 align="center">Input-Duration -- The best 'Time Only' Input (Vanilla JS)</h3> <img src="readme/logo.svg" alt="Logo" width="90" height="90">
42
+ <h3 align="center">Input-Duration -- JavaScript duration input</h3> <img src="readme/logo.svg" alt="Logo" width="90" height="90">
51
43
  </a>
52
44
 
53
45
 
54
46
 
55
- <p align="center">A vanilla JS boosted input that works on Safari in very close imitation of a time input on Chrome <br>... only better. </p>
47
+ <p align="center">A compact duration input with a fixed, locale-independent display for hours, minutes, seconds, and milliseconds.</p>
56
48
  </div>
57
49
 
58
- 'Time Only' means that this control is for time as a unit, or a duration, like for count down timers. Not for time of day nor date on a calendar, so no unwanted AM/PM conversions on Safari, no unwanted date-time selectors. Just time up to 999 hrs and down to 1 millisecond.<br>
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
+
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
+
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.
57
+ <br>
59
58
  <p>
60
59
  <div>
61
60
  </div>
@@ -65,6 +64,8 @@ Remove these:
65
64
  </a> ·
66
65
  <a href="https://6mxpg2.csb.app/" target="_blank">Demo</a>
67
66
  ·
67
+ <a href="https://stephanluis.github.io/input-duration/" target="_blank">Live demo</a>
68
+ ·
68
69
  <a href="https://stephanluis.github.io/input-duration/" target="_blank">Docs</a>
69
70
  ·
70
71
  <a href="https://github.com/StephanLuis/input-duration/issues">Bugs</a>
@@ -72,17 +73,28 @@ Remove these:
72
73
  <a href="https://github.com/StephanLuis/input-duration/issues">Request Feature</a>
73
74
  </p>
74
75
  </div>
75
- Ok, what about mobile?? Yep, input-duration works on all popular operating system / browser combinations.
76
- <br/>
77
76
  </div>
78
77
  <br/>
79
78
 
80
- <h2>Touch and Keyboard Timepicker</h2>
79
+ <h2>Fixed-format duration input</h2>
80
+
81
+ `input-duration` is intended for touch and keyboard use. The HTML structure and displayed field order are controlled by the component; device-specific input and keyboard behavior remains native to the browser.
81
82
 
82
- |OS(mobile) | Browser | | OS(laptop) | Browser |
83
- |-----------------|----------------|--|-------------|-----------|
84
- | Android | Chrome | |Windows 10, 11 | Chrome, Edge |
85
- | iOS | Safari, Chrome | |OsX | Safari, Chrome |
83
+ <h2>Bob is rescued by INPUT-DURATION!</h2>
84
+
85
+ The goal is a small JavaScript duration input that gives countdowns and elapsed-time entry a predictable displayed format across browser and locale settings, while retaining native numeric inputs for editing.
86
+
87
+ <i>Does this lose any functionality?</i> No! `document.querySelector('#bob').value` has a getter and setter, so values can be set and retrieved from JavaScript or the HTML attribute.
88
+
89
+ <img src="readme/safariBad.png" alt="Example comparing a native time input with input-duration" width="80%"/>
90
+
91
+ Native time inputs are useful when the user is choosing a time on a clock. A duration is different: `30:00:00.125` means thirty hours, not 6 AM the next day. Keeping those concepts separate avoids date, timezone, and 12/24-hour display concerns when collecting a duration.
92
+
93
+ Here's why:
94
+ * Enter durations independently of a calendar date or time of day.
95
+ * Keep a fixed `HH:mm:ss.SSS` display regardless of locale-specific clock conventions.
96
+ * Represent durations beyond 24 hours (up to 99 hours in the current implementation).
97
+ * Read and set the value from JavaScript using the component's `.value` property.
86
98
 
87
99
  <!-- TABLE OF CONTENTS -->
88
100
  <details>
@@ -103,7 +115,7 @@ Remove these:
103
115
  </li>
104
116
  <li><a href="#usage">Usage</a></li>
105
117
  <li><a href="#roadmap">Roadmap</a></li>
106
- <li><a href="#teststatus">Test Status</a></li>
118
+ <li><a href="#cross-browser-testing">Cross-browser testing</a></li>
107
119
  <li><a href="#contributing">Contributing</a></li>
108
120
  <li><a href="#license">License</a></li>
109
121
  <li><a href="#contact">Contact</a></li>
@@ -115,50 +127,16 @@ Remove these:
115
127
 
116
128
  <!-- ABOUT THE PROJECT -->
117
129
  ## About The Project
118
- Time inputs <input type='time'> are updated by browsers in unwanted ways! Safari on desktop adds AM/PM if the user is in the USA and on mobile the cool scroll time selector does not allow fractions of a second. The best html5 option doesn't cut it, sadly, so input-duration was designed and buit for a consistant UX no matter the browser, OS, or location settings.
130
+
131
+ HTML's `<input type="time">` represents a time of day. Browsers may localize its visible control (for example, showing a 12-hour clock with AM/PM), even though its value is standardized. It also cannot represent a duration longer than a day.
132
+
133
+ `input-duration` is for duration values, not appointments or clock times. Its separate numeric fields present hours, minutes, seconds, and milliseconds in a fixed `HH:mm:ss.SSS` format. This avoids locale-dependent time-of-day presentation while allowing durations beyond 24 hours and millisecond values in a compact control.
119
134
 
120
135
  <!-- [![Product Name Screen Shot][product-screenshot]](https://example.com) -->
121
136
 
122
137
  <img src="readme/codeDrawing.png" alt="drawing" width="60%"/>
123
138
 
124
139
  <!-- [![HTML Screen Shot][html-screenshot]](https://example.com) -->
125
- *note input-duration needs closing tag!
126
-
127
- <h3> Bob is rescued by INPUT-DURATION! </h3>
128
-
129
- The objective is a minimised javascript plugin that reproduces the behaviour of the input type=time on Chrome. This way at least two of the major browsers are supported, Safari and Chrome, on desktop and mobile operating systems.
130
-
131
- <i>Does this loose any functionality?</i> No! document.querySelector('#bob').value has a getter and setter! So values can be set and retrieved by ID or attribute in HTML.
132
-
133
-
134
-
135
- <img src="readme/safariBad.png" alt="drawing" width="80%"/>
136
- <!-- [![Safari Screen Shot][safari-screenshot]](https://example.com) -->
137
-
138
- Yes! It seems CRAZY in 2022 that there is not a native 'time only' input for HTML. What I mean is there is no user input control for time rendered independantly of date or time of day. Yea, crazy, but now input-duration is a javascript plugin that creates a UX input control for a value of time simply as hours, minutes, seconds --<b>only</b>! <br> Ever thought, why is it difficult to 'code' a countdown timer? Why can't it be done with one control? Just look at google's timer and other coundown timers! All use multiple inputs, usually designed for integers.
139
-
140
- The very worst is Safari on desktop and mobile, which undermine any input type=time coding efforts and update the input based on OS settings! Shouldn't a user be able to 'scientifically' specify a period of time with one control? <br> Maybe it's just me, but the chrome / google timer is 14 separate elements ... those guys must be paid by the hour. The closest HTML5 input type- time when configured with UX for hours to millisecs is very finicky and is rendered differently on Chrome than Safari. <b>Mobile</b> is different from laptop<br> ... so I've simplified that!
141
-
142
-
143
-
144
-
145
-
146
-
147
-
148
- There were a few plugins that remediated the lack of continuity, but even those controls treated time as a date (ex 24hr max). So it seems the current state is the most relable UX is multi input type number. The worst example is the Google countdown timer. That way is wrong because time is a basic unit that should be separate from calendar complexities. That got me thinking, why isn't there an input control for it implemented across browsers?? I needed one, so coded input-duration.
149
-
150
- Here's why:
151
- * Time is hours minutes and seconds (and millisecs) and needn't be associated with part of a day or a date
152
- * The date association breaks UX and is varied with browser implementation
153
- * I found no other crossbrowser solution
154
- * I thought this would be a great open source project
155
- * End use of number inputs (standard hack) for use with time
156
-
157
- Of course, this is functional but in it's infancy. Maybe, with some contribution this is a project that can inspire change to the HTML spec.
158
-
159
-
160
- Feel free to vote for WhatWG inclusion of a into the HTML spec! (link comming soon)
161
-
162
140
  <p align="right">(<a href="#top">back to top</a>)</p>
163
141
 
164
142
 
@@ -279,14 +257,9 @@ See the [open issues](https://github.com/StephanLuis/input-duration/issues) for
279
257
  <p align="right">(<a href="#top">back to top</a>)</p>
280
258
 
281
259
  <!-- TESTSTATUS -->
282
- ## Teststatus
260
+ ## Cross-browser testing
283
261
 
284
- ![TestStatusDIY](https://img.shields.io/static/v1?label=Android:Chrome&message=Pass&color=<COLOR>&style=for-the-badge)
285
- ![TestStatusDIY](https://img.shields.io/static/v1?label=iOS:Safari&message=Pass&color=<COLOR>&style=for-the-badge)
286
- ![TestStatusDIY](https://img.shields.io/static/v1?label=iOS:Chrome&message=Pass&color=<COLOR>&style=for-the-badge)
287
- ![TestStatusDIY](https://img.shields.io/static/v1?label=Windows:Chrome&message=Pass&color=<COLOR>&style=for-the-badge)
288
- ![TestStatusDIY](https://img.shields.io/static/v1?label=osX:Safari&message=Pass&color=<COLOR>&style=for-the-badge)
289
- ![TestStatusDIY](https://img.shields.io/static/v1?label=osX:Chrome&message=Pass&color=<COLOR>&style=for-the-badge)
262
+ The browser and device results linked below are historical and should not be treated as verification against current browser versions. The local regression suite checks component behavior, but current cross-browser rendering and interaction still need to be tested.
290
263
 
291
264
  [Tests & Live Status](https://github.com/StephanLuis/BrowserStackSelenium/blob/main/input-duration/README.md)
292
265
 
@@ -296,6 +269,8 @@ See the [open issues](https://github.com/StephanLuis/input-duration/issues) for
296
269
 
297
270
  Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.
298
271
 
272
+ Run the local regression tests with `npm test`.
273
+
299
274
  If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement".
300
275
  Don't forget to give the project a star! Thanks again!
301
276
 
@@ -350,8 +325,6 @@ Resources I've found helpful and would like to give credit to:
350
325
  <!-- https://www.markdownguide.org/basic-syntax/#reference-style-links -->
351
326
 
352
327
 
353
-
354
-
355
328
  [contributors-shield]: https://img.shields.io/github/contributors/StephanLuis/input-duration?style=for-the-badge
356
329
  [contributors-url]: https://github.com/StephanLuis/input-duration/graphs/contributors
357
330
  [forks-shield]: https://img.shields.io/github/forks/StephanLuis/input-duration?style=for-the-badge
package/docs/index.html CHANGED
@@ -1,177 +1,266 @@
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
-
7
- <script src="scripts/prettify/prettify.js"> </script>
8
- <script src="scripts/prettify/lang-css.js"> </script>
9
- <!--[if lt IE 9]>
10
- <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
11
- <![endif]-->
12
- <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
13
- <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: #17201c;
13
+ background: #f5f7f3;
14
+ font-synthesis: none;
15
+ text-rendering: optimizeLegibility;
16
+ --green: #176b4a;
17
+ --muted: #64716a;
18
+ --line: #dce4dd;
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
+ .eyebrow {
43
+ margin: 0 0 14px;
44
+ color: var(--green);
45
+ font-size: .78rem;
46
+ font-weight: 750;
47
+ letter-spacing: .12em;
48
+ text-transform: uppercase;
49
+ }
50
+
51
+ h1 {
52
+ margin: 0;
53
+ font-size: clamp(2.35rem, 7vw, 4.5rem);
54
+ letter-spacing: -.065em;
55
+ line-height: 1.02;
56
+ }
57
+
58
+ .intro {
59
+ max-width: 610px;
60
+ margin: 20px auto 0;
61
+ color: var(--muted);
62
+ font-size: 1.08rem;
63
+ line-height: 1.6;
64
+ }
65
+
66
+ .scenario {
67
+ display: inline-block;
68
+ margin-top: 20px;
69
+ padding: 10px 15px;
70
+ border: 1px solid #d6e8dc;
71
+ border-radius: 999px;
72
+ background: #eaf4ed;
73
+ color: #25513b;
74
+ font-size: .95rem;
75
+ font-weight: 650;
76
+ }
77
+
78
+ .comparison {
79
+ display: grid;
80
+ grid-template-columns: repeat(2, minmax(0, 1fr));
81
+ gap: 18px;
82
+ }
83
+
84
+ article {
85
+ display: flex;
86
+ min-height: 300px;
87
+ flex-direction: column;
88
+ align-items: flex-start;
89
+ padding: 26px;
90
+ border: 1px solid var(--line);
91
+ border-radius: 18px;
92
+ background: #fff;
93
+ box-shadow: 0 12px 32px rgb(23 32 28 / 5%);
94
+ }
95
+
96
+ article.duration {
97
+ border-color: #b7d9c4;
98
+ box-shadow: 0 12px 32px rgb(23 107 74 / 9%);
99
+ }
100
+
101
+ .card-label {
102
+ margin: 0 0 8px;
103
+ color: var(--muted);
104
+ font-size: .76rem;
105
+ font-weight: 750;
106
+ letter-spacing: .1em;
107
+ text-transform: uppercase;
108
+ }
109
+
110
+ h2 {
111
+ margin: 0;
112
+ font-size: 1.45rem;
113
+ letter-spacing: -.03em;
114
+ }
115
+
116
+ .card-copy {
117
+ min-height: 52px;
118
+ margin: 10px 0 20px;
119
+ color: var(--muted);
120
+ line-height: 1.5;
121
+ }
122
+
123
+ .field {
124
+ display: flex;
125
+ min-height: 54px;
126
+ align-items: center;
127
+ margin: 0 0 18px;
128
+ padding: 8px 12px;
129
+ border: 1px solid #bdc9c0;
130
+ border-radius: 10px;
131
+ background: #fff;
132
+ }
133
+
134
+ input[type="time"] {
135
+ width: 100%;
136
+ border: 0;
137
+ background: transparent;
138
+ color: inherit;
139
+ font: inherit;
140
+ font-size: 1.2rem;
141
+ }
142
+
143
+ input[type="time"]:focus-visible {
144
+ outline: 2px solid var(--green);
145
+ outline-offset: 3px;
146
+ }
147
+
148
+ input-duration {
149
+ display: block;
150
+ max-width: 100%;
151
+ font-size: 1.2rem;
152
+ }
153
+
154
+ .result {
155
+ margin: auto 0 0;
156
+ color: #33463a;
157
+ font-size: .94rem;
158
+ line-height: 1.5;
159
+ }
160
+
161
+ .result strong {
162
+ color: #a23e2c;
163
+ }
164
+
165
+ .duration .result strong {
166
+ color: var(--green);
167
+ }
168
+
169
+ footer {
170
+ margin-top: 28px;
171
+ color: var(--muted);
172
+ font-size: .92rem;
173
+ line-height: 1.6;
174
+ text-align: center;
175
+ }
176
+
177
+ a {
178
+ color: var(--green);
179
+ font-weight: 650;
180
+ }
181
+
182
+ @media (max-width: 650px) {
183
+ main {
184
+ width: min(100% - 28px, 480px);
185
+ padding-top: 34px;
186
+ }
187
+
188
+ .comparison {
189
+ grid-template-columns: 1fr;
190
+ }
191
+
192
+ article {
193
+ min-height: 270px;
194
+ padding: 22px;
195
+ }
196
+ }
197
+ </style>
14
198
  </head>
15
-
16
199
  <body>
17
-
18
- <div id="main">
19
-
20
- <h1 class="page-title">Home</h1>
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
- <h3> </h3>
30
-
31
-
32
-
33
-
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
43
-
44
-
45
-
46
-
47
-
48
-
49
-
50
-
51
-
52
-
53
- <section>
54
-
55
- <header>
56
-
57
- <h2>index.js</h2>
58
-
59
-
60
- </header>
61
-
62
- <article>
63
- <div class="container-overview">
64
-
65
-
66
- <div class="description">INPUT-DURATION</div>
67
-
68
-
69
-
70
-
71
-
72
- <dl class="details">
73
-
74
-
75
-
76
-
77
-
78
-
79
-
80
-
81
-
82
-
83
-
84
-
85
-
86
-
87
-
88
-
89
-
90
-
91
- <dt class="tag-author">Author:</dt>
92
- <dd class="tag-author">
93
- <ul>
94
- <li>Stephan Luis</li>
95
- </ul>
96
- </dd>
97
-
98
-
99
-
100
- <dt class="tag-copyright">Copyright:</dt>
101
- <dd class="tag-copyright"><ul class="dummy"><li>MIT License, Copyright (c) 2022</li></ul></dd>
102
-
103
-
104
-
105
-
106
-
107
-
108
-
109
- <dt class="tag-source">Source:</dt>
110
- <dd class="tag-source"><ul class="dummy"><li>
111
- <a href="index.js.html">index.js</a>, <a href="index.js.html#line1">line 1</a>
112
- </li></ul></dd>
113
-
114
-
115
-
116
-
117
-
118
- <dt class="tag-see">See:</dt>
119
- <dd class="tag-see">
120
- <ul>
121
- <li><a href="https://www.npmjs.com/package/input-duration">NPM input-duration</a></li>
122
-
123
- <li><a href="https://github.com/StephanLuis/input-duration">GitHub input-duration</a></li>
124
- </ul>
125
- </dd>
126
-
127
-
128
-
129
- </dl>
130
-
131
-
132
-
133
-
134
- </div>
135
-
136
-
137
-
138
-
139
-
140
-
141
-
142
-
143
-
144
-
145
-
146
-
147
-
148
-
149
-
150
-
151
-
152
-
153
-
154
-
155
- </article>
156
-
157
- </section>
158
-
159
-
160
-
161
-
162
- </div>
163
-
164
- <nav>
165
- <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="InputDuration.html">InputDuration</a></li></ul>
166
- </nav>
167
-
168
- <br class="clear">
169
-
170
- <footer>
171
- 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)
172
- </footer>
173
-
174
- <script> prettyPrint(); </script>
175
- <script src="scripts/linenumber.js"> </script>
200
+ <main>
201
+ <header>
202
+ <p class="eyebrow">A small input, a very different question</p>
203
+ <h1>Time is not a duration.</h1>
204
+ <p class="intro">You need to set a maintenance window. It’ll take 30 hours. Surely a time picker can handle that?</p>
205
+ <p class="scenario">The task: run for 30 hours</p>
206
+ </header>
207
+
208
+ <section class="comparison" aria-label="Compare a time-of-day input with a duration input">
209
+ <article>
210
+ <p class="card-label">The time picker</p>
211
+ <h2>“What time is it?”</h2>
212
+ <p class="card-copy">HTML's built-in time input asks for a time of day—not an elapsed duration. The clock has other plans.</p>
213
+ <label class="field" for="clock-time">
214
+ <input id="clock-time" type="time" value="06:00" step="60">
215
+ </label>
216
+ <p class="result" id="clock-result" aria-live="polite">06:00 is a time of day—not how long the task takes.</p>
217
+ </article>
218
+
219
+ <article class="duration">
220
+ <p class="card-label">The duration input</p>
221
+ <h2>“How long does it take?”</h2>
222
+ <p class="card-copy">HTML has no built-in duration input. This one handles 30 hours, with a fixed HH:mm:ss.SSS display.</p>
223
+ <input-duration id="duration" role="group" aria-label="Duration in hours, minutes, seconds, and milliseconds" value="30:00:00.000"></input-duration>
224
+ <p class="result" id="duration-result" aria-live="polite"><strong>30:00:00.000</strong> — a 30-hour duration, exactly as requested.</p>
225
+ </article>
226
+ </section>
227
+
228
+ <footer>
229
+ Native HTML time inputs are for times of day; HTML has no built-in control for entering a duration like 30:00:00.000.
230
+ That's the gap <a href="InputDuration.html">input-duration</a> fills. Read the
231
+ <a href="https://github.com/StephanLuis/input-duration">source on GitHub</a>.
232
+ </footer>
233
+ </main>
234
+ <script type="module">
235
+ import '../index.js';
236
+
237
+ const clockInput = document.querySelector('#clock-time');
238
+ const clockResult = document.querySelector('#clock-result');
239
+ const durationInput = document.querySelector('#duration');
240
+ const durationResult = document.querySelector('#duration-result');
241
+
242
+ clockInput.addEventListener('input', () => {
243
+ clockResult.textContent = clockInput.value
244
+ ? `${clockInput.value} is a time of day—not how long the task takes.`
245
+ : 'No time picked. Still not a duration.';
246
+ });
247
+
248
+ const updateDurationResult = () => {
249
+ const value = durationInput.value;
250
+ if (/^[0-9]{2}:[0-5][0-9]:[0-5][0-9]\.[0-9]{3}$/.test(value)) {
251
+ const formattedValue = document.createElement('strong');
252
+ formattedValue.textContent = value;
253
+ durationResult.replaceChildren(
254
+ formattedValue,
255
+ document.createTextNode(' — a duration, exactly as requested.')
256
+ );
257
+ } else {
258
+ durationResult.textContent = 'Keep going—finish entering the duration.';
259
+ }
260
+ };
261
+
262
+ durationInput.addEventListener('input', updateDurationResult);
263
+ durationInput.addEventListener('change', updateDurationResult);
264
+ </script>
176
265
  </body>
177
- </html>
266
+ </html>
@@ -19,7 +19,7 @@
19
19
 
20
20
  <h1 class="page-title">Source: index.js</h1>
21
21
 
22
-
22
+ <h1>Hi from bob</h1>Hi from bobQ!!!</h1>
23
23
 
24
24
 
25
25
 
package/index.js CHANGED
@@ -6,12 +6,11 @@
6
6
  *
7
7
  * @fileoverview INPUT-DURATION
8
8
  * @file INPUT-DURATION
9
- * @author Stephan Luis stephan@learnsense.org
9
+ * @author Stephan Luis
10
10
  * @see <a href="https://www.npmjs.com/package/input-duration">NPM input-duration</a>
11
11
  * @see <a href="https://github.com/StephanLuis/input-duration">GitHub input-duration</a>
12
- * @copyright MIT License, Copyright (c) 2024
13
- * @version 2.2.XXX (DEV) / auto increment
14
- * fake update to check CI , 16 Dec, 6x
12
+ * @copyright MIT License, Copyright (c) 2022
13
+ * @version 2.2.8 (release)
15
14
  */
16
15
 
17
16
 
@@ -23,21 +22,35 @@
23
22
  */
24
23
 
25
24
  class InputDuration extends HTMLElement {
25
+ static get observedAttributes() {
26
+ return ['value'];
27
+ }
28
+
26
29
  constructor() {
27
30
 
28
31
  super();
29
32
 
30
33
  this.myElement = this.attachShadow({ mode: 'open' });
31
34
 
32
- // needed for setting inputs when value attribute is added to chronlyHMS
33
- // <chronly-hms id="bobPrepop" value="99:09:09.999"></chronly-hms>
34
-
35
- this.value = this.getAttribute('value');
36
-
37
35
  this.boot();
38
36
 
39
37
  } // end constructor
40
38
 
39
+ attributeChangedCallback(name, oldValue, newValue) {
40
+
41
+ if (name !== 'value' || oldValue === newValue) {
42
+ return;
43
+ }
44
+
45
+ if (newValue === null) {
46
+ this.resetInputs();
47
+ } else if (this.isValidValue(newValue)) {
48
+ this.setInputValues(newValue);
49
+ } else {
50
+ this.warnInvalidValue(newValue);
51
+ }
52
+ }
53
+
41
54
 
42
55
  /**
43
56
  * get the input-duration value, pulls values from each input
@@ -75,60 +88,52 @@ class InputDuration extends HTMLElement {
75
88
  set value(val) {
76
89
 
77
90
  // for setting the value in js (like document.querySelector("#bobPrepop").value = "05:05:05.005")
78
- if (val) {
79
-
80
-
81
- console.log(val);
82
-
83
-
84
- if (/(^[0-9][0-9]):([0-5][0-9]):([0-5][0-9])\.([0-9][0-9][0-9]$)/.exec(val)) {
85
-
86
-
87
- // split val
88
-
89
- var timeArray = val.split(/[.:]+/);
90
-
91
- console.log('time array is ' + timeArray[0] + ':' + timeArray[1] + ':' + timeArray[2] + ':' + timeArray[3]);
92
-
93
- console.log('this.id = ' + this.id);
94
-
95
- setTimeout(() => {
96
-
97
- this.myElement.querySelector('input[name="startHours"]').value = timeArray[0];
98
- this.myElement.querySelector('input[name="startMinutes"]').value = timeArray[1];
99
- this.myElement.querySelector('input[name="startSeconds"]').value = timeArray[2];
100
- this.myElement.querySelector('input[name="startMilliSecs"]').value = timeArray[3];
101
-
102
- }, 0);
103
-
104
-
105
- this.setAttribute('value', val);
106
-
107
- }
108
-
109
- else {
91
+ if (!val) {
92
+ this.resetInputs();
93
+ this.removeAttribute('value');
94
+ return;
95
+ }
110
96
 
111
- console.warn(`The specified value ${val} does not conform to the required format. The format is "HH:mm:ss.SSS" where HH is 00-99, mm is 00-59, ss is 00-59, and SSS is 000-999.`);
97
+ if (typeof val !== 'string' || !this.isValidValue(val)) {
98
+ this.warnInvalidValue(val);
99
+ return;
100
+ }
112
101
 
113
- }
102
+ this.setInputValues(val);
114
103
 
115
- // I think this is for empty string to clear value
116
- // gives a False bool and removes value from Webcomponent chronlyHMS
104
+ if (this.getAttribute('value') !== val) {
105
+ this.setAttribute('value', val);
106
+ }
107
+ }
117
108
 
118
- } else {
109
+ isValidValue(val) {
110
+ return typeof val === 'string' &&
111
+ /^[0-9]{2}:[0-5][0-9]:[0-5][0-9]\.[0-9]{3}$/.test(val);
112
+ }
119
113
 
120
- setTimeout(() => {
114
+ setInputValues(val) {
115
+ const timeArray = val.split(/[.:]+/);
116
+ const inputNames = ['startHours', 'startMinutes', 'startSeconds', 'startMilliSecs'];
121
117
 
122
- this.myElement.querySelector('input[name="startHours"]').value = '00';
123
- this.myElement.querySelector('input[name="startMinutes"]').value = '00';
124
- this.myElement.querySelector('input[name="startSeconds"]').value = '00';
125
- this.myElement.querySelector('input[name="startMilliSecs"]').value = '000';
118
+ inputNames.forEach((name, index) => {
119
+ const input = this.myElement.querySelector(`input[name="${name}"]`);
120
+ input.value = timeArray[index];
121
+ input.oldValue = input.value;
122
+ });
123
+ }
126
124
 
127
- }, 0);
125
+ resetInputs() {
126
+ const defaults = ['00', '00', '00', '000'];
127
+ const inputs = this.myElement.querySelectorAll('.timeCase input');
128
128
 
129
- this.removeAttribute('value');
129
+ inputs.forEach((input, index) => {
130
+ input.value = defaults[index];
131
+ input.oldValue = input.value;
132
+ });
133
+ }
130
134
 
131
- }
135
+ warnInvalidValue(val) {
136
+ console.warn(`The specified value ${val} does not conform to the required format. The format is "HH:mm:ss.SSS" where HH is 00-99, mm is 00-59, ss is 00-59, and SSS is 000-999.`);
132
137
  }
133
138
 
134
139
 
@@ -162,10 +167,6 @@ class InputDuration extends HTMLElement {
162
167
 
163
168
  addArrowKeyInput() {
164
169
 
165
- console.log('1 this: ' + this);
166
- console.log('1 this.myElement: ' + this.myElement);
167
- console.log('1 this.myElement active: ' + this.myElement);
168
-
169
170
  var mE = this.myElement;
170
171
 
171
172
  // Left and Right arrow key toggle between HMSmS
@@ -255,82 +256,89 @@ class InputDuration extends HTMLElement {
255
256
  </svg>
256
257
  </div>
257
258
  <style>
258
- *:focus {
259
- outline: none;
259
+ :host {
260
+ display: inline-block;
261
+ color: inherit;
262
+ font: inherit;
263
+ vertical-align: middle;
260
264
  }
261
265
 
262
-
263
- div.timeCase{
264
- display: inline-flex;
265
- flex-direction: row;
266
- max-width: 300px;
267
- padding: 3px;
268
- border-style: solid;
269
- border-width: 1px;
270
- 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;
271
279
  }
272
280
 
273
-
274
- /* Each input width set and blinking cursor 'caret' removed */
275
- div.timeCase input{
276
- caret-color: transparent;
277
- width: 15px;
278
- padding-top:1px;
281
+ .timeCase:focus-within {
282
+ border-color: #176b4a;
283
+ outline: 3px solid #176b4a;
284
+ outline-offset: 2px;
279
285
  }
280
286
 
281
-
282
- div.timeCase .D3{
283
- 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;
284
302
  }
285
303
 
286
-
287
- /* Hiding the up and down arrow */
288
- /*https://www.w3schools.com/howto/howto_css_hide_arrow_number.asp*/
289
- /* Chrome, Safari, Edge, Opera */
290
-
291
- input::-webkit-outer-spin-button,
292
- input::-webkit-inner-spin-button {
293
- -webkit-appearance: none;
294
- margin: 0;
304
+ .timeCase input.D3 {
305
+ width: 3ch;
306
+ min-width: 3ch;
295
307
  }
296
308
 
297
-
298
- /* Firefox */
299
-
300
- input[type=number] {
301
- /* -moz-appearance: textfield; */
302
- -moz-appearance: textarea;
303
- -webkit-appearance: textarea;
309
+ .timeCase input::-webkit-outer-spin-button,
310
+ .timeCase input::-webkit-inner-spin-button {
311
+ margin: 0;
312
+ appearance: none;
304
313
  }
305
314
 
306
-
307
- div.timeCase:focus-within {
308
- border: 2px solid black;
309
- padding: 2px;
315
+ .timeCase span {
316
+ color: currentColor;
317
+ opacity: 0.7;
310
318
  }
311
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
+ }
312
330
 
313
- .ts_digit {
314
- border: none;
315
- }
316
-
317
- /* between digit span */
318
- .bds{
319
- padding-bottom: 1px;
331
+ #svgContainer svg {
332
+ display: block;
333
+ width: 100%;
334
+ height: 100%;
320
335
  }
321
336
 
322
- #svgContainer {
323
- width: 12px;
324
- height: 12px;
325
- padding-top: 1px;
326
- padding-right: 2px;
327
- color: black;
328
- }
329
-
330
- *:disabled {
331
- background-color: grey;
332
- color: lightgrey;
333
- opacity: .6;
337
+ .timeCase input:disabled {
338
+ background: transparent;
339
+ color: GrayText;
340
+ cursor: not-allowed;
341
+ opacity: 0.65;
334
342
  }
335
343
  </style> `
336
344
 
@@ -422,26 +430,19 @@ class InputDuration extends HTMLElement {
422
430
  // Digit input 0-9 (typed in by user)
423
431
 
424
432
  function setInputFilter(inputElement, inputFilter) {
425
- // this sets a listener and immediately updates the value of the input number (note: can update this from input text, like by removing setSelectionRange )
433
+ inputElement.oldValue = inputElement.value;
426
434
 
427
- console.log('inputElement: ' + inputElement);
428
- window.inputElement = inputElement;
435
+ inputElement.addEventListener('input', function () {
436
+ if (inputFilter(this.value)) {
437
+ this.oldValue = this.value;
438
+ } else {
439
+ this.value = this.oldValue;
440
+ }
441
+ });
429
442
 
430
443
  inputElement.addEventListener('keyup', function (event) {
431
-
432
- window.kue = event;
433
-
434
- if (/^([0-9]?)$/.test(event.key)) {
435
- if (inputFilter(this.value)) {
436
- this.oldValue = this.value;
437
- this.oldSelectionStart = this.selectionStart;
438
- this.oldSelectionEnd = this.selectionEnd;
439
- } else if (this.hasOwnProperty("oldValue")) {
440
- this.value = this.oldValue;
441
- // this.setSelectionRange(this.oldSelectionStart, this.oldSelectionEnd);
442
- } else {
443
- this.value = "";
444
- }
444
+ if (/^[0-9]$/.test(event.key)) {
445
+ inputFilter(this.value);
445
446
  }
446
447
  });
447
448
  }
@@ -455,7 +456,7 @@ class InputDuration extends HTMLElement {
455
456
  var newV = /^([0-9]?|[0-9][0-9]?)$/.test(value);
456
457
 
457
458
 
458
- if (value.length === 2) {
459
+ if (newV && value.length === 2) {
459
460
 
460
461
  mE.querySelector('input.sM').focus();
461
462
  mE.querySelector('input.sM').select();
@@ -472,7 +473,7 @@ class InputDuration extends HTMLElement {
472
473
 
473
474
  var newV = /^([0-9]?|[0-5][0-9]?)$/.test(value);
474
475
 
475
- if (value > 5 || value.length === 2) {
476
+ if (newV && (value > 5 || value.length === 2)) {
476
477
 
477
478
  mE.querySelector('input.sS').focus();
478
479
  mE.querySelector('input.sS').select();
@@ -488,7 +489,7 @@ class InputDuration extends HTMLElement {
488
489
 
489
490
  var newV = /^([0-9]?|[0-5][0-9]?)$/.test(value);
490
491
 
491
- if (value > 5 || value.length === 2) {
492
+ if (newV && (value > 5 || value.length === 2)) {
492
493
 
493
494
  mE.querySelector('input.sMS').focus();
494
495
  mE.querySelector('input.sMS').select();
@@ -506,7 +507,7 @@ class InputDuration extends HTMLElement {
506
507
  var newV = /^([0-9]?|[0-9][0-9]?|[0-9][0-9][0-9]?)$/.test(value);
507
508
 
508
509
 
509
- if (value.length === 3) {
510
+ if (newV && value.length === 3) {
510
511
 
511
512
  mE.querySelector('input.sMS').blur();
512
513
 
@@ -643,6 +644,8 @@ class InputDuration extends HTMLElement {
643
644
  // end class
644
645
 
645
646
 
646
- export default customElements.define('input-duration', InputDuration);
647
+ customElements.define('input-duration', InputDuration);
648
+
649
+ export default InputDuration;
647
650
 
648
651
  // an option is to make all above an IIFE
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "input-duration",
3
- "version": "2.2.27",
3
+ "version": "3.0.1",
4
4
  "author": "Stephan Luis <stephan@learnsense.org>",
5
- "description": "This is the 'Input-Duration'! It is a webcomponent that provides an hour, minute, second, and millisecond control for HTML5.",
6
- "browser": "indexjs",
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
+ "browser": "./index.js",
7
7
  "module": "index.js",
8
8
  "main": "index.js",
9
9
  "type": "module",
10
10
  "scripts": {
11
- "test": " node -v",
11
+ "test": "node --test Tests/inputDuration.test.js",
12
12
  "build": "webpack && date '+%T'",
13
13
  "devbuild": "webpack --mode=development && date '+%T'",
14
14
  "start": "python3 -m http.server"
@@ -18,19 +18,29 @@
18
18
  "url": "git+https://github.com/StephanLuis/input-duration.git"
19
19
  },
20
20
  "license": "ISC",
21
+ "devDependencies": {
22
+ "jsdom": "^26.1.0"
23
+ },
21
24
  "bugs": {
22
25
  "url": "https://github.com/StephanLuis/input-duration/issues"
23
26
  },
24
27
  "homepage": "https://github.com/StephanLuis/input-duration#readme",
25
28
  "keywords": [
29
+ "javascript",
30
+ "duration input",
31
+ "time duration",
32
+ "countdown timer",
33
+ "elapsed time",
34
+ "millisecond",
35
+ "time input",
36
+ "web component",
26
37
  "vanilla js",
27
38
  "input",
28
39
  "time",
29
40
  "duration",
30
- "webcomponent",
31
41
  "hour",
32
42
  "minute",
33
43
  "second",
34
- "millisecond"
44
+ "milliseconds"
35
45
  ]
36
46
  }
@@ -1,51 +0,0 @@
1
- # triggered on commit to rep
2
- # triggers specific workflow in curl enpoint (that workflow needs workflow_disptach)
3
-
4
- name: CommitToBSEndToEnd
5
-
6
- # Controls when the action will run. Workflow runs when manually triggered using the UI
7
- # or API.
8
- on: push
9
-
10
- # A workflow run is made up of one or more jobs that can run sequentially or in parallel
11
- jobs:
12
- bump-pub:
13
- runs-on: ubuntu-latest
14
- steps:
15
- - uses: actions/checkout@v3
16
- - uses: actions/setup-node@v3
17
- with:
18
- node-version: 16
19
- registry-url: https://registry.npmjs.org/
20
- - run: npm ci
21
-
22
- # bumps minor version
23
- - run: echo "Token ${{ secrets.GITHUB_TOKEN }}"
24
- - run: echo "Repo ${{ github.repository }}"
25
- - run: echo "Head ${{ github.head_ref }}"
26
- - run: npm version patch
27
- - run: git push "https://${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}"
28
-
29
- # publishes with credentials
30
- - run: npm publish
31
- env:
32
- NODE_AUTH_TOKEN: ${{secrets.npm_token}}
33
-
34
-
35
-
36
-
37
- trigger-BStest:
38
- runs-on: ubuntu-latest
39
- steps:
40
-
41
- # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
42
- - name: Trigger Tests
43
-
44
- run: |
45
- curl -L \
46
- -X POST \
47
- -H "Accept: application/vnd.github+json" \
48
- -u "StephanLuis:${{secrets.PAT_KEY}}" \
49
- -H "X-GitHub-Api-Version: 2022-11-28" \
50
- https://api.github.com/repos/StephanLuis/BrowserStackSelenium/actions/workflows/78546395/dispatches \
51
- -d '{"ref":"main"}'
@@ -1,66 +0,0 @@
1
- name: Dev Version and Release
2
-
3
- on:
4
- push:
5
-
6
- # exclude push for these files / dir
7
- paths:
8
- - '**.js'
9
- - '!**.config.js'
10
- - '!.github/**'
11
- - '!Competitors/**'
12
- - '!docs/**'
13
- - '!readme/**'
14
-
15
- env:
16
- node_version: lts/*
17
- new_version: patch
18
-
19
- concurrency: # prevent concurrent releases
20
- group: npm-bump
21
- cancel-in-progress: true
22
-
23
- jobs:
24
- version_and_release:
25
- runs-on: ubuntu-latest
26
- outputs:
27
- tagName: ${{ steps.npm-bump.outputs.release_tag }}
28
- steps:
29
- - uses: actions/checkout@v3
30
- with:
31
- # fetch full history so things like auto-changelog work properly
32
- fetch-depth: 0
33
- - name: Use Node.js ${{ env.node_version }}
34
- uses: actions/setup-node@v3
35
- with:
36
- node-version: ${{ env.node_version }}
37
- # setting a registry enables the NODE_AUTH_TOKEN env variable where we can set an npm token. REQUIRED
38
- registry-url: 'https://registry.npmjs.org'
39
- - run: npm i
40
- - run: npm test
41
-
42
- # https://github.com/marketplace/actions/npm-bump
43
- - name: Version and publish to npm
44
- id: npm-bump
45
- uses: bcomnes/npm-bump@v2
46
- with:
47
- git_email: stephan@learnsense.org
48
- git_username: ${{ github.actor }}
49
- newversion: ${{ env.new_version }}
50
- publish_cmd: npm publish --tag dev
51
- push_version_commit: true # if your prePublishOnly step pushes git commits, you can omit this input or set it to false.
52
- github_token: ${{ secrets.GITHUB_TOKEN }} # built in actions token. Passed tp gh-release if in use.
53
- npm_token: ${{ secrets.NPM_TOKEN }} # user set secret token generated at npm
54
- - run: echo ${{ steps.npm-bump.outputs.release_tag }}
55
-
56
- # Trigger tests on BrowserStackSelenium repo
57
- - name: Test
58
-
59
- run: |
60
- curl -L \
61
- -X POST \
62
- -H "Accept: application/vnd.github+json" \
63
- -u "StephanLuis:${{secrets.PAT_KEY}}" \
64
- -H "X-GitHub-Api-Version: 2022-11-28" \
65
- https://api.github.com/repos/StephanLuis/BrowserStackSelenium/actions/workflows/79764882/dispatches \
66
- -d '{"ref":"main"}'
@@ -1,47 +0,0 @@
1
- # second commit
2
- # from https://github.com/marketplace/actions/npm-bump
3
- name: NPM Version and Release (npm-bump.yml)
4
-
5
- on:
6
- workflow_dispatch:
7
- inputs:
8
- newversion:
9
- description: 'Semantic Version Bump Type (major minor patch)'
10
- required: true
11
-
12
- env:
13
- node_version: lts/*
14
-
15
- concurrency: # prevent concurrent releases
16
- group: npm-bump
17
- cancel-in-progress: true
18
-
19
- jobs:
20
- version_and_release:
21
- runs-on: ubuntu-latest
22
- outputs:
23
- tagName: ${{ steps.npm-bump.outputs.release_tag }}
24
- steps:
25
- - uses: actions/checkout@v3
26
- with:
27
- # fetch full history so things like auto-changelog work properly
28
- fetch-depth: 0
29
- - name: Use Node.js ${{ env.node_version }}
30
- uses: actions/setup-node@v3
31
- with:
32
- node-version: ${{ env.node_version }}
33
- # setting a registry enables the NODE_AUTH_TOKEN env variable where we can set an npm token. REQUIRED
34
- registry-url: 'https://registry.npmjs.org'
35
- - run: npm i
36
- - run: npm test
37
- - name: Version and publish to npm
38
- id: npm-bump
39
- uses: bcomnes/npm-bump@v2
40
- with:
41
- git_email: bcomnes@gmail.com
42
- git_username: ${{ github.actor }}
43
- newversion: ${{ github.event.inputs.newversion }}
44
- push_version_commit: true # if your prePublishOnly step pushes git commits, you can omit this input or set it to false.
45
- github_token: ${{ secrets.GITHUB_TOKEN }} # built in actions token. Passed tp gh-release if in use.
46
- npm_token: ${{ secrets.NPM_TOKEN }} # user set secret token generated at npm
47
- - run: echo ${{ steps.npm-bump.outputs.release_tag }}