@ziix/calendar 0.1.0 → 0.1.2
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 +39 -36
- package/dist/index.d.ts +12 -4
- package/dist/ziix-calendar.css +1 -1
- package/dist/ziix-calendar.js +157 -120
- package/dist/ziix-calendar.js.map +1 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
# @ziix/calendar
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
replacement for the FullCalendar views DMS uses (`timeGridDay`, `resourceTimeGridDay`,
|
|
5
|
-
`resourceTimeline`). No premium scheduler licence, no React/Vue/Preact dependency: a
|
|
6
|
-
plain imperative class you drive through a ref from any framework.
|
|
3
|
+
By [ziix.eu](https://ziix.eu) · [npm](https://www.npmjs.com/package/@ziix/calendar)
|
|
7
4
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
A framework-agnostic resource & time-grid calendar with three views — **day**,
|
|
6
|
+
**resource-day** (resources as columns) and **timeline** (resources as rows) — plus
|
|
7
|
+
drag/resize/select, resource grouping and timezone-correct rendering. No framework
|
|
8
|
+
dependency: a plain imperative class you drive through a ref from React, Preact, Vue,
|
|
9
|
+
Svelte or vanilla JS.
|
|
10
|
+
|
|
11
|
+
> **Status.** All three views and the full interaction engine (drag/resize/select) are
|
|
12
|
+
> implemented and tested, and the package is published on npm.
|
|
11
13
|
|
|
12
14
|
## Install
|
|
13
15
|
|
|
@@ -25,7 +27,7 @@ import '@ziix/calendar/styles.css'
|
|
|
25
27
|
|
|
26
28
|
const cal = new Calendar(document.getElementById('calendar'), {
|
|
27
29
|
view: 'day',
|
|
28
|
-
timezone: 'Europe/Copenhagen', // events are placed in
|
|
30
|
+
timezone: 'Europe/Copenhagen', // events are placed in this timezone, not the browser's
|
|
29
31
|
date: '2026-06-09',
|
|
30
32
|
height: 780,
|
|
31
33
|
slot: { duration: 15, min: '06:00', max: '19:00', labelInterval: 60 },
|
|
@@ -58,9 +60,8 @@ events: async ({ start, end }) => {
|
|
|
58
60
|
|
|
59
61
|
## Timeline view (resources as rows)
|
|
60
62
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
set `view: 'timeline'`.
|
|
63
|
+
A sticky resource area on the left and a horizontally-scrolling time grid on the right.
|
|
64
|
+
Pass `resources` (array or function) and set `view: 'timeline'`.
|
|
64
65
|
|
|
65
66
|
```js
|
|
66
67
|
const cal = new Calendar(el, {
|
|
@@ -92,7 +93,7 @@ const cal = new Calendar(el, {
|
|
|
92
93
|
{ id: 1, title: 'Service', start: '...', end: '...', resourceId: 'E1' },
|
|
93
94
|
],
|
|
94
95
|
|
|
95
|
-
// custom label for the default resource column
|
|
96
|
+
// custom label for the default resource column
|
|
96
97
|
renderResource: (resource) => `<strong>${resource.title}</strong>`,
|
|
97
98
|
|
|
98
99
|
onEventClick: ({ event }) => openOrder(event.extendedProps.orderId),
|
|
@@ -105,8 +106,8 @@ grows to fit them. An event without a matching `resourceId` is not shown in the
|
|
|
105
106
|
|
|
106
107
|
### Resource-day view (resources as columns)
|
|
107
108
|
|
|
108
|
-
`view: 'resource-day'`
|
|
109
|
-
|
|
109
|
+
`view: 'resource-day'` uses the same vertical time axis as the day view, but with one
|
|
110
|
+
column per resource under a sticky, grouped header. Takes the
|
|
110
111
|
same `resources` / `resourceGroupField` / `renderResource` options as the timeline. With
|
|
111
112
|
`editable`, dragging an event sideways moves it to another resource column.
|
|
112
113
|
|
|
@@ -181,7 +182,7 @@ const cal = new Calendar(el, {
|
|
|
181
182
|
|
|
182
183
|
// false ⇒ a drop/resize that would overlap another event on the same resource is
|
|
183
184
|
// rejected and snaps back. May be a function evaluated per drop.
|
|
184
|
-
eventOverlap: () =>
|
|
185
|
+
eventOverlap: () => settings.allowOverlap,
|
|
185
186
|
|
|
186
187
|
// gate which ranges may be selected (e.g. only employee or rental-car rows)
|
|
187
188
|
selectAllow: ({ resource }) => !!resource && /^[EC]/.test(resource.id),
|
|
@@ -233,8 +234,7 @@ working menu implementation.
|
|
|
233
234
|
|
|
234
235
|
## Imperative API
|
|
235
236
|
|
|
236
|
-
The calendar is a plain class you drive through a ref
|
|
237
|
-
consumers rely on:
|
|
237
|
+
The calendar is a plain class you drive through a ref:
|
|
238
238
|
|
|
239
239
|
| Method | Purpose |
|
|
240
240
|
| --- | --- |
|
|
@@ -252,13 +252,12 @@ consumers rely on:
|
|
|
252
252
|
|
|
253
253
|
### Real-time updates
|
|
254
254
|
|
|
255
|
-
Drive incremental updates from a websocket without a full refetch
|
|
255
|
+
Drive incremental updates from a websocket without a full refetch:
|
|
256
256
|
|
|
257
257
|
```js
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
.listen('.eventRemoved', (e) => cal.getEventById(e.id)?.remove())
|
|
258
|
+
socket.on('event:created', (e) => cal.addEvent(e.event))
|
|
259
|
+
socket.on('event:updated', () => cal.refetchEvents())
|
|
260
|
+
socket.on('event:removed', (e) => cal.getEventById(e.id)?.remove())
|
|
262
261
|
```
|
|
263
262
|
|
|
264
263
|
### Using it from Preact / React
|
|
@@ -278,7 +277,7 @@ export function CalendarView({ shopId }) {
|
|
|
278
277
|
useEffect(() => {
|
|
279
278
|
const cal = new Calendar(elRef.current, {
|
|
280
279
|
view: 'timeline',
|
|
281
|
-
timezone:
|
|
280
|
+
timezone: 'Europe/Copenhagen',
|
|
282
281
|
events: ({ start, end }) => fetchEvents(shopId, start, end),
|
|
283
282
|
resources: () => fetchResources(shopId),
|
|
284
283
|
onEventClick: ({ event }) => openOrder(event),
|
|
@@ -297,8 +296,7 @@ export function CalendarView({ shopId }) {
|
|
|
297
296
|
The calendar renders almost no text of its own — column headers, resource labels and
|
|
298
297
|
event content all come from **your** render hooks, so they're already in your language.
|
|
299
298
|
The only built-in strings are the toolbar buttons, and the date in the title. Both are
|
|
300
|
-
driven by the `locale` option — pass your app's translations there
|
|
301
|
-
`trans()` values straight in):
|
|
299
|
+
driven by the `locale` option — pass your app's translations there:
|
|
302
300
|
|
|
303
301
|
```js
|
|
304
302
|
const cal = new Calendar(el, {
|
|
@@ -306,8 +304,8 @@ const cal = new Calendar(el, {
|
|
|
306
304
|
code: 'da',
|
|
307
305
|
intl: 'da-DK', // BCP-47 tag used by Intl to format the title date
|
|
308
306
|
firstDay: 1,
|
|
309
|
-
buttons: { today:
|
|
310
|
-
ariaLabels: { today:
|
|
307
|
+
buttons: { today: t('today'), prev: '‹', next: '›' },
|
|
308
|
+
ariaLabels: { today: t('today'), prev: t('prev'), next: t('next') },
|
|
311
309
|
},
|
|
312
310
|
})
|
|
313
311
|
```
|
|
@@ -343,17 +341,22 @@ npm run typecheck # tsc --noEmit
|
|
|
343
341
|
npm run build # dist/ziix-calendar.js + .css + index.d.ts
|
|
344
342
|
```
|
|
345
343
|
|
|
346
|
-
##
|
|
344
|
+
## Features
|
|
347
345
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
346
|
+
- **Three views** — `day`, `resource-day` (resources as columns) and `timeline`
|
|
347
|
+
(resources as rows, horizontal axis) with grouped resources and custom resource columns
|
|
348
|
+
- **Interaction** — drag-move (incl. across resources), resize, drag-select, with
|
|
349
|
+
overlap and selection gating
|
|
350
|
+
- **Timezone-correct** rendering via dayjs; events placed in the configured timezone
|
|
351
|
+
- **Imperative API** — drive it from any framework through a ref
|
|
352
|
+
- **Real-time friendly** — add/update/remove events incrementally from a websocket
|
|
353
|
+
- **Themeable** via `--zc-*` custom properties; **translatable** via the `locale` option
|
|
354
|
+
- **Typed** — ships TypeScript declarations; one peer dependency (`dayjs`)
|
|
356
355
|
|
|
357
356
|
## License
|
|
358
357
|
|
|
359
358
|
MIT
|
|
359
|
+
|
|
360
|
+
---
|
|
361
|
+
|
|
362
|
+
Built by [ziix](https://ziix.eu).
|
package/dist/index.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export declare class Calendar {
|
|
|
29
29
|
get activeStart(): Dayjs;
|
|
30
30
|
/** End of the currently-shown view range. */
|
|
31
31
|
get activeEnd(): Dayjs;
|
|
32
|
-
/** The active view's type and date window
|
|
32
|
+
/** The active view's type and date window. */
|
|
33
33
|
getView(): {
|
|
34
34
|
type: ViewType;
|
|
35
35
|
activeStart: Dayjs;
|
|
@@ -57,6 +57,14 @@ export declare class Calendar {
|
|
|
57
57
|
getEvents(): CalEvent[];
|
|
58
58
|
getResources(): CalResource[];
|
|
59
59
|
getResourceById(id: string | number): ResourceHandle | null;
|
|
60
|
+
private resourceRenderScheduled;
|
|
61
|
+
/**
|
|
62
|
+
* Coalesce resource-area re-renders: many setExtendedProp calls in a row (e.g.
|
|
63
|
+
* pushing work hours / punch-ins for every resource) collapse into a single
|
|
64
|
+
* cell update on the next microtask, and event bars are never rebuilt — so the
|
|
65
|
+
* timeline doesn't flicker.
|
|
66
|
+
*/
|
|
67
|
+
private scheduleResourceRender;
|
|
60
68
|
private resourceHandle;
|
|
61
69
|
private handle;
|
|
62
70
|
/** Build the inner body of an event, honouring the `renderEvent` hook. */
|
|
@@ -204,7 +212,7 @@ export declare interface EventContextMenuInfo {
|
|
|
204
212
|
jsEvent: MouseEvent;
|
|
205
213
|
}
|
|
206
214
|
|
|
207
|
-
/** Handle returned by addEvent/getEventById
|
|
215
|
+
/** Handle returned by addEvent/getEventById for imperative updates from outside. */
|
|
208
216
|
export declare interface EventHandle {
|
|
209
217
|
id: string;
|
|
210
218
|
event: CalEvent;
|
|
@@ -308,7 +316,7 @@ export declare interface PackedEvent {
|
|
|
308
316
|
/**
|
|
309
317
|
* Pack events that share a single column (resource/day) into side-by-side
|
|
310
318
|
* sub-columns so overlapping events never cover each other — the classic
|
|
311
|
-
* interval-graph greedy colouring
|
|
319
|
+
* interval-graph greedy colouring.
|
|
312
320
|
*
|
|
313
321
|
* Events are expected to already belong to the same column; callers filter by
|
|
314
322
|
* resource first.
|
|
@@ -367,7 +375,7 @@ export declare class ResourceStore {
|
|
|
367
375
|
* Resources in display order. With `resourceOrder: 'id'` they are sorted by a
|
|
368
376
|
* natural id comparison (so 'E2' precedes 'E10'); with a numeric order field
|
|
369
377
|
* they sort by it; otherwise the original input order is preserved (sort is
|
|
370
|
-
* stable)
|
|
378
|
+
* stable).
|
|
371
379
|
*/
|
|
372
380
|
ordered(): CalResource[];
|
|
373
381
|
/**
|
package/dist/ziix-calendar.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.zc{--zc-border: #e2e2e2;--zc-bg: #ffffff;--zc-muted-bg: #f6f6f6;--zc-fg: #1a1a1a;--zc-muted-fg: #6b7280;--zc-today-bg: #f0f6ff;--zc-now: #ef4444;--zc-btn-bg: transparent;--zc-btn-fg: var(--zc-fg);--zc-btn-border: var(--zc-border);--zc-btn-hover-bg: var(--zc-muted-bg);--zc-btn-active-bg: #e7efff;--zc-event-bg: #c7dbff;--zc-event-border: #a9c6ff;--zc-event-fg: #1e3a5f;--zc-nonbusiness: oklch(0 0 0 / .045);--zc-radius: 6px;--zc-font: inherit;display:flex;flex-direction:column;box-sizing:border-box;font-family:var(--zc-font);color:var(--zc-fg);background:var(--zc-bg)}.zc *,.zc *:before,.zc *:after{box-sizing:border-box}.zc-toolbar{display:flex;align-items:center;justify-content:space-between;gap:.5rem;padding:.5rem .75rem;flex:0 0 auto}.zc-toolbar-section{display:flex;align-items:center;gap:.375rem}.zc-btn-group{display:inline-flex}.zc-btn-group .zc-btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0;margin-left:-1px}.zc-btn-group .zc-btn:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.zc-toolbar-center{flex:1 1 auto;justify-content:center}.zc-title{margin:0;font-size:1rem;font-weight:600;text-transform:capitalize}.zc-btn{-webkit-appearance:none;-moz-appearance:none;appearance:none;cursor:pointer;font:inherit;font-size:.875rem;line-height:1.2;padding:.375rem .75rem;border-radius:var(--zc-radius);border:1px solid var(--zc-btn-border);background:var(--zc-btn-bg);color:var(--zc-btn-fg)}.zc-btn:hover{background:var(--zc-btn-hover-bg)}.zc-btn:active{background:var(--zc-btn-active-bg)}.zc-body{flex:1 1 auto;overflow:auto;position:relative;border-top:1px solid var(--zc-border)}.zc-timegrid{display:flex;align-items:stretch;position:relative;min-height:100%}.zc-axis{position:relative;flex:0 0 56px;border-right:1px solid var(--zc-border)}.zc-axis-label{position:absolute;right:6px;transform:translateY(-50%);font-size:.6875rem;color:var(--zc-muted-fg);white-space:nowrap}.zc-col{position:relative;flex:1 1 auto}.zc-slot-line{position:absolute;left:0;right:0;border-top:1px solid var(--zc-border);opacity:.4}.zc-slot-line.zc-slot-major{opacity:1}.zc-event{position:absolute;overflow:hidden;border-radius:var(--zc-radius);border:1px solid var(--zc-event-border);background:var(--zc-event-bg);color:var(--zc-event-fg);font-size:.75rem;cursor:pointer}.zc-event-main{padding:2px 4px;height:100%}.zc-event-default{display:flex;flex-direction:column;gap:1px;line-height:1.2}.zc-event-time{font-variant-numeric:tabular-nums;opacity:.85}.zc-event-title{font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.zc-timeline{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;align-items:stretch}.zc-tl-resource-area{display:flex;flex-direction:column;overflow:hidden;border-right:1px solid var(--zc-border);background:var(--zc-bg)}.zc-tl-resource-head,.zc-tl-resource-row{display:flex;align-items:stretch;flex:0 0 auto}.zc-tl-resource-head{border-bottom:1px solid var(--zc-border);background:var(--zc-muted-bg);font-weight:600;font-size:.75rem}.zc-tl-col-head{display:flex;align-items:center;padding:0 .5rem;overflow:hidden;white-space:nowrap}.zc-tl-resource-body{flex:1 1 auto;overflow:hidden}.zc-tl-resource-row{border-bottom:1px solid var(--zc-border)}.zc-tl-col-cell{display:flex;flex-direction:column;justify-content:center;padding:.
|
|
1
|
+
.zc{--zc-border: #e2e2e2;--zc-bg: #ffffff;--zc-muted-bg: #f6f6f6;--zc-fg: #1a1a1a;--zc-muted-fg: #6b7280;--zc-today-bg: #f0f6ff;--zc-now: #ef4444;--zc-btn-bg: transparent;--zc-btn-fg: var(--zc-fg);--zc-btn-border: var(--zc-border);--zc-btn-hover-bg: var(--zc-muted-bg);--zc-btn-active-bg: #e7efff;--zc-event-bg: #c7dbff;--zc-event-border: #a9c6ff;--zc-event-fg: #1e3a5f;--zc-nonbusiness: oklch(0 0 0 / .045);--zc-radius: 6px;--zc-font: inherit;display:flex;flex-direction:column;box-sizing:border-box;font-family:var(--zc-font);color:var(--zc-fg);background:var(--zc-bg)}.zc *,.zc *:before,.zc *:after{box-sizing:border-box}.zc-toolbar{display:flex;align-items:center;justify-content:space-between;gap:.5rem;padding:.5rem .75rem;flex:0 0 auto}.zc-toolbar-section{display:flex;align-items:center;gap:.375rem}.zc-btn-group{display:inline-flex}.zc-btn-group .zc-btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0;margin-left:-1px}.zc-btn-group .zc-btn:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.zc-toolbar-center{flex:1 1 auto;justify-content:center}.zc-title{margin:0;font-size:1rem;font-weight:600;text-transform:capitalize}.zc-btn{-webkit-appearance:none;-moz-appearance:none;appearance:none;cursor:pointer;font:inherit;font-size:.875rem;line-height:1.2;padding:.375rem .75rem;border-radius:var(--zc-radius);border:1px solid var(--zc-btn-border);background:var(--zc-btn-bg);color:var(--zc-btn-fg)}.zc-btn:hover{background:var(--zc-btn-hover-bg)}.zc-btn:active{background:var(--zc-btn-active-bg)}.zc-body{flex:1 1 auto;overflow:auto;position:relative;border-top:1px solid var(--zc-border)}.zc-timegrid{display:flex;align-items:stretch;position:relative;min-height:100%}.zc-axis{position:relative;flex:0 0 56px;border-right:1px solid var(--zc-border)}.zc-axis-label{position:absolute;right:6px;transform:translateY(-50%);font-size:.6875rem;color:var(--zc-muted-fg);white-space:nowrap}.zc-col{position:relative;flex:1 1 auto}.zc-slot-line{position:absolute;left:0;right:0;border-top:1px solid var(--zc-border);opacity:.4}.zc-slot-line.zc-slot-major{opacity:1}.zc-event{position:absolute;overflow:hidden;border-radius:var(--zc-radius);border:1px solid var(--zc-event-border);background:var(--zc-event-bg);color:var(--zc-event-fg);font-size:.75rem;cursor:pointer}.zc-event-main{padding:2px 4px;height:100%}.zc-event-default{display:flex;flex-direction:column;gap:1px;line-height:1.2}.zc-event-time{font-variant-numeric:tabular-nums;opacity:.85}.zc-event-title{font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.zc-timeline{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;align-items:stretch}.zc-tl-resource-area{display:flex;flex-direction:column;overflow:hidden;border-right:1px solid var(--zc-border);background:var(--zc-bg)}.zc-tl-resource-head,.zc-tl-resource-row{display:flex;align-items:stretch;flex:0 0 auto}.zc-tl-resource-head{border-bottom:1px solid var(--zc-border);background:var(--zc-muted-bg);font-weight:600;font-size:.75rem}.zc-tl-col-head{display:flex;align-items:center;padding:0 .5rem;overflow:hidden;white-space:nowrap}.zc-tl-resource-body{flex:1 1 auto;overflow:hidden}.zc-tl-resource-row{border-bottom:1px solid var(--zc-border)}.zc-tl-col-cell{display:flex;flex-direction:column;justify-content:center;gap:2px;padding:.5rem;overflow:hidden;font-size:.8125rem;min-width:0}.zc-tl-group-row{display:flex;align-items:center;padding:0 .5rem;background:var(--zc-muted-bg);color:var(--zc-muted-fg);font-size:.6875rem;font-weight:700;text-transform:uppercase;letter-spacing:.03em}.zc-tl-time-area{display:flex;flex-direction:column;flex:1 1 auto;min-width:0;overflow:hidden}.zc-tl-time-head{flex:0 0 auto;overflow:hidden;position:relative;border-bottom:1px solid var(--zc-border);background:var(--zc-muted-bg)}.zc-tl-axis{position:relative;height:100%}.zc-tl-axis-label{position:absolute;top:0;bottom:0;display:flex;align-items:center;padding-left:4px;font-size:.6875rem;color:var(--zc-muted-fg);white-space:nowrap;border-left:1px solid var(--zc-border)}.zc-tl-time-body{flex:1 1 auto;overflow:auto;position:relative}.zc-tl-time-canvas{position:relative;min-height:100%}.zc-tl-overlay{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none;z-index:0}.zc-tl-vline{position:absolute;top:0;bottom:0;border-left:1px solid var(--zc-border);opacity:.6}.zc-tl-now{position:absolute;top:0;bottom:0;border-left:2px solid var(--zc-now);z-index:3}.zc-tl-rows{position:relative;z-index:1}.zc-tl-row{position:relative;border-bottom:1px solid var(--zc-border)}.zc-tl-group-spacer{background:var(--zc-muted-bg);opacity:.5}.zc-tl-event{display:flex;align-items:stretch}.zc-tl-event .zc-event-main{padding:3px 6px}.zc-rg{position:absolute;top:0;right:0;bottom:0;left:0;overflow-y:auto;overflow-x:hidden}.zc-rg-head{position:sticky;top:0;z-index:5;display:flex;align-items:stretch;background:var(--zc-muted-bg);border-bottom:1px solid var(--zc-border)}.zc-rg-corner{flex:0 0 56px;width:56px;border-right:1px solid var(--zc-border)}.zc-rg-head-cols{flex:1 1 auto;display:flex;flex-direction:column;min-width:0}.zc-rg-group-row,.zc-rg-label-row{display:flex;align-items:stretch}.zc-rg-group-band{display:flex;align-items:center;justify-content:center;padding:2px 4px;border-left:1px solid var(--zc-border);font-size:.625rem;font-weight:700;text-transform:uppercase;letter-spacing:.03em;color:var(--zc-muted-fg);overflow:hidden;white-space:nowrap}.zc-rg-label{flex:1 1 0;display:flex;align-items:center;justify-content:center;padding:4px 6px;border-left:1px solid var(--zc-border);font-size:.8125rem;font-weight:600;min-width:0;overflow:hidden;text-align:center}.zc-rg-canvas{display:flex;align-items:stretch}.zc-rg-cols{position:relative;flex:1 1 auto;display:flex;align-items:stretch;min-width:0}.zc-rg-col{position:relative;flex:1 1 0;border-left:1px solid var(--zc-border);min-width:0}.zc-rg-col>.zc-select-box{left:2px;right:2px}.zc-rg-now{z-index:4}.zc-body.zc-closed .zc-col,.zc-body.zc-closed .zc-tl-time-canvas,.zc-body.zc-closed .zc-rg-cols{background:var(--zc-nonbusiness)}.zc-event.zc-dragging{z-index:6;opacity:.9;box-shadow:0 2px 8px #0000002e}.zc-resize-handle{position:absolute;z-index:2}.zc-resize-s{left:0;right:0;bottom:0;height:6px;cursor:ns-resize}.zc-resize-e,.zc-resize-w{top:0;bottom:0;width:6px;cursor:ew-resize}.zc-resize-e{right:0}.zc-resize-w{left:0}.zc-select-box{position:absolute;z-index:5;background:var(--zc-event-bg);border:1px solid var(--zc-event-border);opacity:.45;pointer-events:none;border-radius:var(--zc-radius)}.zc-col>.zc-select-box{left:2px;right:2px}.zc-tl-select{top:2px;bottom:2px}.zc-tl-row.zc-drop-target,.zc-rg-col.zc-drop-target{background:var(--zc-today-bg);outline:2px dashed var(--zc-event-border);outline-offset:-2px}.zc-now-indicator{position:absolute;left:0;right:0;height:0;border-top:2px solid var(--zc-now);z-index:4;pointer-events:none}.zc-now-indicator:before{content:"";position:absolute;left:-1px;top:-4px;width:7px;height:7px;border-radius:50%;background:var(--zc-now)}
|