@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 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
- cancelPendingClick();
8056
- timer.current = setTimeout(() => {
8057
- timer.current = null;
8058
- onClick(e);
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
- }, [cancelPendingClick, onDoubleClick]);
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 schedulerEnd = Temporal.PlainDate.from({
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
- const totalSubRows = rows.reduce((acc, row) => {
8627
- const filteredEvents = events.filter((it) => it.rowId === row.rowId);
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
- return acc + Math.max(innerRows.length, 1);
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 visibleRows = Math.min(stateVisibleRows ?? totalSubRows, totalSubRows);
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: getDefaultDivisions(visibleDays, selectedDate),
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 schedulerContentHeight = rowHeightRem * Math.max(visibleRows, 1);
8647
- const weekendsLines = getWeekendLinesByDatesVisible(selectedDate, visibleDays);
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
- // Memoized Row Component
8660
- const Row = React.memo(({ index, style }) => {
8661
- const row = rows[index];
8662
- const rowColor = groupRowColors
8663
- ? Math.floor(index / 2) % 2 === 0 ? evenColor : oddColor
8664
- : index % 2 === 0 ? evenColor : oddColor;
8665
- const header = {
8666
- value: row,
8667
- width: rowHeaderWidth,
8668
- onEvent: onEvent,
8669
- };
8670
- return (jsxRuntime.jsx("div", { style: {
8671
- ...style,
8672
- backgroundColor: rowColor
8673
- }, children: jsxRuntime.jsx(SchedulerRow, { events: events.filter((it) => it.rowId === row.rowId), backgroundEvents: backgroundEvents.filter((it) => it.rowId === row.rowId), rowHeader: header, EventComp: EventComp, BackgroundEventComp: BackgroundEventComp, RowTitleComp: RowTitleComp, weekendLines: weekendsLines, divisionLines: divisionLines, timeLinePercentage: timeLinePercentage, rowHeightInRem: rowHeightRem, onEvent: onEvent, contextMenuItems: contextMenuItems, visibleDays: visibleDays, selectedDate: selectedDate }) }, row.rowId));
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 = events.filter((it) => it.rowId === row.rowId);
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
- return rowsNumber * rowHeightRem * pixelsInRem;
8682
- }, [rows, events]);
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 }) => {