@cwellt_software/cwellt-reactjs-lib 1.3.2 → 1.3.5

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.
Files changed (41) hide show
  1. package/dist/index.cjs.js +336 -169
  2. package/dist/index.css +2 -2
  3. package/dist/index.d.ts +61 -7
  4. package/dist/index.es.js +336 -169
  5. package/dist/src/common/functions/colorManipulation.d.ts.map +1 -1
  6. package/dist/src/common/functions/useSingleAndDoubleClicks.d.ts.map +1 -1
  7. package/dist/src/components/control/choice/multi-filter/CwMultiFilter.d.ts +6 -0
  8. package/dist/src/components/control/choice/multi-filter/CwMultiFilter.d.ts.map +1 -1
  9. package/dist/src/components/control/input/file/CwFileUploadMultiple.d.ts +16 -0
  10. package/dist/src/components/control/input/file/CwFileUploadMultiple.d.ts.map +1 -1
  11. package/dist/src/components/control/input/new-dates/CwDatePicker.d.ts +10 -1
  12. package/dist/src/components/control/input/new-dates/CwDatePicker.d.ts.map +1 -1
  13. package/dist/src/components/control/input/new-dates/CwDateTimePicker.d.ts +6 -1
  14. package/dist/src/components/control/input/new-dates/CwDateTimePicker.d.ts.map +1 -1
  15. package/dist/src/components/control/input/new-dates/CwDateTimePickerCompact.d.ts +8 -1
  16. package/dist/src/components/control/input/new-dates/CwDateTimePickerCompact.d.ts.map +1 -1
  17. package/dist/src/components/custom/scheduler-new/presentation/NewScheduler.d.ts +2 -0
  18. package/dist/src/components/custom/scheduler-new/presentation/NewScheduler.d.ts.map +1 -1
  19. package/dist/src/components/custom/scheduler-new/presentation/components/row/IndicatorRow.d.ts +18 -0
  20. package/dist/src/components/custom/scheduler-new/presentation/components/row/IndicatorRow.d.ts.map +1 -0
  21. package/dist/src/components/custom/scheduler-new/presentation/components/row/SchedulerRow.d.ts +2 -0
  22. package/dist/src/components/custom/scheduler-new/presentation/components/row/SchedulerRow.d.ts.map +1 -1
  23. package/dist/src/components/custom/super-scheduler/SuperScheduler.d.ts +2 -0
  24. package/dist/src/components/custom/super-scheduler/SuperScheduler.d.ts.map +1 -1
  25. package/dist/src/components/display/data/table/CwTable.d.ts +2 -1
  26. package/dist/src/components/display/data/table/CwTable.d.ts.map +1 -1
  27. package/dist/src/components/display/text/message/CwMessage.d.ts +3 -2
  28. package/dist/src/components/display/text/message/CwMessage.d.ts.map +1 -1
  29. package/dist/src/index.d.ts +1 -0
  30. package/dist/src/index.d.ts.map +1 -1
  31. package/dist/src/main.d.ts +0 -1
  32. package/dist/src/main.d.ts.map +1 -1
  33. package/dist/src/playground/PlaygroundApp.d.ts +4 -0
  34. package/dist/src/playground/PlaygroundApp.d.ts.map +1 -0
  35. package/dist/src/playground/pages/ColorContrastPage.d.ts +3 -0
  36. package/dist/src/playground/pages/ColorContrastPage.d.ts.map +1 -0
  37. package/dist/src/playground/pages/DatePickerLocalePage.d.ts +3 -0
  38. package/dist/src/playground/pages/DatePickerLocalePage.d.ts.map +1 -0
  39. package/dist/src/playground/pages/TablePaginationPage.d.ts +3 -0
  40. package/dist/src/playground/pages/TablePaginationPage.d.ts.map +1 -0
  41. package/package.json +2 -2
package/dist/index.cjs.js CHANGED
@@ -125,36 +125,60 @@ function getHSLColor(color, alpha = 1) {
125
125
  const hsl = colorToHSL(color);
126
126
  return `hsla(${hsl.h}, ${hsl.s}%, ${hsl.l}%, ${alpha})`;
127
127
  }
128
+ function hslToLinearRgb(h, s, l) {
129
+ const sNorm = s / 100;
130
+ const lNorm = l / 100;
131
+ const a = sNorm * Math.min(lNorm, 1 - lNorm);
132
+ const f = (n) => {
133
+ const k = (n + h / 30) % 12;
134
+ return lNorm - a * Math.max(-1, Math.min(k - 3, Math.min(9 - k, 1)));
135
+ };
136
+ return [f(0), f(8), f(4)];
137
+ }
138
+ function srgbLuminance(r, g, b) {
139
+ const lin = (c) => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
140
+ return 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b);
141
+ }
142
+ function wcagContrastRatio(lum1, lum2) {
143
+ const lighter = Math.max(lum1, lum2);
144
+ const darker = Math.min(lum1, lum2);
145
+ return (lighter + 0.05) / (darker + 0.05);
146
+ }
128
147
  function getContrastColor(color) {
129
- if (color === null) {
148
+ if (color === null)
130
149
  return '#000000';
131
- }
132
150
  const hsl = colorToHSL(color);
133
- // Saturated greenyellow and cyan tones need lower luminance threshold (40-35)
134
- // Saturated mediumblue and blueviolet tones need higher luminance threshold (55-60)
135
- // Saturated orangered tones need lower luminance threshold (45)
136
- const isYellowGreen = (hsl.h >= 45 && hsl.h <= 180);
137
- const isBlueViolet = (hsl.h >= 210 && hsl.h <= 300);
138
- const isRedOrange = (hsl.h >= 0 && hsl.h <= 30) || (hsl.h >= 330 && hsl.h <= 360);
139
- let threshold = 50;
140
- if (isYellowGreen) {
141
- threshold = 40;
142
- if (hsl.s >= 70) {
143
- threshold = 35;
144
- }
145
- }
146
- else if (isBlueViolet) {
147
- threshold = 55;
148
- if (hsl.s >= 70) {
149
- threshold = 60;
150
- }
151
- }
152
- else if (isRedOrange && hsl.s >= 80) {
153
- threshold = 45;
154
- }
155
- const contrastL = hsl.l >= threshold ? 20 : 90;
156
151
  const contrastS = Math.min(hsl.s, 90);
157
- return `hsl(${hsl.h}, ${contrastS}%, ${contrastL}%)`;
152
+ const bgRgb = hslToLinearRgb(hsl.h, hsl.s, hsl.l);
153
+ const bgLum = srgbLuminance(...bgRgb);
154
+ const darkL = 15;
155
+ const lightL = 90;
156
+ const darkLum = srgbLuminance(...hslToLinearRgb(hsl.h, contrastS, darkL));
157
+ const lightLum = srgbLuminance(...hslToLinearRgb(hsl.h, contrastS, lightL));
158
+ const darkRatio = wcagContrastRatio(bgLum, darkLum);
159
+ const lightRatio = wcagContrastRatio(bgLum, lightLum);
160
+ // On saturated backgrounds, dark-tinted text blends visually with the
161
+ // vibrant color even when math says it has better contrast.
162
+ // Prefer light text unless dark has an overwhelmingly higher ratio.
163
+ const saturatedBias = hsl.s >= 30 && bgLum < 0.4;
164
+ // Mid-grays (#777–#999): dark text "sinks" into the background while
165
+ // white text pops, even though dark wins on WCAG ratio.
166
+ const midGrayBias = hsl.s < 15 && bgLum >= 0.18 && bgLum <= 0.35;
167
+ const goLight = saturatedBias
168
+ ? darkRatio < lightRatio * 3
169
+ : midGrayBias || lightRatio >= darkRatio;
170
+ let targetL = goLight ? lightL : darkL;
171
+ let bestRatio = goLight ? lightRatio : darkRatio;
172
+ if (bestRatio < 4.5) {
173
+ const step = goLight ? 5 : -5;
174
+ const limit = goLight ? 95 : 0;
175
+ while (bestRatio < 4.5 && targetL !== limit) {
176
+ targetL = goLight ? Math.min(targetL + step, limit) : Math.max(targetL + step, limit);
177
+ const candidateLum = srgbLuminance(...hslToLinearRgb(hsl.h, contrastS, targetL));
178
+ bestRatio = wcagContrastRatio(bgLum, candidateLum);
179
+ }
180
+ }
181
+ return `hsl(${hsl.h}, ${contrastS}%, ${targetL}%)`;
158
182
  }
159
183
 
160
184
  const SVG_ICONS = {
@@ -249,7 +273,7 @@ const CwMessage = props => {
249
273
  }, props.duration ?? CW_DEFAULT_MESSAGE_DURATION);
250
274
  return () => clearTimeout(timer);
251
275
  }, [props]);
252
- 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] }));
253
277
  };
254
278
  /**
255
279
  * Hook for displaying inline messages within specific components.
@@ -312,11 +336,11 @@ class CwMessageManager {
312
336
  document.body.prepend(this.messageWrapper);
313
337
  this.root = client.createRoot(this.messageWrapper); // Create a root at the messageWrapper
314
338
  }
315
- showMessage(message, type, duration) {
339
+ showMessage(message, type, duration, onClick) {
316
340
  const msg = document.createElement("div");
317
341
  this.messageWrapper?.prepend(msg);
318
342
  const msgRoot = client.createRoot(msg); // Create a root for the new message
319
- 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) }));
320
344
  }
321
345
  closeMessage(msgRoot) {
322
346
  msgRoot.unmount(); // Unmount the message root
@@ -338,8 +362,8 @@ class CwMessageManager {
338
362
  *
339
363
  * @note For inline messages within components, use `CwNote` or `useCwMessage` hook instead
340
364
  */
