tiny-essentials 1.25.0 → 1.25.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 +1 -1
- package/changelog/1/25/1.md +18 -0
- package/changelog/1/25/2.md +9 -0
- package/dist/v1/TinyAnalogClock.min.js +1 -0
- package/dist/v1/TinyEssentials.min.js +1 -1
- package/dist/v1/TinyTextDiffer.min.js +1 -0
- package/dist/v1/basics/array.cjs +5 -4
- package/dist/v1/basics/array.d.mts +7 -6
- package/dist/v1/basics/array.mjs +5 -4
- package/dist/v1/build/TinyAnalogClock.cjs +7 -0
- package/dist/v1/build/TinyAnalogClock.d.mts +3 -0
- package/dist/v1/build/TinyAnalogClock.mjs +2 -0
- package/dist/v1/build/TinyTextDiffer.cjs +7 -0
- package/dist/v1/build/TinyTextDiffer.d.mts +3 -0
- package/dist/v1/build/TinyTextDiffer.mjs +2 -0
- package/dist/v1/index.cjs +4 -0
- package/dist/v1/index.d.mts +3 -1
- package/dist/v1/index.mjs +3 -1
- package/dist/v1/libs/TinyAnalogClock.cjs +738 -0
- package/dist/v1/libs/TinyAnalogClock.d.mts +342 -0
- package/dist/v1/libs/TinyAnalogClock.mjs +653 -0
- package/dist/v1/libs/TinyTextDiffer.cjs +288 -0
- package/dist/v1/libs/TinyTextDiffer.d.mts +109 -0
- package/dist/v1/libs/TinyTextDiffer.mjs +255 -0
- package/docs/v1/README.md +2 -0
- package/docs/v1/basics/array.md +2 -2
- package/docs/v1/libs/TinyAnalogClock.md +295 -0
- package/docs/v1/libs/TinyTextDiffer.md +114 -0
- package/package.json +9 -1
|
@@ -0,0 +1,738 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Configuration object for the Analog Clock.
|
|
5
|
+
* @typedef {Object} ClockConfig
|
|
6
|
+
* @property {string} bgColor - The background color of the clock face (e.g., '#ffffff', 'rgb(0,0,0)').
|
|
7
|
+
* @property {string} borderColor - The color of the clock's outer border.
|
|
8
|
+
* @property {number} borderWidth - The thickness of the outer border in pixels.
|
|
9
|
+
* @property {string} markColor - The color of the minute/hour tick marks.
|
|
10
|
+
* @property {string} hourHandColor - The color of the hour hand.
|
|
11
|
+
* @property {string} minuteHandColor - The color of the minute hand.
|
|
12
|
+
* @property {string} secondHandColor - The color of the second hand.
|
|
13
|
+
* @property {string} textColor - The color of the numbers on the clock face.
|
|
14
|
+
* @property {string|null} skinUrl - The URL of an image to use as the clock face background. Set to null to use bgColor.
|
|
15
|
+
* @property {boolean} showNumbers - Whether to render the numbers (1-12) on the clock face.
|
|
16
|
+
* @property {boolean} showSeconds - Whether to display the second hand.
|
|
17
|
+
* @property {number} size - The total width and height of the clock in pixels.
|
|
18
|
+
* @property {number} sizeAdjust - Scale factor for the font size of the numbers relative to the clock size.
|
|
19
|
+
* @property {number} padding - Padding in pixels between the clock edge and the tick marks.
|
|
20
|
+
* @property {number} angleDistance - Distance multiplier (0-1) for placing numbers relative to the radius.
|
|
21
|
+
* @property {number} pwH - Hour tick width as a percentage of clock size (0.0 to 1.0).
|
|
22
|
+
* @property {number} phH - Hour tick height as a percentage of clock size (0.0 to 1.0).
|
|
23
|
+
* @property {number} pwM - Minute tick width as a percentage of clock size (0.0 to 1.0).
|
|
24
|
+
* @property {number} phM - Minute tick height as a percentage of clock size (0.0 to 1.0).
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
class TinyAnalogClock {
|
|
28
|
+
/** @type {HTMLElement} */
|
|
29
|
+
#element;
|
|
30
|
+
/** @type {HTMLElement} */
|
|
31
|
+
#faceLayer;
|
|
32
|
+
/** @type {HTMLElement} */
|
|
33
|
+
#skinLayer;
|
|
34
|
+
/** @type {ClockConfig} */
|
|
35
|
+
#config;
|
|
36
|
+
/** @type {number|null} */
|
|
37
|
+
#animationFrame = null;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Retrieves the current date and extracts the specific hour, minute, and second components.
|
|
41
|
+
* @returns {{ now: Date, s: number, m: number, h: number }}
|
|
42
|
+
* @private
|
|
43
|
+
*/
|
|
44
|
+
_getDate() {
|
|
45
|
+
const now = new Date();
|
|
46
|
+
const s = now.getSeconds();
|
|
47
|
+
const m = now.getMinutes();
|
|
48
|
+
const h = now.getHours();
|
|
49
|
+
|
|
50
|
+
return { now, s, m, h };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Creates an instance of TinyAnalogClock.
|
|
55
|
+
* Initializes the DOM structure and starts the animation loop.
|
|
56
|
+
* @param {Partial<ClockConfig>} [options] - Optional configuration overrides.
|
|
57
|
+
*/
|
|
58
|
+
constructor(options = {}) {
|
|
59
|
+
this.#config = {
|
|
60
|
+
bgColor: '#f0f0f0',
|
|
61
|
+
borderColor: '#333',
|
|
62
|
+
borderWidth: 8,
|
|
63
|
+
markColor: '#333',
|
|
64
|
+
hourHandColor: '#000',
|
|
65
|
+
minuteHandColor: '#444',
|
|
66
|
+
secondHandColor: '#d81c1c',
|
|
67
|
+
textColor: '#000',
|
|
68
|
+
skinUrl: null,
|
|
69
|
+
showNumbers: true,
|
|
70
|
+
showSeconds: true,
|
|
71
|
+
size: 800,
|
|
72
|
+
sizeAdjust: 0.04,
|
|
73
|
+
padding: 45,
|
|
74
|
+
angleDistance: 0.95,
|
|
75
|
+
pwH: 0.008,
|
|
76
|
+
phH: 0.08,
|
|
77
|
+
pwM: 0.005,
|
|
78
|
+
phM: 0.03,
|
|
79
|
+
...options,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
this.#element = document.createElement('div');
|
|
83
|
+
this.#element.className = 'analog-clock-container';
|
|
84
|
+
|
|
85
|
+
// Base styles for the container
|
|
86
|
+
this.#element.style.position = 'relative';
|
|
87
|
+
this.#element.style.borderRadius = '50%';
|
|
88
|
+
this.#element.style.overflow = 'hidden';
|
|
89
|
+
this.#element.style.boxSizing = 'border-box';
|
|
90
|
+
|
|
91
|
+
// 1. Skin Layer (Background Image)
|
|
92
|
+
this.#skinLayer = document.createElement('div');
|
|
93
|
+
this.#skinLayer.style.position = 'absolute';
|
|
94
|
+
this.#skinLayer.style.inset = '0';
|
|
95
|
+
this.#skinLayer.style.backgroundSize = 'cover';
|
|
96
|
+
this.#skinLayer.style.backgroundPosition = 'center';
|
|
97
|
+
this.#skinLayer.style.zIndex = '0';
|
|
98
|
+
|
|
99
|
+
// 2. Face Layer (Ticks and Numbers)
|
|
100
|
+
this.#faceLayer = document.createElement('div');
|
|
101
|
+
this.#faceLayer.style.position = 'absolute';
|
|
102
|
+
this.#faceLayer.style.inset = '0';
|
|
103
|
+
this.#faceLayer.style.zIndex = '1';
|
|
104
|
+
this.#faceLayer.style.pointerEvents = 'none';
|
|
105
|
+
|
|
106
|
+
// 3. Hands Layer (Hour, Minute, Second hands)
|
|
107
|
+
const handsLayer = document.createElement('div');
|
|
108
|
+
handsLayer.style.position = 'absolute';
|
|
109
|
+
handsLayer.style.inset = '0';
|
|
110
|
+
handsLayer.style.zIndex = '2';
|
|
111
|
+
handsLayer.style.pointerEvents = 'none';
|
|
112
|
+
handsLayer.innerHTML = `
|
|
113
|
+
<div class="hand hour-hand"></div>
|
|
114
|
+
<div class="hand minute-hand"></div>
|
|
115
|
+
<div class="hand second-hand"></div>
|
|
116
|
+
<div class="center-pin"></div>
|
|
117
|
+
`;
|
|
118
|
+
|
|
119
|
+
this.#element.appendChild(this.#skinLayer);
|
|
120
|
+
this.#element.appendChild(this.#faceLayer);
|
|
121
|
+
this.#element.appendChild(handsLayer);
|
|
122
|
+
|
|
123
|
+
// Inject Styles dynamically to keep the class self-contained
|
|
124
|
+
const style = document.createElement('style');
|
|
125
|
+
style.textContent = `
|
|
126
|
+
.analog-clock-container .hand {
|
|
127
|
+
position: absolute;
|
|
128
|
+
bottom: 50%;
|
|
129
|
+
left: 50%;
|
|
130
|
+
transform-origin: bottom center;
|
|
131
|
+
border-radius: 4px;
|
|
132
|
+
transform: translateX(-50%) rotate(0deg);
|
|
133
|
+
z-index: 5;
|
|
134
|
+
}
|
|
135
|
+
.analog-clock-container .center-pin {
|
|
136
|
+
position: absolute;
|
|
137
|
+
top: 50%; left: 50%;
|
|
138
|
+
width: 12px; height: 12px;
|
|
139
|
+
background: #333;
|
|
140
|
+
border-radius: 50%;
|
|
141
|
+
transform: translate(-50%, -50%);
|
|
142
|
+
z-index: 10;
|
|
143
|
+
}
|
|
144
|
+
.analog-clock-container .clock-mark {
|
|
145
|
+
position: absolute;
|
|
146
|
+
top: 50%; left: 50%;
|
|
147
|
+
background: currentColor;
|
|
148
|
+
transform-origin: center center;
|
|
149
|
+
}
|
|
150
|
+
.analog-clock-container .clock-number {
|
|
151
|
+
position: absolute;
|
|
152
|
+
top: 50%; left: 50%;
|
|
153
|
+
transform: translate(-50%, -50%);
|
|
154
|
+
font-family: sans-serif;
|
|
155
|
+
font-weight: bold;
|
|
156
|
+
text-align: center;
|
|
157
|
+
line-height: 1;
|
|
158
|
+
}
|
|
159
|
+
`;
|
|
160
|
+
this.#element.appendChild(style);
|
|
161
|
+
|
|
162
|
+
this._applyConfig();
|
|
163
|
+
this._renderFace();
|
|
164
|
+
this._startTicker();
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Applies the current configuration to the DOM elements (colors, sizes, visibility).
|
|
169
|
+
* @private
|
|
170
|
+
*/
|
|
171
|
+
_applyConfig() {
|
|
172
|
+
const s = this.#element.style;
|
|
173
|
+
const c = this.#config;
|
|
174
|
+
|
|
175
|
+
s.width = `${c.size}px`;
|
|
176
|
+
s.height = `${c.size}px`;
|
|
177
|
+
s.border = `${c.borderWidth}px solid ${c.borderColor}`;
|
|
178
|
+
s.backgroundColor = c.bgColor;
|
|
179
|
+
|
|
180
|
+
if (c.skinUrl) {
|
|
181
|
+
this.#skinLayer.style.backgroundImage = `url(${c.skinUrl})`;
|
|
182
|
+
this.#skinLayer.style.display = 'block';
|
|
183
|
+
} else {
|
|
184
|
+
this.#skinLayer.style.display = 'none';
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Helper to safely query elements within the clock.
|
|
189
|
+
* @param {string} sel
|
|
190
|
+
* @returns {HTMLDivElement}
|
|
191
|
+
*/
|
|
192
|
+
const q = (sel) => {
|
|
193
|
+
const result = this.#element.querySelector(sel);
|
|
194
|
+
if (!(result instanceof HTMLDivElement))
|
|
195
|
+
throw new Error(`TinyAnalogClock: Element ${sel} not found.`);
|
|
196
|
+
return result;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
// Hour Hand
|
|
200
|
+
const hHand = q('.hour-hand');
|
|
201
|
+
hHand.style.backgroundColor = c.hourHandColor;
|
|
202
|
+
hHand.style.width = `${c.size * 0.025}px`;
|
|
203
|
+
hHand.style.height = `${c.size * 0.25}px`;
|
|
204
|
+
hHand.style.marginLeft = `${(c.size * 0.025) / -2}px`; // Center alignment fix
|
|
205
|
+
hHand.style.bottom = '50%';
|
|
206
|
+
hHand.style.left = '50%';
|
|
207
|
+
hHand.style.transformOrigin = 'bottom center';
|
|
208
|
+
|
|
209
|
+
// Minute Hand
|
|
210
|
+
const mHand = q('.minute-hand');
|
|
211
|
+
mHand.style.backgroundColor = c.minuteHandColor;
|
|
212
|
+
mHand.style.width = `${c.size * 0.015}px`;
|
|
213
|
+
mHand.style.height = `${c.size * 0.35}px`;
|
|
214
|
+
mHand.style.marginLeft = `${(c.size * 0.015) / -2}px`;
|
|
215
|
+
|
|
216
|
+
// Second Hand
|
|
217
|
+
const sHand = q('.second-hand');
|
|
218
|
+
if (c.showSeconds) {
|
|
219
|
+
sHand.style.display = 'block';
|
|
220
|
+
sHand.style.backgroundColor = c.secondHandColor;
|
|
221
|
+
sHand.style.width = `${c.size * 0.005}px`;
|
|
222
|
+
sHand.style.height = `${c.size * 0.4}px`;
|
|
223
|
+
sHand.style.marginLeft = `${(c.size * 0.005) / -2}px`;
|
|
224
|
+
} else {
|
|
225
|
+
sHand.style.display = 'none';
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
q('.center-pin').style.background = c.borderColor;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Renders the clock face, including tick marks and numbers, based on the current size and config.
|
|
233
|
+
* Uses trigonometry to position elements perfectly from the center.
|
|
234
|
+
* @private
|
|
235
|
+
*/
|
|
236
|
+
_renderFace() {
|
|
237
|
+
this.#faceLayer.innerHTML = '';
|
|
238
|
+
const radius = this.#config.size / 2 - this.#config.borderWidth;
|
|
239
|
+
|
|
240
|
+
// 1. Render Ticks (Lines)
|
|
241
|
+
for (let i = 0; i < 60; i++) {
|
|
242
|
+
const isHour = i % 5 === 0;
|
|
243
|
+
const el = document.createElement('div');
|
|
244
|
+
el.className = 'clock-mark';
|
|
245
|
+
|
|
246
|
+
const w = isHour
|
|
247
|
+
? this.#config.size * this.#config.pwH
|
|
248
|
+
: this.#config.size * this.#config.pwM;
|
|
249
|
+
const h = isHour
|
|
250
|
+
? this.#config.size * this.#config.phH
|
|
251
|
+
: this.#config.size * this.#config.phM;
|
|
252
|
+
const color = this.#config.markColor;
|
|
253
|
+
|
|
254
|
+
el.style.width = `${w}px`;
|
|
255
|
+
el.style.height = `${h}px`;
|
|
256
|
+
el.style.color = color;
|
|
257
|
+
|
|
258
|
+
// Distance from center to the TICK's center
|
|
259
|
+
// We want the tick to be close to the edge.
|
|
260
|
+
// Distance = radius - padding - (half of tick height)
|
|
261
|
+
const distanceFromCenter = radius - this.#config.padding - h / 2;
|
|
262
|
+
|
|
263
|
+
// Logic: Start at center (50% 50%) -> Rotate -> Push Outwards
|
|
264
|
+
el.style.transform = `translate(-50%, -50%) rotate(${i * 6}deg) translate(0, -${distanceFromCenter}px)`;
|
|
265
|
+
|
|
266
|
+
this.#faceLayer.appendChild(el);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// 2. Render Numbers
|
|
270
|
+
if (this.#config.showNumbers) {
|
|
271
|
+
for (let i = 1; i <= 12; i++) {
|
|
272
|
+
const angle = i * 30 * (Math.PI / 180);
|
|
273
|
+
// Adjust radius for text position (80% of total radius)
|
|
274
|
+
const dist = radius * this.#config.angleDistance;
|
|
275
|
+
const x = radius + Math.sin(angle) * dist;
|
|
276
|
+
const y = radius - Math.cos(angle) * dist;
|
|
277
|
+
|
|
278
|
+
const num = document.createElement('div');
|
|
279
|
+
num.className = 'clock-number';
|
|
280
|
+
num.innerText = i.toString();
|
|
281
|
+
num.style.left = `${x}px`;
|
|
282
|
+
num.style.top = `${y}px`;
|
|
283
|
+
num.style.color = this.#config.textColor;
|
|
284
|
+
num.style.fontSize = `${this.#config.size * this.#config.sizeAdjust}px`;
|
|
285
|
+
this.#faceLayer.appendChild(num);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Starts the RequestAnimationFrame loop to update hand positions.
|
|
292
|
+
* @private
|
|
293
|
+
*/
|
|
294
|
+
_startTicker() {
|
|
295
|
+
if (this.#animationFrame) return;
|
|
296
|
+
|
|
297
|
+
const update = () => {
|
|
298
|
+
const { s, m, h } = this._getDate();
|
|
299
|
+
|
|
300
|
+
// Calculate degrees
|
|
301
|
+
const sDeg = s * 6;
|
|
302
|
+
const mDeg = m * 6 + s * 0.1;
|
|
303
|
+
const hDeg = (h % 12) * 30 + m * 0.5;
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* @param {string} sel
|
|
307
|
+
* @returns {HTMLDivElement}
|
|
308
|
+
*/
|
|
309
|
+
const q = (sel) => {
|
|
310
|
+
const result = this.#element.querySelector(sel);
|
|
311
|
+
if (!(result instanceof HTMLDivElement)) throw new Error(`${sel} not found.`);
|
|
312
|
+
return result;
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
if (this.#element.querySelector('.second-hand')) {
|
|
316
|
+
q('.second-hand').style.transform = `rotate(${sDeg}deg)`;
|
|
317
|
+
}
|
|
318
|
+
q('.minute-hand').style.transform = `rotate(${mDeg}deg)`;
|
|
319
|
+
q('.hour-hand').style.transform = `rotate(${hDeg}deg)`;
|
|
320
|
+
|
|
321
|
+
this.#animationFrame = requestAnimationFrame(update);
|
|
322
|
+
};
|
|
323
|
+
update();
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// ==========================================
|
|
327
|
+
// GETTERS & SETTERS (Full Access)
|
|
328
|
+
// ==========================================
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Gets the main HTML element of the clock.
|
|
332
|
+
* @returns {HTMLElement} The clock container.
|
|
333
|
+
*/
|
|
334
|
+
get element() {
|
|
335
|
+
return this.#element;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Sets the size of the clock.
|
|
340
|
+
* @param {number} value - The new size in pixels. Must be a positive number.
|
|
341
|
+
* @throws {Error} If value is not a positive number.
|
|
342
|
+
*/
|
|
343
|
+
set size(value) {
|
|
344
|
+
if (typeof value !== 'number' || value <= 0 || isNaN(value)) {
|
|
345
|
+
throw new Error(`TinyAnalogClock: 'size' must be a positive number. Received: ${value}`);
|
|
346
|
+
}
|
|
347
|
+
this.#config.size = value;
|
|
348
|
+
this._applyConfig();
|
|
349
|
+
this._renderFace();
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/** * Gets the clock size in pixels.
|
|
353
|
+
* @returns {number}
|
|
354
|
+
*/
|
|
355
|
+
get size() {
|
|
356
|
+
return this.#config.size;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Sets the background image (skin) of the clock.
|
|
361
|
+
* @param {string|null} url - The URL string of the image, or null to remove the skin.
|
|
362
|
+
* @throws {Error} If value is not a string or null.
|
|
363
|
+
*/
|
|
364
|
+
set skinUrl(url) {
|
|
365
|
+
if (url !== null && typeof url !== 'string') {
|
|
366
|
+
throw new Error(
|
|
367
|
+
`TinyAnalogClock: 'skin' must be a URL string or null. Received type: ${typeof url}`,
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
this.#config.skinUrl = url;
|
|
371
|
+
this._applyConfig();
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Gets the skin URL.
|
|
376
|
+
* @returns {string|null}
|
|
377
|
+
*/
|
|
378
|
+
get skinUrl() {
|
|
379
|
+
return this.#config.skinUrl;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Sets the primary border color.
|
|
384
|
+
* @param {string} color - A valid CSS color string.
|
|
385
|
+
* @throws {Error} If value is not a non-empty string.
|
|
386
|
+
*/
|
|
387
|
+
set borderColor(color) {
|
|
388
|
+
if (typeof color !== 'string' || color.trim() === '') {
|
|
389
|
+
throw new Error(
|
|
390
|
+
`TinyAnalogClock: 'borderColor' must be a non-empty string. Received: ${color}`,
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
this.#config.borderColor = color;
|
|
394
|
+
this._applyConfig();
|
|
395
|
+
this._renderFace();
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Gets the border color.
|
|
400
|
+
* @returns {string}
|
|
401
|
+
*/
|
|
402
|
+
get borderColor() {
|
|
403
|
+
return this.#config.borderColor;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Sets the primary marks color.
|
|
408
|
+
* @param {string} color - A valid CSS color string.
|
|
409
|
+
* @throws {Error} If value is not a non-empty string.
|
|
410
|
+
*/
|
|
411
|
+
set markColor(color) {
|
|
412
|
+
if (typeof color !== 'string' || color.trim() === '') {
|
|
413
|
+
throw new Error(
|
|
414
|
+
`TinyAnalogClock: 'markColor' must be a non-empty string. Received: ${color}`,
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
this.#config.markColor = color;
|
|
418
|
+
this._applyConfig();
|
|
419
|
+
this._renderFace();
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Gets the tick marks color.
|
|
424
|
+
* @returns {string}
|
|
425
|
+
*/
|
|
426
|
+
get markColor() {
|
|
427
|
+
return this.#config.markColor;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Sets the primary text color.
|
|
432
|
+
* @param {string} color - A valid CSS color string.
|
|
433
|
+
* @throws {Error} If value is not a non-empty string.
|
|
434
|
+
*/
|
|
435
|
+
set textColor(color) {
|
|
436
|
+
if (typeof color !== 'string' || color.trim() === '') {
|
|
437
|
+
throw new Error(
|
|
438
|
+
`TinyAnalogClock: 'textColor' must be a non-empty string. Received: ${color}`,
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
this.#config.textColor = color;
|
|
442
|
+
this._applyConfig();
|
|
443
|
+
this._renderFace();
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Gets the text color.
|
|
448
|
+
* @returns {string}
|
|
449
|
+
*/
|
|
450
|
+
get textColor() {
|
|
451
|
+
return this.#config.textColor;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Toggles the visibility of numbers on the clock face.
|
|
456
|
+
* @param {boolean} value - True to show numbers, false to hide.
|
|
457
|
+
* @throws {Error} If value is not a boolean.
|
|
458
|
+
*/
|
|
459
|
+
set showNumbers(value) {
|
|
460
|
+
if (typeof value !== 'boolean') {
|
|
461
|
+
throw new Error(
|
|
462
|
+
`TinyAnalogClock: 'showNumbers' must be a boolean. Received type: ${typeof value}`,
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
this.#config.showNumbers = value;
|
|
466
|
+
this._renderFace();
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Gets numbers visibility.
|
|
471
|
+
* @returns {boolean}
|
|
472
|
+
*/
|
|
473
|
+
get showNumbers() {
|
|
474
|
+
return this.#config.showNumbers;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Sets the background color.
|
|
479
|
+
* @param {string} value
|
|
480
|
+
*/
|
|
481
|
+
set bgColor(value) {
|
|
482
|
+
if (typeof value !== 'string' || !value)
|
|
483
|
+
throw new Error("TinyAnalogClock: 'bgColor' must be a non-empty string.");
|
|
484
|
+
this.#config.bgColor = value;
|
|
485
|
+
this._applyConfig();
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Gets the background color.
|
|
490
|
+
* @returns {string}
|
|
491
|
+
*/
|
|
492
|
+
get bgColor() {
|
|
493
|
+
return this.#config.bgColor;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Sets the border thickness in pixels.
|
|
498
|
+
* @param {number} value
|
|
499
|
+
*/
|
|
500
|
+
set borderWidth(value) {
|
|
501
|
+
if (typeof value !== 'number' || value < 0)
|
|
502
|
+
throw new Error("TinyAnalogClock: 'borderWidth' must be a non-negative number.");
|
|
503
|
+
this.#config.borderWidth = value;
|
|
504
|
+
this._applyConfig();
|
|
505
|
+
this._renderFace(); // Radius changes, so we must re-render face
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Gets the border thickness.
|
|
510
|
+
* @returns {number}
|
|
511
|
+
*/
|
|
512
|
+
get borderWidth() {
|
|
513
|
+
return this.#config.borderWidth;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Sets the hour hand color.
|
|
518
|
+
* @param {string} value
|
|
519
|
+
*/
|
|
520
|
+
set hourHandColor(value) {
|
|
521
|
+
if (typeof value !== 'string' || !value)
|
|
522
|
+
throw new Error("TinyAnalogClock: 'hourHandColor' must be a non-empty string.");
|
|
523
|
+
this.#config.hourHandColor = value;
|
|
524
|
+
this._applyConfig();
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Gets the hour hand color.
|
|
529
|
+
* @returns {string}
|
|
530
|
+
*/
|
|
531
|
+
get hourHandColor() {
|
|
532
|
+
return this.#config.hourHandColor;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Sets the minute hand color.
|
|
537
|
+
* @param {string} value
|
|
538
|
+
*/
|
|
539
|
+
set minuteHandColor(value) {
|
|
540
|
+
if (typeof value !== 'string' || !value)
|
|
541
|
+
throw new Error("TinyAnalogClock: 'minuteHandColor' must be a non-empty string.");
|
|
542
|
+
this.#config.minuteHandColor = value;
|
|
543
|
+
this._applyConfig();
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Gets the minute hand color.
|
|
548
|
+
* @returns {string}
|
|
549
|
+
*/
|
|
550
|
+
get minuteHandColor() {
|
|
551
|
+
return this.#config.minuteHandColor;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Sets the second hand color.
|
|
556
|
+
* @param {string} value
|
|
557
|
+
*/
|
|
558
|
+
set secondHandColor(value) {
|
|
559
|
+
if (typeof value !== 'string' || !value)
|
|
560
|
+
throw new Error("TinyAnalogClock: 'secondHandColor' must be a non-empty string.");
|
|
561
|
+
this.#config.secondHandColor = value;
|
|
562
|
+
this._applyConfig();
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* Gets the second hand color.
|
|
567
|
+
* @returns {string}
|
|
568
|
+
*/
|
|
569
|
+
get secondHandColor() {
|
|
570
|
+
return this.#config.secondHandColor;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Toggle second hand visibility.
|
|
575
|
+
* @param {boolean} value
|
|
576
|
+
*/
|
|
577
|
+
set showSeconds(value) {
|
|
578
|
+
if (typeof value !== 'boolean')
|
|
579
|
+
throw new Error("TinyAnalogClock: 'showSeconds' must be a boolean.");
|
|
580
|
+
this.#config.showSeconds = value;
|
|
581
|
+
this._applyConfig();
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* Gets second hand visibility.
|
|
586
|
+
* @returns {boolean}
|
|
587
|
+
*/
|
|
588
|
+
get showSeconds() {
|
|
589
|
+
return this.#config.showSeconds;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* Sets the font size adjustment factor.
|
|
594
|
+
* @param {number} value - Positive number.
|
|
595
|
+
*/
|
|
596
|
+
set sizeAdjust(value) {
|
|
597
|
+
if (typeof value !== 'number' || value <= 0)
|
|
598
|
+
throw new Error("TinyAnalogClock: 'sizeAdjust' must be a positive number.");
|
|
599
|
+
this.#config.sizeAdjust = value;
|
|
600
|
+
this._renderFace();
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* Gets the font size adjustment factor.
|
|
605
|
+
* @returns {number}
|
|
606
|
+
*/
|
|
607
|
+
get sizeAdjust() {
|
|
608
|
+
return this.#config.sizeAdjust;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Sets the padding from edge to ticks.
|
|
613
|
+
* @param {number} value - Non-negative number in pixels.
|
|
614
|
+
*/
|
|
615
|
+
set padding(value) {
|
|
616
|
+
if (typeof value !== 'number' || value < 0)
|
|
617
|
+
throw new Error("TinyAnalogClock: 'padding' must be a non-negative number.");
|
|
618
|
+
this.#config.padding = value;
|
|
619
|
+
this._renderFace();
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* Gets the padding.
|
|
624
|
+
* @returns {number}
|
|
625
|
+
*/
|
|
626
|
+
get padding() {
|
|
627
|
+
return this.#config.padding;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* Sets the angle distance multiplier for numbers placement.
|
|
632
|
+
* @param {number} value - Positive number.
|
|
633
|
+
*/
|
|
634
|
+
set angleDistance(value) {
|
|
635
|
+
if (typeof value !== 'number' || value <= 0)
|
|
636
|
+
throw new Error("TinyAnalogClock: 'angleDistance' must be a positive number.");
|
|
637
|
+
this.#config.angleDistance = value;
|
|
638
|
+
this._renderFace();
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* Gets the angle distance multiplier.
|
|
643
|
+
* @returns {number}
|
|
644
|
+
*/
|
|
645
|
+
get angleDistance() {
|
|
646
|
+
return this.#config.angleDistance;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* Sets the Hour tick width percentage.
|
|
651
|
+
* @param {number} value - Positive number.
|
|
652
|
+
*/
|
|
653
|
+
set pwH(value) {
|
|
654
|
+
if (typeof value !== 'number' || value <= 0)
|
|
655
|
+
throw new Error("TinyAnalogClock: 'pwH' must be a positive number.");
|
|
656
|
+
this.#config.pwH = value;
|
|
657
|
+
this._renderFace();
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* Gets the Hour tick width percentage.
|
|
662
|
+
* @returns {number}
|
|
663
|
+
*/
|
|
664
|
+
get pwH() {
|
|
665
|
+
return this.#config.pwH;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* Sets the Hour tick height percentage.
|
|
670
|
+
* @param {number} value - Positive number.
|
|
671
|
+
*/
|
|
672
|
+
set phH(value) {
|
|
673
|
+
if (typeof value !== 'number' || value <= 0)
|
|
674
|
+
throw new Error("TinyAnalogClock: 'phH' must be a positive number.");
|
|
675
|
+
this.#config.phH = value;
|
|
676
|
+
this._renderFace();
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* Gets the Hour tick height percentage.
|
|
681
|
+
* @returns {number}
|
|
682
|
+
*/
|
|
683
|
+
get phH() {
|
|
684
|
+
return this.#config.phH;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* Sets the Minute tick width percentage.
|
|
689
|
+
* @param {number} value - Positive number.
|
|
690
|
+
*/
|
|
691
|
+
set pwM(value) {
|
|
692
|
+
if (typeof value !== 'number' || value <= 0)
|
|
693
|
+
throw new Error("TinyAnalogClock: 'pwM' must be a positive number.");
|
|
694
|
+
this.#config.pwM = value;
|
|
695
|
+
this._renderFace();
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
/**
|
|
699
|
+
* Gets the Minute tick width percentage.
|
|
700
|
+
* @returns {number}
|
|
701
|
+
*/
|
|
702
|
+
get pwM() {
|
|
703
|
+
return this.#config.pwM;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/**
|
|
707
|
+
* Sets the Minute tick height percentage.
|
|
708
|
+
* @param {number} value - Positive number.
|
|
709
|
+
*/
|
|
710
|
+
set phM(value) {
|
|
711
|
+
if (typeof value !== 'number' || value <= 0)
|
|
712
|
+
throw new Error("TinyAnalogClock: 'phM' must be a positive number.");
|
|
713
|
+
this.#config.phM = value;
|
|
714
|
+
this._renderFace();
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
/**
|
|
718
|
+
* Gets the Minute tick height percentage.
|
|
719
|
+
* @returns {number}
|
|
720
|
+
*/
|
|
721
|
+
get phM() {
|
|
722
|
+
return this.#config.phM;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
/**
|
|
726
|
+
* Destroys the clock instance, stops animations, and removes the element from DOM.
|
|
727
|
+
* @returns {void}
|
|
728
|
+
*/
|
|
729
|
+
destroy() {
|
|
730
|
+
if (this.#animationFrame) {
|
|
731
|
+
cancelAnimationFrame(this.#animationFrame);
|
|
732
|
+
this.#animationFrame = null;
|
|
733
|
+
}
|
|
734
|
+
this.#element.remove();
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
module.exports = TinyAnalogClock;
|