@open-webapp/calendar 0.6.1 → 0.8.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 +20 -8
- package/dist/core/events.js +2 -4
- package/dist/react/CalendarPane.js +46 -35
- package/dist/styles.css +16 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Multi-account Google Calendar read-only sync (controller) plus React UI (`CalendarPane`, `CalendarSelector`). Storage and encryption are host-supplied adapters.
|
|
4
4
|
|
|
5
|
-
> **0.
|
|
5
|
+
> **0.8.0 is BREAKING.** See [0.8.0](#080-breaking-pre-10-minor) for independent Unaccepted toggling.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -148,7 +148,7 @@ Call at top level of a vitest file; it registers a `describe('StorageAdapter con
|
|
|
148
148
|
| `sources` | `Source[]` | shown accounts; 2+ enables multi-source UI (per-source color and email) |
|
|
149
149
|
| `controller` | `CalendarController` | |
|
|
150
150
|
| `theme?` | `'system' \| 'light' \| 'dark'` | defaults to `'system'`, including when `undefined`; applies to every pane state and all internal selectors |
|
|
151
|
-
| `selectedRsvpStatuses` | `Set<RsvpStatus>` | filter; `DEFAULT_RSVP_STATUSES` = accepted, tentative, needsAction |
|
|
151
|
+
| `selectedRsvpStatuses` | `Set<RsvpStatus>` | host-controlled filter; `DEFAULT_RSVP_STATUSES` = accepted, tentative, needsAction (O pressed) |
|
|
152
152
|
| `onRsvpStatusesChange` | `(next) => void` | |
|
|
153
153
|
| `renderEventAction?` | `(event) => ReactNode` | per-event slot |
|
|
154
154
|
| `connected?` | `boolean` | defaults to `sources.length > 0` (back-compat). `false` shows the not-connected block |
|
|
@@ -162,9 +162,9 @@ Call at top level of a vitest file; it registers a `describe('StorageAdapter con
|
|
|
162
162
|
|
|
163
163
|
#### RSVP chips
|
|
164
164
|
|
|
165
|
-
The chips appear in order: Accepted, Maybe, Declined, Unaccepted.
|
|
165
|
+
The chips appear in order: Accepted, Maybe, Declined, Unaccepted (O). All four independently toggle current supplied membership. Defaults remain accepted, tentative, needsAction, so O is pressed by default.
|
|
166
166
|
|
|
167
|
-
|
|
167
|
+
O is pressed iff `needsAction` belongs to the selection, including mixed sets. Unanswered events follow that membership within existing calendar/window filters. Clicking O preserves other statuses; no previous RSVP selection is saved/restored. Removing the last status returns `DEFAULT_RSVP_STATUSES`: O-only -> O returns defaults with O still pressed. Explicit empty host selections remain empty; host rerenders do not normalize membership or emit callbacks.
|
|
168
168
|
|
|
169
169
|
The Unaccepted badge is always visible, including at zero. It counts `needsAction` events from the controller's selected-calendar, rolling ±30-day snapshot before RSVP filtering. Visible counts cap at `99+`; the chip's `aria-label` and `title` expose the full count.
|
|
170
170
|
|
|
@@ -201,7 +201,7 @@ import '@open-webapp/calendar/styles.css';
|
|
|
201
201
|
|
|
202
202
|
### Theme behavior
|
|
203
203
|
|
|
204
|
-
Both components follow the OS/browser `prefers-color-scheme` preference live in `'system'` mode; omitting `theme` preserves this default. Explicit `'light'` and `'dark'` ignore OS changes. Hosts can change the prop on a mounted component (including back to `'system'` or `undefined`) without remounting, closing an open dropdown, losing its search query, focus or keyboard-active option, changing selected calendars, or resetting the pane's
|
|
204
|
+
Both components follow the OS/browser `prefers-color-scheme` preference live in `'system'` mode; omitting `theme` preserves this default. Explicit `'light'` and `'dark'` ignore OS changes. Hosts can change the prop on a mounted component (including back to `'system'` or `undefined`) without remounting, closing an open dropdown, losing its search query, focus or keyboard-active option, changing selected calendars, or resetting the pane's current RSVP selection. Theme changes do not trigger selection callbacks or controller refreshes.
|
|
205
205
|
|
|
206
206
|
Each pane forwards its requested mode to its unified grouped footer selector, including both account groups, account-local actions and the shared search. The single-trigger structure and cross-account keyboard navigation are preserved; the approved trigger geometry exception is described above. Standalone selectors choose their own mode: an omitted/system selector follows the OS even inside an explicitly dark or light pane. Adjacent and nested instances can use opposite modes independently; no document/body theme switch is applied.
|
|
207
207
|
|
|
@@ -211,7 +211,7 @@ The exported standalone selector keeps its single-list props and behavior; Selec
|
|
|
211
211
|
|
|
212
212
|
### Host CSS
|
|
213
213
|
|
|
214
|
-
Day ribbon styling:
|
|
214
|
+
Day ribbon styling: each day's ribbon alternates between two tones by day parity (`data-parity="even|odd"`), set with `--calendar-day-ribbon-a` and `--calendar-day-ribbon-b`. Parity is stable across scroll, filtering and month boundaries, and the ribbon does not mark today. Labels center in the actual ribbon/viewport intersection below the measured chips and that day's header, including empty Today before an event day. Short ribbons center within their bounds. Scroll, resize, rendered-content and mounted theme changes update measurements; `--calendar-day-header-height` no longer positions the label.
|
|
215
215
|
|
|
216
216
|
`styles.css` is self-contained, using component-scoped semantic palette tokens and native-control `color-scheme`. Each pane/selector root resets the full palette; explicit dark overrides and system-only `prefers-color-scheme` rules keep instances isolated. It requires no host CSS variables. Layout/status variables:
|
|
217
217
|
|
|
@@ -223,9 +223,21 @@ The Unaccepted count badge uses `.calendar-rsvp-chip-badge`, with light and dark
|
|
|
223
223
|
|
|
224
224
|
The shared `.calendar-selector-trigger` uses border-box inline-flex, centered alignment, an 8px gap, 36px minimum height, 8px 12px padding, 6px radius, a 1px solid control border, inherited font and pointer cursor. `.calendar-selector-trigger-label` bounds long labels with ellipsis; `.calendar-selector-chevron` stays 12px and does not shrink or add a tab stop. Existing scoped surface/text/control-border tokens style the resting trigger, the hover token supplies its hover background, and the focus token supplies a 2px `:focus-visible` outline with 2px offset. This approved visual-only exception changes trigger geometry and its derived dropdown position, preserving surrounding layout and theme isolation. Import the shipped stylesheet for both footer and standalone use.
|
|
225
225
|
|
|
226
|
+
## 0.8.0 (BREAKING pre-1.0 minor)
|
|
227
|
+
|
|
228
|
+
- Unaccepted independently toggles `needsAction`, replacing the exclusive shortcut and local restoration introduced in 0.3.0. Hosts relying on pending-only callbacks or restoration must adapt to callbacks reflecting current membership.
|
|
229
|
+
- Defaults and last-deselection fallback remain accepted/tentative/needsAction; O is pressed for defaults and mixed selections containing needsAction.
|
|
230
|
+
- Ribbon labels center in the measured visible intersection, including empty Today, scrolling, resizing and mounted theme changes.
|
|
231
|
+
|
|
232
|
+
## 0.7.0 (minor, breaking-visual)
|
|
233
|
+
|
|
234
|
+
- Day ribbons alternate between two tones by day parity (`data-parity="even|odd"`, days since 1970 of the date key mod 2) instead of highlighting today. Parity is stable across scroll, filter and month boundaries.
|
|
235
|
+
- "Today" is now the browser's local date (previously UTC, which could be off by a day for users away from UTC). Sticky day header text ("Today · Oct 26") is unchanged.
|
|
236
|
+
- Migration: `--calendar-today-accent` was removed in 0.7.0; use `--calendar-day-ribbon-a` / `--calendar-day-ribbon-b`. The ribbon no longer marks today (`data-today` is now `data-parity`).
|
|
237
|
+
|
|
226
238
|
## 0.6.0 (minor, additive-visual)
|
|
227
239
|
|
|
228
|
-
- Each day has a thin vertical ribbon left of its events with a rotated `Mmm D` label;
|
|
240
|
+
- Each day has a thin vertical ribbon left of its events with a rotated `Mmm D` label; ribbons alternate tones by day parity (see 0.7.0). The sticky day header is unchanged.
|
|
229
241
|
- The RSVP status bar moved from the left edge to the right edge of event rows.
|
|
230
242
|
- New helper `formatRibbonLabel`.
|
|
231
243
|
|
|
@@ -245,7 +257,7 @@ The shared `.calendar-selector-trigger` uses border-box inline-flex, centered al
|
|
|
245
257
|
|
|
246
258
|
## 0.3.0 (minor, additive)
|
|
247
259
|
|
|
248
|
-
-
|
|
260
|
+
- Added the Unaccepted exclusive shortcut after Accepted, Maybe, and Declined, with local selection restoration and default fallback. This historical behavior is superseded by the independent toggle in 0.8.0.
|
|
249
261
|
- Adds an always-visible Unaccepted count badge, including zero, computed before RSVP filtering from the controller's selected-calendar ±30-day snapshot. Counts above 99 display `99+`, with the full count in the chip's `aria-label` and `title`.
|
|
250
262
|
- Adds the `.calendar-rsvp-chip-badge` host styling hook. Public props and default RSVP statuses remain compatible.
|
|
251
263
|
|
package/dist/core/events.js
CHANGED
|
@@ -39,9 +39,6 @@ export function getRollingWindow(now) {
|
|
|
39
39
|
const end = new Date(now.getTime() + 30 * MS_PER_DAY);
|
|
40
40
|
return { start: start.toISOString(), end: end.toISOString() };
|
|
41
41
|
}
|
|
42
|
-
function toIsoDate(d) {
|
|
43
|
-
return d.toISOString().slice(0, 10);
|
|
44
|
-
}
|
|
45
42
|
/**
|
|
46
43
|
* Returns events whose `selfResponseStatus` is a member of `selected` —
|
|
47
44
|
* declined is a normal, independently-selectable status now, not hard-excluded.
|
|
@@ -176,7 +173,8 @@ function addDaysToIso(dateKey, days) {
|
|
|
176
173
|
const { year, month, day } = parseIsoDateParts(dateKey);
|
|
177
174
|
const d = new Date(Date.UTC(year, month - 1, day));
|
|
178
175
|
d.setUTCDate(d.getUTCDate() + days);
|
|
179
|
-
|
|
176
|
+
// d is built from pure UTC parts (not "now"), so the UTC date is the right one.
|
|
177
|
+
return d.toISOString().slice(0, 10);
|
|
180
178
|
}
|
|
181
179
|
/**
|
|
182
180
|
* "Today · Sept 26" / "Tomorrow · Sept 27" / else "Sept 28"-style.
|
|
@@ -76,7 +76,12 @@ function openEventPopup(url, popupRef, popupName) {
|
|
|
76
76
|
popup.focus();
|
|
77
77
|
}
|
|
78
78
|
function toIsoDate(d) {
|
|
79
|
-
|
|
79
|
+
const p = (n) => String(n).padStart(2, '0');
|
|
80
|
+
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}`;
|
|
81
|
+
}
|
|
82
|
+
function dayParity(dateKey) {
|
|
83
|
+
const [y, m, d] = dateKey.split('-').map(Number);
|
|
84
|
+
return Math.floor(Date.UTC(y, m - 1, d) / 86400000) % 2 === 0 ? 'even' : 'odd';
|
|
80
85
|
}
|
|
81
86
|
function formatTimeOfDay(dateTime) {
|
|
82
87
|
const d = new Date(dateTime);
|
|
@@ -133,12 +138,6 @@ export function CalendarPane(props) {
|
|
|
133
138
|
const scrollRef = useRef(null);
|
|
134
139
|
const popupRef = useRef(null);
|
|
135
140
|
const [now, setNow] = useState(() => new Date());
|
|
136
|
-
const [savedSelection, setSavedSelection] = useState(null);
|
|
137
|
-
const isExclusive = selectedRsvpStatuses.size === 1 && selectedRsvpStatuses.has('needsAction');
|
|
138
|
-
useEffect(() => {
|
|
139
|
-
if (!isExclusive && savedSelection !== null)
|
|
140
|
-
setSavedSelection(null);
|
|
141
|
-
}, [isExclusive, savedSelection]);
|
|
142
141
|
const unacceptedCount = useMemo(() => events.filter((event) => event.selfResponseStatus === 'needsAction').length, [events]);
|
|
143
142
|
const filteredEvents = useMemo(() => filterByRsvp(events, selectedRsvpStatuses), [events, selectedRsvpStatuses]);
|
|
144
143
|
const calendarsByKey = useMemo(() => {
|
|
@@ -161,21 +160,8 @@ export function CalendarPane(props) {
|
|
|
161
160
|
const nextUpcomingEvent = findNextUpcomingEvent(filteredEvents, now);
|
|
162
161
|
const selectedEventKey = currentEventKeys.values().next().value ??
|
|
163
162
|
(nextUpcomingEvent ? getCalendarEventKey(nextUpcomingEvent) : undefined);
|
|
164
|
-
function handleUnaccepted() {
|
|
165
|
-
if (isExclusive) {
|
|
166
|
-
props.onRsvpStatusesChange(savedSelection ?? new Set(DEFAULT_RSVP_STATUSES));
|
|
167
|
-
setSavedSelection(null);
|
|
168
|
-
}
|
|
169
|
-
else {
|
|
170
|
-
setSavedSelection(new Set(selectedRsvpStatuses));
|
|
171
|
-
props.onRsvpStatusesChange(new Set(['needsAction']));
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
163
|
function handleToggle(status) {
|
|
175
|
-
|
|
176
|
-
if (isExclusive)
|
|
177
|
-
setSavedSelection(null);
|
|
178
|
-
props.onRsvpStatusesChange(toggleRsvpStatus(base, status));
|
|
164
|
+
props.onRsvpStatusesChange(toggleRsvpStatus(selectedRsvpStatuses, status));
|
|
179
165
|
}
|
|
180
166
|
const dayGroups = groupByDay(filteredEvents, todayIso);
|
|
181
167
|
function scrollToToday(behavior = 'auto') {
|
|
@@ -272,26 +258,51 @@ export function CalendarPane(props) {
|
|
|
272
258
|
useEffect(() => {
|
|
273
259
|
const chipsEl = chipsRef.current;
|
|
274
260
|
const paneEl = paneRef.current;
|
|
275
|
-
|
|
261
|
+
const scrollEl = scrollRef.current;
|
|
262
|
+
if (!chipsEl || !paneEl || !scrollEl)
|
|
276
263
|
return;
|
|
264
|
+
const ribbons = Array.from(scrollEl.querySelectorAll('.calendar-day-ribbon'));
|
|
265
|
+
function updateRibbonWindows() {
|
|
266
|
+
const viewport = scrollEl.getBoundingClientRect();
|
|
267
|
+
const chipsBottom = chipsEl.getBoundingClientRect().bottom;
|
|
268
|
+
for (const ribbon of ribbons) {
|
|
269
|
+
const bounds = ribbon.getBoundingClientRect();
|
|
270
|
+
const header = ribbon.parentElement?.previousElementSibling;
|
|
271
|
+
const headerBottom = header?.getBoundingClientRect().bottom ?? chipsBottom;
|
|
272
|
+
const top = Math.max(bounds.top, viewport.top, chipsBottom, headerBottom);
|
|
273
|
+
const bottom = Math.min(bounds.bottom, viewport.bottom);
|
|
274
|
+
ribbon.style.setProperty('--calendar-ribbon-window-top', `${Math.min(bounds.height, Math.max(0, top - bounds.top))}px`);
|
|
275
|
+
ribbon.style.setProperty('--calendar-ribbon-window-height', `${Math.max(0, bottom - top)}px`);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
// Observe layout inputs only: the absolutely positioned window cannot resize them.
|
|
277
279
|
const observer = new ResizeObserver((entries) => {
|
|
278
280
|
for (const entry of entries) {
|
|
279
|
-
if (entry.target ===
|
|
280
|
-
// The day ribbon centers its label in the visible scroll area, so it needs that height.
|
|
281
|
+
if (entry.target === scrollEl) {
|
|
281
282
|
paneEl.style.setProperty('--calendar-scroll-height', `${entry.target.getBoundingClientRect().height}px`);
|
|
282
|
-
continue;
|
|
283
283
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
284
|
+
else if (entry.target === chipsEl) {
|
|
285
|
+
// contentRect excludes padding; the sticky day-header offset needs the
|
|
286
|
+
// chip row's full border-box height (chips have vertical padding).
|
|
287
|
+
const height = entry.borderBoxSize?.[0]?.blockSize ?? entry.target.getBoundingClientRect().height;
|
|
288
|
+
paneEl.style.setProperty('--chip-row-height', `${height}px`);
|
|
289
|
+
}
|
|
288
290
|
}
|
|
291
|
+
updateRibbonWindows();
|
|
289
292
|
});
|
|
293
|
+
observer.observe(scrollEl);
|
|
290
294
|
observer.observe(chipsEl);
|
|
291
|
-
|
|
292
|
-
observer.observe(
|
|
293
|
-
|
|
294
|
-
|
|
295
|
+
for (const header of dayHeaderRefs.current.values())
|
|
296
|
+
observer.observe(header);
|
|
297
|
+
for (const ribbon of ribbons)
|
|
298
|
+
observer.observe(ribbon);
|
|
299
|
+
scrollEl.addEventListener('scroll', updateRibbonWindows, { passive: true });
|
|
300
|
+
updateRibbonWindows();
|
|
301
|
+
return () => {
|
|
302
|
+
observer.disconnect();
|
|
303
|
+
scrollEl.removeEventListener('scroll', updateRibbonWindows);
|
|
304
|
+
};
|
|
305
|
+
}, [connected, needsReauth, filteredEvents, todayIso, theme]);
|
|
295
306
|
if (!connected) {
|
|
296
307
|
return (_jsx("div", { className: "calendar-pane", "data-calendar-theme": theme, children: _jsx("div", { className: "calendar-pane-prompt", children: props.emptyState ?? (_jsxs(_Fragment, { children: [_jsx("p", { children: "Not connected to Google Calendar." }), _jsx("button", { className: "calendar-connect-button", onClick: props.onConnect, children: "Connect Google Calendar" })] })) }) }));
|
|
297
308
|
}
|
|
@@ -305,14 +316,14 @@ export function CalendarPane(props) {
|
|
|
305
316
|
const loading = ctrlState.loading && filteredEvents.length === 0;
|
|
306
317
|
const emptyStateText = 'No matching events in the next 30 days';
|
|
307
318
|
const showCalendarSelector = calendarSources.length > 0;
|
|
308
|
-
return (_jsxs("div", { className: "calendar-pane", "data-calendar-theme": theme, ref: paneRef, children: [_jsxs("div", { className: "calendar-pane-scroll", ref: scrollRef, children: [_jsxs("div", { className: "calendar-rsvp-chips", ref: chipsRef, children: [_jsx("button", { type: "button", className: "calendar-rsvp-chip calendar-scroll-today-btn", "aria-label": "Scroll to today", title: "Scroll to today", onClick: () => recenter('smooth'), children: _jsxs("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", children: [_jsx("rect", { x: "2", y: "3", width: "11", height: "10", rx: "1.5", stroke: "currentColor", strokeWidth: "1.2" }), _jsx("path", { d: "M2 6H13", stroke: "currentColor", strokeWidth: "1.2" }), _jsx("path", { d: "M5 2V4M10 2V4", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round" }), _jsx("circle", { cx: "7.5", cy: "9.5", r: "1.3", fill: "currentColor" })] }) }), RSVP_CHIPS.map((chip) => (_jsxs("button", { type: "button", className: `calendar-rsvp-chip${
|
|
319
|
+
return (_jsxs("div", { className: "calendar-pane", "data-calendar-theme": theme, ref: paneRef, children: [_jsxs("div", { className: "calendar-pane-scroll", ref: scrollRef, children: [_jsxs("div", { className: "calendar-rsvp-chips", ref: chipsRef, children: [_jsx("button", { type: "button", className: "calendar-rsvp-chip calendar-scroll-today-btn", "aria-label": "Scroll to today", title: "Scroll to today", onClick: () => recenter('smooth'), children: _jsxs("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", children: [_jsx("rect", { x: "2", y: "3", width: "11", height: "10", rx: "1.5", stroke: "currentColor", strokeWidth: "1.2" }), _jsx("path", { d: "M2 6H13", stroke: "currentColor", strokeWidth: "1.2" }), _jsx("path", { d: "M5 2V4M10 2V4", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round" }), _jsx("circle", { cx: "7.5", cy: "9.5", r: "1.3", fill: "currentColor" })] }) }), RSVP_CHIPS.map((chip) => (_jsxs("button", { type: "button", className: `calendar-rsvp-chip${selectedRsvpStatuses.has(chip.status) ? ' selected' : ''}`, "aria-pressed": selectedRsvpStatuses.has(chip.status), "aria-label": chip.status === 'needsAction' ? `${chip.label} (${unacceptedCount})` : chip.label, title: chip.status === 'needsAction' ? `${chip.label} (${unacceptedCount})` : chip.label, onClick: () => handleToggle(chip.status), children: [chip.icon, chip.status === 'needsAction' && (_jsx("span", { className: "calendar-rsvp-chip-badge", "aria-hidden": "true", children: unacceptedCount > 99 ? '99+' : unacceptedCount }))] }, chip.status)))] }), needsScopeSources.map((s) => (_jsx("div", { children: props.sourceNotice?.(s) }, s.id))), error && (_jsx("div", { className: "empty-state calendar-empty-state", role: "alert", children: _jsx("p", { children: "Couldn't load calendar events. Try again shortly." }) })), !(error && filteredEvents.length === 0) && (_jsxs("div", { className: "calendar-day-groups", children: [dayGroups.map((group) => (_jsxs("div", { className: "calendar-day-group", children: [_jsx("div", { className: "calendar-day-header", ref: (el) => {
|
|
309
320
|
if (el) {
|
|
310
321
|
dayHeaderRefs.current.set(group.dateKey, el);
|
|
311
322
|
}
|
|
312
323
|
else {
|
|
313
324
|
dayHeaderRefs.current.delete(group.dateKey);
|
|
314
325
|
}
|
|
315
|
-
}, children: formatDayHeader(group.dateKey, todayIso) }), group.events.length > 0 && (_jsxs("div", { className: "calendar-day-body", children: [_jsx("div", { className: "calendar-day-ribbon", "aria-hidden": "true", "data-
|
|
326
|
+
}, children: formatDayHeader(group.dateKey, todayIso) }), group.events.length > 0 && (_jsxs("div", { className: "calendar-day-body", children: [_jsx("div", { className: "calendar-day-ribbon", "aria-hidden": "true", "data-parity": dayParity(group.dateKey), children: _jsx("div", { className: "calendar-day-ribbon-window", children: _jsx("span", { className: "calendar-day-ribbon-label", children: formatRibbonLabel(group.dateKey) }) }) }), _jsx("div", { className: "calendar-day-events", children: group.events.map((event) => {
|
|
316
327
|
const eventKey = getCalendarEventKey(event);
|
|
317
328
|
const calendar = calendarsByKey.get(`${event.sourceId}\u0000${event.calendarId}`);
|
|
318
329
|
const showCalendarLabel = calendar && !calendar.primary;
|
package/dist/styles.css
CHANGED
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
--calendar-chip-text: #333;
|
|
12
12
|
--calendar-chip-border: #ccc;
|
|
13
13
|
--calendar-chip-selected: #081a59;
|
|
14
|
-
--calendar-
|
|
14
|
+
--calendar-day-ribbon-a: #e4e8ef;
|
|
15
|
+
--calendar-day-ribbon-b: #c8d6f0;
|
|
15
16
|
--calendar-chip-selected-text: #ffffff;
|
|
16
17
|
--calendar-badge: #e8eefb;
|
|
17
18
|
--calendar-badge-text: #081a59;
|
|
@@ -204,6 +205,7 @@
|
|
|
204
205
|
}
|
|
205
206
|
|
|
206
207
|
.calendar-day-ribbon {
|
|
208
|
+
position: relative;
|
|
207
209
|
flex: 0 0 16px;
|
|
208
210
|
width: 16px;
|
|
209
211
|
min-height: 48px;
|
|
@@ -217,16 +219,18 @@
|
|
|
217
219
|
align-items: flex-start;
|
|
218
220
|
}
|
|
219
221
|
|
|
220
|
-
.calendar-day-ribbon[data-
|
|
221
|
-
background: var(--calendar-
|
|
222
|
-
|
|
222
|
+
.calendar-day-ribbon[data-parity='even'] {
|
|
223
|
+
background: var(--calendar-day-ribbon-a);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.calendar-day-ribbon[data-parity='odd'] {
|
|
227
|
+
background: var(--calendar-day-ribbon-b);
|
|
223
228
|
}
|
|
224
229
|
|
|
225
230
|
.calendar-day-ribbon-window {
|
|
226
|
-
position:
|
|
227
|
-
top:
|
|
228
|
-
|
|
229
|
-
height: min(100%, calc(var(--calendar-scroll-height, 100vh) - var(--chip-row-height, 44px) - var(--calendar-day-header-height, 33px)));
|
|
231
|
+
position: absolute;
|
|
232
|
+
top: var(--calendar-ribbon-window-top, 0px);
|
|
233
|
+
height: var(--calendar-ribbon-window-height, 100%);
|
|
230
234
|
display: flex;
|
|
231
235
|
align-items: center;
|
|
232
236
|
}
|
|
@@ -395,7 +399,8 @@
|
|
|
395
399
|
--calendar-chip-text: #eee;
|
|
396
400
|
--calendar-chip-border: #444;
|
|
397
401
|
--calendar-chip-selected: #22d0ef;
|
|
398
|
-
--calendar-
|
|
402
|
+
--calendar-day-ribbon-a: #2b3342;
|
|
403
|
+
--calendar-day-ribbon-b: #1c3a5e;
|
|
399
404
|
--calendar-chip-selected-text: #081a59;
|
|
400
405
|
--calendar-badge: #081a59;
|
|
401
406
|
--calendar-badge-text: #e8eefb;
|
|
@@ -437,7 +442,8 @@
|
|
|
437
442
|
--calendar-chip-text: #eee;
|
|
438
443
|
--calendar-chip-border: #444;
|
|
439
444
|
--calendar-chip-selected: #22d0ef;
|
|
440
|
-
|
|
445
|
+
--calendar-day-ribbon-a: #2b3342;
|
|
446
|
+
--calendar-day-ribbon-b: #1c3a5e;
|
|
441
447
|
--calendar-chip-selected-text: #081a59;
|
|
442
448
|
--calendar-badge: #081a59;
|
|
443
449
|
--calendar-badge-text: #e8eefb;
|