@nomideusz/svelte-calendar 0.9.0 → 0.10.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 +15 -7
- package/dist/calendar/Calendar.svelte +167 -59
- package/dist/calendar/Calendar.svelte.d.ts +7 -0
- package/dist/core/locale.d.ts +15 -1
- package/dist/core/locale.js +9 -2
- package/dist/theme/auto.js +27 -6
- package/dist/theme/presets.d.ts +4 -4
- package/dist/theme/presets.js +11 -7
- package/dist/views/agenda/AgendaDay.svelte +259 -110
- package/dist/views/agenda/AgendaWeek.svelte +190 -65
- package/dist/views/mobile/MobileDay.svelte +408 -213
- package/dist/views/mobile/MobileWeek.svelte +136 -74
- package/dist/views/mobile/swipe.d.ts +28 -0
- package/dist/views/mobile/swipe.js +64 -0
- package/dist/views/month/MonthGrid.svelte +166 -30
- package/dist/views/planner/PlannerDay.svelte +155 -80
- package/dist/views/planner/PlannerWeek.svelte +1155 -630
- package/dist/views/planner/PlannerWeek.svelte.d.ts +2 -0
- package/dist/views/shared/context.svelte.d.ts +3 -0
- package/dist/views/shared/context.svelte.js +13 -9
- package/dist/views/shared/format.d.ts +2 -1
- package/dist/views/shared/format.js +8 -5
- package/dist/widget/CalendarWidget.svelte +33 -5
- package/dist/widget/CalendarWidget.svelte.d.ts +16 -2
- package/dist/widget/widget.d.ts +9 -0
- package/dist/widget/widget.js +59 -13
- package/package.json +1 -1
- package/widget/widget.js +3263 -2727
- package/widget/svelte-calendar.css +0 -3054
|
@@ -13,6 +13,8 @@ interface Props {
|
|
|
13
13
|
}) => void;
|
|
14
14
|
selectedEventId?: string | null;
|
|
15
15
|
readOnly?: boolean;
|
|
16
|
+
/** Visible hour range [startHour, endHour) */
|
|
17
|
+
visibleHours?: [number, number];
|
|
16
18
|
[key: string]: unknown;
|
|
17
19
|
}
|
|
18
20
|
declare const PlannerWeek: import("svelte").Component<Props, {}, "">;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
2
|
import type { TimelineEvent, BlockedSlot } from '../../core/types.js';
|
|
3
|
+
import { type CalendarLabels } from '../../core/locale.js';
|
|
3
4
|
import type { DragState } from '../../engine/drag.svelte.js';
|
|
4
5
|
import type { ViewState } from '../../engine/view-state.svelte.js';
|
|
5
6
|
export interface CalendarContext {
|
|
@@ -39,6 +40,8 @@ export interface CalendarContext {
|
|
|
39
40
|
} | undefined;
|
|
40
41
|
readonly eventSnippet: Snippet<[TimelineEvent]> | undefined;
|
|
41
42
|
readonly emptySnippet: Snippet | undefined;
|
|
43
|
+
/** Per-instance labels (Calendar's `labels` prop merged over globals); global labels when headless. */
|
|
44
|
+
readonly labels: CalendarLabels;
|
|
42
45
|
}
|
|
43
46
|
/**
|
|
44
47
|
* Read the calendar context.
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* This module reads it and returns a typed interface for views.
|
|
6
6
|
*/
|
|
7
7
|
import { getContext } from 'svelte';
|
|
8
|
+
import { getLabels } from '../../core/locale.js';
|
|
8
9
|
import { sod } from '../../core/time.js';
|
|
9
10
|
/**
|
|
10
11
|
* Read the calendar context.
|
|
@@ -13,6 +14,15 @@ import { sod } from '../../core/time.js';
|
|
|
13
14
|
*/
|
|
14
15
|
export function useCalendarContext() {
|
|
15
16
|
const raw = getContext('calendar');
|
|
17
|
+
// Memoized: views read these per-cell per-render (often at 1Hz), so avoid
|
|
18
|
+
// allocating a fresh Set / wrapper object on every access.
|
|
19
|
+
const disabledSet = $derived(new Set(raw?.disabledDates?.map(d => sod(d.getTime())) ?? []));
|
|
20
|
+
const loadRange = raw
|
|
21
|
+
? {
|
|
22
|
+
get current() { return raw.loadRange; },
|
|
23
|
+
set: (r) => raw.setLoadRange(r),
|
|
24
|
+
}
|
|
25
|
+
: undefined;
|
|
16
26
|
return {
|
|
17
27
|
get viewState() { return raw?.viewState; },
|
|
18
28
|
get drag() { return raw?.drag; },
|
|
@@ -33,16 +43,10 @@ export function useCalendarContext() {
|
|
|
33
43
|
get ondayclick() { return raw?.ondayclick; },
|
|
34
44
|
get timezone() { return raw?.timezone; },
|
|
35
45
|
get disabledDates() { return raw?.disabledDates; },
|
|
36
|
-
get disabledSet() { return
|
|
37
|
-
get loadRange() {
|
|
38
|
-
if (!raw)
|
|
39
|
-
return undefined;
|
|
40
|
-
return {
|
|
41
|
-
get current() { return raw.loadRange; },
|
|
42
|
-
set: (r) => raw.setLoadRange(r),
|
|
43
|
-
};
|
|
44
|
-
},
|
|
46
|
+
get disabledSet() { return disabledSet; },
|
|
47
|
+
get loadRange() { return loadRange; },
|
|
45
48
|
get eventSnippet() { return raw?.eventSnippet; },
|
|
46
49
|
get emptySnippet() { return raw?.emptySnippet; },
|
|
50
|
+
get labels() { return raw?.labels ?? getLabels(); },
|
|
47
51
|
};
|
|
48
52
|
}
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
* Extracted from duplicated code in AgendaDay and AgendaWeek.
|
|
4
4
|
*/
|
|
5
5
|
import type { TimelineEvent } from '../../core/types.js';
|
|
6
|
+
import { type CalendarLabels } from '../../core/locale.js';
|
|
6
7
|
export declare function fmtTime(d: Date, locale?: string): string;
|
|
7
8
|
export declare function duration(ev: TimelineEvent): string;
|
|
8
|
-
export declare function timeUntilMs(ms: number, now: number): string;
|
|
9
|
+
export declare function timeUntilMs(ms: number, now: number, labels?: CalendarLabels): string;
|
|
9
10
|
export declare function progress(ev: TimelineEvent, now: number): number;
|
|
10
11
|
/**
|
|
11
12
|
* Group overlapping events into time slots.
|
|
@@ -5,24 +5,27 @@ export function fmtTime(d, locale) {
|
|
|
5
5
|
export function duration(ev) {
|
|
6
6
|
return fmtDuration(ev.start, ev.end);
|
|
7
7
|
}
|
|
8
|
-
export function timeUntilMs(ms, now) {
|
|
9
|
-
const L = getLabels();
|
|
8
|
+
export function timeUntilMs(ms, now, labels) {
|
|
9
|
+
const L = labels ?? getLabels();
|
|
10
10
|
const diff = ms - now;
|
|
11
11
|
if (diff <= 0)
|
|
12
12
|
return L.now;
|
|
13
13
|
const tMins = Math.floor(diff / 60000);
|
|
14
14
|
if (tMins < 60)
|
|
15
|
-
return
|
|
15
|
+
return L.inMinutes(tMins);
|
|
16
16
|
const hrs = Math.floor(tMins / 60);
|
|
17
17
|
const rm = tMins % 60;
|
|
18
18
|
if (hrs < 24)
|
|
19
|
-
return
|
|
19
|
+
return L.inHours(hrs, rm);
|
|
20
20
|
const days = Math.floor(hrs / 24);
|
|
21
|
-
return
|
|
21
|
+
return L.inDays(days);
|
|
22
22
|
}
|
|
23
23
|
export function progress(ev, now) {
|
|
24
24
|
const s = ev.start.getTime();
|
|
25
25
|
const e = ev.end.getTime();
|
|
26
|
+
// Zero-length (or inverted) event: fully "done" once its start has passed.
|
|
27
|
+
if (e <= s)
|
|
28
|
+
return now >= s ? 1 : 0;
|
|
26
29
|
return Math.min(1, Math.max(0, (now - s) / (e - s)));
|
|
27
30
|
}
|
|
28
31
|
export function groupIntoSlots(evts) {
|
|
@@ -20,22 +20,43 @@ import { presets } from "../theme/presets.js";
|
|
|
20
20
|
let {
|
|
21
21
|
api,
|
|
22
22
|
events,
|
|
23
|
-
theme = "
|
|
23
|
+
theme = "auto",
|
|
24
24
|
view = "week-planner",
|
|
25
25
|
height = "600",
|
|
26
26
|
locale,
|
|
27
27
|
dir,
|
|
28
28
|
mondaystart = "true",
|
|
29
|
-
headers
|
|
29
|
+
headers,
|
|
30
|
+
readonly,
|
|
31
|
+
pills,
|
|
32
|
+
nav,
|
|
33
|
+
mobile,
|
|
34
|
+
days,
|
|
35
|
+
compact,
|
|
36
|
+
timezone
|
|
30
37
|
} = $props();
|
|
31
|
-
const
|
|
38
|
+
const heightValue = $derived.by(() => {
|
|
39
|
+
const trimmed = height.trim();
|
|
40
|
+
if (trimmed === "auto") return "auto";
|
|
41
|
+
if (/^\d+(px)?$/.test(trimmed)) return parseInt(trimmed, 10);
|
|
42
|
+
console.warn(`[day-calendar] Unsupported height "${height}" \u2014 use pixels or "auto". Falling back to 600.`);
|
|
43
|
+
return 600;
|
|
44
|
+
});
|
|
32
45
|
const isMondayStart = $derived(mondaystart !== "false");
|
|
33
46
|
const themeStyle = $derived(
|
|
34
|
-
presets[theme]
|
|
47
|
+
theme in presets ? presets[theme] : presets.neutral
|
|
35
48
|
);
|
|
36
49
|
const dirValue = $derived(
|
|
37
50
|
dir === "rtl" || dir === "ltr" || dir === "auto" ? dir : void 0
|
|
38
51
|
);
|
|
52
|
+
const mobileValue = $derived(
|
|
53
|
+
mobile === "true" ? true : mobile === "false" ? false : "auto"
|
|
54
|
+
);
|
|
55
|
+
const daysValue = $derived.by(() => {
|
|
56
|
+
if (!days) return void 0;
|
|
57
|
+
const n = parseInt(days, 10);
|
|
58
|
+
return Number.isNaN(n) || n < 1 || n > 7 ? void 0 : n;
|
|
59
|
+
});
|
|
39
60
|
function parseHeaders(json) {
|
|
40
61
|
if (!json) return void 0;
|
|
41
62
|
try {
|
|
@@ -98,8 +119,15 @@ const adapter = $derived.by(() => {
|
|
|
98
119
|
{adapter}
|
|
99
120
|
{view}
|
|
100
121
|
theme={themeStyle}
|
|
101
|
-
height={
|
|
122
|
+
height={heightValue}
|
|
102
123
|
mondayStart={isMondayStart}
|
|
103
124
|
dir={dirValue}
|
|
104
125
|
{locale}
|
|
126
|
+
readOnly={readonly === 'true'}
|
|
127
|
+
showModePills={pills !== 'false'}
|
|
128
|
+
showNavigation={nav !== 'false'}
|
|
129
|
+
mobile={mobileValue}
|
|
130
|
+
days={daysValue}
|
|
131
|
+
compact={compact === 'true'}
|
|
132
|
+
timezone={timezone || undefined}
|
|
105
133
|
/>
|
|
@@ -3,11 +3,11 @@ interface Props {
|
|
|
3
3
|
api?: string;
|
|
4
4
|
/** JSON string of events for static/inline data (alternative to api) */
|
|
5
5
|
events?: string;
|
|
6
|
-
/** Theme preset name:
|
|
6
|
+
/** Theme preset name: auto (default — adapts to the host page), neutral, midnight */
|
|
7
7
|
theme?: string;
|
|
8
8
|
/** Default view ID */
|
|
9
9
|
view?: string;
|
|
10
|
-
/** Calendar height
|
|
10
|
+
/** Calendar height: pixels (e.g. "600") or "auto" */
|
|
11
11
|
height?: string;
|
|
12
12
|
/** BCP 47 locale tag (e.g. 'en-US', 'pl-PL') */
|
|
13
13
|
locale?: string;
|
|
@@ -17,6 +17,20 @@ interface Props {
|
|
|
17
17
|
mondaystart?: string;
|
|
18
18
|
/** Custom HTTP headers as JSON string for REST adapter */
|
|
19
19
|
headers?: string;
|
|
20
|
+
/** Read-only mode: "true" disables drag/resize/create */
|
|
21
|
+
readonly?: string;
|
|
22
|
+
/** Show the Day/Week/Month pills (default: true) */
|
|
23
|
+
pills?: string;
|
|
24
|
+
/** Show prev/next/today navigation (default: true) */
|
|
25
|
+
nav?: string;
|
|
26
|
+
/** Mobile mode: "auto" (default), "true", "false" */
|
|
27
|
+
mobile?: string;
|
|
28
|
+
/** Days shown in week mode, e.g. "3" or "5" */
|
|
29
|
+
days?: string;
|
|
30
|
+
/** Compact agenda rendering: "true" */
|
|
31
|
+
compact?: string;
|
|
32
|
+
/** IANA timezone, e.g. "Europe/Warsaw" */
|
|
33
|
+
timezone?: string;
|
|
20
34
|
}
|
|
21
35
|
declare const CalendarWidget: import("svelte").Component<Props, {}, "">;
|
|
22
36
|
type CalendarWidget = ReturnType<typeof CalendarWidget>;
|
package/dist/widget/widget.d.ts
CHANGED
|
@@ -1 +1,10 @@
|
|
|
1
|
+
declare global {
|
|
2
|
+
/**
|
|
3
|
+
* The widget's bundled CSS, assigned at the top of widget.js by the
|
|
4
|
+
* inlineCss build plugin (vite.config.widget.ts). Undefined when this
|
|
5
|
+
* module is consumed through the normal library path (dev, SvelteKit),
|
|
6
|
+
* where styles ship through the regular Svelte CSS pipeline instead.
|
|
7
|
+
*/
|
|
8
|
+
var __DAY_CALENDAR_CSS__: string | undefined;
|
|
9
|
+
}
|
|
1
10
|
export {};
|
package/dist/widget/widget.js
CHANGED
|
@@ -3,20 +3,70 @@
|
|
|
3
3
|
*
|
|
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
|
+
*
|
|
7
|
+
* The component mounts into an open shadow root so host-page CSS (resets,
|
|
8
|
+
* theme stylesheets) cannot bleed into the calendar and calendar styles
|
|
9
|
+
* cannot leak out. The bundled CSS is injected into each shadow root — see
|
|
10
|
+
* `injectStyles` below and the inlineCss plugin in vite.config.widget.ts.
|
|
6
11
|
*/
|
|
7
12
|
import { asClassComponent } from 'svelte/legacy';
|
|
8
13
|
import CalendarWidget from './CalendarWidget.svelte';
|
|
9
14
|
const CalendarWidgetClass = asClassComponent(CalendarWidget);
|
|
15
|
+
/**
|
|
16
|
+
* Shared constructable stylesheet — parsed once, adopted by every
|
|
17
|
+
* <day-calendar> shadow root on the page.
|
|
18
|
+
*/
|
|
19
|
+
let sharedSheet = null;
|
|
20
|
+
/**
|
|
21
|
+
* Add the bundled widget CSS to a shadow root.
|
|
22
|
+
*
|
|
23
|
+
* Prefers `adoptedStyleSheets` (one parse for N widgets); falls back to a
|
|
24
|
+
* <style> element where constructable stylesheets are unavailable. No-op
|
|
25
|
+
* when the CSS global is undefined (dev / library usage).
|
|
26
|
+
*/
|
|
27
|
+
function injectStyles(root) {
|
|
28
|
+
const css = globalThis.__DAY_CALENDAR_CSS__;
|
|
29
|
+
if (!css)
|
|
30
|
+
return;
|
|
31
|
+
if ('adoptedStyleSheets' in root && typeof CSSStyleSheet !== 'undefined') {
|
|
32
|
+
try {
|
|
33
|
+
if (!sharedSheet) {
|
|
34
|
+
sharedSheet = new CSSStyleSheet();
|
|
35
|
+
sharedSheet.replaceSync(css);
|
|
36
|
+
}
|
|
37
|
+
root.adoptedStyleSheets = [...root.adoptedStyleSheets, sharedSheet];
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// Constructable stylesheets unsupported (older engines) — fall through.
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const style = document.createElement('style');
|
|
45
|
+
style.setAttribute('data-day-calendar', '');
|
|
46
|
+
style.textContent = css;
|
|
47
|
+
root.appendChild(style);
|
|
48
|
+
}
|
|
49
|
+
const WIDGET_ATTRS = [
|
|
50
|
+
'api', 'events', 'theme', 'view', 'height', 'locale', 'dir', 'mondaystart',
|
|
51
|
+
'headers', 'readonly', 'pills', 'nav', 'mobile', 'days', 'compact', 'timezone',
|
|
52
|
+
];
|
|
10
53
|
class DayCalendarElement extends HTMLElement {
|
|
11
54
|
instance = null;
|
|
55
|
+
stylesInjected = false;
|
|
12
56
|
static get observedAttributes() {
|
|
13
|
-
return [
|
|
57
|
+
return [...WIDGET_ATTRS];
|
|
14
58
|
}
|
|
15
59
|
connectedCallback() {
|
|
16
60
|
if (this.instance)
|
|
17
61
|
return;
|
|
62
|
+
// The shadow root survives disconnect/reconnect — attach only once.
|
|
63
|
+
const root = this.shadowRoot ?? this.attachShadow({ mode: 'open' });
|
|
64
|
+
if (!this.stylesInjected) {
|
|
65
|
+
injectStyles(root);
|
|
66
|
+
this.stylesInjected = true;
|
|
67
|
+
}
|
|
18
68
|
this.instance = new CalendarWidgetClass({
|
|
19
|
-
target:
|
|
69
|
+
target: root,
|
|
20
70
|
props: this.readProps(),
|
|
21
71
|
});
|
|
22
72
|
}
|
|
@@ -32,17 +82,13 @@ class DayCalendarElement extends HTMLElement {
|
|
|
32
82
|
});
|
|
33
83
|
}
|
|
34
84
|
readProps() {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
dir: this.getAttribute('dir') ?? undefined,
|
|
43
|
-
mondaystart: this.getAttribute('mondaystart') ?? undefined,
|
|
44
|
-
headers: this.getAttribute('headers') ?? undefined,
|
|
45
|
-
};
|
|
85
|
+
const props = {};
|
|
86
|
+
for (const attr of WIDGET_ATTRS) {
|
|
87
|
+
const value = this.getAttribute(attr);
|
|
88
|
+
if (value !== null)
|
|
89
|
+
props[attr] = value;
|
|
90
|
+
}
|
|
91
|
+
return props;
|
|
46
92
|
}
|
|
47
93
|
}
|
|
48
94
|
if (!customElements.get('day-calendar')) {
|