@gfazioli/mantine-clock 2.1.17 → 3.1.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.
Files changed (61) hide show
  1. package/README.md +9 -4
  2. package/dist/cjs/Clock.cjs +240 -563
  3. package/dist/cjs/Clock.cjs.map +1 -1
  4. package/dist/cjs/Clock.module.css.cjs +1 -1
  5. package/dist/cjs/ClockDigital.cjs +123 -0
  6. package/dist/cjs/ClockDigital.cjs.map +1 -0
  7. package/dist/cjs/ClockDigital.module.css.cjs +7 -0
  8. package/dist/cjs/ClockDigital.module.css.cjs.map +1 -0
  9. package/dist/cjs/ClockDigitalMediaVariables.cjs +76 -0
  10. package/dist/cjs/ClockDigitalMediaVariables.cjs.map +1 -0
  11. package/dist/cjs/ClockFaceStatic.cjs +96 -0
  12. package/dist/cjs/ClockFaceStatic.cjs.map +1 -0
  13. package/dist/cjs/RealClock.cjs +379 -0
  14. package/dist/cjs/RealClock.cjs.map +1 -0
  15. package/dist/cjs/clock-utils.cjs +63 -0
  16. package/dist/cjs/clock-utils.cjs.map +1 -0
  17. package/dist/cjs/geometry.cjs +217 -0
  18. package/dist/cjs/geometry.cjs.map +1 -0
  19. package/dist/cjs/hooks/use-clock-count-down.cjs +92 -124
  20. package/dist/cjs/hooks/use-clock-count-down.cjs.map +1 -1
  21. package/dist/cjs/hooks/use-clock.cjs +27 -38
  22. package/dist/cjs/hooks/use-clock.cjs.map +1 -1
  23. package/dist/cjs/index.cjs +6 -0
  24. package/dist/cjs/index.cjs.map +1 -1
  25. package/dist/esm/Clock.mjs +240 -563
  26. package/dist/esm/Clock.mjs.map +1 -1
  27. package/dist/esm/Clock.module.css.mjs +1 -1
  28. package/dist/esm/ClockDigital.mjs +121 -0
  29. package/dist/esm/ClockDigital.mjs.map +1 -0
  30. package/dist/esm/ClockDigital.module.css.mjs +5 -0
  31. package/dist/esm/ClockDigital.module.css.mjs.map +1 -0
  32. package/dist/esm/ClockDigitalMediaVariables.mjs +74 -0
  33. package/dist/esm/ClockDigitalMediaVariables.mjs.map +1 -0
  34. package/dist/esm/ClockFaceStatic.mjs +94 -0
  35. package/dist/esm/ClockFaceStatic.mjs.map +1 -0
  36. package/dist/esm/RealClock.mjs +377 -0
  37. package/dist/esm/RealClock.mjs.map +1 -0
  38. package/dist/esm/clock-utils.mjs +56 -0
  39. package/dist/esm/clock-utils.mjs.map +1 -0
  40. package/dist/esm/geometry.mjs +208 -0
  41. package/dist/esm/geometry.mjs.map +1 -0
  42. package/dist/esm/hooks/use-clock-count-down.mjs +92 -124
  43. package/dist/esm/hooks/use-clock-count-down.mjs.map +1 -1
  44. package/dist/esm/hooks/use-clock.mjs +27 -38
  45. package/dist/esm/hooks/use-clock.mjs.map +1 -1
  46. package/dist/esm/index.mjs +2 -0
  47. package/dist/esm/index.mjs.map +1 -1
  48. package/dist/styles.css +2 -2
  49. package/dist/styles.layer.css +2 -2
  50. package/dist/types/Clock.d.ts +73 -4
  51. package/dist/types/ClockDigital.d.ts +50 -0
  52. package/dist/types/ClockDigitalMediaVariables.d.ts +9 -0
  53. package/dist/types/ClockFaceStatic.d.ts +18 -0
  54. package/dist/types/RealClock.d.ts +12 -0
  55. package/dist/types/clock-utils.d.ts +23 -0
  56. package/dist/types/geometry.d.ts +92 -0
  57. package/dist/types/hooks/use-clock-count-down.d.ts +25 -28
  58. package/dist/types/hooks/use-clock.d.ts +14 -10
  59. package/dist/types/index.d.mts +5 -1
  60. package/dist/types/index.d.ts +5 -1
  61. package/package.json +15 -7
@@ -2,27 +2,21 @@
2
2
  'use strict';
3
3
 
4
4
  var dayjs = require('dayjs');
5
- var timezonePlugin = require('dayjs/plugin/timezone');
5
+ var timezone = require('dayjs/plugin/timezone');
6
6
  var utc = require('dayjs/plugin/utc');
7
7
  var React = require('react');
8
8
  var core = require('@mantine/core');
9
+ var hooks = require('@mantine/hooks');
10
+ var clockUtils = require('./clock-utils.cjs');
11
+ var ClockDigital = require('./ClockDigital.cjs');
12
+ var ClockFaceStatic = require('./ClockFaceStatic.cjs');
13
+ var geometry = require('./geometry.cjs');
14
+ var RealClock = require('./RealClock.cjs');
9
15
  var Clock_module = require('./Clock.module.css.cjs');
10
16
 
11
17
  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