341
- function CwDisplayMessage(message, type, duration) {
342
- CwMessageManager.getInstance().showMessage(message, type, duration);
365
+ function CwDisplayMessage(message, type, duration, onClick) {
366
+ CwMessageManager.getInstance().showMessage(message, type, duration, onClick);
343
367
  }
344
368
 
345
369
  /**
@@ -1864,7 +1888,7 @@ function CwAccordionContainer(CwelltAccordionContainerProps) {
1864
1888
  *
1865
1889
  * @returns React component
1866
1890
  */
1867
- function CwTable({ columns, data, pagination = false, pageSizeOptions = [5, 10, 20, 50], expandedRowRender, onExpand, className, classNameRow, style, classNameContainer, id, textNoData = "No data available at the moment", rowKey = "key", loading = false, scrollHeight, stickyHeader = false, rowSelection }) {
1891
+ function CwTable({ columns, data, pagination = false, pageSizeOptions = [5, 10, 20, 50], expandedRowRender, onExpand, className, classNameRow, style, classNameContainer, id, textNoData = "No data available at the moment", rowKey = "key", loading = false, scrollHeight, stickyHeader = false, pageLabel = "page", rowSelection }) {
1868
1892
  const [currentPage, setCurrentPage] = React.useState(1);
1869
1893
  const [expandedRowKey, setExpandedRowKey] = React.useState(null);
1870
1894
  const [sortConfig, setSortConfig] = React.useState({
@@ -2049,16 +2073,16 @@ function CwTable({ columns, data, pagination = false, pageSizeOptions = [5, 10,
2049
2073
  ? "sortable"
2050
2074
  : sortConfig.direction === "asc"
2051
2075
  ? "sortable-asc"
2052
- : "sortable-desc" }))] }), jsxRuntime.jsx("span", { onMouseDown: (e) => startResize(e, col.key), className: "th-column-resizer" })] }, col.key)))] }) }), jsxRuntime.jsx("tbody", { children: renderTableBody() })] }) }), pagination && data.length > pageSizeOptions[0] && (jsxRuntime.jsxs("footer", { className: "cw-table-pagination", children: [jsxRuntime.jsx("button", { onClick: () => handlePageChange(1), disabled: currentPage === 1 || totalPages === 1, className: "cw-button-icon cwi-chevron-left-double", title: "First" }), jsxRuntime.jsx("button", { onClick: () => handlePageChange(currentPage - 1), disabled: currentPage === 1 || totalPages === 1, className: "cw-button-icon cwi-chevron-left", title: "Previous" }), jsxRuntime.jsx("input", { type: "text", inputMode: "numeric", value: currentPage, onChange: (e) => {
2053
- const value = parseInt(e.target.value, 10);
2054
- if (!isNaN(value))
2055
- handlePageChange(value);
2056
- }, onBlur: (e) => {
2057
- const value = parseInt(e.target.value, 10);
2058
- if (isNaN(value) || value < 1 || value > totalPages) {
2059
- handlePageChange(1);
2060
- }
2061
- }, min: 1, max: totalPages }), jsxRuntime.jsxs("span", { children: ["of ", totalPages] }), jsxRuntime.jsx("button", { onClick: () => handlePageChange(currentPage + 1), disabled: currentPage === totalPages || totalPages === 1, className: "cw-button-icon cwi-chevron-right", title: "Next" }), jsxRuntime.jsx("button", { onClick: () => handlePageChange(totalPages), disabled: currentPage === totalPages || totalPages === 1, className: "cw-button-icon cwi-chevron-right-double", title: "Last" }), jsxRuntime.jsx("select", { value: localItemsPerPage, onChange: handleItemsPerPageChange, children: pageSizeOptions.map(size => (jsxRuntime.jsxs("option", { value: size, children: [size, " / page"] }, size))) })] }))] }));
2076
+ : "sortable-desc" }))] }), jsxRuntime.jsx("span", { onMouseDown: (e) => startResize(e, col.key), className: "th-column-resizer" })] }, col.key)))] }) }), jsxRuntime.jsx("tbody", { children: renderTableBody() })] }) }), pagination && data.length > pageSizeOptions[0] && (jsxRuntime.jsxs("footer", { className: "cw-table-pagination", children: [jsxRuntime.jsxs("div", { className: "cw-table-pagination-size", children: [jsxRuntime.jsx(CwIcon, { iconId: "list" }), jsxRuntime.jsx("select", { value: localItemsPerPage, onChange: handleItemsPerPageChange, children: pageSizeOptions.map(size => (jsxRuntime.jsx("option", { value: size, children: pageLabel ? `${size} / ${pageLabel}` : size }, size))) })] }), jsxRuntime.jsxs("div", { className: "cw-table-pagination-nav", children: [jsxRuntime.jsx(CwButton, { onClick: () => handlePageChange(1), disabled: currentPage === 1 || totalPages === 1, variant: "icon", icon: "chevron-left-double" }), jsxRuntime.jsx(CwButton, { onClick: () => handlePageChange(currentPage - 1), disabled: currentPage === 1 || totalPages === 1, variant: "icon", icon: "chevron-left" }), jsxRuntime.jsx("input", { type: "text", inputMode: "numeric", value: currentPage, onChange: (e) => {
2077
+ const value = parseInt(e.target.value, 10);
2078
+ if (!isNaN(value))
2079
+ handlePageChange(value);
2080
+ }, onBlur: (e) => {
2081
+ const value = parseInt(e.target.value, 10);
2082
+ if (isNaN(value) || value < 1 || value > totalPages) {
2083
+ handlePageChange(1);
2084
+ }
2085
+ }, min: 1, max: totalPages }), jsxRuntime.jsx("span", { children: "/" }), jsxRuntime.jsx("span", { children: totalPages }), jsxRuntime.jsx(CwButton, { onClick: () => handlePageChange(currentPage + 1), disabled: currentPage === totalPages || totalPages === 1, variant: "icon", icon: "chevron-right" }), jsxRuntime.jsx(CwButton, { onClick: () => handlePageChange(totalPages), disabled: currentPage === totalPages || totalPages === 1, variant: "icon", icon: "chevron-right-double" })] })] }))] }));
2062
2086
  }
2063
2087
 
2064
2088
  var styles$i = {"dropIndicator":"cw-sortable-table-module__dropIndicator__ov-Jz","dragging":"cw-sortable-table-module__dragging__MrLrz","skeletonRow":"cw-sortable-table-module__skeletonRow__vyD0M","skeleton":"cw-sortable-table-module__skeleton__QGXAD","saveBar":"cw-sortable-table-module__saveBar__3OdoZ cw-flex-row cw-align-center-center cw-gap-small"};
