<bmx-calendar>
A diary.
The whole diary is one element with a JSON attribute and no script — which is what a Razor
page, a Blazor component, a Django template or an htmx swap can actually produce. An all-day event's
end is inclusive here: “the 9th to the 11th” is three days, because that is what
the person typing it means. The iCalendar convention is exclusive and is right for a machine, which can subtract a
day.
Use the keyboard. Tab reaches the grid once, not forty-two times. The arrows move a day, up and
down move a week, and Page Up and Page Down move a month — then Enter steps into a day so
the events in it are reachable, and Escape comes back out. That two-mode model is what lets a month be a
grid at all; without it the events in a month view are either forty-two extra tab stops or unreachable.
The Conference is one bar across three days rather than three pieces that happen to line up —
and if it crossed a Sunday it would draw as two bars, each open at the end where it continues. That clipping is
weekBars in src/core/diary.ts, which is the same lane-packing function a day column will
use for overlapping meetings: a three-day event crossing a week and two meetings overlapping at half past ten look
nothing alike and are the same problem.
The hour rail is decoration and is not announced. The obvious markup for a time grid — a row
per half hour, a cell per day — is a lie the moment two meetings overlap or one runs from 9:15 to 10:45:
there is no honest cell for it to be in, and a reader arrowing through that table hears fragments of meetings in
slots they do not start in. So each day is one cell whose name is its events in time order, and
Enter steps into it for the detail. Left and right walk the days; up and down move between the all-day
strip and the column, because that is what is above and below in a week.
day-start and day-end window the view, because a diary drawn midnight to midnight spends
two thirds of its height on hours nobody has meetings in. Tuesday's early call starts at half past six and
the grid begins at eight — it is clipped to the top edge and drawn open, and still announced at half
past six, because the reader is being told when the meeting is rather than where the box was cut. The
Conference has no time of day, so it has no position on a time axis: it is in the strip and never
in the column.
The agenda is a different view rather than a stylesheet over the same markup, because it is a different
thing to a screen reader: a month is a grid, where the position of a cell means “Tuesday” and
“next week”, and an agenda has no second dimension at all. It also leaves empty days out, which is the
whole difference between an agenda and a very thin month — somebody asking what is coming up should not have
to scroll past twenty empty rows to find out.
+2 more is drawn and never read: what it announces is “2 more: One to one, Retro”. And a
limit that would hide exactly one hides none, because a +1 takes the room of the event it replaced and
says less — the same rule, and the same function, as the chip set and the avatar group.
editable lets a reader move and resize. The keyboard is the real gesture and the pointer is
the second one: Enter steps into the day, Space picks a meeting up, the arrows move
it a quarter hour or a day, Shift with them resizes, Enter drops it and Esc puts
it back — and every step is announced, because a block that moves on screen and says nothing is a mode with
no output. Both routes are the same state machine in src/core/diary.ts, so they cannot disagree.
“Payroll” is readonly and refuses to be picked up at all.
Nothing changes until the drop. The moving block is drawn from a proposal and your
events array is untouched; on release the component emits bmxEventChange with both the
old times and the new ones, and the page reassigns events if it accepts. A page that ignores the
event has a calendar whose blocks always snap back, which is the honest behaviour for a page that has not agreed
to the change. To refuse a drop before the reader lets go — a day that is closed, a room already
taken — assign a dropFilter function from script; the block is then drawn and announced as
refused while it is still in their hand.
Every property, event and method: https://docs.binarymission.co.uk/webcomponents/bmx/bmx-calendar.html