Skip to content
weftGitHub

EventEditor

A side sheet with every field of an event: title, time, repeats, zone, place.

SourcePackage @weft-js/warp

Preview

tsx
import { state } from '@weft-js/core'import { Button, Card, EventEditor, Row, Stack, Text } from '@weft-js/warp'import type { CalEvent } from '@weft-js/warp-core'export function Change() {  const open = state(false)  const event = state(REVIEW)  return (    <Card title={event.value.title} sweep={false}>      <Stack gap="related">        <Text subtle>{when(event.value)}{event.value.location ? `, ${event.value.location}` : ''}</Text>        <Row><Button emphasis="outline" tone="neutral" icon="settings" onClick={() => (open.value = true)}>Edit event</Button></Row>      </Stack>      <EventEditor open={open} event={event.value} calendars={TEAM_CALENDARS} timeZone={TEAM_ZONE} today={TODAY} locale="en-GB" onSave={e => (event.value = e)} />    </Card>  )}const TODAY = '2026-11-03'const TEAM_ZONE = 'Europe/Stockholm'const TEAM_CALENDARS = ['Platform', 'Web', 'Data', 'Mobile', 'Releases', 'Time off'].map((name, i) => ({ id: i + 1, name }))const TEAM_EVENTS: CalEvent[] = [  { id: 'e1', title: 'Platform standup', start: '2026-11-02T09:00', end: '2026-11-02T09:15', cal: 1, recurrence: 'FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR', location: 'Room Atlas', attendees: ['Mara Lindqvist', 'Jun Tanaka'] },  { id: 'e2', title: 'Platform standup', start: '2026-11-03T09:00', end: '2026-11-03T09:15', cal: 1, recurrence: 'FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR', location: 'Room Atlas', attendees: ['Mara Lindqvist', 'Jun Tanaka'] },  { id: 'e3', title: 'Checkout review', start: '2026-11-03T10:00', end: '2026-11-03T11:00', cal: 2, video: true, attendees: ['Priya Raman', 'Tomás Oliveira', 'Mara Lindqvist'] },  { id: 'e4', title: 'Incident review: mobile-bff', start: '2026-11-03T13:00', end: '2026-11-03T13:45', cal: 4, video: true, location: 'Room Birch', attendees: ['Sam Okafor', 'Inés Vidal', 'Jun Tanaka'] },  { id: 'e5', title: 'Ranking v2 handover', start: '2026-11-03T15:00', end: '2026-11-03T16:00', cal: 3, location: 'Room Cedar', attendees: ['Lena Fischer', 'Noor Haddad'] },  { id: 'e6', title: 'Priya out of office', start: '2026-11-03', end: '2026-11-03', allDay: true, cal: 6 },  { id: 'e7', title: 'Release 2026.11', start: '2026-11-04T11:00', end: '2026-11-04T12:00', cal: 5, attendees: ['Mara Lindqvist', 'Jun Tanaka', 'Tomás Oliveira'] },  { id: 'e8', title: 'Design crit', start: '2026-11-04T14:00', end: '2026-11-04T15:00', cal: 2, tentative: true, video: true },  { id: 'e9', title: 'Cost review', start: '2026-11-05T10:00', end: '2026-11-05T11:00', cal: 1, recurrence: 'FREQ=MONTHLY', attendees: ['Mara Lindqvist', 'Lena Fischer'] },  { id: 'e10', title: 'On-call handover', start: '2026-11-06T16:00', end: '2026-11-06T16:30', cal: 1, recurrence: 'FREQ=WEEKLY', video: true },]const REVIEW = TEAM_EVENTS.find(e => e.id === 'e4')!const when = (e: CalEvent) => `${e.start.slice(0, 10)}, ${e.start.slice(11)} to ${e.end.slice(11)}`

Installation

npm install @weft-js/warp

Usage

tsx
import { EventEditor } from '@weft-js/warp'

A new event

A title is required, and the end must come after the start.

tsx
import { state } from '@weft-js/core'import { Button, EventEditor, Row, Stack, Text } from '@weft-js/warp'import type { CalEvent } from '@weft-js/warp-core'export function New() {  const open = state(false)  const added = state<CalEvent[]>([])  return (    <Stack gap="related">      {added.value.map(e => <Text key={e.id}>{e.title}: {when(e)}</Text>)}      {!added.value.length && <Text subtle>Nothing added yet.</Text>}      <Row><Button icon="plus" onClick={() => (open.value = true)}>New event</Button></Row>      <EventEditor open={open} event={BLANK} isNew calendars={TEAM_CALENDARS} timeZone={TEAM_ZONE} today={TODAY} locale="en-GB" onSave={e => (added.value = [...added.value, { ...e, id: `new-${added.value.length}` }])} />    </Stack>  )}const TODAY = '2026-11-03'const TEAM_ZONE = 'Europe/Stockholm'const TEAM_CALENDARS = ['Platform', 'Web', 'Data', 'Mobile', 'Releases', 'Time off'].map((name, i) => ({ id: i + 1, name }))const when = (e: CalEvent) => `${e.start.slice(0, 10)}, ${e.start.slice(11)} to ${e.end.slice(11)}`const BLANK: CalEvent = { id: 'new', title: '', start: `${TODAY}T14:00`, end: `${TODAY}T14:30`, cal: 1 }

When to use

Use it for

  • Creating or changing an event
  • The way to do anything a drag can do

Accessibility

  • Focus goes to the title on open and to the first problem on a failed save.

API Reference

PropTypeDefaultDescription
openState<boolean>—A state you own.
eventCalEvent | null—The event to edit. Passing a new object rebuilds the fields.
isNewbooleanfalseTitles it New event.
calendarsCalendarSource[]—The calendars on offer.
timeZonestring—The IANA zone the times are edited in, such as Europe/Berlin.
localestring—The language and region used to format values.
todaystring—Pins today's date, for server rendering and tests. The browser's clock otherwise.
EventTypeDefaultDescription
onSave(e: CalEvent) => void—Gets the finished event.
onCancel(() => void)—Cancel was pressed.

RelatedEvent popoverMove sheetDrawer