@nomideusz/svelte-calendar 0.3.0 → 0.3.1
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 +33 -6
- package/dist/adapters/memory.d.ts +7 -2
- package/dist/adapters/memory.js +9 -6
- package/dist/adapters/recurring.d.ts +7 -2
- package/dist/adapters/recurring.js +9 -6
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +2 -0
- package/dist/core/palette.d.ts +23 -0
- package/dist/core/palette.js +109 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/views/agenda/Agenda.svelte +110 -0
- package/dist/views/day/DayGrid.svelte +33 -0
- package/dist/views/day/DayTimeline.svelte +27 -0
- package/dist/views/week/WeekGrid.svelte +35 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -169,7 +169,7 @@ The `visibleHours` prop is a `[startHour, endHour)` tuple. It applies to the Wee
|
|
|
169
169
|
|
|
170
170
|
## Subtitle & Tags on Events
|
|
171
171
|
|
|
172
|
-
`TimelineEvent`
|
|
172
|
+
`TimelineEvent` supports `subtitle` and `tags` fields — rendered automatically in **all views**:
|
|
173
173
|
|
|
174
174
|
```ts
|
|
175
175
|
const events: TimelineEvent[] = [
|
|
@@ -185,8 +185,9 @@ const events: TimelineEvent[] = [
|
|
|
185
185
|
];
|
|
186
186
|
```
|
|
187
187
|
|
|
188
|
-
- **subtitle** —
|
|
189
|
-
- **tags** —
|
|
188
|
+
- **subtitle** — secondary text below the title (all views: WeekGrid, DayGrid, DayTimeline, Agenda, EventBlock)
|
|
189
|
+
- **tags** — accent-colored pills after the title (all views)
|
|
190
|
+
- In space-constrained views (DayGrid, DayTimeline), subtitle/tags appear only when the event block is tall/wide enough
|
|
190
191
|
|
|
191
192
|
## Color Map & Auto-Coloring
|
|
192
193
|
|
|
@@ -201,10 +202,36 @@ const adapter = createMemoryAdapter(events, {
|
|
|
201
202
|
},
|
|
202
203
|
});
|
|
203
204
|
|
|
204
|
-
//
|
|
205
|
+
// Auto-assign from a built-in 15-color vivid palette
|
|
205
206
|
const adapter = createMemoryAdapter(events, { autoColor: true });
|
|
206
207
|
```
|
|
207
208
|
|
|
209
|
+
### Theme-Aware Auto-Coloring
|
|
210
|
+
|
|
211
|
+
Pass the theme's accent hex to `autoColor` and the palette is generated to harmonize with your theme — colors rotate via golden-angle hue spacing from the accent, with lightness adjusted for dark/light backgrounds:
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
// Harmonious palette seeded from indigo accent
|
|
215
|
+
const adapter = createMemoryAdapter(events, { autoColor: '#6366f1' });
|
|
216
|
+
|
|
217
|
+
// Works with the recurring adapter too
|
|
218
|
+
const adapter = createRecurringAdapter(schedule, { autoColor: '#ef4444' });
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
| `autoColor` value | Behaviour |
|
|
222
|
+
|---|---|
|
|
223
|
+
| `true` | Original 15-color vivid palette (fixed, theme-independent) |
|
|
224
|
+
| `'#ef4444'` | Golden-angle hue rotation from that accent; lightness adapted to dark/light |
|
|
225
|
+
|
|
226
|
+
You can also use the palette generator directly:
|
|
227
|
+
|
|
228
|
+
```ts
|
|
229
|
+
import { generatePalette } from '@nomideusz/svelte-calendar';
|
|
230
|
+
|
|
231
|
+
generatePalette('#6366f1', 8); // 8 theme-harmonious hex colors
|
|
232
|
+
generatePalette(); // default vivid 15-color palette
|
|
233
|
+
```
|
|
234
|
+
|
|
208
235
|
Both `createMemoryAdapter` and `createRecurringAdapter` accept `colorMap` and `autoColor` options. Events with an explicit `color` field always take priority.
|
|
209
236
|
|
|
210
237
|
## Settings Panel
|
|
@@ -421,8 +448,8 @@ import {
|
|
|
421
448
|
|
|
422
449
|
| Adapter | Use |
|
|
423
450
|
|---------|-----|
|
|
424
|
-
| `createMemoryAdapter(events, options?)` | In-memory — great for demos and prototyping. Supports `colorMap` and `autoColor
|
|
425
|
-
| `createRecurringAdapter(schedule, options?)` | Weekly recurring schedules — auto-projects onto viewed weeks. Read-only. |
|
|
451
|
+
| `createMemoryAdapter(events, options?)` | In-memory — great for demos and prototyping. Supports `colorMap` and `autoColor` (including theme-aware). |
|
|
452
|
+
| `createRecurringAdapter(schedule, options?)` | Weekly recurring schedules — auto-projects onto viewed weeks. Read-only. Supports `colorMap` and `autoColor`. |
|
|
426
453
|
| `createRestAdapter(options)` | Fetch from a REST API with configurable endpoints. |
|
|
427
454
|
|
|
428
455
|
## Standalone Views
|
|
@@ -14,7 +14,12 @@ import type { CalendarAdapter } from './types.js';
|
|
|
14
14
|
export interface MemoryAdapterOptions {
|
|
15
15
|
/** Map of category/title to color */
|
|
16
16
|
colorMap?: Record<string, string>;
|
|
17
|
-
/**
|
|
18
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Auto-assign colors to events by category or title.
|
|
19
|
+
* true → use the default vivid palette
|
|
20
|
+
* string → hex accent color (e.g. '#6366f1') to generate a
|
|
21
|
+
* theme-harmonious palette via golden-angle hue rotation
|
|
22
|
+
*/
|
|
23
|
+
autoColor?: boolean | string;
|
|
19
24
|
}
|
|
20
25
|
export declare function createMemoryAdapter(initial?: TimelineEvent[], options?: MemoryAdapterOptions): CalendarAdapter;
|
package/dist/adapters/memory.js
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
|
+
import { generatePalette, VIVID_PALETTE } from '../core/palette.js';
|
|
1
2
|
let counter = 0;
|
|
2
3
|
function uid() {
|
|
3
4
|
return `mem-${Date.now()}-${++counter}`;
|
|
4
5
|
}
|
|
5
6
|
/** Default palette for auto-coloring */
|
|
6
|
-
const AUTO_COLORS =
|
|
7
|
-
'#ef4444', '#f97316', '#eab308', '#22c55e', '#14b8a6',
|
|
8
|
-
'#3b82f6', '#6366f1', '#a855f7', '#ec4899', '#f43f5e',
|
|
9
|
-
'#06b6d4', '#84cc16', '#d946ef', '#0ea5e9', '#10b981',
|
|
10
|
-
];
|
|
7
|
+
const AUTO_COLORS = VIVID_PALETTE;
|
|
11
8
|
export function createMemoryAdapter(initial = [], options = {}) {
|
|
12
9
|
const { colorMap, autoColor } = options;
|
|
13
10
|
const events = [...initial];
|
|
11
|
+
// Resolve palette: vivid default or theme-aware
|
|
12
|
+
const palette = autoColor
|
|
13
|
+
? typeof autoColor === 'string'
|
|
14
|
+
? generatePalette(autoColor)
|
|
15
|
+
: AUTO_COLORS
|
|
16
|
+
: AUTO_COLORS;
|
|
14
17
|
// Build auto-color assignments
|
|
15
18
|
const colorAssignments = new Map();
|
|
16
19
|
let colorIndex = 0;
|
|
@@ -24,7 +27,7 @@ export function createMemoryAdapter(initial = [], options = {}) {
|
|
|
24
27
|
return colorMap[key];
|
|
25
28
|
if (autoColor) {
|
|
26
29
|
if (!colorAssignments.has(key)) {
|
|
27
|
-
colorAssignments.set(key,
|
|
30
|
+
colorAssignments.set(key, palette[colorIndex % palette.length]);
|
|
28
31
|
colorIndex++;
|
|
29
32
|
}
|
|
30
33
|
return colorAssignments.get(key);
|
|
@@ -27,8 +27,13 @@ export interface RecurringAdapterOptions {
|
|
|
27
27
|
mondayStart?: boolean;
|
|
28
28
|
/** Map of category/title to color */
|
|
29
29
|
colorMap?: Record<string, string>;
|
|
30
|
-
/**
|
|
31
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Auto-assign colors to events by category or title.
|
|
32
|
+
* true → use the default vivid palette
|
|
33
|
+
* string → hex accent color (e.g. '#6366f1') to generate a
|
|
34
|
+
* theme-harmonious palette via golden-angle hue rotation
|
|
35
|
+
*/
|
|
36
|
+
autoColor?: boolean | string;
|
|
32
37
|
}
|
|
33
38
|
/**
|
|
34
39
|
* Create a CalendarAdapter that projects recurring weekly events
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { startOfWeek } from '../core/time.js';
|
|
2
2
|
import { DAY_MS } from '../core/time.js';
|
|
3
|
+
import { generatePalette, VIVID_PALETTE } from '../core/palette.js';
|
|
3
4
|
/** Parse "HH:MM" into [hours, minutes] */
|
|
4
5
|
function parseTime(time) {
|
|
5
6
|
const [h, m] = time.split(':').map(Number);
|
|
@@ -54,11 +55,7 @@ function getOverlappingWeeks(range, mondayStart) {
|
|
|
54
55
|
return weeks;
|
|
55
56
|
}
|
|
56
57
|
/** Default palette for auto-coloring */
|
|
57
|
-
const AUTO_COLORS =
|
|
58
|
-
'#ef4444', '#f97316', '#eab308', '#22c55e', '#14b8a6',
|
|
59
|
-
'#3b82f6', '#6366f1', '#a855f7', '#ec4899', '#f43f5e',
|
|
60
|
-
'#06b6d4', '#84cc16', '#d946ef', '#0ea5e9', '#10b981',
|
|
61
|
-
];
|
|
58
|
+
const AUTO_COLORS = VIVID_PALETTE;
|
|
62
59
|
/**
|
|
63
60
|
* Create a CalendarAdapter that projects recurring weekly events
|
|
64
61
|
* onto concrete dates for whatever range the calendar requests.
|
|
@@ -67,6 +64,12 @@ const AUTO_COLORS = [
|
|
|
67
64
|
*/
|
|
68
65
|
export function createRecurringAdapter(schedule, options = {}) {
|
|
69
66
|
const { mondayStart = true, colorMap, autoColor } = options;
|
|
67
|
+
// Resolve palette: vivid default or theme-aware
|
|
68
|
+
const palette = autoColor
|
|
69
|
+
? typeof autoColor === 'string'
|
|
70
|
+
? generatePalette(autoColor)
|
|
71
|
+
: AUTO_COLORS
|
|
72
|
+
: AUTO_COLORS;
|
|
70
73
|
// Build auto-color assignments
|
|
71
74
|
const colorAssignments = new Map();
|
|
72
75
|
if (autoColor || colorMap) {
|
|
@@ -77,7 +80,7 @@ export function createRecurringAdapter(schedule, options = {}) {
|
|
|
77
80
|
colorAssignments.set(key, colorMap[key]);
|
|
78
81
|
}
|
|
79
82
|
else if (autoColor && !colorAssignments.has(key)) {
|
|
80
|
-
colorAssignments.set(key,
|
|
83
|
+
colorAssignments.set(key, palette[colorIndex % palette.length]);
|
|
81
84
|
colorIndex++;
|
|
82
85
|
}
|
|
83
86
|
}
|
package/dist/core/index.d.ts
CHANGED
|
@@ -5,3 +5,4 @@ export { setDefaultLocale, getDefaultLocale, is24HourLocale, fmtH, weekdayShort,
|
|
|
5
5
|
export { toZonedTime, fromZonedTime, nowInZone, formatInTimeZone, } from './timezone.js';
|
|
6
6
|
export type { TimelineEvent, WeekTimelineProps, DayTimelineProps, } from './types.js';
|
|
7
7
|
export { timeToX } from './types.js';
|
|
8
|
+
export { generatePalette, VIVID_PALETTE } from './palette.js';
|
package/dist/core/index.js
CHANGED
|
@@ -8,3 +8,5 @@ export { setDefaultLocale, getDefaultLocale, is24HourLocale, fmtH, weekdayShort,
|
|
|
8
8
|
// Timezone utilities
|
|
9
9
|
export { toZonedTime, fromZonedTime, nowInZone, formatInTimeZone, } from './timezone.js';
|
|
10
10
|
export { timeToX } from './types.js';
|
|
11
|
+
// Palette
|
|
12
|
+
export { generatePalette, VIVID_PALETTE } from './palette.js';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smart auto-color palette generator.
|
|
3
|
+
*
|
|
4
|
+
* Given a base accent hex (e.g. from `--dt-accent`), generates a
|
|
5
|
+
* palette of perceptually distinct colors that harmonize with the theme.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* generatePalette('#ef4444', 8) // 8 theme-harmonious colors
|
|
9
|
+
* generatePalette(undefined, 8) // falls back to the vivid default
|
|
10
|
+
*/
|
|
11
|
+
export declare const VIVID_PALETTE: string[];
|
|
12
|
+
/**
|
|
13
|
+
* Generate `count` visually distinct and theme-harmonious colors
|
|
14
|
+
* by rotating hue evenly from the base accent, keeping saturation
|
|
15
|
+
* and lightness within a pleasant range.
|
|
16
|
+
*
|
|
17
|
+
* Dark themes (l < 0.5): bump lightness to 0.55–0.65 so colors pop on dark bg.
|
|
18
|
+
* Light themes (l ≥ 0.5): pull lightness to 0.38–0.48 so colors read on light bg.
|
|
19
|
+
*
|
|
20
|
+
* @param accent Hex color string (e.g. '#ef4444'). If undefined, returns VIVID_PALETTE.
|
|
21
|
+
* @param count Number of colors to generate (default: 15).
|
|
22
|
+
*/
|
|
23
|
+
export declare function generatePalette(accent?: string, count?: number): string[];
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smart auto-color palette generator.
|
|
3
|
+
*
|
|
4
|
+
* Given a base accent hex (e.g. from `--dt-accent`), generates a
|
|
5
|
+
* palette of perceptually distinct colors that harmonize with the theme.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* generatePalette('#ef4444', 8) // 8 theme-harmonious colors
|
|
9
|
+
* generatePalette(undefined, 8) // falls back to the vivid default
|
|
10
|
+
*/
|
|
11
|
+
// ── Hardcoded vivid fallback (original behavior) ────────
|
|
12
|
+
export const VIVID_PALETTE = [
|
|
13
|
+
'#ef4444', '#f97316', '#eab308', '#22c55e', '#14b8a6',
|
|
14
|
+
'#3b82f6', '#6366f1', '#a855f7', '#ec4899', '#f43f5e',
|
|
15
|
+
'#06b6d4', '#84cc16', '#d946ef', '#0ea5e9', '#10b981',
|
|
16
|
+
];
|
|
17
|
+
// ── Color math (hex ↔ HSL) ──────────────────────────────
|
|
18
|
+
function hexToRgb(hex) {
|
|
19
|
+
const h = hex.replace('#', '');
|
|
20
|
+
const n = h.length === 3
|
|
21
|
+
? parseInt(h[0] + h[0] + h[1] + h[1] + h[2] + h[2], 16)
|
|
22
|
+
: parseInt(h, 16);
|
|
23
|
+
return [(n >> 16) & 255, (n >> 8) & 255, n & 255];
|
|
24
|
+
}
|
|
25
|
+
function rgbToHsl(r, g, b) {
|
|
26
|
+
r /= 255;
|
|
27
|
+
g /= 255;
|
|
28
|
+
b /= 255;
|
|
29
|
+
const max = Math.max(r, g, b), min = Math.min(r, g, b);
|
|
30
|
+
const l = (max + min) / 2;
|
|
31
|
+
if (max === min)
|
|
32
|
+
return [0, 0, l];
|
|
33
|
+
const d = max - min;
|
|
34
|
+
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
35
|
+
let h = 0;
|
|
36
|
+
if (max === r)
|
|
37
|
+
h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
|
|
38
|
+
else if (max === g)
|
|
39
|
+
h = ((b - r) / d + 2) / 6;
|
|
40
|
+
else
|
|
41
|
+
h = ((r - g) / d + 4) / 6;
|
|
42
|
+
return [h, s, l];
|
|
43
|
+
}
|
|
44
|
+
function hslToHex(h, s, l) {
|
|
45
|
+
h = ((h % 1) + 1) % 1; // normalize to [0, 1)
|
|
46
|
+
const hue2rgb = (p, q, t) => {
|
|
47
|
+
if (t < 0)
|
|
48
|
+
t += 1;
|
|
49
|
+
if (t > 1)
|
|
50
|
+
t -= 1;
|
|
51
|
+
if (t < 1 / 6)
|
|
52
|
+
return p + (q - p) * 6 * t;
|
|
53
|
+
if (t < 1 / 2)
|
|
54
|
+
return q;
|
|
55
|
+
if (t < 2 / 3)
|
|
56
|
+
return p + (q - p) * (2 / 3 - t) * 6;
|
|
57
|
+
return p;
|
|
58
|
+
};
|
|
59
|
+
let r, g, b;
|
|
60
|
+
if (s === 0) {
|
|
61
|
+
r = g = b = l;
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
65
|
+
const p = 2 * l - q;
|
|
66
|
+
r = hue2rgb(p, q, h + 1 / 3);
|
|
67
|
+
g = hue2rgb(p, q, h);
|
|
68
|
+
b = hue2rgb(p, q, h - 1 / 3);
|
|
69
|
+
}
|
|
70
|
+
const toHex = (v) => Math.round(v * 255).toString(16).padStart(2, '0');
|
|
71
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
72
|
+
}
|
|
73
|
+
// ── Palette generation ──────────────────────────────────
|
|
74
|
+
/**
|
|
75
|
+
* Generate `count` visually distinct and theme-harmonious colors
|
|
76
|
+
* by rotating hue evenly from the base accent, keeping saturation
|
|
77
|
+
* and lightness within a pleasant range.
|
|
78
|
+
*
|
|
79
|
+
* Dark themes (l < 0.5): bump lightness to 0.55–0.65 so colors pop on dark bg.
|
|
80
|
+
* Light themes (l ≥ 0.5): pull lightness to 0.38–0.48 so colors read on light bg.
|
|
81
|
+
*
|
|
82
|
+
* @param accent Hex color string (e.g. '#ef4444'). If undefined, returns VIVID_PALETTE.
|
|
83
|
+
* @param count Number of colors to generate (default: 15).
|
|
84
|
+
*/
|
|
85
|
+
export function generatePalette(accent, count = 15) {
|
|
86
|
+
if (!accent)
|
|
87
|
+
return VIVID_PALETTE.slice(0, count);
|
|
88
|
+
const [r, g, b] = hexToRgb(accent);
|
|
89
|
+
const [baseH, baseS, baseL] = rgbToHsl(r, g, b);
|
|
90
|
+
// Determine a good saturation range
|
|
91
|
+
const sat = Math.max(0.45, Math.min(0.8, baseS));
|
|
92
|
+
// Light vs dark theme: adjust lightness for contrast
|
|
93
|
+
const isDark = baseL < 0.5;
|
|
94
|
+
const lCenter = isDark ? 0.6 : 0.43;
|
|
95
|
+
const lRange = 0.05;
|
|
96
|
+
const colors = [];
|
|
97
|
+
for (let i = 0; i < count; i++) {
|
|
98
|
+
// Golden-angle hue rotation for maximum perceptual spread
|
|
99
|
+
const hue = baseH + (i * 0.618033988749895);
|
|
100
|
+
// Slight lightness oscillation for differentiation
|
|
101
|
+
const lOff = ((i % 3) - 1) * lRange;
|
|
102
|
+
// Slight saturation variation
|
|
103
|
+
const sOff = ((i % 2) === 0 ? 0.04 : -0.04);
|
|
104
|
+
const s = Math.max(0.35, Math.min(0.85, sat + sOff));
|
|
105
|
+
const l = Math.max(0.3, Math.min(0.7, lCenter + lOff));
|
|
106
|
+
colors.push(hslToHex(hue, s, l));
|
|
107
|
+
}
|
|
108
|
+
return colors;
|
|
109
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export { createEventStore, createViewState, createSelection, createDragState, }
|
|
|
7
7
|
export type { EventStore, ViewState, ViewStateOptions, CalendarViewId, BuiltInViewId, ViewGranularity, ViewDateRange, Selection, DragState, DragMode, DragPayload, } from './engine/index.js';
|
|
8
8
|
export { createMemoryAdapter, createRestAdapter, createRecurringAdapter } from './adapters/index.js';
|
|
9
9
|
export type { CalendarAdapter, DateRange, RestAdapterOptions, RecurringEvent, RecurringAdapterOptions, MemoryAdapterOptions, } from './adapters/index.js';
|
|
10
|
-
export { createClock, DAY_MS, HOUR_MS, HOURS, sod, startOfWeek, addDaysMs, diffDays, pad, fractionalHour, fmtHM, fmtS, dayNum, dayOfWeek, fmtH, weekdayShort, weekdayLong, monthShort, monthLong, dateShort, dateWithWeekday, fmtDay, fmtWeekRange, setDefaultLocale, getDefaultLocale, is24HourLocale, timeToX, toZonedTime, fromZonedTime, nowInZone, formatInTimeZone, } from './core/index.js';
|
|
10
|
+
export { createClock, DAY_MS, HOUR_MS, HOURS, sod, startOfWeek, addDaysMs, diffDays, pad, fractionalHour, fmtHM, fmtS, dayNum, dayOfWeek, fmtH, weekdayShort, weekdayLong, monthShort, monthLong, dateShort, dateWithWeekday, fmtDay, fmtWeekRange, setDefaultLocale, getDefaultLocale, is24HourLocale, timeToX, toZonedTime, fromZonedTime, nowInZone, formatInTimeZone, generatePalette, VIVID_PALETTE, } from './core/index.js';
|
|
11
11
|
export type { Clock, TimelineEvent, WeekTimelineProps, DayTimelineProps, } from './core/index.js';
|
|
12
12
|
export { midnight, parchment, indigo, neutral, bare, presets } from './theme/index.js';
|
|
13
13
|
export type { PresetName } from './theme/index.js';
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,6 @@ export { createEventStore, createViewState, createSelection, createDragState, }
|
|
|
9
9
|
// ─── Adapters ───────────────────────────────────────────
|
|
10
10
|
export { createMemoryAdapter, createRestAdapter, createRecurringAdapter } from './adapters/index.js';
|
|
11
11
|
// ─── Core: clock, time, locale, types ───────────────────
|
|
12
|
-
export { createClock, DAY_MS, HOUR_MS, HOURS, sod, startOfWeek, addDaysMs, diffDays, pad, fractionalHour, fmtHM, fmtS, dayNum, dayOfWeek, fmtH, weekdayShort, weekdayLong, monthShort, monthLong, dateShort, dateWithWeekday, fmtDay, fmtWeekRange, setDefaultLocale, getDefaultLocale, is24HourLocale, timeToX, toZonedTime, fromZonedTime, nowInZone, formatInTimeZone, } from './core/index.js';
|
|
12
|
+
export { createClock, DAY_MS, HOUR_MS, HOURS, sod, startOfWeek, addDaysMs, diffDays, pad, fractionalHour, fmtHM, fmtS, dayNum, dayOfWeek, fmtH, weekdayShort, weekdayLong, monthShort, monthLong, dateShort, dateWithWeekday, fmtDay, fmtWeekRange, setDefaultLocale, getDefaultLocale, is24HourLocale, timeToX, toZonedTime, fromZonedTime, nowInZone, formatInTimeZone, generatePalette, VIVID_PALETTE, } from './core/index.js';
|
|
13
13
|
// ─── Themes ─────────────────────────────────────────────
|
|
14
14
|
export { midnight, parchment, indigo, neutral, bare, presets } from './theme/index.js';
|
|
@@ -287,6 +287,9 @@
|
|
|
287
287
|
<div class="ag-card-stripe"></div>
|
|
288
288
|
<div class="ag-card-body">
|
|
289
289
|
<span class="ag-card-title">{ev.title}</span>
|
|
290
|
+
{#if ev.subtitle}
|
|
291
|
+
<span class="ag-card-sub">{ev.subtitle}</span>
|
|
292
|
+
{/if}
|
|
290
293
|
<span class="ag-card-meta">
|
|
291
294
|
{#if isNow}
|
|
292
295
|
until {fmtTime(ev.end)}
|
|
@@ -295,6 +298,13 @@
|
|
|
295
298
|
{/if}
|
|
296
299
|
<span class="ag-card-dur">{duration(ev)}</span>
|
|
297
300
|
</span>
|
|
301
|
+
{#if ev.tags?.length}
|
|
302
|
+
<div class="ag-card-tags">
|
|
303
|
+
{#each ev.tags as tag}
|
|
304
|
+
<span class="ag-card-tag">{tag}</span>
|
|
305
|
+
{/each}
|
|
306
|
+
</div>
|
|
307
|
+
{/if}
|
|
298
308
|
{#if isNow}
|
|
299
309
|
<div class="ag-card-progress">
|
|
300
310
|
<div class="ag-card-progress-fill" style:width="{progress(ev) * 100}%"></div>
|
|
@@ -411,10 +421,20 @@
|
|
|
411
421
|
<span class="ag-q-card-title">{ev.title}</span>
|
|
412
422
|
<span class="ag-q-card-eta">{timeUntilEv(ev)}</span>
|
|
413
423
|
</div>
|
|
424
|
+
{#if ev.subtitle}
|
|
425
|
+
<span class="ag-q-card-sub">{ev.subtitle}</span>
|
|
426
|
+
{/if}
|
|
414
427
|
<div class="ag-q-card-meta">
|
|
415
428
|
{fmtTime(ev.start)} – {fmtTime(ev.end)}
|
|
416
429
|
<span class="ag-q-card-dur">{duration(ev)}</span>
|
|
417
430
|
</div>
|
|
431
|
+
{#if ev.tags?.length}
|
|
432
|
+
<div class="ag-q-card-tags">
|
|
433
|
+
{#each ev.tags as tag}
|
|
434
|
+
<span class="ag-q-card-tag">{tag}</span>
|
|
435
|
+
{/each}
|
|
436
|
+
</div>
|
|
437
|
+
{/if}
|
|
418
438
|
</div>
|
|
419
439
|
</div>
|
|
420
440
|
{/each}
|
|
@@ -494,10 +514,20 @@
|
|
|
494
514
|
<span class="ag-plan-order">{i + 1}</span>
|
|
495
515
|
<span class="ag-plan-title">{ev.title}</span>
|
|
496
516
|
</div>
|
|
517
|
+
{#if ev.subtitle}
|
|
518
|
+
<span class="ag-plan-sub">{ev.subtitle}</span>
|
|
519
|
+
{/if}
|
|
497
520
|
<div class="ag-plan-meta">
|
|
498
521
|
{fmtTime(ev.start)} – {fmtTime(ev.end)}
|
|
499
522
|
<span class="ag-plan-dur">{duration(ev)}</span>
|
|
500
523
|
</div>
|
|
524
|
+
{#if ev.tags?.length}
|
|
525
|
+
<div class="ag-plan-tags">
|
|
526
|
+
{#each ev.tags as tag}
|
|
527
|
+
<span class="ag-plan-tag">{tag}</span>
|
|
528
|
+
{/each}
|
|
529
|
+
</div>
|
|
530
|
+
{/if}
|
|
501
531
|
</div>
|
|
502
532
|
</div>
|
|
503
533
|
{/each}
|
|
@@ -584,6 +614,14 @@
|
|
|
584
614
|
<span class="ag-compact-dot"></span>
|
|
585
615
|
<span class="ag-compact-time">{fmtTime(ev.start)}</span>
|
|
586
616
|
<span class="ag-compact-title">{ev.title}</span>
|
|
617
|
+
{#if ev.subtitle}
|
|
618
|
+
<span class="ag-compact-sub">{ev.subtitle}</span>
|
|
619
|
+
{/if}
|
|
620
|
+
{#if ev.tags?.length}
|
|
621
|
+
{#each ev.tags as tag}
|
|
622
|
+
<span class="ag-compact-tag">{tag}</span>
|
|
623
|
+
{/each}
|
|
624
|
+
{/if}
|
|
587
625
|
<span class="ag-compact-dur">{duration(ev)}</span>
|
|
588
626
|
</div>
|
|
589
627
|
{/each}
|
|
@@ -740,6 +778,24 @@
|
|
|
740
778
|
margin-left: 6px;
|
|
741
779
|
color: var(--dt-text-3, rgba(255, 255, 255, 0.3));
|
|
742
780
|
}
|
|
781
|
+
.ag-card-sub {
|
|
782
|
+
font-size: 11px;
|
|
783
|
+
color: var(--dt-text-2, rgba(255, 255, 255, 0.45));
|
|
784
|
+
line-height: 1;
|
|
785
|
+
}
|
|
786
|
+
.ag-card-tags {
|
|
787
|
+
display: flex;
|
|
788
|
+
gap: 4px;
|
|
789
|
+
flex-wrap: wrap;
|
|
790
|
+
}
|
|
791
|
+
.ag-card-tag {
|
|
792
|
+
font: 500 9px / 1 var(--dt-sans, system-ui, sans-serif);
|
|
793
|
+
color: var(--ev-color, var(--dt-accent));
|
|
794
|
+
background: color-mix(in srgb, var(--ev-color, var(--dt-accent)) 15%, transparent);
|
|
795
|
+
padding: 2px 5px;
|
|
796
|
+
border-radius: 3px;
|
|
797
|
+
white-space: nowrap;
|
|
798
|
+
}
|
|
743
799
|
.ag-card-progress {
|
|
744
800
|
height: 3px;
|
|
745
801
|
background: var(--dt-border, rgba(255, 255, 255, 0.06));
|
|
@@ -998,6 +1054,25 @@
|
|
|
998
1054
|
margin-left: 6px;
|
|
999
1055
|
color: var(--dt-text-3, rgba(255, 255, 255, 0.3));
|
|
1000
1056
|
}
|
|
1057
|
+
.ag-q-card-sub {
|
|
1058
|
+
font-size: 11px;
|
|
1059
|
+
color: var(--dt-text-2, rgba(255, 255, 255, 0.45));
|
|
1060
|
+
line-height: 1;
|
|
1061
|
+
}
|
|
1062
|
+
.ag-q-card-tags {
|
|
1063
|
+
display: flex;
|
|
1064
|
+
gap: 4px;
|
|
1065
|
+
flex-wrap: wrap;
|
|
1066
|
+
margin-top: 2px;
|
|
1067
|
+
}
|
|
1068
|
+
.ag-q-card-tag {
|
|
1069
|
+
font: 500 9px / 1 var(--dt-sans, system-ui, sans-serif);
|
|
1070
|
+
color: var(--ev-color, var(--dt-accent));
|
|
1071
|
+
background: color-mix(in srgb, var(--ev-color, var(--dt-accent)) 15%, transparent);
|
|
1072
|
+
padding: 2px 5px;
|
|
1073
|
+
border-radius: 3px;
|
|
1074
|
+
white-space: nowrap;
|
|
1075
|
+
}
|
|
1001
1076
|
|
|
1002
1077
|
/* ── PAST: minimal right gutter ── */
|
|
1003
1078
|
.ag-q-done {
|
|
@@ -1214,6 +1289,27 @@
|
|
|
1214
1289
|
margin-left: 6px;
|
|
1215
1290
|
color: var(--dt-text-3, rgba(255, 255, 255, 0.3));
|
|
1216
1291
|
}
|
|
1292
|
+
.ag-plan-sub {
|
|
1293
|
+
font-size: 11px;
|
|
1294
|
+
color: var(--dt-text-2, rgba(255, 255, 255, 0.45));
|
|
1295
|
+
line-height: 1;
|
|
1296
|
+
padding-left: 22px;
|
|
1297
|
+
}
|
|
1298
|
+
.ag-plan-tags {
|
|
1299
|
+
display: flex;
|
|
1300
|
+
gap: 4px;
|
|
1301
|
+
flex-wrap: wrap;
|
|
1302
|
+
padding-left: 22px;
|
|
1303
|
+
margin-top: 2px;
|
|
1304
|
+
}
|
|
1305
|
+
.ag-plan-tag {
|
|
1306
|
+
font: 500 9px / 1 var(--dt-sans, system-ui, sans-serif);
|
|
1307
|
+
color: var(--ev-color, var(--dt-accent));
|
|
1308
|
+
background: color-mix(in srgb, var(--ev-color, var(--dt-accent)) 15%, transparent);
|
|
1309
|
+
padding: 2px 5px;
|
|
1310
|
+
border-radius: 3px;
|
|
1311
|
+
white-space: nowrap;
|
|
1312
|
+
}
|
|
1217
1313
|
|
|
1218
1314
|
/* Header badges for past/future days */
|
|
1219
1315
|
.ag-badge {
|
|
@@ -1396,6 +1492,20 @@
|
|
|
1396
1492
|
color: var(--dt-text-3, rgba(255, 255, 255, 0.3));
|
|
1397
1493
|
flex-shrink: 0;
|
|
1398
1494
|
}
|
|
1495
|
+
.ag-compact-sub {
|
|
1496
|
+
font-size: 10px;
|
|
1497
|
+
color: var(--dt-text-3, rgba(255, 255, 255, 0.35));
|
|
1498
|
+
flex-shrink: 0;
|
|
1499
|
+
}
|
|
1500
|
+
.ag-compact-tag {
|
|
1501
|
+
font: 500 8px / 1 var(--dt-sans, system-ui, sans-serif);
|
|
1502
|
+
color: var(--ev-color, var(--dt-accent));
|
|
1503
|
+
background: color-mix(in srgb, var(--ev-color, var(--dt-accent)) 12%, transparent);
|
|
1504
|
+
padding: 1px 4px;
|
|
1505
|
+
border-radius: 3px;
|
|
1506
|
+
white-space: nowrap;
|
|
1507
|
+
flex-shrink: 0;
|
|
1508
|
+
}
|
|
1399
1509
|
.ag-compact-more {
|
|
1400
1510
|
font-size: 11px;
|
|
1401
1511
|
color: var(--dt-text-3);
|
|
@@ -656,6 +656,16 @@
|
|
|
656
656
|
</span>
|
|
657
657
|
{/if}
|
|
658
658
|
<span class="fs-ev-title">{p.ev.title}</span>
|
|
659
|
+
{#if p.ev.subtitle && p.heightPx > 48}
|
|
660
|
+
<span class="fs-ev-sub">{p.ev.subtitle}</span>
|
|
661
|
+
{/if}
|
|
662
|
+
{#if p.ev.tags?.length && p.heightPx > 72}
|
|
663
|
+
<span class="fs-ev-tags">
|
|
664
|
+
{#each p.ev.tags as tag}
|
|
665
|
+
<span class="fs-ev-tag">{tag}</span>
|
|
666
|
+
{/each}
|
|
667
|
+
</span>
|
|
668
|
+
{/if}
|
|
659
669
|
</div>
|
|
660
670
|
</div>
|
|
661
671
|
{/each}
|
|
@@ -1055,6 +1065,29 @@
|
|
|
1055
1065
|
white-space: nowrap;
|
|
1056
1066
|
}
|
|
1057
1067
|
|
|
1068
|
+
.fs-ev-sub {
|
|
1069
|
+
font: 400 11px / 1 var(--dt-sans);
|
|
1070
|
+
color: var(--dt-text-2);
|
|
1071
|
+
opacity: 0.6;
|
|
1072
|
+
white-space: nowrap;
|
|
1073
|
+
overflow: hidden;
|
|
1074
|
+
text-overflow: ellipsis;
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
.fs-ev-tags {
|
|
1078
|
+
display: flex;
|
|
1079
|
+
gap: 4px;
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
.fs-ev-tag {
|
|
1083
|
+
font: 500 8px / 1 var(--dt-sans);
|
|
1084
|
+
color: var(--ev-color, var(--dt-accent));
|
|
1085
|
+
background: color-mix(in srgb, var(--ev-color, var(--dt-accent)) 18%, transparent);
|
|
1086
|
+
padding: 1px 4px;
|
|
1087
|
+
border-radius: 3px;
|
|
1088
|
+
white-space: nowrap;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1058
1091
|
/* ─── Focus-visible (accessibility) ──────────── */
|
|
1059
1092
|
.fs-event:focus-visible,
|
|
1060
1093
|
.fs-night:focus-visible {
|
|
@@ -269,6 +269,14 @@
|
|
|
269
269
|
onkeydown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); oneventclick?.(p.ev); } }}
|
|
270
270
|
>
|
|
271
271
|
<span class="dt-ev-title">{p.ev.title}</span>
|
|
272
|
+
{#if p.ev.subtitle}
|
|
273
|
+
<span class="dt-ev-sub">{p.ev.subtitle}</span>
|
|
274
|
+
{/if}
|
|
275
|
+
{#if p.ev.tags?.length}
|
|
276
|
+
{#each p.ev.tags as tag}
|
|
277
|
+
<span class="dt-ev-tag">{tag}</span>
|
|
278
|
+
{/each}
|
|
279
|
+
{/if}
|
|
272
280
|
</div>
|
|
273
281
|
{/each}
|
|
274
282
|
</div>
|
|
@@ -476,6 +484,7 @@
|
|
|
476
484
|
padding: 0 8px;
|
|
477
485
|
display: flex;
|
|
478
486
|
align-items: center;
|
|
487
|
+
gap: 4px;
|
|
479
488
|
cursor: pointer;
|
|
480
489
|
background: color-mix(in srgb, var(--ev-color) 22%, transparent);
|
|
481
490
|
border-left: 3px solid var(--ev-color);
|
|
@@ -502,4 +511,22 @@
|
|
|
502
511
|
overflow: hidden;
|
|
503
512
|
text-overflow: ellipsis;
|
|
504
513
|
}
|
|
514
|
+
|
|
515
|
+
.dt-ev-sub {
|
|
516
|
+
font: 400 9px / 1 var(--dt-sans, 'Outfit', system-ui, sans-serif);
|
|
517
|
+
color: var(--dt-text-2, rgba(148, 163, 184, 0.6));
|
|
518
|
+
white-space: nowrap;
|
|
519
|
+
overflow: hidden;
|
|
520
|
+
text-overflow: ellipsis;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
.dt-ev-tag {
|
|
524
|
+
font: 500 7px / 1 var(--dt-sans, 'Outfit', system-ui, sans-serif);
|
|
525
|
+
color: var(--ev-color, var(--dt-accent));
|
|
526
|
+
background: color-mix(in srgb, var(--ev-color, var(--dt-accent)) 20%, transparent);
|
|
527
|
+
padding: 1px 3px;
|
|
528
|
+
border-radius: 2px;
|
|
529
|
+
white-space: nowrap;
|
|
530
|
+
flex-shrink: 0;
|
|
531
|
+
}
|
|
505
532
|
</style>
|
|
@@ -303,8 +303,16 @@
|
|
|
303
303
|
onkeydown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); e.stopPropagation(); oneventclick?.(ev); } }}
|
|
304
304
|
>
|
|
305
305
|
<span class="wg-ev-time">{fmtAmPm(ev.start)}</span>
|
|
306
|
-
<span class="wg-ev-title">{ev.title}</span>
|
|
307
|
-
|
|
306
|
+
<span class="wg-ev-title">{ev.title}</span> {#if ev.subtitle}
|
|
307
|
+
<span class="wg-ev-sub">{ev.subtitle}</span>
|
|
308
|
+
{/if}
|
|
309
|
+
{#if ev.tags?.length}
|
|
310
|
+
<span class="wg-ev-tags">
|
|
311
|
+
{#each ev.tags as tag}
|
|
312
|
+
<span class="wg-ev-tag">{tag}</span>
|
|
313
|
+
{/each}
|
|
314
|
+
</span>
|
|
315
|
+
{/if} </div>
|
|
308
316
|
{/each}
|
|
309
317
|
{#if day.events.length > MAX_EVENTS_SHOWN}
|
|
310
318
|
<div class="wg-ev-more">+{day.events.length - MAX_EVENTS_SHOWN} more</div>
|
|
@@ -480,7 +488,8 @@
|
|
|
480
488
|
.wg-ev {
|
|
481
489
|
display: flex;
|
|
482
490
|
align-items: center;
|
|
483
|
-
|
|
491
|
+
flex-wrap: wrap;
|
|
492
|
+
gap: 3px 5px;
|
|
484
493
|
padding: 3px 6px;
|
|
485
494
|
border-radius: 4px;
|
|
486
495
|
background: color-mix(in srgb, var(--ev-color) 12%, transparent);
|
|
@@ -516,6 +525,29 @@
|
|
|
516
525
|
text-overflow: ellipsis;
|
|
517
526
|
}
|
|
518
527
|
|
|
528
|
+
.wg-ev-sub {
|
|
529
|
+
font: 400 10px / 1 var(--dt-sans, system-ui, sans-serif);
|
|
530
|
+
color: var(--dt-text-3, rgba(0, 0, 0, 0.4));
|
|
531
|
+
white-space: nowrap;
|
|
532
|
+
overflow: hidden;
|
|
533
|
+
text-overflow: ellipsis;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
.wg-ev-tags {
|
|
537
|
+
display: flex;
|
|
538
|
+
gap: 3px;
|
|
539
|
+
flex-shrink: 0;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
.wg-ev-tag {
|
|
543
|
+
font: 500 8px / 1 var(--dt-sans, system-ui, sans-serif);
|
|
544
|
+
color: var(--ev-color, var(--dt-accent));
|
|
545
|
+
background: color-mix(in srgb, var(--ev-color, var(--dt-accent)) 15%, transparent);
|
|
546
|
+
padding: 1px 4px;
|
|
547
|
+
border-radius: 3px;
|
|
548
|
+
white-space: nowrap;
|
|
549
|
+
}
|
|
550
|
+
|
|
519
551
|
.wg-ev-more {
|
|
520
552
|
font: 500 10px / 1 var(--dt-sans, system-ui, sans-serif);
|
|
521
553
|
color: var(--dt-text-3, rgba(0, 0, 0, 0.35));
|
package/package.json
CHANGED