react-big-schedule 6.1.1-beta.0 → 7.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 +28 -10
- package/dist/components/ResourceEvents.js +8 -4
- package/dist/components/SchedulerData.js +22 -6
- package/dist/components/SchedulerHeader.js +1 -0
- package/dist/components/index.js +65 -62
- 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
|
@@ -567,19 +567,20 @@ 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
|
|
|
@@ -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
|
|
@@ -291,10 +291,14 @@ class ResourceEvents extends PureComponent {
|
|
|
291
291
|
const maxRightIndex = Math.min(headers.length, resourceEvents.headerItems.length);
|
|
292
292
|
let safeRightIndex = Math.max(1, rightIndex, safeLeftIndex + 1);
|
|
293
293
|
safeRightIndex = Math.min(safeRightIndex, maxRightIndex);
|
|
294
|
+
const isVertical = schedulerData.isVerticalResourceView();
|
|
295
|
+
// vertical view: rows are time slots and columns are resources, so the roles swap
|
|
296
|
+
const selectedRowIds = this.getSelectedResourceIds();
|
|
294
297
|
let startTime, endTime;
|
|
295
|
-
if (
|
|
296
|
-
startTime = resourceEvents.slotId;
|
|
297
|
-
|
|
298
|
+
if (isVertical) {
|
|
299
|
+
startTime = selectedRowIds[0] ?? resourceEvents.slotId;
|
|
300
|
+
const lastRowId = selectedRowIds[selectedRowIds.length - 1] ?? startTime;
|
|
301
|
+
endTime = localeDayjs(new Date(lastRowId)).add(config.minuteStep, 'minutes').format(DATETIME_FORMAT);
|
|
298
302
|
} else {
|
|
299
303
|
startTime = headers[safeLeftIndex].time;
|
|
300
304
|
endTime = resourceEvents.headerItems[safeRightIndex - 1].end;
|
|
@@ -304,7 +308,7 @@ class ResourceEvents extends PureComponent {
|
|
|
304
308
|
}
|
|
305
309
|
|
|
306
310
|
// Get selected resource IDs
|
|
307
|
-
const selectedResourceIds =
|
|
311
|
+
const selectedResourceIds = isVertical ? headers.slice(safeLeftIndex, safeRightIndex).map(header => header.id) : selectedRowIds;
|
|
308
312
|
const slotId = selectedResourceIds.length > 0 ? selectedResourceIds[0] : resourceEvents.slotId;
|
|
309
313
|
const slotName = selectedResourceIds.length > 0 ? schedulerData.getResourceById(slotId)?.name || slotId : resourceEvents.slotName;
|
|
310
314
|
this.setState({
|
|
@@ -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) {
|
|
@@ -606,31 +602,38 @@ function Scheduler(props) {
|
|
|
606
602
|
width: `${width}px`,
|
|
607
603
|
tableLayout: 'fixed'
|
|
608
604
|
}), [width]);
|
|
609
|
-
return /*#__PURE__*/
|
|
610
|
-
|
|
611
|
-
className: `rbs ${
|
|
612
|
-
style:
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
605
|
+
return /*#__PURE__*/_jsx("div", {
|
|
606
|
+
ref: containerRef,
|
|
607
|
+
className: className ? `rbs-container ${className}` : 'rbs-container',
|
|
608
|
+
style: style,
|
|
609
|
+
children: /*#__PURE__*/_jsxs("table", {
|
|
610
|
+
id: "rbs-root",
|
|
611
|
+
className: `rbs ${schedulerData.isVerticalResourceView() ? 'vertical-view' : ''}`,
|
|
612
|
+
style: rootTableStyle,
|
|
613
|
+
ref: schedulerRootRef,
|
|
614
|
+
children: [resourceColumnWidth !== undefined && /*#__PURE__*/_jsxs("colgroup", {
|
|
615
|
+
children: [/*#__PURE__*/_jsx("col", {
|
|
616
|
+
style: {
|
|
617
|
+
width: resourceColumnWidth
|
|
618
|
+
}
|
|
619
|
+
}), /*#__PURE__*/_jsx("col", {})]
|
|
620
|
+
}), /*#__PURE__*/_jsx("thead", {
|
|
621
|
+
children: /*#__PURE__*/_jsx("tr", {
|
|
622
|
+
children: /*#__PURE__*/_jsx("td", {
|
|
623
|
+
colSpan: "2",
|
|
624
|
+
children: schedulerHeader
|
|
625
|
+
})
|
|
625
626
|
})
|
|
626
|
-
})
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
})
|
|
627
|
+
}), /*#__PURE__*/_jsx("tbody", {
|
|
628
|
+
children: tbodyContent
|
|
629
|
+
})]
|
|
630
|
+
})
|
|
630
631
|
});
|
|
631
632
|
}
|
|
632
633
|
Scheduler.propTypes = {
|
|
633
634
|
parentRef: PropTypes.object,
|
|
635
|
+
className: PropTypes.string,
|
|
636
|
+
style: PropTypes.object,
|
|
634
637
|
schedulerData: PropTypes.object.isRequired,
|
|
635
638
|
prevClick: PropTypes.func.isRequired,
|
|
636
639
|
nextClick: PropTypes.func.isRequired,
|
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.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",
|