@cwellt_software/cwellt-reactjs-lib 1.3.3 → 1.3.6
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/dist/index.cjs.js +230 -108
- package/dist/index.css +2 -2
- package/dist/index.d.ts +16 -3
- package/dist/index.es.js +230 -108
- package/dist/src/common/functions/useSingleAndDoubleClicks.d.ts.map +1 -1
- package/dist/src/components/custom/scheduler-new/presentation/NewScheduler.d.ts +2 -0
- package/dist/src/components/custom/scheduler-new/presentation/NewScheduler.d.ts.map +1 -1
- package/dist/src/components/custom/scheduler-new/presentation/components/row/IndicatorRow.d.ts +18 -0
- package/dist/src/components/custom/scheduler-new/presentation/components/row/IndicatorRow.d.ts.map +1 -0
- package/dist/src/components/custom/scheduler-new/presentation/components/row/SchedulerRow.d.ts +2 -0
- package/dist/src/components/custom/scheduler-new/presentation/components/row/SchedulerRow.d.ts.map +1 -1
- package/dist/src/components/custom/scheduler-new/presentation/logic/eventIsVisible.d.ts.map +1 -1
- package/dist/src/components/custom/scheduler-new/presentation/logic/filterVisibleEvents.d.ts.map +1 -1
- package/dist/src/components/custom/super-scheduler/SuperScheduler.d.ts +2 -0
- package/dist/src/components/custom/super-scheduler/SuperScheduler.d.ts.map +1 -1
- package/dist/src/components/display/text/message/CwMessage.d.ts +3 -2
- package/dist/src/components/display/text/message/CwMessage.d.ts.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/package.json +4 -3
- package/readme.md +1 -4
package/dist/index.cjs.js
CHANGED
|
@@ -273,7 +273,7 @@ const CwMessage = props => {
|
|
|
273
273
|
}, props.duration ?? CW_DEFAULT_MESSAGE_DURATION);
|
|
274
274
|
return () => clearTimeout(timer);
|
|
275
275
|
}, [props]);
|
|
276
|
-
return (jsxRuntime.jsxs("div", { className: "cw-message", "data-message-type": Object.keys(exports.CwMessageType).find(key => exports.CwMessageType[key] === props.messageType), children: [props.messageType && jsxRuntime.jsx(CwIcon, { iconId: props.messageType.toString(), size: "large" }), props.message] }));
|
|
276
|
+
return (jsxRuntime.jsxs("div", { className: "cw-message", "data-message-type": Object.keys(exports.CwMessageType).find(key => exports.CwMessageType[key] === props.messageType), onClick: props.onClick, style: props.onClick ? { cursor: "pointer" } : undefined, children: [props.messageType && jsxRuntime.jsx(CwIcon, { iconId: props.messageType.toString(), size: "large" }), props.message] }));
|
|
277
277
|
};
|
|
278
278
|
/**
|
|
279
279
|
* Hook for displaying inline messages within specific components.
|
|
@@ -336,11 +336,11 @@ class CwMessageManager {
|
|
|
336
336
|
document.body.prepend(this.messageWrapper);
|
|
337
337
|
this.root = client.createRoot(this.messageWrapper); // Create a root at the messageWrapper
|
|
338
338
|
}
|
|
339
|
-
showMessage(message, type, duration) {
|
|
339
|
+
showMessage(message, type, duration, onClick) {
|
|
340
340
|
const msg = document.createElement("div");
|
|
341
341
|
this.messageWrapper?.prepend(msg);
|
|
342
342
|
const msgRoot = client.createRoot(msg); // Create a root for the new message
|
|
343
|
-
msgRoot.render(jsxRuntime.jsx(CwMessage, { message: message, messageType: type, duration: duration, onClose: () => this.closeMessage(msgRoot) }));
|
|
343
|
+
msgRoot.render(jsxRuntime.jsx(CwMessage, { message: message, messageType: type, duration: duration, onClick: onClick, onClose: () => this.closeMessage(msgRoot) }));
|
|
344
344
|
}
|
|
345
345
|
closeMessage(msgRoot) {
|
|
346
346
|
msgRoot.unmount(); // Unmount the message root
|
|
@@ -362,8 +362,8 @@ class CwMessageManager {
|
|
|
362
362
|
*
|
|
363
363
|
* @note For inline messages within components, use `CwNote` or `useCwMessage` hook instead
|
|
364
364
|
*/
|
|
365
|
-
function CwDisplayMessage(message, type, duration) {
|
|
366
|
-
CwMessageManager.getInstance().showMessage(message, type, duration);
|
|
365
|
+
function CwDisplayMessage(message, type, duration, onClick) {
|
|
366
|
+
CwMessageManager.getInstance().showMessage(message, type, duration, onClick);
|
|
367
367
|
}
|
|
368
368
|
|
|
369
369
|
/**
|
|
@@ -8040,30 +8040,90 @@ const BackgroundEvent = ({ value, heightRem }) => {
|
|
|
8040
8040
|
}, children: value.icons }) : null, jsxRuntime.jsx("span", { className: styles$2["scheduler-event-text"], children: value.name })] }) }) }) }) })) : null;
|
|
8041
8041
|
};
|
|
8042
8042
|
|
|
8043
|
+
const eventIsVisible = (startDate, endDate, selectedDate, visibleDays) => {
|
|
8044
|
+
const schedulerEndDate = new Date(selectedDate.getTime() + visibleDays * 24 * 60 * 60 * 1000);
|
|
8045
|
+
const isBefore = selectedDate > startDate && selectedDate > endDate;
|
|
8046
|
+
const isAfter = schedulerEndDate < startDate && schedulerEndDate < endDate;
|
|
8047
|
+
return !isBefore && !isAfter;
|
|
8048
|
+
};
|
|
8049
|
+
|
|
8050
|
+
const hoursBetween = (date1, date2) => {
|
|
8051
|
+
const oneHourInMillis = 60 * 60 * 1000; // hours*minutes*seconds*milliseconds
|
|
8052
|
+
const timeDiff = date2.getTime() - date1.getTime();
|
|
8053
|
+
return timeDiff / oneHourInMillis;
|
|
8054
|
+
};
|
|
8055
|
+
const getEventSizes = (schedulerDate, totalDays, startDate, endDate) => {
|
|
8056
|
+
if (!eventIsVisible(startDate, endDate, schedulerDate, totalDays)) {
|
|
8057
|
+
return {
|
|
8058
|
+
left: 0,
|
|
8059
|
+
width: 0,
|
|
8060
|
+
};
|
|
8061
|
+
}
|
|
8062
|
+
const totalHours = totalDays * 24;
|
|
8063
|
+
// const dateString = schedulerDate.toISOString().split('T')[0];
|
|
8064
|
+
//const schedulerDateAtZero = new Date(dateString)
|
|
8065
|
+
let startHours = hoursBetween(schedulerDate, startDate);
|
|
8066
|
+
let durationHours = hoursBetween(startDate, endDate);
|
|
8067
|
+
const startOutOfScheduler = startHours < 0;
|
|
8068
|
+
if (startOutOfScheduler) {
|
|
8069
|
+
durationHours += startHours;
|
|
8070
|
+
startHours = 0;
|
|
8071
|
+
}
|
|
8072
|
+
const left = (startHours / totalHours) * 100;
|
|
8073
|
+
// Minimum width equivalent to 15 minutes so zero/near-zero duration events stay visible
|
|
8074
|
+
const MIN_WIDTH_HOURS = 0.25;
|
|
8075
|
+
const minWidth = (MIN_WIDTH_HOURS * 100) / totalHours;
|
|
8076
|
+
const width = Math.max((durationHours * 100) / totalHours, minWidth);
|
|
8077
|
+
return {
|
|
8078
|
+
left,
|
|
8079
|
+
width,
|
|
8080
|
+
};
|
|
8081
|
+
};
|
|
8082
|
+
|
|
8083
|
+
const INDICATOR_HEIGHT_REM = 0.25;
|
|
8084
|
+
const IndicatorRow = ({ indicators, selectedDate, visibleDays }) => {
|
|
8085
|
+
return (jsxRuntime.jsx("div", { style: {
|
|
8086
|
+
position: "relative",
|
|
8087
|
+
height: `${INDICATOR_HEIGHT_REM}rem`,
|
|
8088
|
+
pointerEvents: "none",
|
|
8089
|
+
}, "data-name": "indicator-row", children: indicators.map((indicator) => {
|
|
8090
|
+
const { left, width } = getEventSizes(selectedDate, visibleDays, indicator.start, indicator.end);
|
|
8091
|
+
if (width <= 0)
|
|
8092
|
+
return null;
|
|
8093
|
+
const bar = (jsxRuntime.jsx("div", { style: {
|
|
8094
|
+
position: "absolute",
|
|
8095
|
+
left: `${left}%`,
|
|
8096
|
+
width: `${width}%`,
|
|
8097
|
+
height: `${INDICATOR_HEIGHT_REM}rem`,
|
|
8098
|
+
backgroundColor: indicator.color,
|
|
8099
|
+
borderRadius: "1px",
|
|
8100
|
+
pointerEvents: "auto",
|
|
8101
|
+
} }, indicator.id));
|
|
8102
|
+
if (indicator.tooltip) {
|
|
8103
|
+
return (jsxRuntime.jsx(CwTooltipNew, { content: indicator.tooltip, position: "bottom", dissapearsWhenHover: true, showDelay: 200, children: bar }, indicator.id));
|
|
8104
|
+
}
|
|
8105
|
+
return bar;
|
|
8106
|
+
}) }));
|
|
8107
|
+
};
|
|
8108
|
+
|
|
8109
|
+
// Fires onClick on the first click of a sequence (detail === 1) and
|
|
8110
|
+
// onDoubleClick on the native dblclick event. Subsequent clicks of a
|
|
8111
|
+
// double-click sequence (detail > 1) are suppressed so the same logical
|
|
8112
|
+
// interaction does not fire onClick twice. Selection therefore appears
|
|
8113
|
+
// within one paint frame instead of being delayed by an artificial timer.
|
|
8043
8114
|
function useSingleAndDoubleClicks(onClick, onDoubleClick) {
|
|
8044
|
-
const timer = React.useRef(null);
|
|
8045
|
-
const cancelPendingClick = React.useCallback(() => {
|
|
8046
|
-
if (timer.current) {
|
|
8047
|
-
clearTimeout(timer.current);
|
|
8048
|
-
timer.current = null;
|
|
8049
|
-
}
|
|
8050
|
-
}, [timer]);
|
|
8051
8115
|
const handleClick = React.useCallback((e) => {
|
|
8052
|
-
// We only cache the most recent click event, so cancel any pending clicks
|
|
8053
8116
|
e.stopPropagation();
|
|
8054
8117
|
e.preventDefault();
|
|
8055
|
-
|
|
8056
|
-
|
|
8057
|
-
|
|
8058
|
-
|
|
8059
|
-
}, 500);
|
|
8060
|
-
}, [timer, cancelPendingClick, onClick]);
|
|
8118
|
+
if (e.detail > 1)
|
|
8119
|
+
return;
|
|
8120
|
+
onClick(e);
|
|
8121
|
+
}, [onClick]);
|
|
8061
8122
|
const handleDoubleClick = React.useCallback((e) => {
|
|
8062
8123
|
e.stopPropagation();
|
|
8063
8124
|
e.preventDefault();
|
|
8064
|
-
cancelPendingClick();
|
|
8065
8125
|
onDoubleClick(e);
|
|
8066
|
-
}, [
|
|
8126
|
+
}, [onDoubleClick]);
|
|
8067
8127
|
return { handleClick, handleDoubleClick };
|
|
8068
8128
|
}
|
|
8069
8129
|
|
|
@@ -8237,7 +8297,7 @@ const SchedulerEvent = ({ value, heightRem, onEvent }) => {
|
|
|
8237
8297
|
|
|
8238
8298
|
const SchedulerRow = React.memo((props) => {
|
|
8239
8299
|
const [isContextMenuOpen, setIsContextMenuOpen] = React.useState(false);
|
|
8240
|
-
const { events, backgroundEvents, rowHeader, contextMenuItems, RowTitleComp, EventComp, BackgroundEventComp, weekendLines, divisionLines, timeLinePercentage, selectedDate, visibleDays, onEvent, } = props;
|
|
8300
|
+
const { events, backgroundEvents, indicatorRows, rowHeader, contextMenuItems, RowTitleComp, EventComp, BackgroundEventComp, weekendLines, divisionLines, timeLinePercentage, selectedDate, visibleDays, onEvent, } = props;
|
|
8241
8301
|
const internalRows = separateEventsToInnerRows(events);
|
|
8242
8302
|
const schedulerDivRef = React.useRef(null);
|
|
8243
8303
|
const { handleClick, handleDoubleClick } = useSingleAndDoubleClicks((e) => {
|
|
@@ -8294,7 +8354,7 @@ const SchedulerRow = React.memo((props) => {
|
|
|
8294
8354
|
height: props.rowHeightInRem + "rem",
|
|
8295
8355
|
pointerEvents: "none"
|
|
8296
8356
|
}, children: internalRow.map((event) => (jsxRuntime.jsx(EventComp, { value: event, heightRem: props.rowHeightInRem, onEvent: onEvent }, event.id))) }, index));
|
|
8297
|
-
}), !(timeLinePercentage < 0 || timeLinePercentage > 100) && (jsxRuntime.jsx(TimeLine, { color: "red", left: `${timeLinePercentage}%`, top: "0px", height: `100%` }))] }) })] }));
|
|
8357
|
+
}), indicatorRows.length > 0 && (jsxRuntime.jsx(IndicatorRow, { indicators: indicatorRows, selectedDate: selectedDate, visibleDays: visibleDays })), !(timeLinePercentage < 0 || timeLinePercentage > 100) && (jsxRuntime.jsx(TimeLine, { color: "red", left: `${timeLinePercentage}%`, top: "0px", height: `100%` }))] }) })] }));
|
|
8298
8358
|
}, (prevProps, nextProps) => {
|
|
8299
8359
|
// This memo is necessary to prevent re-render all the rows when a user makes drag and drop
|
|
8300
8360
|
const getEventKey = (event) => {
|
|
@@ -8320,17 +8380,15 @@ const SchedulerRow = React.memo((props) => {
|
|
|
8320
8380
|
return false;
|
|
8321
8381
|
}
|
|
8322
8382
|
}
|
|
8383
|
+
if (prevProps.indicatorRows.length !== nextProps.indicatorRows.length) {
|
|
8384
|
+
return false;
|
|
8385
|
+
}
|
|
8323
8386
|
// If we got here, the events are the same
|
|
8324
8387
|
return true;
|
|
8325
8388
|
});
|
|
8326
8389
|
|
|
8327
8390
|
const filterVisibleEvents = (events, selectedDate, visibleDays) => {
|
|
8328
|
-
const
|
|
8329
|
-
year: selectedDate.getFullYear(),
|
|
8330
|
-
month: selectedDate.getMonth() + 1,
|
|
8331
|
-
day: selectedDate.getDate(),
|
|
8332
|
-
}).add({ days: visibleDays });
|
|
8333
|
-
const lastDayOfScheduler = new Date(schedulerEnd.year, schedulerEnd.month - 1, schedulerEnd.day);
|
|
8391
|
+
const lastDayOfScheduler = new Date(selectedDate.getTime() + visibleDays * 24 * 60 * 60 * 1000);
|
|
8334
8392
|
return events.filter((event) => event.endDate > selectedDate && event.startDate < lastDayOfScheduler);
|
|
8335
8393
|
};
|
|
8336
8394
|
|
|
@@ -8510,51 +8568,6 @@ const getWeeksByDays = (visibleDays, selectedDate) => {
|
|
|
8510
8568
|
return weeks;
|
|
8511
8569
|
};
|
|
8512
8570
|
|
|
8513
|
-
const eventIsVisible = (startDate, endDate, selectedDate, visibleDays) => {
|
|
8514
|
-
const schedulerEnd = Temporal.PlainDate.from({
|
|
8515
|
-
year: selectedDate.getFullYear(),
|
|
8516
|
-
month: selectedDate.getMonth() + 1,
|
|
8517
|
-
day: selectedDate.getDate(),
|
|
8518
|
-
}).add({ days: visibleDays });
|
|
8519
|
-
const schedulerEndDate = new Date(schedulerEnd.year, schedulerEnd.month - 1, schedulerEnd.day);
|
|
8520
|
-
const isBefore = selectedDate > startDate && selectedDate > endDate;
|
|
8521
|
-
const isAfter = schedulerEndDate < startDate && schedulerEndDate < endDate;
|
|
8522
|
-
return !isBefore && !isAfter;
|
|
8523
|
-
};
|
|
8524
|
-
|
|
8525
|
-
const hoursBetween = (date1, date2) => {
|
|
8526
|
-
const oneHourInMillis = 60 * 60 * 1000; // hours*minutes*seconds*milliseconds
|
|
8527
|
-
const timeDiff = date2.getTime() - date1.getTime();
|
|
8528
|
-
return timeDiff / oneHourInMillis;
|
|
8529
|
-
};
|
|
8530
|
-
const getEventSizes = (schedulerDate, totalDays, startDate, endDate) => {
|
|
8531
|
-
if (!eventIsVisible(startDate, endDate, schedulerDate, totalDays)) {
|
|
8532
|
-
return {
|
|
8533
|
-
left: 0,
|
|
8534
|
-
width: 0,
|
|
8535
|
-
};
|
|
8536
|
-
}
|
|
8537
|
-
const totalHours = totalDays * 24;
|
|
8538
|
-
// const dateString = schedulerDate.toISOString().split('T')[0];
|
|
8539
|
-
//const schedulerDateAtZero = new Date(dateString)
|
|
8540
|
-
let startHours = hoursBetween(schedulerDate, startDate);
|
|
8541
|
-
let durationHours = hoursBetween(startDate, endDate);
|
|
8542
|
-
const startOutOfScheduler = startHours < 0;
|
|
8543
|
-
if (startOutOfScheduler) {
|
|
8544
|
-
durationHours += startHours;
|
|
8545
|
-
startHours = 0;
|
|
8546
|
-
}
|
|
8547
|
-
const left = (startHours / totalHours) * 100;
|
|
8548
|
-
// Minimum width equivalent to 15 minutes so zero/near-zero duration events stay visible
|
|
8549
|
-
const MIN_WIDTH_HOURS = 0.25;
|
|
8550
|
-
const minWidth = (MIN_WIDTH_HOURS * 100) / totalHours;
|
|
8551
|
-
const width = Math.max((durationHours * 100) / totalHours, minWidth);
|
|
8552
|
-
return {
|
|
8553
|
-
left,
|
|
8554
|
-
width,
|
|
8555
|
-
};
|
|
8556
|
-
};
|
|
8557
|
-
|
|
8558
8571
|
function getLinesByDivisions(headerDivision, _) {
|
|
8559
8572
|
const lines = headerDivision.flatMap((division) => division.bottom.map((_, i) => ({ isDayBoundary: i === 0 })));
|
|
8560
8573
|
const left = 100 / lines.length;
|
|
@@ -8610,8 +8623,65 @@ const getNow = (isUtc) => {
|
|
|
8610
8623
|
};
|
|
8611
8624
|
const milliSecondsInSecond = 1000;
|
|
8612
8625
|
const refreshMilliSeconds = 45 * milliSecondsInSecond;
|
|
8626
|
+
const EMPTY_EVENTS = [];
|
|
8627
|
+
const EMPTY_BG_EVENTS = [];
|
|
8628
|
+
const EMPTY_INDICATORS = [];
|
|
8629
|
+
// Memoised row body. Receives flat props (the per-row buckets are looked up
|
|
8630
|
+
// by the outer Row wrapper). As long as those bucket arrays keep their
|
|
8631
|
+
// reference (see `groupByRowIdStable`) and the rest of the render-context
|
|
8632
|
+
// values are stable, this row will skip re-rendering on selection clicks
|
|
8633
|
+
// that don't affect it.
|
|
8634
|
+
const RowContent = React.memo(function RowContent(props) {
|
|
8635
|
+
const header = {
|
|
8636
|
+
value: props.row,
|
|
8637
|
+
width: props.rowHeaderWidth,
|
|
8638
|
+
onEvent: props.onEvent,
|
|
8639
|
+
};
|
|
8640
|
+
return (jsxRuntime.jsx("div", { style: {
|
|
8641
|
+
...props.style,
|
|
8642
|
+
backgroundColor: props.rowColor
|
|
8643
|
+
}, children: jsxRuntime.jsx(SchedulerRow, { events: props.events, backgroundEvents: props.backgroundEvents, indicatorRows: props.indicatorRows, rowHeader: header, EventComp: props.EventComp, BackgroundEventComp: props.BackgroundEventComp, RowTitleComp: props.RowTitleComp, weekendLines: props.weekendLines, divisionLines: props.divisionLines, timeLinePercentage: props.timeLinePercentage, rowHeightInRem: props.rowHeightRem, onEvent: props.onEvent, contextMenuItems: props.contextMenuItems, visibleDays: props.visibleDays, selectedDate: props.selectedDate }) }, props.row.rowId));
|
|
8644
|
+
});
|
|
8645
|
+
// Outer (unmemoised) row: looks up per-row buckets from the shared `data`
|
|
8646
|
+
// object and forwards them as flat props to `RowContent`. Because the
|
|
8647
|
+
// referenced arrays are reused when their contents are unchanged
|
|
8648
|
+
// (see `groupByRowIdStable`), `RowContent.memo` will skip the work.
|
|
8649
|
+
const Row = ({ index, style, data }) => {
|
|
8650
|
+
const row = data.rows[index];
|
|
8651
|
+
const rowColor = data.groupRowColors
|
|
8652
|
+
? Math.floor(index / 2) % 2 === 0 ? data.evenColor : data.oddColor
|
|
8653
|
+
: index % 2 === 0 ? data.evenColor : data.oddColor;
|
|
8654
|
+
return (jsxRuntime.jsx(RowContent, { style: style, row: row, rowColor: rowColor, events: data.eventsByRow.get(row.rowId) ?? EMPTY_EVENTS, backgroundEvents: data.backgroundEventsByRow.get(row.rowId) ?? EMPTY_BG_EVENTS, indicatorRows: data.indicatorRowsByRow.get(row.rowId) ?? EMPTY_INDICATORS, EventComp: data.EventComp, BackgroundEventComp: data.BackgroundEventComp, RowTitleComp: data.RowTitleComp, rowHeaderWidth: data.rowHeaderWidth, rowHeightRem: data.rowHeightRem, weekendLines: data.weekendLines, divisionLines: data.divisionLines, timeLinePercentage: data.timeLinePercentage, selectedDate: data.selectedDate, visibleDays: data.visibleDays, contextMenuItems: data.contextMenuItems, onEvent: data.onEvent }));
|
|
8655
|
+
};
|
|
8656
|
+
// Bucket items by rowId, but reuse previous bucket arrays whenever a row's
|
|
8657
|
+
// contents have not changed. Keeping bucket references stable is essential
|
|
8658
|
+
// for `RowContent.memo` to skip work when only one row's events change
|
|
8659
|
+
// (e.g. on selection clicks).
|
|
8660
|
+
const groupByRowIdStable = (items, prev) => {
|
|
8661
|
+
const next = new Map();
|
|
8662
|
+
for (const item of items) {
|
|
8663
|
+
const bucket = next.get(item.rowId);
|
|
8664
|
+
if (bucket) {
|
|
8665
|
+
bucket.push(item);
|
|
8666
|
+
}
|
|
8667
|
+
else {
|
|
8668
|
+
next.set(item.rowId, [item]);
|
|
8669
|
+
}
|
|
8670
|
+
}
|
|
8671
|
+
if (!prev)
|
|
8672
|
+
return next;
|
|
8673
|
+
for (const [rowId, bucket] of next) {
|
|
8674
|
+
const prevBucket = prev.get(rowId);
|
|
8675
|
+
if (prevBucket
|
|
8676
|
+
&& prevBucket.length === bucket.length
|
|
8677
|
+
&& prevBucket.every((v, i) => v === bucket[i])) {
|
|
8678
|
+
next.set(rowId, prevBucket);
|
|
8679
|
+
}
|
|
8680
|
+
}
|
|
8681
|
+
return next;
|
|
8682
|
+
};
|
|
8613
8683
|
const Scheduler = (props) => {
|
|
8614
|
-
const { header: headerContent, id, events: eventsState, backgroundEvents, contextMenuItems, EventComp, RowTitleComp, orderCategories = ["title"], useOrderCategory, onEvent, groupRowColors, rowHeaderWidth = 180, rowHeightRem = 1.75, } = props;
|
|
8684
|
+
const { header: headerContent, id, events: eventsState, backgroundEvents, indicatorRows: allIndicatorRows = [], contextMenuItems, EventComp, RowTitleComp, orderCategories = ["title"], useOrderCategory, onEvent, groupRowColors, rowHeaderWidth = 180, rowHeightRem = 1.75, } = props;
|
|
8615
8685
|
const BackgroundEventComp = props.BackgroundEventComp ?? BackgroundEvent;
|
|
8616
8686
|
const rows = useOrderCategory === false
|
|
8617
8687
|
? props.rows
|
|
@@ -8619,21 +8689,49 @@ const Scheduler = (props) => {
|
|
|
8619
8689
|
const instanceRef = React.useRef(null);
|
|
8620
8690
|
// const rowHeaderWidth = "180px";
|
|
8621
8691
|
const { selectedDate, visibleDays, isUtc, isHeaderVisible, visibleRows: stateVisibleRows } = props.state;
|
|
8622
|
-
const events = filterVisibleEvents(eventsState, selectedDate, visibleDays);
|
|
8692
|
+
const events = React.useMemo(() => filterVisibleEvents(eventsState, selectedDate, visibleDays), [eventsState, selectedDate, visibleDays]);
|
|
8623
8693
|
React.useEffect(() => {
|
|
8624
8694
|
instanceRef.current?.resetAfterIndex(0);
|
|
8625
|
-
}, [orderCategories, props.rows.length, events]);
|
|
8626
|
-
|
|
8627
|
-
|
|
8695
|
+
}, [orderCategories, props.rows.length, events, allIndicatorRows]);
|
|
8696
|
+
// Bucket events / backgroundEvents / indicatorRows by rowId once per render
|
|
8697
|
+
// instead of running events.filter() per row inside the row renderer.
|
|
8698
|
+
// Reuse previous bucket arrays when their contents are unchanged so the
|
|
8699
|
+
// memoised RowContent below can skip work (essential for selection clicks
|
|
8700
|
+
// to be cheap).
|
|
8701
|
+
const eventsByRowRef = React.useRef();
|
|
8702
|
+
const backgroundEventsByRowRef = React.useRef();
|
|
8703
|
+
const indicatorRowsByRowRef = React.useRef();
|
|
8704
|
+
const eventsByRow = React.useMemo(() => {
|
|
8705
|
+
const next = groupByRowIdStable(events, eventsByRowRef.current);
|
|
8706
|
+
eventsByRowRef.current = next;
|
|
8707
|
+
return next;
|
|
8708
|
+
}, [events]);
|
|
8709
|
+
const backgroundEventsByRow = React.useMemo(() => {
|
|
8710
|
+
const next = groupByRowIdStable(backgroundEvents, backgroundEventsByRowRef.current);
|
|
8711
|
+
backgroundEventsByRowRef.current = next;
|
|
8712
|
+
return next;
|
|
8713
|
+
}, [backgroundEvents]);
|
|
8714
|
+
const indicatorRowsByRow = React.useMemo(() => {
|
|
8715
|
+
const next = groupByRowIdStable(allIndicatorRows, indicatorRowsByRowRef.current);
|
|
8716
|
+
indicatorRowsByRowRef.current = next;
|
|
8717
|
+
return next;
|
|
8718
|
+
}, [allIndicatorRows]);
|
|
8719
|
+
const totalHeightRem = rows.reduce((acc, row) => {
|
|
8720
|
+
const filteredEvents = eventsByRow.get(row.rowId) ?? [];
|
|
8628
8721
|
const innerRows = separateEventsToInnerRows(filteredEvents);
|
|
8629
|
-
|
|
8722
|
+
const eventHeight = Math.max(innerRows.length, 1) * rowHeightRem;
|
|
8723
|
+
const hasIndicators = indicatorRowsByRow.has(row.rowId);
|
|
8724
|
+
return acc + eventHeight + (hasIndicators ? INDICATOR_HEIGHT_REM : 0);
|
|
8630
8725
|
}, 0);
|
|
8631
|
-
const
|
|
8726
|
+
const schedulerContentHeight = stateVisibleRows != null
|
|
8727
|
+
? Math.min(totalHeightRem, stateVisibleRows * rowHeightRem)
|
|
8728
|
+
: Math.max(totalHeightRem, rowHeightRem);
|
|
8632
8729
|
const totalHours = visibleDays * 24;
|
|
8633
8730
|
const [timeLinePercentage, setTimeLinePercentage] = React.useState(0);
|
|
8731
|
+
const divisions = React.useMemo(() => getDefaultDivisions(visibleDays, selectedDate), [visibleDays, selectedDate]);
|
|
8634
8732
|
const header = {
|
|
8635
8733
|
content: headerContent,
|
|
8636
|
-
divisions
|
|
8734
|
+
divisions,
|
|
8637
8735
|
visibleDays: visibleDays,
|
|
8638
8736
|
width: rowHeaderWidth,
|
|
8639
8737
|
selectedDate,
|
|
@@ -8643,9 +8741,8 @@ const Scheduler = (props) => {
|
|
|
8643
8741
|
};
|
|
8644
8742
|
const evenColor = "var(--cw-color-surface-container-low)";
|
|
8645
8743
|
const oddColor = "var(--cw-color-surface-container)";
|
|
8646
|
-
const
|
|
8647
|
-
const
|
|
8648
|
-
const divisionLines = getLinesByDivisions(header.divisions);
|
|
8744
|
+
const weekendsLines = React.useMemo(() => getWeekendLinesByDatesVisible(selectedDate, visibleDays), [selectedDate, visibleDays]);
|
|
8745
|
+
const divisionLines = React.useMemo(() => getLinesByDivisions(divisions), [divisions, visibleDays]);
|
|
8649
8746
|
// Timeline percentage calculation
|
|
8650
8747
|
React.useEffect(() => {
|
|
8651
8748
|
const updateTimeLinePercentage = () => {
|
|
@@ -8656,30 +8753,55 @@ const Scheduler = (props) => {
|
|
|
8656
8753
|
const interval = setInterval(updateTimeLinePercentage, refreshMilliSeconds);
|
|
8657
8754
|
return () => clearInterval(interval);
|
|
8658
8755
|
}, [selectedDate, isUtc, totalHours]);
|
|
8659
|
-
|
|
8660
|
-
|
|
8661
|
-
|
|
8662
|
-
|
|
8663
|
-
|
|
8664
|
-
|
|
8665
|
-
|
|
8666
|
-
|
|
8667
|
-
|
|
8668
|
-
|
|
8669
|
-
|
|
8670
|
-
|
|
8671
|
-
|
|
8672
|
-
|
|
8673
|
-
|
|
8674
|
-
|
|
8756
|
+
const itemData = React.useMemo(() => ({
|
|
8757
|
+
rows,
|
|
8758
|
+
eventsByRow,
|
|
8759
|
+
backgroundEventsByRow,
|
|
8760
|
+
indicatorRowsByRow,
|
|
8761
|
+
EventComp: EventComp,
|
|
8762
|
+
BackgroundEventComp,
|
|
8763
|
+
RowTitleComp: RowTitleComp,
|
|
8764
|
+
rowHeaderWidth,
|
|
8765
|
+
rowHeightRem,
|
|
8766
|
+
weekendLines: weekendsLines,
|
|
8767
|
+
divisionLines,
|
|
8768
|
+
timeLinePercentage,
|
|
8769
|
+
selectedDate,
|
|
8770
|
+
visibleDays,
|
|
8771
|
+
contextMenuItems,
|
|
8772
|
+
onEvent,
|
|
8773
|
+
groupRowColors,
|
|
8774
|
+
evenColor,
|
|
8775
|
+
oddColor,
|
|
8776
|
+
}), [
|
|
8777
|
+
rows,
|
|
8778
|
+
eventsByRow,
|
|
8779
|
+
backgroundEventsByRow,
|
|
8780
|
+
indicatorRowsByRow,
|
|
8781
|
+
EventComp,
|
|
8782
|
+
BackgroundEventComp,
|
|
8783
|
+
RowTitleComp,
|
|
8784
|
+
rowHeaderWidth,
|
|
8785
|
+
rowHeightRem,
|
|
8786
|
+
weekendsLines,
|
|
8787
|
+
divisionLines,
|
|
8788
|
+
timeLinePercentage,
|
|
8789
|
+
selectedDate,
|
|
8790
|
+
visibleDays,
|
|
8791
|
+
contextMenuItems,
|
|
8792
|
+
onEvent,
|
|
8793
|
+
groupRowColors,
|
|
8794
|
+
]);
|
|
8675
8795
|
const getItemSize = React.useCallback((index) => {
|
|
8676
8796
|
const row = rows[index];
|
|
8677
|
-
const filteredEvents =
|
|
8797
|
+
const filteredEvents = eventsByRow.get(row.rowId) ?? [];
|
|
8678
8798
|
const innerRows = separateEventsToInnerRows(filteredEvents);
|
|
8679
8799
|
const rowsNumber = innerRows.length > 0 ? innerRows.length : 1;
|
|
8680
8800
|
const pixelsInRem = 16;
|
|
8681
|
-
|
|
8682
|
-
|
|
8801
|
+
const hasIndicators = indicatorRowsByRow.has(row.rowId);
|
|
8802
|
+
const indicatorPixels = hasIndicators ? INDICATOR_HEIGHT_REM * pixelsInRem : 0;
|
|
8803
|
+
return rowsNumber * rowHeightRem * pixelsInRem + indicatorPixels;
|
|
8804
|
+
}, [rows, eventsByRow, indicatorRowsByRow, rowHeightRem]);
|
|
8683
8805
|
// Render
|
|
8684
8806
|
return (jsxRuntime.jsxs("div", { id: id, style: {
|
|
8685
8807
|
position: "relative",
|
|
@@ -8690,7 +8812,7 @@ const Scheduler = (props) => {
|
|
|
8690
8812
|
position: "sticky",
|
|
8691
8813
|
top: 0,
|
|
8692
8814
|
zIndex: 1,
|
|
8693
|
-
}, children: jsxRuntime.jsx(SchedulerHeader, { ...header }) })), jsxRuntime.jsx(reactWindow.VariableSizeList, { height: schedulerContentHeight * 16, itemCount: rows.length, itemSize: getItemSize, width: "100%", style: { overflowX: "hidden" }, ref: instanceRef, className: styles$2["hide-scrollbar"], children: Row })] }));
|
|
8815
|
+
}, children: jsxRuntime.jsx(SchedulerHeader, { ...header }) })), jsxRuntime.jsx(reactWindow.VariableSizeList, { height: schedulerContentHeight * 16, itemCount: rows.length, itemSize: getItemSize, itemData: itemData, width: "100%", style: { overflowX: "hidden" }, ref: instanceRef, className: styles$2["hide-scrollbar"], children: Row })] }));
|
|
8694
8816
|
};
|
|
8695
8817
|
|
|
8696
8818
|
class UiEvent {
|
|
@@ -9053,13 +9175,13 @@ const PinRowHeader = ({ value, width, onEvent }) => {
|
|
|
9053
9175
|
jsxRuntime.jsxs("span", { className: styles["scheduler-crewmember-functions"], children: ["(", value.title3, ")"] }), value.subtitle2 && jsxRuntime.jsxs("span", { children: ["-", value.subtitle2] })] })] }), isLoading ? jsxRuntime.jsx("span", { className: "cwi-icons cwi-spinner" }) : undefined] }) }, value.rowId) }, value.rowId);
|
|
9054
9176
|
};
|
|
9055
9177
|
|
|
9056
|
-
const SuperScheduler = ({ id, state, header, rows, events, pinnedOrderCategory, unPinnedOrderCategory, backgroundEvents, contextMenuItems, onEvent, rowHeightRem = 1.75 }) => {
|
|
9178
|
+
const SuperScheduler = ({ id, state, header, rows, events, pinnedOrderCategory, unPinnedOrderCategory, backgroundEvents, indicatorRows = [], contextMenuItems, onEvent, rowHeightRem = 1.75 }) => {
|
|
9057
9179
|
const pinnedRows = rows.filter((it) => it.isPinned);
|
|
9058
9180
|
const notPinnedRows = rows.filter((it) => !it.isPinned);
|
|
9059
9181
|
const isFirstVisible = pinnedRows.length > 0;
|
|
9060
|
-
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [isFirstVisible && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(Scheduler, { id: `${id}-pinned`, state: state, header: header, rows: pinnedRows, events: events, backgroundEvents: backgroundEvents, contextMenuItems: contextMenuItems, orderCategories: pinnedOrderCategory, onEvent: onEvent, EventComp: SchedulerEvent, RowTitleComp: PinRowHeader, rowHeightRem: rowHeightRem }), jsxRuntime.jsx("div", { children: jsxRuntime.jsx(CwButton, { onClick: () => {
|
|
9182
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [isFirstVisible && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(Scheduler, { id: `${id}-pinned`, state: state, header: header, rows: pinnedRows, events: events, backgroundEvents: backgroundEvents, indicatorRows: indicatorRows, contextMenuItems: contextMenuItems, orderCategories: pinnedOrderCategory, onEvent: onEvent, EventComp: SchedulerEvent, RowTitleComp: PinRowHeader, rowHeightRem: rowHeightRem }), jsxRuntime.jsx("div", { children: jsxRuntime.jsx(CwButton, { onClick: () => {
|
|
9061
9183
|
onEvent(new OnClearPinned());
|
|
9062
|
-
}, children: "Clear pinned" }) })] })), jsxRuntime.jsx(Scheduler, { id: `${id}-notPinned`, state: { ...state, isHeaderVisible: !isFirstVisible }, header: header, rows: notPinnedRows, events: events, backgroundEvents: backgroundEvents, contextMenuItems: contextMenuItems, orderCategories: unPinnedOrderCategory, onEvent: onEvent, EventComp: SchedulerEvent, RowTitleComp: PinRowHeader, rowHeightRem: rowHeightRem })] }));
|
|
9184
|
+
}, children: "Clear pinned" }) })] })), jsxRuntime.jsx(Scheduler, { id: `${id}-notPinned`, state: { ...state, isHeaderVisible: !isFirstVisible }, header: header, rows: notPinnedRows, events: events, backgroundEvents: backgroundEvents, indicatorRows: indicatorRows, contextMenuItems: contextMenuItems, orderCategories: unPinnedOrderCategory, onEvent: onEvent, EventComp: SchedulerEvent, RowTitleComp: PinRowHeader, rowHeightRem: rowHeightRem })] }));
|
|
9063
9185
|
};
|
|
9064
9186
|
|
|
9065
9187
|
const CwFindAirport = ({ handleChange, searchType = "OnlyDatabase", placeHolder = "Search airport…", required = false, cblConfig, className = "", value, disabled = false, displayMode, initialDisplayText, labelProps, alignProps, width }) => {
|