@gfazioli/mantine-clock 1.0.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,574 @@
1
+ 'use client';
2
+ 'use strict';
3
+
4
+ var dayjs = require('dayjs');
5
+ var timezonePlugin = require('dayjs/plugin/timezone');
6
+ var utc = require('dayjs/plugin/utc');
7
+ var React = require('react');
8
+ var core = require('@mantine/core');
9
+ var Clock_module = require('./Clock.module.css.cjs');
10
+
11
+ dayjs.extend(utc);
12
+ dayjs.extend(timezonePlugin);
13
+ const defaultProps = {
14
+ size: 400,
15
+ hourHandSize: 0.017,
16
+ minuteHandSize: 0.011,
17
+ secondHandSize: 6e-3,
18
+ hourHandLength: 0.4,
19
+ minuteHandLength: 0.57,
20
+ secondHandLength: 0.68,
21
+ secondHandOpacity: 1,
22
+ minuteHandOpacity: 1,
23
+ hourHandOpacity: 1,
24
+ running: true
25
+ };
26
+ const defaultClockSizes = {
27
+ xs: 100,
28
+ sm: 200,
29
+ md: 400,
30
+ lg: 480,
31
+ xl: 512
32
+ };
33
+ const varsResolver = core.createVarsResolver(
34
+ (theme, {
35
+ size,
36
+ color,
37
+ hourTicksColor,
38
+ hourTicksOpacity,
39
+ minuteTicksColor,
40
+ minuteTicksOpacity,
41
+ primaryNumbersColor,
42
+ primaryNumbersOpacity,
43
+ secondaryNumbersColor,
44
+ secondaryNumbersOpacity,
45
+ secondHandColor,
46
+ minuteHandColor,
47
+ hourHandColor
48
+ }) => {
49
+ const sizeValue = size || "md";
50
+ const clockSize = typeof sizeValue === "string" && sizeValue in defaultClockSizes ? defaultClockSizes[sizeValue] : sizeValue;
51
+ const effectiveSize = Math.round(core.px(core.getSize(clockSize, "clock-size")));
52
+ return {
53
+ root: {
54
+ "--clock-size": `${effectiveSize}px`,
55
+ "--clock-color": core.parseThemeColor({
56
+ color: color || "",
57
+ theme
58
+ }).value,
59
+ "--clock-hour-ticks-color": core.parseThemeColor({
60
+ color: hourTicksColor || "",
61
+ theme
62
+ }).value,
63
+ "--clock-hour-ticks-opacity": (Math.round((hourTicksOpacity || 1) * 100) / 100).toString(),
64
+ "--clock-minute-ticks-color": core.parseThemeColor({
65
+ color: minuteTicksColor || "",
66
+ theme
67
+ }).value,
68
+ "--clock-minute-ticks-opacity": (Math.round((minuteTicksOpacity || 1) * 100) / 100).toString(),
69
+ "--clock-primary-numbers-color": core.parseThemeColor({
70
+ color: primaryNumbersColor || "",
71
+ theme
72
+ }).value,
73
+ "--clock-primary-numbers-opacity": (Math.round((primaryNumbersOpacity || 1) * 100) / 100).toString(),
74
+ "--clock-secondary-numbers-color": core.parseThemeColor({
75
+ color: secondaryNumbersColor || "",
76
+ theme
77
+ }).value,
78
+ "--clock-secondary-numbers-opacity": (Math.round((secondaryNumbersOpacity || 1) * 100) / 100).toString(),
79
+ "--clock-second-hand-color": core.parseThemeColor({
80
+ color: secondHandColor || "",
81
+ theme
82
+ }).value,
83
+ "--clock-minute-hand-color": core.parseThemeColor({
84
+ color: minuteHandColor || "",
85
+ theme
86
+ }).value,
87
+ "--clock-hour-hand-color": core.parseThemeColor({
88
+ color: hourHandColor || "",
89
+ theme
90
+ }).value
91
+ }
92
+ };
93
+ }
94
+ );
95
+ const parseTimeValue = (value) => {
96
+ if (!value) {
97
+ return null;
98
+ }
99
+ if (value instanceof Date) {
100
+ return value;
101
+ }
102
+ if (dayjs.isDayjs(value)) {
103
+ return value.toDate();
104
+ }
105
+ if (typeof value === "string") {
106
+ const timeRegex = /^(\d{1,2}):(\d{1,2})(?::(\d{1,2}))?$/;
107
+ const match = value.match(timeRegex);
108
+ if (match) {
109
+ const hours = parseInt(match[1], 10);
110
+ const minutes = parseInt(match[2], 10);
111
+ const seconds = parseInt(match[3] || "0", 10);
112
+ const date = /* @__PURE__ */ new Date();
113
+ date.setHours(hours, minutes, seconds, 0);
114
+ return date;
115
+ }
116
+ const parsed = new Date(value);
117
+ if (!isNaN(parsed.getTime())) {
118
+ return parsed;
119
+ }
120
+ }
121
+ return null;
122
+ };
123
+ const RealClock = (props) => {
124
+ const {
125
+ time,
126
+ timezone: timezone2,
127
+ getStyles,
128
+ effectiveSize,
129
+ hourHandSize,
130
+ minuteHandSize,
131
+ secondHandSize,
132
+ hourHandLength,
133
+ minuteHandLength,
134
+ secondHandLength,
135
+ secondHandBehavior,
136
+ secondHandOpacity,
137
+ minuteHandOpacity,
138
+ hourHandOpacity,
139
+ hourTicksOpacity,
140
+ minuteTicksOpacity,
141
+ primaryNumbersOpacity,
142
+ secondaryNumbersOpacity,
143
+ hourNumbersDistance = 0.75,
144
+ // Default distance for hour numbers
145
+ primaryNumbersProps,
146
+ secondaryNumbersProps
147
+ } = props;
148
+ const timezoneTime = timezone2 && timezone2 !== "" ? dayjs(time).tz(timezone2) : dayjs(time);
149
+ const hours = timezoneTime.hour() % 12;
150
+ const minutes = timezoneTime.minute();
151
+ const seconds = timezoneTime.second();
152
+ const milliseconds = timezoneTime.millisecond();
153
+ const hourAngle = hours * 30 + minutes * 0.5;
154
+ const minuteAngle = minutes * 6;
155
+ let secondAngle = 0;
156
+ switch (secondHandBehavior) {
157
+ case "tick":
158
+ secondAngle = seconds * 6;
159
+ break;
160
+ case "tick-half":
161
+ secondAngle = (seconds + Math.floor(milliseconds / 500) * 0.5) * 6;
162
+ break;
163
+ case "tick-high-freq":
164
+ secondAngle = (seconds + Math.floor(milliseconds / 125) * 0.125) * 6;
165
+ break;
166
+ case "smooth":
167
+ default:
168
+ secondAngle = (seconds + milliseconds / 1e3) * 6;
169
+ break;
170
+ }
171
+ const size = effectiveSize;
172
+ const clockRadius = Math.round(size / 2);
173
+ const numberRadius = Math.round(clockRadius * hourNumbersDistance);
174
+ const calculatedHourHandLength = Math.round(
175
+ clockRadius * (hourHandLength ?? defaultProps.hourHandLength)
176
+ );
177
+ const calculatedMinuteHandLength = Math.round(
178
+ clockRadius * (minuteHandLength ?? defaultProps.minuteHandLength)
179
+ );
180
+ const calculatedSecondHandLength = Math.round(
181
+ clockRadius * (secondHandLength ?? defaultProps.secondHandLength)
182
+ );
183
+ const centerSize = Math.round(size * 0.034);
184
+ const tickOffset = Math.round(size * 0.028);
185
+ return /* @__PURE__ */ React.createElement(core.Box, { ...getStyles("clockContainer") }, /* @__PURE__ */ React.createElement(core.Box, { ...getStyles("glassWrapper") }, /* @__PURE__ */ React.createElement(core.Box, { ...getStyles("clockFace") }, /* @__PURE__ */ React.createElement(core.Box, { ...getStyles("hourMarks") }, hourTicksOpacity !== 0 && Array.from({ length: 12 }, (_, i) => /* @__PURE__ */ React.createElement(
186
+ core.Box,
187
+ {
188
+ key: `hour-tick-${i}`,
189
+ ...getStyles("hourTick", {
190
+ style: {
191
+ top: tickOffset,
192
+ left: "50%",
193
+ transformOrigin: `50% ${clockRadius - tickOffset}px`,
194
+ transform: `translateX(-50%) rotate(${i * 30}deg)`
195
+ }
196
+ })
197
+ }
198
+ )), minuteTicksOpacity !== 0 && Array.from({ length: 60 }, (_, i) => {
199
+ if (i % 5 === 0) {
200
+ return null;
201
+ }
202
+ return /* @__PURE__ */ React.createElement(
203
+ core.Box,
204
+ {
205
+ key: `minute-tick-${i}`,
206
+ ...getStyles("minuteTick", {
207
+ style: {
208
+ top: tickOffset,
209
+ left: "50%",
210
+ transformOrigin: `50% ${clockRadius - tickOffset}px`,
211
+ transform: `translateX(-50%) rotate(${i * 6}deg)`
212
+ }
213
+ })
214
+ }
215
+ );
216
+ }), primaryNumbersOpacity !== 0 && [12, 3, 6, 9].map((num) => {
217
+ const i = num === 12 ? 0 : num;
218
+ const angle = (i * 30 - 90) * (Math.PI / 180);
219
+ const x = Math.round(clockRadius + Math.cos(angle) * numberRadius);
220
+ const y = Math.round(clockRadius + Math.sin(angle) * numberRadius);
221
+ return /* @__PURE__ */ React.createElement(
222
+ core.Text,
223
+ {
224
+ key: `primary-number-${num}`,
225
+ ...primaryNumbersProps,
226
+ ...getStyles("primaryNumber", {
227
+ className: getStyles("number").className,
228
+ style: {
229
+ left: x,
230
+ top: y
231
+ }
232
+ })
233
+ },
234
+ num
235
+ );
236
+ }), secondaryNumbersOpacity !== 0 && [1, 2, 4, 5, 7, 8, 10, 11].map((num) => {
237
+ const i = num;
238
+ const angle = (i * 30 - 90) * (Math.PI / 180);
239
+ const x = Math.round(clockRadius + Math.cos(angle) * numberRadius);
240
+ const y = Math.round(clockRadius + Math.sin(angle) * numberRadius);
241
+ return /* @__PURE__ */ React.createElement(
242
+ core.Text,
243
+ {
244
+ key: `secondary-number-${num}`,
245
+ ...secondaryNumbersProps,
246
+ ...getStyles("secondaryNumber", {
247
+ className: getStyles("number").className,
248
+ style: {
249
+ left: x,
250
+ top: y
251
+ }
252
+ })
253
+ },
254
+ num
255
+ );
256
+ })), (hourHandOpacity ?? defaultProps.hourHandOpacity) !== 0 && /* @__PURE__ */ React.createElement(
257
+ core.Box,
258
+ {
259
+ ...getStyles("hand", {
260
+ className: getStyles("hourHand").className,
261
+ style: {
262
+ width: Math.round(size * (hourHandSize ?? defaultProps.hourHandSize) * 100) / 100,
263
+ height: calculatedHourHandLength,
264
+ opacity: Math.round((hourHandOpacity ?? defaultProps.hourHandOpacity) * 100) / 100,
265
+ bottom: clockRadius,
266
+ left: clockRadius,
267
+ marginLeft: Math.round(-(size * (hourHandSize ?? defaultProps.hourHandSize)) / 2 * 100) / 100,
268
+ borderRadius: `${Math.round(size * (hourHandSize ?? defaultProps.hourHandSize) * 100) / 100}px`,
269
+ transform: `rotate(${Math.round(hourAngle * 100) / 100}deg)`
270
+ }
271
+ })
272
+ }
273
+ ), (minuteHandOpacity ?? defaultProps.minuteHandOpacity) !== 0 && /* @__PURE__ */ React.createElement(
274
+ core.Box,
275
+ {
276
+ ...getStyles("hand", {
277
+ className: getStyles("minuteHand").className,
278
+ style: {
279
+ width: Math.round(size * (minuteHandSize ?? defaultProps.minuteHandSize) * 100) / 100,
280
+ height: calculatedMinuteHandLength,
281
+ opacity: Math.round((minuteHandOpacity ?? defaultProps.minuteHandOpacity) * 100) / 100,
282
+ bottom: clockRadius,
283
+ left: clockRadius,
284
+ marginLeft: Math.round(
285
+ -(size * (minuteHandSize ?? defaultProps.minuteHandSize)) / 2 * 100
286
+ ) / 100,
287
+ borderRadius: `${Math.round(size * (minuteHandSize ?? defaultProps.minuteHandSize) * 100) / 100}px`,
288
+ transform: `rotate(${Math.round(minuteAngle * 100) / 100}deg)`
289
+ }
290
+ })
291
+ }
292
+ ), (secondHandOpacity ?? defaultProps.secondHandOpacity) !== 0 && /* @__PURE__ */ React.createElement(
293
+ core.Box,
294
+ {
295
+ ...getStyles("secondHandContainer", {
296
+ style: {
297
+ width: Math.round(size * (secondHandSize ?? defaultProps.secondHandSize) * 100) / 100,
298
+ height: calculatedSecondHandLength,
299
+ top: clockRadius - calculatedSecondHandLength,
300
+ left: clockRadius,
301
+ marginLeft: Math.round(
302
+ -(size * (secondHandSize ?? defaultProps.secondHandSize)) / 2 * 100
303
+ ) / 100,
304
+ transformOrigin: `${Math.round(size * (secondHandSize ?? defaultProps.secondHandSize) / 2 * 100) / 100}px ${calculatedSecondHandLength}px`,
305
+ transform: `rotate(${Math.round(secondAngle * 100) / 100}deg)`
306
+ }
307
+ })
308
+ },
309
+ /* @__PURE__ */ React.createElement(
310
+ core.Box,
311
+ {
312
+ ...getStyles("secondHand", {
313
+ style: {
314
+ width: Math.round(size * (secondHandSize ?? defaultProps.secondHandSize) * 100) / 100,
315
+ height: calculatedSecondHandLength,
316
+ opacity: Math.round((secondHandOpacity ?? defaultProps.secondHandOpacity) * 100) / 100
317
+ }
318
+ })
319
+ }
320
+ ),
321
+ /* @__PURE__ */ React.createElement(
322
+ core.Box,
323
+ {
324
+ ...getStyles("secondHandCounterweight", {
325
+ style: {
326
+ width: Math.round(size * 6e-3 * 3 * 100) / 100,
327
+ opacity: Math.round((secondHandOpacity ?? defaultProps.secondHandOpacity) * 100) / 100,
328
+ left: Math.round(
329
+ size * (secondHandSize ?? defaultProps.secondHandSize) / 2 - size * 6e-3 * 3 / 2
330
+ )
331
+ }
332
+ })
333
+ }
334
+ )
335
+ ), /* @__PURE__ */ React.createElement(core.Box, { ...getStyles("centerBlur") }), /* @__PURE__ */ React.createElement(
336
+ core.Box,
337
+ {
338
+ ...getStyles("centerDot", {
339
+ style: {
340
+ width: centerSize,
341
+ height: centerSize,
342
+ opacity: Math.round((secondHandOpacity ?? defaultProps.secondHandOpacity) * 100) / 100,
343
+ top: Math.round(clockRadius - centerSize / 2),
344
+ left: Math.round(clockRadius - centerSize / 2)
345
+ }
346
+ })
347
+ }
348
+ ))));
349
+ };
350
+ const Clock = core.factory((_props, ref) => {
351
+ const props = core.useProps("Clock", defaultProps, _props);
352
+ const [time, setTime] = React.useState(/* @__PURE__ */ new Date());
353
+ const [hasMounted, setHasMounted] = React.useState(false);
354
+ const intervalRef = React.useRef(null);
355
+ const startTimeRef = React.useRef(null);
356
+ const realStartTimeRef = React.useRef(null);
357
+ const {
358
+ // Clock-specific props that should not be passed to DOM
359
+ size,
360
+ color,
361
+ hourTicksColor,
362
+ hourTicksOpacity,
363
+ minuteTicksColor,
364
+ minuteTicksOpacity,
365
+ primaryNumbersColor,
366
+ primaryNumbersOpacity,
367
+ secondaryNumbersColor,
368
+ secondaryNumbersOpacity,
369
+ secondHandBehavior,
370
+ secondHandColor,
371
+ secondHandOpacity,
372
+ secondHandLength,
373
+ secondHandSize,
374
+ minuteHandColor,
375
+ minuteHandOpacity,
376
+ minuteHandSize,
377
+ minuteHandLength,
378
+ hourHandColor,
379
+ hourHandOpacity,
380
+ hourHandSize,
381
+ hourHandLength,
382
+ hourTicksOpacity: _hourTicksOpacity,
383
+ minuteTicksOpacity: _minuteTicksOpacity,
384
+ hourNumbersDistance,
385
+ primaryNumbersProps,
386
+ secondaryNumbersProps,
387
+ timezone: timezone2,
388
+ running,
389
+ value,
390
+ // Styles API props
391
+ classNames,
392
+ style,
393
+ styles,
394
+ unstyled,
395
+ vars,
396
+ className,
397
+ ...others
398
+ } = props;
399
+ const getStyles = core.useStyles({
400
+ name: "Clock",
401
+ props,
402
+ classes: Clock_module,
403
+ className,
404
+ style,
405
+ classNames,
406
+ styles,
407
+ unstyled,
408
+ vars,
409
+ varsResolver
410
+ });
411
+ const effectiveSize = Math.round(
412
+ core.px(
413
+ core.getSize(
414
+ typeof size === "string" && size in defaultClockSizes ? defaultClockSizes[size] : size || defaultProps.size,
415
+ "clock-size"
416
+ )
417
+ )
418
+ );
419
+ React.useEffect(() => {
420
+ setHasMounted(true);
421
+ }, []);
422
+ const getEffectiveTime = () => {
423
+ const parsedValue = parseTimeValue(value);
424
+ if (!running) {
425
+ if (parsedValue) {
426
+ return parsedValue;
427
+ }
428
+ return time;
429
+ }
430
+ if (parsedValue && startTimeRef.current && realStartTimeRef.current) {
431
+ const now = /* @__PURE__ */ new Date();
432
+ const elapsed = now.getTime() - realStartTimeRef.current.getTime();
433
+ return new Date(startTimeRef.current.getTime() + elapsed);
434
+ }
435
+ return time;
436
+ };
437
+ React.useEffect(() => {
438
+ if (intervalRef.current) {
439
+ clearInterval(intervalRef.current);
440
+ intervalRef.current = null;
441
+ }
442
+ if (!running) {
443
+ return;
444
+ }
445
+ const parsedValue = parseTimeValue(value);
446
+ if (parsedValue) {
447
+ startTimeRef.current = parsedValue;
448
+ realStartTimeRef.current = /* @__PURE__ */ new Date();
449
+ } else {
450
+ startTimeRef.current = null;
451
+ realStartTimeRef.current = null;
452
+ }
453
+ let interval = 1e3;
454
+ switch (secondHandBehavior) {
455
+ case "smooth":
456
+ interval = 16;
457
+ break;
458
+ case "tick-half":
459
+ interval = 500;
460
+ break;
461
+ case "tick-high-freq":
462
+ interval = 125;
463
+ break;
464
+ case "tick":
465
+ default:
466
+ interval = 1e3;
467
+ break;
468
+ }
469
+ const updateTime = () => {
470
+ setTime(/* @__PURE__ */ new Date());
471
+ };
472
+ updateTime();
473
+ intervalRef.current = setInterval(updateTime, interval);
474
+ return () => {
475
+ if (intervalRef.current) {
476
+ clearInterval(intervalRef.current);
477
+ }
478
+ };
479
+ }, [running, value, secondHandBehavior, secondHandOpacity]);
480
+ if (!hasMounted) {
481
+ return /* @__PURE__ */ React.createElement(core.Box, { ...getStyles("root"), ref, ...others }, /* @__PURE__ */ React.createElement(core.Box, { ...getStyles("clockContainer") }, /* @__PURE__ */ React.createElement(core.Box, { ...getStyles("glassWrapper") }, /* @__PURE__ */ React.createElement(core.Box, { ...getStyles("clockFace") }, /* @__PURE__ */ React.createElement(core.Box, { ...getStyles("hourMarks") }, (hourTicksOpacity || 1) !== 0 && Array.from({ length: 12 }, (_, i) => /* @__PURE__ */ React.createElement(
482
+ core.Box,
483
+ {
484
+ key: `hour-tick-${i}`,
485
+ ...getStyles("hourTick", {
486
+ style: {
487
+ top: Math.round(effectiveSize * 0.028),
488
+ left: "50%",
489
+ transformOrigin: `50% ${Math.round(effectiveSize / 2) - Math.round(effectiveSize * 0.028)}px`,
490
+ transform: `translateX(-50%) rotate(${i * 30}deg)`
491
+ }
492
+ })
493
+ }
494
+ )), (minuteTicksOpacity || 1) !== 0 && Array.from({ length: 60 }, (_, i) => {
495
+ if (i % 5 === 0) {
496
+ return null;
497
+ }
498
+ return /* @__PURE__ */ React.createElement(
499
+ core.Box,
500
+ {
501
+ key: `minute-tick-${i}`,
502
+ ...getStyles("minuteTick", {
503
+ style: {
504
+ top: Math.round(effectiveSize * 0.028),
505
+ left: "50%",
506
+ transformOrigin: `50% ${Math.round(effectiveSize / 2) - Math.round(effectiveSize * 0.028)}px`,
507
+ transform: `translateX(-50%) rotate(${i * 6}deg)`
508
+ }
509
+ })
510
+ }
511
+ );
512
+ }), (primaryNumbersOpacity || 1) !== 0 && [12, 3, 6, 9].map((num) => {
513
+ const i = num === 12 ? 0 : num;
514
+ const angle = (i * 30 - 90) * (Math.PI / 180);
515
+ const clockRadius = Math.round(effectiveSize / 2);
516
+ const numberRadius = Math.round(clockRadius * (hourNumbersDistance || 0.75));
517
+ const x = Math.round(clockRadius + Math.cos(angle) * numberRadius);
518
+ const y = Math.round(clockRadius + Math.sin(angle) * numberRadius);
519
+ return /* @__PURE__ */ React.createElement(
520
+ core.Text,
521
+ {
522
+ key: `primary-number-${num}`,
523
+ ...primaryNumbersProps,
524
+ ...getStyles("primaryNumber", {
525
+ className: getStyles("number").className,
526
+ style: {
527
+ left: x,
528
+ top: y
529
+ }
530
+ })
531
+ },
532
+ num
533
+ );
534
+ }), (secondaryNumbersOpacity || 1) !== 0 && [1, 2, 4, 5, 7, 8, 10, 11].map((num) => {
535
+ const i = num;
536
+ const angle = (i * 30 - 90) * (Math.PI / 180);
537
+ const clockRadius = Math.round(effectiveSize / 2);
538
+ const numberRadius = Math.round(clockRadius * (hourNumbersDistance || 0.75));
539
+ const x = Math.round(clockRadius + Math.cos(angle) * numberRadius);
540
+ const y = Math.round(clockRadius + Math.sin(angle) * numberRadius);
541
+ return /* @__PURE__ */ React.createElement(
542
+ core.Text,
543
+ {
544
+ key: `secondary-number-${num}`,
545
+ ...secondaryNumbersProps,
546
+ ...getStyles("secondaryNumber", {
547
+ className: getStyles("number").className,
548
+ style: {
549
+ left: x,
550
+ top: y
551
+ }
552
+ })
553
+ },
554
+ num
555
+ );
556
+ }))))));
557
+ }
558
+ const effectiveTime = getEffectiveTime();
559
+ return /* @__PURE__ */ React.createElement(core.Box, { ...getStyles("root"), ref, ...others }, /* @__PURE__ */ React.createElement(
560
+ RealClock,
561
+ {
562
+ time: effectiveTime,
563
+ getStyles,
564
+ effectiveSize,
565
+ ...props
566
+ }
567
+ ));
568
+ });
569
+ Clock.classes = Clock_module;
570
+ Clock.displayName = "Clock";
571
+
572
+ exports.Clock = Clock;
573
+ exports.defaultProps = defaultProps;
574
+ //# sourceMappingURL=Clock.cjs.map