feat: initial release — framework-agnostic calendar engine with React, Vue, Angular, Lit, and Svelte adapters#1
Merged
Merged
Conversation
added 30 commits
June 1, 2026 20:01
Nx (Cloud off) + pnpm workspace with eight package scaffolds (core, localizer, localizer-temporal, localizer-luxon, styles, dnd, react, codemods). Adds TS (ES2024/ESM) base config, Vite library builds with .d.ts emit, Vitest (per-file 85/95 coverage bar), flat ESLint with @nx/enforce-module-boundaries scope-tag graph + Appendix A naming rules, Prettier, commitlint + Husky hooks, Vitest workspace, four CI/CD workflows (PR validation, CodeQL, release, docs deploy), and memory/ checkpoint files. All targets green: typecheck, lint, test, build across 8 projects.
Implements @big-calendar/localizer Phase 1 Task 1a: - LocalizerContract / LocalizerOptions / DateParts types (string-in/string-out) - abstract Localizer<T> template class with the full ~40-method contract built on a small protected-abstract engine primitive set - getWeekInfo ponyfill (native Intl.Locale week API + CLDR region fallback) - formatDuration ponyfill (native Intl.DurationFormat + NumberFormat/ListFormat fallback) - DEFAULT_FORMATS named Intl option sets - 44 contract tests via a UTC fixture localizer; 95.78% branch / 100% function
…k 1b plan Pauses Phase 1 mid-Task-1b for a VS Code restart. Adds the lazy-loaded temporal-polyfill@0.3.2 runtime dependency and records the full, probe-verified localizer-temporal implementation design in memory/PROGRESS.md so the next session can implement without re-investigation.
…oral API Implements @big-calendar/localizer-temporal Phase 1 Task 1b: - loadTemporal(): lazy, feature-detected polyfill loader (native globalThis.Temporal preferred; temporal-polyfill dynamically imported otherwise; cached) - TemporalLocalizer: all engine primitives over Temporal.ZonedDateTime — DST-aware startOf/endOf, offset via offsetNanoseconds, RFC 3339 vs RFC 9557 serialization, date-only (PlainDate) parsing, normalized to the localizer timezone - createTemporalLocalizer() async factory - 17 tests incl. real EST/EDT DST, bracket round-trip, loader native+cached branches
Task 1c: desk-review spike (Jan-2026 platform-knowledge cutoff) of Subgrid, Popover API, CSS Anchor Positioning, and :dir(). Outcome — adopt subgrid + Popover + :dir() (Baseline); anchor positioning is non-Baseline so floating-ui stays the default positioning engine. Records the fallback matrix and defers empirical Playwright verification to Phase 3. Closes Phase 1.
Phase 2a foundation: Views/Navigate constants (values matched to v1 for parity), ViewKey/EventId/ResourceId types, and the accessor module (accessor/wrapAccessor/resolveAccessors) with v1-parity default field names. 100% coverage.
Phase 2b: isolated store (createCalendarStore) over @preact/signals-core holding date/view/selected/events/backgroundEvents/resources signals plus actions (navigate/setView/setDate/select/setEvents/setBackgroundEvents/ setResources/destroy). Basic navigateDate (TODAY/DATE/PREV/NEXT, per-view step; agenda by length) built on LocalizerContract. Drilldown and visible-range derivation deferred to 2c. 100% branch/function coverage.
Pure, fraction-based ports of the v1 day-event layout algorithms: overlap packs shared-time events into grown container/row/leaf columns; no-overlap packs them into non-overlapping even columns. Both take pre-positioned events (top/height as 0..1 fractions) and compute the horizontal left/width plus a paint-order zIndex. Adds a default-algorithm registry and resolver (key or custom fn). Removes the scaffold smoke test.
Pure monthViewModel: splits the padded day grid into weeks, clamps each overlapping event into 1-based day-column segments (one per week it spans), and stacks them into non-overlapping levels with a per-week limit that spills overflow into each week's extra (the +N more list). Ports v1's eventSegments / eventLevels math against the LocalizerContract, dropping the legacy segmentOffset tz hack (the Temporal localizer already works in-zone).
Adds createSlotMetrics (ports v1 getSlotMetrics to 0..1 fractions over the LocalizerContract; DST-safe via getSlotDate/getDstOffset) and timeGridViewModel for the day/week/work_week views: splits events into an all-day header row vs timed events (v1's allDay/date-only/multi-day rule), builds one time column per visible day, positions timed events with slot metrics, and packs them with the chosen day-layout algorithm (overlap default, key or custom fn). Extracts the month segmentation into a shared views/segments module (datedEvents, eventSegments, stackIntoLevels, sortRowEvents, rowSegments) now reused by both the month grid and the time-grid all-day row; month view model rewritten to consume it.
agendaViewModel walks the visible days and lists, per day with events, every event touching it sorted by start (empty days omitted — v1 parity). groupEventsByResource buckets events under their resource(s) in resource order (single null group when there are no resources; multi-id events land in each matching group; unmatched events are dropped) — a pure port of v1's Resources.groupEvents, reusable across the resource-aware views.
createSelection is a pure signals FSM over slot indices (§8.2): start/to/ complete drive a drag (pointer or keyboard Shift+Arrow), click commits a single slot, cancel aborts. onSelecting fires on every range change and may veto by returning false; onSelect fires on commit. A normalized range signal drives the live highlight overlay. Date translation, long-press timing and event hit-testing stay in the adapters.
DEFAULT_MESSAGES holds the built-in English strings (v1 parity, including the work_week key and the showMore(total) formatter); resolveMessages merges caller overrides over the defaults for i18n without mutating the defaults.
Adds buildViewModel (dispatches view → month / time-grid / agenda model, tagged by kind/view) and a store-level viewModel computed signal that rebuilds from view + range + events + config. Adds the parity time-grid config options (step, timeslots, min, max, dayLayoutAlgorithm, allDayMaxRows, showMultiDayTimes, showAllEvents, selectable); the store resolves min/max to a minutes window and folds showAllEvents into the all-day row limit. Completes the §4.2 store shape's derived view model.
added 21 commits
June 26, 2026 12:07
…torybook alias configs Moves the per-package @big-calendar/* source alias map into a shared packages/aliases.ts helper. All vitest configs and storybook viteFinal hooks now call packageAliases(pkgs) instead of maintaining their own copies of the same list. Eliminates drift risk when new packages are added.
…in decorator
Toolbar globals (locale, timeZone, localizer type) now reach embedded ref
iframes in Storybook composition mode. The hub preview.ts relays UPDATE_GLOBALS
via postMessage; the decorator's new message listener picks them up and rebuilds
the localizer. manager.ts handles hard-reload at a ref story URL by injecting a
hidden iframe to prime SET_GLOBALS from the hub.
withLocalizerDecorator now wraps each story in
<div dir={localizer.direction} lang={localizer.language}> so RTL stories flip
automatically when the locale toolbar global changes. Switched to a synchronous
Luxon default to avoid top-level-await unhandledrejection on cold load.
floatingPosition: add isRTL platform method so @floating-ui/core interprets start/end placement suffixes correctly in RTL layouts. useFloatingAnchor: switch from logical inset-inline/block to physical left/top — @floating-ui/core always returns physical viewport offsets, so logical properties misplace panels in RTL. DefaultToolbar: replace glyph characters with messages.previous / messages.today / messages.next so toolbar labels honour the configured locale.
Two stories demonstrating the i18n layers working together: ArabicRTL — Arabic locale, full messages override, dir/lang set from localizer.direction/language, whole layout flips via CSS logical properties. SpanishMessages — Spanish locale with complete messages translation including the screen-reader instruction strings.
tokens.css: event border now derives from event-bg (not AccentColor) so it stays coherent with custom --bc-color-event-bg overrides. event.css + toolbar.css: switch border properties to use --bc-color-event-border. month.css: date numbers anchor to physical right in RTL via :dir(rtl) override. timegrid.css: add missing inline-start border on the time grid container.
Vue's MonthView was missing the ResizeObserver-based row measurement that React has via useMonthRowMeasure. Without it, store.measuredWeekLimit stayed at Infinity and the month view showed all events regardless of available row height — no +N more indicators ever appeared. Adds useMonthRowMeasure.ts (Vue watchEffect equivalent of the React hook) and calls it from useMonthView, watching roving.containerRef which is already set to the month grid DOM element by the template. Adds 8 unit tests mirroring the React test suite. Also fixes pre-existing lint errors in useRovingSelection.test.ts (unused function params).
Wire drag-to-move and edge-resize in the Vue adapter via useCalendarDnd and @big-calendar/dnd. Three bugs fixed along the way: - useTimeGridView was snapshotting the ComputedRef grid value at setup time (const grid = gridRef.value), so the template never re-rendered when events changed after a drop. Return the ComputedRef directly so Vue's auto-unwrap tracks it as a live dependency. - useCalendarStore wrapped onEventDrop/onEventResize conditionally, so config.onEventDrop was null when attrs settled after store creation. Always create the wrapper; read the latest callback at call time via the reactive attrs proxy. - Calendar.vue had a duplicate useCalendarDnd call alongside the story's DraggableCalendar wrapper, causing dual-registration warnings from Pragmatic DnD. Removed the internal call. Also activates the Vue package in the storybook-site composed build: scripts, wait-for-storybooks, core main.ts refs, and project.json.
- vue view components, composables, DnD, top-layer UI, accessibility - storybook stories matching react coverage (Standard, ScrollToTime, TimeWindow, EventCallbacks, Selection, DnD, BackgroundEvents, Resources, EventTypeAccessor, ResourceTypeAccessor, Localization) - composition-mode globals bridge: localStorage cache in core/preview.ts, manager.ts addon with Channel.handleEvent patch for correct source routing, bc-globals-sync postMessage relay to vue ref iframe - localizerRef.ts (sync shallowRef) + side-effect withVueLocalizerDecorator pattern to avoid async module chain in preview.ts - bodyNowIndicatorProps: body-spanning now-indicator parity with react - MCP vue recipe (basic-setup-vue.ts); MCP server MDX docs moved to core - .gitignore: add playwright-report, test-results, playwright/.auth
- PROGRESS.md: mark vue adapter complete, add angular phase 11 task list - PROGRESS-ARCHIVE.md: add phase 10 completion summary, test counts - DECISIONS.md: update active phase range to phase 11+ - DECISIONS-ARCHIVE.md: update header + topic index for phase 10 decisions
…11-1) - add packages/angular with Angular 21, @analogjs/vite-plugin-angular, @analogjs/vitest-angular - wire Storybook at port 6009 using @storybook/angular@10.5.0-alpha.9 - add angular ref to composition hub (packages/core/.storybook/main.ts) - add @big-calendar/angular alias to packages/aliases.ts - upgrade root Storybook to 10.5.0-alpha.9 (all framework packages) for Angular 21 support - approve @parcel/watcher, lmdb, msgpackr-extract builds in pnpm-workspace.yaml
… (task 11-2) - toAngularSignal(): bridge preact ReadonlySignal to Angular Signal via DestroyRef cleanup - injectCalendarStore(): getter factory; effect() syncs reactive props to CalendarStore - CalendarProps type mirrors Vue/React adapters - testing/localizers.ts harness for describe.each coverage - 25 tests across Temporal and Luxon localizer cases - vitest.setup.ts: zone.js inline + setupTestBed
…ature parity Angular adapter: CalendarComponent, MonthViewComponent, TimeGridViewComponent, AgendaViewComponent, DefaultToolbarComponent, CalendarProvider, CalendarDnd (directive + service), primitives (BcDialog, BcPopover, BcTooltip, BcEventRoving, BcRovingSelection), headless inject hooks (injectMonthView, injectTimeGridView, injectAgendaView, injectCalendarStore), signals bridge (toAngularSignal). Full canonical story set (Calendar, BackgroundEvents, CustomRendering, EventCallbacks, EventDragAndDrop, DropFromOutside, EventTypeAccessor, Resources, ResourceTypeAccessor, Selectable, Localization) with Controls, Actions, and localizer toolbar wired. Also includes: ADAPTER_STANDARDS.md (cross-framework parity rules), Playwright visual comparison suite (Angular vs React), MCP angular recipe, Vue test fixes, storybook-site composition updates.
…rity - new package: @big-calendar/lit — Lit v3 web components adapter - CalendarElement, MonthViewElement, TimeGridViewElement, AgendaViewElement - DefaultToolbarElement, BcDialogElement, BcPopoverElement, BcTooltipElement - CalendarController (reactive controller, @lit/context provider) - CalendarDndController for drag-and-drop support - light DOM via createRenderRoot() for shared bc-* CSS - storybook on port 6010 (@storybook/web-components-vite) - 11 canonical stories with Controls + Actions wired - 24 MDX documentation files - localizer toolbar decorator (bc-globals-sync bridge) - mcp: added basic-setup-lit recipe (resource count 12→13, recipes 7→8) - playwright: compare-lit-react.spec.js for visual parity vs React - root README: added Vue, Angular, Lit to package table + quick-start sections - fix(vue): create 25 missing index.ts barrel files (vue:build was failing) - fix(react): create DefaultBackgroundEvent/index.ts (react:build was failing) - infrastructure: lit registered in storybook-site, compose scripts, core refs
…ring - Move allDay segments out of bc-allday-slots into sibling bc-allday-segments container in the plain layout, matching React/Angular DOM structure - Fix SelectableWithBackgroundEvents Actions panel by adding fn() directly to story args (meta.args propagation insufficient for typed StoryObj) - Add showAllEvents (bc-show-all-events) class mechanism across core, react, lit, and angular adapters - Fix now-indicator position instability on viewport resize by using slot-count * slot-height geometry instead of 100% of time-body height - Wire --bc-slot-count into Lit and Angular root styles - Add bc-calendar-dnd, onEventDrop, onEventResize to DropFromOutside story - Update ADAPTER_STANDARDS with fn() placement rule and DnD persistence rule
…ure parity Adds the Svelte 5 adapter with signal bridge, context system, composables, view components, DnD, Storybook stories + MDX docs, 173 unit tests, MCP recipe, Playwright parity spec, and root README/memory updates.
- nx 22 → 23 (run migrations: releaseTag.pattern, .nx/migrate-runs gitignore) - pnpm 11.1 → 11.9, vite 8.0 → 8.1, vitest/coverage-v8 4.1.8 → 4.1.9 - eslint 10.4 → 10.6, prettier 3.8 → 3.9, commitlint 21.0 → 21.2 - CI actions: pnpm/action-setup v4→v6, nrwl/nx-set-shas v4→v5, upload-pages-artifact v3→v5, deploy-pages v4→v5, codeql v3→v4 - codeql: rename language: → languages: for v4 action schema - pr-validation: add build target to affected run - mcp: update resource/recipe counts for Svelte recipe added in Phase 13
The bc-date-cell / bc-today / bc-off-range classes that drive grid cell borders were previously owned by each DefaultMonthDate component. Any custom dateCell component had to know to include the outer wrapper or borders would disappear. Move the non-replaceable wrapper div into MonthView (React, Vue, Svelte) and restructure the Angular conditional so the wrapper always frames the cell whether or not a custom bcMonthDateCell template is supplied. Lit already hardcoded the wrapper in MonthViewElement and needed no change. DefaultMonthDate in React, Vue, and Svelte now renders only the inner bc-date-number button. Update the Svelte and Angular story components to remove the manual bc-date-cell wrapper they previously had to supply.
|
You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool. What Enabling Code Scanning Means:
For more information about GitHub Code Scanning, check out the documentation. |
There was a problem hiding this comment.
CodeQL found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.
- Replace polynomial regexes in localizer-luxon with indexOf/slice (ReDoS fix) - Add origin verification to postMessage handler in storybook-shared - Remove unused imports across playwright and story files - Wire missing cleanup listener in EventTypeAccessor WeekViewTyped story - Suppress CodeQL false positives with explanatory comments
| * adapters) is on the Lit roadmap. For the current release, CSS is the primary | ||
| * customization surface and already covers the most common use-cases. | ||
| */ | ||
| import { html } from 'lit' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This is the initial release of Big Calendar — a ground-up rewrite of
react-big-calendaras a framework-agnostic Nx monorepo. The same calendar engine powers five independent framework adapters, all built from a shared core with no duplicated logic.Packages shipping in this release
@big-calendar/core@big-calendar/localizer@big-calendar/localizer-temporal@big-calendar/localizer-luxon@big-calendar/styles@big-calendar/dnd@big-calendar/react@big-calendar/vue@big-calendar/angular@big-calendar/lit@big-calendar/svelte@big-calendar/codemodsreact-big-calendar@big-calendar/mcpWhat's in the engine
+N more)DateobjectsWhat's in the adapters
All five adapters ship:
componentspropTooling and CI
Test plan
nx run-many -t lint typecheck test build(verified locally before this PR)