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 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`; `besidesWidth` is no longer needed).
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 => this.resolveDragObjFunc(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 (config.relativeMove) {
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 besidesWidth = this.measuredByContainer ? 0 : this.config.besidesWidth;
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,
@@ -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);
@@ -16,7 +16,8 @@ export const CellUnit = {
16
16
  Hour: 1,
17
17
  Week: 2,
18
18
  Month: 3,
19
- Year: 4
19
+ Year: 4,
20
+ Quarter: 5
20
21
  };
21
22
  export const SummaryPos = {
22
23
  Top: 0,
@@ -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: [{
@@ -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": "7.1.1-beta.2",
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",