@@ -2815,10 +2839,26 @@ function CwFileUpload(fileUploadProps) {
2815
2839
 
2816
2840
  var styles$e = {"fileUploadContainer":"cw-file-upload-multiple-module__fileUploadContainer__liEc1","hiddenInput":"cw-file-upload-multiple-module__hiddenInput__TZBBI","uploadArea":"cw-file-upload-multiple-module__uploadArea__DdOhs","uploadAreaDisabled":"cw-file-upload-multiple-module__uploadAreaDisabled__VWeFX","uploadTitle":"cw-file-upload-multiple-module__uploadTitle__gjRk8","uploadSubtitle":"cw-file-upload-multiple-module__uploadSubtitle__Z0S5t","filesContainer":"cw-file-upload-multiple-module__filesContainer__g44PY","fileItem":"cw-file-upload-multiple-module__fileItem__w27Dg","fileIcon":"cw-file-upload-multiple-module__fileIcon__iJJUX","fileExtension":"cw-file-upload-multiple-module__fileExtension__vOuHv","fileInfo":"cw-file-upload-multiple-module__fileInfo__R5ZTv","fileName":"cw-file-upload-multiple-module__fileName__DjepK","fileSize":"cw-file-upload-multiple-module__fileSize__b8GSm","smallButton":"cw-file-upload-multiple-module__smallButton__siUAh"};
2817
2841
 
2842
+ const DEFAULT_LABELS = {
2843
+ uploadDisabled: 'Upload disabled',
2844
+ clickToUpload: 'Click to upload or drag and drop',
2845
+ acceptedFiles: (accept) => `Accepted files: ${accept}`,
2846
+ singleFileOnly: '(Single file only)',
2847
+ allTypesAccepted: 'All file types accepted',
2848
+ changeFile: 'Change File',
2849
+ clearAll: 'Clear all',
2850
+ addMoreFiles: 'Add More Files',
2851
+ filesSelected: (count) => `${count} file${count !== 1 ? 's' : ''} selected`,
2852
+ fileTooLarge: (name, maxSize) => `File "${name}" is too large. Maximum size allowed is ${maxSize}MB.`,
2853
+ invalidFormat: (name, accepted) => `File "${name}" has an invalid format. Allowed formats: ${accepted}`,
2854
+ invalidMimeType: (name, types) => `File "${name}" has an invalid MIME type. Allowed types: ${types}`,
2855
+ alreadySelected: (name) => `File "${name}" is already selected.`,
2856
+ };
2818
2857
  function CwFileUploadMultiple(fileUploadProps) {
2819
2858
  const fileInputRef = React.useRef(null);
2820
2859
  const [selectedFiles, setSelectedFiles] = React.useState([]);
2821
2860
  const [existingFile, setExistingFile] = React.useState(fileUploadProps.initialFileName);
2861
+ const labels = { ...DEFAULT_LABELS, ...fileUploadProps.labels };
2822
2862
  React.useEffect(() => {
2823
2863
  setExistingFile(fileUploadProps.initialFileName);
2824
2864
  }, [fileUploadProps.initialFileName]);
@@ -2832,7 +2872,7 @@ function CwFileUploadMultiple(fileUploadProps) {
2832
2872
  if (fileSizeInMB > fileUploadProps.maxFileSize) {
2833
2873
  return {
2834
2874
  isValid: false,
2835
- error: `File "${file.name}" is too large. Maximum size allowed is ${fileUploadProps.maxFileSize}MB.`
2875
+ error: labels.fileTooLarge(file.name, fileUploadProps.maxFileSize)
2836
2876
  };
2837
2877
  }
2838
2878
  }
@@ -2849,7 +2889,7 @@ function CwFileUploadMultiple(fileUploadProps) {
2849
2889
  if (!isExtensionValid) {
2850
2890
  return {
2851
2891
  isValid: false,
2852
- error: `File "${file.name}" has an invalid format. Allowed formats: ${fileUploadProps.accept}`
2892
+ error: labels.invalidFormat(file.name, fileUploadProps.accept)
2853
2893
  };
2854
2894
  }
2855
2895
  }
@@ -2859,7 +2899,7 @@ function CwFileUploadMultiple(fileUploadProps) {
2859
2899
  if (!isMimeTypeValid) {
2860
2900
  return {
2861
2901
  isValid: false,
2862
- error: `File "${file.name}" has an invalid MIME type. Allowed types: ${fileUploadProps.allowedTypes.join(', ')}`
2902
+ error: labels.invalidMimeType(file.name, fileUploadProps.allowedTypes.join(', '))
2863
2903
  };
2864
2904
  }
2865
2905
  }
@@ -2873,7 +2913,7 @@ function CwFileUploadMultiple(fileUploadProps) {
2873
2913
  // Check if the file already exists (by name and size)
2874
2914
  const isDuplicate = existingFiles.some(existingFile => existingFile.name === file.name && existingFile.size === file.size);
2875
2915
  if (isDuplicate) {
2876
- errors.push(`File "${file.name}" is already selected.`);
2916
+ errors.push(labels.alreadySelected(file.name));
2877
2917
  continue;
2878
2918
  }
2879
2919
  const validation = validateFile(file);
@@ -2964,12 +3004,12 @@ function CwFileUploadMultiple(fileUploadProps) {
2964
3004
  }
2965
3005
  }
2966
3006
  };
2967
- return (jsxRuntime.jsxs("div", { className: `${styles$e.fileUploadContainer} ${fileUploadProps.className}`, children: [jsxRuntime.jsx("input", { ref: fileInputRef, type: "file", name: fileUploadProps.name, accept: fileUploadProps.accept, multiple: fileUploadProps.multiple, onChange: handleFileSelectInternal, disabled: fileUploadProps.disabled, "aria-label": "files", className: styles$e.hiddenInput }), selectedFiles.length === 0 && !existingFile ? (jsxRuntime.jsxs("div", { className: `${styles$e.uploadArea} ${fileUploadProps.disabled ? styles$e.uploadAreaDisabled : ''}`, onDragOver: handleDragOver, onDrop: handleDrop, onClick: !fileUploadProps.disabled ? handleButtonClick : undefined, children: [jsxRuntime.jsx(CwIcon, { iconId: "upload" }), jsxRuntime.jsx("p", { className: `${styles$e.uploadTitle}`, children: fileUploadProps.disabled ? 'Upload disabled' : 'Click to upload or drag and drop' }), jsxRuntime.jsxs("p", { className: `${styles$e.uploadSubtitle}`, children: [fileUploadProps.accept ? `Accepted files: ${fileUploadProps.accept}` : 'All file types accepted', !fileUploadProps.multiple && ' (Single file only)'] })] })) : selectedFiles.length === 0 && existingFile ? (jsxRuntime.jsxs("div", { className: styles$e.filesContainer, children: [jsxRuntime.jsxs("div", { className: styles$e.fileItem, children: [jsxRuntime.jsxs("div", { className: styles$e.fileIcon, children: [jsxRuntime.jsx(CwIcon, { iconId: "page" }), jsxRuntime.jsx("span", { className: styles$e.fileExtension, children: getFileExtension(existingFile) })] }), jsxRuntime.jsx("div", { className: styles$e.fileInfo, children: jsxRuntime.jsx("p", { className: styles$e.fileName, children: existingFile }) }), jsxRuntime.jsx(CwButton, { variant: "icon", icon: "close", color: "neutral", onClick: () => {
3007
+ return (jsxRuntime.jsxs("div", { className: `${styles$e.fileUploadContainer} ${fileUploadProps.className}`, children: [jsxRuntime.jsx("input", { ref: fileInputRef, type: "file", name: fileUploadProps.name, accept: fileUploadProps.accept, multiple: fileUploadProps.multiple, onChange: handleFileSelectInternal, disabled: fileUploadProps.disabled, "aria-label": "files", className: styles$e.hiddenInput }), selectedFiles.length === 0 && !existingFile ? (jsxRuntime.jsxs("div", { className: `${styles$e.uploadArea} ${fileUploadProps.disabled ? styles$e.uploadAreaDisabled : ''}`, onDragOver: handleDragOver, onDrop: handleDrop, onClick: !fileUploadProps.disabled ? handleButtonClick : undefined, children: [jsxRuntime.jsx(CwIcon, { iconId: "upload" }), jsxRuntime.jsx("p", { className: `${styles$e.uploadTitle}`, children: fileUploadProps.disabled ? labels.uploadDisabled : labels.clickToUpload }), jsxRuntime.jsxs("p", { className: `${styles$e.uploadSubtitle}`, children: [fileUploadProps.accept ? labels.acceptedFiles(fileUploadProps.accept) : labels.allTypesAccepted, !fileUploadProps.multiple && ` ${labels.singleFileOnly}`] })] })) : selectedFiles.length === 0 && existingFile ? (jsxRuntime.jsxs("div", { className: styles$e.filesContainer, children: [jsxRuntime.jsxs("div", { className: styles$e.fileItem, children: [jsxRuntime.jsxs("div", { className: styles$e.fileIcon, children: [jsxRuntime.jsx(CwIcon, { iconId: "page" }), jsxRuntime.jsx("span", { className: styles$e.fileExtension, children: getFileExtension(existingFile) })] }), jsxRuntime.jsx("div", { className: styles$e.fileInfo, children: jsxRuntime.jsx("p", { className: styles$e.fileName, children: existingFile }) }), jsxRuntime.jsx(CwButton, { variant: "icon", icon: "close", color: "neutral", onClick: () => {
2968
3008
  setExistingFile(undefined);
2969
3009
  if (fileUploadProps.onSelect) {
2970
3010
  fileUploadProps.onSelect(null);
2971
3011
  }
2972
- }, disabled: fileUploadProps.disabled, className: styles$e.smallButton })] }), jsxRuntime.jsx(CwButton, { text: "Change File", icon: "refresh", onClick: handleButtonClick, disabled: fileUploadProps.disabled })] })) : (jsxRuntime.jsxs("div", { className: styles$e.filesContainer, children: [jsxRuntime.jsxs("div", { className: "cw-flex-row cw-align-between-center", children: [jsxRuntime.jsxs("small", { className: styles$e.filesCount, children: [selectedFiles.length, " file", selectedFiles.length !== 1 ? 's' : '', " selected"] }), jsxRuntime.jsx(CwButton, { onClick: removeAllFiles, disabled: fileUploadProps.disabled, color: "danger", variant: "outline", icon: "delete", text: "Clear all" })] }), selectedFiles.map((file, index) => (jsxRuntime.jsxs("div", { className: styles$e.fileItem, children: [jsxRuntime.jsxs("div", { className: styles$e.fileIcon, children: [jsxRuntime.jsx(CwIcon, { iconId: "page" }), jsxRuntime.jsx("span", { className: styles$e.fileExtension, children: getFileExtension(file.name) })] }), jsxRuntime.jsxs("div", { className: styles$e.fileInfo, children: [jsxRuntime.jsx("p", { className: styles$e.fileName, children: file.name }), jsxRuntime.jsxs("p", { className: styles$e.fileSize, children: [(file.size / 1024).toFixed(1), " KB"] })] }), jsxRuntime.jsx(CwButton, { variant: "icon", icon: "close", color: "neutral", onClick: () => removeFile(index), className: styles$e.smallButton })] }, index))), fileUploadProps.multiple && (jsxRuntime.jsx(CwButton, { text: "Add More Files", icon: "plus", variant: "outline", onClick: handleButtonClick, disabled: fileUploadProps.disabled })), !fileUploadProps.multiple && (jsxRuntime.jsx(CwButton, { text: "Change File", icon: "refresh", onClick: handleButtonClick, disabled: fileUploadProps.disabled }))] }))] }));
3012
+ }, disabled: fileUploadProps.disabled, className: styles$e.smallButton })] }), jsxRuntime.jsx(CwButton, { text: labels.changeFile, icon: "refresh", onClick: handleButtonClick, disabled: fileUploadProps.disabled })] })) : (jsxRuntime.jsxs("div", { className: styles$e.filesContainer, children: [jsxRuntime.jsxs("div", { className: "cw-flex-row cw-align-between-center", children: [jsxRuntime.jsx("small", { className: styles$e.filesCount, children: labels.filesSelected(selectedFiles.length) }), jsxRuntime.jsx(CwButton, { onClick: removeAllFiles, disabled: fileUploadProps.disabled, color: "danger", variant: "outline", icon: "delete", text: labels.clearAll })] }), selectedFiles.map((file, index) => (jsxRuntime.jsxs("div", { className: styles$e.fileItem, children: [jsxRuntime.jsxs("div", { className: styles$e.fileIcon, children: [jsxRuntime.jsx(CwIcon, { iconId: "page" }), jsxRuntime.jsx("span", { className: styles$e.fileExtension, children: getFileExtension(file.name) })] }), jsxRuntime.jsxs("div", { className: styles$e.fileInfo, children: [jsxRuntime.jsx("p", { className: styles$e.fileName, children: file.name }), jsxRuntime.jsxs("p", { className: styles$e.fileSize, children: [(file.size / 1024).toFixed(1), " KB"] })] }), jsxRuntime.jsx(CwButton, { variant: "icon", icon: "close", color: "neutral", onClick: () => removeFile(index), className: styles$e.smallButton })] }, index))), fileUploadProps.multiple && (jsxRuntime.jsx(CwButton, { text: labels.addMoreFiles, icon: "plus", variant: "outline", onClick: handleButtonClick, disabled: fileUploadProps.disabled })), !fileUploadProps.multiple && (jsxRuntime.jsx(CwButton, { text: labels.changeFile, icon: "refresh", onClick: handleButtonClick, disabled: fileUploadProps.disabled }))] }))] }));
2973
3013
  }
