@nomideusz/svelte-calendar 0.3.1 → 0.4.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.
@@ -1,21 +1,37 @@
1
1
  import type { PresetName } from '../../theme/presets.js';
2
- type RangeField = {
2
+ type BaseField = {
3
3
  key: string;
4
4
  label: string;
5
+ group?: string;
6
+ };
7
+ type RangeField = BaseField & {
5
8
  type: 'range';
6
9
  min: number;
7
10
  max: number;
8
11
  step: number;
12
+ enabledWhen?: string;
9
13
  };
10
- type ToggleField = {
11
- key: string;
12
- label: string;
14
+ type ToggleField = BaseField & {
13
15
  type: 'toggle';
14
16
  };
15
- export type SettingsField = RangeField | ToggleField;
17
+ type SelectField = BaseField & {
18
+ type: 'select';
19
+ options: {
20
+ value: string;
21
+ label: string;
22
+ }[];
23
+ };
24
+ type SegmentField = BaseField & {
25
+ type: 'segment';
26
+ options: {
27
+ value: string;
28
+ label: string;
29
+ }[];
30
+ };
31
+ export type SettingsField = RangeField | ToggleField | SelectField | SegmentField;
16
32
  interface Props {
17
33
  fields: SettingsField[];
18
- values: Record<string, number | boolean>;
34
+ values: Record<string, string | number | boolean>;
19
35
  theme: PresetName;
20
36
  }
21
37
  declare const Settings: import("svelte").Component<Props, {}, "values" | "theme">;
@@ -20,6 +20,7 @@
20
20
  interface Props {
21
21
  weekOffset?: number;
22
22
  mondayStart?: boolean;
23
+ locale?: string;
23
24
  hourHeight?: number;
24
25
  dayWidth?: number;
25
26
  nowPosition?: number;
@@ -37,6 +38,7 @@
37
38
 
38
39
  let {
39
40
  mondayStart = true,
41
+ locale,
40
42
  height = 520,
41
43
  events = [],
42
44
  style = '',
@@ -116,7 +118,7 @@
116
118
  // Month label: show when first day of week is day 1-7
117
119
  const startDate = new Date(weekStart);
118
120
  const showMonth = startDate.getDate() <= 7;
119
- const monthLabel = showMonth ? monthLong(weekStart).toUpperCase() : null;
121
+ const monthLabel = showMonth ? monthLong(weekStart, locale).toUpperCase() : null;
120
122
 
121
123
  result.push({ weekStart, isCurrent, monthLabel, days });
122
124
  }
@@ -126,11 +128,10 @@
126
128
 
127
129
  // ─── Format helpers ─────────────────────────────────
128
130
  function fmtAmPm(d: Date): string {
129
- let h = d.getHours();
130
- const m = d.getMinutes();
131
- const ampm = h >= 12 ? 'PM' : 'AM';
132
- h = h % 12 || 12;
133
- return m > 0 ? `${h}:${m.toString().padStart(2, '0')}${ampm}` : `${h}${ampm}`;
131
+ return d
132
+ .toLocaleTimeString(locale ?? 'en-US', { hour: 'numeric', minute: '2-digit', hour12: true })
133
+ .toUpperCase()
134
+ .replace(' ', '');
134
135
  }
135
136
 
136
137
  function fmtNowTime(tick: number): string {
@@ -274,13 +275,13 @@
274
275
  class:wg-cell--weekend={day.isWeekend}
275
276
  role="gridcell"
276
277
  tabindex="0"
277
- aria-label="{new Date(day.ms).toLocaleDateString('en-US', { weekday: 'long', month: 'short', day: 'numeric' })}{day.isToday ? ' (today)' : ''}, {day.events.length} events"
278
+ aria-label="{new Date(day.ms).toLocaleDateString(locale ?? 'en-US', { weekday: 'long', month: 'short', day: 'numeric' })}{day.isToday ? ' (today)' : ''}, {day.events.length} events"
278
279
  onclick={(e) => handleDayCellClick(day.ms, e)}
279
280
  onkeydown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleDayCellClick(day.ms, e); } }}
280
281
  >
281
282
  <!-- Day label inside the cell -->
282
283
  <div class="wg-cell-hd" class:wg-cell-hd--today={day.isToday}>
283
- <span class="wg-day-wd">{weekdayShort(day.ms)}</span>
284
+ <span class="wg-day-wd">{weekdayShort(day.ms, locale)}</span>
284
285
  <span class="wg-day-num" class:wg-day-num--today={day.isToday}>
285
286
  {day.dayNum}
286
287
  </span>
@@ -2,6 +2,7 @@ import type { TimelineEvent } from '../../core/types.js';
2
2
  interface Props {
3
3
  weekOffset?: number;
4
4
  mondayStart?: boolean;
5
+ locale?: string;
5
6
  hourHeight?: number;
6
7
  dayWidth?: number;
7
8
  nowPosition?: number;
@@ -21,6 +21,7 @@
21
21
  let {
22
22
  weekOffset = 0,
23
23
  mondayStart = true,
24
+ locale,
24
25
  height = 400,
25
26
  events = [],
26
27
  style = '',
@@ -29,7 +30,7 @@
29
30
  selectedEventId = null,
30
31
  visibleHours,
31
32
  ...rest
32
- }: WeekTimelineProps & { visibleHours?: [number, number]; [key: string]: unknown } = $props();
33
+ }: WeekTimelineProps & { locale?: string; visibleHours?: [number, number]; [key: string]: unknown } = $props();
33
34
 
34
35
  const startHour = $derived(visibleHours?.[0] ?? 0);
35
36
  const endHour = $derived(visibleHours?.[1] ?? 24);
@@ -43,7 +44,7 @@
43
44
  : startOfWeek(clock.today, mondayStart) + weekOffset * 7 * DAY_MS,
44
45
  );
