Event actions
Every move and resize also works without dragging, with a single pointer or the keyboard, through each event's actions menu (WCAG 2.5.7).
Dragging is hard or impossible for many people: those who use a switch, a head pointer, voice
control, or a hand that cannot hold a button down. WCAG 2.5.7 (Dragging Movements, level AA) asks
that whatever a drag does can also be done with single pointer actions. In Hapio Calendar that is
built in: inside <CalendarEditing> every event that can change has its actions, with nothing
to configure. Try them in the event actions example.
The menu
Each event that can be moved or resized gets a "More actions" button, named "More actions for
Haircut · Anna": just above the top inline-end corner of an event in a week or day view (outside
the event, so it never covers what a pointer presses to drag the event; an event at the very top
of the view's time window has no room above it, so its button sits over its top corner instead),
at the end of a bar in a month or the all-day row, and at the end of the line in lists and the
"+N more" popover. It stays out of the way until the event is hovered, where the pointer can hover
(a mouse or a trackpad: (hover: hover)), or the event or the button has keyboard focus
(:focus-visible). On a touchscreen it shows, and takes taps, only on an event a long-press has
selected; in lists and the "+N more" popover it is always shown. The same menu opens with a
right click on the event, Shift+F10 or the ContextMenu key on the focused event, and
Space on an event that Space cannot grab (one in the "+N more" popover). The button is not a tab
stop: keyboard users open the menu from the event, so a view keeps at most three tab stops.
One case remains: a mid-day event scrolled to within 24 px under the sticky day headers keeps its button under them until you scroll a little. A right click, a long-press and Shift+F10 open its menu there as anywhere.
The menu offers what the event allows, in this order:
| Item | Does |
|---|---|
| Move to… | places the event with the slot cursor or a click (below) |
| Change time… | opens a form with its dates and times |
| Earlier, Later | one snap (timed events in week and day views) |
| Previous day, Next day | the same time on the previous or next visible day |
| Longer, Shorter | the end, one snap (timed) or one day (all-day) |
| Change resource… | opens the form on its resource (with resources, when the event's editable.resource holds) |
A step that cannot be taken here (it would leave the visible range or the time window, or make
the event shorter than one snap) stays listed, disabled. The rules are checked when an item is
chosen: a step they refuse changes nothing and says why. Every change made through the menu reaches
onEventChange with via: 'menu', judged by the same rules as a drag.
Move to…: placing
"Move to…" starts placing: screen readers hear "Moving Haircut · Ben. Choose a time with the arrow keys and press Enter, or click a time. Press Escape to cancel.", and focus moves to the slot cursor (week and day views) or the event's day cell (month).
- The arrow keys move the cursor, the event's mirror follows, and the cursor's name ends with the verdict: "available" or "unavailable, overlaps another event". In a month, where the day cell's name stays its date, each step announces the verdict instead.
- Enter or Space places the event there; a click or tap on any slot or day places it at that point. Either is a single pointer action.
- PageUp and PageDown go to the previous or next period, and placing continues there.
- Escape cancels, and focus returns to the event.
Change time…: the reschedule form
"Change time…" opens a dialog, "Change time of Haircut · Anna", with the event's start and end dates and times (native date and time inputs, so they follow the browser's locale), an all-day checkbox and, with resources, the resource. On a DST day it asks which offset a wall time that happens twice should have ("02:30 happens twice on this day (the clocks go back). Which start time?"), and it says when a time does not exist ("02:30 does not exist on this day (the clocks go forward): it will be 03:30"). Problems are each field's description: an empty field, an end before the start, a rule that refuses the new time. Save on an invalid form moves focus to the first field at fault; Enter saves and Escape closes it without a change.
Recurring events
For an occurrence of a recurring series, every action works the same and the change then waits for its scope in the scope dialog: "This event", "This and following events" or "All events". See Recurrence.
Your own markup
The registry's calendar-event-actions item renders the menu with your DropdownMenu and the
form with your Dialog, Input, Label and Button. The button's "⋯" comes from
calendar-theme (a mask in the text color, drawn only while the button shows), so a week of
hidden buttons adds one element per event. To build them yourself,
useEventActions(segment) from @hapio/calendar-react/interaction gives the anchor, the
trigger, the items and the form model; the
event-actions rule of the accessibility contract lists what
your markup must keep, and runConformance's actions suite checks it. An editable calendar
that offers its own alternative to dragging instead (a form on onEventClick, say) leaves
actions out of the suites and checks itself against WCAG 2.5.7.
Moving and resizing events
Drag, long-press or grab events from the keyboard, judge each place with rules, and save changes with an optimistic overlay that reverts on failure.
Dragging in from outside
Drop or place items from a palette, files from the desktop, or another drag-and-drop library's items onto the calendar.