2974
3014
 
2975
3015
  function CwInput(CwInputProps) {
@@ -4329,7 +4369,7 @@ const CwMultiFilterTag = props => {
4329
4369
  } : undefined }) }));
4330
4370
  };
4331
4371
 
4332
- var styles$7 = {"cw-multi-filter-catalog-container":"cw-multi-filter-module__cw-multi-filter-catalog-container__S3nsq","cw-multi-filter":"cw-multi-filter-module__cw-multi-filter__zipBK","cw-multi-filter-search":"cw-multi-filter-module__cw-multi-filter-search__eyHr0"};
4372
+ var styles$7 = {"cw-multi-filter-catalog-container":"cw-multi-filter-module__cw-multi-filter-catalog-container__S3nsq","cw-multi-filter":"cw-multi-filter-module__cw-multi-filter__zipBK","category-selected":"cw-multi-filter-module__category-selected__eYbes","cw-multi-filter-search":"cw-multi-filter-module__cw-multi-filter-search__eyHr0"};
4333
4373
 
4334
4374
  /**
4335
4375
  * A multiple filter selector, a MULTI-SELECT even. Allows users to select and filter items based on tags.
@@ -4470,7 +4510,7 @@ var styles$7 = {"cw-multi-filter-catalog-container":"cw-multi-filter-module__cw-
4470
4510
  * @param {CwMultiFilterProps} props
4471
4511
  * @returns Set the `onChange` callback to a function to check for changes in the selected filters
4472
4512
  */
