react-big-schedule 6.1.1-beta.0 → 7.1.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 +49 -31
- package/dist/components/EventItem.js +4 -2
- package/dist/components/ResourceEvents.js +25 -7
- package/dist/components/SchedulerData.js +22 -6
- package/dist/components/SchedulerHeader.js +1 -0
- package/dist/components/index.js +68 -63
- package/dist/config/scheduler.js +1 -1
- package/dist/css/style.css +8 -0
- package/dist/index.d.ts +5 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ Unlock the potential of your React projects with react-big-schedule and revoluti
|
|
|
17
17
|
|
|
18
18
|
### This project code is forked from:
|
|
19
19
|
|
|
20
|
-
- [react-big-scheduler](https://
|
|
20
|
+
- [react-big-scheduler](https://github.com/StephenChou1017/react-big-scheduler).
|
|
21
21
|
- [react-big-scheduler-stch](https://github.com/hbatalhaStch/react-big-scheduler).
|
|
22
22
|
|
|
23
23
|
## Installation
|
|
@@ -93,7 +93,7 @@ Special thanks to the following contributors:
|
|
|
93
93
|
|
|
94
94
|
We would like to acknowledge the following projects for their inspiration and contributions:
|
|
95
95
|
|
|
96
|
-
- [react-big-scheduler](https://
|
|
96
|
+
- [react-big-scheduler](https://github.com/StephenChou1017/react-big-scheduler).
|
|
97
97
|
- [react-big-scheduler-stch](https://github.com/hbatalhaStch/react-big-scheduler).
|
|
98
98
|
|
|
99
99
|
## Roadmap
|
|
@@ -240,10 +240,10 @@ constructor(date=dayjs().format(DATE_FORMAT), viewType = ViewType.Week,
|
|
|
240
240
|
view type, and will render the time window of the `2022-12` month in `ViewType.Month` view type.
|
|
241
241
|
- `viewType` is the initial view type, now Scheduler supports `Day`, `Week`, `Month`, `Quarter`, `Year` 5 built-in view types,
|
|
242
242
|
in addition Scheduler now supports `Custom`, `Custom1`, `Custom2` 3 custom view types at the same time, in which you can control
|
|
243
|
-
the time window yourself, refer to [this example](https://
|
|
243
|
+
the time window yourself, refer to [this example](https://react-big-schedule.vercel.app/custom-time). `viewType`,
|
|
244
244
|
`showAgenda` and `isEventPerspective` are a group which should be contained in the SchedulerData.config.views array,
|
|
245
245
|
and they together decide which view should be rendered. When `showAgenda` and `isEventPerspective` are both `false`,
|
|
246
|
-
Scheduler will render the resource view, refer to [this example](https://
|
|
246
|
+
Scheduler will render the resource view, refer to [this example](https://react-big-schedule.vercel.app/basic).
|
|
247
247
|
- `showAgenda` is a bool value, if true, Scheduler will display the agenda view of current view type. Agenda view is
|
|
248
248
|
read only.
|
|
249
249
|
- `isEventPerspective` is a bool value, if true, Scheduler will display the task view of current view type. In
|
|
@@ -410,7 +410,7 @@ addResource(resource);
|
|
|
410
410
|
```
|
|
411
411
|
|
|
412
412
|
Add the `resource` to the `SchedulerData.resources`, make sure that `resource.id` is not duplicated. Refer
|
|
413
|
-
to [this example](https://
|
|
413
|
+
to [this example](https://react-big-schedule.vercel.app/customization).
|
|
414
414
|
|
|
415
415
|
#### addEventGroup
|
|
416
416
|
|
|
@@ -529,7 +529,7 @@ getViewEndDate();
|
|
|
529
529
|
|
|
530
530
|
Returns a dayjs object with the endDate of the currently selected view.
|
|
531
531
|
|
|
532
|
-
### Locale support(Refer to [this example](https://
|
|
532
|
+
### Locale support(Refer to [this example](https://react-big-schedule.vercel.app/custom-time) for details.)
|
|
533
533
|
|
|
534
534
|
#### SchedulerData.config.resourceName
|
|
535
535
|
|
|
@@ -567,25 +567,26 @@ Used to resolve the locale string of date label of Scheduler component.(Refer to
|
|
|
567
567
|
|
|
568
568
|
#### schedulerWidth
|
|
569
569
|
|
|
570
|
-
The width of Scheduler. Scheduler uses responsive layout so schedulerWidth should be a percentage
|
|
571
|
-
|
|
572
|
-
`
|
|
573
|
-
|
|
574
|
-
|
|
570
|
+
The width of Scheduler. Scheduler uses responsive layout so schedulerWidth should be a percentage.
|
|
571
|
+
|
|
572
|
+
The Scheduler renders inside a container that is `width: 100%` and `max-height: 100%` of its parent, so it fills the parent
|
|
573
|
+
and follows it when resized (it measures that container with a `ResizeObserver`; `besidesWidth` is no longer needed).
|
|
574
|
+
Resource columns keep their configured width and the time columns stretch to fill the rest.
|
|
575
|
+
|
|
576
|
+
To size it differently, pass `className` (e.g. tailwind classes) or `style` to the `Scheduler` component, for example
|
|
577
|
+
`<Scheduler className="w-1/2 max-h-[600px]" ... />`. Those win over the defaults, which have zero CSS specificity.
|
|
578
|
+
When the parent has a fixed height the Scheduler scrolls inside it, otherwise it grows to fit its rows.
|
|
575
579
|
|
|
576
580
|
#### responsiveByParent
|
|
577
581
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
in case it is not passed resposiveness will fall back to being determined by the window width).
|
|
581
|
-
Meaning:
|
|
582
|
-
`SchedulerData.documentWidth` is the width of the parent and will change automatically when resized
|
|
582
|
+
Only needed to make the Scheduler's *height* follow a parent element: pass that element through the `parentRef` prop of the
|
|
583
|
+
Scheduler component. Without `parentRef` the window height is used.
|
|
583
584
|
|
|
584
585
|
#### schedulerMaxHeight
|
|
585
586
|
|
|
586
587
|
The max height of Scheduler. If the desired height is bigger than the max height, the header row of Scheduler will be
|
|
587
588
|
frozen and vertical scroll bar will appear, but this won't happen when the max height is set to `0`. Refer
|
|
588
|
-
to [this example](https://
|
|
589
|
+
to [this example](https://react-big-schedule.vercel.app/customization).
|
|
589
590
|
|
|
590
591
|
**If you are using `responsiveByParent`, you can omit this setting or set it to `0` to ensure the Scheduler height conforms to the parent container.**
|
|
591
592
|
When conforming to the parent container, the Scheduler will fill the available space and scroll bars will appear as needed.
|
|
@@ -619,7 +620,7 @@ Width of Scheduler table cells in resource view and task view of different view
|
|
|
619
620
|
#### dayMaxEvents, weekMaxEvents, monthMaxEvents, yearMaxEvents, quarterMaxEvents
|
|
620
621
|
|
|
621
622
|
Max events count of a cell in resource view and task view of different view types. A '+N more' will appear when exceeded.
|
|
622
|
-
Refer to [this example](https://
|
|
623
|
+
Refer to [this example](https://react-big-schedule.vercel.app/add-more).
|
|
623
624
|
|
|
624
625
|
#### eventItemHeight
|
|
625
626
|
|
|
@@ -696,13 +697,13 @@ Controls whether to create new event item in resource view and task view.
|
|
|
696
697
|
#### crossResourceMove
|
|
697
698
|
|
|
698
699
|
Controls whether to cross-slot move an event item in resource view and task view. If `false`, the `slotId` and `slotName`
|
|
699
|
-
won't change in the `moveEvent` method. Refer to [this example](https://
|
|
700
|
+
won't change in the `moveEvent` method. Refer to [this example](https://react-big-schedule.vercel.app/customization).
|
|
700
701
|
|
|
701
702
|
#### checkConflict
|
|
702
703
|
|
|
703
704
|
Controls whether to check conflicts when creating, resizing or moving an event item in resource view and task view. If
|
|
704
705
|
`true`, Scheduler will call the `conflictOccurred` function if given. Refer to
|
|
705
|
-
[this example](https://
|
|
706
|
+
[this example](https://react-big-schedule.vercel.app/customization).
|
|
706
707
|
|
|
707
708
|
#### scrollToSpecialDayjsEnabled
|
|
708
709
|
|
|
@@ -792,7 +793,7 @@ getSummary(schedulerData, headerEvents, slotId, slotName, headerStart, headerEnd
|
|
|
792
793
|
```
|
|
793
794
|
|
|
794
795
|
Method that defines the summary text displayed in the Scheduler cells.Refer
|
|
795
|
-
to [this example](https://
|
|
796
|
+
to [this example](https://react-big-schedule.vercel.app/customization).
|
|
796
797
|
|
|
797
798
|
#### getCustomDateFunc
|
|
798
799
|
|
|
@@ -801,7 +802,7 @@ getCustomDate(schedulerData, num, (date = undefined));
|
|
|
801
802
|
```
|
|
802
803
|
|
|
803
804
|
Method that controls the start and end of time window when current view type is Custom, Custom1 or Custom2.Refer
|
|
804
|
-
to [this example](https://
|
|
805
|
+
to [this example](https://react-big-schedule.vercel.app/custom-time).
|
|
805
806
|
|
|
806
807
|
#### getNonAgendaViewBodyCellBgColorFunc
|
|
807
808
|
|
|
@@ -837,6 +838,23 @@ parentRef: PropTypes.object;
|
|
|
837
838
|
|
|
838
839
|
ref of the component that is the parent of the Scheduler component
|
|
839
840
|
|
|
841
|
+
#### className
|
|
842
|
+
|
|
843
|
+
```js
|
|
844
|
+
className: PropTypes.string;
|
|
845
|
+
```
|
|
846
|
+
|
|
847
|
+
Class names for the Scheduler's container (e.g. tailwind classes such as `w-1/2 max-h-[600px]`). They override the default
|
|
848
|
+
`width: 100%` / `max-height: 100%`.
|
|
849
|
+
|
|
850
|
+
#### style
|
|
851
|
+
|
|
852
|
+
```js
|
|
853
|
+
style: PropTypes.object;
|
|
854
|
+
```
|
|
855
|
+
|
|
856
|
+
Inline style for the Scheduler's container.
|
|
857
|
+
|
|
840
858
|
#### prevClick
|
|
841
859
|
|
|
842
860
|
```js
|
|
@@ -932,7 +950,7 @@ rightCustomHeader: PropTypes.object;
|
|
|
932
950
|
```
|
|
933
951
|
|
|
934
952
|
Component you need to put in the Scheduler header, it could be a div or a react component. Refer
|
|
935
|
-
to [this example](https://
|
|
953
|
+
to [this example](https://react-big-schedule.vercel.app/customization).
|
|
936
954
|
|
|
937
955
|
#### conflictOccurred
|
|
938
956
|
|
|
@@ -951,7 +969,7 @@ eventItemTemplateResolver: PropTypes.func;
|
|
|
951
969
|
eventItemTemplateResolver(schedulerData, event, bgColor, isStart, isEnd, mustAddCssClass, mustBeHeight, agendaMaxEventWidth);
|
|
952
970
|
```
|
|
953
971
|
|
|
954
|
-
Use this function, you can customize the event style. Refer to [this example](https://
|
|
972
|
+
Use this function, you can customize the event style. Refer to [this example](https://react-big-schedule.vercel.app/customization).
|
|
955
973
|
|
|
956
974
|
### eventItemPopoverTemplateResolver
|
|
957
975
|
|
|
@@ -960,7 +978,7 @@ eventItemPopoverTemplateResolver: PropTypes.func;
|
|
|
960
978
|
eventItemPopoverTemplateResolver(schedulerData, eventItem, title, start, end, statusColor);
|
|
961
979
|
```
|
|
962
980
|
|
|
963
|
-
Use this function, you can customize the event's popover style. Refer to [this example](https://
|
|
981
|
+
Use this function, you can customize the event's popover style. Refer to [this example](https://react-big-schedule.vercel.app/customization).
|
|
964
982
|
|
|
965
983
|
#### slotItemTemplateResolver
|
|
966
984
|
|
|
@@ -978,7 +996,7 @@ nonAgendaCellHeaderTemplateResolver: PropTypes.func;
|
|
|
978
996
|
nonAgendaCellHeaderTemplateResolver(schedulerData, item, formattedDateItems, style);
|
|
979
997
|
```
|
|
980
998
|
|
|
981
|
-
Use this function, you can customize the table header cell style. Refer to [this example](https://
|
|
999
|
+
Use this function, you can customize the table header cell style. Refer to [this example](https://react-big-schedule.vercel.app/customization).
|
|
982
1000
|
|
|
983
1001
|
#### onScrollLeft, onScrollRight
|
|
984
1002
|
|
|
@@ -989,7 +1007,7 @@ onScrollRight: PropTypes.func;
|
|
|
989
1007
|
onScrollRight(schedulerData, schedulerContent, maxScrollLeft);
|
|
990
1008
|
```
|
|
991
1009
|
|
|
992
|
-
Callback function fired when the scheduler content div scrolls to leftmost or rightmost. Refer to [this example](https://
|
|
1010
|
+
Callback function fired when the scheduler content div scrolls to leftmost or rightmost. Refer to [this example](https://react-big-schedule.vercel.app/customization).
|
|
993
1011
|
|
|
994
1012
|
#### onScrollTop, onScrollBottom
|
|
995
1013
|
|
|
@@ -1000,7 +1018,7 @@ onScrollBottom: PropTypes.func;
|
|
|
1000
1018
|
onScrollBottom(schedulerData, schedulerContent, maxScrollTop);
|
|
1001
1019
|
```
|
|
1002
1020
|
|
|
1003
|
-
Callback function fired when the scheduler content div scrolls to topmost or bottommost. Refer to [this example](https://
|
|
1021
|
+
Callback function fired when the scheduler content div scrolls to topmost or bottommost. Refer to [this example](https://react-big-schedule.vercel.app/customization).
|
|
1004
1022
|
|
|
1005
1023
|
#### slotClickedFunc
|
|
1006
1024
|
|
|
@@ -1009,7 +1027,7 @@ slotClickedFunc: PropTypes.func;
|
|
|
1009
1027
|
```
|
|
1010
1028
|
|
|
1011
1029
|
If it's set, slots will be clickable, and will fire this function when a slot is clicked. Refer
|
|
1012
|
-
to [this example](https://
|
|
1030
|
+
to [this example](https://react-big-schedule.vercel.app/customization).
|
|
1013
1031
|
|
|
1014
1032
|
#### dndSources
|
|
1015
1033
|
|
|
@@ -1019,7 +1037,7 @@ dndSources: PropTypes.array;
|
|
|
1019
1037
|
|
|
1020
1038
|
DnDSource array that registered to Scheduler. Use [DnDSource](https://github.com/ansulagrawal/react-big-schedule/blob/master/src/components/DnDSource.js),
|
|
1021
1039
|
we can simplify the drag and drop coding in React-Big-Scheduler. Refer
|
|
1022
|
-
to [this example](https://
|
|
1040
|
+
to [this example](https://react-big-schedule.vercel.app/drag-and-drop).
|
|
1023
1041
|
|
|
1024
1042
|
#### onSetAddMoreState
|
|
1025
1043
|
|
|
@@ -1030,7 +1048,7 @@ onSetAddMoreState(newState);
|
|
|
1030
1048
|
|
|
1031
1049
|
Callback function fired when a '+N more' is clicked, is used to control the visibility and the position of the `AddMorePopover`.
|
|
1032
1050
|
`newState` is a json such as {headerItem: headerItem, left: 20, top: 20, height: 100}. Refer
|
|
1033
|
-
to [this example](https://
|
|
1051
|
+
to [this example](https://react-big-schedule.vercel.app/add-more).
|
|
1034
1052
|
|
|
1035
1053
|
#### subtitleGetter
|
|
1036
1054
|
|
|
@@ -720,7 +720,8 @@ EventItem.propTypes = {
|
|
|
720
720
|
viewEvent2Click: PropTypes.func,
|
|
721
721
|
viewEvent2Text: PropTypes.string,
|
|
722
722
|
conflictOccurred: PropTypes.func,
|
|
723
|
-
eventItemTemplateResolver: PropTypes.func
|
|
723
|
+
eventItemTemplateResolver: PropTypes.func,
|
|
724
|
+
eventItemPopoverTemplateResolver: PropTypes.func
|
|
724
725
|
};
|
|
725
726
|
EventItem.defaultProps = {
|
|
726
727
|
isDragging: undefined,
|
|
@@ -736,5 +737,6 @@ EventItem.defaultProps = {
|
|
|
736
737
|
viewEvent2Click: undefined,
|
|
737
738
|
viewEvent2Text: undefined,
|
|
738
739
|
conflictOccurred: undefined,
|
|
739
|
-
eventItemTemplateResolver: undefined
|
|
740
|
+
eventItemTemplateResolver: undefined,
|
|
741
|
+
eventItemPopoverTemplateResolver: undefined
|
|
740
742
|
};
|
|
@@ -29,6 +29,7 @@ class ResourceEvents extends PureComponent {
|
|
|
29
29
|
viewEvent2Text: PropTypes.string,
|
|
30
30
|
newEvent: PropTypes.func,
|
|
31
31
|
eventItemTemplateResolver: PropTypes.func,
|
|
32
|
+
eventItemPopoverTemplateResolver: PropTypes.func,
|
|
32
33
|
onSelectionChange: PropTypes.func,
|
|
33
34
|
isRowSelected: PropTypes.bool,
|
|
34
35
|
selectionPreview: PropTypes.shape({
|
|
@@ -268,7 +269,7 @@ class ResourceEvents extends PureComponent {
|
|
|
268
269
|
rightIndex
|
|
269
270
|
} = this.state;
|
|
270
271
|
this.cleanupDragInteraction();
|
|
271
|
-
|
|
272
|
+
const resetSelection = () => {
|
|
272
273
|
this.setState({
|
|
273
274
|
startX: 0,
|
|
274
275
|
leftIndex: 0,
|
|
@@ -284,17 +285,33 @@ class ResourceEvents extends PureComponent {
|
|
|
284
285
|
left: 0,
|
|
285
286
|
width: 0
|
|
286
287
|
});
|
|
288
|
+
};
|
|
289
|
+
if (headers.length === 0 || !resourceEvents.headerItems || resourceEvents.headerItems.length === 0) {
|
|
290
|
+
resetSelection();
|
|
287
291
|
return;
|
|
288
292
|
}
|
|
293
|
+
|
|
294
|
+
// a cell width of 0 (container not measured yet) makes the drag indexes NaN or Infinity
|
|
295
|
+
const toIndex = (value, fallback) => Number.isFinite(value) ? value : fallback;
|
|
289
296
|
const maxLeftIndex = headers.length - 1;
|
|
290
|
-
const safeLeftIndex = Math.max(0, Math.min(leftIndex, maxLeftIndex));
|
|
297
|
+
const safeLeftIndex = Math.max(0, Math.min(toIndex(leftIndex, 0), maxLeftIndex));
|
|
291
298
|
const maxRightIndex = Math.min(headers.length, resourceEvents.headerItems.length);
|
|
292
|
-
let safeRightIndex = Math.max(1, rightIndex, safeLeftIndex + 1);
|
|
299
|
+
let safeRightIndex = Math.max(1, toIndex(rightIndex, 1), safeLeftIndex + 1);
|
|
293
300
|
safeRightIndex = Math.min(safeRightIndex, maxRightIndex);
|
|
301
|
+
|
|
302
|
+
// headers and headerItems can be out of step while the view rebuilds; drop the selection instead of throwing
|
|
303
|
+
if (!headers[safeLeftIndex] || !resourceEvents.headerItems[safeRightIndex - 1]) {
|
|
304
|
+
resetSelection();
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
const isVertical = schedulerData.isVerticalResourceView();
|
|
308
|
+
// vertical view: rows are time slots and columns are resources, so the roles swap
|
|
309
|
+
const selectedRowIds = this.getSelectedResourceIds();
|
|
294
310
|
let startTime, endTime;
|
|
295
|
-
if (
|
|
296
|
-
startTime = resourceEvents.slotId;
|
|
297
|
-
|
|
311
|
+
if (isVertical) {
|
|
312
|
+
startTime = selectedRowIds[0] ?? resourceEvents.slotId;
|
|
313
|
+
const lastRowId = selectedRowIds[selectedRowIds.length - 1] ?? startTime;
|
|
314
|
+
endTime = localeDayjs(new Date(lastRowId)).add(config.minuteStep, 'minutes').format(DATETIME_FORMAT);
|
|
298
315
|
} else {
|
|
299
316
|
startTime = headers[safeLeftIndex].time;
|
|
300
317
|
endTime = resourceEvents.headerItems[safeRightIndex - 1].end;
|
|
@@ -304,7 +321,7 @@ class ResourceEvents extends PureComponent {
|
|
|
304
321
|
}
|
|
305
322
|
|
|
306
323
|
// Get selected resource IDs
|
|
307
|
-
const selectedResourceIds =
|
|
324
|
+
const selectedResourceIds = isVertical ? headers.slice(safeLeftIndex, safeRightIndex).map(header => header.id) : selectedRowIds;
|
|
308
325
|
const slotId = selectedResourceIds.length > 0 ? selectedResourceIds[0] : resourceEvents.slotId;
|
|
309
326
|
const slotName = selectedResourceIds.length > 0 ? schedulerData.getResourceById(slotId)?.name || slotId : resourceEvents.slotName;
|
|
310
327
|
this.setState({
|
|
@@ -571,6 +588,7 @@ class ResourceEvents extends PureComponent {
|
|
|
571
588
|
viewEvent2Click: this.props.viewEvent2Click,
|
|
572
589
|
viewEvent2Text: this.props.viewEvent2Text,
|
|
573
590
|
eventItemTemplateResolver: this.props.eventItemTemplateResolver,
|
|
591
|
+
eventItemPopoverTemplateResolver: this.props.eventItemPopoverTemplateResolver,
|
|
574
592
|
subtitleGetter: this.props.subtitleGetter,
|
|
575
593
|
updateEventStart: this.props.updateEventStart,
|
|
576
594
|
updateEventEnd: this.props.updateEventEnd,
|
|
@@ -23,6 +23,8 @@ 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;
|
|
26
28
|
this.schedulerHeaderHeight = 0;
|
|
27
29
|
this._shouldReloadViewType = false;
|
|
28
30
|
this.version = 0;
|
|
@@ -370,7 +372,8 @@ export default class SchedulerData {
|
|
|
370
372
|
return !!contentCellWidth.endsWith && contentCellWidth.endsWith('%');
|
|
371
373
|
}
|
|
372
374
|
getSchedulerWidth() {
|
|
373
|
-
const
|
|
375
|
+
const besidesWidth = this.measuredByContainer ? 0 : this.config.besidesWidth;
|
|
376
|
+
const baseWidth = this.documentWidth - besidesWidth > 0 ? this.documentWidth - besidesWidth : 0;
|
|
374
377
|
return this.isSchedulerResponsive() ? parseInt(baseWidth * Number(this.config.schedulerWidth.slice(0, -1)) / 100, 10) : this.config.schedulerWidth;
|
|
375
378
|
}
|
|
376
379
|
getSchedulerHeight() {
|
|
@@ -385,10 +388,16 @@ export default class SchedulerData {
|
|
|
385
388
|
// fallback to max height if set, else just use base height
|
|
386
389
|
return this.config.schedulerMaxHeight || validBaseHeight;
|
|
387
390
|
}
|
|
388
|
-
|
|
391
|
+
|
|
392
|
+
// configured resource column width in px (config may be a percentage of the scheduler width)
|
|
393
|
+
_getConfiguredResourceTableWidth() {
|
|
389
394
|
const resourceTableConfigWidth = this.getResourceTableConfigWidth();
|
|
395
|
+
return this.isResourceViewResponsive() ? parseInt(this.getSchedulerWidth() * Number(resourceTableConfigWidth.slice(0, -1)) / 100, 10) : resourceTableConfigWidth;
|
|
396
|
+
}
|
|
397
|
+
getResourceTableWidth() {
|
|
390
398
|
const schedulerWidth = this.getSchedulerWidth();
|
|
391
|
-
let resourceTableWidth = this.
|
|
399
|
+
let resourceTableWidth = this._getConfiguredResourceTableWidth();
|
|
400
|
+
// only the few px left after the cells are stretched go to the resource column
|
|
392
401
|
if (this.isSchedulerResponsive() && this.getContentTableWidth() + resourceTableWidth < schedulerWidth) resourceTableWidth = schedulerWidth - this.getContentTableWidth();
|
|
393
402
|
return resourceTableWidth;
|
|
394
403
|
}
|
|
@@ -397,9 +406,10 @@ export default class SchedulerData {
|
|
|
397
406
|
const schedulerWidth = this.getSchedulerWidth();
|
|
398
407
|
const cellWidth = this.isContentViewResponsive() ? parseInt(schedulerWidth * Number(contentCellConfigWidth.slice(0, -1)) / 100, 10) : contentCellConfigWidth;
|
|
399
408
|
|
|
400
|
-
//
|
|
401
|
-
if (this.
|
|
402
|
-
const
|
|
409
|
+
// stretch the cells to fill the container instead of widening the resource column
|
|
410
|
+
if (this.isSchedulerResponsive() && !this.showAgenda && this.headers?.length) {
|
|
411
|
+
const resourceWidth = this.config.resourceViewEnabled === false ? 0 : this._getConfiguredResourceTableWidth();
|
|
412
|
+
const fillWidth = Math.floor((schedulerWidth - resourceWidth) / this.headers.length);
|
|
403
413
|
return Math.max(cellWidth, fillWidth);
|
|
404
414
|
}
|
|
405
415
|
return cellWidth;
|
|
@@ -603,6 +613,12 @@ export default class SchedulerData {
|
|
|
603
613
|
const configProperty = viewConfigMap[this.viewType] || 'customCellWidth';
|
|
604
614
|
return this.config[configProperty];
|
|
605
615
|
}
|
|
616
|
+
_setMeasuredByContainer(measuredByContainer) {
|
|
617
|
+
if (this.measuredByContainer !== measuredByContainer) {
|
|
618
|
+
this.measuredByContainer = measuredByContainer;
|
|
619
|
+
this.bumpVersion();
|
|
620
|
+
}
|
|
621
|
+
}
|
|
606
622
|
_setDocumentWidth(documentWidth) {
|
|
607
623
|
if (documentWidth >= 0 && documentWidth !== this.documentWidth) {
|
|
608
624
|
this.documentWidth = documentWidth;
|
package/dist/components/index.js
CHANGED
|
@@ -41,11 +41,21 @@ const initDndContext = (schedulerData, dndSources) => {
|
|
|
41
41
|
* @returns {JSX.Element} The root scheduler table element containing the rendered scheduler UI.
|
|
42
42
|
*/
|
|
43
43
|
|
|
44
|
+
// content-box height: clientHeight includes the padding, which the scheduler cannot use
|
|
45
|
+
const getInnerHeight = el => {
|
|
46
|
+
const {
|
|
47
|
+
paddingTop,
|
|
48
|
+
paddingBottom
|
|
49
|
+
} = window.getComputedStyle(el);
|
|
50
|
+
return el.clientHeight - (parseFloat(paddingTop) || 0) - (parseFloat(paddingBottom) || 0);
|
|
51
|
+
};
|
|
44
52
|
function Scheduler(props) {
|
|
45
53
|
const {
|
|
46
54
|
schedulerData,
|
|
47
55
|
dndSources,
|
|
48
56
|
parentRef,
|
|
57
|
+
className,
|
|
58
|
+
style,
|
|
49
59
|
prevClick,
|
|
50
60
|
nextClick,
|
|
51
61
|
onViewChange,
|
|
@@ -72,6 +82,7 @@ function Scheduler(props) {
|
|
|
72
82
|
});
|
|
73
83
|
const [, setRenderTrigger] = useState(0);
|
|
74
84
|
const schedulerRootRef = useRef(null);
|
|
85
|
+
const containerRef = useRef(null);
|
|
75
86
|
|
|
76
87
|
// Layout/header refs - declare before setSchedulerHeaderRef useCallback
|
|
77
88
|
const schedulerHeaderRef = useRef(null);
|
|
@@ -110,54 +121,39 @@ function Scheduler(props) {
|
|
|
110
121
|
}
|
|
111
122
|
return undefined;
|
|
112
123
|
}, [schedulerData]);
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
schedulerData._setDocumentWidth(document.documentElement.clientWidth);
|
|
117
|
-
schedulerData._setDocumentHeight(document.documentElement.clientHeight);
|
|
118
|
-
} finally {
|
|
119
|
-
schedulerData.endBatch();
|
|
120
|
-
}
|
|
121
|
-
}, [schedulerData]);
|
|
124
|
+
|
|
125
|
+
// Width comes from the scheduler's own wrapper (100% wide of its parent). `parentRef` only supplies the height
|
|
126
|
+
// when responsiveByParent is on.
|
|
122
127
|
useEffect(() => {
|
|
123
|
-
|
|
128
|
+
const {
|
|
129
|
+
responsiveByParent
|
|
130
|
+
} = schedulerData.config;
|
|
131
|
+
const parentEl = responsiveByParent ? parentRef?.current : null;
|
|
132
|
+
// the wrapper is 100% wide of its parent (padding excluded), so it gives the width in both modes
|
|
133
|
+
const widthEl = containerRef.current;
|
|
134
|
+
if (!widthEl) return undefined;
|
|
135
|
+
const update = () => {
|
|
124
136
|
schedulerData.beginBatch();
|
|
125
137
|
try {
|
|
126
|
-
schedulerData.
|
|
127
|
-
schedulerData.
|
|
138
|
+
schedulerData._setMeasuredByContainer(true);
|
|
139
|
+
schedulerData._setDocumentWidth(widthEl.clientWidth);
|
|
140
|
+
if (responsiveByParent) {
|
|
141
|
+
schedulerData._setDocumentHeight(parentEl ? getInnerHeight(parentEl) : document.documentElement.clientHeight);
|
|
142
|
+
}
|
|
128
143
|
} finally {
|
|
129
144
|
schedulerData.endBatch();
|
|
130
145
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const
|
|
137
|
-
if (
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
schedulerData._setDocumentHeight(parentEl.clientHeight);
|
|
143
|
-
} finally {
|
|
144
|
-
schedulerData.endBatch();
|
|
145
|
-
}
|
|
146
|
-
};
|
|
147
|
-
updateParentSize();
|
|
148
|
-
|
|
149
|
-
// Disconnect any previous observer to prevent memory leaks
|
|
150
|
-
if (ulObserverRef.current) {
|
|
151
|
-
ulObserverRef.current.disconnect();
|
|
152
|
-
}
|
|
153
|
-
ulObserverRef.current = new ResizeObserver(updateParentSize);
|
|
154
|
-
ulObserverRef.current.observe(parentEl);
|
|
155
|
-
return () => {
|
|
156
|
-
if (ulObserverRef.current) {
|
|
157
|
-
ulObserverRef.current.disconnect();
|
|
158
|
-
}
|
|
159
|
-
};
|
|
160
|
-
}
|
|
146
|
+
};
|
|
147
|
+
update();
|
|
148
|
+
ulObserverRef.current = new ResizeObserver(update);
|
|
149
|
+
ulObserverRef.current.observe(widthEl);
|
|
150
|
+
if (parentEl) ulObserverRef.current.observe(parentEl);
|
|
151
|
+
const listenToWindow = responsiveByParent && !parentEl;
|
|
152
|
+
if (listenToWindow) window.addEventListener('resize', update);
|
|
153
|
+
return () => {
|
|
154
|
+
ulObserverRef.current?.disconnect();
|
|
155
|
+
if (listenToWindow) window.removeEventListener('resize', update);
|
|
156
|
+
};
|
|
161
157
|
}, [parentRef, schedulerData]);
|
|
162
158
|
useEffect(() => {
|
|
163
159
|
if (schedulerData.config.responsiveByParent && schedulerHeaderEl) {
|
|
@@ -368,10 +364,11 @@ function Scheduler(props) {
|
|
|
368
364
|
viewEvent2Text: props.viewEvent2Text,
|
|
369
365
|
newEvent: props.newEvent,
|
|
370
366
|
eventItemTemplateResolver: props.eventItemTemplateResolver,
|
|
367
|
+
eventItemPopoverTemplateResolver: props.eventItemPopoverTemplateResolver,
|
|
371
368
|
onSelectionChange: handleSelectionChange,
|
|
372
369
|
isRowSelected: selectedIdsSet.has(item.slotId),
|
|
373
370
|
selectionPreview: selectionPreview
|
|
374
|
-
}, item.slotId)), [displayRenderData, schedulerData, schedulerDataVersion, eventDndSource, dndContext, props.onSetAddMoreState, props.updateEventStart, props.updateEventEnd, props.moveEvent, props.movingEvent, props.conflictOccurred, props.subtitleGetter, props.eventItemClick, props.viewEventClick, props.viewEventText, props.viewEvent2Click, props.viewEvent2Text, props.newEvent, props.eventItemTemplateResolver, handleSelectionChange, selectedIdsSet, selectionPreview]);
|
|
371
|
+
}, item.slotId)), [displayRenderData, schedulerData, schedulerDataVersion, eventDndSource, dndContext, props.onSetAddMoreState, props.updateEventStart, props.updateEventEnd, props.moveEvent, props.movingEvent, props.conflictOccurred, props.subtitleGetter, props.eventItemClick, props.viewEventClick, props.viewEventText, props.viewEvent2Click, props.viewEvent2Text, props.newEvent, props.eventItemTemplateResolver, props.eventItemPopoverTemplateResolver, handleSelectionChange, selectedIdsSet, selectionPreview]);
|
|
375
372
|
let tbodyContent = /*#__PURE__*/_jsx("tr", {});
|
|
376
373
|
if (showAgenda) {
|
|
377
374
|
tbodyContent = /*#__PURE__*/_jsx(AgendaView, {
|
|
@@ -606,31 +603,38 @@ function Scheduler(props) {
|
|
|
606
603
|
width: `${width}px`,
|
|
607
604
|
tableLayout: 'fixed'
|
|
608
605
|
}), [width]);
|
|
609
|
-
return /*#__PURE__*/
|
|
610
|
-
|
|
611
|
-
className: `rbs ${
|
|
612
|
-
style:
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
606
|
+
return /*#__PURE__*/_jsx("div", {
|
|
607
|
+
ref: containerRef,
|
|
608
|
+
className: className ? `rbs-container ${className}` : 'rbs-container',
|
|
609
|
+
style: style,
|
|
610
|
+
children: /*#__PURE__*/_jsxs("table", {
|
|
611
|
+
id: "rbs-root",
|
|
612
|
+
className: `rbs ${schedulerData.isVerticalResourceView() ? 'vertical-view' : ''}`,
|
|
613
|
+
style: rootTableStyle,
|
|
614
|
+
ref: schedulerRootRef,
|
|
615
|
+
children: [resourceColumnWidth !== undefined && /*#__PURE__*/_jsxs("colgroup", {
|
|
616
|
+
children: [/*#__PURE__*/_jsx("col", {
|
|
617
|
+
style: {
|
|
618
|
+
width: resourceColumnWidth
|
|
619
|
+
}
|
|
620
|
+
}), /*#__PURE__*/_jsx("col", {})]
|
|
621
|
+
}), /*#__PURE__*/_jsx("thead", {
|
|
622
|
+
children: /*#__PURE__*/_jsx("tr", {
|
|
623
|
+
children: /*#__PURE__*/_jsx("td", {
|
|
624
|
+
colSpan: "2",
|
|
625
|
+
children: schedulerHeader
|
|
626
|
+
})
|
|
625
627
|
})
|
|
626
|
-
})
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
})
|
|
628
|
+
}), /*#__PURE__*/_jsx("tbody", {
|
|
629
|
+
children: tbodyContent
|
|
630
|
+
})]
|
|
631
|
+
})
|
|
630
632
|
});
|
|
631
633
|
}
|
|
632
634
|
Scheduler.propTypes = {
|
|
633
635
|
parentRef: PropTypes.object,
|
|
636
|
+
className: PropTypes.string,
|
|
637
|
+
style: PropTypes.object,
|
|
634
638
|
schedulerData: PropTypes.object.isRequired,
|
|
635
639
|
prevClick: PropTypes.func.isRequired,
|
|
636
640
|
nextClick: PropTypes.func.isRequired,
|
|
@@ -652,6 +656,7 @@ Scheduler.propTypes = {
|
|
|
652
656
|
viewEvent2Text: PropTypes.string,
|
|
653
657
|
conflictOccurred: PropTypes.func,
|
|
654
658
|
eventItemTemplateResolver: PropTypes.func,
|
|
659
|
+
eventItemPopoverTemplateResolver: PropTypes.func,
|
|
655
660
|
dndSources: PropTypes.array,
|
|
656
661
|
slotClickedFunc: PropTypes.func,
|
|
657
662
|
toggleExpandFunc: PropTypes.func,
|
package/dist/config/scheduler.js
CHANGED
package/dist/css/style.css
CHANGED
|
@@ -82,6 +82,14 @@
|
|
|
82
82
|
margin-top: -1%;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
/* Container: fills its parent. :where() keeps specificity at zero so any className (e.g. tailwind) or style prop wins. */
|
|
86
|
+
:where(.rbs-container) {
|
|
87
|
+
width: 100%;
|
|
88
|
+
max-width: 100%;
|
|
89
|
+
max-height: 100%;
|
|
90
|
+
overflow: auto;
|
|
91
|
+
}
|
|
92
|
+
|
|
85
93
|
/* Tables */
|
|
86
94
|
.rbs table.resource-table,
|
|
87
95
|
.rbs table.scheduler-bg-table,
|
package/dist/index.d.ts
CHANGED
|
@@ -121,6 +121,10 @@ export interface SchedulerProps<EventType extends EventItem = EventItem> {
|
|
|
121
121
|
dndSources?: DnDSource[];
|
|
122
122
|
// biome-ignore lint/suspicious/noExplicitAny: legacy any usage
|
|
123
123
|
parentRef?: React.RefObject<any>;
|
|
124
|
+
/** Class names for the scheduler's container, e.g. tailwind classes. They win over the default 100% width / max height. */
|
|
125
|
+
className?: string;
|
|
126
|
+
/** Inline style for the scheduler's container. */
|
|
127
|
+
style?: CSSProperties;
|
|
124
128
|
// biome-ignore lint/suspicious/noExplicitAny: legacy any usage
|
|
125
129
|
CustomResourceHeader?: React.ComponentType<any>;
|
|
126
130
|
CustomResourceCell?: React.ComponentType<CustomResourceCellProps<EventType>>;
|
|
@@ -326,6 +330,7 @@ export interface State<EventType extends EventItem = EventItem> {
|
|
|
326
330
|
|
|
327
331
|
export interface SchedulerDataConfig {
|
|
328
332
|
schedulerWidth?: `${number}%`;
|
|
333
|
+
/** @deprecated The scheduler measures its own container now, so this offset is ignored. */
|
|
329
334
|
besidesWidth?: number;
|
|
330
335
|
underneathHeight?: number;
|
|
331
336
|
schedulerMaxHeight?: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-big-schedule",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.1.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",
|