svelte-analog-clock 0.0.0-stage → 0.2.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.
- package/LICENSE +22 -0
- package/README.md +49 -2
- package/dist/Clock.css +62 -0
- package/dist/Clock.svelte +329 -0
- package/dist/Clock.svelte.d.ts +148 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +3 -0
- package/package.json +65 -4
- package/src/Clock.css +62 -0
- package/src/Clock.svelte +329 -0
- package/src/index.ts +8 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018–2026 Wojciech Maj
|
|
4
|
+
Copyright (c) 2026 Adhi Ravishankar
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,50 @@
|
|
|
1
|
-
#
|
|
1
|
+
# svelte-analog-clock
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
An analog clock for your Svelte 5 app. Svelte port of [React-Clock](https://github.com/wojtekmaj/react-clock).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install svelte-analog-clock
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Requires Svelte 5 or later.
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```svelte
|
|
16
|
+
<script lang="ts">
|
|
17
|
+
import Clock from 'svelte-analog-clock';
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<Clock value={new Date()} />
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Styles are imported automatically. Elements use BEM class names starting with `svelte-clock`.
|
|
24
|
+
|
|
25
|
+
## Props
|
|
26
|
+
|
|
27
|
+
| Prop name | Description | Default value |
|
|
28
|
+
| -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | ----------------------- |
|
|
29
|
+
| class | Class name(s) added along with `"svelte-clock"`. | n/a |
|
|
30
|
+
| formatHour | `(locale, hour) => string`, used for numbers rendered with `renderNumbers`. | `Intl.NumberFormat` |
|
|
31
|
+
| hourHandLength, minuteHandLength, secondHandLength | Hand lengths, in percents of the clock radius. | `50`, `70`, `90` |
|
|
32
|
+
| hourHandOppositeLength, minuteHandOppositeLength, secondHandOppositeLength | Lengths of the tails of the hands, in percents of the clock radius. | `10` |
|
|
33
|
+
| hourHandWidth, minuteHandWidth, secondHandWidth | Hand widths, in pixels. | `4`, `2`, `1` |
|
|
34
|
+
| hourMarksLength, minuteMarksLength | Mark lengths, in percents of the clock radius. | `10`, `6` |
|
|
35
|
+
| hourMarksWidth, minuteMarksWidth | Mark widths, in pixels. | `3`, `1` |
|
|
36
|
+
| locale | Locale used to format hour numbers. | User's browser settings |
|
|
37
|
+
| renderHourMarks, renderMinuteMarks | Whether to render marks. | `true` |
|
|
38
|
+
| renderMinuteHand, renderSecondHand | Whether to render hands. | `true` |
|
|
39
|
+
| renderNumbers | Whether to render hour numbers. | `false` |
|
|
40
|
+
| size | Clock size, in pixels. | `150` |
|
|
41
|
+
| useMillisecondPrecision | Whether the second hand moves smoothly. Only applies when `value` is `undefined`. | `false` |
|
|
42
|
+
| value | A `Date` or a time string such as `"22:15:30"`. `undefined` shows the current time and keeps ticking; `null` renders no hands. | current time |
|
|
43
|
+
|
|
44
|
+
## Part of a monorepo
|
|
45
|
+
|
|
46
|
+
`svelte-analog-clock` is part of the [svelte-datetime-pickers monorepo](https://github.com/adhiravishankar/svelte-datetime-pickers). See the [live demo and Storybook](https://adhiravishankar.github.io/svelte-datetime-pickers/).
|
|
47
|
+
|
|
48
|
+
## License
|
|
49
|
+
|
|
50
|
+
The MIT License.
|
package/dist/Clock.css
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
.svelte-clock {
|
|
2
|
+
display: block;
|
|
3
|
+
position: relative;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.svelte-clock,
|
|
7
|
+
.svelte-clock *,
|
|
8
|
+
.svelte-clock *:before,
|
|
9
|
+
.svelte-clock *:after {
|
|
10
|
+
-moz-box-sizing: border-box;
|
|
11
|
+
-webkit-box-sizing: border-box;
|
|
12
|
+
box-sizing: border-box;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.svelte-clock__face {
|
|
16
|
+
position: absolute;
|
|
17
|
+
top: 0;
|
|
18
|
+
bottom: 0;
|
|
19
|
+
left: 0;
|
|
20
|
+
right: 0;
|
|
21
|
+
border: 1px solid black;
|
|
22
|
+
border-radius: 50%;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.svelte-clock__hand {
|
|
26
|
+
position: absolute;
|
|
27
|
+
top: 0;
|
|
28
|
+
bottom: 0;
|
|
29
|
+
left: 50%;
|
|
30
|
+
right: 50%;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.svelte-clock__hand__body {
|
|
34
|
+
position: absolute;
|
|
35
|
+
background-color: black;
|
|
36
|
+
transform: translateX(-50%);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.svelte-clock__second-hand__body {
|
|
40
|
+
background-color: red;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.svelte-clock__mark {
|
|
44
|
+
position: absolute;
|
|
45
|
+
top: 0;
|
|
46
|
+
bottom: 0;
|
|
47
|
+
left: 50%;
|
|
48
|
+
right: 50%;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.svelte-clock__mark__body {
|
|
52
|
+
position: absolute;
|
|
53
|
+
background-color: black;
|
|
54
|
+
transform: translateX(-50%);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.svelte-clock__mark__number {
|
|
58
|
+
position: absolute;
|
|
59
|
+
left: -40px;
|
|
60
|
+
width: 80px;
|
|
61
|
+
text-align: center;
|
|
62
|
+
}
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import './Clock.css';
|
|
3
|
+
|
|
4
|
+
import type { ClassValue } from 'svelte/elements';
|
|
5
|
+
|
|
6
|
+
type ClockProps = {
|
|
7
|
+
/**
|
|
8
|
+
* Class name(s) that will be added along with `"svelte-clock"` to the main `<time>` element.
|
|
9
|
+
*/
|
|
10
|
+
class?: ClassValue;
|
|
11
|
+
/**
|
|
12
|
+
* Function that formats hour numbers rendered when `renderNumbers` is set. Receives the locale and an hour from 1 to 12.
|
|
13
|
+
*/
|
|
14
|
+
formatHour?: (locale: string | undefined, hour: number) => string;
|
|
15
|
+
/**
|
|
16
|
+
* Hour hand length, in percents of the clock radius.
|
|
17
|
+
*
|
|
18
|
+
* @default 50
|
|
19
|
+
*/
|
|
20
|
+
hourHandLength?: number;
|
|
21
|
+
/**
|
|
22
|
+
* Hour hand opposite length, in percents of the clock radius.
|
|
23
|
+
*
|
|
24
|
+
* @default 10
|
|
25
|
+
*/
|
|
26
|
+
hourHandOppositeLength?: number;
|
|
27
|
+
/**
|
|
28
|
+
* Hour hand width, in pixels.
|
|
29
|
+
*
|
|
30
|
+
* @default 4
|
|
31
|
+
*/
|
|
32
|
+
hourHandWidth?: number;
|
|
33
|
+
/**
|
|
34
|
+
* Hour marks length, in percents of the clock radius.
|
|
35
|
+
*
|
|
36
|
+
* @default 10
|
|
37
|
+
*/
|
|
38
|
+
hourMarksLength?: number;
|
|
39
|
+
/**
|
|
40
|
+
* Hour marks width, in pixels.
|
|
41
|
+
*
|
|
42
|
+
* @default 3
|
|
43
|
+
*/
|
|
44
|
+
hourMarksWidth?: number;
|
|
45
|
+
/**
|
|
46
|
+
* Locale used to format hour numbers.
|
|
47
|
+
*
|
|
48
|
+
* @example 'hu-HU'
|
|
49
|
+
*/
|
|
50
|
+
locale?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Minute hand length, in percents of the clock radius.
|
|
53
|
+
*
|
|
54
|
+
* @default 70
|
|
55
|
+
*/
|
|
56
|
+
minuteHandLength?: number;
|
|
57
|
+
/**
|
|
58
|
+
* Minute hand opposite length, in percents of the clock radius.
|
|
59
|
+
*
|
|
60
|
+
* @default 10
|
|
61
|
+
*/
|
|
62
|
+
minuteHandOppositeLength?: number;
|
|
63
|
+
/**
|
|
64
|
+
* Minute hand width, in pixels.
|
|
65
|
+
*
|
|
66
|
+
* @default 2
|
|
67
|
+
*/
|
|
68
|
+
minuteHandWidth?: number;
|
|
69
|
+
/**
|
|
70
|
+
* Minute marks length, in percents of the clock radius.
|
|
71
|
+
*
|
|
72
|
+
* @default 6
|
|
73
|
+
*/
|
|
74
|
+
minuteMarksLength?: number;
|
|
75
|
+
/**
|
|
76
|
+
* Minute marks width, in pixels.
|
|
77
|
+
*
|
|
78
|
+
* @default 1
|
|
79
|
+
*/
|
|
80
|
+
minuteMarksWidth?: number;
|
|
81
|
+
/**
|
|
82
|
+
* Whether hour marks should be rendered.
|
|
83
|
+
*
|
|
84
|
+
* @default true
|
|
85
|
+
*/
|
|
86
|
+
renderHourMarks?: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Whether the minute hand should be rendered.
|
|
89
|
+
*
|
|
90
|
+
* @default true
|
|
91
|
+
*/
|
|
92
|
+
renderMinuteHand?: boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Whether minute marks should be rendered.
|
|
95
|
+
*
|
|
96
|
+
* @default true
|
|
97
|
+
*/
|
|
98
|
+
renderMinuteMarks?: boolean;
|
|
99
|
+
/**
|
|
100
|
+
* Whether hour numbers should be rendered.
|
|
101
|
+
*
|
|
102
|
+
* @default false
|
|
103
|
+
*/
|
|
104
|
+
renderNumbers?: boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Whether the second hand should be rendered.
|
|
107
|
+
*
|
|
108
|
+
* @default true
|
|
109
|
+
*/
|
|
110
|
+
renderSecondHand?: boolean;
|
|
111
|
+
/**
|
|
112
|
+
* Second hand length, in percents of the clock radius.
|
|
113
|
+
*
|
|
114
|
+
* @default 90
|
|
115
|
+
*/
|
|
116
|
+
secondHandLength?: number;
|
|
117
|
+
/**
|
|
118
|
+
* Second hand opposite length, in percents of the clock radius.
|
|
119
|
+
*
|
|
120
|
+
* @default 10
|
|
121
|
+
*/
|
|
122
|
+
secondHandOppositeLength?: number;
|
|
123
|
+
/**
|
|
124
|
+
* Second hand width, in pixels.
|
|
125
|
+
*
|
|
126
|
+
* @default 1
|
|
127
|
+
*/
|
|
128
|
+
secondHandWidth?: number;
|
|
129
|
+
/**
|
|
130
|
+
* Clock size, in pixels.
|
|
131
|
+
*
|
|
132
|
+
* @default 150
|
|
133
|
+
*/
|
|
134
|
+
size?: number;
|
|
135
|
+
/**
|
|
136
|
+
* Whether the second hand should move smoothly. Only applies when `value` is `undefined`.
|
|
137
|
+
*
|
|
138
|
+
* @default false
|
|
139
|
+
*/
|
|
140
|
+
useMillisecondPrecision?: boolean;
|
|
141
|
+
/**
|
|
142
|
+
* Time to display. When `undefined`, the clock shows the current time and keeps ticking. When `null`, no hands are rendered.
|
|
143
|
+
*
|
|
144
|
+
* @example new Date()
|
|
145
|
+
* @example '22:15:00'
|
|
146
|
+
*/
|
|
147
|
+
value?: Date | string | null;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
let {
|
|
151
|
+
class: className,
|
|
152
|
+
formatHour = (locale, hour) => new Intl.NumberFormat(locale).format(hour),
|
|
153
|
+
hourHandLength = 50,
|
|
154
|
+
hourHandOppositeLength = 10,
|
|
155
|
+
hourHandWidth = 4,
|
|
156
|
+
hourMarksLength = 10,
|
|
157
|
+
hourMarksWidth = 3,
|
|
158
|
+
locale,
|
|
159
|
+
minuteHandLength = 70,
|
|
160
|
+
minuteHandOppositeLength = 10,
|
|
161
|
+
minuteHandWidth = 2,
|
|
162
|
+
minuteMarksLength = 6,
|
|
163
|
+
minuteMarksWidth = 1,
|
|
164
|
+
renderHourMarks = true,
|
|
165
|
+
renderMinuteHand = true,
|
|
166
|
+
renderMinuteMarks = true,
|
|
167
|
+
renderNumbers = false,
|
|
168
|
+
renderSecondHand = true,
|
|
169
|
+
secondHandLength = 90,
|
|
170
|
+
secondHandOppositeLength = 10,
|
|
171
|
+
secondHandWidth = 1,
|
|
172
|
+
size = 150,
|
|
173
|
+
useMillisecondPrecision = false,
|
|
174
|
+
value,
|
|
175
|
+
}: ClockProps = $props();
|
|
176
|
+
|
|
177
|
+
const hourMarks = Array.from({ length: 12 }, (_, index) => index);
|
|
178
|
+
const minuteMarks = Array.from({ length: 60 }, (_, index) => index).filter(
|
|
179
|
+
(index) => index % 5 !== 0,
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
let now = $state(new Date());
|
|
183
|
+
|
|
184
|
+
$effect(() => {
|
|
185
|
+
if (value !== undefined) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
now = new Date();
|
|
190
|
+
|
|
191
|
+
if (useMillisecondPrecision) {
|
|
192
|
+
let frame = requestAnimationFrame(function tick() {
|
|
193
|
+
now = new Date();
|
|
194
|
+
frame = requestAnimationFrame(tick);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
return () => cancelAnimationFrame(frame);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const interval = setInterval(() => {
|
|
201
|
+
now = new Date();
|
|
202
|
+
}, 1000);
|
|
203
|
+
|
|
204
|
+
return () => clearInterval(interval);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
function parseTime(input: Date | string): Date | null {
|
|
208
|
+
if (input instanceof Date) {
|
|
209
|
+
return Number.isNaN(input.getTime()) ? null : input;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Time-only strings, e.g. "22:15" or "22:15:30"
|
|
213
|
+
const match = /^(\d{1,2}):(\d{2})(?::(\d{2})(?:\.(\d+))?)?$/.exec(input);
|
|
214
|
+
|
|
215
|
+
if (match) {
|
|
216
|
+
const date = new Date();
|
|
217
|
+
date.setHours(
|
|
218
|
+
Number(match[1]),
|
|
219
|
+
Number(match[2]),
|
|
220
|
+
Number(match[3] ?? 0),
|
|
221
|
+
Number((match[4] ?? '0').padEnd(3, '0').slice(0, 3)),
|
|
222
|
+
);
|
|
223
|
+
return date;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const date = new Date(input);
|
|
227
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const date = $derived(value === undefined ? now : value === null ? null : parseTime(value));
|
|
231
|
+
|
|
232
|
+
const angles = $derived.by(() => {
|
|
233
|
+
if (!date) {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const hours = date.getHours() % 12;
|
|
238
|
+
const minutes = date.getMinutes();
|
|
239
|
+
const seconds = date.getSeconds();
|
|
240
|
+
const milliseconds = useMillisecondPrecision ? date.getMilliseconds() : 0;
|
|
241
|
+
|
|
242
|
+
return {
|
|
243
|
+
hour: hours * 30 + minutes * 0.5 + seconds / 120,
|
|
244
|
+
minute: minutes * 6 + seconds * 0.1,
|
|
245
|
+
second: seconds * 6 + milliseconds * 0.006,
|
|
246
|
+
};
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
const datetime = $derived(date ? date.toISOString() : undefined);
|
|
250
|
+
</script>
|
|
251
|
+
|
|
252
|
+
{#snippet hand(
|
|
253
|
+
name: string,
|
|
254
|
+
angle: number,
|
|
255
|
+
length: number,
|
|
256
|
+
oppositeLength: number,
|
|
257
|
+
width: number,
|
|
258
|
+
)}
|
|
259
|
+
<div class="svelte-clock__hand svelte-clock__{name}-hand" style:transform="rotate({angle}deg)">
|
|
260
|
+
<div
|
|
261
|
+
class="svelte-clock__hand__body svelte-clock__{name}-hand__body"
|
|
262
|
+
style:width="{width}px"
|
|
263
|
+
style:top="{50 - length / 2}%"
|
|
264
|
+
style:bottom="{50 - oppositeLength / 2}%"
|
|
265
|
+
></div>
|
|
266
|
+
</div>
|
|
267
|
+
{/snippet}
|
|
268
|
+
|
|
269
|
+
<time class={['svelte-clock', className]} {datetime} style:width="{size}px" style:height="{size}px">
|
|
270
|
+
<div class="svelte-clock__face">
|
|
271
|
+
{#if renderMinuteMarks}
|
|
272
|
+
{#each minuteMarks as index (index)}
|
|
273
|
+
<div class="svelte-clock__mark svelte-clock__minute-mark" style:transform="rotate({index * 6}deg)">
|
|
274
|
+
<div
|
|
275
|
+
class="svelte-clock__mark__body svelte-clock__minute-mark__body"
|
|
276
|
+
style:width="{minuteMarksWidth}px"
|
|
277
|
+
style:bottom="{100 - minuteMarksLength / 2}%"
|
|
278
|
+
></div>
|
|
279
|
+
</div>
|
|
280
|
+
{/each}
|
|
281
|
+
{/if}
|
|
282
|
+
{#if renderHourMarks}
|
|
283
|
+
{#each hourMarks as index (index)}
|
|
284
|
+
<div class="svelte-clock__mark svelte-clock__hour-mark" style:transform="rotate({index * 30}deg)">
|
|
285
|
+
<div
|
|
286
|
+
class="svelte-clock__mark__body svelte-clock__hour-mark__body"
|
|
287
|
+
style:width="{hourMarksWidth}px"
|
|
288
|
+
style:bottom="{100 - hourMarksLength / 2}%"
|
|
289
|
+
></div>
|
|
290
|
+
</div>
|
|
291
|
+
{/each}
|
|
292
|
+
{/if}
|
|
293
|
+
{#if renderNumbers}
|
|
294
|
+
{#each hourMarks as index (index)}
|
|
295
|
+
{@const hourNumber = index === 0 ? 12 : index}
|
|
296
|
+
<div class="svelte-clock__mark" style:transform="rotate({index * 30}deg)">
|
|
297
|
+
<div
|
|
298
|
+
class="svelte-clock__mark__number"
|
|
299
|
+
style:top="{hourMarksLength}%"
|
|
300
|
+
style:transform="rotate({-index * 30}deg)"
|
|
301
|
+
>
|
|
302
|
+
{formatHour(locale, hourNumber)}
|
|
303
|
+
</div>
|
|
304
|
+
</div>
|
|
305
|
+
{/each}
|
|
306
|
+
{/if}
|
|
307
|
+
</div>
|
|
308
|
+
{#if angles}
|
|
309
|
+
{@render hand('hour', angles.hour, hourHandLength, hourHandOppositeLength, hourHandWidth)}
|
|
310
|
+
{#if renderMinuteHand}
|
|
311
|
+
{@render hand(
|
|
312
|
+
'minute',
|
|
313
|
+
angles.minute,
|
|
314
|
+
minuteHandLength,
|
|
315
|
+
minuteHandOppositeLength,
|
|
316
|
+
minuteHandWidth,
|
|
317
|
+
)}
|
|
318
|
+
{/if}
|
|
319
|
+
{#if renderSecondHand}
|
|
320
|
+
{@render hand(
|
|
321
|
+
'second',
|
|
322
|
+
angles.second,
|
|
323
|
+
secondHandLength,
|
|
324
|
+
secondHandOppositeLength,
|
|
325
|
+
secondHandWidth,
|
|
326
|
+
)}
|
|
327
|
+
{/if}
|
|
328
|
+
{/if}
|
|
329
|
+
</time>
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import './Clock.css';
|
|
2
|
+
import type { ClassValue } from 'svelte/elements';
|
|
3
|
+
type ClockProps = {
|
|
4
|
+
/**
|
|
5
|
+
* Class name(s) that will be added along with `"svelte-clock"` to the main `<time>` element.
|
|
6
|
+
*/
|
|
7
|
+
class?: ClassValue;
|
|
8
|
+
/**
|
|
9
|
+
* Function that formats hour numbers rendered when `renderNumbers` is set. Receives the locale and an hour from 1 to 12.
|
|
10
|
+
*/
|
|
11
|
+
formatHour?: (locale: string | undefined, hour: number) => string;
|
|
12
|
+
/**
|
|
13
|
+
* Hour hand length, in percents of the clock radius.
|
|
14
|
+
*
|
|
15
|
+
* @default 50
|
|
16
|
+
*/
|
|
17
|
+
hourHandLength?: number;
|
|
18
|
+
/**
|
|
19
|
+
* Hour hand opposite length, in percents of the clock radius.
|
|
20
|
+
*
|
|
21
|
+
* @default 10
|
|
22
|
+
*/
|
|
23
|
+
hourHandOppositeLength?: number;
|
|
24
|
+
/**
|
|
25
|
+
* Hour hand width, in pixels.
|
|
26
|
+
*
|
|
27
|
+
* @default 4
|
|
28
|
+
*/
|
|
29
|
+
hourHandWidth?: number;
|
|
30
|
+
/**
|
|
31
|
+
* Hour marks length, in percents of the clock radius.
|
|
32
|
+
*
|
|
33
|
+
* @default 10
|
|
34
|
+
*/
|
|
35
|
+
hourMarksLength?: number;
|
|
36
|
+
/**
|
|
37
|
+
* Hour marks width, in pixels.
|
|
38
|
+
*
|
|
39
|
+
* @default 3
|
|
40
|
+
*/
|
|
41
|
+
hourMarksWidth?: number;
|
|
42
|
+
/**
|
|
43
|
+
* Locale used to format hour numbers.
|
|
44
|
+
*
|
|
45
|
+
* @example 'hu-HU'
|
|
46
|
+
*/
|
|
47
|
+
locale?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Minute hand length, in percents of the clock radius.
|
|
50
|
+
*
|
|
51
|
+
* @default 70
|
|
52
|
+
*/
|
|
53
|
+
minuteHandLength?: number;
|
|
54
|
+
/**
|
|
55
|
+
* Minute hand opposite length, in percents of the clock radius.
|
|
56
|
+
*
|
|
57
|
+
* @default 10
|
|
58
|
+
*/
|
|
59
|
+
minuteHandOppositeLength?: number;
|
|
60
|
+
/**
|
|
61
|
+
* Minute hand width, in pixels.
|
|
62
|
+
*
|
|
63
|
+
* @default 2
|
|
64
|
+
*/
|
|
65
|
+
minuteHandWidth?: number;
|
|
66
|
+
/**
|
|
67
|
+
* Minute marks length, in percents of the clock radius.
|
|
68
|
+
*
|
|
69
|
+
* @default 6
|
|
70
|
+
*/
|
|
71
|
+
minuteMarksLength?: number;
|
|
72
|
+
/**
|
|
73
|
+
* Minute marks width, in pixels.
|
|
74
|
+
*
|
|
75
|
+
* @default 1
|
|
76
|
+
*/
|
|
77
|
+
minuteMarksWidth?: number;
|
|
78
|
+
/**
|
|
79
|
+
* Whether hour marks should be rendered.
|
|
80
|
+
*
|
|
81
|
+
* @default true
|
|
82
|
+
*/
|
|
83
|
+
renderHourMarks?: boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Whether the minute hand should be rendered.
|
|
86
|
+
*
|
|
87
|
+
* @default true
|
|
88
|
+
*/
|
|
89
|
+
renderMinuteHand?: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Whether minute marks should be rendered.
|
|
92
|
+
*
|
|
93
|
+
* @default true
|
|
94
|
+
*/
|
|
95
|
+
renderMinuteMarks?: boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Whether hour numbers should be rendered.
|
|
98
|
+
*
|
|
99
|
+
* @default false
|
|
100
|
+
*/
|
|
101
|
+
renderNumbers?: boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Whether the second hand should be rendered.
|
|
104
|
+
*
|
|
105
|
+
* @default true
|
|
106
|
+
*/
|
|
107
|
+
renderSecondHand?: boolean;
|
|
108
|
+
/**
|
|
109
|
+
* Second hand length, in percents of the clock radius.
|
|
110
|
+
*
|
|
111
|
+
* @default 90
|
|
112
|
+
*/
|
|
113
|
+
secondHandLength?: number;
|
|
114
|
+
/**
|
|
115
|
+
* Second hand opposite length, in percents of the clock radius.
|
|
116
|
+
*
|
|
117
|
+
* @default 10
|
|
118
|
+
*/
|
|
119
|
+
secondHandOppositeLength?: number;
|
|
120
|
+
/**
|
|
121
|
+
* Second hand width, in pixels.
|
|
122
|
+
*
|
|
123
|
+
* @default 1
|
|
124
|
+
*/
|
|
125
|
+
secondHandWidth?: number;
|
|
126
|
+
/**
|
|
127
|
+
* Clock size, in pixels.
|
|
128
|
+
*
|
|
129
|
+
* @default 150
|
|
130
|
+
*/
|
|
131
|
+
size?: number;
|
|
132
|
+
/**
|
|
133
|
+
* Whether the second hand should move smoothly. Only applies when `value` is `undefined`.
|
|
134
|
+
*
|
|
135
|
+
* @default false
|
|
136
|
+
*/
|
|
137
|
+
useMillisecondPrecision?: boolean;
|
|
138
|
+
/**
|
|
139
|
+
* Time to display. When `undefined`, the clock shows the current time and keeps ticking. When `null`, no hands are rendered.
|
|
140
|
+
*
|
|
141
|
+
* @example new Date()
|
|
142
|
+
* @example '22:15:00'
|
|
143
|
+
*/
|
|
144
|
+
value?: Date | string | null;
|
|
145
|
+
};
|
|
146
|
+
declare const Clock: import("svelte").Component<ClockProps, {}, "">;
|
|
147
|
+
type Clock = ReturnType<typeof Clock>;
|
|
148
|
+
export default Clock;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,67 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svelte-analog-clock",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "An analog clock for your Svelte app.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"analog-clock",
|
|
7
|
+
"clock",
|
|
8
|
+
"svelte",
|
|
9
|
+
"time"
|
|
10
|
+
],
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"author": {
|
|
13
|
+
"name": "Adhi Ravishankar",
|
|
14
|
+
"email": "adhiravishankar@gmail.com"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist/**/*",
|
|
18
|
+
"src/**/*",
|
|
19
|
+
"!**/*.spec.*"
|
|
20
|
+
],
|
|
21
|
+
"type": "module",
|
|
22
|
+
"sideEffects": [
|
|
23
|
+
"*.css",
|
|
24
|
+
"*.svelte"
|
|
25
|
+
],
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"svelte": "./dist/index.js",
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"svelte": "./dist/index.js",
|
|
32
|
+
"default": "./dist/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./dist/*.css": "./dist/*.css"
|
|
35
|
+
},
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public",
|
|
38
|
+
"provenance": true
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "svelte-package --input src --output dist",
|
|
42
|
+
"clean": "node -e \"fs.rmSync('./dist', { recursive: true, force: true })\"",
|
|
43
|
+
"format": "vp fmt --check",
|
|
44
|
+
"lint": "vp lint",
|
|
45
|
+
"prepack": "npm run clean && npm run build",
|
|
46
|
+
"test": "npm run lint && npm run tsc && npm run format && npm run unit",
|
|
47
|
+
"tsc": "svelte-check --tsconfig ./tsconfig.json",
|
|
48
|
+
"unit": "vp test",
|
|
49
|
+
"watch": "svelte-package --input src --output dist --watch"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@sveltejs/package": "^3.0.1",
|
|
53
|
+
"@sveltejs/vite-plugin-svelte": "^7.3.1",
|
|
54
|
+
"@vitest/browser-playwright": "5.0.5",
|
|
55
|
+
"playwright": "^1.64.1",
|
|
56
|
+
"svelte": "^5.57.2",
|
|
57
|
+
"svelte-check": "^4.7.7",
|
|
58
|
+
"typescript": "^6.0.3",
|
|
59
|
+
"vite": "npm:@voidzero-dev/vite-plus-core@1.4.0",
|
|
60
|
+
"vite-plus": "1.4.0",
|
|
61
|
+
"vitest": "5.0.5",
|
|
62
|
+
"vitest-browser-svelte": "^3.1.0"
|
|
63
|
+
},
|
|
64
|
+
"peerDependencies": {
|
|
65
|
+
"svelte": "^5.0.0"
|
|
66
|
+
}
|
|
67
|
+
}
|
package/src/Clock.css
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
.svelte-clock {
|
|
2
|
+
display: block;
|
|
3
|
+
position: relative;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.svelte-clock,
|
|
7
|
+
.svelte-clock *,
|
|
8
|
+
.svelte-clock *:before,
|
|
9
|
+
.svelte-clock *:after {
|
|
10
|
+
-moz-box-sizing: border-box;
|
|
11
|
+
-webkit-box-sizing: border-box;
|
|
12
|
+
box-sizing: border-box;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.svelte-clock__face {
|
|
16
|
+
position: absolute;
|
|
17
|
+
top: 0;
|
|
18
|
+
bottom: 0;
|
|
19
|
+
left: 0;
|
|
20
|
+
right: 0;
|
|
21
|
+
border: 1px solid black;
|
|
22
|
+
border-radius: 50%;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.svelte-clock__hand {
|
|
26
|
+
position: absolute;
|
|
27
|
+
top: 0;
|
|
28
|
+
bottom: 0;
|
|
29
|
+
left: 50%;
|
|
30
|
+
right: 50%;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.svelte-clock__hand__body {
|
|
34
|
+
position: absolute;
|
|
35
|
+
background-color: black;
|
|
36
|
+
transform: translateX(-50%);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.svelte-clock__second-hand__body {
|
|
40
|
+
background-color: red;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.svelte-clock__mark {
|
|
44
|
+
position: absolute;
|
|
45
|
+
top: 0;
|
|
46
|
+
bottom: 0;
|
|
47
|
+
left: 50%;
|
|
48
|
+
right: 50%;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.svelte-clock__mark__body {
|
|
52
|
+
position: absolute;
|
|
53
|
+
background-color: black;
|
|
54
|
+
transform: translateX(-50%);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.svelte-clock__mark__number {
|
|
58
|
+
position: absolute;
|
|
59
|
+
left: -40px;
|
|
60
|
+
width: 80px;
|
|
61
|
+
text-align: center;
|
|
62
|
+
}
|
package/src/Clock.svelte
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import './Clock.css';
|
|
3
|
+
|
|
4
|
+
import type { ClassValue } from 'svelte/elements';
|
|
5
|
+
|
|
6
|
+
type ClockProps = {
|
|
7
|
+
/**
|
|
8
|
+
* Class name(s) that will be added along with `"svelte-clock"` to the main `<time>` element.
|
|
9
|
+
*/
|
|
10
|
+
class?: ClassValue;
|
|
11
|
+
/**
|
|
12
|
+
* Function that formats hour numbers rendered when `renderNumbers` is set. Receives the locale and an hour from 1 to 12.
|
|
13
|
+
*/
|
|
14
|
+
formatHour?: (locale: string | undefined, hour: number) => string;
|
|
15
|
+
/**
|
|
16
|
+
* Hour hand length, in percents of the clock radius.
|
|
17
|
+
*
|
|
18
|
+
* @default 50
|
|
19
|
+
*/
|
|
20
|
+
hourHandLength?: number;
|
|
21
|
+
/**
|
|
22
|
+
* Hour hand opposite length, in percents of the clock radius.
|
|
23
|
+
*
|
|
24
|
+
* @default 10
|
|
25
|
+
*/
|
|
26
|
+
hourHandOppositeLength?: number;
|
|
27
|
+
/**
|
|
28
|
+
* Hour hand width, in pixels.
|
|
29
|
+
*
|
|
30
|
+
* @default 4
|
|
31
|
+
*/
|
|
32
|
+
hourHandWidth?: number;
|
|
33
|
+
/**
|
|
34
|
+
* Hour marks length, in percents of the clock radius.
|
|
35
|
+
*
|
|
36
|
+
* @default 10
|
|
37
|
+
*/
|
|
38
|
+
hourMarksLength?: number;
|
|
39
|
+
/**
|
|
40
|
+
* Hour marks width, in pixels.
|
|
41
|
+
*
|
|
42
|
+
* @default 3
|
|
43
|
+
*/
|
|
44
|
+
hourMarksWidth?: number;
|
|
45
|
+
/**
|
|
46
|
+
* Locale used to format hour numbers.
|
|
47
|
+
*
|
|
48
|
+
* @example 'hu-HU'
|
|
49
|
+
*/
|
|
50
|
+
locale?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Minute hand length, in percents of the clock radius.
|
|
53
|
+
*
|
|
54
|
+
* @default 70
|
|
55
|
+
*/
|
|
56
|
+
minuteHandLength?: number;
|
|
57
|
+
/**
|
|
58
|
+
* Minute hand opposite length, in percents of the clock radius.
|
|
59
|
+
*
|
|
60
|
+
* @default 10
|
|
61
|
+
*/
|
|
62
|
+
minuteHandOppositeLength?: number;
|
|
63
|
+
/**
|
|
64
|
+
* Minute hand width, in pixels.
|
|
65
|
+
*
|
|
66
|
+
* @default 2
|
|
67
|
+
*/
|
|
68
|
+
minuteHandWidth?: number;
|
|
69
|
+
/**
|
|
70
|
+
* Minute marks length, in percents of the clock radius.
|
|
71
|
+
*
|
|
72
|
+
* @default 6
|
|
73
|
+
*/
|
|
74
|
+
minuteMarksLength?: number;
|
|
75
|
+
/**
|
|
76
|
+
* Minute marks width, in pixels.
|
|
77
|
+
*
|
|
78
|
+
* @default 1
|
|
79
|
+
*/
|
|
80
|
+
minuteMarksWidth?: number;
|
|
81
|
+
/**
|
|
82
|
+
* Whether hour marks should be rendered.
|
|
83
|
+
*
|
|
84
|
+
* @default true
|
|
85
|
+
*/
|
|
86
|
+
renderHourMarks?: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Whether the minute hand should be rendered.
|
|
89
|
+
*
|
|
90
|
+
* @default true
|
|
91
|
+
*/
|
|
92
|
+
renderMinuteHand?: boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Whether minute marks should be rendered.
|
|
95
|
+
*
|
|
96
|
+
* @default true
|
|
97
|
+
*/
|
|
98
|
+
renderMinuteMarks?: boolean;
|
|
99
|
+
/**
|
|
100
|
+
* Whether hour numbers should be rendered.
|
|
101
|
+
*
|
|
102
|
+
* @default false
|
|
103
|
+
*/
|
|
104
|
+
renderNumbers?: boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Whether the second hand should be rendered.
|
|
107
|
+
*
|
|
108
|
+
* @default true
|
|
109
|
+
*/
|
|
110
|
+
renderSecondHand?: boolean;
|
|
111
|
+
/**
|
|
112
|
+
* Second hand length, in percents of the clock radius.
|
|
113
|
+
*
|
|
114
|
+
* @default 90
|
|
115
|
+
*/
|
|
116
|
+
secondHandLength?: number;
|
|
117
|
+
/**
|
|
118
|
+
* Second hand opposite length, in percents of the clock radius.
|
|
119
|
+
*
|
|
120
|
+
* @default 10
|
|
121
|
+
*/
|
|
122
|
+
secondHandOppositeLength?: number;
|
|
123
|
+
/**
|
|
124
|
+
* Second hand width, in pixels.
|
|
125
|
+
*
|
|
126
|
+
* @default 1
|
|
127
|
+
*/
|
|
128
|
+
secondHandWidth?: number;
|
|
129
|
+
/**
|
|
130
|
+
* Clock size, in pixels.
|
|
131
|
+
*
|
|
132
|
+
* @default 150
|
|
133
|
+
*/
|
|
134
|
+
size?: number;
|
|
135
|
+
/**
|
|
136
|
+
* Whether the second hand should move smoothly. Only applies when `value` is `undefined`.
|
|
137
|
+
*
|
|
138
|
+
* @default false
|
|
139
|
+
*/
|
|
140
|
+
useMillisecondPrecision?: boolean;
|
|
141
|
+
/**
|
|
142
|
+
* Time to display. When `undefined`, the clock shows the current time and keeps ticking. When `null`, no hands are rendered.
|
|
143
|
+
*
|
|
144
|
+
* @example new Date()
|
|
145
|
+
* @example '22:15:00'
|
|
146
|
+
*/
|
|
147
|
+
value?: Date | string | null;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
let {
|
|
151
|
+
class: className,
|
|
152
|
+
formatHour = (locale, hour) => new Intl.NumberFormat(locale).format(hour),
|
|
153
|
+
hourHandLength = 50,
|
|
154
|
+
hourHandOppositeLength = 10,
|
|
155
|
+
hourHandWidth = 4,
|
|
156
|
+
hourMarksLength = 10,
|
|
157
|
+
hourMarksWidth = 3,
|
|
158
|
+
locale,
|
|
159
|
+
minuteHandLength = 70,
|
|
160
|
+
minuteHandOppositeLength = 10,
|
|
161
|
+
minuteHandWidth = 2,
|
|
162
|
+
minuteMarksLength = 6,
|
|
163
|
+
minuteMarksWidth = 1,
|
|
164
|
+
renderHourMarks = true,
|
|
165
|
+
renderMinuteHand = true,
|
|
166
|
+
renderMinuteMarks = true,
|
|
167
|
+
renderNumbers = false,
|
|
168
|
+
renderSecondHand = true,
|
|
169
|
+
secondHandLength = 90,
|
|
170
|
+
secondHandOppositeLength = 10,
|
|
171
|
+
secondHandWidth = 1,
|
|
172
|
+
size = 150,
|
|
173
|
+
useMillisecondPrecision = false,
|
|
174
|
+
value,
|
|
175
|
+
}: ClockProps = $props();
|
|
176
|
+
|
|
177
|
+
const hourMarks = Array.from({ length: 12 }, (_, index) => index);
|
|
178
|
+
const minuteMarks = Array.from({ length: 60 }, (_, index) => index).filter(
|
|
179
|
+
(index) => index % 5 !== 0,
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
let now = $state(new Date());
|
|
183
|
+
|
|
184
|
+
$effect(() => {
|
|
185
|
+
if (value !== undefined) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
now = new Date();
|
|
190
|
+
|
|
191
|
+
if (useMillisecondPrecision) {
|
|
192
|
+
let frame = requestAnimationFrame(function tick() {
|
|
193
|
+
now = new Date();
|
|
194
|
+
frame = requestAnimationFrame(tick);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
return () => cancelAnimationFrame(frame);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const interval = setInterval(() => {
|
|
201
|
+
now = new Date();
|
|
202
|
+
}, 1000);
|
|
203
|
+
|
|
204
|
+
return () => clearInterval(interval);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
function parseTime(input: Date | string): Date | null {
|
|
208
|
+
if (input instanceof Date) {
|
|
209
|
+
return Number.isNaN(input.getTime()) ? null : input;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Time-only strings, e.g. "22:15" or "22:15:30"
|
|
213
|
+
const match = /^(\d{1,2}):(\d{2})(?::(\d{2})(?:\.(\d+))?)?$/.exec(input);
|
|
214
|
+
|
|
215
|
+
if (match) {
|
|
216
|
+
const date = new Date();
|
|
217
|
+
date.setHours(
|
|
218
|
+
Number(match[1]),
|
|
219
|
+
Number(match[2]),
|
|
220
|
+
Number(match[3] ?? 0),
|
|
221
|
+
Number((match[4] ?? '0').padEnd(3, '0').slice(0, 3)),
|
|
222
|
+
);
|
|
223
|
+
return date;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const date = new Date(input);
|
|
227
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const date = $derived(value === undefined ? now : value === null ? null : parseTime(value));
|
|
231
|
+
|
|
232
|
+
const angles = $derived.by(() => {
|
|
233
|
+
if (!date) {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const hours = date.getHours() % 12;
|
|
238
|
+
const minutes = date.getMinutes();
|
|
239
|
+
const seconds = date.getSeconds();
|
|
240
|
+
const milliseconds = useMillisecondPrecision ? date.getMilliseconds() : 0;
|
|
241
|
+
|
|
242
|
+
return {
|
|
243
|
+
hour: hours * 30 + minutes * 0.5 + seconds / 120,
|
|
244
|
+
minute: minutes * 6 + seconds * 0.1,
|
|
245
|
+
second: seconds * 6 + milliseconds * 0.006,
|
|
246
|
+
};
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
const datetime = $derived(date ? date.toISOString() : undefined);
|
|
250
|
+
</script>
|
|
251
|
+
|
|
252
|
+
{#snippet hand(
|
|
253
|
+
name: string,
|
|
254
|
+
angle: number,
|
|
255
|
+
length: number,
|
|
256
|
+
oppositeLength: number,
|
|
257
|
+
width: number,
|
|
258
|
+
)}
|
|
259
|
+
<div class="svelte-clock__hand svelte-clock__{name}-hand" style:transform="rotate({angle}deg)">
|
|
260
|
+
<div
|
|
261
|
+
class="svelte-clock__hand__body svelte-clock__{name}-hand__body"
|
|
262
|
+
style:width="{width}px"
|
|
263
|
+
style:top="{50 - length / 2}%"
|
|
264
|
+
style:bottom="{50 - oppositeLength / 2}%"
|
|
265
|
+
></div>
|
|
266
|
+
</div>
|
|
267
|
+
{/snippet}
|
|
268
|
+
|
|
269
|
+
<time class={['svelte-clock', className]} {datetime} style:width="{size}px" style:height="{size}px">
|
|
270
|
+
<div class="svelte-clock__face">
|
|
271
|
+
{#if renderMinuteMarks}
|
|
272
|
+
{#each minuteMarks as index (index)}
|
|
273
|
+
<div class="svelte-clock__mark svelte-clock__minute-mark" style:transform="rotate({index * 6}deg)">
|
|
274
|
+
<div
|
|
275
|
+
class="svelte-clock__mark__body svelte-clock__minute-mark__body"
|
|
276
|
+
style:width="{minuteMarksWidth}px"
|
|
277
|
+
style:bottom="{100 - minuteMarksLength / 2}%"
|
|
278
|
+
></div>
|
|
279
|
+
</div>
|
|
280
|
+
{/each}
|
|
281
|
+
{/if}
|
|
282
|
+
{#if renderHourMarks}
|
|
283
|
+
{#each hourMarks as index (index)}
|
|
284
|
+
<div class="svelte-clock__mark svelte-clock__hour-mark" style:transform="rotate({index * 30}deg)">
|
|
285
|
+
<div
|
|
286
|
+
class="svelte-clock__mark__body svelte-clock__hour-mark__body"
|
|
287
|
+
style:width="{hourMarksWidth}px"
|
|
288
|
+
style:bottom="{100 - hourMarksLength / 2}%"
|
|
289
|
+
></div>
|
|
290
|
+
</div>
|
|
291
|
+
{/each}
|
|
292
|
+
{/if}
|
|
293
|
+
{#if renderNumbers}
|
|
294
|
+
{#each hourMarks as index (index)}
|
|
295
|
+
{@const hourNumber = index === 0 ? 12 : index}
|
|
296
|
+
<div class="svelte-clock__mark" style:transform="rotate({index * 30}deg)">
|
|
297
|
+
<div
|
|
298
|
+
class="svelte-clock__mark__number"
|
|
299
|
+
style:top="{hourMarksLength}%"
|
|
300
|
+
style:transform="rotate({-index * 30}deg)"
|
|
301
|
+
>
|
|
302
|
+
{formatHour(locale, hourNumber)}
|
|
303
|
+
</div>
|
|
304
|
+
</div>
|
|
305
|
+
{/each}
|
|
306
|
+
{/if}
|
|
307
|
+
</div>
|
|
308
|
+
{#if angles}
|
|
309
|
+
{@render hand('hour', angles.hour, hourHandLength, hourHandOppositeLength, hourHandWidth)}
|
|
310
|
+
{#if renderMinuteHand}
|
|
311
|
+
{@render hand(
|
|
312
|
+
'minute',
|
|
313
|
+
angles.minute,
|
|
314
|
+
minuteHandLength,
|
|
315
|
+
minuteHandOppositeLength,
|
|
316
|
+
minuteHandWidth,
|
|
317
|
+
)}
|
|
318
|
+
{/if}
|
|
319
|
+
{#if renderSecondHand}
|
|
320
|
+
{@render hand(
|
|
321
|
+
'second',
|
|
322
|
+
angles.second,
|
|
323
|
+
secondHandLength,
|
|
324
|
+
secondHandOppositeLength,
|
|
325
|
+
secondHandWidth,
|
|
326
|
+
)}
|
|
327
|
+
{/if}
|
|
328
|
+
{/if}
|
|
329
|
+
</time>
|