@open-webapp/calendar 0.6.0 → 0.6.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.
@@ -130,6 +130,7 @@ export function CalendarPane(props) {
130
130
  const hasScrolledForEventsRef = useRef(false);
131
131
  const paneRef = useRef(null);
132
132
  const chipsRef = useRef(null);
133
+ const scrollRef = useRef(null);
133
134
  const popupRef = useRef(null);
134
135
  const [now, setNow] = useState(() => new Date());
135
136
  const [savedSelection, setSavedSelection] = useState(null);
@@ -274,15 +275,21 @@ export function CalendarPane(props) {
274
275
  if (!chipsEl || !paneEl)
275
276
  return;
276
277
  const observer = new ResizeObserver((entries) => {
277
- const entry = entries[0];
278
- if (!entry)
279
- return;
280
- // contentRect excludes padding; the sticky day-header offset needs the
281
- // chip row's full border-box height (chips have vertical padding).
282
- const height = entry.borderBoxSize?.[0]?.blockSize ?? entry.target.getBoundingClientRect().height;
283
- paneEl.style.setProperty('--chip-row-height', `${height}px`);
278
+ for (const entry of entries) {
279
+ if (entry.target === scrollRef.current) {
280
+ // The day ribbon centers its label in the visible scroll area, so it needs that height.
281
+ paneEl.style.setProperty('--calendar-scroll-height', `${entry.target.getBoundingClientRect().height}px`);
282
+ continue;
283
+ }
284
+ // contentRect excludes padding; the sticky day-header offset needs the
285
+ // chip row's full border-box height (chips have vertical padding).
286
+ const height = entry.borderBoxSize?.[0]?.blockSize ?? entry.target.getBoundingClientRect().height;
287
+ paneEl.style.setProperty('--chip-row-height', `${height}px`);
288
+ }
284
289
  });
285
290
  observer.observe(chipsEl);
291
+ if (scrollRef.current)
292
+ observer.observe(scrollRef.current);
286
293
  return () => observer.disconnect();
287
294
  }, [connected, needsReauth]);
288
295
  if (!connected) {
@@ -298,14 +305,14 @@ export function CalendarPane(props) {
298
305
  const loading = ctrlState.loading && filteredEvents.length === 0;
299
306
  const emptyStateText = 'No matching events in the next 30 days';
300
307
  const showCalendarSelector = calendarSources.length > 0;
301
- return (_jsxs("div", { className: "calendar-pane", "data-calendar-theme": theme, ref: paneRef, children: [_jsxs("div", { className: "calendar-pane-scroll", 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${(chip.status === 'needsAction' ? isExclusive : selectedRsvpStatuses.has(chip.status)) ? ' selected' : ''}`, "aria-pressed": chip.status === 'needsAction' ? isExclusive : selectedRsvpStatuses.has(chip.status), "aria-label": chip.status === 'needsAction' ? `${chip.label} (${unacceptedCount})` : chip.label, title: chip.status === 'needsAction' ? `${chip.label} (${unacceptedCount})` : chip.label, onClick: () => chip.status === 'needsAction' ? handleUnaccepted() : 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) => {
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${(chip.status === 'needsAction' ? isExclusive : selectedRsvpStatuses.has(chip.status)) ? ' selected' : ''}`, "aria-pressed": chip.status === 'needsAction' ? isExclusive : selectedRsvpStatuses.has(chip.status), "aria-label": chip.status === 'needsAction' ? `${chip.label} (${unacceptedCount})` : chip.label, title: chip.status === 'needsAction' ? `${chip.label} (${unacceptedCount})` : chip.label, onClick: () => chip.status === 'needsAction' ? handleUnaccepted() : 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) => {
302
309
  if (el) {
303
310
  dayHeaderRefs.current.set(group.dateKey, el);
304
311
  }
305
312
  else {
306
313
  dayHeaderRefs.current.delete(group.dateKey);
307
314
  }
308
- }, 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-today": group.dateKey === todayIso ? 'true' : undefined, children: _jsx("span", { className: "calendar-day-ribbon-label", children: formatRibbonLabel(group.dateKey) }) }), _jsx("div", { className: "calendar-day-events", children: group.events.map((event) => {
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-today": group.dateKey === todayIso ? 'true' : undefined, 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) => {
309
316
  const eventKey = getCalendarEventKey(event);
310
317
  const calendar = calendarsByKey.get(`${event.sourceId}\u0000${event.calendarId}`);
311
318
  const showCalendarLabel = calendar && !calendar.primary;
package/dist/styles.css CHANGED
@@ -222,11 +222,18 @@
222
222
  color: var(--calendar-chip-selected-text);
223
223
  }
224
224
 
225
+ .calendar-day-ribbon-window {
226
+ position: sticky;
227
+ top: calc(var(--chip-row-height, 44px) + var(--calendar-day-header-height, 33px));
228
+ /* Visible part of the ribbon: as tall as the scroll area below the sticky headers, capped at the ribbon. */
229
+ height: min(100%, calc(var(--calendar-scroll-height, 100vh) - var(--chip-row-height, 44px) - var(--calendar-day-header-height, 33px)));
230
+ display: flex;
231
+ align-items: center;
232
+ }
233
+
225
234
  .calendar-day-ribbon-label {
226
235
  writing-mode: vertical-rl;
227
236
  transform: rotate(180deg);
228
- position: sticky;
229
- top: calc(var(--chip-row-height, 44px) + var(--calendar-day-header-height, 33px));
230
237
  white-space: nowrap;
231
238
  padding: 6px 0;
232
239
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-webapp/calendar",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",