svelte-analog-clock 0.3.0 → 0.5.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/README.md CHANGED
@@ -5,7 +5,7 @@ An analog clock for your Svelte 5 app. Svelte port of [React-Clock](https://gith
5
5
  ## Installation
6
6
 
7
7
  ```sh
8
- npm install svelte-analog-clock
8
+ npm install svelte-analog-clock svelte-date-utils
9
9
  ```
10
10
 
11
11
  Requires Svelte 5 or later.
@@ -15,11 +15,17 @@ Requires Svelte 5 or later.
15
15
  ```svelte
16
16
  <script lang="ts">
17
17
  import Clock from 'svelte-analog-clock';
18
+ import { AdapterDate } from 'svelte-date-utils';
19
+ import { provideDateAdapter } from 'svelte-date-utils';
20
+
21
+ provideDateAdapter(new AdapterDate());
18
22
  </script>
19
23
 
20
24
  <Clock value={new Date()} />
21
25
  ```
22
26
 
27
+ Install `svelte-date-utils` alongside the clock. Its adapter is required and comes from Svelte context. Call `provideDateAdapter(adapter)` in this component or an ancestor before rendering `Clock`; without it, the clock throws an error. The adapter determines the date object type accepted by `value`. `AdapterDate` uses native `Date`; date-fns, Luxon and `@internationalized/date` adapters are exported from `svelte-date-utils/date-fns`, `svelte-date-utils/luxon` and `svelte-date-utils/internationalized`.
28
+
23
29
  Styles are imported automatically. Elements use BEM class names starting with `svelte-clock`.
24
30
 
25
31
  ## Props
package/dist/Clock.svelte CHANGED
@@ -1,9 +1,11 @@
1
- <script lang="ts">
1
+ <script lang="ts" generics="T">
2
2
  import './Clock.css';
3
+ import { untrack } from 'svelte';
4
+ import { getDateAdapter } from 'svelte-date-utils';
3
5
 
4
6
  import type { ClassValue } from 'svelte/elements';
5
7
 
6
- type ClockProps = {
8
+ type ClockProps<T> = {
7
9
  /**
8
10
  * Class name(s) that will be added along with `"svelte-clock"` to the main `<time>` element.
9
11
  */
@@ -144,7 +146,7 @@
144
146
  * @example new Date()
145
147
  * @example '22:15:00'
146
148
  */
147
- value?: Date | string | null;
149
+ value?: T | string | null;
148
150
  };
149
151
 
150
152
  let {
@@ -172,25 +174,27 @@
172
174
  size = 150,
173
175
  useMillisecondPrecision = false,
174
176
  value,
175
- }: ClockProps = $props();
177
+ }: ClockProps<T> = $props();
178
+
179
+ const adapter = getDateAdapter<T>('Clock');
176
180
 
177
181
  const hourMarks = Array.from({ length: 12 }, (_, index) => index);
178
182
  const minuteMarks = Array.from({ length: 60 }, (_, index) => index).filter(
179
183
  (index) => index % 5 !== 0,
180
184
  );
181
185
 
182
- let now = $state(new Date());
186
+ let now = $state(untrack(() => adapter.date()));
183
187
 
184
188
  $effect(() => {
185
189
  if (value !== undefined) {
186
190
  return;
187
191
  }
188
192
 
189
- now = new Date();
193
+ now = adapter.date();
190
194
 
191
195
  if (useMillisecondPrecision) {
192
196
  let frame = requestAnimationFrame(function tick() {
193
- now = new Date();
197
+ now = adapter.date();
194
198
  frame = requestAnimationFrame(tick);
195
199
  });
196
200
 
@@ -198,33 +202,28 @@
198
202
  }
199
203
 
200
204
  const interval = setInterval(() => {
201
- now = new Date();
205
+ now = adapter.date();
202
206
  }, 1000);
203
207
 
204
208
  return () => clearInterval(interval);
205
209
  });
206
210
 
207
- function parseTime(input: Date | string): Date | null {
208
- if (input instanceof Date) {
209
- return Number.isNaN(input.getTime()) ? null : input;
210
- }
211
-
211
+ function parseTime(input: T | string): T | null {
212
+ if (typeof input !== 'string') return adapter.isValid(input) ? input : null;
212
213
  // Time-only strings, e.g. "22:15" or "22:15:30"
213
214
  const match = /^(\d{1,2}):(\d{2})(?::(\d{2})(?:\.(\d+))?)?$/.exec(input);
214
215
 
215
216
  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
- );
217
+ let date = adapter.date();
218
+ date = adapter.setHours(date, Number(match[1]));
219
+ date = adapter.setMinutes(date, Number(match[2]));
220
+ date = adapter.setSeconds(date, Number(match[3] ?? 0));
221
+ date = adapter.setMilliseconds(date, Number((match[4] ?? '0').padEnd(3, '0').slice(0, 3)));
223
222
  return date;
224
223
  }
225
224
 
226
- const date = new Date(input);
227
- return Number.isNaN(date.getTime()) ? null : date;
225
+ const date = adapter.date(input);
226
+ return adapter.isValid(date) ? date : null;
228
227
  }
