@dile/ui 2.12.0 → 2.13.0

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.
@@ -0,0 +1 @@
1
+ export { DileTimer } from "./src/DileTimer.js";
@@ -0,0 +1,294 @@
1
+ import { html, css, LitElement } from "lit";
2
+ import "../../button/button.js";
3
+
4
+ export class DileTimer extends LitElement {
5
+ static properties = {
6
+ duration: { type: Number },
7
+ remaining: { type: Number, reflect: true },
8
+ running: { type: Boolean, reflect: true },
9
+ tick: { type: Number },
10
+ autoStart: { type: Boolean },
11
+ format: { type: String },
12
+ title: { type: String },
13
+ subtitle: { type: String },
14
+ controls: { type: Boolean, reflect: true },
15
+ startText: { type: String, attribute: "start-text" },
16
+ pauseText: { type: String, attribute: "pause-text" },
17
+ resetText: { type: String, attribute: "reset-text" },
18
+ disabled: { type: Boolean, reflect: true },
19
+ };
20
+
21
+ constructor() {
22
+ super();
23
+ this.duration = 60_000;
24
+ this.remaining = this.duration;
25
+ this.running = false;
26
+ this.tick = 250;
27
+ this.autoStart = false;
28
+ this.format = "mm:ss";
29
+ this.title = "";
30
+ this.subtitle = "";
31
+ this.controls = false;
32
+ this.startText = "Start";
33
+ this.pauseText = "Pause";
34
+ this.resetText = "Reset";
35
+ this.disabled = false;
36
+ this._intervalId = null;
37
+ this._targetEndTs = null;
38
+ this._hasAutoStarted = false;
39
+ }
40
+
41
+ static get styles() {
42
+ return css`
43
+ :host {
44
+ display: inline-flex;
45
+ flex-direction: column;
46
+ align-items: stretch;
47
+ justify-content: center;
48
+ font-family: var(--dile-timer-font-family, inherit);
49
+ color: var(--dile-timer-color, inherit);
50
+ background: var(--dile-timer-background, transparent);
51
+ border-radius: var(--dile-timer-border-radius, 10px);
52
+ border: var(--dile-timer-border, 1px solid rgba(0, 0, 0, 0.08));
53
+ width: var(--dile-timer-width, auto);
54
+ min-width: var(--dile-timer-min-width, 220px);
55
+ max-width: var(--dile-timer-max-width, 100%);
56
+ box-sizing: border-box;
57
+ padding: var(--dile-timer-padding, 12px);
58
+ letter-spacing: var(--dile-timer-letter-spacing, 0.02em);
59
+ font-variant-numeric: tabular-nums;
60
+ }
61
+
62
+ :host([running]) {
63
+ color: var(--dile-timer-running-color, inherit);
64
+ }
65
+
66
+ :host([remaining="0"]) {
67
+ color: var(--dile-timer-finished-color, #5ba75d);
68
+ }
69
+
70
+ header {
71
+ display: flex;
72
+ flex-direction: column;
73
+ gap: 4px;
74
+ margin-bottom: 10px;
75
+ }
76
+
77
+ .title {
78
+ font-size: var(--dile-timer-title-font-size, 1rem);
79
+ font-weight: var(--dile-timer-title-font-weight, 700);
80
+ color: var(--dile-timer-title-color, inherit);
81
+ margin: 0;
82
+ }
83
+
84
+ .subtitle {
85
+ font-size: var(--dile-timer-subtitle-font-size, 0.9rem);
86
+ font-weight: var(--dile-timer-subtitle-font-weight, 400);
87
+ color: var(--dile-timer-subtitle-color, rgba(0, 0, 0, 0.65));
88
+ margin: 0;
89
+ }
90
+
91
+ .display {
92
+ display: flex;
93
+ align-items: center;
94
+ justify-content: center;
95
+ height: var(--dile-timer-height, 72px);
96
+ font-size: var(--dile-timer-font-size, 2rem);
97
+ font-weight: var(--dile-timer-font-weight, 700);
98
+ border-radius: var(--dile-timer-display-border-radius, 10px);
99
+ background: var(--dile-timer-display-background, rgba(0, 0, 0, 0.04));
100
+ color: var(--dile-timer-display-color, inherit);
101
+ }
102
+
103
+ :host([remaining="0"]) .display {
104
+ background: var(--dile-timer-finished-display-background, rgba(211, 47, 47, 0.1));
105
+ }
106
+
107
+ .controls {
108
+ display: grid;
109
+ grid-template-columns: repeat(3, minmax(0, 1fr));
110
+ gap: 10px;
111
+ margin-top: 12px;
112
+ }
113
+
114
+ .controls dile-button {
115
+ --dile-button-padding-y: var(--dile-timer-button-padding-y, 0.55rem);
116
+ --dile-button-padding-x: var(--dile-timer-button-padding-x, 0.75rem);
117
+ --dile-button-border-radius: var(--dile-timer-button-border-radius, 10px);
118
+ --dile-primary-color: var(--dile-timer-button-background, #ffffff);
119
+ --dile-primary-dark-color: var(--dile-timer-button-border-color, rgba(0, 0, 0, 0.12));
120
+ --dile-on-primary-color: var(--dile-timer-button-color, rgba(0, 0, 0, 0.82));
121
+ --dile-button-hover-background-color: var(--dile-timer-button-hover-background, rgba(0, 0, 0, 0.04));
122
+ --dile-button-hover-border-color: var(--dile-timer-button-hover-border, rgba(0, 0, 0, 0.18));
123
+ --dile-button-hover-text-color: var(--dile-timer-button-hover-color, rgba(0, 0, 0, 0.82));
124
+ --dile-button-ring-color: var(--dile-timer-button-focus-ring, rgba(18, 201, 233, 0.35));
125
+ --dile-button-font-size: var(--dile-timer-button-font-size, 0.95rem);
126
+ --dile-button-font-weight: var(--dile-timer-button-font-weight, 600);
127
+ width: 100%;
128
+ display: block;
129
+ text-align: center;
130
+ }
131
+
132
+ :host([disabled]) {
133
+ opacity: 0.7;
134
+ pointer-events: none;
135
+ }`;
136
+ }
137
+
138
+ render() {
139
+ return html`
140
+ ${this._headerTemplate}
141
+ <div class="display">
142
+ ${this._formatMs(this.remaining)}
143
+ </div>
144
+ ${this.controls ? this._controlsTemplate : ""}
145
+ `;
146
+ }
147
+
148
+ get _headerTemplate() {
149
+ if (!this.title && !this.subtitle) return "";
150
+ return html`
151
+ <header>
152
+ ${this.title ? html`<p class="title">${this.title}</p>` : ""}
153
+ ${this.subtitle ? html`<p class="subtitle">${this.subtitle}</p>` : ""}
154
+ </header>`;
155
+ }
156
+
157
+ get _controlsTemplate() {
158
+ return html`
159
+ <div class="controls">
160
+ <dile-button @click=${this._onStartClick} ?disabled=${this.disabled || this.running}>
161
+ ${this.startText}
162
+ </dile-button>
163
+ <dile-button @click=${this._onPauseClick} ?disabled=${this.disabled || !this.running}>
164
+ ${this.pauseText}
165
+ </dile-button>
166
+ <dile-button @click=${this._onResetClick} ?disabled=${this.disabled}>
167
+ ${this.resetText}
168
+ </dile-button>
169
+ </div>`;
170
+ }
171
+
172
+ _onStartClick() {
173
+ if (this.disabled) return;
174
+ this.start();
175
+ }
176
+
177
+ _onPauseClick() {
178
+ if (this.disabled) return;
179
+ this.pause();
180
+ }
181
+
182
+ _onResetClick() {
183
+ if (this.disabled) return;
184
+ this.reset();
185
+ }
186
+
187
+ willUpdate(changed) {
188
+ if (changed.has("duration")) {
189
+ const d = Number(this.duration);
190
+ this.duration = Number.isFinite(d) && d >= 0 ? d : 0;
191
+ if (!this.running) {
192
+ this.remaining = this.duration;
193
+ this._targetEndTs = null;
194
+ }
195
+ if (this.autoStart && !this._hasAutoStarted) {
196
+ this._hasAutoStarted = true;
197
+ this.start();
198
+ }
199
+ }
200
+ }
201
+
202
+ disconnectedCallback() {
203
+ super.disconnectedCallback();
204
+ this._stopInterval();
205
+ }
206
+
207
+ start() {
208
+ if (this.running) return;
209
+ if (this.remaining <= 0) {
210
+ this.remaining = this.duration;
211
+ }
212
+ this.running = true;
213
+ const now = Date.now();
214
+ this._targetEndTs = now + this.remaining;
215
+ this._startInterval();
216
+ this._dispatchTick();
217
+ }
218
+
219
+ pause() {
220
+ if (!this.running) return;
221
+ this._syncRemaining();
222
+ this.running = false;
223
+ this._stopInterval();
224
+ this._dispatchTick();
225
+ }
226
+
227
+ reset() {
228
+ this.running = false;
229
+ this._stopInterval();
230
+ this.remaining = this.duration;
231
+ this._targetEndTs = null;
232
+ this._dispatchTick();
233
+ }
234
+
235
+ _startInterval() {
236
+ this._stopInterval();
237
+ this._intervalId = window.setInterval(() => this._onInterval(), this.tick);
238
+ }
239
+
240
+ _stopInterval() {
241
+ if (this._intervalId != null) {
242
+ clearInterval(this._intervalId);
243
+ this._intervalId = null;
244
+ }
245
+ }
246
+
247
+ _onInterval() {
248
+ if (!this.running) return;
249
+ this._syncRemaining();
250
+ this._dispatchTick();
251
+ if (this.remaining <= 0) {
252
+ this.remaining = 0;
253
+ this.running = false;
254
+ this._stopInterval();
255
+ this._eventDispatcher("dile-timer-finished", { duration: this.duration });
256
+ }
257
+ }
258
+
259
+ _syncRemaining() {
260
+ if (this._targetEndTs == null) return;
261
+ this.remaining = Math.max(0, this._targetEndTs - Date.now());
262
+ }
263
+
264
+ _dispatchTick() {
265
+ this._eventDispatcher("dile-timer-tick", {
266
+ remaining: this.remaining,
267
+ duration: this.duration,
268
+ running: this.running,
269
+ });
270
+ }
271
+
272
+ _eventDispatcher(name, detail) {
273
+ this.dispatchEvent(
274
+ new CustomEvent(name, {
275
+ detail,
276
+ bubbles: true,
277
+ composed: true,
278
+ })
279
+ );
280
+ }
281
+
282
+ _formatMs(ms) {
283
+ const safe = Math.max(0, Number(ms) || 0);
284
+ const totalSeconds = Math.floor(safe / 1000);
285
+ const seconds = totalSeconds % 60;
286
+ const totalMinutes = Math.floor(totalSeconds / 60);
287
+ const minutes = totalMinutes % 60;
288
+ const hours = Math.floor(totalMinutes / 60);
289
+ const pad2 = (n) => String(n).padStart(2, "0");
290
+ return (this.format === "hh:mm:ss" || hours > 0)
291
+ ? `${pad2(hours)}:${pad2(minutes)}:${pad2(seconds)}`
292
+ : `${pad2(minutes)}:${pad2(seconds)}`;
293
+ }
294
+ }
@@ -0,0 +1,3 @@
1
+ import { DileTimer } from "./src/DileTimer.js";
2
+
3
+ window.customElements.define("dile-timer", DileTimer);
@@ -12,18 +12,28 @@
12
12
  export const DileCloseDocumentClick = (SuperClass) =>