45
46
  const currentWeekVisible = $derived(clock.today >= weekStartMs && clock.today < weekStartMs + 7 * DAY_MS);
46
- const weekLabel = $derived(fmtWeekRange(weekStartMs));
47
+ const weekLabel = $derived(fmtWeekRange(weekStartMs, locale));
47
48
 
48
49
  let selectedCell = $state<{ day: number; hour: number } | null>(null);
49
50
 
@@ -96,7 +97,7 @@
96
97
 
97
98
  out.push({
98
99
  ms,
99
- label: weekdayShort(ms),
100
+ label: weekdayShort(ms, locale),
100
101
  num: dayNum(ms),
101
102
  today: isToday,
102
103
  past: ms < clock.today,
@@ -118,11 +119,11 @@
118
119
  if (!selectedCell) return '';
119
120
  const day = heatDays[selectedCell.day];
120
121
  if (!day) return '';
121
- return `${day.label} ${day.num}, ${fmtH(selectedCell.hour)}`;
122
+ return `${day.label} ${day.num}, ${fmtH(selectedCell.hour, locale)}`;
122
123
  });
123
124
 
124
125
  function fmtTime(d: Date): string {
125
- return d.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true }).toLowerCase();
126
+ return d.toLocaleTimeString(locale ?? 'en-US', { hour: 'numeric', minute: '2-digit', hour12: true }).toLowerCase();
126
127
  }
127
128
 
