enhancement
major
minor
major
minor
Goal
TopLogic has no calendar view. Add one as a green-field React control plus a matching declarative element in the com.top_logic.layout.view .view.xml composition layer, taking Outlook as rough guidance for the view catalog and interaction model. This is a newly designed component; nothing is carried over from existing UI.
Scope (v1)
Granularities (Outlook-style), built on two renderers:
- Time-grid renderer → Day, Work week, Week, and (free) N-day.
- Month-grid renderer → Month and Year (12 mini-months, drill-down).
Editing: full pointer interaction — drag-move to reschedule, resize to change duration, drag-select an empty slot to create.
Non-event visuals: config-driven working-hours band (start/end hour) and non-working-day shading with locale defaults, plus current-time indicator. A pluggable per-day decorator seam is the extension point for holidays etc. No holiday backend (none exists in the platform today).
Architecture: two layers
Layer 1 — plain Java model (usable without any configuration):
- CalendarEvent interface: getStart(), getEnd(), isAllDay(), getTitle(), getTooltip(), getCategory() (→ theme color), isMovable(), isResizable(), getBusinessObject() (maps a rendered bar back to the domain object for the selection channel).
- CalendarModel: supplies events for a [from, to) interval and receives edits (moveEvent, resizeEvent, createEvent). An application can implement these interfaces directly and render them with the React control CalendarViewControl (sibling of TableViewControl / TLTableView.tsx).
'''Layer 2 — declarative CalendarElement (@TagName("calendar"), modeled on com.top_logic.layout.view.element.TableElement):'''
- Binds a model channel of business objects; an <event> config sub-element (the <column> analogue) carries per-field TL-Script expressions: start, end, all-day, title, tooltip, category, movable, resizable.
- Compiles them (QueryExecutor) into an `ExpressionCalendarEvent implements CalendarEvent` wrapper per business object, so Layer 1 sees a uniform CalendarModel and never knows about expressions.
Channels & commands
- model (in) — business objects, scoped to the visible range by the supplier.
- selection (out, two-way) — selected event → drives master/detail exactly like a table row (reuses TableElement's ChannelRef selection idiom).
- visible-range (out) — publishes the current [from, to) (a com.top_logic.base.time range) so a supplier component lazily queries only what is shown. The RowSourceObserver re-run-on-channel-change mechanism is the template.
- Recordable ViewCommand`s: navigation (`prev/`next`/`today`/`goto`), switch-granularity, and the edit trio moveEvent/`resizeEvent`/`createSlot`.
Declarative edit write-back: the <calendar> config declares on-move / on-resize / on-create TL-Script expressions receiving the object and the new start/end; CalendarElement opens the transaction around them.
Reuse
Rendering and interaction are all new. Only date math and the established view-layer wiring are reused:
- `com.top_logic.base.time.{DayRange, WeekRange, MonthRange, YearRange, TimeRangeIterator}` + TimeRangeService — compute the day/week/month cells of a view (grid geometry).
- com.top_logic.basic.time.CalendarUtil — locale/TZ Calendar, first-day-of-week, calendar-week number, SimpleDateFormat; TimeZones / UserTimeZoneDefault for the render zone; com.top_logic.basic.Day for all-day events.
- React control base com.top_logic.layout.react.control.ReactControl; reference pair TableViewControl ↔ react-src/controls/TLTableView.tsx.
- UIElement + channel binding pattern: TableElement and its Config.
Out of scope (later tickets)
Recurrence (RRULE expansion / exception dates), per-event timezone conversion, cross-resource lanes / room-booking side-by-side, infinite-scroll month, and ICS export / printing.
Verification
Wire an editable example calendar into com.top_logic.demo.react (/view/) and verify all five granularities plus drag-move/resize/create in the browser.