13
13
  class extends SuperClass {
14
14
 
15
+ static closeDocumentHandler = null;
16
+
15
17
  constructor() {
16
18
  super()
17
19
  if(!DileCloseDocumentClick.elements) {
18
20
  DileCloseDocumentClick.elements = [];
19
21
  }
20
- this.closeDocumentHandler = this.closeAll.bind(this);
21
22
  }
22
23
 
23
24
  connectedCallback() {
24
25
  super.connectedCallback();
25
- if (!DileCloseDocumentClick.elements.length) {
26
- document.addEventListener("click", this.closeDocumentHandler);
26
+ if (!DileCloseDocumentClick.elements?.length) {
27
+ if (!DileCloseDocumentClick.closeDocumentHandler) {
28
+ DileCloseDocumentClick.closeDocumentHandler = () => {
29
+ if(DileCloseDocumentClick.elements) {
30
+ for (let ele of DileCloseDocumentClick.elements) {
31
+ ele.close();
32
+ }
33
+ }
34
+ };
35
+ }
36
+ document.addEventListener("click", DileCloseDocumentClick.closeDocumentHandler);
27
37
  }
28
38
  DileCloseDocumentClick.elements.push(this);
29
39
  }
@@ -34,8 +44,7 @@ export const DileCloseDocumentClick = (SuperClass) =>
34
44
  (item) => item != this
35
45
  );
36
46
  if (DileCloseDocumentClick.elements.length == 0) {
37
- DileCloseDocumentClick.elements = null;
38
- document.removeEventListener("click", this.closeDocumentHandler);
47
+ document.removeEventListener("click", DileCloseDocumentClick.closeDocumentHandler);
39
48
  }
40
49
  }
41
50
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dile/ui",
3
- "version": "2.12.0",
3
+ "version": "2.13.0",
4
4
  "description": "UI Core components from dile-components.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -26,5 +26,5 @@
26
26
  "publishConfig": {
27
27
  "access": "public"
28
28
  },
29
- "gitHead": "5e351778fe7c3206effd8c3589ba6ef6992d694b"
29
+ "gitHead": "fe694c1012976386590ba407e057b6851fdfa0df"
30
30
  }