react-big-schedule 7.1.1-beta.2 → 8.0.0
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 +23 -1
- package/dist/components/DnDContext.js +73 -0
- package/dist/components/DnDSource.js +10 -2
- package/dist/components/EventItem.js +70 -2
- package/dist/components/HeaderView.js +2 -1
- package/dist/components/ResourceEvents.js +27 -2
- package/dist/components/SchedulerData.js +11 -17
- package/dist/components/index.js +0 -1
- package/dist/config/default.js +2 -1
- package/dist/config/scheduler.js +5 -1
- package/dist/css/style.css +32 -0
- package/dist/index.d.ts +10 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -107,6 +107,16 @@ We would like to acknowledge the following projects for their inspiration and co
|
|
|
107
107
|
We are continuously working on enhancing react-big-schedule and welcome your feedback and suggestions for future improvements.
|
|
108
108
|
|
|
109
109
|
|
|
110
|
+
## Supported versions
|
|
111
|
+
|
|
112
|
+
Only **8.0.0 or later** is supported. Issues and security reports for older versions are not handled; please upgrade to the latest release first.
|
|
113
|
+
|
|
114
|
+
### Upgrading to 8.0
|
|
115
|
+
|
|
116
|
+
- `besidesWidth` and `SchedulerData.setBesidesWidth()` are removed. The scheduler measures its own container, so just delete the option.
|
|
117
|
+
- The locale is kept per `SchedulerData` instance (`setSchedulerLocale` / `localeDayjs.locale`) and no longer changes the global dayjs. Call `dayjs.locale(...)` yourself if your app needs the global locale to change.
|
|
118
|
+
- Events in the vertical resource view can now be moved and resized like in the horizontal views. Set `movable`, `startResizable` and `endResizable` to `false` in the config to keep that view read-only.
|
|
119
|
+
|
|
110
120
|
## Use and Setup
|
|
111
121
|
|
|
112
122
|
1.) To install react-big-schedule, use your preferred package manager:
|
|
@@ -274,6 +284,18 @@ const prevClick = data => {
|
|
|
274
284
|
};
|
|
275
285
|
```
|
|
276
286
|
|
|
287
|
+
#### Vertical resource view
|
|
288
|
+
|
|
289
|
+
`ViewType.VerticalResource` shows resources as columns and time as rows. Events render and drag-to-create works across resources and time slots. Drag an event to another resource column and/or time row to move it (`moveEvent` receives the resource as `slotId`), and drag its top or bottom edge to change its start or end (`updateEventStart` / `updateEventEnd`). Row height grows with the number of overlapping events, like the horizontal views. Refer to [this example](https://react-big-schedule.vercel.app/vertical-view).
|
|
290
|
+
|
|
291
|
+
#### Drop preview
|
|
292
|
+
|
|
293
|
+
While an event, task or resource is dragged over a row, the cells where it would be placed are highlighted. Configure it in the `SchedulerData` config: `dropPreviewEnabled` (default `true`), `dropPreviewClassName` and `dropPreviewStyle` (an inline style object, e.g. `{ border: '2px dashed #52c41a', background: 'rgba(82, 196, 26, 0.16)' }`). Refer to [this example](https://react-big-schedule.vercel.app/drag-and-drop).
|
|
294
|
+
|
|
295
|
+
#### Weekly / monthly columns
|
|
296
|
+
|
|
297
|
+
Columns are days or hours by default. A custom view (`ViewType.Custom`, `Custom1`, `Custom2`) picks its own cadence through `behaviors.getCustomDateFunc`, which returns `cellUnit: CellUnit.Week`, `CellUnit.Month`, `CellUnit.Quarter` or `CellUnit.Year` along with the date range. Events then span the week/month cells they overlap. Header text comes from `nonAgendaWeekCellHeaderFormat` / `nonAgendaMonthCellHeaderFormat` / `nonAgendaQuarterCellHeaderFormat` / `nonAgendaYearCellHeaderFormat` (use `|` to split a header into lines). Per-cell totals come from `behaviors.getSummaryFunc`. Refer to [this example](https://react-big-schedule.vercel.app/cadence).
|
|
298
|
+
|
|
277
299
|
#### Theming with antd `ConfigProvider`
|
|
278
300
|
|
|
279
301
|
The antd parts (header controls, date picker popover, event popover) pick up an antd `ConfigProvider` placed above `<Scheduler>`, so antd theme tokens apply to them. The timeline grid itself is styled by the scheduler's own CSS.
|
|
@@ -596,7 +618,7 @@ Used to resolve the locale string of date label of Scheduler component.(Refer to
|
|
|
596
618
|
The width of Scheduler. Scheduler uses responsive layout so schedulerWidth should be a percentage.
|
|
597
619
|
|
|
598
620
|
The Scheduler renders inside a container that is `width: 100%` and `max-height: 100%` of its parent, so it fills the parent
|
|
599
|
-
and follows it when resized (it measures that container with a `ResizeObserver
|
|
621
|
+
and follows it when resized (it measures that container with a `ResizeObserver`).
|
|
600
622
|
Resource columns keep their configured width and the time columns stretch to fill the rest.
|
|
601
623
|
|
|
602
624
|
To size it differently, pass `className` (e.g. tailwind classes) or `style` to the `Scheduler` component, for example
|
|
@@ -21,6 +21,65 @@ export default class DnDContext {
|
|
|
21
21
|
initialEnd
|
|
22
22
|
};
|
|
23
23
|
};
|
|
24
|
+
|
|
25
|
+
// Vertical resource view: rows are time slots and columns are resources, so the pointer column picks the
|
|
26
|
+
// resource and the pointer row, relative to the row the event was grabbed in, shifts its time.
|
|
27
|
+
getVerticalMove = (props, monitor, component) => {
|
|
28
|
+
const {
|
|
29
|
+
schedulerData,
|
|
30
|
+
resourceEvents
|
|
31
|
+
} = props;
|
|
32
|
+
const {
|
|
33
|
+
localeDayjs,
|
|
34
|
+
config
|
|
35
|
+
} = schedulerData;
|
|
36
|
+
const event = monitor.getItem();
|
|
37
|
+
const pos = getPos(component.eventContainer);
|
|
38
|
+
const column = Math.floor((monitor.getClientOffset().x - pos.x) / schedulerData.getContentCellWidth());
|
|
39
|
+
const header = resourceEvents.headerItems[Math.max(0, Math.min(column, resourceEvents.headerItems.length - 1))];
|
|
40
|
+
if (!header) return null;
|
|
41
|
+
const grabbedSlotId = this.sourceMap.get(monitor.getItemType())?.dragSlotId ?? resourceEvents.slotId;
|
|
42
|
+
const shift = localeDayjs(resourceEvents.slotId).diff(localeDayjs(grabbedSlotId));
|
|
43
|
+
const start = localeDayjs(event.start).add(shift, 'ms');
|
|
44
|
+
const end = start.add(localeDayjs(event.end).diff(localeDayjs(event.start)), 'ms');
|
|
45
|
+
|
|
46
|
+
// crossResourceMove disabled: the event stays in its resource and only moves in time
|
|
47
|
+
const keepResource = config.crossResourceMove === false;
|
|
48
|
+
const slotId = keepResource ? schedulerData._getEventSlotId(event) : header.id;
|
|
49
|
+
const slotName = schedulerData.getSlotById(slotId)?.name;
|
|
50
|
+
return {
|
|
51
|
+
slotId,
|
|
52
|
+
slotName,
|
|
53
|
+
start: start.format(DATETIME_FORMAT),
|
|
54
|
+
end: end.format(DATETIME_FORMAT),
|
|
55
|
+
isVertical: true
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Box shown while dragging: the cells (in this row) the dragged item would cover, as { left, width }.
|
|
60
|
+
getDropPreview = (resourceEvents, cellWidth, firstIndex, start, end) => {
|
|
61
|
+
const {
|
|
62
|
+
headerItems
|
|
63
|
+
} = resourceEvents;
|
|
64
|
+
let first = firstIndex;
|
|
65
|
+
let count = 1;
|
|
66
|
+
if (start && end) {
|
|
67
|
+
const from = new Date(start);
|
|
68
|
+
const to = new Date(end);
|
|
69
|
+
const covered = headerItems.reduce((acc, header, index) => {
|
|
70
|
+
if (new Date(header.end) > from && new Date(header.start) < to) acc.push(index);
|
|
71
|
+
return acc;
|
|
72
|
+
}, []);
|
|
73
|
+
if (covered.length > 0) {
|
|
74
|
+
first = covered[0];
|
|
75
|
+
count = covered.length;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
left: first * cellWidth,
|
|
80
|
+
width: count * cellWidth
|
|
81
|
+
};
|
|
82
|
+
};
|
|
24
83
|
getDropSpec = () => ({
|
|
25
84
|
drop: (props, monitor, component) => {
|
|
26
85
|
const {
|
|
@@ -32,6 +91,9 @@ export default class DnDContext {
|
|
|
32
91
|
localeDayjs
|
|
33
92
|
} = schedulerData;
|
|
34
93
|
const type = monitor.getItemType();
|
|
94
|
+
if (schedulerData.isVerticalResourceView() && type === DnDTypes.EVENT) {
|
|
95
|
+
return this.getVerticalMove(props, monitor, component);
|
|
96
|
+
}
|
|
35
97
|
const pos = getPos(component.eventContainer);
|
|
36
98
|
const cellWidth = schedulerData.getContentCellWidth();
|
|
37
99
|
let initialStartTime = null;
|
|
@@ -75,6 +137,16 @@ export default class DnDContext {
|
|
|
75
137
|
this.config = config;
|
|
76
138
|
const item = monitor.getItem();
|
|
77
139
|
const type = monitor.getItemType();
|
|
140
|
+
if (schedulerData.isVerticalResourceView() && type === DnDTypes.EVENT) {
|
|
141
|
+
const move = this.getVerticalMove(props, monitor, component);
|
|
142
|
+
const column = Math.floor((monitor.getClientOffset().x - getPos(component.eventContainer).x) / schedulerData.getContentCellWidth());
|
|
143
|
+
const columnIndex = Math.max(0, Math.min(column, resourceEvents.headerItems.length - 1));
|
|
144
|
+
component.setDropPreview?.(this.getDropPreview(resourceEvents, schedulerData.getContentCellWidth(), columnIndex));
|
|
145
|
+
if (move && movingEvent) {
|
|
146
|
+
movingEvent(schedulerData, move.slotId, move.slotName, move.start, move.end, 'Move', type, item);
|
|
147
|
+
}
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
78
150
|
const pos = getPos(component.eventContainer);
|
|
79
151
|
const cellWidth = schedulerData.getContentCellWidth();
|
|
80
152
|
let initialStart = null;
|
|
@@ -119,6 +191,7 @@ export default class DnDContext {
|
|
|
119
191
|
}
|
|
120
192
|
action = 'Move';
|
|
121
193
|
}
|
|
194
|
+
component.setDropPreview?.(this.getDropPreview(resourceEvents, cellWidth, leftIndex, isEvent ? newStart : undefined, isEvent ? newEnd : undefined));
|
|
122
195
|
if (movingEvent) {
|
|
123
196
|
movingEvent(schedulerData, slotId, slotName, newStart, newEnd, action, type, item);
|
|
124
197
|
}
|
|
@@ -4,12 +4,18 @@ export default class DnDSource {
|
|
|
4
4
|
this.resolveDragObjFunc = resolveDragObjFunc;
|
|
5
5
|
this.dndType = dndType;
|
|
6
6
|
this.DnDEnabled = DnDEnabled;
|
|
7
|
+
// vertical view: the time row the user grabbed the event in (an event is drawn once per row it covers)
|
|
8
|
+
this.dragSlotId = undefined;
|
|
7
9
|
}
|
|
8
10
|
getDragSpec = () => ({
|
|
9
11
|
// beginDrag: (props, monitor, component) => this.resolveDragObjFunc(props),
|
|
10
|
-
beginDrag: props =>
|
|
12
|
+
beginDrag: props => {
|
|
13
|
+
this.dragSlotId = props.slotId;
|
|
14
|
+
return this.resolveDragObjFunc(props);
|
|
15
|
+
},
|
|
11
16
|
// endDrag: (props, monitor, component) => {
|
|
12
17
|
endDrag: (props, monitor) => {
|
|
18
|
+
this.dragSlotId = undefined;
|
|
13
19
|
if (!monitor.didDrop()) return;
|
|
14
20
|
const {
|
|
15
21
|
moveEvent,
|
|
@@ -41,7 +47,9 @@ export default class DnDSource {
|
|
|
41
47
|
const isEvent = type === DnDTypes.EVENT;
|
|
42
48
|
if (isEvent) {
|
|
43
49
|
const event = item;
|
|
44
|
-
if (
|
|
50
|
+
if (dropResult.isVertical) {
|
|
51
|
+
// the vertical drop already resolved the final start/end (see DnDContext.getVerticalMove)
|
|
52
|
+
} else if (config.relativeMove) {
|
|
45
53
|
newStart = localeDayjs(event.start).add(localeDayjs(newStart).diff(localeDayjs(new Date(initialStart))), 'ms').format(DATETIME_FORMAT);
|
|
46
54
|
} else if (viewType !== ViewType.Day) {
|
|
47
55
|
const tmpDayjs = localeDayjs(newStart);
|
|
@@ -211,6 +211,10 @@ class EventItem extends Component {
|
|
|
211
211
|
conflictOccurred
|
|
212
212
|
} = this.props;
|
|
213
213
|
schedulerData._stopResizing();
|
|
214
|
+
if (schedulerData.isVerticalResourceView()) {
|
|
215
|
+
await this.stopVerticalResize(ev, 'start');
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
214
218
|
const {
|
|
215
219
|
width: stateWidth
|
|
216
220
|
} = this.state;
|
|
@@ -293,6 +297,65 @@ class EventItem extends Component {
|
|
|
293
297
|
this.subscribeResizeEvent(this.props);
|
|
294
298
|
} else if (updateEventStart !== undefined) updateEventStart(schedulerData, eventItem, newStart);
|
|
295
299
|
};
|
|
300
|
+
|
|
301
|
+
// Vertical resource view: rows are time slots, so the row under the pointer gives the new start (its top
|
|
302
|
+
// edge) or end (its bottom edge). The event is drawn once per row, so this works for any row height.
|
|
303
|
+
stopVerticalResize = async (ev, dragType) => {
|
|
304
|
+
const {
|
|
305
|
+
schedulerData,
|
|
306
|
+
eventItem,
|
|
307
|
+
updateEventStart,
|
|
308
|
+
updateEventEnd,
|
|
309
|
+
conflictOccurred
|
|
310
|
+
} = this.props;
|
|
311
|
+
const {
|
|
312
|
+
config,
|
|
313
|
+
events,
|
|
314
|
+
localeDayjs
|
|
315
|
+
} = schedulerData;
|
|
316
|
+
const row = document.elementFromPoint(ev.clientX, ev.clientY)?.closest('[data-slot-id]');
|
|
317
|
+
if (!row) return;
|
|
318
|
+
const rowStart = localeDayjs(row.dataset.slotId);
|
|
319
|
+
const isStart = dragType === 'start';
|
|
320
|
+
const newStart = isStart ? rowStart.format(DATETIME_FORMAT) : eventItem.start;
|
|
321
|
+
const newEnd = isStart ? eventItem.end : rowStart.add(config.minuteStep, 'minutes').format(DATETIME_FORMAT);
|
|
322
|
+
if (!localeDayjs(newEnd).isAfter(localeDayjs(newStart))) return;
|
|
323
|
+
if (isStart ? newStart === eventItem.start : newEnd === eventItem.end) return;
|
|
324
|
+
const slotId = schedulerData._getEventSlotId(eventItem);
|
|
325
|
+
const hasConflict = config.checkConflict && events.some(e => {
|
|
326
|
+
if (schedulerData._getEventSlotId(e) !== slotId || e.id === eventItem.id) return false;
|
|
327
|
+
return localeDayjs(newStart).isBefore(localeDayjs(e.end)) && localeDayjs(newEnd).isAfter(localeDayjs(e.start));
|
|
328
|
+
});
|
|
329
|
+
if (hasConflict) {
|
|
330
|
+
conflictOccurred?.(schedulerData, isStart ? 'StartResize' : 'EndResize', eventItem, DnDTypes.EVENT, slotId, schedulerData.getSlotById(slotId)?.name, newStart, newEnd);
|
|
331
|
+
} else if (isStart) updateEventStart?.(schedulerData, eventItem, newStart);else updateEventEnd?.(schedulerData, eventItem, newEnd);
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
// Vertical resource view: the event is drawn once per time row; only the row holding the event's start
|
|
335
|
+
// (or end) gets the top (or bottom) handle.
|
|
336
|
+
getVerticalEdges = () => {
|
|
337
|
+
const {
|
|
338
|
+
schedulerData,
|
|
339
|
+
eventItem,
|
|
340
|
+
slotId
|
|
341
|
+
} = this.props;
|
|
342
|
+
if (!schedulerData.isVerticalResourceView() || !slotId) return {
|
|
343
|
+
top: true,
|
|
344
|
+
bottom: true
|
|
345
|
+
};
|
|
346
|
+
const {
|
|
347
|
+
localeDayjs,
|
|
348
|
+
config
|
|
349
|
+
} = schedulerData;
|
|
350
|
+
const rowStart = localeDayjs(slotId);
|
|
351
|
+
const rowEnd = rowStart.add(config.minuteStep, 'minutes');
|
|
352
|
+
const eventStart = localeDayjs(eventItem.start);
|
|
353
|
+
const eventEnd = localeDayjs(eventItem.end);
|
|
354
|
+
return {
|
|
355
|
+
top: !eventStart.isBefore(rowStart) && eventStart.isBefore(rowEnd),
|
|
356
|
+
bottom: eventEnd.isAfter(rowStart) && !eventEnd.isAfter(rowEnd)
|
|
357
|
+
};
|
|
358
|
+
};
|
|
296
359
|
cancelStartDrag = ev => {
|
|
297
360
|
ev.stopPropagation();
|
|
298
361
|
this.startResizer.removeEventListener('touchmove', this.doStartDrag, false);
|
|
@@ -364,6 +427,10 @@ class EventItem extends Component {
|
|
|
364
427
|
conflictOccurred
|
|
365
428
|
} = this.props;
|
|
366
429
|
schedulerData._stopResizing();
|
|
430
|
+
if (schedulerData.isVerticalResourceView()) {
|
|
431
|
+
await this.stopVerticalResize(ev, 'end');
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
367
434
|
const {
|
|
368
435
|
width: stateWidth
|
|
369
436
|
} = this.state;
|
|
@@ -544,13 +611,14 @@ class EventItem extends Component {
|
|
|
544
611
|
});
|
|
545
612
|
const start = localeDayjs(new Date(eventItem.start));
|
|
546
613
|
const eventTitle = isInPopover ? `${start.format('HH:mm')} ${titleText}` : titleText;
|
|
614
|
+
const verticalEdges = this.getVerticalEdges();
|
|
547
615
|
let startResizeDiv = /*#__PURE__*/_jsx("div", {});
|
|
548
|
-
if (startResizable(this.props)) startResizeDiv = /*#__PURE__*/_jsx("div", {
|
|
616
|
+
if (startResizable(this.props) && verticalEdges.top) startResizeDiv = /*#__PURE__*/_jsx("div", {
|
|
549
617
|
className: "event-resizer event-start-resizer",
|
|
550
618
|
ref: ref => this.startResizer = ref
|
|
551
619
|
});
|
|
552
620
|
let endResizeDiv = /*#__PURE__*/_jsx("div", {});
|
|
553
|
-
if (endResizable(this.props)) endResizeDiv = /*#__PURE__*/_jsx("div", {
|
|
621
|
+
if (endResizable(this.props) && verticalEdges.bottom) endResizeDiv = /*#__PURE__*/_jsx("div", {
|
|
554
622
|
className: "event-resizer event-end-resizer",
|
|
555
623
|
ref: ref => this.endResizer = ref
|
|
556
624
|
});
|
|
@@ -97,7 +97,8 @@ function HeaderView({
|
|
|
97
97
|
const formatMap = {
|
|
98
98
|
[CellUnit.Week]: config.nonAgendaWeekCellHeaderFormat,
|
|
99
99
|
[CellUnit.Month]: config.nonAgendaMonthCellHeaderFormat,
|
|
100
|
-
[CellUnit.Year]: config.nonAgendaYearCellHeaderFormat
|
|
100
|
+
[CellUnit.Year]: config.nonAgendaYearCellHeaderFormat,
|
|
101
|
+
[CellUnit.Quarter]: config.nonAgendaQuarterCellHeaderFormat
|
|
101
102
|
};
|
|
102
103
|
return formatMap[cellUnitParam] || config.nonAgendaOtherCellHeaderFormat;
|
|
103
104
|
}, [config]);
|
|
@@ -47,7 +47,9 @@ class ResourceEvents extends PureComponent {
|
|
|
47
47
|
// Add vertical selection tracking
|
|
48
48
|
originalStartRowIndex: -1,
|
|
49
49
|
startRowIndex: -1,
|
|
50
|
-
endRowIndex: -1
|
|
50
|
+
endRowIndex: -1,
|
|
51
|
+
// where the dragged item would land (set from the drop target's hover)
|
|
52
|
+
dropPreview: null
|
|
51
53
|
};
|
|
52
54
|
this.supportTouch = false; // 'ontouchstart' in window;
|
|
53
55
|
}
|
|
@@ -81,6 +83,14 @@ class ResourceEvents extends PureComponent {
|
|
|
81
83
|
width: 0
|
|
82
84
|
});
|
|
83
85
|
}
|
|
86
|
+
setDropPreview = dropPreview => {
|
|
87
|
+
if (!Number.isFinite(dropPreview.left) || !Number.isFinite(dropPreview.width)) return;
|
|
88
|
+
const current = this.state.dropPreview;
|
|
89
|
+
if (current?.left === dropPreview.left && current?.width === dropPreview.width) return;
|
|
90
|
+
this.setState({
|
|
91
|
+
dropPreview
|
|
92
|
+
});
|
|
93
|
+
};
|
|
84
94
|
cleanupDragInteraction = () => {
|
|
85
95
|
document.documentElement.removeEventListener('touchmove', this.doDrag, false);
|
|
86
96
|
document.documentElement.removeEventListener('touchend', this.stopDrag, false);
|
|
@@ -574,6 +584,7 @@ class ResourceEvents extends PureComponent {
|
|
|
574
584
|
isStart: isStart,
|
|
575
585
|
isEnd: isEnd,
|
|
576
586
|
isInPopover: false,
|
|
587
|
+
slotId: resourceEvents.slotId,
|
|
577
588
|
left: left,
|
|
578
589
|
width: width,
|
|
579
590
|
top: top,
|
|
@@ -629,13 +640,27 @@ class ResourceEvents extends PureComponent {
|
|
|
629
640
|
}
|
|
630
641
|
}
|
|
631
642
|
});
|
|
643
|
+
|
|
644
|
+
// drop preview: styled box where the item being dragged over this row would be placed
|
|
645
|
+
const {
|
|
646
|
+
dropPreview
|
|
647
|
+
} = this.state;
|
|
648
|
+
const dropPreviewBox = this.props.isOver && this.props.canDrop && config.dropPreviewEnabled !== false && dropPreview ? /*#__PURE__*/_jsx("div", {
|
|
649
|
+
className: `drop-preview${config.dropPreviewClassName ? ` ${config.dropPreviewClassName}` : ''}`,
|
|
650
|
+
style: {
|
|
651
|
+
left: dropPreview.left,
|
|
652
|
+
width: dropPreview.width,
|
|
653
|
+
...config.dropPreviewStyle
|
|
654
|
+
}
|
|
655
|
+
}) : null;
|
|
632
656
|
const eventContainer = /*#__PURE__*/_jsxs("div", {
|
|
633
657
|
ref: this.eventContainerRef,
|
|
634
658
|
className: "event-container",
|
|
659
|
+
"data-slot-id": resourceEvents.slotId,
|
|
635
660
|
style: {
|
|
636
661
|
height: resourceEvents.rowHeight
|
|
637
662
|
},
|
|
638
|
-
children: [selectedArea, sharedSelectedArea, verticalSelectionOverlay, eventList]
|
|
663
|
+
children: [selectedArea, sharedSelectedArea, verticalSelectionOverlay, dropPreviewBox, eventList]
|
|
639
664
|
});
|
|
640
665
|
return /*#__PURE__*/_jsx("tr", {
|
|
641
666
|
children: /*#__PURE__*/_jsx("td", {
|
|
@@ -23,8 +23,6 @@ export default class SchedulerData {
|
|
|
23
23
|
this.scrollToSpecialDayjs = false;
|
|
24
24
|
this.documentWidth = 0;
|
|
25
25
|
this.documentHeight = 0;
|
|
26
|
-
// true once the scheduler measures its own container, so besidesWidth (a window-based offset) is not applied
|
|
27
|
-
this.measuredByContainer = false;
|
|
28
26
|
this.schedulerHeaderHeight = 0;
|
|
29
27
|
this._shouldReloadViewType = false;
|
|
30
28
|
this.version = 0;
|
|
@@ -169,12 +167,6 @@ export default class SchedulerData {
|
|
|
169
167
|
this.bumpVersion();
|
|
170
168
|
}
|
|
171
169
|
}
|
|
172
|
-
setBesidesWidth(besidesWidth) {
|
|
173
|
-
if (besidesWidth >= 0 && this.config.besidesWidth !== besidesWidth) {
|
|
174
|
-
this.config.besidesWidth = besidesWidth;
|
|
175
|
-
this.bumpVersion();
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
170
|
setUnderneathHeight(underneathHeight) {
|
|
179
171
|
if (underneathHeight >= 0 && this.config.underneathHeight !== underneathHeight) {
|
|
180
172
|
this.config.underneathHeight = underneathHeight;
|
|
@@ -389,8 +381,7 @@ export default class SchedulerData {
|
|
|
389
381
|
return !!contentCellWidth.endsWith && contentCellWidth.endsWith('%');
|
|
390
382
|
}
|
|
391
383
|
getSchedulerWidth() {
|
|
392
|
-
const
|
|
393
|
-
const baseWidth = this.documentWidth - besidesWidth > 0 ? this.documentWidth - besidesWidth : 0;
|
|
384
|
+
const baseWidth = this.documentWidth > 0 ? this.documentWidth : 0;
|
|
394
385
|
return this.isSchedulerResponsive() ? parseInt(baseWidth * Number(this.config.schedulerWidth.slice(0, -1)) / 100, 10) : this.config.schedulerWidth;
|
|
395
386
|
}
|
|
396
387
|
getSchedulerHeight() {
|
|
@@ -641,12 +632,6 @@ export default class SchedulerData {
|
|
|
641
632
|
const configProperty = viewConfigMap[this.viewType] || 'customCellWidth';
|
|
642
633
|
return this.config[configProperty];
|
|
643
634
|
}
|
|
644
|
-
_setMeasuredByContainer(measuredByContainer) {
|
|
645
|
-
if (this.measuredByContainer !== measuredByContainer) {
|
|
646
|
-
this.measuredByContainer = measuredByContainer;
|
|
647
|
-
this.bumpVersion();
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
635
|
_setDocumentWidth(documentWidth) {
|
|
651
636
|
if (documentWidth >= 0 && documentWidth !== this.documentWidth) {
|
|
652
637
|
this.documentWidth = documentWidth;
|
|
@@ -857,6 +842,14 @@ export default class SchedulerData {
|
|
|
857
842
|
});
|
|
858
843
|
header = header.add(1, 'months').startOf('month');
|
|
859
844
|
}
|
|
845
|
+
} else if (this.cellUnit === CellUnit.Quarter) {
|
|
846
|
+
while (header >= start && header <= end) {
|
|
847
|
+
const time = header.format(DATE_FORMAT);
|
|
848
|
+
timeSlots.push({
|
|
849
|
+
time
|
|
850
|
+
});
|
|
851
|
+
header = header.add(1, 'quarters').startOf('quarter');
|
|
852
|
+
}
|
|
860
853
|
} else if (this.cellUnit === CellUnit.Year) {
|
|
861
854
|
while (header >= start && header <= end) {
|
|
862
855
|
const time = header.format(DATE_FORMAT);
|
|
@@ -1057,7 +1050,8 @@ export default class SchedulerData {
|
|
|
1057
1050
|
[CellUnit.Hour]: 'minutes',
|
|
1058
1051
|
[CellUnit.Week]: 'weeks',
|
|
1059
1052
|
[CellUnit.Month]: 'months',
|
|
1060
|
-
[CellUnit.Year]: 'years'
|
|
1053
|
+
[CellUnit.Year]: 'years',
|
|
1054
|
+
[CellUnit.Quarter]: 'quarters'
|
|
1061
1055
|
}[this.cellUnit] || 'days';
|
|
1062
1056
|
return {
|
|
1063
1057
|
amount: unit === 'minutes' ? this.config.minuteStep : 1,
|
package/dist/components/index.js
CHANGED
|
@@ -135,7 +135,6 @@ function Scheduler(props) {
|
|
|
135
135
|
const update = () => {
|
|
136
136
|
schedulerData.beginBatch();
|
|
137
137
|
try {
|
|
138
|
-
schedulerData._setMeasuredByContainer(true);
|
|
139
138
|
schedulerData._setDocumentWidth(widthEl.clientWidth);
|
|
140
139
|
if (responsiveByParent) {
|
|
141
140
|
schedulerData._setDocumentHeight(parentEl ? getInnerHeight(parentEl) : document.documentElement.clientHeight);
|
package/dist/config/default.js
CHANGED
package/dist/config/scheduler.js
CHANGED
|
@@ -11,7 +11,6 @@ import { SummaryPos, ViewType } from './default';
|
|
|
11
11
|
*/
|
|
12
12
|
export default {
|
|
13
13
|
schedulerWidth: '100%',
|
|
14
|
-
besidesWidth: 20,
|
|
15
14
|
underneathHeight: 20,
|
|
16
15
|
schedulerMaxHeight: 0,
|
|
17
16
|
tableHeaderHeight: 40,
|
|
@@ -59,6 +58,10 @@ export default {
|
|
|
59
58
|
// month row above the day cells (month names spanning their days), like the week number row
|
|
60
59
|
showMonthRow: false,
|
|
61
60
|
monthRowHeight: 24,
|
|
61
|
+
// box shown on the row/cell where a dragged event, task or resource would be placed
|
|
62
|
+
dropPreviewEnabled: true,
|
|
63
|
+
dropPreviewClassName: '',
|
|
64
|
+
dropPreviewStyle: undefined,
|
|
62
65
|
startResizable: true,
|
|
63
66
|
endResizable: true,
|
|
64
67
|
movable: true,
|
|
@@ -89,6 +92,7 @@ export default {
|
|
|
89
92
|
nonAgendaWeekCellHeaderFormat: 'ww/YYYY',
|
|
90
93
|
nonAgendaMonthCellHeaderFormat: 'MMM YYYY',
|
|
91
94
|
nonAgendaYearCellHeaderFormat: 'YYYY',
|
|
95
|
+
nonAgendaQuarterCellHeaderFormat: '[Q]Q YYYY',
|
|
92
96
|
nonAgendaOtherCellHeaderFormat: 'ddd M/D',
|
|
93
97
|
minuteStep: 30,
|
|
94
98
|
views: [{
|
package/dist/css/style.css
CHANGED
|
@@ -165,6 +165,18 @@
|
|
|
165
165
|
position: relative;
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
+
.rbs .drop-preview {
|
|
169
|
+
position: absolute;
|
|
170
|
+
top: 0;
|
|
171
|
+
bottom: 0;
|
|
172
|
+
z-index: 3;
|
|
173
|
+
box-sizing: border-box;
|
|
174
|
+
pointer-events: none;
|
|
175
|
+
border: 2px dashed #1677ff;
|
|
176
|
+
border-radius: 4px;
|
|
177
|
+
background: rgba(22, 119, 255, 0.14);
|
|
178
|
+
}
|
|
179
|
+
|
|
168
180
|
.rbs .event-item {
|
|
169
181
|
margin: 1px 0;
|
|
170
182
|
width: 100%;
|
|
@@ -277,6 +289,26 @@
|
|
|
277
289
|
right: -1px;
|
|
278
290
|
}
|
|
279
291
|
|
|
292
|
+
/* Vertical resource view: events resize from their top / bottom edge */
|
|
293
|
+
.rbs.vertical-view .event-resizer {
|
|
294
|
+
width: auto;
|
|
295
|
+
height: 7px;
|
|
296
|
+
left: -1px;
|
|
297
|
+
right: -1px;
|
|
298
|
+
top: auto;
|
|
299
|
+
bottom: auto;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.rbs.vertical-view .event-start-resizer {
|
|
303
|
+
cursor: n-resize;
|
|
304
|
+
top: -1px;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
.rbs.vertical-view .event-end-resizer {
|
|
308
|
+
cursor: s-resize;
|
|
309
|
+
bottom: -1px;
|
|
310
|
+
}
|
|
311
|
+
|
|
280
312
|
/* Buttons */
|
|
281
313
|
.rbs-txt-btn-dis {
|
|
282
314
|
border: none;
|
package/dist/index.d.ts
CHANGED
|
@@ -233,6 +233,10 @@ export class DnDSource {
|
|
|
233
233
|
export enum CellUnit {
|
|
234
234
|
Day,
|
|
235
235
|
Hour,
|
|
236
|
+
Week,
|
|
237
|
+
Month,
|
|
238
|
+
Year,
|
|
239
|
+
Quarter,
|
|
236
240
|
}
|
|
237
241
|
|
|
238
242
|
export enum ViewType {
|
|
@@ -330,8 +334,6 @@ export interface State<EventType extends EventItem = EventItem> {
|
|
|
330
334
|
|
|
331
335
|
export interface SchedulerDataConfig {
|
|
332
336
|
schedulerWidth?: `${number}%`;
|
|
333
|
-
/** @deprecated The scheduler measures its own container now, so this offset is ignored. */
|
|
334
|
-
besidesWidth?: number;
|
|
335
337
|
underneathHeight?: number;
|
|
336
338
|
schedulerMaxHeight?: number;
|
|
337
339
|
tableHeaderHeight?: number;
|
|
@@ -420,6 +422,12 @@ export interface SchedulerDataConfig {
|
|
|
420
422
|
showWeekNumber?: boolean;
|
|
421
423
|
weekNumberRowHeight?: number;
|
|
422
424
|
showMonthRow?: boolean;
|
|
425
|
+
/** Show a box where the dragged event / task / resource would be placed (default true). */
|
|
426
|
+
dropPreviewEnabled?: boolean;
|
|
427
|
+
/** Extra class name for the drop preview box. */
|
|
428
|
+
dropPreviewClassName?: string;
|
|
429
|
+
/** Inline style for the drop preview box, e.g. `{ border: '2px dashed green' }`. */
|
|
430
|
+
dropPreviewStyle?: React.CSSProperties;
|
|
423
431
|
monthRowHeight?: number;
|
|
424
432
|
weekNumberLabel?: string;
|
|
425
433
|
headerBorderColor?: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-big-schedule",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.0.0",
|
|
4
4
|
"description": "React Big Schedule is a powerful and intuitive scheduler and resource planning solution built with React. Seamlessly integrate this modern browser-compatible component into your applications to effectively manage time, appointments, and resources. With drag-and-drop functionality, interactive UI, and granular views, react-big-schedule empowers users to effortlessly schedule and allocate resources with precision. Enhance productivity and streamline your workflow with this React-based solution, designed to optimize time management and simplify calendar-based operations. Perfect for applications requiring advanced scheduling capabilities, react-big-schedule offers a seamless and intuitive experience for managing appointments, resource allocation, and time slots. Unlock the potential of your React projects with react-big-schedule and revolutionize the way you handle scheduling and resource planning. It is the updated version of react-big-scheduler",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react-big-schedule",
|