229
228
 
230
229
  const date = $derived(value === undefined ? now : value === null ? null : parseTime(value));
@@ -234,10 +233,10 @@
234
233
  return null;
235
234
  }
236
235
 
237
- const hours = date.getHours() % 12;
238
- const minutes = date.getMinutes();
239
- const seconds = date.getSeconds();
240
- const milliseconds = useMillisecondPrecision ? date.getMilliseconds() : 0;
236
+ const hours = adapter.getHours(date) % 12;
237
+ const minutes = adapter.getMinutes(date);
238
+ const seconds = adapter.getSeconds(date);
239
+ const milliseconds = useMillisecondPrecision ? adapter.getMilliseconds(date) : 0;
241
240
 
242
241
  return {
243
242
  hour: hours * 30 + minutes * 0.5 + seconds / 120,
@@ -246,7 +245,7 @@
246
245
  };
247
246
  });
248
247
 
249
- const datetime = $derived(date ? date.toISOString() : undefined);
248
+ const datetime = $derived(date ? adapter.toJsDate(date).toISOString() : undefined);
250
249
  </script>
251
250
 
252
251
  {#snippet hand(
@@ -1,6 +1,6 @@
1
1
  import './Clock.css';
2
2
  import type { ClassValue } from 'svelte/elements';
3
- type ClockProps = {
3
+ type ClockProps<T> = {
4
4
  /**
5
5
  * Class name(s) that will be added along with `"svelte-clock"` to the main `<time>` element.
6
6
  */
@@ -141,8 +141,29 @@ type ClockProps = {
141
141
  * @example new Date()
142
142
  * @example '22:15:00'
143
143
  */
144
- value?: Date | string | null;
144
+ value?: T | string | null;
145
145
  };
146
- declare const Clock: import("svelte").Component<ClockProps, {}, "">;
147
- type Clock = ReturnType<typeof Clock>;
146
+ declare function $$render<T>(): {
147
+ props: ClockProps<T>;
148
+ exports: {};
149
+ bindings: "";
150
+ slots: {};
151
+ events: {};
152
+ };
153
+ declare class __sveltets_Render<T> {
154
+ props(): ReturnType<typeof $$render<T>>['props'];
155
+ events(): ReturnType<typeof $$render<T>>['events'];
156
+ slots(): ReturnType<typeof $$render<T>>['slots'];
157
+ bindings(): "";
158
+ exports(): {};
159
+ }
160
+ interface $$IsomorphicComponent {
161
+ new <T>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
162
+ $$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
163
+ } & ReturnType<__sveltets_Render<T>['exports']>;
164
+ <T>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
165
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
166
+ }
167
+ declare const Clock: $$IsomorphicComponent;
168
+ type Clock<T> = InstanceType<typeof Clock<T>>;
148
169
  export default Clock;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelte-analog-clock",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "An analog clock for your Svelte app.",
5
5
  "keywords": [
6
6
  "analog-clock",
package/src/Clock.svelte CHANGED
@@ -1,9 +1,11 @@
1
- <script lang="ts">
1
+ <script lang="ts" generics="T">
2
2
  import './Clock.css';
3
+ import { untrack } from 'svelte';
4
+ import { getDateAdapter } from 'svelte-date-utils';
3
5
 
4
6
  import type { ClassValue } from 'svelte/elements';
5
7
 
6
- type ClockProps = {
8
+ type ClockProps<T> = {
7
9
  /**
8
10
  * Class name(s) that will be added along with `"svelte-clock"` to the main `<time>` element.
9
11
  */
@@ -144,7 +146,7 @@
144
146
  * @example new Date()
145
147
  * @example '22:15:00'
146
148
  */
147
- value?: Date | string | null;
149
+ value?: T | string | null;
148
150
  };
149
151
 
150
152
  let {
@@ -172,25 +174,27 @@
172
174
  size = 150,
173
175
  useMillisecondPrecision = false,
174
176
  value,
175
- }: ClockProps = $props();
177
+ }: ClockProps<T> = $props();
178
+
179
+ const adapter = getDateAdapter<T>('Clock');
176
180
 
177
181
  const hourMarks = Array.from({ length: 12 }, (_, index) => index);
178
182
  const minuteMarks = Array.from({ length: 60 }, (_, index) => index).filter(
179
183
  (index) => index % 5 !== 0,
180
184
  );
181
185
 
182
- let now = $state(new Date());
186
+ let now = $state(untrack(() => adapter.date()));
183
187
 
184
188
  $effect(() => {
185
189
  if (value !== undefined) {
186
190
  return;
187
191
  }
188
192
 
189
- now = new Date();
193
+ now = adapter.date();
190
194
 
191
195
  if (useMillisecondPrecision) {
192
196
  let frame = requestAnimationFrame(function tick() {
193
- now = new Date();
197
+ now = adapter.date();
194
198
  frame = requestAnimationFrame(tick);
195
199
  });
196
200
 
@@ -198,33 +202,28 @@
198
202
  }
199
203
 
200
204
  const interval = setInterval(() => {
201
- now = new Date();
205
+ now = adapter.date();
202
206
  }, 1000);
203
207
 
204
208
  return () => clearInterval(interval);
205
209
  });
206
210
 
207
- function parseTime(input: Date | string): Date | null {
208
- if (input instanceof Date) {
209
- return Number.isNaN(input.getTime()) ? null : input;
210
- }
211
-
211
+ function parseTime(input: T | string): T | null {
212
+ if (typeof input !== 'string') return adapter.isValid(input) ? input : null;
212
213
  // Time-only strings, e.g. "22:15" or "22:15:30"
213
214
  const match = /^(\d{1,2}):(\d{2})(?::(\d{2})(?:\.(\d+))?)?$/.exec(input);
214
215
 
215
216
  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
- );
217
+ let date = adapter.date();
218
+ date = adapter.setHours(date, Number(match[1]));
219
+ date = adapter.setMinutes(date, Number(match[2]));
220
+ date = adapter.setSeconds(date, Number(match[3] ?? 0));
221
+ date = adapter.setMilliseconds(date, Number((match[4] ?? '0').padEnd(3, '0').slice(0, 3)));
223
222
  return date;
224
223
  }
225
224
 
226
- const date = new Date(input);
227
- return Number.isNaN(date.getTime()) ? null : date;
225
+ const date = adapter.date(input);
226
+ return adapter.isValid(date) ? date : null;
228
227
  }
229
228
 
230
229
  const date = $derived(value === undefined ? now : value === null ? null : parseTime(value));
@@ -234,10 +233,10 @@
234
233
  return null;
235
234
  }
236
235
 
237
- const hours = date.getHours() % 12;
238
- const minutes = date.getMinutes();
239
- const seconds = date.getSeconds();
240
- const milliseconds = useMillisecondPrecision ? date.getMilliseconds() : 0;
236
+ const hours = adapter.getHours(date) % 12;
237
+ const minutes = adapter.getMinutes(date);
238
+ const seconds = adapter.getSeconds(date);
239
+ const milliseconds = useMillisecondPrecision ? adapter.getMilliseconds(date) : 0;
241
240
 
242
241
  return {
243
242
  hour: hours * 30 + minutes * 0.5 + seconds / 120,
@@ -246,7 +245,7 @@
246
245
  };
247
246
  });
248
247
 
249
- const datetime = $derived(date ? date.toISOString() : undefined);
248
+ const datetime = $derived(date ? adapter.toJsDate(date).toISOString() : undefined);
250
249
  </script>
251
250
 
252
251
  {#snippet hand(