128
129
  function duration(ev: TimelineEvent): string {
@@ -188,7 +189,7 @@
188
189
  {#each Array(visibleHourCount) as _, idx}
189
190
  {@const h = startHour + idx}
190
191
  {#if (h - startHour) % 3 === 0}
191
- <span class="hm-hour-mark" style:left="calc({idx} * (100% / {visibleHourCount}))">{fmtH(h)}</span>
192
+ <span class="hm-hour-mark" style:left="calc({idx} * (100% / {visibleHourCount}))">{fmtH(h, locale)}</span>
192
193
  {/if}
193
194
  {/each}
194
195
  </div>
@@ -209,7 +210,7 @@
209
210
  class:hm-cell--selected={selectedCell?.day === di && selectedCell?.hour === hi}
210
211
  style:background={heatColor(cell.intensity)}
211
212
  onclick={() => selectCell(di, hi)}
212
- title="{fmtH(cell.hour)}: {cell.events.length > 0 ? cell.events.map(e => e.title).join(', ') : 'free'}"
213
+ title="{fmtH(cell.hour, locale)}: {cell.events.length > 0 ? cell.events.map(e => e.title).join(', ') : 'free'}"
213
214
  >
214
215
  {#if cell.isNow}
215
216
  <span class="hm-cell-now"></span>
@@ -1,5 +1,6 @@
1
1
  import type { WeekTimelineProps } from '../../core/types.js';
2
2
  type $$ComponentProps = WeekTimelineProps & {
3
+ locale?: string;
3
4
  visibleHours?: [number, number];
4
5
  [key: string]: unknown;
5
6
  };
@@ -13,8 +13,6 @@
13
13
  locale="en-US"
14
14
  ></day-calendar>
15
15
  -->
16
- <svelte:options customElement="day-calendar" />
17
-
18
16
  <script lang="ts">
19
17
  import Calendar from '../calendar/Calendar.svelte';
20
18
  import type { CalendarView } from '../calendar/Calendar.svelte';
@@ -1,7 +1 @@
1
- /**
2
- * Widget entry point — registers <day-calendar> as a custom element.
3
- *
4
- * This file is the entry point for the standalone widget bundle (widget.js).
5
- * Import it via a <script> tag on any HTML page.
6
- */
7
- import './CalendarWidget.svelte';
1
+ export {};
@@ -4,6 +4,47 @@
4
4
  * This file is the entry point for the standalone widget bundle (widget.js).
5
5
  * Import it via a <script> tag on any HTML page.
6
6
  */
7
- import './CalendarWidget.svelte';
8
- // The Svelte custom element is auto-registered via <svelte:options customElement="day-calendar" />
9
- // Nothing else needed — the component self-registers when this module is loaded.
7
+ import { asClassComponent } from 'svelte/legacy';
8
+ import CalendarWidget from './CalendarWidget.svelte';
9
+ const CalendarWidgetClass = asClassComponent(CalendarWidget);
10
+ class DayCalendarElement extends HTMLElement {
11
+ instance = null;
12
+ static get observedAttributes() {
13
+ return ['api', 'events', 'theme', 'view', 'height', 'locale', 'dir', 'mondaystart', 'headers'];
14
+ }
15
+ connectedCallback() {
16
+ if (this.instance)
17
+ return;
18
+ this.instance = new CalendarWidgetClass({
19
+ target: this,
20
+ props: this.readProps(),
21
+ });
22
+ }
23
+ disconnectedCallback() {
24
+ this.instance?.$destroy();
25
+ this.instance = null;
26
+ }
27
+ attributeChangedCallback(name, _oldValue, newValue) {
28
+ if (!this.instance)
29
+ return;
30
+ this.instance.$set({
31
+ [name]: newValue ?? undefined,
32
+ });
33
+ }
34
+ readProps() {
35
+ return {
36
+ api: this.getAttribute('api') ?? undefined,
37
+ events: this.getAttribute('events') ?? undefined,
38
+ theme: this.getAttribute('theme') ?? undefined,
39
+ view: this.getAttribute('view') ?? undefined,
40
+ height: this.getAttribute('height') ?? undefined,
41
+ locale: this.getAttribute('locale') ?? undefined,
42
+ dir: this.getAttribute('dir') ?? undefined,
43
+ mondaystart: this.getAttribute('mondaystart') ?? undefined,
44
+ headers: this.getAttribute('headers') ?? undefined,
45
+ };
46
+ }
47
+ }
48
+ if (!customElements.get('day-calendar')) {
49
+ customElements.define('day-calendar', DayCalendarElement);
50
+ }
package/package.json CHANGED
@@ -1,68 +1,66 @@
1
1
  {
2
- "name": "@nomideusz/svelte-calendar",
3
- "version": "0.3.1",
4
- "description": "A themeable, pluggable Svelte 5 calendar with Day and Week views — Grid, Timeline, Agenda, Heatmap.",
5
- "type": "module",
6
- "license": "MIT",
7
- "svelte": "./dist/index.js",
8
- "types": "./dist/index.d.ts",
9
- "exports": {
10
- ".": {
11
- "types": "./dist/index.d.ts",
12
- "svelte": "./dist/index.js",
13
- "default": "./dist/index.js"
14
- },
15
- "./widget": "./widget/widget.js"
16
- },
17
- "files": [
18
- "dist",
19
- "widget",
20
- "!dist/**/*.test.*",
21
- "!dist/**/*.spec.*"
22
- ],
23
- "scripts": {
24
- "dev": "vite dev",
25
- "build": "vite build",
26
- "package": "svelte-kit sync && svelte-package",
27
- "build:widget": "vite build --config vite.config.widget.ts",
28
- "prepublishOnly": "npm run package",
29
- "preview": "vite preview",
30
- "prepare": "svelte-kit sync || echo ''",
31
- "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
32
- "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
33
- "test": "vitest run",
34
- "test:watch": "vitest"
35
- },
36
- "peerDependencies": {
37
- "svelte": "^5.0.0"
38
- },
39
- "devDependencies": {
40
- "@sveltejs/adapter-auto": "^7.0.0",
41
- "@sveltejs/kit": "^2.50.2",
42
- "@sveltejs/package": "^2.5.7",
43
- "@sveltejs/vite-plugin-svelte": "^6.2.4",
44
- "svelte": "^5.51.0",
45
- "svelte-check": "^4.3.6",
46
- "typescript": "^5.9.3",
47
- "vite": "^7.3.1",
48
- "vitest": "^4.0.18"
49
- },
50
- "dependencies": {
51
- "date-fns": "^4.1.0",
52
- "date-fns-tz": "^3.2.0"
53
- },
54
- "keywords": [
55
- "svelte",
56
- "calendar",
57
- "scheduler",
58
- "timeline",
59
- "week",
60
- "day",
61
- "booking",
62
- "svelte5"
63
- ],
64
- "repository": {
65
- "type": "git",
66
- "url": "https://github.com/nomideusz/svelte-calendar"
67
- }
68
- }
2
+ "name": "@nomideusz/svelte-calendar",
3
+ "version": "0.4.0",
4
+ "description": "A themeable, pluggable Svelte 5 calendar with Day and Week views — Grid, Timeline, Agenda, Heatmap.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "svelte": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "svelte": "./dist/index.js",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "./widget": "./widget/widget.js"
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "widget",
20
+ "!dist/**/*.test.*",
21
+ "!dist/**/*.spec.*"
22
+ ],
23
+ "peerDependencies": {
24
+ "svelte": "^5.0.0"
25
+ },
26
+ "devDependencies": {
27
+ "@sveltejs/adapter-auto": "^7.0.0",
28
+ "@sveltejs/kit": "^2.50.2",
29
+ "@sveltejs/package": "^2.5.7",
30
+ "@sveltejs/vite-plugin-svelte": "^6.2.4",
31
+ "svelte": "^5.51.0",
32
+ "svelte-check": "^4.3.6",
33
+ "typescript": "^5.9.3",
34
+ "vite": "^7.3.1",
35
+ "vitest": "^4.0.18"
36
+ },
37
+ "dependencies": {
38
+ "date-fns": "^4.1.0",
39
+ "date-fns-tz": "^3.2.0"
40
+ },
41
+ "keywords": [
42
+ "svelte",
43
+ "calendar",
44
+ "scheduler",
45
+ "timeline",
46
+ "week",
47
+ "day",
48
+ "booking",
49
+ "svelte5"
50
+ ],
51
+ "repository": {
52
+ "type": "git",
53
+ "url": "https://github.com/nomideusz/svelte-calendar"
54
+ },
55
+ "scripts": {
56
+ "dev": "vite dev",
57
+ "build": "vite build",
58
+ "package": "svelte-kit sync && svelte-package",
59
+ "build:widget": "vite build --config vite.config.widget.ts",
60
+ "preview": "vite preview",
61
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
62
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
63
+ "test": "vitest run",
64
+ "test:watch": "vitest"
65
+ }
66
+ }