@innosolutions/inno-calendar 1.0.61 → 1.0.63

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,279 +1,305 @@
1
- # @inno/calendar
2
-
3
- A headless-first, fully customizable React calendar for enterprise applications.
4
-
5
- [![npm version](https://img.shields.io/npm/v/@innosolutions/inno-calendar.svg)](https://www.npmjs.com/package/@innosolutions/inno-calendar)
6
- [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)
7
- [![React](https://img.shields.io/badge/React-18+-61DAFB.svg)](https://reactjs.org/)
8
-
9
- > **🤖 AI Agents:** For comprehensive implementation details, copy the contents of [AGENT.md](./AGENT.md) into your context.
10
-
11
- ## Installation
12
-
13
- ```bash
14
- npm install @innosolutions/inno-calendar
15
- ```
16
-
17
- ```bash
18
- # Optional: Radix UI for enhanced popovers/tooltips
19
- npm install @radix-ui/react-popover @radix-ui/react-tooltip @radix-ui/react-dropdown-menu
20
- ```
21
-
22
- ## Quick Start
23
-
24
- ```tsx
25
- import { InnoCalendar, type CalendarEvent } from "@innosolutions/inno-calendar";
26
- import "@innosolutions/inno-calendar/styles";
27
-
28
- const events: CalendarEvent[] = [
29
- {
30
- id: "1",
31
- title: "Team Meeting",
32
- startDate: new Date("2026-02-04T09:00:00"),
33
- endDate: new Date("2026-02-04T10:00:00"),
34
- color: "blue",
35
- },
36
- ];
37
-
38
- function MyCalendar() {
39
- return (
40
- <InnoCalendar
41
- events={events}
42
- initialView="week"
43
- onEventClick={(event) => console.log(event)}
44
- onSlotSelect={(selection) =>
45
- console.log(selection.startDate, selection.endDate)
46
- }
47
- />
48
- );
49
- }
50
- ```
51
-
52
- ## Features
53
-
54
- - **8 View Modes** — Day, Week, Month, Year, Agenda, Timeline (1/3/7 day)
55
- - **Headless Architecture** — Use hooks directly or pre-built components
56
- - **Drag & Drop** — Drag to select time ranges, drag events to reschedule
57
- - **TypeScript** — Fully typed with generic event data support
58
- - **Customizable** — Custom popovers, settings panels, filters
59
- - **No External Dependencies** — Native Date API, minimal footprint
60
-
61
- ## Views
62
-
63
- | View | Description |
64
- | --------------- | -------------------------- |
65
- | `day` | Single day hourly grid |
66
- | `week` | 7-day grid with time slots |
67
- | `month` | Monthly calendar |
68
- | `year` | 12-month overview |
69
- | `agenda` | Scrollable event list |
70
- | `timeline-day` | Resource timeline (1 day) |
71
- | `timeline-3day` | Resource timeline (3 days) |
72
- | `timeline-week` | Resource timeline (7 days) |
73
-
74
- ## Event Structure
75
-
76
- ```tsx
77
- interface CalendarEvent<TData = Record<string, unknown>> {
78
- // Required
79
- id: string;
80
- title: ReactNode; // String or JSX
81
- startDate: Date;
82
- endDate: Date;
83
-
84
- // Optional display fields
85
- color?:
86
- | "blue"
87
- | "green"
88
- | "red"
89
- | "yellow"
90
- | "purple"
91
- | "orange"
92
- | "pink"
93
- | "teal"
94
- | "gray"
95
- | "indigo";
96
- hexColor?: string; // Hex override (takes precedence over color)
97
- description?: ReactNode; // String or JSX
98
- isCanceled?: boolean;
99
- isAllDay?: boolean;
100
- isMultiDay?: boolean;
101
- isRecurring?: boolean;
102
- resourceId?: string; // For resource/timeline views
103
-
104
- // Built-in filtering (optional skip if you filter server-side)
105
- scheduleTypeId?: number;
106
- scheduleTypeName?: ReactNode; // String or JSX
107
- participants?: ICalendarUser[];
108
-
109
- // @deprecated — still functional, but migrate to `data` bag.
110
- // Will be removed in the next major version.
111
- meetingTookPlace?: boolean; // data.meetingTookPlace
112
- isAccepted?: boolean; // data.isAccepted
113
- companyId?: number; // → data.companyId
114
- cancelReason?: string | null; // → data.cancelReason
115
- user?: ICalendarUser; // → data.user
116
- // ... and others (see types.ts for full list)
117
-
118
- data?: TData; // Your custom domain-specific fields (RECOMMENDED)
119
- }
120
- ```
121
-
122
- ### ReactNode Props
123
-
124
- `title`, `description`, and `scheduleTypeName` accept `ReactNode` — you can pass plain strings (fully backwards compatible) or custom JSX:
125
-
126
- ```tsx
127
- const event: CalendarEvent = {
128
- id: "1",
129
- title: <span><UserIcon className="h-3 w-3 inline" /> John Doe</span>,
130
- description: <p className="italic">VIP client meeting</p>,
131
- startDate: new Date(),
132
- endDate: new Date(),
133
- };
134
- ```
135
-
136
- The built-in search and `aria-label` automatically extract plain text from JSX nodes via the `reactNodeToText` utility.
137
-
138
- ### Extended Props (data.extendedProps)
139
-
140
- For rendering an arbitrary number of custom lines on EventCard/EventBlock, use the `extendedProps` array in the `data` bag:
141
-
142
- ```tsx
143
- const event: CalendarEvent = {
144
- id: "1",
145
- title: "Practical Course",
146
- startDate: new Date(),
147
- endDate: new Date(),
148
- data: {
149
- extendedProps: [
150
- <p key="student" className="font-medium">Student: Jane Doe</p>,
151
- <p key="phone">Phone: +32 123 456 789</p>,
152
- <div key="info" className="flex items-center gap-1">
153
- <CarIcon className="h-3 w-3" />
154
- <span>Automatic transmission</span>
155
- </div>,
156
- ],
157
- },
158
- };
159
- ```
160
-
161
- Each entry renders as its own row. Visible on EventCard (full variant) and EventBlock (events >= 45min). Also shown in tooltips.
162
-
163
- ## Props
164
-
165
- ```tsx
166
- <InnoCalendar
167
- // Data
168
- events={events}
169
- users={users}
170
- scheduleTypes={scheduleTypes}
171
- // Initial State
172
- initialView="week"
173
- initialDate={new Date()}
174
- // Callbacks
175
- onEventClick={(event) => {}}
176
- onSlotSelect={(selection) => {}}
177
- onSlotClick={(date, hour) => {}}
178
- onAddEvent={() => {}}
179
- onEventDrop={(result) => {}}
180
- onDateChange={(date, view) => {}}
181
- onViewChange={(view) => {}}
182
- // Customization
183
- renderPopover={({ event, onClose }) => <CustomPopover />}
184
- settingsContent={<SettingsPanel />}
185
- filterContent={<FiltersRow />}
186
- showHeader={true}
187
- />
188
- ```
189
-
190
- ## Provider Pattern
191
-
192
- For shared state across components:
193
-
194
- ```tsx
195
- import {
196
- InnoCalendarProvider,
197
- useInnoCalendar,
198
- CalendarHeader,
199
- WeekView,
200
- } from "@innosolutions/inno-calendar";
201
-
202
- function App() {
203
- return (
204
- <InnoCalendarProvider initialEvents={events} initialView="week">
205
- <CalendarContent />
206
- </InnoCalendarProvider>
207
- );
208
- }
209
-
210
- function CalendarContent() {
211
- const { view, filteredEvents, selectedDate } = useInnoCalendar();
212
- return (
213
- <>
214
- <CalendarHeader />
215
- <WeekView events={filteredEvents} date={selectedDate} />
216
- </>
217
- );
218
- }
219
- ```
220
-
221
- ## Working Hours
222
-
223
- ```tsx
224
- const workingHours = {
225
- 0: { enabled: false, from: 8, to: 17 }, // Sunday
226
- 1: { enabled: true, from: 9, to: 18 }, // Monday
227
- 2: { enabled: true, from: 9, to: 18 }, // Tuesday
228
- 3: { enabled: true, from: 9, to: 18 }, // Wednesday
229
- 4: { enabled: true, from: 9, to: 18 }, // Thursday
230
- 5: { enabled: true, from: 9, to: 17 }, // Friday
231
- 6: { enabled: false, from: 8, to: 12 }, // Saturday
232
- };
233
-
234
- <InnoCalendar
235
- events={events}
236
- preferencesConfig={{
237
- defaults: { workingHours },
238
- }}
239
- />;
240
- ```
241
-
242
- ## Styles
243
-
244
- Import the bundled styles for proper rendering:
245
-
246
- ```tsx
247
- import "@innosolutions/inno-calendar/styles";
248
- ```
249
-
250
- Or include CSS custom properties for theming:
251
-
252
- ```css
253
- :root {
254
- --inno-border-color: #e5e7eb;
255
- --inno-primary: #3b82f6;
256
- --inno-hour-height: 96px;
257
- }
258
- ```
259
-
260
- ## TypeScript
261
-
262
- ```tsx
263
- import type {
264
- CalendarEvent,
265
- TCalendarView,
266
- TEventColor,
267
- ICalendarUser,
268
- IScheduleType,
269
- ISelectionResult,
270
- TWorkingHours,
271
- } from "@innosolutions/inno-calendar";
272
-
273
- // Utility: extract plain text from any ReactNode (for search, labels, etc.)
274
- import { reactNodeToText } from "@innosolutions/inno-calendar";
275
- ```
276
-
277
- ## License
278
-
279
- MIT © [InnoSolutions](https://github.com/innosolutions)
1
+ # @inno/calendar
2
+
3
+ A headless-first, fully customizable React calendar for enterprise applications.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@innosolutions/inno-calendar.svg)](https://www.npmjs.com/package/@innosolutions/inno-calendar)
6
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)
7
+ [![React](https://img.shields.io/badge/React-18+-61DAFB.svg)](https://reactjs.org/)
8
+
9
+ > **🤖 AI Agents:** For comprehensive implementation details, copy the contents of [AGENT.md](./AGENT.md) into your context.
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npm install @innosolutions/inno-calendar
15
+ ```
16
+
17
+ ```bash
18
+ # Optional: Radix UI for enhanced popovers/tooltips
19
+ npm install @radix-ui/react-popover @radix-ui/react-tooltip @radix-ui/react-dropdown-menu
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ```tsx
25
+ import { InnoCalendar, type CalendarEvent } from "@innosolutions/inno-calendar";
26
+ import "@innosolutions/inno-calendar/styles";
27
+
28
+ const events: CalendarEvent[] = [
29
+ {
30
+ id: "1",
31
+ title: "Team Meeting",
32
+ startDate: new Date("2026-02-04T09:00:00"),
33
+ endDate: new Date("2026-02-04T10:00:00"),
34
+ color: "blue",
35
+ },
36
+ ];
37
+
38
+ function MyCalendar() {
39
+ return (
40
+ <InnoCalendar
41
+ events={events}
42
+ initialView="week"
43
+ onEventClick={(event) => console.log(event)}
44
+ onSlotSelect={(selection) =>
45
+ console.log(selection.startDate, selection.endDate)
46
+ }
47
+ />
48
+ );
49
+ }
50
+ ```
51
+
52
+ ## Features
53
+
54
+ - **8 View Modes** — Day, Week, Month, Year, Agenda, Timeline (1/3/7 day)
55
+ - **Headless Architecture** — Use hooks directly or pre-built components
56
+ - **Drag & Drop** — Drag to select time ranges, drag events to reschedule
57
+ - **TypeScript** — Fully typed with generic event data support
58
+ - **Customizable** — Custom popovers, settings panels, filters
59
+ - **No External Dependencies** — Native Date API, minimal footprint
60
+
61
+ ## Views
62
+
63
+ | View | Description |
64
+ | --------------- | -------------------------- |
65
+ | `day` | Single day hourly grid |
66
+ | `week` | 7-day grid with time slots |
67
+ | `month` | Monthly calendar |
68
+ | `year` | 12-month overview |
69
+ | `agenda` | Scrollable event list |
70
+ | `timeline-day` | Resource timeline (1 day) |
71
+ | `timeline-3day` | Resource timeline (3 days) |
72
+ | `timeline-week` | Resource timeline (7 days) |
73
+ | `resource-day` | Resource view (1 day) |
74
+ | `resource-week` | Resource view (7 days) |
75
+
76
+ ## Event Structure
77
+
78
+ ```tsx
79
+ interface CalendarEvent<TData = Record<string, unknown>> {
80
+ // Required
81
+ id: string;
82
+ title: ReactNode; // String or JSX
83
+ startDate: Date;
84
+ endDate: Date;
85
+
86
+ // Optional display fields
87
+ color?:
88
+ | "blue"
89
+ | "green"
90
+ | "red"
91
+ | "yellow"
92
+ | "purple"
93
+ | "orange"
94
+ | "pink"
95
+ | "teal"
96
+ | "gray"
97
+ | "indigo";
98
+ hexColor?: string; // Hex override (takes precedence over color)
99
+ description?: ReactNode; // String or JSX
100
+ isCanceled?: boolean;
101
+ isAllDay?: boolean;
102
+ isMultiDay?: boolean;
103
+ isRecurring?: boolean;
104
+ resourceId?: string; // For resource/timeline views
105
+
106
+ // Built-in filtering (optional skip if you filter server-side)
107
+ scheduleTypeId?: number;
108
+ scheduleTypeName?: ReactNode; // String or JSX
109
+ participants?: ICalendarUser[];
110
+
111
+ // @deprecated still functional, but migrate to `data` bag.
112
+ // Will be removed in the next major version.
113
+ meetingTookPlace?: boolean; // → data.meetingTookPlace
114
+ isAccepted?: boolean; // → data.isAccepted
115
+ companyId?: number; // → data.companyId
116
+ cancelReason?: string | null; // → data.cancelReason
117
+ user?: ICalendarUser; // → data.user
118
+ // ... and others (see types.ts for full list)
119
+
120
+ data?: TData; // Your custom domain-specific fields (RECOMMENDED)
121
+ }
122
+ ```
123
+
124
+ ### ReactNode Props
125
+
126
+ `title`, `description`, and `scheduleTypeName` accept `ReactNode` — you can pass plain strings (fully backwards compatible) or custom JSX:
127
+
128
+ ```tsx
129
+ const event: CalendarEvent = {
130
+ id: "1",
131
+ title: <span><UserIcon className="h-3 w-3 inline" /> John Doe</span>,
132
+ description: <p className="italic">VIP client meeting</p>,
133
+ startDate: new Date(),
134
+ endDate: new Date(),
135
+ };
136
+ ```
137
+
138
+ The built-in search and `aria-label` automatically extract plain text from JSX nodes via the `reactNodeToText` utility.
139
+
140
+ ### Extended Props (data.extendedProps)
141
+
142
+ For rendering an arbitrary number of custom lines on EventCard/EventBlock, use the `extendedProps` array in the `data` bag:
143
+
144
+ ```tsx
145
+ const event: CalendarEvent = {
146
+ id: "1",
147
+ title: "Practical Course",
148
+ startDate: new Date(),
149
+ endDate: new Date(),
150
+ data: {
151
+ extendedProps: [
152
+ <p key="student" className="font-medium">Student: Jane Doe</p>,
153
+ <p key="phone">Phone: +32 123 456 789</p>,
154
+ <div key="info" className="flex items-center gap-1">
155
+ <CarIcon className="h-3 w-3" />
156
+ <span>Automatic transmission</span>
157
+ </div>,
158
+ ],
159
+ },
160
+ };
161
+ ```
162
+
163
+ Each entry renders as its own row. Visible on EventCard (full variant) and EventBlock (events >= 45min). Also shown in tooltips.
164
+
165
+ ## Props
166
+
167
+ ```tsx
168
+ <InnoCalendar
169
+ // Data
170
+ events={events}
171
+ users={users}
172
+ alwaysShowResources={alwaysShowResources}
173
+ scheduleTypes={scheduleTypes}
174
+ // Initial State
175
+ initialView="week"
176
+ initialDate={new Date()}
177
+ // Callbacks
178
+ onEventClick={(event) => {}}
179
+ onSlotSelect={(selection) => {}}
180
+ onSlotClick={(date, hour) => {}}
181
+ onAddEvent={() => {}}
182
+ onEventDrop={(result) => {}}
183
+ onDateChange={(date, view) => {}}
184
+ onViewChange={(view) => {}}
185
+ // Customization
186
+ renderPopover={({ event, onClose }) => <CustomPopover />}
187
+ settingsContent={<SettingsPanel />}
188
+ filterContent={<FiltersRow />}
189
+ showHeader={true}
190
+ />
191
+ ```
192
+
193
+ ## Provider Pattern
194
+
195
+ For shared state across components:
196
+
197
+ ```tsx
198
+ import {
199
+ InnoCalendarProvider,
200
+ useInnoCalendar,
201
+ CalendarHeader,
202
+ WeekView,
203
+ } from "@innosolutions/inno-calendar";
204
+
205
+ function App() {
206
+ return (
207
+ <InnoCalendarProvider initialEvents={events} initialView="week">
208
+ <CalendarContent />
209
+ </InnoCalendarProvider>
210
+ );
211
+ }
212
+
213
+ function CalendarContent() {
214
+ const { view, filteredEvents, selectedDate } = useInnoCalendar();
215
+ return (
216
+ <>
217
+ <CalendarHeader />
218
+ <WeekView events={filteredEvents} date={selectedDate} />
219
+ </>
220
+ );
221
+ }
222
+ ```
223
+
224
+ ## Resource Views
225
+
226
+ The resource views (`resource-day`, `resource-week`) group events by resource and render one row per resource.
227
+
228
+ **Default flow** resource rows are derived from the events: each unique resource referenced by an event (via `event.resourceId`, `event.data.user`, `event.user`, or `event.participants`) gets its own row. Resources without events are not shown.
229
+
230
+ **Always-show resources** pass `alwaysShowResources` to guarantee a row for a resource even when it has no events in the current data. Rows without events appear at the bottom so event-containing rows keep visual priority. If a resource listed here also has events, its row is positioned naturally among the event rows and the events are still attached to it.
231
+
232
+ ```tsx
233
+ <InnoCalendar
234
+ events={events}
235
+ initialView="resource-week"
236
+ alwaysShowResources={[
237
+ { id: "room-1", name: "Meeting Room A", avatar: "/rooms/a.jpg" },
238
+ { id: "room-2", name: "Meeting Room B" },
239
+ ]}
240
+ />
241
+ ```
242
+
243
+ Each entry accepts the full `ICalendarUser` shape — at minimum `id` and `name` are needed to render the row header; `avatar` and `email` are optional. Updating the array re-renders the resource view immediately.
244
+
245
+ This prop is scoped to the resource views only — it is ignored in `timeline-*` and all other views.
246
+
247
+ ## Working Hours
248
+
249
+ ```tsx
250
+ const workingHours = {
251
+ 0: { enabled: false, from: 8, to: 17 }, // Sunday
252
+ 1: { enabled: true, from: 9, to: 18 }, // Monday
253
+ 2: { enabled: true, from: 9, to: 18 }, // Tuesday
254
+ 3: { enabled: true, from: 9, to: 18 }, // Wednesday
255
+ 4: { enabled: true, from: 9, to: 18 }, // Thursday
256
+ 5: { enabled: true, from: 9, to: 17 }, // Friday
257
+ 6: { enabled: false, from: 8, to: 12 }, // Saturday
258
+ };
259
+
260
+ <InnoCalendar
261
+ events={events}
262
+ preferencesConfig={{
263
+ defaults: { workingHours },
264
+ }}
265
+ />;
266
+ ```
267
+
268
+ ## Styles
269
+
270
+ Import the bundled styles for proper rendering:
271
+
272
+ ```tsx
273
+ import "@innosolutions/inno-calendar/styles";
274
+ ```
275
+
276
+ Or include CSS custom properties for theming:
277
+
278
+ ```css
279
+ :root {
280
+ --inno-border-color: #e5e7eb;
281
+ --inno-primary: #3b82f6;
282
+ --inno-hour-height: 96px;
283
+ }
284
+ ```
285
+
286
+ ## TypeScript
287
+
288
+ ```tsx
289
+ import type {
290
+ CalendarEvent,
291
+ TCalendarView,
292
+ TEventColor,
293
+ ICalendarUser,
294
+ IScheduleType,
295
+ ISelectionResult,
296
+ TWorkingHours,
297
+ } from "@innosolutions/inno-calendar";
298
+
299
+ // Utility: extract plain text from any ReactNode (for search, labels, etc.)
300
+ import { reactNodeToText } from "@innosolutions/inno-calendar";
301
+ ```
302
+
303
+ ## License
304
+
305
+ MIT © [InnoSolutions](https://github.com/innosolutions)