react-timelane 0.0.2 → 0.0.4

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.
Files changed (31) hide show
  1. package/README.md +141 -51
  2. package/dist/components/Timelane.d.ts +4 -2
  3. package/dist/components/TimelaneAside.d.ts +8 -8
  4. package/dist/components/TimelaneBodyInner.d.ts +8 -8
  5. package/dist/components/{core/CoreItem → TimelaneItem}/DragResizeComponent.d.ts +2 -2
  6. package/dist/components/TimelaneItem/TimelaneItem.d.ts +14 -0
  7. package/dist/components/{core/CoreSwimlane → TimelaneLane}/AvailableSpaceIndicator.d.ts +4 -4
  8. package/dist/components/{core/CoreSwimlane → TimelaneLane}/DropTarget.d.ts +1 -1
  9. package/dist/components/{core/CoreSwimlane → TimelaneLane}/OverlapIndicator.d.ts +1 -1
  10. package/dist/components/TimelaneLane/TimelaneLane.d.ts +16 -0
  11. package/dist/components/TimelaneSettingsContext.d.ts +1 -1
  12. package/dist/components/TimelaneWrapper.d.ts +2 -2
  13. package/dist/components/utils.d.ts +27 -0
  14. package/dist/hooks/useScroll.d.ts +3 -3
  15. package/dist/index.d.ts +3 -4
  16. package/dist/react-timelane.css +1 -1
  17. package/dist/react-timelane.js +347 -355
  18. package/dist/types/Item.d.ts +12 -0
  19. package/dist/types/Lane.d.ts +5 -0
  20. package/dist/types/LaneId.d.ts +1 -0
  21. package/dist/types/index.d.ts +3 -3
  22. package/package.json +1 -1
  23. package/dist/components/core/CoreItem/CoreItemComponent.d.ts +0 -14
  24. package/dist/components/core/CoreSwimlane/CoreSwimlane.d.ts +0 -16
  25. package/dist/components/core/CoreSwimlane/utils.d.ts +0 -13
  26. package/dist/components/core/utils.d.ts +0 -15
  27. package/dist/types/CoreItem.d.ts +0 -12
  28. package/dist/types/SwimlaneId.d.ts +0 -1
  29. package/dist/types/SwimlaneT.d.ts +0 -5
  30. /package/dist/components/{core/CoreSwimlane → TimelaneLane}/DropPreview.d.ts +0 -0
  31. /package/dist/components/{layout → TimelaneLayout}/TimelaneLayout.d.ts +0 -0
package/README.md CHANGED
@@ -1,54 +1,144 @@
1
- # React + TypeScript + Vite
2
-
3
- This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
4
-
5
- Currently, two official plugins are available:
6
-
7
- - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh
8
- - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
9
-
10
- ## Expanding the ESLint configuration
11
-
12
- If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
13
-
14
- ```js
15
- export default tseslint.config({
16
- extends: [
17
- // Remove ...tseslint.configs.recommended and replace with this
18
- ...tseslint.configs.recommendedTypeChecked,
19
- // Alternatively, use this for stricter rules
20
- ...tseslint.configs.strictTypeChecked,
21
- // Optionally, add this for stylistic rules
22
- ...tseslint.configs.stylisticTypeChecked,
23
- ],
24
- languageOptions: {
25
- // other options...
26
- parserOptions: {
27
- project: ['./tsconfig.node.json', './tsconfig.app.json'],
28
- tsconfigRootDir: import.meta.dirname,
29
- },
30
- },
31
- })
1
+ # react-timelane
2
+
3
+ A React/TypeScript library to build timelines / horizontally scrollable calendars with multiple lanes.
4
+
5
+ ## Features
6
+
7
+ react-timelane has a particular focus on usability and comes with many neat features:
8
+
9
+ - item drag and drop
10
+ - item resizing
11
+ - jump (scroll) to point in time, lane or item
12
+ - item selection via mouse range
13
+
14
+ ## Demo
15
+
16
+ https://dhansmair.github.io/react-timelane
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ npm install react-timelane
32
22
  ```
33
23
 