4473
- const CwMultiFilter = ({ allTags, id, onChangeSelectedTags, selectedTags, style }) => {
4513
+ const CwMultiFilter = ({ allTags, id, onChangeSelectedTags, selectedTags, placeholder = "Write to filter", allCategoriesLabel = "ALL CATEGORIES", className, style }) => {
4474
4514
  const [filteredTags, setFilteredTags] = React.useState(new Set());
4475
4515
  const [inputTextValue, setInputTextValue] = React.useState("");
4476
4516
  const [isPanelOpen, setIsPanelOpen] = React.useState(false);
@@ -4572,9 +4612,9 @@ const CwMultiFilter = ({ allTags, id, onChangeSelectedTags, selectedTags, style
4572
4612
  inputRef.current?.focus();
4573
4613
  }
4574
4614
  };
4575
- return (jsxRuntime.jsxs("form", { ref: componentRef, id: id, className: styles$7["cw-multi-filter"], style: style, onSubmit: (e) => {
4615
+ return (jsxRuntime.jsxs("form", { ref: componentRef, id: id, className: `${styles$7["cw-multi-filter"]}${className ? ` ${className}` : ""}`, style: style, onSubmit: (e) => {
4576
4616
  e.preventDefault();
4577
- }, children: [jsxRuntime.jsxs("div", { onClick: handleDivClick, className: styles$7["cw-multi-filter-search"], style: isPanelOpen ? { outline: "1px solid var(--cw-color-primary)", outlineOffset: "-2px" } : {}, children: [jsxRuntime.jsxs("ul", { id: id + "_selected_filters", children: [Array.from(selectedTags).map(tag => (React.createElement(CwMultiFilterTag, { ...tag, key: tag.ID, Selectable: false, Removable: true, OnRemove: () => removeTag(tag.ID) }))), jsxRuntime.jsx("input", { type: "text", id: id + "_input", ref: inputRef, value: inputTextValue, spellCheck: false, onFocus: () => setIsPanelOpen(true), onChange: e => handleInputText(e.target.value), autoComplete: "off", placeholder: "Write to filter", onKeyDown: e => {
4617
+ }, children: [jsxRuntime.jsxs("div", { onClick: handleDivClick, className: styles$7["cw-multi-filter-search"], style: isPanelOpen ? { outline: "1px solid var(--cw-color-primary)", outlineOffset: "-2px" } : {}, children: [jsxRuntime.jsxs("ul", { id: id + "_selected_filters", children: [Array.from(selectedTags).map(tag => (React.createElement(CwMultiFilterTag, { ...tag, key: tag.ID, Selectable: false, Removable: true, OnRemove: () => removeTag(tag.ID) }))), jsxRuntime.jsx("input", { type: "text", id: id + "_input", ref: inputRef, value: inputTextValue, spellCheck: false, onFocus: () => setIsPanelOpen(true), onChange: e => handleInputText(e.target.value), autoComplete: "off", placeholder: placeholder, onKeyDown: e => {
4578
4618
  switch (e.key) {
4579
4619
  case "Enter":
4580
4620
  case "Tab": {
@@ -4604,15 +4644,7 @@ const CwMultiFilter = ({ allTags, id, onChangeSelectedTags, selectedTags, style
4604
4644
  } })] }), selectedTags.size > 0 ? (jsxRuntime.jsx("input", { type: "reset", value: "\u00D7", onClick: e => {
4605
4645
  e.preventDefault();
4606
4646
  onChangeSelectedTags(new Set());
4607
- } })) : null] }), jsxRuntime.jsxs("section", { className: styles$7["cw-multi-filter-catalog-container"], "data-display-none": !isPanelOpen, children: [jsxRuntime.jsxs("nav", { children: [jsxRuntime.jsx("button", { style: selectedCategory === "All"
4608
- ? {
4609
- backgroundColor: "var(--cw-color-primary-container)",
4610
- color: "var(--cw-color-primary)",
4611
- outline: "2px solid var(--cw-color-primary)",
4612
- outlineOffset: "-2px",
4613
- fontWeight: 400
4614
- }
4615
- : {}, onClick: () => handleClickCategory("All"), children: "SEARCH IN ALL CATEGORIES" }), Array.from(categoriesMappedToTags().keys()).map(category => (jsxRuntime.jsx("button", { style: selectedCategory === category
4647
+ } })) : null] }), jsxRuntime.jsxs("section", { className: styles$7["cw-multi-filter-catalog-container"], "data-display-none": !isPanelOpen, children: [jsxRuntime.jsxs("nav", { children: [jsxRuntime.jsx("button", { className: selectedCategory === "All" ? styles$7["category-selected"] : undefined, onClick: () => handleClickCategory("All"), children: allCategoriesLabel }), Array.from(categoriesMappedToTags().keys()).map(category => (jsxRuntime.jsx("button", { style: selectedCategory === category
4616
4648
  ? {
4617
4649
  backgroundColor: getColor(category).primary,
4618
4650
  color: getColor(category).onPrimary,
@@ -5162,10 +5194,13 @@ function usePickerPopup({ anchorRef, isOpen, onClose, position = "left-bottom" }
5162
5194
  return { popupRef, popupStyle: style, renderPopup };
5163
5195
  }
5164
5196
 
5165
- function CwDatePicker({ value, onChange, minDate, maxDate, disabledDates, disabledMatcher, defaultMonth, labelProps, alignProps, placeholder = "Select a date", displayFormat = "dd.MM.yyyy", disabled, required, className, showClear = true, popupPosition = "left-bottom", numberOfMonths = 1, showTodayButton = false, }) {
5197
+ function CwDatePicker({ value, onChange, minDate, maxDate, disabledDates, disabledMatcher, defaultMonth, labelProps, alignProps, placeholder = "Select a date", displayFormat = "dd.MM.yyyy", disabled, required, className, showClear = true, popupPosition = "left-bottom", numberOfMonths = 1, showTodayButton = false, locale: locale$1 = locale.enGB, todayLabel = "Today", feedback, }) {
5166
5198
  const [isOpen, setIsOpen] = React.useState(false);
5167
5199
  const [inputValue, setInputValue] = React.useState("");
5168
5200
  const containerRef = React.useRef(null);
5201
+ const feedbackMessages = feedback
5202
+ ? Array.isArray(feedback) ? feedback : [feedback]
5203
+ : [];
5169
5204
  const wrapperRef = React.useRef(null);
5170
5205
  const inputRef = React.useRef(null);
5171
5206
  const prevValueRef = React.useRef(undefined);
@@ -5333,11 +5368,11 @@ function CwDatePicker({ value, onChange, minDate, maxDate, disabledDates, disabl
5333
5368
  ...(maxDate ? [{ after: maxDate }] : []),
5334
5369
  ...(disabledMatcher ? [disabledMatcher] : []),
5335
5370
  ], [disabledDates, minDate, maxDate, disabledMatcher]);
5336
- return (jsxRuntime.jsx("div", { ref: containerRef, className: `cw-datepicker ${className || ""}`, children: jsxRuntime.jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsxRuntime.jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxRuntime.jsxs("div", { ref: wrapperRef, className: styles$4.pickerWrapper, children: [jsxRuntime.jsx("input", { ref: inputRef, type: "text", value: inputValue, placeholder: placeholder, onChange: handleInputChange, onBlur: handleInputBlur, onClick: handleInputClick, onKeyDown: handleInputKeyDown, disabled: disabled, required: required }), jsxRuntime.jsx("div", { className: styles$4.pickerIcons, children: showClear && value && !disabled ? (jsxRuntime.jsx(CwButton, { type: "button", variant: "icon", color: "neutral", icon: "close", onClick: handleClear, tabIndex: -1, "aria-label": "Clear date" })) : (jsxRuntime.jsx(CwIcon, { iconId: "calendar" })) }), renderPopup(jsxRuntime.jsxs("div", { ref: popupRef, className: styles$4.pickerPopup, style: popupStyle, children: [jsxRuntime.jsx(reactDayPicker.DayPicker, { mode: "single", selected: value || undefined, defaultMonth: defaultMonth || value || undefined, onSelect: handleDaySelect, disabled: disabledDays, numberOfMonths: numberOfMonths, modifiers: {
5337
- today: new Date(),
5338
- }, modifiersClassNames: {
5339
- today: "rdp-day-today",
5340
- } }), showTodayButton && (jsxRuntime.jsx("footer", { className: "cw-flex-row cw-align-right-center", children: jsxRuntime.jsx(CwButton, { type: "button", variant: "outline", onClick: handleTodayClick, text: "Today" }) }))] }))] })] }) }));
5371
+ return (jsxRuntime.jsxs("div", { ref: containerRef, className: `cw-datepicker ${className || ""}`, children: [jsxRuntime.jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsxRuntime.jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxRuntime.jsxs("div", { ref: wrapperRef, className: styles$4.pickerWrapper, children: [jsxRuntime.jsx("input", { ref: inputRef, type: "text", value: inputValue, placeholder: placeholder, onChange: handleInputChange, onBlur: handleInputBlur, onClick: handleInputClick, onKeyDown: handleInputKeyDown, disabled: disabled, required: required }), jsxRuntime.jsx("div", { className: styles$4.pickerIcons, children: showClear && value && !disabled ? (jsxRuntime.jsx(CwButton, { type: "button", variant: "icon", color: "neutral", icon: "close", onClick: handleClear, tabIndex: -1, "aria-label": "Clear date" })) : (jsxRuntime.jsx(CwIcon, { iconId: "calendar" })) }), renderPopup(jsxRuntime.jsxs("div", { ref: popupRef, className: styles$4.pickerPopup, style: popupStyle, children: [jsxRuntime.jsx(reactDayPicker.DayPicker, { mode: "single", selected: value || undefined, defaultMonth: defaultMonth || value || undefined, onSelect: handleDaySelect, disabled: disabledDays, numberOfMonths: numberOfMonths, locale: locale$1, modifiers: {
5372
+ today: new Date(),
5373
+ }, modifiersClassNames: {
5374
+ today: "rdp-day-today",
5375
+ } }), showTodayButton && (jsxRuntime.jsx("footer", { className: "cw-flex-row cw-align-right-center", children: jsxRuntime.jsx(CwButton, { type: "button", variant: "outline", onClick: handleTodayClick, text: todayLabel }) }))] }))] })] }), feedbackMessages.map((feedbackItem, index) => (jsxRuntime.jsx("p", { className: "cw-input-info", "data-color": feedbackItem.type, children: feedbackItem.message }, index)))] }));
5341
5376
  }
5342
5377
 
5343
5378
  var rangeStyles = {"rangeWrapper":"cw-range-picker-module__rangeWrapper__1nIVs","rangePopup":"cw-range-picker-module__rangePopup__E5jd1","presetList":"cw-range-picker-module__presetList__INiLo"};
@@ -6185,7 +6220,7 @@ function combineDateTimeInOffset(date, hours, minutes, timezoneOffset) {
6185
6220
  return new Date(zdt.epochMilliseconds);
6186
6221
  }
6187
6222
 
6188
- function CwDateTimePicker({ value, onChange, minDateTime, maxDateTime, disabledDates, disabledMatcher, timeInterval = 15, minTime, maxTime, labelProps, alignProps, datePlaceholder = "dd.MM.yyyy", timePlaceholder = "HH:mm", disabled, required, className, showClear = true, popupPosition = "left-bottom", numberOfMonths = 1, showNowButton = false, timezoneOffset, }) {
6223
+ function CwDateTimePicker({ value, onChange, minDateTime, maxDateTime, disabledDates, disabledMatcher, timeInterval = 15, minTime, maxTime, labelProps, alignProps, datePlaceholder = "dd.MM.yyyy", timePlaceholder = "HH:mm", disabled, required, className, showClear = true, popupPosition = "left-bottom", numberOfMonths = 1, showNowButton = false, nowLabel = "Now", locale: locale$1 = locale.enGB, timezoneOffset, }) {
6189
6224
  // ========================================
6190
6225
  // PROPS NORMALIZATION
6191
6226
  // ========================================
@@ -6425,12 +6460,12 @@ function CwDateTimePicker({ value, onChange, minDateTime, maxDateTime, disabledD
6425
6460
  const displayMaxDate = React.useMemo(() => normalizedMaxDateTime && timezoneOffset !== undefined
6426
6461
  ? getDateInOffset(normalizedMaxDateTime, timezoneOffset)
6427
6462
  : normalizedMaxDateTime, [normalizedMaxDateTime, timezoneOffset]);
6428
- return jsxRuntime.jsx("div", { className: `cw-datetimepicker cw-datetimepicker-separate ${className || ""}`, children: jsxRuntime.jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsxRuntime.jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxRuntime.jsxs("div", { className: "cw-flex-row cw-align-left-center cw-gap-small", children: [jsxRuntime.jsx(CwDatePicker, { value: selectedDate, onChange: handleDateChange, minDate: displayMinDate, maxDate: displayMaxDate, disabledDates: disabledDates, disabledMatcher: disabledMatcher, placeholder: datePlaceholder, disabled: disabled, required: required, showClear: showClear, numberOfMonths: numberOfMonths, popupPosition: popupPosition }), jsxRuntime.jsx(CwTimePicker, { value: selectedTime, onChange: handleTimeChange, interval: timeInterval, minTime: timeRestrictions.minTime, maxTime: timeRestrictions.maxTime, placeholder: timePlaceholder, disabled: disabled, required: required, showClear: showClear, popupPosition: popupPosition }), showNowButton && (jsxRuntime.jsx(CwButton, { type: "button", variant: "outline", onClick: handleNowClick, disabled: disabled, title: "Set to current date and time", text: "Now" }))] })] }) });
6463
+ return jsxRuntime.jsx("div", { className: `cw-datetimepicker cw-datetimepicker-separate ${className || ""}`, children: jsxRuntime.jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsxRuntime.jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxRuntime.jsxs("div", { className: "cw-flex-row cw-align-left-center cw-gap-small", children: [jsxRuntime.jsx(CwDatePicker, { value: selectedDate, onChange: handleDateChange, minDate: displayMinDate, maxDate: displayMaxDate, disabledDates: disabledDates, disabledMatcher: disabledMatcher, placeholder: datePlaceholder, disabled: disabled, required: required, showClear: showClear, numberOfMonths: numberOfMonths, popupPosition: popupPosition, locale: locale$1 }), jsxRuntime.jsx(CwTimePicker, { value: selectedTime, onChange: handleTimeChange, interval: timeInterval, minTime: timeRestrictions.minTime, maxTime: timeRestrictions.maxTime, placeholder: timePlaceholder, disabled: disabled, required: required, showClear: showClear, popupPosition: popupPosition }), showNowButton && (jsxRuntime.jsx(CwButton, { type: "button", variant: "outline", onClick: handleNowClick, disabled: disabled, text: nowLabel }))] })] }) });
6429
6464
  }
6430
6465
 
6431
6466
  var compactStyles = {"compactPopup":"cw-datetime-compact-module__compactPopup__GiuNY","calendarWrapper":"cw-datetime-compact-module__calendarWrapper__P4Nlq","timeWrapper":"cw-datetime-compact-module__timeWrapper__uMe-A","compactTimeList":"cw-datetime-compact-module__compactTimeList__MzSQT"};
6432
6467
 
6433
- function CwDateTimePickerCompact({ value, onChange, minDateTime, maxDateTime, disabledDates, disabledMatcher, timeInterval = 15, minTime, maxTime, labelProps, alignProps, placeholder = "dd.mm.yyyy HH:mm", disabled, required, className, showClear = true, popupPosition = "left-bottom", numberOfMonths = 1, showTodayButton = false, }) {
6468
+ function CwDateTimePickerCompact({ value, onChange, minDateTime, maxDateTime, disabledDates, disabledMatcher, timeInterval = 15, minTime, maxTime, labelProps, alignProps, placeholder = "dd.mm.yyyy HH:mm", disabled, required, className, showClear = true, popupPosition = "left-bottom", numberOfMonths = 1, showTodayButton = false, nowLabel = "Now", timeLabel = "Time", locale: locale$1 = locale.enGB, }) {
6434
6469
  const [isOpen, setIsOpen] = React.useState(false);
6435
6470
  const [inputValue, setInputValue] = React.useState("");
6436
6471
  const [selectedDate, setSelectedDate] = React.useState(value);
@@ -6661,11 +6696,11 @@ function CwDateTimePickerCompact({ value, onChange, minDateTime, maxDateTime, di
6661
6696
  ...(maxDateTime ? [{ after: maxDateTime }] : []),
6662
6697
  ...(disabledMatcher ? [disabledMatcher] : []),
6663
6698
  ], [disabledDates, minDateTime, maxDateTime, disabledMatcher]);
6664
- return (jsxRuntime.jsx("div", { ref: containerRef, className: `cw-datetimepicker ${className || ""}`, children: jsxRuntime.jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsxRuntime.jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxRuntime.jsxs("div", { ref: wrapperRef, className: styles$4.pickerWrapper, children: [jsxRuntime.jsx("input", { ref: inputRef, type: "text", value: inputValue, placeholder: placeholder, onChange: handleInputChange, onBlur: handleInputBlur, onClick: handleInputClick, onKeyDown: handleInputKeyDown, disabled: disabled, required: required, maxLength: 16, style: { width: "24ch" } }), jsxRuntime.jsx("div", { className: styles$4.pickerIcons, children: showClear && value && !disabled ? (jsxRuntime.jsx(CwButton, { type: "button", variant: "icon", color: "neutral", icon: "close", onClick: handleClear, tabIndex: -1, "aria-label": "Clear datetime" })) : (jsxRuntime.jsx(CwIcon, { iconId: "calendar-time" })) }), renderPopup(jsxRuntime.jsxs("div", { ref: popupRef, className: `${styles$4.pickerPopup} ${compactStyles.compactPopup}`, style: popupStyle, children: [jsxRuntime.jsxs("div", { className: compactStyles.calendarWrapper, children: [jsxRuntime.jsx(reactDayPicker.DayPicker, { mode: "single", selected: selectedDate, onSelect: handleDaySelect, defaultMonth: selectedDate, disabled: disabledDays, numberOfMonths: numberOfMonths, modifiers: {
6699
+ return (jsxRuntime.jsx("div", { ref: containerRef, className: `cw-datetimepicker ${className || ""}`, children: jsxRuntime.jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsxRuntime.jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxRuntime.jsxs("div", { ref: wrapperRef, className: styles$4.pickerWrapper, children: [jsxRuntime.jsx("input", { ref: inputRef, type: "text", value: inputValue, placeholder: placeholder, onChange: handleInputChange, onBlur: handleInputBlur, onClick: handleInputClick, onKeyDown: handleInputKeyDown, disabled: disabled, required: required, maxLength: 16 }), jsxRuntime.jsx("div", { className: styles$4.pickerIcons, children: showClear && value && !disabled ? (jsxRuntime.jsx(CwButton, { type: "button", variant: "icon", color: "neutral", icon: "close", onClick: handleClear, tabIndex: -1, "aria-label": "Clear datetime" })) : (jsxRuntime.jsx(CwIcon, { iconId: "calendar-time" })) }), renderPopup(jsxRuntime.jsxs("div", { ref: popupRef, className: `${styles$4.pickerPopup} ${compactStyles.compactPopup}`, style: popupStyle, children: [jsxRuntime.jsxs("div", { className: compactStyles.calendarWrapper, children: [jsxRuntime.jsx(reactDayPicker.DayPicker, { mode: "single", selected: selectedDate, onSelect: handleDaySelect, defaultMonth: selectedDate, disabled: disabledDays, numberOfMonths: numberOfMonths, locale: locale$1, modifiers: {
6665
6700
  today: new Date(),
6666
6701
  }, modifiersClassNames: {
6667
6702
  today: "rdp-day-today",
6668
- } }), showTodayButton && (jsxRuntime.jsx("footer", { className: "cw-flex-row cw-align-center-center", children: jsxRuntime.jsx(CwButton, { type: "button", variant: "outline", icon: "check-big", onClick: handleNowClick, text: "Now" }) }))] }), jsxRuntime.jsxs("div", { className: compactStyles.timeWrapper, children: [jsxRuntime.jsxs("header", { children: [jsxRuntime.jsx(CwIcon, { iconId: "clock", size: "medium" }), jsxRuntime.jsx("span", { children: "Time" })] }), jsxRuntime.jsx("div", { ref: timeListRef, className: `${timeStyles.timePickerList} ${compactStyles.compactTimeList}`, children: timeOptions.map((time) => {
6703
+ } }), showTodayButton && (jsxRuntime.jsx("footer", { className: "cw-flex-row cw-align-center-center", children: jsxRuntime.jsx(CwButton, { type: "button", variant: "outline", icon: "check-big", onClick: handleNowClick, text: nowLabel }) }))] }), jsxRuntime.jsxs("div", { className: compactStyles.timeWrapper, children: [jsxRuntime.jsxs("header", { children: [jsxRuntime.jsx(CwIcon, { iconId: "clock", size: "medium" }), jsxRuntime.jsx("span", { children: timeLabel })] }), jsxRuntime.jsx("div", { ref: timeListRef, className: `${timeStyles.timePickerList} ${compactStyles.compactTimeList}`, children: timeOptions.map((time) => {
6669
6704
  const isSelected = time === selectedTime;
6670
6705
  return (jsxRuntime.jsx("button", { type: "button", className: isSelected ? timeStyles.selected : "", onClick: () => handleTimeSelect(time), children: time }, time));
6671
6706
  }) })] })] }))] })] }) }));
@@ -8005,30 +8040,95 @@ const BackgroundEvent = ({ value, heightRem }) => {
8005
8040
  }, children: value.icons }) : null, jsxRuntime.jsx("span", { className: styles$2["scheduler-event-text"], children: value.name })] }) }) }) }) })) : null;
8006
8041
  };
8007
8042
 
8043
+ const eventIsVisible = (startDate, endDate, selectedDate, visibleDays) => {
8044
+ const schedulerEnd = Temporal.PlainDate.from({
8045
+ year: selectedDate.getFullYear(),
8046
+ month: selectedDate.getMonth() + 1,
8047
+ day: selectedDate.getDate(),
8048
+ }).add({ days: visibleDays });
8049
+ const schedulerEndDate = new Date(schedulerEnd.year, schedulerEnd.month - 1, schedulerEnd.day);
8050
+ const isBefore = selectedDate > startDate && selectedDate > endDate;
8051
+ const isAfter = schedulerEndDate < startDate && schedulerEndDate < endDate;
8052
+ return !isBefore && !isAfter;
8053
+ };
8054
+
8055
+ const hoursBetween = (date1, date2) => {
8056
+ const oneHourInMillis = 60 * 60 * 1000; // hours*minutes*seconds*milliseconds
8057
+ const timeDiff = date2.getTime() - date1.getTime();
8058
+ return timeDiff / oneHourInMillis;
8059
+ };
8060
+ const getEventSizes = (schedulerDate, totalDays, startDate, endDate) => {
8061
+ if (!eventIsVisible(startDate, endDate, schedulerDate, totalDays)) {
8062
+ return {
8063
+ left: 0,
8064
+ width: 0,
8065
+ };
8066
+ }
8067
+ const totalHours = totalDays * 24;
8068
+ // const dateString = schedulerDate.toISOString().split('T')[0];
8069
+ //const schedulerDateAtZero = new Date(dateString)
8070
+ let startHours = hoursBetween(schedulerDate, startDate);
8071
+ let durationHours = hoursBetween(startDate, endDate);
8072
+ const startOutOfScheduler = startHours < 0;
8073
+ if (startOutOfScheduler) {
8074
+ durationHours += startHours;
8075
+ startHours = 0;
8076
+ }
8077
+ const left = (startHours / totalHours) * 100;
8078
+ // Minimum width equivalent to 15 minutes so zero/near-zero duration events stay visible
8079
+ const MIN_WIDTH_HOURS = 0.25;
8080
+ const minWidth = (MIN_WIDTH_HOURS * 100) / totalHours;
8081
+ const width = Math.max((durationHours * 100) / totalHours, minWidth);
8082
+ return {
8083
+ left,
8084
+ width,
8085
+ };
8086
+ };
8087
+
8088
+ const INDICATOR_HEIGHT_REM = 0.25;
8089
+ const IndicatorRow = ({ indicators, selectedDate, visibleDays }) => {
8090
+ return (jsxRuntime.jsx("div", { style: {
8091
+ position: "relative",
8092
+ height: `${INDICATOR_HEIGHT_REM}rem`,
8093
+ pointerEvents: "none",
8094
+ }, "data-name": "indicator-row", children: indicators.map((indicator) => {
8095
+ const { left, width } = getEventSizes(selectedDate, visibleDays, indicator.start, indicator.end);
8096
+ if (width <= 0)
8097
+ return null;
8098
+ const bar = (jsxRuntime.jsx("div", { style: {
8099
+ position: "absolute",
8100
+ left: `${left}%`,
8101
+ width: `${width}%`,
8102
+ height: `${INDICATOR_HEIGHT_REM}rem`,
8103
+ backgroundColor: indicator.color,
8104
+ borderRadius: "1px",
8105
+ pointerEvents: "auto",
8106
+ } }, indicator.id));
8107
+ if (indicator.tooltip) {
8108
+ return (jsxRuntime.jsx(CwTooltipNew, { content: indicator.tooltip, position: "bottom", dissapearsWhenHover: true, showDelay: 200, children: bar }, indicator.id));
8109
+ }
8110
+ return bar;
8111
+ }) }));
8112
+ };
8113
+
8114
+ // Fires onClick on the first click of a sequence (detail === 1) and
8115
+ // onDoubleClick on the native dblclick event. Subsequent clicks of a
8116
+ // double-click sequence (detail > 1) are suppressed so the same logical
8117
+ // interaction does not fire onClick twice. Selection therefore appears
8118
+ // within one paint frame instead of being delayed by an artificial timer.
8008
8119
  function useSingleAndDoubleClicks(onClick, onDoubleClick) {
8009
- const timer = React.useRef(null);
8010
- const cancelPendingClick = React.useCallback(() => {
8011
- if (timer.current) {
8012
- clearTimeout(timer.current);
8013
- timer.current = null;
8014
- }
8015
- }, [timer]);
8016
8120
  const handleClick = React.useCallback((e) => {
8017
- // We only cache the most recent click event, so cancel any pending clicks
8018
8121
  e.stopPropagation();
8019
8122
  e.preventDefault();
8020
- cancelPendingClick();
8021
- timer.current = setTimeout(() => {
8022
- timer.current = null;
8023
- onClick(e);
8024
- }, 500);
8025
- }, [timer, cancelPendingClick, onClick]);
8123
+ if (e.detail > 1)
8124
+ return;
8125
+ onClick(e);
8126
+ }, [onClick]);
8026
8127
  const handleDoubleClick = React.useCallback((e) => {
8027
8128
  e.stopPropagation();
8028
8129
  e.preventDefault();
8029
- cancelPendingClick();
8030
8130
  onDoubleClick(e);
8031
- }, [cancelPendingClick, onDoubleClick]);
8131
+ }, [onDoubleClick]);
8032
8132
  return { handleClick, handleDoubleClick };
8033
8133
  }
8034
8134
 
@@ -8202,7 +8302,7 @@ const SchedulerEvent = ({ value, heightRem, onEvent }) => {
8202
8302
 
8203
8303
  const SchedulerRow = React.memo((props) => {
8204
8304
  const [isContextMenuOpen, setIsContextMenuOpen] = React.useState(false);
8205
- const { events, backgroundEvents, rowHeader, contextMenuItems, RowTitleComp, EventComp, BackgroundEventComp, weekendLines, divisionLines, timeLinePercentage, selectedDate, visibleDays, onEvent, } = props;
8305
+ const { events, backgroundEvents, indicatorRows, rowHeader, contextMenuItems, RowTitleComp, EventComp, BackgroundEventComp, weekendLines, divisionLines, timeLinePercentage, selectedDate, visibleDays, onEvent, } = props;
8206
8306
  const internalRows = separateEventsToInnerRows(events);
8207
8307
  const schedulerDivRef = React.useRef(null);
8208
8308
  const { handleClick, handleDoubleClick } = useSingleAndDoubleClicks((e) => {
@@ -8259,7 +8359,7 @@ const SchedulerRow = React.memo((props) => {
8259
8359
  height: props.rowHeightInRem + "rem",
8260
8360
  pointerEvents: "none"
8261
8361
  }, children: internalRow.map((event) => (jsxRuntime.jsx(EventComp, { value: event, heightRem: props.rowHeightInRem, onEvent: onEvent }, event.id))) }, index));
8262
- }), !(timeLinePercentage < 0 || timeLinePercentage > 100) && (jsxRuntime.jsx(TimeLine, { color: "red", left: `${timeLinePercentage}%`, top: "0px", height: `100%` }))] }) })] }));
8362
+ }), 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%` }))] }) })] }));
8263
8363
  }, (prevProps, nextProps) => {
8264
8364
  // This memo is necessary to prevent re-render all the rows when a user makes drag and drop
8265
8365
  const getEventKey = (event) => {
@@ -8285,6 +8385,9 @@ const SchedulerRow = React.memo((props) => {
8285
8385
  return false;
8286
8386
  }
8287
8387
  }
8388
+ if (prevProps.indicatorRows.length !== nextProps.indicatorRows.length) {
8389
+ return false;
8390
+ }
8288
8391
  // If we got here, the events are the same
8289
8392
  return true;
8290
8393
  });
@@ -8475,51 +8578,6 @@ const getWeeksByDays = (visibleDays, selectedDate) => {
8475
8578
  return weeks;
8476
8579
  };
8477
8580
 
8478
- const eventIsVisible = (startDate, endDate, selectedDate, visibleDays) => {
8479
- const schedulerEnd = Temporal.PlainDate.from({
8480
- year: selectedDate.getFullYear(),
8481
- month: selectedDate.getMonth() + 1,
8482
- day: selectedDate.getDate(),
8483
- }).add({ days: visibleDays });
8484
- const schedulerEndDate = new Date(schedulerEnd.year, schedulerEnd.month - 1, schedulerEnd.day);
8485
- const isBefore = selectedDate > startDate && selectedDate > endDate;
8486
- const isAfter = schedulerEndDate < startDate && schedulerEndDate < endDate;
8487
- return !isBefore && !isAfter;
8488
- };
8489
-
8490
- const hoursBetween = (date1, date2) => {
8491
- const oneHourInMillis = 60 * 60 * 1000; // hours*minutes*seconds*milliseconds
8492
- const timeDiff = date2.getTime() - date1.getTime();
8493
- return timeDiff / oneHourInMillis;
8494
- };
8495
- const getEventSizes = (schedulerDate, totalDays, startDate, endDate) => {
8496
- if (!eventIsVisible(startDate, endDate, schedulerDate, totalDays)) {
8497
- return {
8498
- left: 0,
8499
- width: 0,
8500
- };
8501
- }
8502
- const totalHours = totalDays * 24;
8503
- // const dateString = schedulerDate.toISOString().split('T')[0];
8504
- //const schedulerDateAtZero = new Date(dateString)
8505
- let startHours = hoursBetween(schedulerDate, startDate);
8506
- let durationHours = hoursBetween(startDate, endDate);
8507
- const startOutOfScheduler = startHours < 0;
8508
- if (startOutOfScheduler) {
8509
- durationHours += startHours;
8510
- startHours = 0;
8511
- }
8512
- const left = (startHours / totalHours) * 100;
8513
- // Minimum width equivalent to 15 minutes so zero/near-zero duration events stay visible
8514
- const MIN_WIDTH_HOURS = 0.25;
8515
- const minWidth = (MIN_WIDTH_HOURS * 100) / totalHours;
8516
- const width = Math.max((durationHours * 100) / totalHours, minWidth);
8517
- return {
8518
- left,
8519
- width,
8520
- };
8521
- };
8522
-
8523
8581
  function getLinesByDivisions(headerDivision, _) {
8524
8582
  const lines = headerDivision.flatMap((division) => division.bottom.map((_, i) => ({ isDayBoundary: i === 0 })));
8525
8583
  const left = 100 / lines.length;
@@ -8575,8 +8633,65 @@ const getNow = (isUtc) => {
8575
8633
  };
8576
8634
  const milliSecondsInSecond = 1000;
8577
8635
  const refreshMilliSeconds = 45 * milliSecondsInSecond;
8636
+ const EMPTY_EVENTS = [];
8637
+ const EMPTY_BG_EVENTS = [];
8638
+ const EMPTY_INDICATORS = [];
8639
+ // Memoised row body. Receives flat props (the per-row buckets are looked up
8640
+ // by the outer Row wrapper). As long as those bucket arrays keep their
8641
+ // reference (see `groupByRowIdStable`) and the rest of the render-context
8642
+ // values are stable, this row will skip re-rendering on selection clicks
8643
+ // that don't affect it.
8644
+ const RowContent = React.memo(function RowContent(props) {
8645
+ const header = {
8646
+ value: props.row,
8647
+ width: props.rowHeaderWidth,
8648
+ onEvent: props.onEvent,
8649
+ };
8650
+ return (jsxRuntime.jsx("div", { style: {
8651
+ ...props.style,
8652
+ backgroundColor: props.rowColor
8653
+ }, 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));
8654
+ });
8655
+ // Outer (unmemoised) row: looks up per-row buckets from the shared `data`
8656
+ // object and forwards them as flat props to `RowContent`. Because the
8657
+ // referenced arrays are reused when their contents are unchanged
8658
+ // (see `groupByRowIdStable`), `RowContent.memo` will skip the work.
8659
+ const Row = ({ index, style, data }) => {
8660
+ const row = data.rows[index];
8661
+ const rowColor = data.groupRowColors
8662
+ ? Math.floor(index / 2) % 2 === 0 ? data.evenColor : data.oddColor
8663
+ : index % 2 === 0 ? data.evenColor : data.oddColor;
8664
+ 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 }));
8665
+ };
8666
+ // Bucket items by rowId, but reuse previous bucket arrays whenever a row's
8667
+ // contents have not changed. Keeping bucket references stable is essential
8668
+ // for `RowContent.memo` to skip work when only one row's events change
8669
+ // (e.g. on selection clicks).
8670
+ const groupByRowIdStable = (items, prev) => {
8671
+ const next = new Map();
8672
+ for (const item of items) {
8673
+ const bucket = next.get(item.rowId);
8674
+ if (bucket) {
8675
+ bucket.push(item);
8676
+ }
8677
+ else {
8678
+ next.set(item.rowId, [item]);
8679
+ }
8680
+ }
8681
+ if (!prev)
8682
+ return next;
8683
+ for (const [rowId, bucket] of next) {
8684
+ const prevBucket = prev.get(rowId);
8685
+ if (prevBucket
8686
+ && prevBucket.length === bucket.length
8687
+ && prevBucket.every((v, i) => v === bucket[i])) {
8688
+ next.set(rowId, prevBucket);
8689
+ }
8690
+ }
8691
+ return next;
8692
+ };
8578
8693
  const Scheduler = (props) => {
8579
- const { header: headerContent, id, events: eventsState, backgroundEvents, contextMenuItems, EventComp, RowTitleComp, orderCategories = ["title"], useOrderCategory, onEvent, groupRowColors, rowHeaderWidth = 180, rowHeightRem = 1.75, } = props;
8694
+ const { header: headerContent, id, events: eventsState, backgroundEvents, indicatorRows: allIndicatorRows = [], contextMenuItems, EventComp, RowTitleComp, orderCategories = ["title"], useOrderCategory, onEvent, groupRowColors, rowHeaderWidth = 180, rowHeightRem = 1.75, } = props;
8580
8695
  const BackgroundEventComp = props.BackgroundEventComp ?? BackgroundEvent;
8581
8696
  const rows = useOrderCategory === false
8582
8697
  ? props.rows
@@ -8584,21 +8699,49 @@ const Scheduler = (props) => {
8584
8699
  const instanceRef = React.useRef(null);
8585
8700
  // const rowHeaderWidth = "180px";
8586
8701
  const { selectedDate, visibleDays, isUtc, isHeaderVisible, visibleRows: stateVisibleRows } = props.state;
8587
- const events = filterVisibleEvents(eventsState, selectedDate, visibleDays);
8702
+ const events = React.useMemo(() => filterVisibleEvents(eventsState, selectedDate, visibleDays), [eventsState, selectedDate, visibleDays]);
8588
8703
  React.useEffect(() => {
8589
8704
  instanceRef.current?.resetAfterIndex(0);
8590
- }, [orderCategories, props.rows.length, events]);
8591
- const totalSubRows = rows.reduce((acc, row) => {
8592
- const filteredEvents = events.filter((it) => it.rowId === row.rowId);
8705
+ }, [orderCategories, props.rows.length, events, allIndicatorRows]);
8706
+ // Bucket events / backgroundEvents / indicatorRows by rowId once per render
8707
+ // instead of running events.filter() per row inside the row renderer.
8708
+ // Reuse previous bucket arrays when their contents are unchanged so the
8709
+ // memoised RowContent below can skip work (essential for selection clicks
8710
+ // to be cheap).
8711
+ const eventsByRowRef = React.useRef();
8712
+ const backgroundEventsByRowRef = React.useRef();
8713
+ const indicatorRowsByRowRef = React.useRef();
8714
+ const eventsByRow = React.useMemo(() => {
8715
+ const next = groupByRowIdStable(events, eventsByRowRef.current);
8716
+ eventsByRowRef.current = next;
8717
+ return next;
8718
+ }, [events]);
8719
+ const backgroundEventsByRow = React.useMemo(() => {
8720
+ const next = groupByRowIdStable(backgroundEvents, backgroundEventsByRowRef.current);
8721
+ backgroundEventsByRowRef.current = next;
8722
+ return next;
8723
+ }, [backgroundEvents]);
8724
+ const indicatorRowsByRow = React.useMemo(() => {
8725
+ const next = groupByRowIdStable(allIndicatorRows, indicatorRowsByRowRef.current);
8726
+ indicatorRowsByRowRef.current = next;
8727
+ return next;
8728
+ }, [allIndicatorRows]);
8729
+ const totalHeightRem = rows.reduce((acc, row) => {
8730
+ const filteredEvents = eventsByRow.get(row.rowId) ?? [];
8593
8731
  const innerRows = separateEventsToInnerRows(filteredEvents);
8594
- return acc + Math.max(innerRows.length, 1);
8732
+ const eventHeight = Math.max(innerRows.length, 1) * rowHeightRem;
8733
+ const hasIndicators = indicatorRowsByRow.has(row.rowId);
8734
+ return acc + eventHeight + (hasIndicators ? INDICATOR_HEIGHT_REM : 0);
8595
8735
  }, 0);
8596
- const visibleRows = Math.min(stateVisibleRows ?? totalSubRows, totalSubRows);
8736
+ const schedulerContentHeight = stateVisibleRows != null
8737
+ ? Math.min(totalHeightRem, stateVisibleRows * rowHeightRem)
8738
+ : Math.max(totalHeightRem, rowHeightRem);
8597
8739
  const totalHours = visibleDays * 24;
8598
8740
  const [timeLinePercentage, setTimeLinePercentage] = React.useState(0);
8741
+ const divisions = React.useMemo(() => getDefaultDivisions(visibleDays, selectedDate), [visibleDays, selectedDate]);
8599
8742
  const header = {
8600
8743
  content: headerContent,
8601
- divisions: getDefaultDivisions(visibleDays, selectedDate),
8744
+ divisions,
8602
8745
  visibleDays: visibleDays,
8603
8746
  width: rowHeaderWidth,
8604
8747
  selectedDate,
@@ -8608,9 +8751,8 @@ const Scheduler = (props) => {
8608
8751
  };
8609
8752
  const evenColor = "var(--cw-color-surface-container-low)";
8610
8753
  const oddColor = "var(--cw-color-surface-container)";
8611
- const schedulerContentHeight = rowHeightRem * Math.max(visibleRows, 1);
8612
- const weekendsLines = getWeekendLinesByDatesVisible(selectedDate, visibleDays);
8613
- const divisionLines = getLinesByDivisions(header.divisions);
8754
+ const weekendsLines = React.useMemo(() => getWeekendLinesByDatesVisible(selectedDate, visibleDays), [selectedDate, visibleDays]);
8755
+ const divisionLines = React.useMemo(() => getLinesByDivisions(divisions), [divisions, visibleDays]);
8614
8756
  // Timeline percentage calculation
8615
8757
  React.useEffect(() => {
8616
8758
  const updateTimeLinePercentage = () => {
@@ -8621,30 +8763,55 @@ const Scheduler = (props) => {
8621
8763
  const interval = setInterval(updateTimeLinePercentage, refreshMilliSeconds);
8622
8764
  return () => clearInterval(interval);
8623
8765
  }, [selectedDate, isUtc, totalHours]);
8624
- // Memoized Row Component
8625
- const Row = React.memo(({ index, style }) => {
8626
- const row = rows[index];
8627
- const rowColor = groupRowColors
8628
- ? Math.floor(index / 2) % 2 === 0 ? evenColor : oddColor
8629
- : index % 2 === 0 ? evenColor : oddColor;
8630
- const header = {
8631
- value: row,
8632
- width: rowHeaderWidth,
8633
- onEvent: onEvent,
8634
- };
8635
- return (jsxRuntime.jsx("div", { style: {
8636
- ...style,
8637
- backgroundColor: rowColor
8638
- }, 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));
8639
- });
8766
+ const itemData = React.useMemo(() => ({
8767
+ rows,
8768
+ eventsByRow,
8769
+ backgroundEventsByRow,
8770
+ indicatorRowsByRow,
8771
+ EventComp: EventComp,
8772
+ BackgroundEventComp,
8773
+ RowTitleComp: RowTitleComp,
8774
+ rowHeaderWidth,
8775
+ rowHeightRem,
8776
+ weekendLines: weekendsLines,
8777
+ divisionLines,
8778
+ timeLinePercentage,
8779
+ selectedDate,
8780
+ visibleDays,
8781
+ contextMenuItems,
8782
+ onEvent,
8783
+ groupRowColors,
8784
+ evenColor,
8785
+ oddColor,
8786
+ }), [
8787
+ rows,
8788
+ eventsByRow,
8789
+ backgroundEventsByRow,
8790
+ indicatorRowsByRow,
8791
+ EventComp,
8792
+ BackgroundEventComp,
8793
+ RowTitleComp,
8794
+ rowHeaderWidth,
8795
+ rowHeightRem,
8796
+ weekendsLines,
8797
+ divisionLines,
8798
+ timeLinePercentage,
8799
+ selectedDate,
8800
+ visibleDays,
8801
+ contextMenuItems,
8802
+ onEvent,
8803
+ groupRowColors,
8804
+ ]);
8640
8805
  const getItemSize = React.useCallback((index) => {
8641
8806
  const row = rows[index];
8642
- const filteredEvents = events.filter((it) => it.rowId === row.rowId);
8807
+ const filteredEvents = eventsByRow.get(row.rowId) ?? [];
8643
8808
  const innerRows = separateEventsToInnerRows(filteredEvents);
8644
8809
  const rowsNumber = innerRows.length > 0 ? innerRows.length : 1;
8645
8810
  const pixelsInRem = 16;
8646
- return rowsNumber * rowHeightRem * pixelsInRem;
8647
- }, [rows, events]);
8811
+ const hasIndicators = indicatorRowsByRow.has(row.rowId);
8812
+ const indicatorPixels = hasIndicators ? INDICATOR_HEIGHT_REM * pixelsInRem : 0;
8813
+ return rowsNumber * rowHeightRem * pixelsInRem + indicatorPixels;
8814
+ }, [rows, eventsByRow, indicatorRowsByRow, rowHeightRem]);
8648
8815
  // Render
8649
8816
  return (jsxRuntime.jsxs("div", { id: id, style: {
8650
8817
  position: "relative",
@@ -8655,7 +8822,7 @@ const Scheduler = (props) => {
8655
8822
  position: "sticky",
8656
8823
  top: 0,
8657
8824
  zIndex: 1,
8658
- }, 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 })] }));
8825
+ }, 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 })] }));
8659
8826
  };
8660
8827
 
8661
8828
  class UiEvent {
@@ -9018,13 +9185,13 @@ const PinRowHeader = ({ value, width, onEvent }) => {
9018
9185
  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);
9019
9186
  };
9020
9187
 
9021
- const SuperScheduler = ({ id, state, header, rows, events, pinnedOrderCategory, unPinnedOrderCategory, backgroundEvents, contextMenuItems, onEvent, rowHeightRem = 1.75 }) => {
9188
+ const SuperScheduler = ({ id, state, header, rows, events, pinnedOrderCategory, unPinnedOrderCategory, backgroundEvents, indicatorRows = [], contextMenuItems, onEvent, rowHeightRem = 1.75 }) => {
9022
9189
  const pinnedRows = rows.filter((it) => it.isPinned);
9023
9190
  const notPinnedRows = rows.filter((it) => !it.isPinned);
9024
9191
  const isFirstVisible = pinnedRows.length > 0;
9025
- 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: () => {
9192
+ 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: () => {
9026
9193
  onEvent(new OnClearPinned());
9027
- }, 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 })] }));
9194
+ }, 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 })] }));
9028
9195
  };
9029
9196
 
9030
9197
  const CwFindAirport = ({ handleChange, searchType = "OnlyDatabase", placeHolder = "Search airport…", required = false, cblConfig, className = "", value, disabled = false, displayMode, initialDisplayText, labelProps, alignProps, width }) => {