- };
18
+ dayjs.extend(timezone);
19
+ const defaultProps = clockUtils.defaultClockProps;
26
20
  const defaultClockSizes = {
27
21
  xs: 100,
28
22
  sm: 200,
@@ -30,464 +24,41 @@ const defaultClockSizes = {
30
24
  lg: 480,
31
25
  xl: 512
32
26
  };
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
- secondsArcColor,
49
- minutesArcColor,
50
- hoursArcColor
51
- }) => {
52
- const sizeValue = size || "md";
53
- const clockSize = typeof sizeValue === "string" && sizeValue in defaultClockSizes ? defaultClockSizes[sizeValue] : sizeValue;
54
- const effectiveSize = Math.round(core.px(core.getSize(clockSize, "clock-size")));
55
- return {
56
- root: {
57
- "--clock-size": `${effectiveSize}px`,
58
- "--clock-color": core.parseThemeColor({
59
- color: color || "",
60
- theme
61
- }).value,
62
- "--clock-hour-ticks-color": core.parseThemeColor({
63
- color: hourTicksColor || "",
64
- theme
65
- }).value,
66
- "--clock-hour-ticks-opacity": (Math.round((hourTicksOpacity || 1) * 100) / 100).toString(),
67
- "--clock-minute-ticks-color": core.parseThemeColor({
68
- color: minuteTicksColor || "",
69
- theme
70
- }).value,
71
- "--clock-minute-ticks-opacity": (Math.round((minuteTicksOpacity || 1) * 100) / 100).toString(),
72
- "--clock-primary-numbers-color": core.parseThemeColor({
73
- color: primaryNumbersColor || "",
74
- theme
75
- }).value,
76
- "--clock-primary-numbers-opacity": (Math.round((primaryNumbersOpacity || 1) * 100) / 100).toString(),
77
- "--clock-secondary-numbers-color": core.parseThemeColor({
78
- color: secondaryNumbersColor || "",
79
- theme
80
- }).value,
81
- "--clock-secondary-numbers-opacity": (Math.round((secondaryNumbersOpacity || 1) * 100) / 100).toString(),
82
- "--clock-second-hand-color": core.parseThemeColor({
83
- color: secondHandColor || "",
84
- theme
85
- }).value,
86
- "--clock-minute-hand-color": core.parseThemeColor({
87
- color: minuteHandColor || "",
88
- theme
89
- }).value,
90
- "--clock-hour-hand-color": core.parseThemeColor({
91
- color: hourHandColor || "",
92
- theme
93
- }).value,
94
- "--clock-seconds-arc-color": core.parseThemeColor({
95
- color: secondsArcColor || secondHandColor || "",
96
- theme
97
- }).value,
98
- "--clock-minutes-arc-color": core.parseThemeColor({
99
- color: minutesArcColor || minuteHandColor || "",
100
- theme
101
- }).value,
102
- "--clock-hours-arc-color": core.parseThemeColor({
103
- color: hoursArcColor || hourHandColor || "",
104
- theme
105
- }).value
106
- }
107
- };
108
- }
109
- );
110
- const parseTimeValue = (value) => {
111
- if (!value) {
112
- return null;
113
- }
114
- if (value instanceof Date) {
115
- return value;
116
- }
117
- if (dayjs.isDayjs(value)) {
118
- return value.toDate();
119
- }
120
- if (typeof value === "string") {
121
- const timeRegex = /^(\d{1,2}):(\d{1,2})(?::(\d{1,2}))?$/;
122
- const match = value.match(timeRegex);
123
- if (match) {
124
- const hours = parseInt(match[1], 10);
125
- const minutes = parseInt(match[2], 10);
126
- const seconds = parseInt(match[3] || "0", 10);
127
- const date = /* @__PURE__ */ new Date();
128
- date.setHours(hours, minutes, seconds, 0);
129
- return date;
130
- }
131
- const parsed = new Date(value);
132
- if (!isNaN(parsed.getTime())) {
133
- return parsed;
134
- }
135
- }
136
- return null;
137
- };
138
- const RealClock = (props) => {
139
- const {
140
- time,
141
- timezone: timezone2,
142
- getStyles,
143
- effectiveSize,
144
- hourHandSize,
145
- minuteHandSize,
146
- secondHandSize,
147
- hourHandLength,
148
- minuteHandLength,
149
- secondHandLength,
150
- secondHandBehavior,
151
- secondHandOpacity,
152
- minuteHandOpacity,
153
- hourHandOpacity,
154
- hourTicksOpacity,
155
- minuteTicksOpacity,
156
- primaryNumbersOpacity,
157
- secondaryNumbersOpacity,
158
- hourNumbersDistance = 0.75,
159
- // Default distance for hour numbers
160
- primaryNumbersProps,
161
- secondaryNumbersProps,
162
- withSecondsArc,
163
- secondsArcFrom,
164
- secondsArcDirection = "clockwise",
165
- withMinutesArc,
166
- minutesArcFrom,
167
- minutesArcDirection = "clockwise",
168
- withHoursArc,
169
- hoursArcFrom,
170
- hoursArcDirection = "clockwise",
171
- secondsArcOpacity,
172
- minutesArcOpacity,
173
- hoursArcOpacity
174
- } = props;
175
- const timezoneTime = timezone2 && timezone2 !== "" ? dayjs(time).tz(timezone2) : dayjs(time);
176
- const hours = timezoneTime.hour() % 12;
177
- const minutes = timezoneTime.minute();
178
- const seconds = timezoneTime.second();
179
- const milliseconds = timezoneTime.millisecond();
180
- const hourAngle = hours * 30 + minutes * 0.5;
181
- const minuteAngle = minutes * 6;
182
- let secondAngle = 0;
183
- switch (secondHandBehavior) {
184
- case "tick":
185
- secondAngle = seconds * 6;
186
- break;
187
- case "tick-half":
188
- secondAngle = (seconds + Math.floor(milliseconds / 500) * 0.5) * 6;
189
- break;
190
- case "tick-high-freq":
191
- secondAngle = (seconds + Math.floor(milliseconds / 125) * 0.125) * 6;
192
- break;
193
- case "smooth":
194
- default:
195
- secondAngle = (seconds + milliseconds / 1e3) * 6;
196
- break;
197
- }
198
- const size = effectiveSize;
199
- const clockRadius = Math.round(size / 2);
200
- const numberRadius = Math.round(clockRadius * hourNumbersDistance);
201
- const calculatedHourHandLength = Math.round(
202
- clockRadius * (hourHandLength ?? defaultProps.hourHandLength)
203
- );
204
- const calculatedMinuteHandLength = Math.round(
205
- clockRadius * (minuteHandLength ?? defaultProps.minuteHandLength)
206
- );
207
- const calculatedSecondHandLength = Math.round(
208
- clockRadius * (secondHandLength ?? defaultProps.secondHandLength)
209
- );
210
- const centerSize = Math.round(size * 0.034);
211
- const tickOffset = Math.round(size * 0.028);
212
- const toClockAngle = (deg) => (deg % 360 + 360) % 360;
213
- const secAngleFromDate = (d) => {
214
- if (!d) {
215
- return 0;
27
+ const varsResolver = core.createVarsResolver((theme, props) => {
28
+ const c = (color) => core.parseThemeColor({ color, theme }).value;
29
+ return {
30
+ root: {
31
+ "--clock-size": void 0,
32
+ "--clock-color": c(props.color || ""),
33
+ "--clock-hour-ticks-color": c(props.hourTicksColor || ""),
34
+ "--clock-hour-ticks-opacity": clockUtils.round2(props.hourTicksOpacity ?? 1).toString(),
35
+ "--clock-minute-ticks-color": c(props.minuteTicksColor || ""),
36
+ "--clock-minute-ticks-opacity": clockUtils.round2(props.minuteTicksOpacity ?? 1).toString(),
37
+ "--clock-primary-numbers-color": c(props.primaryNumbersColor || ""),
38
+ "--clock-primary-numbers-opacity": clockUtils.round2(props.primaryNumbersOpacity ?? 1).toString(),
39
+ "--clock-secondary-numbers-color": c(props.secondaryNumbersColor || ""),
40
+ "--clock-secondary-numbers-opacity": clockUtils.round2(props.secondaryNumbersOpacity ?? 1).toString(),
41
+ "--clock-second-hand-color": c(props.secondHandColor || ""),
42
+ "--clock-minute-hand-color": c(props.minuteHandColor || ""),
43
+ "--clock-hour-hand-color": c(props.hourHandColor || ""),
44
+ "--clock-seconds-arc-color": c(props.secondsArcColor || props.secondHandColor || ""),
45
+ "--clock-minutes-arc-color": c(props.minutesArcColor || props.minuteHandColor || ""),
46
+ "--clock-hours-arc-color": c(props.hoursArcColor || props.hourHandColor || "")
216
47
  }
217
- const dt = timezone2 && timezone2 !== "" ? dayjs(d).tz(timezone2) : dayjs(d);
218
- const s = dt.second();
219
- const ms = dt.millisecond();
220
- return toClockAngle((s + ms / 1e3) * 6);
221
48
  };
222
- const minAngleFromDate = (d) => {
223
- if (!d) {
224
- return 0;
225
- }
226
- const dt = timezone2 && timezone2 !== "" ? dayjs(d).tz(timezone2) : dayjs(d);
227
- const m = dt.minute();
228
- return toClockAngle(m * 6);
229
- };
230
- const hourAngleFromDate = (d) => {
231
- if (!d) {
232
- return 0;
233
- }
234
- const dt = timezone2 && timezone2 !== "" ? dayjs(d).tz(timezone2) : dayjs(d);
235
- const h = dt.hour() % 12;
236
- const m = dt.minute();
237
- return toClockAngle(h * 30 + m * 0.5);
238
- };
239
- const describeSector = (cx, cy, r, startDeg, endDeg, direction) => {
240
- const start = toClockAngle(startDeg);
241
- const end = toClockAngle(endDeg);
242
- let delta = 0;
243
- if (direction === "clockwise") {
244
- delta = end - start;
245
- if (delta < 0) {
246
- delta += 360;
247
- }
248
- } else {
249
- delta = start - end;
250
- if (delta < 0) {
251
- delta += 360;
252
- }
253
- }
254
- const largeArc = delta >= 180 ? 1 : 0;
255
- const sweep = direction === "clockwise" ? 1 : 0;
256
- const aStart = start * Math.PI / 180;
257
- const aEnd = end * Math.PI / 180;
258
- const x1 = cx + r * Math.sin(aStart);
259
- const y1 = cy - r * Math.cos(aStart);
260
- const x2 = cx + r * Math.sin(aEnd);
261
- const y2 = cy - r * Math.cos(aEnd);
262
- const fmt = (n) => Number.isFinite(n) ? n.toFixed(3) : "0";
263
- return `M ${fmt(cx)} ${fmt(cy)} L ${fmt(x1)} ${fmt(y1)} A ${fmt(r)} ${fmt(r)} 0 ${largeArc} ${sweep} ${fmt(x2)} ${fmt(y2)} Z`;
264
- };
265
- const showSecArc = withSecondsArc === true && (secondsArcOpacity ?? 1) !== 0;
266
- const showMinArc = withMinutesArc === true && (minutesArcOpacity ?? 1) !== 0;
267
- const showHrArc = withHoursArc === true && (hoursArcOpacity ?? 1) !== 0;
268
- return /* @__PURE__ */ React.createElement(core.Box, { ...getStyles("clockContainer") }, /* @__PURE__ */ React.createElement(core.Box, { ...getStyles("glassWrapper") }, /* @__PURE__ */ React.createElement(core.Box, { ...getStyles("clockFace") }, (showSecArc || showMinArc || showHrArc) && /* @__PURE__ */ React.createElement(
269
- "svg",
270
- {
271
- ...getStyles("arcsLayer", { style: { width: size, height: size } }),
272
- viewBox: `0 0 ${size} ${size}`
273
- },
274
- showHrArc && /* @__PURE__ */ React.createElement(
275
- "path",
276
- {
277
- d: describeSector(
278
- clockRadius,
279
- clockRadius,
280
- calculatedHourHandLength,
281
- hourAngleFromDate(parseTimeValue(hoursArcFrom) ?? null),
282
- hourAngle,
283
- hoursArcDirection
284
- ),
285
- fill: "var(--clock-hours-arc-color-resolved)",
286
- fillOpacity: Math.round((hoursArcOpacity ?? 1) * 100) / 100
287
- }
288
- ),
289
- showMinArc && /* @__PURE__ */ React.createElement(
290
- "path",
291
- {
292
- d: describeSector(
293
- clockRadius,
294
- clockRadius,
295
- calculatedMinuteHandLength,
296
- minAngleFromDate(parseTimeValue(minutesArcFrom) ?? null),
297
- minuteAngle,
298
- minutesArcDirection
299
- ),
300
- fill: "var(--clock-minutes-arc-color-resolved)",
301
- fillOpacity: Math.round((minutesArcOpacity ?? 1) * 100) / 100
302
- }
303
- ),
304
- showSecArc && /* @__PURE__ */ React.createElement(
305
- "path",
306
- {
307
- d: describeSector(
308
- clockRadius,
309
- clockRadius,
310
- calculatedSecondHandLength,
311
- secAngleFromDate(parseTimeValue(secondsArcFrom) ?? null),
312
- secondAngle,
313
- secondsArcDirection
314
- ),
315
- fill: "var(--clock-seconds-arc-color-resolved)",
316
- fillOpacity: Math.round((secondsArcOpacity ?? 1) * 100) / 100
317
- }
318
- )
319
- ), /* @__PURE__ */ React.createElement(core.Box, { ...getStyles("hourMarks") }, hourTicksOpacity !== 0 && Array.from({ length: 12 }, (_, i) => /* @__PURE__ */ React.createElement(
320
- core.Box,
321
- {
322
- key: `hour-tick-${i}`,
323
- ...getStyles("hourTick", {
324
- style: {
325
- top: tickOffset,
326
- left: "50%",
327
- transformOrigin: `50% ${clockRadius - tickOffset}px`,
328
- transform: `translateX(-50%) rotate(${i * 30}deg)`
329
- }
330
- })
331
- }
332
- )), minuteTicksOpacity !== 0 && Array.from({ length: 60 }, (_, i) => {
333
- if (i % 5 === 0) {
334
- return null;
335
- }
336
- return /* @__PURE__ */ React.createElement(
337
- core.Box,
338
- {
339
- key: `minute-tick-${i}`,
340
- ...getStyles("minuteTick", {
341
- style: {
342
- top: tickOffset,
343
- left: "50%",
344
- transformOrigin: `50% ${clockRadius - tickOffset}px`,
345
- transform: `translateX(-50%) rotate(${i * 6}deg)`
346
- }
347
- })
348
- }
349
- );
350
- }), primaryNumbersOpacity !== 0 && [12, 3, 6, 9].map((num) => {
351
- const i = num === 12 ? 0 : num;
352
- const angle = (i * 30 - 90) * (Math.PI / 180);
353
- const x = Math.round(clockRadius + Math.cos(angle) * numberRadius);
354
- const y = Math.round(clockRadius + Math.sin(angle) * numberRadius);
355
- return /* @__PURE__ */ React.createElement(
356
- core.Text,
357
- {
358
- key: `primary-number-${num}`,
359
- ...primaryNumbersProps,
360
- ...getStyles("primaryNumber", {
361
- className: getStyles("number").className,
362
- style: {
363
- left: x,
364
- top: y
365
- }
366
- })
367
- },
368
- num
369
- );
370
- }), secondaryNumbersOpacity !== 0 && [1, 2, 4, 5, 7, 8, 10, 11].map((num) => {
371
- const i = num;
372
- const angle = (i * 30 - 90) * (Math.PI / 180);
373
- const x = Math.round(clockRadius + Math.cos(angle) * numberRadius);
374
- const y = Math.round(clockRadius + Math.sin(angle) * numberRadius);
375
- return /* @__PURE__ */ React.createElement(
376
- core.Text,
377
- {
378
- key: `secondary-number-${num}`,
379
- ...secondaryNumbersProps,
380
- ...getStyles("secondaryNumber", {
381
- className: getStyles("number").className,
382
- style: {
383
- left: x,
384
- top: y
385
- }
386
- })
387
- },
388
- num
389
- );
390
- })), (hourHandOpacity ?? defaultProps.hourHandOpacity) !== 0 && /* @__PURE__ */ React.createElement(
391
- core.Box,
392
- {
393
- ...getStyles("hand", {
394
- className: getStyles("hourHand").className,
395
- style: {
396
- width: Math.round(size * (hourHandSize ?? defaultProps.hourHandSize) * 100) / 100,
397
- height: calculatedHourHandLength,
398
- opacity: Math.round((hourHandOpacity ?? defaultProps.hourHandOpacity) * 100) / 100,
399
- bottom: clockRadius,
400
- left: clockRadius,
401
- marginLeft: Math.round(-(size * (hourHandSize ?? defaultProps.hourHandSize)) / 2 * 100) / 100,
402
- borderRadius: `${Math.round(size * (hourHandSize ?? defaultProps.hourHandSize) * 100) / 100}px`,
403
- transform: `rotate(${Math.round(hourAngle * 100) / 100}deg)`
404
- }
405
- })
406
- }
407
- ), (minuteHandOpacity ?? defaultProps.minuteHandOpacity) !== 0 && /* @__PURE__ */ React.createElement(
408
- core.Box,
409
- {
410
- ...getStyles("hand", {
411
- className: getStyles("minuteHand").className,
412
- style: {
413
- width: Math.round(size * (minuteHandSize ?? defaultProps.minuteHandSize) * 100) / 100,
414
- height: calculatedMinuteHandLength,
415
- opacity: Math.round((minuteHandOpacity ?? defaultProps.minuteHandOpacity) * 100) / 100,
416
- bottom: clockRadius,
417
- left: clockRadius,
418
- marginLeft: Math.round(
419
- -(size * (minuteHandSize ?? defaultProps.minuteHandSize)) / 2 * 100
420
- ) / 100,
421
- borderRadius: `${Math.round(size * (minuteHandSize ?? defaultProps.minuteHandSize) * 100) / 100}px`,
422
- transform: `rotate(${Math.round(minuteAngle * 100) / 100}deg)`
423
- }
424
- })
425
- }
426
- ), (secondHandOpacity ?? defaultProps.secondHandOpacity) !== 0 && /* @__PURE__ */ React.createElement(
427
- core.Box,
428
- {
429
- ...getStyles("secondHandContainer", {
430
- style: {
431
- width: Math.round(size * (secondHandSize ?? defaultProps.secondHandSize) * 100) / 100,
432
- height: calculatedSecondHandLength,
433
- top: clockRadius - calculatedSecondHandLength,
434
- left: clockRadius,
435
- marginLeft: Math.round(
436
- -(size * (secondHandSize ?? defaultProps.secondHandSize)) / 2 * 100
437
- ) / 100,
438
- transformOrigin: `${Math.round(size * (secondHandSize ?? defaultProps.secondHandSize) / 2 * 100) / 100}px ${calculatedSecondHandLength}px`,
439
- transform: `rotate(${Math.round(secondAngle * 100) / 100}deg)`
440
- }
441
- })
442
- },
443
- /* @__PURE__ */ React.createElement(
444
- core.Box,
445
- {
446
- ...getStyles("secondHand", {
447
- style: {
448
- width: Math.round(size * (secondHandSize ?? defaultProps.secondHandSize) * 100) / 100,
449
- height: calculatedSecondHandLength,
450
- opacity: Math.round((secondHandOpacity ?? defaultProps.secondHandOpacity) * 100) / 100
451
- }
452
- })
453
- }
454
- ),
455
- /* @__PURE__ */ React.createElement(
456
- core.Box,
457
- {
458
- ...getStyles("secondHandCounterweight", {
459
- style: {
460
- width: Math.round(size * 6e-3 * 3 * 100) / 100,
461
- opacity: Math.round((secondHandOpacity ?? defaultProps.secondHandOpacity) * 100) / 100,
462
- left: Math.round(
463
- size * (secondHandSize ?? defaultProps.secondHandSize) / 2 - size * 6e-3 * 3 / 2
464
- )
465
- }
466
- })
467
- }
468
- )
469
- ), /* @__PURE__ */ React.createElement(core.Box, { ...getStyles("centerBlur") }), /* @__PURE__ */ React.createElement(
470
- core.Box,
471
- {
472
- ...getStyles("centerDot", {
473
- style: {
474
- width: centerSize,
475
- height: centerSize,
476
- opacity: Math.round((secondHandOpacity ?? defaultProps.secondHandOpacity) * 100) / 100,
477
- top: Math.round(clockRadius - centerSize / 2),
478
- left: Math.round(clockRadius - centerSize / 2)
479
- }
480
- })
481
- }
482
- ))));
483
- };
49
+ });
484
50
  const Clock = core.factory((_props, ref) => {
485
51
  const props = core.useProps("Clock", defaultProps, _props);
486
52
  const [time, setTime] = React.useState(/* @__PURE__ */ new Date());
487
53
  const [hasMounted, setHasMounted] = React.useState(false);
488
54
  const intervalRef = React.useRef(null);
55
+ const rafRef = React.useRef(null);
489
56
  const startTimeRef = React.useRef(null);
490
57
  const realStartTimeRef = React.useRef(null);
58
+ const onTimeChange = hooks.useCallbackRef(_props.onTimeChange);
59
+ const containerRef = React.useRef(null);
60
+ const [measuredSize, setMeasuredSize] = React.useState(null);
61
+ const mergedRef = hooks.useMergedRef(ref, containerRef);
491
62
  const {
492
63
  // Clock-specific props that should not be passed to DOM
493
64
  size,
@@ -513,14 +84,13 @@ const Clock = core.factory((_props, ref) => {
513
84
  hourHandOpacity,
514
85
  hourHandSize,
515
86
  hourHandLength,
516
- hourTicksOpacity: _hourTicksOpacity,
517
- minuteTicksOpacity: _minuteTicksOpacity,
518
87
  hourNumbersDistance,
519
88
  primaryNumbersProps,
520
89
  secondaryNumbersProps,
521
90
  timezone: timezone2,
522
91
  running,
523
92
  value,
93
+ ariaLabel,
524
94
  withSecondsArc,
525
95
  secondsArcFrom,
526
96
  secondsArcDirection,
@@ -536,6 +106,17 @@ const Clock = core.factory((_props, ref) => {
536
106
  hoursArcDirection,
537
107
  hoursArcColor,
538
108
  hoursArcOpacity,
109
+ sectors,
110
+ faceContent,
111
+ animateOnMount,
112
+ animateOnMountDuration,
113
+ shape,
114
+ aspectRatio,
115
+ borderRadius,
116
+ onTimeChange: _onTimeChange,
117
+ renderHourHand,
118
+ renderMinuteHand,
119
+ renderSecondHand,
539
120
  // Styles API props
540
121
  classNames,
541
122
  style,
@@ -557,19 +138,48 @@ const Clock = core.factory((_props, ref) => {
557
138
  vars,
558
139
  varsResolver
559
140
  });
560
- const effectiveSize = Math.round(
141
+ const resolvedSize = core.useMatches(
142
+ typeof size === "object" && size !== null && !Array.isArray(size) ? size : { base: size ?? defaultProps.size }
143
+ );
144
+ const isAutoSize = resolvedSize === "auto";
145
+ const effectiveSize = isAutoSize ? measuredSize ?? 400 : Math.round(
561
146
  core.px(
562
147
  core.getSize(
563
- typeof size === "string" && size in defaultClockSizes ? defaultClockSizes[size] : size || defaultProps.size,
148
+ typeof resolvedSize === "string" && resolvedSize in defaultClockSizes ? defaultClockSizes[resolvedSize] : resolvedSize || defaultProps.size,
564
149
  "clock-size"
565
150
  )
566
151
  )
567
152
  );
153
+ const geometry$1 = React.useMemo(
154
+ () => geometry.createGeometry(effectiveSize, shape, { aspectRatio, borderRadius }),
155
+ [effectiveSize, shape, aspectRatio, borderRadius]
156
+ );
568
157
  React.useEffect(() => {
569
158
  setHasMounted(true);
570
159
  }, []);
160
+ React.useEffect(() => {
161
+ if (!isAutoSize || !hasMounted) {
162
+ return;
163
+ }
164
+ const element = containerRef.current;
165
+ if (!element || typeof ResizeObserver === "undefined") {
166
+ return;
167
+ }
168
+ const observer = new ResizeObserver((entries) => {
169
+ const entry = entries[0];
170
+ if (entry) {
171
+ const { width, height } = entry.contentRect;
172
+ const newSize = Math.round(Math.min(width, height));
173
+ if (newSize > 0) {
174
+ setMeasuredSize(newSize);
175
+ }
176
+ }
177
+ });
178
+ observer.observe(element);
179
+ return () => observer.disconnect();
180
+ }, [isAutoSize, hasMounted]);
571
181
  const getEffectiveTime = () => {
572
- const parsedValue = parseTimeValue(value);
182
+ const parsedValue = clockUtils.parseTimeValue(value);
573
183
  if (!running) {
574
184
  if (parsedValue) {
575
185
  return parsedValue;
@@ -588,10 +198,14 @@ const Clock = core.factory((_props, ref) => {
588
198
  clearInterval(intervalRef.current);
589
199
  intervalRef.current = null;
590
200
  }
201
+ if (rafRef.current !== null) {
202
+ cancelAnimationFrame(rafRef.current);
203
+ rafRef.current = null;
204
+ }
591
205
  if (!running) {
592
206
  return;
593
207
  }
594
- const parsedValue = parseTimeValue(value);
208
+ const parsedValue = clockUtils.parseTimeValue(value);
595
209
  if (parsedValue) {
596
210
  startTimeRef.current = parsedValue;
597
211
  realStartTimeRef.current = /* @__PURE__ */ new Date();
@@ -599,122 +213,185 @@ const Clock = core.factory((_props, ref) => {
599
213
  startTimeRef.current = null;
600
214
  realStartTimeRef.current = null;
601
215
  }
602
- let interval = 1e3;
603
- switch (secondHandBehavior) {
604
- case "smooth":
605
- interval = 16;
606
- break;
607
- case "tick-half":
608
- interval = 500;
609
- break;
610
- case "tick-high-freq":
611
- interval = 125;
612
- break;
613
- case "tick":
614
- default:
615
- interval = 1e3;
616
- break;
617
- }
216
+ const fireOnTimeChange = (now) => {
217
+ const tz = timezone2 && timezone2 !== "" ? dayjs(now).tz(timezone2) : dayjs(now);
218
+ onTimeChange({
219
+ hours: tz.hour(),
220
+ minutes: tz.minute(),
221
+ seconds: tz.second(),
222
+ milliseconds: tz.millisecond()
223
+ });
224
+ };
618
225
  const updateTime = () => {
619
- setTime(/* @__PURE__ */ new Date());
226
+ const now = /* @__PURE__ */ new Date();
227
+ setTime(now);
228
+ fireOnTimeChange(now);
620
229
  };
621
230
  updateTime();
622
- intervalRef.current = setInterval(updateTime, interval);
231
+ if (secondHandBehavior === "smooth") {
232
+ const animate = () => {
233
+ const now = /* @__PURE__ */ new Date();
234
+ setTime(now);
235
+ fireOnTimeChange(now);
236
+ rafRef.current = requestAnimationFrame(animate);
237
+ };
238
+ animate();
239
+ } else {
240
+ let interval = 1e3;
241
+ switch (secondHandBehavior) {
242
+ case "tick-half":
243
+ interval = 500;
244
+ break;
245
+ case "tick-high-freq":
246
+ interval = 125;
247
+ break;
248
+ case "tick":
249
+ default:
250
+ interval = 1e3;
251
+ break;
252
+ }
253
+ intervalRef.current = setInterval(updateTime, interval);
254
+ }
623
255
  return () => {
624
256
  if (intervalRef.current) {
625
257
  clearInterval(intervalRef.current);
626
258
  }
259
+ if (rafRef.current !== null) {
260
+ cancelAnimationFrame(rafRef.current);
261
+ }
627
262
  };
628
- }, [running, value, secondHandBehavior, secondHandOpacity]);
263
+ }, [running, value, secondHandBehavior, timezone2]);
629
264
  if (!hasMounted) {
630
- 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(
265
+ return /* @__PURE__ */ React.createElement(
631
266
  core.Box,
632
267
  {
633
- key: `hour-tick-${i}`,
634
- ...getStyles("hourTick", {
268
+ ...getStyles("root", {
635
269
  style: {
636
- top: Math.round(effectiveSize * 0.028),
637
- left: "50%",
638
- transformOrigin: `50% ${Math.round(effectiveSize / 2) - Math.round(effectiveSize * 0.028)}px`,
639
- transform: `translateX(-50%) rotate(${i * 30}deg)`
270
+ ...{ "--clock-size": `${effectiveSize}px` },
271
+ ...isAutoSize ? { width: "100%", height: "100%" } : { width: geometry$1.width, height: geometry$1.height }
640
272
  }
641
- })
642
- }
643
- )), (minuteTicksOpacity || 1) !== 0 && Array.from({ length: 60 }, (_, i) => {
644
- if (i % 5 === 0) {
645
- return null;
646
- }
647
- return /* @__PURE__ */ React.createElement(
273
+ }),
274
+ ref: mergedRef,
275
+ role: "img",
276
+ "aria-label": ariaLabel || "Clock",
277
+ ...others
278
+ },
279
+ /* @__PURE__ */ React.createElement(
648
280
  core.Box,
649
281
  {
650
- key: `minute-tick-${i}`,
651
- ...getStyles("minuteTick", {
282
+ ...getStyles("clockContainer", {
652
283
  style: {
653
- top: Math.round(effectiveSize * 0.028),
654
- left: "50%",
655
- transformOrigin: `50% ${Math.round(effectiveSize / 2) - Math.round(effectiveSize * 0.028)}px`,
656
- transform: `translateX(-50%) rotate(${i * 6}deg)`
657
- }
658
- })
659
- }
660
- );
661
- }), (primaryNumbersOpacity || 1) !== 0 && [12, 3, 6, 9].map((num) => {
662
- const i = num === 12 ? 0 : num;
663
- const angle = (i * 30 - 90) * (Math.PI / 180);
664
- const clockRadius = Math.round(effectiveSize / 2);
665
- const numberRadius = Math.round(clockRadius * (hourNumbersDistance || 0.75));
666
- const x = Math.round(clockRadius + Math.cos(angle) * numberRadius);
667
- const y = Math.round(clockRadius + Math.sin(angle) * numberRadius);
668
- return /* @__PURE__ */ React.createElement(
669
- core.Text,
670
- {
671
- key: `primary-number-${num}`,
672
- ...primaryNumbersProps,
673
- ...getStyles("primaryNumber", {
674
- className: getStyles("number").className,
675
- style: {
676
- left: x,
677
- top: y
284
+ width: geometry$1.width,
285
+ height: geometry$1.height
678
286
  }
679
287
  })
680
288
  },
681
- num
682
- );
683
- }), (secondaryNumbersOpacity || 1) !== 0 && [1, 2, 4, 5, 7, 8, 10, 11].map((num) => {
684
- const i = num;
685
- const angle = (i * 30 - 90) * (Math.PI / 180);
686
- const clockRadius = Math.round(effectiveSize / 2);
687
- const numberRadius = Math.round(clockRadius * (hourNumbersDistance || 0.75));
688
- const x = Math.round(clockRadius + Math.cos(angle) * numberRadius);
689
- const y = Math.round(clockRadius + Math.sin(angle) * numberRadius);
690
- return /* @__PURE__ */ React.createElement(
691
- core.Text,
692
- {
693
- key: `secondary-number-${num}`,
694
- ...secondaryNumbersProps,
695
- ...getStyles("secondaryNumber", {
696
- className: getStyles("number").className,
697
- style: {
698
- left: x,
699
- top: y
700
- }
701
- })
702
- },
703
- num
704
- );
705
- }))))));
289
+ /* @__PURE__ */ React.createElement(
290
+ core.Box,
291
+ {
292
+ ...getStyles("glassWrapper", {
293
+ style: { width: geometry$1.width, height: geometry$1.height }
294
+ })
295
+ },
296
+ /* @__PURE__ */ React.createElement(
297
+ core.Box,
298
+ {
299
+ ...getStyles("clockFace", {
300
+ style: {
301
+ width: geometry$1.width,
302
+ height: geometry$1.height,
303
+ borderRadius: geometry$1.borderRadius(),
304
+ clipPath: geometry$1.clipPath()
305
+ }
306
+ })
307
+ },
308
+ faceContent && /* @__PURE__ */ React.createElement(core.Box, { ...getStyles("faceContent") }, faceContent),
309
+ /* @__PURE__ */ React.createElement(
310
+ ClockFaceStatic.ClockFaceStatic,
311
+ {
312
+ getStyles,
313
+ effectiveSize,
314
+ geometry: geometry$1,
315
+ hourTicksOpacity,
316
+ minuteTicksOpacity,
317
+ primaryNumbersOpacity,
318
+ secondaryNumbersOpacity,
319
+ hourNumbersDistance,
320
+ primaryNumbersProps,
321
+ secondaryNumbersProps
322
+ }
323
+ )
324
+ )
325
+ )
326
+ )
327
+ );
706
328
  }
707
329
  const effectiveTime = getEffectiveTime();
708
- return /* @__PURE__ */ React.createElement(core.Box, { ...getStyles("root"), ref, ...others }, /* @__PURE__ */ React.createElement(
709
- RealClock,
330
+ const timezoneTime = timezone2 && timezone2 !== "" ? dayjs(effectiveTime).tz(timezone2) : dayjs(effectiveTime);
331
+ const computedAriaLabel = ariaLabel || `Clock showing ${String(timezoneTime.hour()).padStart(2, "0")}:${String(timezoneTime.minute()).padStart(2, "0")}:${String(timezoneTime.second()).padStart(2, "0")}`;
332
+ return /* @__PURE__ */ React.createElement(
333
+ core.Box,
710
334
  {
711
- time: effectiveTime,
712
- getStyles,
713
- effectiveSize,
714
- ...props
715
- }
716
- ));
335
+ ...getStyles("root", {
336
+ style: {
337
+ ...{ "--clock-size": `${effectiveSize}px` },
338
+ ...isAutoSize ? { width: "100%", height: "100%" } : { width: geometry$1.width, height: geometry$1.height }
339
+ }
340
+ }),
341
+ ref: mergedRef,
342
+ role: "img",
343
+ "aria-label": computedAriaLabel,
344
+ ...others
345
+ },
346
+ /* @__PURE__ */ React.createElement(
347
+ RealClock.RealClock,
348
+ {
349
+ time: effectiveTime,
350
+ getStyles,
351
+ effectiveSize,
352
+ geometry: geometry$1,
353
+ timezone: timezone2,
354
+ hourHandSize,
355
+ minuteHandSize,
356
+ secondHandSize,
357
+ hourHandLength,
358
+ minuteHandLength,
359
+ secondHandLength,
360
+ secondHandBehavior,
361
+ secondHandOpacity,
362
+ minuteHandOpacity,
363
+ hourHandOpacity,
364
+ hourTicksOpacity,
365
+ minuteTicksOpacity,
366
+ primaryNumbersOpacity,
367
+ secondaryNumbersOpacity,
368
+ hourNumbersDistance,
369
+ primaryNumbersProps,
370
+ secondaryNumbersProps,
371
+ withSecondsArc,
372
+ secondsArcFrom,
373
+ secondsArcDirection,
374
+ secondsArcOpacity,
375
+ withMinutesArc,
376
+ minutesArcFrom,
377
+ minutesArcDirection,
378
+ minutesArcOpacity,
379
+ withHoursArc,
380
+ hoursArcFrom,
381
+ hoursArcDirection,
382
+ hoursArcOpacity,
383
+ sectors,
384
+ faceContent,
385
+ animateOnMount,
386
+ animateOnMountDuration,
387
+ renderHourHand,
388
+ renderMinuteHand,
389
+ renderSecondHand
390
+ }
391
+ )
392
+ );
717
393
  });
394
+ Clock.Digital = ClockDigital.ClockDigital;
718
395
  Clock.classes = Clock_module;
719
396
  Clock.displayName = "Clock";
720
397