34
- You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
35
-
36
- ```js
37
- // eslint.config.js
38
- import reactX from 'eslint-plugin-react-x'
39
- import reactDom from 'eslint-plugin-react-dom'
40
-
41
- export default tseslint.config({
42
- plugins: {
43
- // Add the react-x and react-dom plugins
44
- 'react-x': reactX,
45
- 'react-dom': reactDom,
46
- },
47
- rules: {
48
- // other rules...
49
- // Enable its recommended typescript rules
50
- ...reactX.configs['recommended-typescript'].rules,
51
- ...reactDom.configs.recommended.rules,
52
- },
53
- })
24
+ ## Code Example
25
+
26
+ see `docs/src/components/MyTimelane.tsx` for the full example:
27
+
28
+ ```typescript
29
+ import { addDays, min } from "date-fns";
30
+ import { useState, type MouseEvent } from "react";
31
+ import {
32
+ type AvailableSpace,
33
+ type Lane,
34
+ type Item,
35
+ type ItemId,
36
+ type TimelaneSettings,
37
+ TimelaneSettingsProvider,
38
+ useScroll,
39
+ } from "react-timelane";
40
+ import type Allocation from "../models/Allocation";
41
+ import type Resource from "../models/Resource";
42
+ import AllocationComponent from "./AllocationComponent";
43
+
44
+ import { Timelane as TL } from "react-timelane";
45
+
46
+ const defaultSettings: TimelaneSettings = {
47
+ showMonths: true,
48
+ showWeeks: true,
49
+ showDays: true,
50
+ start: new Date(2025, 3, 1),
51
+ end: new Date(2025, 6, 2),
52
+ pixelsPerDay: 50,
53
+ pixelsPerResource: 100,
54
+ allowOverlaps: false,
55
+ focusedDate: null,
56
+ };
57
+
58
+ interface MyTimelaneProps {
59
+ resources: Resource[];
60
+ allocations: Allocation[];
61
+ onAllocationCreate: (allocation: Allocation) => void;
62
+ onAllocationUpdate: (allocation: Allocation) => void;
63
+ }
64
+
65
+ function MyTimelane({
66
+ resources,
67
+ allocations,
68
+ onAllocationCreate,
69
+ onAllocationUpdate,
70
+ }: MyTimelaneProps) {
71
+ const [selection, setSelection] = useState<ItemId[]>([]);
72
+
73
+ const lanes: Lane[] = resources.map((resource) => ({
74
+ id: resource.id,
75
+ capacity: resource.capacity,
76
+ }));
77
+
78
+ const items: Item<Allocation>[] = allocations.map((allocation) => ({
79
+ id: allocation.id,
80
+ laneId: allocation.resourceId,
81
+ start: allocation.start,
82
+ end: allocation.end,
83
+ size: allocation.size,
84
+ offset: allocation.offset,
85
+ payload: allocation,
86
+ }));
87
+
88
+ function handleItemUpdate(item: Item<Allocation>) {
89
+ const updatedAllocation: Allocation = {
90
+ ...item.payload,
91
+ resourceId: item.laneId,
92
+ start: item.start,
93
+ end: item.end,
94
+ size: item.size,
95
+ offset: item.offset,
96
+ };
97
+
98
+ onAllocationUpdate(updatedAllocation);
99
+ }
100
+
101
+ return (
102
+ <TL.Container>
103
+ <TL.Header
104
+ onDayClick={({ day }) => {
105
+ console.log("day clicked", day);
106
+ }}
107
+ onMonthClick={({ firstDay }) => {
108
+ console.log("month clicked", firstDay);
109
+ }}
110
+ onWeekClick={({ firstDay }) => {
111
+ console.log("week clicked", firstDay);
112
+ }}
113
+ />
114
+ <TL.Body
115
+ onSelect={(selection) => {
116
+ setSelection(selection);
117
+ }}
118
+ >
119
+ {lanes.map((lane) => (
120
+ <TL.Lane
121
+ key={lane.id}
122
+ lane={lane}
123
+ items={items.filter((item) => item.laneId === lane.id)}
124
+ onItemUpdate={handleItemUpdate}
125
+ renderItem={(item, isDragged) => (
126
+ <AllocationComponent
127
+ allocation={item.payload}
128
+ isDragged={isDragged}
129
+ isSelected={selection.includes(item.id)}
130
+ />
131
+ )}
132
+ />
133
+ ))}
134
+ </TL.Body>
135
+ <TL.Background />
136
+ <TL.Aside
137
+ lanes={lanes}
138
+ renderLaneHeader={(lane) => <div>{lane.id}</div>}
139
+ />
140
+ <TL.Layout.Corner />
141
+ </TL.Container>
142
+ );
143
+ }
54
144
  ```
@@ -1,5 +1,7 @@
1
1
  import { PropsWithChildren } from 'react';
2
- import { CoreSwimlane, TimelaneBody, TimelaneLayout } from '..';
2
+ import { TimelaneLane } from './TimelaneLane/TimelaneLane';
3
+ import { TimelaneBody } from './TimelaneBody';
4
+ import { TimelaneLayout } from './TimelaneLayout/TimelaneLayout';
3
5
  import { TimelaneHeader } from './TimelaneHeader/TimelaneHeader';
4
6
  import { TimelaneBackground } from './TimelaneBackground';
5
7
  import { TimelaneAside } from './TimelaneAside';
@@ -10,6 +12,6 @@ export declare namespace Timelane {
10
12
  var Body: typeof TimelaneBody;
11
13
  var Background: typeof TimelaneBackground;
12
14
  var Aside: typeof TimelaneAside;
13
- var Lane: typeof CoreSwimlane;
15
+ var Lane: typeof TimelaneLane;
14
16
  var Layout: typeof TimelaneLayout;
15
17
  }
@@ -1,13 +1,13 @@
1
1
  import { MouseEvent, ReactElement } from 'react';
2
- import { SwimlaneT } from '../types';
2
+ import { Lane } from '../types';
3
3
  interface TimelaneAsideProps {
4
- lanes: SwimlaneT[];
5
- focusedLane?: SwimlaneT | null;
6
- setFocusedLane?: (lane: SwimlaneT | null) => void;
7
- onLaneHeaderClick?: (lane: SwimlaneT, e: MouseEvent) => void;
8
- onLaneHeaderDoubleClick?: (lane: SwimlaneT, e: MouseEvent) => void;
9
- onLaneHeaderContextMenu?: (lane: SwimlaneT, e: MouseEvent) => void;
10
- renderLaneHeader?: (lane: SwimlaneT) => ReactElement;
4
+ lanes: Lane[];
5
+ focusedLane?: Lane | null;
6
+ setFocusedLane?: (lane: Lane | null) => void;
7
+ onLaneHeaderClick?: (lane: Lane, e: MouseEvent) => void;
8
+ onLaneHeaderDoubleClick?: (lane: Lane, e: MouseEvent) => void;
9
+ onLaneHeaderContextMenu?: (lane: Lane, e: MouseEvent) => void;
10
+ renderLaneHeader?: (lane: Lane) => ReactElement;
11
11
  }
12
12
  export declare function TimelaneAside({ lanes, focusedLane, setFocusedLane, onLaneHeaderClick, onLaneHeaderDoubleClick, onLaneHeaderContextMenu, renderLaneHeader, }: TimelaneAsideProps): import("react/jsx-runtime").JSX.Element;
13
13
  export {};
@@ -1,13 +1,13 @@
1
1
  import { MouseEvent, ReactElement } from 'react';
2
- import { AvailableSpace, CoreItem, SwimlaneT } from '../types';
2
+ import { AvailableSpace, Item, Lane } from '../types';
3
3
  interface TimelaneBodyInnerProps<T> {
4
- lanes: SwimlaneT[];
5
- items: CoreItem<T>[];
6
- renderItem?: (item: CoreItem<T>, isDragged: boolean) => ReactElement;
7
- onItemUpdate?: (item: CoreItem<T>) => void;
8
- onLaneClick?: (lane: SwimlaneT, when: Date, availableSpace: AvailableSpace | null, e: MouseEvent) => void;
9
- onLaneDoubleClick?: (lane: SwimlaneT, when: Date, availableSpace: AvailableSpace | null, e: MouseEvent) => void;
10
- onLaneContextMenu?: (lane: SwimlaneT, when: Date, e: MouseEvent) => void;
4
+ lanes: Lane[];
5
+ items: Item<T>[];
6
+ renderItem?: (item: Item<T>, isDragged: boolean) => ReactElement;
7
+ onItemUpdate?: (item: Item<T>) => void;
8
+ onLaneClick?: (lane: Lane, when: Date, availableSpace: AvailableSpace | null, e: MouseEvent) => void;
9
+ onLaneDoubleClick?: (lane: Lane, when: Date, availableSpace: AvailableSpace | null, e: MouseEvent) => void;
10
+ onLaneContextMenu?: (lane: Lane, when: Date, e: MouseEvent) => void;
11
11
  }
12
12
  /**
13
13
  * @deprecated The component should not be used
@@ -1,7 +1,7 @@
1
1
  import { PropsWithChildren } from 'react';
2
- import { Position, Rectangle, Dimensions, CoreItem } from '../../../types';
2
+ import { Position, Rectangle, Dimensions, Item } from '../../types';
3
3
  interface DragResizeComponentProps<T> {
4
- item: CoreItem<T>;
4
+ item: Item<T>;
5
5
  rectangle: Rectangle;
6
6
  boundingRectangle: Dimensions;
7
7
  onDragStart: (grabPosition: Position, relativeGrabPosition: Position) => void;
@@ -0,0 +1,14 @@
1
+ import { PropsWithChildren } from 'react';
2
+ import { Item, Position, Lane, TimelaneSettings } from '../../types';
3
+ interface TimelaneItemProps<T> {
4
+ item: Item<T>;
5
+ lane: Lane;
6
+ settings: TimelaneSettings;
7
+ onDragStart: (grabPosition: Position, relativeGrabPosition: Position) => void;
8
+ onDrop: () => void;
9
+ onDrag: () => void;
10
+ onUpdate: (updatedItem: Item<T>) => void;
11
+ onResizeStart: () => void;
12
+ }
13
+ export default function TimelaneItem<T>({ lane, item, settings, children, onDragStart, onDrag, onDrop, onUpdate, onResizeStart, }: PropsWithChildren<TimelaneItemProps<T>>): import("react/jsx-runtime").JSX.Element;
14
+ export {};
@@ -1,11 +1,11 @@
1
1
  import { PropsWithChildren } from 'react';
2
- import { CoreItem, Pixels, SwimlaneT, TimeRange } from '../../../types';
2
+ import { Item, Pixels, Lane, TimeRange } from '../../types';
3
3
  interface AvailableSpaceIndicatorProps<T> {
4
4
  pixels: Pixels;
5
5
  range: TimeRange;
6
- swimlane: SwimlaneT;
6
+ lane: Lane;
7
7
  debug: boolean;
8
- items: CoreItem<T>[];
8
+ items: Item<T>[];
9
9
  }
10
10
  /**
11
11
  * A component that displays the available space at the mouse cursor position.
@@ -17,5 +17,5 @@ interface AvailableSpaceIndicatorProps<T> {
17
17
  * @param param0
18
18
  * @returns
19
19
  */
20
- export default function AvailableSpaceIndicator<T>({ pixels, range, swimlane, items, debug, children, }: PropsWithChildren<AvailableSpaceIndicatorProps<T>>): import("react/jsx-runtime").JSX.Element;
20
+ export default function AvailableSpaceIndicator<T>({ pixels, range, lane, items, debug, children, }: PropsWithChildren<AvailableSpaceIndicatorProps<T>>): import("react/jsx-runtime").JSX.Element;
21
21
  export {};
@@ -1,5 +1,5 @@
1
1
  import { PropsWithChildren } from 'react';
2
- import { GrabInfo, Position } from '../../../types';
2
+ import { GrabInfo, Position } from '../../types';
3
3
  interface DropTargetProps {
4
4
  onDragStart?: (mousePos: Position, grabInfo: GrabInfo, data: any) => void;
5
5
  onDrag?: (mousePos: Position, grabInfo: GrabInfo, data: any) => void;
@@ -1,4 +1,4 @@
1
- import { Rectangle } from '../../../types';
1
+ import { Rectangle } from '../../types';
2
2
  interface OverlapIndicatorProps {
3
3
  overlap: Rectangle;
4
4
  }
@@ -0,0 +1,16 @@
1
+ import { MouseEvent, ReactElement } from 'react';
2
+ import { AvailableSpace, Item, Lane } from '../../types';
3
+ interface TimelaneLaneProps<T> {
4
+ lane: Lane;
5
+ items: Item<T>[];
6
+ focused?: boolean;
7
+ onItemUpdate?: (item: Item<T>) => void;
8
+ onMouseUp?: (e: MouseEvent) => void;
9
+ onClick?: (when: Date, availableSpace: AvailableSpace | null, e: MouseEvent) => void;
10
+ onDoubleClick?: (when: Date, availableSpace: AvailableSpace | null, e: MouseEvent) => void;
11
+ onContextMenu?: (when: Date, e: MouseEvent) => void;
12
+ renderItem?: (item: Item<T>, isDragged: boolean) => ReactElement;
13
+ onResizeStart?: (data: T) => void;
14
+ }
15
+ export declare function TimelaneLane<T>({ lane, items, focused, onItemUpdate, onMouseUp, onClick, onDoubleClick, onContextMenu, renderItem, onResizeStart, }: TimelaneLaneProps<T>): import("react/jsx-runtime").JSX.Element;
16
+ export {};
@@ -1,5 +1,5 @@
1
1
  import { Dispatch, SetStateAction } from 'react';
2
- import { TimelaneSettings } from '../types/TimelaneSettings';
2
+ import { TimelaneSettings } from '../types/';
3
3
  export interface TimelaneContextOuter {
4
4
  settings: TimelaneSettings;
5
5
  setSettings: Dispatch<SetStateAction<TimelaneSettings>>;
@@ -1,8 +1,8 @@
1
1
  import { PropsWithChildren } from 'react';
2
- import { SwimlaneT } from '../types';
2
+ import { Lane } from '../types';
3
3
  interface TimelaneWrapperProps {
4
4
  focusedDay?: Date | null;
5
- focusedSwimlane?: SwimlaneT | null;
5
+ focusedLane?: Lane | null;
6
6
  start?: Date;
7
7
  end?: Date;
8
8
  pixelsPerDay?: number;
@@ -0,0 +1,27 @@
1
+ import { DragLocationHistory, ElementDragPayload } from '@atlaskit/pragmatic-drag-and-drop/dist/types/internal-types';
2
+ import { Item, Dimensions, Pixels, Position, Rectangle, Lane, TimeRange, AvailableSpace, GrabInfo, Grid } from '../types';
3
+ export declare function getDropTargetDimensions(lane: Lane, pixels: Pixels, range: TimeRange): Dimensions;
4
+ export declare function getDropTargetHeight(lane: Lane, pixels: Pixels): number;
5
+ export declare function getDropTargetWidth(lane: Lane, pixels: Pixels, range: TimeRange): number;
6
+ export declare function getItemRectangle<T>(item: Item<T>, lane: Lane, range: TimeRange, pixels: Pixels): Rectangle;
7
+ export declare function getItemDimensions<T>(item: Item<T>, lane: Lane, pixels: Pixels): Dimensions;
8
+ export declare function dateToPixel(date: Date, start: Date, pixels: Pixels): number;
9
+ export declare function offsetToPixel(offset: number, capacity: number, pixels: Pixels): number;
10
+ export declare function getItemPosition<T>(item: Item<T>, lane: Lane, start: Date, pixels: Pixels): Position;
11
+ export declare function getGrabPosition(source: ElementDragPayload, location: DragLocationHistory): {
12
+ absolute: Position;
13
+ relative: Position;
14
+ };
15
+ export declare function getUpdatedItem<T>(oldItem: Item<T>, lane: Lane, dropPreviewRect: Rectangle, pixels: Pixels, range: TimeRange): Item<T>;
16
+ export declare function getDropPreviewRectangle<S, T>(lane: Lane, items: Item<T>[], item: Item<S>, mousePosition: Position | null, grabInfo: GrabInfo, pixels: Pixels, grid: Grid, range: TimeRange, allowOverlaps: boolean): Rectangle | null;
17
+ /**
18
+ * Returns true if two rectangles (l1, r1) and (l2, r2) overlap
19
+ * @param a
20
+ * @param b
21
+ * @returns
22
+ */
23
+ export declare function doOverlap(a: Rectangle, b: Rectangle): boolean;
24
+ export declare function getOverlap(a: Rectangle, b: Rectangle): Rectangle | null;
25
+ export declare function itemsDoOverlap<T>(a: Item<T>, b: Item<T>): boolean;
26
+ export declare function isWithinTargetDimensions(item: Rectangle, targetDimensions: Dimensions): boolean;
27
+ export declare function getAvailableSpace<T>(clickedDate: Date, clickedOffset: number, lane: Lane, items: Item<T>[], range: TimeRange): AvailableSpace | null;
@@ -1,10 +1,10 @@
1
- import { ItemId, SwimlaneId } from '../types';
1
+ import { ItemId, LaneId } from '../types';
2
2
  export declare const useScroll: () => {
3
3
  scrollTo: (destination: {
4
4
  horz?: Date;
5
- vert?: SwimlaneId;
5
+ vert?: LaneId;
6
6
  } | ItemId) => void;
7
7
  scrollToDate: (date: Date) => void;
8
8
  scrollToItem: (itemId: ItemId) => void;
9
- scrollToLane: (laneId: SwimlaneId) => void;
9
+ scrollToLane: (laneId: LaneId) => void;
10
10
  };
package/dist/index.d.ts CHANGED
@@ -1,13 +1,12 @@
1
- export { TimelaneLayout } from './components/layout/TimelaneLayout';
1
+ export { TimelaneLayout } from './components/TimelaneLayout/TimelaneLayout';
2
2
  export { TimelaneAside } from './components/TimelaneAside';
3
3
  export { TimelaneBackground } from './components/TimelaneBackground';
4
- export { CoreSwimlane } from './components/core/CoreSwimlane/CoreSwimlane';
4
+ export { TimelaneLane } from './components/TimelaneLane/TimelaneLane';
5
5
  export { TimelaneBody } from './components/TimelaneBody';
6
6
  export { Timelane } from './components/Timelane';
7
7
  export { TimelaneSettingsProvider } from './components/TimelaneSettingsProvider';
8
8
  export * from './components/TimelaneHeader';
9
9
  export * from './types';
10
- export * from './components/core/utils';
11
- export * from './components/core/CoreSwimlane/utils';
10
+ export * from './components/utils';
12
11
  export { useScroll } from './hooks/useScroll';
13
12
  export { useTimelaneContext } from './hooks/useTimelaneContext';
@@ -1 +1 @@
1
- .timelane-layout{position:relative;width:100%;height:100%;overflow:auto}.timelane-layout .timelane-layout-inner{position:relative;width:fit-content;height:fit-content;display:grid;grid-template-areas:"corner-tl header corner-tr" "aside-l body aside-r" "corner-bl footer corner-br"}.timelane-layout .timelane-layout-inner .timelane-layout-header,.timelane-layout .timelane-layout-inner .timelane-layout-footer,.timelane-layout .timelane-layout-inner .timelane-layout-aside,.timelane-layout .timelane-layout-inner .timelane-layout-corner{background:#fff}.timelane-layout .timelane-layout-inner .timelane-layout-header{grid-area:header;position:sticky;position:-webkit-sticky;top:0;z-index:101;overflow:hidden}.timelane-layout .timelane-layout-inner .timelane-layout-body{grid-area:body;z-index:100}.timelane-layout .timelane-layout-inner .timelane-layout-background{grid-area:body;z-index:-1}.timelane-layout .timelane-layout-inner .timelane-layout-footer{grid-area:footer;position:sticky;position:-webkit-sticky;bottom:0;z-index:101;border-top:1px solid lightgray}.timelane-layout .timelane-layout-inner .timelane-layout-aside{grid-area:aside-l;position:sticky;position:-webkit-sticky;left:0;z-index:101}.timelane-layout .timelane-layout-inner .timelane-layout-aside.timelane-layout-aside-right{grid-area:aside-r;right:0;left:initial;border-left:1px solid lightgray}.timelane-layout .timelane-layout-inner .timelane-layout-corner{position:sticky;position:-webkit-sticky;z-index:102}.timelane-layout .timelane-layout-inner .timelane-layout-corner.timelane-layout-corner-top-left{grid-area:corner-tl;top:0;left:0;border-bottom:1px solid lightgray;border-right:1px solid lightgray}.timelane-layout .timelane-layout-inner .timelane-layout-corner.timelane-layout-corner-top-right{grid-area:corner-tr;top:0;right:0;border-bottom:1px solid lightgray;border-left:1px solid lightgray}.timelane-layout .timelane-layout-inner .timelane-layout-corner.timelane-layout-corner-bottom-left{grid-area:corner-bl;bottom:0;left:0;border-top:1px solid lightgray;border-right:1px solid lightgray}.timelane-layout .timelane-layout-inner .timelane-layout-corner.timelane-layout-corner-bottom-right{grid-area:corner-br;bottom:0;right:0;border-top:1px solid lightgray;border-left:1px solid lightgray}*{box-sizing:border-box}.timelane{--timelane-aside-width: 150px;--timelane-border-color-light: #f0f0f0;--timelane-border-color-normal: lightgray;--timelane-border-color-dark: gray;--timelane-highlight-color: #f8f8f8;--timelane-focused-color: rgba(0, 0, 255, .1);--timelane-hover-color: rgba(0, 0, 0, .05);overflow:auto;position:relative;width:100%;height:100%;border-left:1px solid var(--timelane-border-color-normal)}.timelane .timelane-header{top:0;z-index:101;background:#fff}.timelane .timelane-header .timelane-header-months{display:flex;flex-flow:row nowrap}.timelane .timelane-header .timelane-header-months .timelane-header-month-label{border-right:1px solid var(--timelane-border-color-normal);border-bottom:1px solid var(--timelane-border-color-normal);overflow:hidden;font-size:1em;height:30px;line-height:30px;text-align:center;cursor:pointer}.timelane .timelane-header .timelane-header-months .timelane-header-month-label:hover{background:var(--timelane-hover-color)}.timelane .timelane-header .timelane-header-weeks{display:flex;flex-flow:row nowrap}.timelane .timelane-header .timelane-header-weeks .timelane-header-week-label{border-right:1px solid var(--timelane-border-color-normal);border-bottom:1px solid var(--timelane-border-color-normal);overflow:hidden;font-size:1em;height:30px;line-height:30px;text-align:center;cursor:pointer}.timelane .timelane-header .timelane-header-weeks .timelane-header-week-label:hover{background:var(--timelane-hover-color)}.timelane .timelane-header .timelane-header-days{display:flex;flex-flow:row nowrap}.timelane .timelane-header .timelane-header-days .timelane-header-day-label{border-right:1px solid var(--timelane-border-color-normal);border-bottom:1px solid var(--timelane-border-color-normal);overflow:hidden;font-size:.8em;height:20px;line-height:20px;text-align:center;cursor:pointer}.timelane .timelane-header .timelane-header-days .timelane-header-day-label:hover{background:var(--timelane-hover-color)}.timelane .timelane-header .timelane-header-days .timelane-header-day-label.timelane-header-day-label-focused{background:var(--timelane-focused-color)}.timelane .timelane-header-corner{z-index:102;background:#fff;border-right:1px solid var(--timelane-border-color-normal);border-bottom:1px solid var(--timelane-border-color-normal)}.timelane .timelane-body{z-index:99;position:relative;width:fit-content}.timelane .timelane-body .timelane-swimlane{border-color:var(--timelane-border-color-normal)!important;border-top:1px solid gray;border-bottom:1px solid gray;margin-top:-1px;overflow:hidden}.timelane .timelane-body .timelane-swimlane .timelane-drop-target{position:relative;width:100%;height:100%}.timelane .timelane-body .timelane-swimlane .timelane-drop-target .timelane-item{position:absolute;cursor:pointer;border-radius:3px;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.timelane .timelane-body .timelane-swimlane .timelane-drop-target .timelane-item.timelane-item-marked .timelane-allocation{border:2px dashed rgba(0,0,0,.5)}.timelane .timelane-body .timelane-swimlane .timelane-drop-target .timelane-item .timelane-item-drag-handle{height:100%}.timelane .timelane-body .timelane-swimlane .timelane-drop-target .timelane-item .timelane-item-resize-handle{background:#bed7dc;border-radius:5px;top:4px!important;bottom:4px!important;height:auto!important;width:6px!important;z-index:2;background:transparent!important;transition:.2s}.timelane .timelane-body .timelane-swimlane .timelane-drop-target .timelane-item .timelane-item-resize-handle:hover{background:#0000001a!important}.timelane .timelane-body .timelane-swimlane .timelane-drop-target .timelane-item .timelane-item-resize-handle.timelane-item-resize-handle-left{left:0!important;margin-left:1px}.timelane .timelane-body .timelane-swimlane .timelane-drop-target .timelane-item .timelane-item-resize-handle.timelane-item-resize-handle-right{right:0!important}.timelane .timelane-body .timelane-swimlane .timelane-drop-target .timelane-item .timelane-allocation{background:#92a8d1;border-radius:2px;border:2px solid transparent;height:calc(100% - 1px);overflow:hidden;cursor:pointer;padding:0 4px;margin-left:1px;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;container-type:size;container-name:timelane-allocation}.timelane .timelane-body .timelane-swimlane .timelane-drop-target .timelane-item .timelane-allocation .timelane-allocation-title{font-weight:700;transform-origin:top left;line-height:30px;white-space:nowrap}@container timelane-allocation (max-height: 30px){.timelane .timelane-body .timelane-swimlane .timelane-drop-target .timelane-item .timelane-allocation .timelane-allocation-title{margin:0}}.timelane .timelane-body .timelane-swimlane .timelane-drop-target .timelane-item .timelane-allocation.timelane-allocation-selected{border:2px dashed rgba(0,0,0,.5)}.timelane .timelane-body .timelane-swimlane .timelane-drop-target .timelane-item.dragging{opacity:0}.timelane .timelane-body .timelane-swimlane .timelane-drop-target .timelane-drop-preview{background:#00f;position:absolute;border-radius:2px;color:#fff}.timelane .timelane-body .timelane-swimlane.timelane-row-focused{background:var(--timelane-focused-color)}.timelane .timelane-aside{border-right:1px solid var(--timelane-border-color-normal);width:var(--timelane-aside-width);background:#fff}.timelane .timelane-aside .timelane-aside-swimlane-header{border:1px solid var(--timelane-border-color-normal);border-right:none;border-left:none;margin-top:-1px;padding:10px;overflow:hidden;position:relative;cursor:pointer}.timelane .timelane-aside .timelane-aside-swimlane-header.timelane-aside-swimlane-header-focused{background:var(--timelane-focused-color)}.timelane .timelane-aside .timelane-aside-swimlane-header:hover .timelane-aside-resource-menu{opacity:1}.timelane .timelane-aside .timelane-aside-swimlane-header .timelane-aside-resource-menu{position:absolute;top:5px;right:5px;opacity:0;transition:.15s}.timelane .timelane-aside .timelane-aside-swimlane-header .timelane-aside-resource-menu.timelane-aside-resource-menu-open{opacity:1}.timelane .timelane-background{position:relative;width:100%;height:100%}.timelane .timelane-background .timelane-background-inner{display:flex;flex-flow:row nowrap;height:100%}.timelane .timelane-background .timelane-background-inner .timelane-background-day-label{border-right:1px solid var(--timelane-border-color-light);overflow:hidden;font-size:.8em;height:100%;z-index:-100}.timelane .timelane-background .timelane-background-inner .timelane-background-day-label.timelane-background-day-label-sunday{border-right:1px solid var(--timelane-border-color-normal)}.timelane .timelane-background .timelane-background-inner .timelane-background-day-label.timelane-background-day-label-focused{background:var(--timelane-focused-color)}.timelane .timelane-background .timelane-background-inner .timelane-background-focused-day-position{position:absolute;height:100%;background:var(--timelane-focused-color);z-index:-101}.timelane-header-tooltip,.timelane-header-day-tooltip{font-size:2em}
1
+ .timelane-layout{position:relative;width:100%;height:100%;overflow:auto}.timelane-layout .timelane-layout-inner{position:relative;width:fit-content;height:fit-content;display:grid;grid-template-areas:"corner-tl header corner-tr" "aside-l body aside-r" "corner-bl footer corner-br"}.timelane-layout .timelane-layout-inner .timelane-layout-header,.timelane-layout .timelane-layout-inner .timelane-layout-footer,.timelane-layout .timelane-layout-inner .timelane-layout-aside,.timelane-layout .timelane-layout-inner .timelane-layout-corner{background:#fff}.timelane-layout .timelane-layout-inner .timelane-layout-header{grid-area:header;position:sticky;position:-webkit-sticky;top:0;z-index:101;overflow:hidden}.timelane-layout .timelane-layout-inner .timelane-layout-body{grid-area:body;z-index:100}.timelane-layout .timelane-layout-inner .timelane-layout-background{grid-area:body;z-index:-1}.timelane-layout .timelane-layout-inner .timelane-layout-footer{grid-area:footer;position:sticky;position:-webkit-sticky;bottom:0;z-index:101;border-top:1px solid lightgray}.timelane-layout .timelane-layout-inner .timelane-layout-aside{grid-area:aside-l;position:sticky;position:-webkit-sticky;left:0;z-index:101}.timelane-layout .timelane-layout-inner .timelane-layout-aside.timelane-layout-aside-right{grid-area:aside-r;right:0;left:initial;border-left:1px solid lightgray}.timelane-layout .timelane-layout-inner .timelane-layout-corner{position:sticky;position:-webkit-sticky;z-index:102}.timelane-layout .timelane-layout-inner .timelane-layout-corner.timelane-layout-corner-top-left{grid-area:corner-tl;top:0;left:0;border-bottom:1px solid lightgray;border-right:1px solid lightgray}.timelane-layout .timelane-layout-inner .timelane-layout-corner.timelane-layout-corner-top-right{grid-area:corner-tr;top:0;right:0;border-bottom:1px solid lightgray;border-left:1px solid lightgray}.timelane-layout .timelane-layout-inner .timelane-layout-corner.timelane-layout-corner-bottom-left{grid-area:corner-bl;bottom:0;left:0;border-top:1px solid lightgray;border-right:1px solid lightgray}.timelane-layout .timelane-layout-inner .timelane-layout-corner.timelane-layout-corner-bottom-right{grid-area:corner-br;bottom:0;right:0;border-top:1px solid lightgray;border-left:1px solid lightgray}*{box-sizing:border-box}.timelane{--timelane-aside-width: 150px;--timelane-border-color-light: #f0f0f0;--timelane-border-color-normal: lightgray;--timelane-border-color-dark: gray;--timelane-highlight-color: #f8f8f8;--timelane-focused-color: rgba(0, 0, 255, .1);--timelane-hover-color: rgba(0, 0, 0, .05);overflow:auto;position:relative;width:100%;height:100%;border-left:1px solid var(--timelane-border-color-normal)}.timelane .timelane-header{top:0;z-index:101;background:#fff}.timelane .timelane-header .timelane-header-months{display:flex;flex-flow:row nowrap}.timelane .timelane-header .timelane-header-months .timelane-header-month-label{border-right:1px solid var(--timelane-border-color-normal);border-bottom:1px solid var(--timelane-border-color-normal);overflow:hidden;font-size:1em;height:30px;line-height:30px;text-align:center;cursor:pointer}.timelane .timelane-header .timelane-header-months .timelane-header-month-label:hover{background:var(--timelane-hover-color)}.timelane .timelane-header .timelane-header-weeks{display:flex;flex-flow:row nowrap}.timelane .timelane-header .timelane-header-weeks .timelane-header-week-label{border-right:1px solid var(--timelane-border-color-normal);border-bottom:1px solid var(--timelane-border-color-normal);overflow:hidden;font-size:1em;height:30px;line-height:30px;text-align:center;cursor:pointer}.timelane .timelane-header .timelane-header-weeks .timelane-header-week-label:hover{background:var(--timelane-hover-color)}.timelane .timelane-header .timelane-header-days{display:flex;flex-flow:row nowrap}.timelane .timelane-header .timelane-header-days .timelane-header-day-label{border-right:1px solid var(--timelane-border-color-normal);border-bottom:1px solid var(--timelane-border-color-normal);overflow:hidden;font-size:.8em;height:20px;line-height:20px;text-align:center;cursor:pointer}.timelane .timelane-header .timelane-header-days .timelane-header-day-label:hover{background:var(--timelane-hover-color)}.timelane .timelane-header .timelane-header-days .timelane-header-day-label.timelane-header-day-label-focused{background:var(--timelane-focused-color)}.timelane .timelane-header-corner{z-index:102;background:#fff;border-right:1px solid var(--timelane-border-color-normal);border-bottom:1px solid var(--timelane-border-color-normal)}.timelane .timelane-body{z-index:99;position:relative;width:fit-content}.timelane .timelane-body .timelane-lane{border-color:var(--timelane-border-color-normal)!important;border-top:1px solid gray;border-bottom:1px solid gray;margin-top:-1px;overflow:hidden}.timelane .timelane-body .timelane-lane .timelane-drop-target{position:relative;width:100%;height:100%}.timelane .timelane-body .timelane-lane .timelane-drop-target .timelane-item{position:absolute;cursor:pointer;border-radius:3px;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.timelane .timelane-body .timelane-lane .timelane-drop-target .timelane-item.timelane-item-marked .timelane-allocation{border:2px dashed rgba(0,0,0,.5)}.timelane .timelane-body .timelane-lane .timelane-drop-target .timelane-item .timelane-item-drag-handle{height:100%}.timelane .timelane-body .timelane-lane .timelane-drop-target .timelane-item .timelane-item-resize-handle{background:#bed7dc;border-radius:5px;top:4px!important;bottom:4px!important;height:auto!important;width:6px!important;z-index:2;background:transparent!important;transition:.2s}.timelane .timelane-body .timelane-lane .timelane-drop-target .timelane-item .timelane-item-resize-handle:hover{background:#0000001a!important}.timelane .timelane-body .timelane-lane .timelane-drop-target .timelane-item .timelane-item-resize-handle.timelane-item-resize-handle-left{left:0!important;margin-left:1px}.timelane .timelane-body .timelane-lane .timelane-drop-target .timelane-item .timelane-item-resize-handle.timelane-item-resize-handle-right{right:0!important}.timelane .timelane-body .timelane-lane .timelane-drop-target .timelane-item .timelane-allocation{background:#92a8d1;border-radius:2px;border:2px solid transparent;height:calc(100% - 1px);overflow:hidden;cursor:pointer;padding:0 4px;margin-left:1px;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;container-type:size;container-name:timelane-allocation}.timelane .timelane-body .timelane-lane .timelane-drop-target .timelane-item .timelane-allocation .timelane-allocation-title{font-weight:700;transform-origin:top left;line-height:30px;white-space:nowrap}@container timelane-allocation (max-height: 30px){.timelane .timelane-body .timelane-lane .timelane-drop-target .timelane-item .timelane-allocation .timelane-allocation-title{margin:0}}.timelane .timelane-body .timelane-lane .timelane-drop-target .timelane-item .timelane-allocation.timelane-allocation-selected{border:2px dashed rgba(0,0,0,.5)}.timelane .timelane-body .timelane-lane .timelane-drop-target .timelane-item.dragging{opacity:0}.timelane .timelane-body .timelane-lane .timelane-drop-target .timelane-drop-preview{background:#00f;position:absolute;border-radius:2px;color:#fff}.timelane .timelane-body .timelane-lane.timelane-row-focused{background:var(--timelane-focused-color)}.timelane .timelane-aside{border-right:1px solid var(--timelane-border-color-normal);width:var(--timelane-aside-width);background:#fff}.timelane .timelane-aside .timelane-aside-lane-header{border:1px solid var(--timelane-border-color-normal);border-right:none;border-left:none;margin-top:-1px;padding:10px;overflow:hidden;position:relative;cursor:pointer}.timelane .timelane-aside .timelane-aside-lane-header.timelane-aside-lane-header-focused{background:var(--timelane-focused-color)}.timelane .timelane-aside .timelane-aside-lane-header:hover .timelane-aside-resource-menu{opacity:1}.timelane .timelane-aside .timelane-aside-lane-header .timelane-aside-resource-menu{position:absolute;top:5px;right:5px;opacity:0;transition:.15s}.timelane .timelane-aside .timelane-aside-lane-header .timelane-aside-resource-menu.timelane-aside-resource-menu-open{opacity:1}.timelane .timelane-background{position:relative;width:100%;height:100%}.timelane .timelane-background .timelane-background-inner{display:flex;flex-flow:row nowrap;height:100%}.timelane .timelane-background .timelane-background-inner .timelane-background-day-label{border-right:1px solid var(--timelane-border-color-light);overflow:hidden;font-size:.8em;height:100%;z-index:-100}.timelane .timelane-background .timelane-background-inner .timelane-background-day-label.timelane-background-day-label-sunday{border-right:1px solid var(--timelane-border-color-normal)}.timelane .timelane-background .timelane-background-inner .timelane-background-day-label.timelane-background-day-label-focused{background:var(--timelane-focused-color)}.timelane .timelane-background .timelane-background-inner .timelane-background-focused-day-position{position:absolute;height:100%;background:var(--timelane-focused-color);z-index:-101}.timelane-header-tooltip,.timelane-header-day-tooltip{font-size:2em}