@cwellt_software/cwellt-reactjs-lib 1.6.3 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs.js +2479 -146
- package/dist/index.css +1 -1
- package/dist/index.d.ts +679 -2
- package/dist/index.es.js +2465 -147
- package/dist/src/components/custom/report-viewer/CwReportGauge.d.ts +19 -0
- package/dist/src/components/custom/report-viewer/CwReportGauge.d.ts.map +1 -0
- package/dist/src/components/custom/report-viewer/CwReportLineChart.d.ts +21 -0
- package/dist/src/components/custom/report-viewer/CwReportLineChart.d.ts.map +1 -0
- package/dist/src/components/custom/report-viewer/CwReportPage.d.ts +36 -0
- package/dist/src/components/custom/report-viewer/CwReportPage.d.ts.map +1 -0
- package/dist/src/components/custom/report-viewer/CwReportParameterForm.d.ts +26 -0
- package/dist/src/components/custom/report-viewer/CwReportParameterForm.d.ts.map +1 -0
- package/dist/src/components/custom/report-viewer/CwReportPieChart.d.ts +13 -0
- package/dist/src/components/custom/report-viewer/CwReportPieChart.d.ts.map +1 -0
- package/dist/src/components/custom/report-viewer/CwReportToolbar.d.ts +40 -0
- package/dist/src/components/custom/report-viewer/CwReportToolbar.d.ts.map +1 -0
- package/dist/src/components/custom/report-viewer/CwReportViewer.d.ts +50 -0
- package/dist/src/components/custom/report-viewer/CwReportViewer.d.ts.map +1 -0
- package/dist/src/components/custom/report-viewer/ReportItem.d.ts +21 -0
- package/dist/src/components/custom/report-viewer/ReportItem.d.ts.map +1 -0
- package/dist/src/components/custom/report-viewer/ReportTextbox.d.ts +10 -0
- package/dist/src/components/custom/report-viewer/ReportTextbox.d.ts.map +1 -0
- package/dist/src/components/custom/report-viewer/logic/rdlChartMarkers.d.ts +26 -0
- package/dist/src/components/custom/report-viewer/logic/rdlChartMarkers.d.ts.map +1 -0
- package/dist/src/components/custom/report-viewer/logic/rdlChartScale.d.ts +75 -0
- package/dist/src/components/custom/report-viewer/logic/rdlChartScale.d.ts.map +1 -0
- package/dist/src/components/custom/report-viewer/logic/rdlFind.d.ts +38 -0
- package/dist/src/components/custom/report-viewer/logic/rdlFind.d.ts.map +1 -0
- package/dist/src/components/custom/report-viewer/logic/rdlGauge.d.ts +40 -0
- package/dist/src/components/custom/report-viewer/logic/rdlGauge.d.ts.map +1 -0
- package/dist/src/components/custom/report-viewer/logic/rdlListMarkers.d.ts +12 -0
- package/dist/src/components/custom/report-viewer/logic/rdlListMarkers.d.ts.map +1 -0
- package/dist/src/components/custom/report-viewer/logic/rdlNumberFormat.d.ts +8 -0
- package/dist/src/components/custom/report-viewer/logic/rdlNumberFormat.d.ts.map +1 -0
- package/dist/src/components/custom/report-viewer/logic/rdlPageGeometry.d.ts +38 -0
- package/dist/src/components/custom/report-viewer/logic/rdlPageGeometry.d.ts.map +1 -0
- package/dist/src/components/custom/report-viewer/logic/rdlParameterValues.d.ts +42 -0
- package/dist/src/components/custom/report-viewer/logic/rdlParameterValues.d.ts.map +1 -0
- package/dist/src/components/custom/report-viewer/logic/rdlStyle.d.ts +62 -0
- package/dist/src/components/custom/report-viewer/logic/rdlStyle.d.ts.map +1 -0
- package/dist/src/components/custom/report-viewer/logic/rdlTableLayout.d.ts +33 -0
- package/dist/src/components/custom/report-viewer/logic/rdlTableLayout.d.ts.map +1 -0
- package/dist/src/components/custom/report-viewer/logic/rdlToggles.d.ts +20 -0
- package/dist/src/components/custom/report-viewer/logic/rdlToggles.d.ts.map +1 -0
- package/dist/src/components/custom/report-viewer/logic/rdlUnits.d.ts +9 -0
- package/dist/src/components/custom/report-viewer/logic/rdlUnits.d.ts.map +1 -0
- package/dist/src/components/custom/report-viewer/logic/reportOutputGuard.d.ts +20 -0
- package/dist/src/components/custom/report-viewer/logic/reportOutputGuard.d.ts.map +1 -0
- package/dist/src/components/custom/report-viewer/logic/reportViewerLayout.d.ts +17 -0
- package/dist/src/components/custom/report-viewer/logic/reportViewerLayout.d.ts.map +1 -0
- package/dist/src/components/custom/report-viewer/rdlTypes.d.ts +333 -0
- package/dist/src/components/custom/report-viewer/rdlTypes.d.ts.map +1 -0
- package/dist/src/components/custom/report-viewer/reportChartShared.d.ts +53 -0
- package/dist/src/components/custom/report-viewer/reportChartShared.d.ts.map +1 -0
- package/dist/src/components/custom/report-viewer/reportRenderContext.d.ts +25 -0
- package/dist/src/components/custom/report-viewer/reportRenderContext.d.ts.map +1 -0
- package/dist/src/components/custom/report-viewer/useReportParameterForm.d.ts +30 -0
- package/dist/src/components/custom/report-viewer/useReportParameterForm.d.ts.map +1 -0
- package/dist/src/components/custom/report-viewer/useReportViewerState.d.ts +74 -0
- package/dist/src/components/custom/report-viewer/useReportViewerState.d.ts.map +1 -0
- package/dist/src/components/custom/report-viewer/useSidebarResize.d.ts +22 -0
- package/dist/src/components/custom/report-viewer/useSidebarResize.d.ts.map +1 -0
- package/dist/src/components/display/data/tooltip_manager/CwTooltipManager.d.ts +1 -1
- package/dist/src/components/display/data/tooltip_manager/CwTooltipManager.d.ts.map +1 -1
- package/dist/src/index.d.ts +15 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/playground/PlaygroundApp.d.ts.map +1 -1
- package/dist/src/playground/pages/ReportViewerPage.d.ts +8 -0
- package/dist/src/playground/pages/ReportViewerPage.d.ts.map +1 -0
- package/dist/src/playground/pages/reportViewerFakeData.d.ts +13 -0
- package/dist/src/playground/pages/reportViewerFakeData.d.ts.map +1 -0
- package/dist/test/components/custom/report-viewer/rdlChartMarkers.test.d.ts +2 -0
- package/dist/test/components/custom/report-viewer/rdlChartMarkers.test.d.ts.map +1 -0
- package/dist/test/components/custom/report-viewer/rdlChartScale.test.d.ts +2 -0
- package/dist/test/components/custom/report-viewer/rdlChartScale.test.d.ts.map +1 -0
- package/dist/test/components/custom/report-viewer/rdlFind.test.d.ts +2 -0
- package/dist/test/components/custom/report-viewer/rdlFind.test.d.ts.map +1 -0
- package/dist/test/components/custom/report-viewer/rdlGauge.test.d.ts +2 -0
- package/dist/test/components/custom/report-viewer/rdlGauge.test.d.ts.map +1 -0
- package/dist/test/components/custom/report-viewer/rdlNumberFormat.test.d.ts +2 -0
- package/dist/test/components/custom/report-viewer/rdlNumberFormat.test.d.ts.map +1 -0
- package/dist/test/components/custom/report-viewer/rdlParameterValues.test.d.ts +2 -0
- package/dist/test/components/custom/report-viewer/rdlParameterValues.test.d.ts.map +1 -0
- package/dist/test/components/custom/report-viewer/rdlTableLayout.test.d.ts +2 -0
- package/dist/test/components/custom/report-viewer/rdlTableLayout.test.d.ts.map +1 -0
- package/dist/test/components/custom/report-viewer/rdlText.test.d.ts +2 -0
- package/dist/test/components/custom/report-viewer/rdlText.test.d.ts.map +1 -0
- package/dist/test/components/custom/report-viewer/rdlToggles.test.d.ts +2 -0
- package/dist/test/components/custom/report-viewer/rdlToggles.test.d.ts.map +1 -0
- package/dist/test/components/custom/report-viewer/rdlUnits.test.d.ts +2 -0
- package/dist/test/components/custom/report-viewer/rdlUnits.test.d.ts.map +1 -0
- package/dist/test/components/custom/report-viewer/reportOutputGuard.test.d.ts +2 -0
- package/dist/test/components/custom/report-viewer/reportOutputGuard.test.d.ts.map +1 -0
- package/dist/test/components/custom/report-viewer/reportViewerLayout.test.d.ts +2 -0
- package/dist/test/components/custom/report-viewer/reportViewerLayout.test.d.ts.map +1 -0
- package/dist/test/components/custom/report-viewer/useReportParameterForm.test.d.ts +2 -0
- package/dist/test/components/custom/report-viewer/useReportParameterForm.test.d.ts.map +1 -0
- package/dist/test/components/custom/report-viewer/useReportViewerState.test.d.ts +2 -0
- package/dist/test/components/custom/report-viewer/useReportViewerState.test.d.ts.map +1 -0
- package/dist/test/helpers/renderHookHeadless.d.ts +8 -0
- package/dist/test/helpers/renderHookHeadless.d.ts.map +1 -0
- package/package.json +89 -89
package/dist/index.cjs.js
CHANGED
|
@@ -194,7 +194,7 @@ function getContrastColor(color) {
|
|
|
194
194
|
return `hsl(${hsl.h}, ${contrastS}%, ${targetL}%)`;
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
-
var styles$
|
|
197
|
+
var styles$D = {"cw-tooltip-content":"cw-tooltip-module__cw-tooltip-content__RZczd","cw-tooltip":"cw-tooltip-module__cw-tooltip__1KYig"};
|
|
198
198
|
|
|
199
199
|
// Constants
|
|
200
200
|
const margin$1 = 16;
|
|
@@ -484,7 +484,7 @@ const CwTooltipManager = () => {
|
|
|
484
484
|
if (!state.isVisible || !state.content) {
|
|
485
485
|
return null;
|
|
486
486
|
}
|
|
487
|
-
return reactDom.createPortal(jsxRuntime.jsx("div", { ref: tooltipRef, className: styles$
|
|
487
|
+
return reactDom.createPortal(jsxRuntime.jsx("div", { ref: tooltipRef, className: styles$D["cw-tooltip-content"], "data-position": actualPosition, "data-visible": state.isVisible, "data-inline": state.displayInline, style: {
|
|
488
488
|
top: `${tooltipPosition.top}px`,
|
|
489
489
|
left: `${tooltipPosition.left}px`,
|
|
490
490
|
opacity: (isPositioned && !isMeasuring) ? 1 : 0,
|
|
@@ -565,7 +565,7 @@ const CwTooltipNew = ({ children, content = null, position = "right", dissapears
|
|
|
565
565
|
React.useEffect(() => {
|
|
566
566
|
ensureTooltipManager();
|
|
567
567
|
}, []);
|
|
568
|
-
return (jsxRuntime.jsx("div", { ref: containerRef, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, style: overlayStyle, "data-inline": displayInline, className: styles$
|
|
568
|
+
return (jsxRuntime.jsx("div", { ref: containerRef, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, style: overlayStyle, "data-inline": displayInline, className: styles$D["cw-tooltip"], children: children }));
|
|
569
569
|
};
|
|
570
570
|
|
|
571
571
|
const SVG_ICONS = {
|
|
@@ -685,7 +685,7 @@ function resolveBadgeAria(label, loading, hasText) {
|
|
|
685
685
|
return hasText ? {} : { 'aria-hidden': 'true' };
|
|
686
686
|
}
|
|
687
687
|
|
|
688
|
-
var styles$
|
|
688
|
+
var styles$C = {"badge":"cw-badge-module__badge__PKlak"};
|
|
689
689
|
|
|
690
690
|
/**
|
|
691
691
|
* CwBadge - the small coloured circle with a number, used for alert and notification counts.
|
|
@@ -721,7 +721,7 @@ const CwBadge = ({ count, content, color = 'info', variant = 'solid', size = 'sm
|
|
|
721
721
|
? formatBadgeCount(count, max)
|
|
722
722
|
: undefined;
|
|
723
723
|
const isDot = !loading && !hasContent && count === undefined;
|
|
724
|
-
const badgeClassNames = ['cw-badge', styles$
|
|
724
|
+
const badgeClassNames = ['cw-badge', styles$C.badge, className].filter(Boolean).join(' ');
|
|
725
725
|
return (jsxRuntime.jsx("span", { ...dataAttributes, className: badgeClassNames, "data-color": color, "data-variant": variant, "data-size": size, "data-loading": loading ? 'true' : undefined, "data-dot": isDot ? 'true' : undefined, ...resolveBadgeAria(label, loading, !loading && (hasContent || countText !== undefined)), style: style, children: loading ? jsxRuntime.jsx(CwIcon, { iconId: "spinner", size: "small" }) : hasContent ? content : countText }));
|
|
726
726
|
};
|
|
727
727
|
|
|
@@ -1109,7 +1109,7 @@ function CwLoading({ isLoading = false, text = '', size = 'regular', iconPositio
|
|
|
1109
1109
|
return (jsxRuntime.jsxs("div", { "aria-busy": isLoading, className: "cw-loading-container", "data-children": !!children, children: [overlay, jsxRuntime.jsx("div", { className: "cw-loading-disabled-content", children: children })] }));
|
|
1110
1110
|
}
|
|
1111
1111
|
|
|
1112
|
-
var styles$
|
|
1112
|
+
var styles$B = {"skeleton":"cw-skeleton-module__skeleton__Jvx2x","group":"cw-skeleton-module__group__Cm8AU"};
|
|
1113
1113
|
|
|
1114
1114
|
/*
|
|
1115
1115
|
* Internal helper for skeleton allocations (CwSkeleton, tables, cards).
|
|
@@ -1151,8 +1151,8 @@ function CwSkeleton({ variant = 'text', width, height, count = 1, radius, gap })
|
|
|
1151
1151
|
};
|
|
1152
1152
|
// The un-hashed cw-skeleton / cw-skeleton-group classes carry no styles in the
|
|
1153
1153
|
// library - they are stable public hooks for exceptional external overrides.
|
|
1154
|
-
const skeletonClassName = ['cw-skeleton', styles$
|
|
1155
|
-
const groupClassName = ['cw-skeleton-group', styles$
|
|
1154
|
+
const skeletonClassName = ['cw-skeleton', styles$B.skeleton].filter(Boolean).join(' ');
|
|
1155
|
+
const groupClassName = ['cw-skeleton-group', styles$B.group].filter(Boolean).join(' ');
|
|
1156
1156
|
const lineCount = normalizeSkeletonCount(count, 1);
|
|
1157
1157
|
if (lineCount <= 1) {
|
|
1158
1158
|
return (jsxRuntime.jsx("span", { className: skeletonClassName, "data-variant": variant, "aria-hidden": "true", style: style }));
|
|
@@ -1164,7 +1164,7 @@ function CwSkeleton({ variant = 'text', width, height, count = 1, radius, gap })
|
|
|
1164
1164
|
return (jsxRuntime.jsx("span", { className: groupClassName, "aria-hidden": "true", style: groupStyle, children: skeletonElements }));
|
|
1165
1165
|
}
|
|
1166
1166
|
|
|
1167
|
-
var styles$
|
|
1167
|
+
var styles$A = {"progress":"cw-progress-module__progress__d-g2-","value":"cw-progress-module__value__Q-DrH","label":"cw-progress-module__label__jwOWp","track":"cw-progress-module__track__572-3","footer":"cw-progress-module__footer__0Wo4G","header":"cw-progress-module__header__mv5ct","fill":"cw-progress-module__fill__fX1UA"};
|
|
1168
1168
|
|
|
1169
1169
|
/**
|
|
1170
1170
|
* CwProgress - a determinate or indeterminate progress bar.
|
|
@@ -1188,17 +1188,17 @@ function CwProgress({ value, indeterminate = false, label, rightLabel, footer, s
|
|
|
1188
1188
|
const valueContent = rightLabel ?? (percent !== undefined ? `${percent}%` : '');
|
|
1189
1189
|
// The un-hashed cw-progress class carries no styles in the library - a stable
|
|
1190
1190
|
// public hook for exceptional external overrides only.
|
|
1191
|
-
const rootClassName = ['cw-progress', styles$
|
|
1192
|
-
const labelSpan = label ? jsxRuntime.jsx("span", { className: styles$
|
|
1193
|
-
const valueLabel = showValue ? (jsxRuntime.jsx("span", { className: styles$
|
|
1194
|
-
return (jsxRuntime.jsxs("div", { ...dataAttributes, className: rootClassName, style: style, "data-layout": layout, "data-size": size, "data-color": color, "data-prominent": prominent || undefined, "data-indeterminate": indeterminate || undefined, children: [layout === 'column' && (labelSpan || valueLabel) && (jsxRuntime.jsxs("header", { className: styles$
|
|
1191
|
+
const rootClassName = ['cw-progress', styles$A.progress, className].filter(Boolean).join(' ');
|
|
1192
|
+
const labelSpan = label ? jsxRuntime.jsx("span", { className: styles$A.label, id: labelId, children: label }) : null;
|
|
1193
|
+
const valueLabel = showValue ? (jsxRuntime.jsx("span", { className: styles$A.value, "data-mono": monoValue || undefined, children: valueContent })) : null;
|
|
1194
|
+
return (jsxRuntime.jsxs("div", { ...dataAttributes, className: rootClassName, style: style, "data-layout": layout, "data-size": size, "data-color": color, "data-prominent": prominent || undefined, "data-indeterminate": indeterminate || undefined, children: [layout === 'column' && (labelSpan || valueLabel) && (jsxRuntime.jsxs("header", { className: styles$A.header, children: [labelSpan, valueLabel] })), layout === 'row' && labelSpan, jsxRuntime.jsx("div", { className: styles$A.track, role: "progressbar", ...(label && { 'aria-labelledby': labelId }), ...(!indeterminate && percent !== undefined && {
|
|
1195
1195
|
'aria-valuenow': percent,
|
|
1196
1196
|
'aria-valuemin': 0,
|
|
1197
1197
|
'aria-valuemax': 100
|
|
1198
|
-
}), children: jsxRuntime.jsx("div", { className: styles$
|
|
1198
|
+
}), children: jsxRuntime.jsx("div", { className: styles$A.fill, style: !indeterminate && clamped !== undefined ? { width: `${clamped}%` } : undefined }) }), layout === 'row' && valueLabel, footer != null && jsxRuntime.jsx("footer", { className: styles$A.footer, children: footer })] }));
|
|
1199
1199
|
}
|
|
1200
1200
|
|
|
1201
|
-
var styles$
|
|
1201
|
+
var styles$z = {"cw-generic-tooltip-content":"cw-generic-tooltip-module__cw-generic-tooltip-content__la-Si","cw-generic-tooltip":"cw-generic-tooltip-module__cw-generic-tooltip__Ij76M"};
|
|
1202
1202
|
|
|
1203
1203
|
// Constants moved outside to prevent recreation
|
|
1204
1204
|
const margin = 16;
|
|
@@ -1325,12 +1325,12 @@ const CwGenericTooltip = ({ children, content = null, position = defaultPosition
|
|
|
1325
1325
|
const { setTooltipTimeout, clearTooltipTimeout } = useTooltipDelay(() => {
|
|
1326
1326
|
setIsVisible(true);
|
|
1327
1327
|
}, showDelay);
|
|
1328
|
-
const { position: tooltipPosition, actualPosition } = useTooltipPosition(isVisible, containerRef, position, styles$
|
|
1328
|
+
const { position: tooltipPosition, actualPosition } = useTooltipPosition(isVisible, containerRef, position, styles$z["cw-generic-tooltip-content"]);
|
|
1329
1329
|
// Memoize tooltip content creation
|
|
1330
1330
|
const tooltipContent = React.useMemo(() => {
|
|
1331
1331
|
if (hide || !isVisible || !content)
|
|
1332
1332
|
return null;
|
|
1333
|
-
return reactDom.createPortal(jsxRuntime.jsx("div", { className: styles$
|
|
1333
|
+
return reactDom.createPortal(jsxRuntime.jsx("div", { className: styles$z["cw-generic-tooltip-content"], "data-position": actualPosition, "data-visible": isVisible, "data-inline": displayInline, style: {
|
|
1334
1334
|
position: 'fixed',
|
|
1335
1335
|
top: `${tooltipPosition.top}px`,
|
|
1336
1336
|
left: `${tooltipPosition.left}px`,
|
|
@@ -1345,7 +1345,7 @@ const CwGenericTooltip = ({ children, content = null, position = defaultPosition
|
|
|
1345
1345
|
clearTooltipTimeout();
|
|
1346
1346
|
setIsVisible(false);
|
|
1347
1347
|
}, [clearTooltipTimeout]);
|
|
1348
|
-
return (jsxRuntime.jsxs("div", { ref: containerRef, className: styles$
|
|
1348
|
+
return (jsxRuntime.jsxs("div", { ref: containerRef, className: styles$z["cw-generic-tooltip"], onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, style: overlayStyle, "data-inline": displayInline, children: [tooltipContent, children] }));
|
|
1349
1349
|
};
|
|
1350
1350
|
|
|
1351
1351
|
const CwButton = React.forwardRef(function CwButton({ text, variant = 'solid', color = 'primary', size = 'medium', iconPosition = 'left', pressed, className = '', icon = "", title, tooltipPosition = "bottom", badge, children, ...buttonProps }, ref) {
|
|
@@ -1413,7 +1413,7 @@ function CwModal(custModalProps) {
|
|
|
1413
1413
|
return (jsxRuntime.jsx("div", { children: custModalProps.modalState && (jsxRuntime.jsxs("div", { className: custModalProps.classNameModalOverlay + " cwelltModalOverlay", children: [jsxRuntime.jsx("div", { className: "cwelltModalOverlayBg", onClick: custModalProps.onCloseModal }), jsxRuntime.jsx(Draggable, { disabled: isModalDisabled, axis: "both", nodeRef: draggableRef, children: jsxRuntime.jsxs("div", { className: custModalProps.classNameModal + " cwelltContainerModal", ref: draggableRef, style: widthModalDef !== "40em" ? modalStyle.widthCustomStyle : modalStyle.widthDefStyle, children: [jsxRuntime.jsxs("div", { className: "cwelltModalHeader", onMouseOver: cwelltOnMouseOverModal, onMouseOut: cwelltOnMouseOutModal, children: [jsxRuntime.jsx("div", { className: "cwelltModalTitle", children: custModalProps.titleModal }), jsxRuntime.jsx("button", { className: "cwelltBtnCloseModal", onClick: custModalProps.onCloseModal, children: jsxRuntime.jsx("span", { className: "cwelltCloseIcon" }) })] }), jsxRuntime.jsx("div", { className: "cwelltModalBody", children: jsxRuntime.jsx("div", { className: "cwelltContainerModalBody", style: { position: "relative" }, children: custModalProps.children }) }), isHide === false ? (jsxRuntime.jsx("footer", { className: "legacy-modal-footer", onMouseOver: cwelltOnMouseOverModal, onMouseOut: cwelltOnMouseOutModal, children: jsxRuntime.jsx(CwButton, { variant: "icon", icon: "save", title: "Save", onClick: custModalProps.onSaveModal, form: custModalProps.formSaveModal, hidden: custModalProps.HideBtnModal, type: custModalProps.HtmlSubmitModal }) })) : (jsxRuntime.jsx("div", { style: { display: "none" } }))] }) })] })) }));
|
|
1414
1414
|
}
|
|
1415
1415
|
|
|
1416
|
-
var styles$
|
|
1416
|
+
var styles$y = {"overlayPositioned":"CwConfirmationPopup-module__overlayPositioned__11qYB","popup":"CwConfirmationPopup-module__popup__ahPjM","message":"CwConfirmationPopup-module__message__MnNL4","buttons":"CwConfirmationPopup-module__buttons__fGYYE","button":"CwConfirmationPopup-module__button__ugYQE","confirmButton":"CwConfirmationPopup-module__confirmButton__rTP4S","cancelButton":"CwConfirmationPopup-module__cancelButton__Ry694"};
|
|
1417
1417
|
|
|
1418
1418
|
/**
|
|
1419
1419
|
* Small anchored confirmation for a destructive action.
|
|
@@ -1482,15 +1482,15 @@ function CwConfirmationPopup(props) {
|
|
|
1482
1482
|
onCancel();
|
|
1483
1483
|
}
|
|
1484
1484
|
};
|
|
1485
|
-
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [renderChildren(), isOpen && (jsxRuntime.jsx("div", { className: styles$
|
|
1485
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [renderChildren(), isOpen && (jsxRuntime.jsx("div", { className: styles$y.overlayPositioned, onClick: handleOverlayClick, children: jsxRuntime.jsxs("div", { ref: popupRef, className: styles$y.popup, style: {
|
|
1486
1486
|
position: 'absolute',
|
|
1487
1487
|
top: `${position.top}px`,
|
|
1488
1488
|
left: `${position.left}px`,
|
|
1489
1489
|
transform: 'none'
|
|
1490
|
-
}, children: [jsxRuntime.jsx("p", { className: styles$
|
|
1490
|
+
}, children: [jsxRuntime.jsx("p", { className: styles$y.message, children: message }), jsxRuntime.jsxs("div", { className: styles$y.buttons, children: [jsxRuntime.jsx("button", { className: `${styles$y.button} ${styles$y.confirmButton}`, onClick: onConfirm, children: confirmText }), jsxRuntime.jsx("button", { className: `${styles$y.button} ${styles$y.cancelButton}`, onClick: onCancel, children: cancelText })] })] }) }))] }));
|
|
1491
1491
|
}
|
|
1492
1492
|
|
|
1493
|
-
var styles$
|
|
1493
|
+
var styles$x = {"cw-dialog-main":"cw-dialog-module__cw-dialog-main__cHxHt","cw-dialog-button-close":"cw-dialog-module__cw-dialog-button-close__9GRd8"};
|
|
1494
1494
|
|
|
1495
1495
|
// Helper function to parse size values
|
|
1496
1496
|
const parseSize = (size) => {
|
|
@@ -1763,7 +1763,7 @@ const CwDialog = props => {
|
|
|
1763
1763
|
}
|
|
1764
1764
|
}
|
|
1765
1765
|
}, [open]);
|
|
1766
|
-
const header = React.useMemo(() => (jsxRuntime.jsxs("header", { onMouseDown: handleMouseDown, children: [jsxRuntime.jsx("span", { children: headline }), customHeader || (jsxRuntime.jsx("button", { className: styles$
|
|
1766
|
+
const header = React.useMemo(() => (jsxRuntime.jsxs("header", { onMouseDown: handleMouseDown, children: [jsxRuntime.jsx("span", { children: headline }), customHeader || (jsxRuntime.jsx("button", { className: styles$x["cw-dialog-button-close"], onClick: onClose }))] })), [handleMouseDown, headline, customHeader, onClose]);
|
|
1767
1767
|
const content = React.useMemo(() => (jsxRuntime.jsx("section", { children: children })), [children]);
|
|
1768
1768
|
const footer = React.useMemo(() => (jsxRuntime.jsx("footer", { children: customFooter || (jsxRuntime.jsx(CwButton, { variant: "icon", icon: "save", title: "Save", onClick: onSave, tooltipPosition: "top", disabled: disableSave, "data-testid": "cw-dialog-save" })) })), [customFooter, onSave, disableSave]);
|
|
1769
1769
|
const resizeHandles = React.useMemo(() => size.autoHeight
|
|
@@ -1790,7 +1790,7 @@ const CwDialog = props => {
|
|
|
1790
1790
|
: `${convertFromPx(size.height, size.heightUnit)}${size.heightUnit}`;
|
|
1791
1791
|
return { displayWidth, displayHeight };
|
|
1792
1792
|
}, [size.width, size.height, size.widthUnit, size.heightUnit, size.autoHeight]);
|
|
1793
|
-
const dialogContent = (jsxRuntime.jsx("div", { "data-has-scrim": hasScrim, className: styles$
|
|
1793
|
+
const dialogContent = (jsxRuntime.jsx("div", { "data-has-scrim": hasScrim, className: styles$x["cw-dialog-main"], onMouseDown: handleScrimMouseDown, onMouseUp: handleScrimMouseUpOrLeave, onMouseLeave: handleScrimMouseUpOrLeave, "data-pressing": isPressingScrim, children: jsxRuntime.jsxs("dialog", { ...domProps, ref: dialogRef, style: {
|
|
1794
1794
|
left: `${position.x}px`,
|
|
1795
1795
|
top: `${position.y}px`,
|
|
1796
1796
|
width: displayDimensions.displayWidth,
|
|
@@ -1952,7 +1952,7 @@ function CwAlign(props) {
|
|
|
1952
1952
|
}, children: props.children }));
|
|
1953
1953
|
}
|
|
1954
1954
|
|
|
1955
|
-
var styles$
|
|
1955
|
+
var styles$w = {"card":"cw-card-module__card__HJUT0","content":"cw-card-module__content__ma9qy","clickable":"cw-card-module__clickable__Y-V3X","disabled":"cw-card-module__disabled__0wHh1","headerContent":"cw-card-module__headerContent__x4Jfl","footerTags":"cw-card-module__footerTags__80sSW"};
|
|
1956
1956
|
|
|
1957
1957
|
/**
|
|
1958
1958
|
* CwCard - A simple card component for displaying content in a contained format
|
|
@@ -1978,10 +1978,10 @@ const CwCard = ({ id, title, subtitle, alignment = 'center', children, footer, o
|
|
|
1978
1978
|
// Construct class names using CSS modules
|
|
1979
1979
|
const cardClassNames = [
|
|
1980
1980
|
'cw-card',
|
|
1981
|
-
styles$
|
|
1981
|
+
styles$w.card,
|
|
1982
1982
|
className,
|
|
1983
|
-
clickable ? styles$
|
|
1984
|
-
disabled ? styles$
|
|
1983
|
+
clickable ? styles$w.clickable : '',
|
|
1984
|
+
disabled ? styles$w.disabled : '',
|
|
1985
1985
|
].filter(Boolean).join(' ');
|
|
1986
1986
|
// Handle click event when card is clickable
|
|
1987
1987
|
const handleClick = () => {
|
|
@@ -1999,15 +1999,15 @@ const CwCard = ({ id, title, subtitle, alignment = 'center', children, footer, o
|
|
|
1999
1999
|
const hasFooter = hasChips || footer || (hasActions && resolvedDirection === "row");
|
|
2000
2000
|
// Actions component to reuse. While loading, each provided action renders a circular
|
|
2001
2001
|
// ghost sized to the CwButton icon footprint (2rem) - no interactive buttons.
|
|
2002
|
-
const ActionsComponent = hasActions && !disabled ? (jsxRuntime.jsx("div", { className: styles$
|
|
2002
|
+
const ActionsComponent = hasActions && !disabled ? (jsxRuntime.jsx("div", { className: styles$w.actions, children: isLoading ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [extraActions && jsxRuntime.jsx(CwSkeleton, { variant: "circle", height: "2rem" }), onEdit && jsxRuntime.jsx(CwSkeleton, { variant: "circle", height: "2rem" }), onDelete && jsxRuntime.jsx(CwSkeleton, { variant: "circle", height: "2rem" })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [extraActions ?? null, onEdit && jsxRuntime.jsx(CwButton, { variant: "icon", icon: "edit", onClick: onEdit }), onDelete && jsxRuntime.jsx(CwButton, { variant: "icon", icon: "delete", color: "danger", onClick: onDelete })] })) })) : null;
|
|
2003
2003
|
return (jsxRuntime.jsxs("div", { ...(id && { id }), className: cardClassNames, ...(style && { style }), ...(clickable && !isLoading && {
|
|
2004
2004
|
role: 'button',
|
|
2005
2005
|
tabIndex: 0,
|
|
2006
2006
|
onClick: handleClick,
|
|
2007
|
-
}), "data-variant": variant, "data-direction": resolvedDirection, "aria-busy": isLoading || undefined, children: [isLoading ? (jsxRuntime.jsxs("header", { children: [jsxRuntime.jsx("div", { className: styles$
|
|
2007
|
+
}), "data-variant": variant, "data-direction": resolvedDirection, "aria-busy": isLoading || undefined, children: [isLoading ? (jsxRuntime.jsxs("header", { children: [jsxRuntime.jsx("div", { className: styles$w.headerContent, "data-align": resolvedAlign, children: jsxRuntime.jsx(CwSkeleton, { width: "8rem", height: "1.5em" }) }), resolvedDirection === "column" && ActionsComponent] })) : (hasHeader && (jsxRuntime.jsxs("header", { children: [(title || subtitle) && (jsxRuntime.jsxs("div", { className: styles$w.headerContent, "data-align": resolvedAlign, children: [title && jsxRuntime.jsx("h5", { children: title }), subtitle && jsxRuntime.jsx("strong", { children: subtitle })] })), resolvedDirection === "column" && ActionsComponent] }))), jsxRuntime.jsx("div", { className: styles$w.content, children: isLoading ? jsxRuntime.jsx(CwSkeleton, { count: normalizeSkeletonCount(skeletonLines, 3) }) : children }), hasFooter && (isLoading ? (jsxRuntime.jsx("footer", { children: jsxRuntime.jsx("div", { className: styles$w.footerTags, children: jsxRuntime.jsx(CwSkeleton, { variant: "rect", width: "4rem", height: "1.5em", radius: "1rem" }) }) })) : (jsxRuntime.jsxs("footer", { children: [hasChips && (jsxRuntime.jsx("div", { className: styles$w.footerTags, children: chips.map((chip, index) => (jsxRuntime.jsx(CwChip, { label: chip.label, color: chip.color, colorScheme: chip.colorScheme, className: styles$w.chip, ...(chip.customColor && { customColor: chip.customColor }), ...(chip.variant && { variant: chip.variant }), ...(chip.icon && { icon: chip.icon }) }, index))) })), footer && (jsxRuntime.jsx("div", { className: styles$w.footerContent, children: footer }))] }))), resolvedDirection === "row" && ActionsComponent] }));
|
|
2008
2008
|
};
|
|
2009
2009
|
|
|
2010
|
-
var styles$
|
|
2010
|
+
var styles$v = {"cardContainer":"cw-card-list-module__cardContainer__l3YEh","pagination":"cw-card-list-module__pagination__5Ay78","pageInfo":"cw-card-list-module__pageInfo__uiMel","emptyState":"cw-card-list-module__emptyState__RlqiS"};
|
|
2011
2011
|
|
|
2012
2012
|
/**
|
|
2013
2013
|
* Option to display in the CwSelect
|
|
@@ -2102,10 +2102,10 @@ function CwCardList({ items, renderCard, pageSize = 10, layout = 'grid', default
|
|
|
2102
2102
|
icon: sortDirection === 'asc' ? 'arrow-up' : 'arrow-down',
|
|
2103
2103
|
onClick: () => setSortDirection(prev => prev === 'asc' ? 'desc' : 'asc'),
|
|
2104
2104
|
title: `Sort ${sortDirection === 'asc' ? 'descending' : 'ascending'}`,
|
|
2105
|
-
}, value: sortKey, onChange: (e) => handleSortChange(e.target.value), children: sortOptions.map(option => (jsxRuntime.jsx(CwOption, { value: option.key, children: option.label }, option.key))) }) })), isLoading ? (jsxRuntime.jsx("div", { className: `${styles$
|
|
2105
|
+
}, value: sortKey, onChange: (e) => handleSortChange(e.target.value), children: sortOptions.map(option => (jsxRuntime.jsx(CwOption, { value: option.key, children: option.label }, option.key))) }) })), isLoading ? (jsxRuntime.jsx("div", { className: `${styles$v.cardContainer}`, "data-layout": layout, style: gridStyle, "aria-busy": true, children: Array.from({ length: normalizeSkeletonCount(skeletonCards, 6) }, (_, index) => (jsxRuntime.jsx(CwCard, { isLoading: true, children: null }, `skeleton_${index}`))) })) : sortedItems.length === 0 ? (jsxRuntime.jsx("div", { className: styles$v.emptyState, children: emptyState || jsxRuntime.jsx("p", { children: "No items to display" }) })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: `${styles$v.cardContainer}`, "data-layout": layout, style: gridStyle, children: visibleItems.map((item, index) => (jsxRuntime.jsx(React.Fragment, { children: renderCard(item, index) }, keyExtractor ? keyExtractor(item, index) : index))) }), totalPages > 1 && (jsxRuntime.jsxs("div", { className: styles$v.pagination, children: [jsxRuntime.jsx(CwButton, { variant: "icon", icon: "chevron-left", disabled: currentPage === 1, onClick: () => setCurrentPage(p => Math.max(1, p - 1)) }), jsxRuntime.jsxs("span", { className: styles$v.pageInfo, children: [currentPage, " / ", totalPages] }), jsxRuntime.jsx(CwButton, { variant: "icon", icon: "chevron-right", disabled: currentPage === totalPages, onClick: () => setCurrentPage(p => Math.min(totalPages, p + 1)) })] }))] }))] }));
|
|
2106
2106
|
}
|
|
2107
2107
|
|
|
2108
|
-
var styles$
|
|
2108
|
+
var styles$u = {"cw-accordion":"cw-accordion-module__cw-accordion__ErvlW","cw-accordion-body":"cw-accordion-module__cw-accordion-body__xlI8b"};
|
|
2109
2109
|
|
|
2110
2110
|
/**
|
|
2111
2111
|
* Legacy collapsible section with its own header markup.
|
|
@@ -2120,7 +2120,7 @@ function CwAccordionContainer(CwelltAccordionContainerProps) {
|
|
|
2120
2120
|
setVisible_accordionBody(!isVisible_accordionBody);
|
|
2121
2121
|
};
|
|
2122
2122
|
// #endregion
|
|
2123
|
-
return (jsxRuntime.jsxs("div", { className: styles$
|
|
2123
|
+
return (jsxRuntime.jsxs("div", { className: styles$u["cw-accordion"], style: CwelltAccordionContainerProps.style, "data-open": isVisible_accordionBody, children: [jsxRuntime.jsxs("header", { onClick: () => showAccordionBody(), children: [jsxRuntime.jsx("div", { children: CwelltAccordionContainerProps.desc_text }), jsxRuntime.jsx("button", { className: "cw-button-icon" })] }), jsxRuntime.jsx("div", { className: styles$u["cw-accordion-body"], children: CwelltAccordionContainerProps.children })] }));
|
|
2124
2124
|
}
|
|
2125
2125
|
|
|
2126
2126
|
/*
|
|
@@ -2399,7 +2399,7 @@ function CwTable({ columns, data, pagination = false, pageSizeOptions = [5, 10,
|
|
|
2399
2399
|
}, min: 1, max: totalPages }), jsxRuntime.jsx("span", { children: "/" }), jsxRuntime.jsx("span", { children: totalPages }), jsxRuntime.jsx(CwButton, { onClick: () => handlePageChange(page + 1), disabled: page === totalPages || totalPages <= 1, variant: "icon", icon: "chevron-right" }), jsxRuntime.jsx(CwButton, { onClick: () => handlePageChange(totalPages), disabled: page === totalPages || totalPages <= 1, variant: "icon", icon: "chevron-right-double" })] })] }))] }));
|
|
2400
2400
|
}
|
|
2401
2401
|
|
|
2402
|
-
var styles$
|
|
2402
|
+
var styles$t = {"dropIndicator":"cw-sortable-table-module__dropIndicator__ov-Jz","dragging":"cw-sortable-table-module__dragging__MrLrz","saveBar":"cw-sortable-table-module__saveBar__3OdoZ cw-flex-row cw-align-center-center cw-gap-small"};
|
|
2403
2403
|
|
|
2404
2404
|
/**
|
|
2405
2405
|
* Hook that provides state management for CwSortableTable.
|
|
@@ -2462,7 +2462,7 @@ function useSortableTable(initialItems) {
|
|
|
2462
2462
|
}
|
|
2463
2463
|
|
|
2464
2464
|
// ─── Drop indicator row ───────────────────────────────────────────────────────
|
|
2465
|
-
const DropIndicatorRow = ({ colSpan }) => (jsxRuntime.jsx("tr", { className: styles$
|
|
2465
|
+
const DropIndicatorRow = ({ colSpan }) => (jsxRuntime.jsx("tr", { className: styles$t.dropIndicator, "aria-hidden": true, children: jsxRuntime.jsx("td", { colSpan: colSpan, children: jsxRuntime.jsx("div", {}) }) }));
|
|
2466
2466
|
// ─── Loading skeleton ─────────────────────────────────────────────────────────
|
|
2467
2467
|
// Plain td cells (no local class): the ghost cells get the same global cw-table
|
|
2468
2468
|
// padding as real body cells, so row heights match the loaded rows exactly.
|
|
@@ -2565,11 +2565,11 @@ function CwSortableTable({ columns, data, rowKey = "key", onReorder, loading = f
|
|
|
2565
2565
|
return items.map((item, index) => {
|
|
2566
2566
|
const key = item[rowKey];
|
|
2567
2567
|
const isDragging = draggedItem?.[rowKey] === key;
|
|
2568
|
-
return (jsxRuntime.jsxs(React.Fragment, { children: [dropIndicatorIndex === index && (jsxRuntime.jsx(DropIndicatorRow, { colSpan: totalCols })), jsxRuntime.jsxs("tr", { draggable: true, onDragStart: (e) => handleDragStart(e, item), onDragOver: (e) => handleDragOver(e, index), onDragEnd: handleDragEnd, className: isDragging ? styles$
|
|
2568
|
+
return (jsxRuntime.jsxs(React.Fragment, { children: [dropIndicatorIndex === index && (jsxRuntime.jsx(DropIndicatorRow, { colSpan: totalCols })), jsxRuntime.jsxs("tr", { draggable: true, onDragStart: (e) => handleDragStart(e, item), onDragOver: (e) => handleDragOver(e, index), onDragEnd: handleDragEnd, className: isDragging ? styles$t.dragging : "", children: [showHandle && (jsxRuntime.jsx("td", { className: "cw-table-col-action", children: jsxRuntime.jsx(CwIcon, { iconId: "grip-dots", color: "neutral", style: { opacity: 0.75 } }) })), columns.map(col => (jsxRuntime.jsx("td", { className: col.className ?? "", style: col.width ? { width: col.width } : undefined, children: renderCell(item, col) }, `${String(key)}_${col.key}`)))] }), index === items.length - 1 && dropIndicatorIndex === items.length && (jsxRuntime.jsx(DropIndicatorRow, { colSpan: totalCols }))] }, String(key)));
|
|
2569
2569
|
});
|
|
2570
2570
|
};
|
|
2571
2571
|
// ── Render ──────────────────────────────────────────────────────────────
|
|
2572
|
-
return (jsxRuntime.jsxs("div", { className: `cw-table-container ${className ?? ""}`, style: containerStyle, "aria-busy": loading || undefined, children: [jsxRuntime.jsxs("table", { className: `cw-table cw-sortable-table ${tableClassName ?? ""}`, style: { width: "100%" }, children: [jsxRuntime.jsx("thead", { children: jsxRuntime.jsxs("tr", { children: [showHandle && jsxRuntime.jsx("th", { className: "cw-table-col-action" }), columns.map(col => (jsxRuntime.jsx("th", { className: col.className ?? "", style: col.width ? { width: col.width, minWidth: 50 } : undefined, children: col.title }, col.key)))] }) }), jsxRuntime.jsx("tbody", { children: renderBody() })] }), !loading && hasChanges && (jsxRuntime.jsxs("footer", { className: styles$
|
|
2572
|
+
return (jsxRuntime.jsxs("div", { className: `cw-table-container ${className ?? ""}`, style: containerStyle, "aria-busy": loading || undefined, children: [jsxRuntime.jsxs("table", { className: `cw-table cw-sortable-table ${tableClassName ?? ""}`, style: { width: "100%" }, children: [jsxRuntime.jsx("thead", { children: jsxRuntime.jsxs("tr", { children: [showHandle && jsxRuntime.jsx("th", { className: "cw-table-col-action" }), columns.map(col => (jsxRuntime.jsx("th", { className: col.className ?? "", style: col.width ? { width: col.width, minWidth: 50 } : undefined, children: col.title }, col.key)))] }) }), jsxRuntime.jsx("tbody", { children: renderBody() })] }), !loading && hasChanges && (jsxRuntime.jsxs("footer", { className: styles$t.saveBar, children: [jsxRuntime.jsx(CwButton, { variant: "outline", color: "neutral", icon: "undo", text: discardLabel, onClick: handleDiscard }), jsxRuntime.jsx(CwButton, { variant: "solid", color: "primary", icon: "save", text: saveLabel, onClick: () => handleSave(onReorder) })] }))] }));
|
|
2573
2573
|
}
|
|
2574
2574
|
|
|
2575
2575
|
/**
|
|
@@ -2770,7 +2770,7 @@ function CwTableServerSide({ columns, data, totalItems, pagination = false, page
|
|
|
2770
2770
|
}, min: 1, max: totalPages }), jsxRuntime.jsx("span", { children: "/" }), jsxRuntime.jsx("span", { children: totalPages }), jsxRuntime.jsx(CwButton, { onClick: () => handlePageChange(state.page + 1), disabled: state.page === totalPages || totalPages <= 1, variant: "icon", icon: "chevron-right" }), jsxRuntime.jsx(CwButton, { onClick: () => handlePageChange(totalPages), disabled: state.page === totalPages || totalPages <= 1, variant: "icon", icon: "chevron-right-double" })] })] }))] }));
|
|
2771
2771
|
}
|
|
2772
2772
|
|
|
2773
|
-
var styles$
|
|
2773
|
+
var styles$s = {"cw-tabs":"cw-tabs-module__cw-tabs__1pmji","cw-tabs-content":"cw-tabs-module__cw-tabs-content__HTp8d"};
|
|
2774
2774
|
|
|
2775
2775
|
const BADGE_COLOR_SCHEMES = ['primary', 'danger', 'warning', 'success', 'info', 'neutral'];
|
|
2776
2776
|
function isBadgeColorScheme(value) {
|
|
@@ -2824,11 +2824,11 @@ function CwTabs(CwTabsProps) {
|
|
|
2824
2824
|
const tabsListStyle = position === 'left' && CwTabsProps.tabsListWidth
|
|
2825
2825
|
? { minWidth: CwTabsProps.tabsListWidth }
|
|
2826
2826
|
: undefined;
|
|
2827
|
-
return (jsxRuntime.jsxs("div", { id: CwTabsProps.id, "data-testid": CwTabsProps.testId, className: styles$
|
|
2827
|
+
return (jsxRuntime.jsxs("div", { id: CwTabsProps.id, "data-testid": CwTabsProps.testId, className: styles$s['cw-tabs'] + (CwTabsProps.className ? ` ${CwTabsProps.className}` : "") + "", style: CwTabsProps.style, "data-tabs-position": position, children: [jsxRuntime.jsx("ul", { style: tabsListStyle, children: CwTabsProps.tabs.map(tab => {
|
|
2828
2828
|
const badgeScheme = tab.badgeColor && isBadgeColorScheme(tab.badgeColor) ? tab.badgeColor : undefined;
|
|
2829
2829
|
const rawBadgeColor = tab.badgeColor && !badgeScheme ? tab.badgeColor : undefined;
|
|
2830
2830
|
return (jsxRuntime.jsxs("li", { className: `${tab.key === activeTab ? "cw-tab-active" : ""}`, onClick: () => handleTabClick(tab), "data-active": tab.key === activeTab, "data-tab-key": tab.key, "data-badge-themed": tab.badgeColor ? "true" : undefined, children: [jsxRuntime.jsx(TabIcon, { icon: tab.icon }), tab.title, (tab.badge !== undefined || tab.badgeColor) && (jsxRuntime.jsx(CwBadge, { count: typeof tab.badge === 'number' ? tab.badge : undefined, content: typeof tab.badge === 'string' ? tab.badge : undefined, size: "medium", color: badgeScheme ?? 'primary', variant: badgeScheme ? 'solid' : 'soft', style: rawBadgeColor ? { backgroundColor: rawBadgeColor, color: getContrastColor(rawBadgeColor) } : undefined }))] }, tab.key));
|
|
2831
|
-
}) }), jsxRuntime.jsx("div", { className: styles$
|
|
2831
|
+
}) }), jsxRuntime.jsx("div", { className: styles$s['cw-tabs-content'], "data-active-tab-key": activeTab ?? undefined, style: CwTabsProps.contentHeight ? { '--tabs-height': CwTabsProps.contentHeight } : undefined, children: activeTab !== null && CwTabsProps.tabs.find(tab => tab.key === activeTab)?.content })] }));
|
|
2832
2832
|
}
|
|
2833
2833
|
|
|
2834
2834
|
/**
|
|
@@ -2856,7 +2856,7 @@ const CwExpandable = ({ briefing, onToggle, onOpen, onClose, children, ...detail
|
|
|
2856
2856
|
return (jsxRuntime.jsxs("details", { ref: myRef, ...detailsProps, className: "cw-expandable", children: [jsxRuntime.jsx("summary", { children: briefing }), children && jsxRuntime.jsx("section", { children: children })] }));
|
|
2857
2857
|
};
|
|
2858
2858
|
|
|
2859
|
-
var styles$
|
|
2859
|
+
var styles$r = {"cw-master-detail":"cw-master-detail-module__cw-master-detail__O8-FA","resizer":"cw-master-detail-module__resizer__hvGhL","detail":"cw-master-detail-module__detail__BG4Dm","skeletonRow":"cw-master-detail-module__skeletonRow__6BUgt","detailSkeleton":"cw-master-detail-module__detailSkeleton__GcDS4","detail-empty":"cw-master-detail-module__detail-empty__KFScK"};
|
|
2860
2860
|
|
|
2861
2861
|
/*
|
|
2862
2862
|
* Fixed rem widths for the ghosts: the master rows and detail panel size their
|
|
@@ -2900,12 +2900,12 @@ const CwMasterDetail = ({ id, className, style, items, selectedKey, onSelect, de
|
|
|
2900
2900
|
document.body.style.userSelect = '';
|
|
2901
2901
|
};
|
|
2902
2902
|
}, [isResizing, minMasterWidth, maxMasterWidth]);
|
|
2903
|
-
const rootClassName = [styles$
|
|
2903
|
+
const rootClassName = [styles$r['cw-master-detail'], className].filter(Boolean).join(' ');
|
|
2904
2904
|
return (jsxRuntime.jsxs("div", { id: id, ref: containerRef, className: rootClassName, style: style, "aria-busy": loading || undefined, children: [jsxRuntime.jsxs("aside", { style: { width: masterWidth, minWidth: masterWidth }, children: [showSearch && (jsxRuntime.jsx("search", { children: jsxRuntime.jsx("input", { type: "text", placeholder: searchPlaceholder, onChange: (e) => onSearch?.(e.target.value) }) })), masterHeader && (jsxRuntime.jsx("header", { children: masterHeader })), jsxRuntime.jsx("ul", { children: loading
|
|
2905
|
-
? Array.from({ length: skeletonRows }, (_unused, rowIndex) => (jsxRuntime.jsxs("li", { "aria-hidden": "true", children: [jsxRuntime.jsx(CwSkeleton, { variant: "circle", height: "2rem" }), jsxRuntime.jsxs("div", { className: styles$
|
|
2906
|
-
: items.map((item) => (jsxRuntime.jsx("li", { "data-master-key": item.key, "data-selected": item.key === selectedKey, "data-disabled": item.disabled, onClick: () => onSelect?.(item.key), children: item.content }, item.key))) }), masterFooter && (jsxRuntime.jsx("footer", { children: masterFooter }))] }), resizable && (jsxRuntime.jsx("div", { className: styles$
|
|
2905
|
+
? Array.from({ length: skeletonRows }, (_unused, rowIndex) => (jsxRuntime.jsxs("li", { "aria-hidden": "true", children: [jsxRuntime.jsx(CwSkeleton, { variant: "circle", height: "2rem" }), jsxRuntime.jsxs("div", { className: styles$r.skeletonRow, children: [jsxRuntime.jsx(CwSkeleton, { variant: "text", width: MASTER_TITLE_WIDTHS[rowIndex % MASTER_TITLE_WIDTHS.length] }), jsxRuntime.jsx(CwSkeleton, { variant: "text", width: MASTER_SUBTITLE_WIDTH })] })] }, `skeleton_${rowIndex}`)))
|
|
2906
|
+
: items.map((item) => (jsxRuntime.jsx("li", { "data-master-key": item.key, "data-selected": item.key === selectedKey, "data-disabled": item.disabled, onClick: () => onSelect?.(item.key), children: item.content }, item.key))) }), masterFooter && (jsxRuntime.jsx("footer", { children: masterFooter }))] }), resizable && (jsxRuntime.jsx("div", { className: styles$r.resizer, "data-resizing": isResizing, onMouseDown: handleMouseDown })), jsxRuntime.jsx("section", { className: styles$r.detail, children: loading ? (jsxRuntime.jsxs("div", { className: styles$r.detailSkeleton, "aria-hidden": "true", children: [jsxRuntime.jsx(CwSkeleton, { variant: "text", width: "14rem", height: "1.5em" }), jsxRuntime.jsx(CwSkeleton, { variant: "text", width: "22rem" }), jsxRuntime.jsx(CwSkeleton, { variant: "text", width: "18rem" }), jsxRuntime.jsx(CwSkeleton, { variant: "text", width: "20rem" })] })) : selectedKey && detailContent
|
|
2907
2907
|
? detailContent
|
|
2908
|
-
: (jsxRuntime.jsx("div", { className: styles$
|
|
2908
|
+
: (jsxRuntime.jsx("div", { className: styles$r['detail-empty'], children: emptyDetail ?? 'Select an item to view details' })) })] }));
|
|
2909
2909
|
};
|
|
2910
2910
|
|
|
2911
2911
|
/*
|
|
@@ -2945,7 +2945,7 @@ const CwKeyValueList = ({ items, className = "", emptyValue = "-", direction = "
|
|
|
2945
2945
|
return (jsxRuntime.jsx("dl", { className: `cw-keyvalue-list ${className}`, "data-direction": groupLayout?.direction, "data-align": groupLayout?.align, "data-justify": groupLayout?.justify, "data-gap": groupLayout?.gap, "aria-busy": loading || undefined, ...htmlProps, children: renderItems() }));
|
|
2946
2946
|
};
|
|
2947
2947
|
|
|
2948
|
-
var styles$
|
|
2948
|
+
var styles$q = {"sortableList":"cw-sortable-list-module__sortableList__PyLO-","sortableItem":"cw-sortable-list-module__sortableItem__FAnn2","readOnly":"cw-sortable-list-module__readOnly__r7GcH","dragging":"cw-sortable-list-module__dragging__MD715","moved":"cw-sortable-list-module__moved__sE6-N","expandedContent":"cw-sortable-list-module__expandedContent__Kocna","sortableHandle":"cw-sortable-list-module__sortableHandle__HvYBK","sortableContent":"cw-sortable-list-module__sortableContent__C6JhR","sortableTitle":"cw-sortable-list-module__sortableTitle__EgWXr","sortableExtraContent":"cw-sortable-list-module__sortableExtraContent__s4LSv","sortableActions":"cw-sortable-list-module__sortableActions__gwQOU","dropIndicator":"cw-sortable-list-module__dropIndicator__MNSF-","emptyMessage":"cw-sortable-list-module__emptyMessage__gMpaL"};
|
|
2949
2949
|
|
|
2950
2950
|
function CwSortableList({ items, onReorder, renderItem, className = '', movedItems = new Set(), emptyMessage = "No elements to show", readOnly = false, maxHeight = "300px", }) {
|
|
2951
2951
|
const [draggedItem, setDraggedItem] = React.useState(null);
|
|
@@ -3008,15 +3008,15 @@ function CwSortableList({ items, onReorder, renderItem, className = '', movedIte
|
|
|
3008
3008
|
}
|
|
3009
3009
|
};
|
|
3010
3010
|
if (items.length === 0) {
|
|
3011
|
-
return (jsxRuntime.jsx("div", { className: `${styles$
|
|
3011
|
+
return (jsxRuntime.jsx("div", { className: `${styles$q.sortableList} ${styles$q.emptyState} ${className}`, children: jsxRuntime.jsx("div", { className: styles$q.emptyMessage, children: emptyMessage }) }));
|
|
3012
3012
|
}
|
|
3013
|
-
return (jsxRuntime.jsx("div", { className: `${styles$
|
|
3013
|
+
return (jsxRuntime.jsx("div", { className: `${styles$q.sortableList} ${className}`, style: { maxHeight }, children: items.map((item, index) => {
|
|
3014
3014
|
const { title, extraContent, actions, showHandle = true, expandedContent } = renderItem(item);
|
|
3015
3015
|
const isDragging = draggedItem?.id === item.id;
|
|
3016
3016
|
const isMoved = movedItems.has(item.id);
|
|
3017
3017
|
const canExpand = Boolean(expandedContent);
|
|
3018
3018
|
const isExpanded = expandedItems.has(item.id);
|
|
3019
|
-
return (jsxRuntime.jsxs(React.Fragment, { children: [dropIndicatorIndex === index && jsxRuntime.jsx("div", { className: styles$
|
|
3019
|
+
return (jsxRuntime.jsxs(React.Fragment, { children: [dropIndicatorIndex === index && jsxRuntime.jsx("div", { className: styles$q.dropIndicator }), jsxRuntime.jsxs("div", { className: `${styles$q.sortableItem} ${isDragging ? styles$q.dragging : ''} ${isMoved ? styles$q.moved : ''} ${readOnly ? styles$q.readOnly : ''}`, draggable: !readOnly, onDragStart: (e) => handleDragStart(e, item), onDragOver: (e) => handleDragOver(e, index), onDragEnd: handleDragEnd, children: [jsxRuntime.jsxs("header", { children: [canExpand ? (jsxRuntime.jsx(CwButton, { variant: "icon", onClick: () => toggleExpanded(item.id), type: "button", title: isExpanded ? "Collapse" : "Expand", icon: isExpanded ? "chevron-up" : "chevron-down" })) : showHandle ? (jsxRuntime.jsx("div", { className: styles$q.sortableHandle, children: jsxRuntime.jsx(CwIcon, { iconId: "grip-dots" }) })) : null, jsxRuntime.jsxs("div", { className: styles$q.sortableContent, children: [jsxRuntime.jsx("div", { className: styles$q.sortableTitle, children: title }), extraContent && jsxRuntime.jsx("div", { className: styles$q.sortableExtraContent, children: extraContent })] }), jsxRuntime.jsx("div", { className: styles$q.sortableActions, children: actions })] }), isExpanded && expandedContent && (jsxRuntime.jsx("div", { className: styles$q.expandedContent, children: expandedContent }))] }), !readOnly && dropIndicatorIndex === items.length && index === items.length - 1 && (jsxRuntime.jsx("div", { className: styles$q.dropIndicator }))] }, item.id));
|
|
3020
3020
|
}) }));
|
|
3021
3021
|
}
|
|
3022
3022
|
|
|
@@ -3143,7 +3143,7 @@ const CwTableGrouped = props => {
|
|
|
3143
3143
|
group.rows.map((child, j) => (jsxRuntime.jsx("tr", { "data-row": true, "data-row-last-of-group": j == group.rows.length - 1, children: child.cells.map((cell, k) => (jsxRuntime.jsx("td", { children: cell.content }, k))) }, j)))] }, i))) }), jsxRuntime.jsx("tfoot", {})] }) }));
|
|
3144
3144
|
};
|
|
3145
3145
|
|
|
3146
|
-
var styles$
|
|
3146
|
+
var styles$p = {"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"};
|
|
3147
3147
|
|
|
3148
3148
|
const DEFAULT_LABELS$1 = {
|
|
3149
3149
|
uploadDisabled: 'Upload disabled',
|
|
@@ -3329,12 +3329,12 @@ function CwFileUploadMultiple(fileUploadProps) {
|
|
|
3329
3329
|
}
|
|
3330
3330
|
}
|
|
3331
3331
|
};
|
|
3332
|
-
return (jsxRuntime.jsxs("div", { className: `${styles$
|
|
3332
|
+
return (jsxRuntime.jsxs("div", { className: `${styles$p.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$p.hiddenInput }), selectedFiles.length === 0 && !existingFile ? (jsxRuntime.jsxs("div", { className: `${styles$p.uploadArea} ${fileUploadProps.disabled ? styles$p.uploadAreaDisabled : ''}`, onDragOver: handleDragOver, onDrop: handleDrop, onClick: !fileUploadProps.disabled ? handleButtonClick : undefined, children: [jsxRuntime.jsx(CwIcon, { iconId: "upload" }), jsxRuntime.jsx("p", { className: `${styles$p.uploadTitle}`, children: fileUploadProps.disabled ? labels.uploadDisabled : labels.clickToUpload }), jsxRuntime.jsxs("p", { className: `${styles$p.uploadSubtitle}`, children: [fileUploadProps.accept ? labels.acceptedFiles(fileUploadProps.accept) : labels.allTypesAccepted, !fileUploadProps.multiple && ` ${labels.singleFileOnly}`] })] })) : selectedFiles.length === 0 && existingFile ? (jsxRuntime.jsxs("div", { className: styles$p.filesContainer, children: [jsxRuntime.jsxs("div", { className: styles$p.fileItem, children: [jsxRuntime.jsxs("div", { className: styles$p.fileIcon, children: [jsxRuntime.jsx(CwIcon, { iconId: "page" }), jsxRuntime.jsx("span", { className: styles$p.fileExtension, children: getFileExtension(existingFile) })] }), jsxRuntime.jsx("div", { className: styles$p.fileInfo, children: jsxRuntime.jsx("p", { className: styles$p.fileName, children: existingFile }) }), jsxRuntime.jsx(CwButton, { variant: "icon", icon: "close", color: "neutral", onClick: () => {
|
|
3333
3333
|
setExistingFile(undefined);
|
|
3334
3334
|
if (fileUploadProps.onSelect) {
|
|
3335
3335
|
fileUploadProps.onSelect(null);
|
|
3336
3336
|
}
|
|
3337
|
-
}, disabled: fileUploadProps.disabled, className: styles$
|
|
3337
|
+
}, disabled: fileUploadProps.disabled, className: styles$p.smallButton })] }), jsxRuntime.jsx(CwButton, { text: labels.changeFile, icon: "refresh", onClick: handleButtonClick, disabled: fileUploadProps.disabled })] })) : (jsxRuntime.jsxs("div", { className: styles$p.filesContainer, children: [jsxRuntime.jsxs("header", { className: "cw-flex-row cw-align-between-center cw-flex-wrap", children: [jsxRuntime.jsx("small", { className: styles$p.filesCount, children: fileUploadProps.statusText ?? labels.filesSelected(selectedFiles.length) }), jsxRuntime.jsxs("div", { className: "cw-flex-row", children: [fileUploadProps.onConfirm && selectedFiles.length > 0 && (jsxRuntime.jsx(CwButton, { onClick: () => {
|
|
3338
3338
|
let files = fileInputRef.current?.files ?? null;
|
|
3339
3339
|
if (!files) {
|
|
3340
3340
|
const dataTransfer = new DataTransfer();
|
|
@@ -3342,7 +3342,7 @@ function CwFileUploadMultiple(fileUploadProps) {
|
|
|
3342
3342
|
files = dataTransfer.files;
|
|
3343
3343
|
}
|
|
3344
3344
|
fileUploadProps.onConfirm?.(files);
|
|
3345
|
-
}, disabled: fileUploadProps.disabled || fileUploadProps.confirmDisabled, icon: "upload", text: fileUploadProps.confirmLabel ?? labels.upload })), !fileUploadProps.hideClearAll && (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$
|
|
3345
|
+
}, disabled: fileUploadProps.disabled || fileUploadProps.confirmDisabled, icon: "upload", text: fileUploadProps.confirmLabel ?? labels.upload })), !fileUploadProps.hideClearAll && (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$p.fileItem, children: [jsxRuntime.jsxs("div", { className: styles$p.fileIcon, children: [jsxRuntime.jsx(CwIcon, { iconId: "page" }), jsxRuntime.jsx("span", { className: styles$p.fileExtension, children: getFileExtension(file.name) })] }), jsxRuntime.jsxs("div", { className: styles$p.fileInfo, children: [jsxRuntime.jsx("p", { className: styles$p.fileName, children: file.name }), jsxRuntime.jsxs("p", { className: styles$p.fileSize, children: [(file.size / 1024).toFixed(1), " KB"] })] }), jsxRuntime.jsx(CwButton, { variant: "icon", icon: "close", color: "neutral", onClick: () => removeFile(index), className: styles$p.smallButton })] }, `${file.name}-${file.size}`))), 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 }))] }))] }));
|
|
3346
3346
|
}
|
|
3347
3347
|
|
|
3348
3348
|
/**
|
|
@@ -3395,7 +3395,7 @@ function CwDigit(props) {
|
|
|
3395
3395
|
return (jsxRuntime.jsxs("div", { className: "cw-input-text", "data-direction": labelProps ? flexDirection : "column", "data-feedback-position": feedbackPosition, children: [jsxRuntime.jsxs(CwAlign, { ...alignProps, itemProp: inputProps.required === true ? "required" : "", children: [labelProps && (jsxRuntime.jsxs(CwLabel, { ...labelProps, children: [iconProps && jsxRuntime.jsx(CwIcon, { ...iconProps }), labelProps.text] })), jsxRuntime.jsxs("div", { className: "cw-input-button", children: [jsxRuntime.jsx("input", { type: "number", ...inputProps, "aria-describedby": describedBy }), buttonProps && jsxRuntime.jsx(CwButton, { ...buttonProps })] })] }), feedbackMessages.map((feedbackItem, index) => (jsxRuntime.jsx("p", { id: feedbackIds[index], className: "cw-input-info", "data-color": feedbackItem.type, "aria-live": "polite", children: feedbackItem.message }, index)))] }));
|
|
3396
3396
|
}
|
|
3397
3397
|
|
|
3398
|
-
var styles$
|
|
3398
|
+
var styles$o = {"colorPreview":"cw-color-picker-module__colorPreview__ylJcD","hueBar":"cw-color-picker-module__hueBar__bFhyC","hueBarSlider":"cw-color-picker-module__hueBarSlider__D53IV","colorPickerInteractiveArea":"cw-color-picker-module__colorPickerInteractiveArea__KZFR1","colorArea":"cw-color-picker-module__colorArea__xgpSE","colorAreaOverlay":"cw-color-picker-module__colorAreaOverlay__pmgOc","hueBackground":"cw-color-picker-module__hueBackground__Mks78","whiteGradient":"cw-color-picker-module__whiteGradient__Bt-fU","blackGradient":"cw-color-picker-module__blackGradient__VnEKJ","colorAreaCursor":"cw-color-picker-module__colorAreaCursor__lotg3"};
|
|
3399
3399
|
|
|
3400
3400
|
const CwColorPicker = ({ initialColor, onChange, previewText = "Color preview" }) => {
|
|
3401
3401
|
const [rgb, setRgb] = React.useState({ r: 255, g: 255, b: 255 });
|
|
@@ -3598,13 +3598,13 @@ const CwColorPicker = ({ initialColor, onChange, previewText = "Color preview" }
|
|
|
3598
3598
|
// Calculate background color for the main area based on current hue
|
|
3599
3599
|
const hueColor = hsvToRgb(hsv.h, 100, 100);
|
|
3600
3600
|
const hueHex = rgbToHex(hueColor.r, hueColor.g, hueColor.b);
|
|
3601
|
-
return (jsxRuntime.jsxs("div", { className: "cw-flex-column cw-gap-small", children: [jsxRuntime.jsxs("div", { className: "cw-flex-row cw-align-between-center cw-gap-small", children: [jsxRuntime.jsx("div", { className: styles$
|
|
3601
|
+
return (jsxRuntime.jsxs("div", { className: "cw-flex-column cw-gap-small", children: [jsxRuntime.jsxs("div", { className: "cw-flex-row cw-align-between-center cw-gap-small", children: [jsxRuntime.jsx("div", { className: styles$o.colorPreview, style: { backgroundColor: hexColor, color: getContrastColor(hexColor) }, children: previewText }), jsxRuntime.jsx("button", { type: "button", className: "cw-button", onClick: toggleInputMode, style: { minWidth: "3ch" }, children: inputMode === 'hex' ? 'RGB' : 'HEX' })] }), jsxRuntime.jsx("div", { className: "color-inputs-row", children: inputMode === 'hex' ? (jsxRuntime.jsx("input", { type: "text", value: hexColor, onChange: handleHexChange })) : (jsxRuntime.jsxs("div", { className: "cw-grid-base-3 cw-gap-small", children: [jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("label", { children: "R:" }), jsxRuntime.jsx("input", { type: "number", name: "r", min: "0", max: "255", value: rgb.r, onChange: handleRgbChange })] }), jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("label", { children: "G:" }), jsxRuntime.jsx("input", { type: "number", name: "g", min: "0", max: "255", value: rgb.g, onChange: handleRgbChange })] }), jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("label", { children: "B:" }), jsxRuntime.jsx("input", { type: "number", name: "b", min: "0", max: "255", value: rgb.b, onChange: handleRgbChange })] })] })) }), jsxRuntime.jsxs("div", { className: styles$o.colorPickerInteractiveArea, children: [jsxRuntime.jsx("div", { className: styles$o.hueBar, ref: hueBarRef, onClick: handleHueBarClick, children: jsxRuntime.jsx("div", { className: styles$o.hueBarSlider, style: { top: `${(360 - hsv.h) / 360 * 100}%` } }) }), jsxRuntime.jsxs("div", { className: styles$o.colorArea, ref: colorAreaRef, onClick: handleColorAreaClick, children: [jsxRuntime.jsxs("div", { className: styles$o.colorAreaOverlay, children: [jsxRuntime.jsx("div", { className: styles$o.hueBackground, style: { backgroundColor: hueHex } }), jsxRuntime.jsx("div", { className: styles$o.whiteGradient }), jsxRuntime.jsx("div", { className: styles$o.blackGradient })] }), jsxRuntime.jsx("div", { className: styles$o.colorAreaCursor, style: {
|
|
3602
3602
|
left: `${hsv.s}%`,
|
|
3603
3603
|
top: `${100 - hsv.v}%`
|
|
3604
3604
|
} })] })] })] }));
|
|
3605
3605
|
};
|
|
3606
3606
|
|
|
3607
|
-
var styles$
|
|
3607
|
+
var styles$n = {"container":"cw-input-color-module__container__x7gpk","selectColor":"cw-input-color-module__selectColor__DTo3V","disabled":"cw-input-color-module__disabled__O1fK5","readOnly":"cw-input-color-module__readOnly__HL-Bz","colorDropdown":"cw-input-color-module__colorDropdown__pX2bc"};
|
|
3608
3608
|
|
|
3609
3609
|
const CwInputColor = ({ value, onChange, previewText = "Color preview", disabled = false, readOnly = false, width = '4rem', height = '2rem', labelProps, layoutProps, className, style, }) => {
|
|
3610
3610
|
const [isOpen, setIsOpen] = React.useState(false);
|
|
@@ -3737,11 +3737,11 @@ const CwInputColor = ({ value, onChange, previewText = "Color preview", disabled
|
|
|
3737
3737
|
break;
|
|
3738
3738
|
}
|
|
3739
3739
|
};
|
|
3740
|
-
return (jsxRuntime.jsxs("div", { className: `cw-input-color ${styles$
|
|
3740
|
+
return (jsxRuntime.jsxs("div", { className: `cw-input-color ${styles$n.container}${className ? ` ${className}` : ''}`, style: style, "data-direction": layoutProps?.direction ?? "row", "data-align": layoutProps?.align, children: [labelProps && (jsxRuntime.jsx(CwLabel, { ...labelProps })), jsxRuntime.jsx("div", { ref: containerRef, onClick: handleToggle, className: `${styles$n.selectColor} ${disabled ? styles$n.disabled : ''} ${readOnly ? styles$n.readOnly : ''}`, style: {
|
|
3741
3741
|
width,
|
|
3742
3742
|
height,
|
|
3743
3743
|
backgroundColor: value,
|
|
3744
|
-
}, "aria-label": "Open color picker", "aria-expanded": isOpen, "aria-haspopup": "dialog", "aria-readonly": readOnly, role: "button", tabIndex: disabled ? -1 : 0, onKeyDown: handleKeyDown }), isOpen && reactDom.createPortal(jsxRuntime.jsx("div", { ref: dropdownRef, className: styles$
|
|
3744
|
+
}, "aria-label": "Open color picker", "aria-expanded": isOpen, "aria-haspopup": "dialog", "aria-readonly": readOnly, role: "button", tabIndex: disabled ? -1 : 0, onKeyDown: handleKeyDown }), isOpen && reactDom.createPortal(jsxRuntime.jsx("div", { ref: dropdownRef, className: styles$n.colorDropdown, style: dropdownStyle, role: "dialog", "aria-modal": "true", "aria-label": "Color picker", children: jsxRuntime.jsx(CwColorPicker, { initialColor: value, onChange: handleColorChange, previewText: previewText }) }), document.body)] }));
|
|
3745
3745
|
};
|
|
3746
3746
|
|
|
3747
3747
|
/**
|
|
@@ -3996,7 +3996,7 @@ function CwInputDateText(props) {
|
|
|
3996
3996
|
return (jsxRuntime.jsx("div", { className: "cw-input-date-text", children: jsxRuntime.jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsxRuntime.jsxs(CwLabel, { ...labelProps, children: [iconProps && jsxRuntime.jsx(CwIcon, { ...iconProps }), labelProps.text] })), jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("input", { type: "text", disabled: disabled, maxLength: 10, onChange: onChangeInputText, pattern: "^$ --|[.0-9\\/\\-]{10}", placeholder: format.toLowerCase(), readOnly: readOnly, ref: textInputRef, required: required, size: width, tabIndex: tabIndex, value: displayedDate }), jsxRuntime.jsxs("span", { children: [jsxRuntime.jsx(CwIcon, { iconId: "calendar" }), jsxRuntime.jsx("input", { type: "date", disabled: disabled, onChange: onChangeInputDate, readOnly: readOnly, ref: dateInputRef, tabIndex: -1, value: date, ...inputProps })] })] }), buttonProps && jsxRuntime.jsx(CwButton, { ...buttonProps })] }) }));
|
|
3997
3997
|
}
|
|
3998
3998
|
|
|
3999
|
-
var styles$
|
|
3999
|
+
var styles$m = {"hiddenInput":"cw-input-image-module__hiddenInput__pS7hb","dropZone":"cw-input-image-module__dropZone__ygrGu","title":"cw-input-image-module__title__Rji5M","subtitle":"cw-input-image-module__subtitle__djYYu","container":"cw-input-image-module__container__qh7Tm"};
|
|
4000
4000
|
|
|
4001
4001
|
const DEFAULT_LABELS = {
|
|
4002
4002
|
dropImageHere: "Drop image here",
|
|
@@ -4075,7 +4075,7 @@ function CwInputImage(props) {
|
|
|
4075
4075
|
const file = e.currentTarget.files?.[0];
|
|
4076
4076
|
void handleFile(file ?? null);
|
|
4077
4077
|
};
|
|
4078
|
-
return (jsxRuntime.jsxs("div", { className: `cw-input-image ${styles$
|
|
4078
|
+
return (jsxRuntime.jsxs("div", { className: `cw-input-image ${styles$m.container}`, "data-size": size, children: [jsxRuntime.jsxs("div", { onDragOver: (e) => e.preventDefault(), onDrop: handleDrop, className: styles$m.dropZone, children: [jsxRuntime.jsx(CwIcon, { iconId: "upload" }), jsxRuntime.jsxs("div", { className: "cw-flex-column cw-gap-tiny", children: [jsxRuntime.jsx("p", { className: styles$m.title, children: labels.dropImageHere }), jsxRuntime.jsx("p", { className: styles$m.subtitle, children: labels.acceptedFormats(allowedFormats.join(", ").replace(/image\//g, "")) })] })] }), jsxRuntime.jsxs("div", { className: "cw-flex-row cw-align-center-center cw-gap-small", children: [jsxRuntime.jsx(CwButton, { onClick: onPasteButtonClick, icon: "paste", text: labels.paste, size: size }), jsxRuntime.jsx(CwButton, { onClick: () => inputRef.current?.click(), icon: "folder-open", text: labels.browse, size: size })] }), jsxRuntime.jsx("input", { ref: inputRef, type: "file", accept: allowedFormats.join(","), onChange: handleFileChange, className: styles$m.hiddenInput, ...inputProps })] }));
|
|
4079
4079
|
}
|
|
4080
4080
|
|
|
4081
4081
|
/**
|
|
@@ -4207,7 +4207,7 @@ const CwImageArea = React.forwardRef((props, ref) => {
|
|
|
4207
4207
|
});
|
|
4208
4208
|
CwImageArea.displayName = "CwImageArea";
|
|
4209
4209
|
|
|
4210
|
-
var styles$
|
|
4210
|
+
var styles$l = {"container":"cw-weekday-selector-module__container__pwcvx","days":"cw-weekday-selector-module__days__Dd3RH"};
|
|
4211
4211
|
|
|
4212
4212
|
/* 2024-01-01 was a Monday. Formatted in UTC so a negative browser offset cannot shift a day. */
|
|
4213
4213
|
const MONDAY_UTC = Date.UTC(2024, 0, 1);
|
|
@@ -4405,16 +4405,16 @@ const CwWeekdaySelector = ({ value = "mtwrfsu", onChange, disabled = false, labe
|
|
|
4405
4405
|
// Points at the label element rather than copying its text, which also covers a labelProps that
|
|
4406
4406
|
// carries children instead of `text`.
|
|
4407
4407
|
const labelId = labelProps?.id ?? `${feedbackId}-label`;
|
|
4408
|
-
return (jsxRuntime.jsxs("div", { ...dataAttributes, className: `cw-weekday-selector ${styles$
|
|
4408
|
+
return (jsxRuntime.jsxs("div", { ...dataAttributes, className: `cw-weekday-selector ${styles$l.container}${className ? ` ${className}` : ""}`, "data-direction": labelProps ? layout?.direction ?? "row" : "column", "data-align": layout?.align, "data-gap": layout?.gap, children: [labelProps && jsxRuntime.jsx(CwLabel, { id: labelId, ...labelProps }), jsxRuntime.jsx("div", { role: "group", className: styles$l.days, "aria-labelledby": labelProps ? labelId : undefined, "aria-describedby": describedBy, children: Weekdays.getFullWeek().map(day => (jsxRuntime.jsx("input", { type: "checkbox", "data-day": day, "data-letter": dayNames[day].letter, "aria-label": dayNames[day].full, title: dayNames[day].full, checked: selectedDays.includes(day), onChange: (e) => handleChange(day, e.target.checked), disabled: disabled }, day))) }), feedbackMessages.map((feedbackItem, index) => (jsxRuntime.jsx("p", { id: feedbackIds[index], className: "cw-input-feedback", "data-color": feedbackItem.type, "aria-live": "polite", children: feedbackItem.message }, index)))] }));
|
|
4409
4409
|
};
|
|
4410
4410
|
|
|
4411
|
-
var styles$
|
|
4411
|
+
var styles$k = {"check-container":"cw-check-module__check-container__4nscZ","group":"cw-check-module__group__6JCQo","options":"cw-check-module__options__-g9LF"};
|
|
4412
4412
|
|
|
4413
4413
|
function CwCheck(props) {
|
|
4414
4414
|
const { labelProps, labelText, direction, alignment, className, ...inputProps } = props;
|
|
4415
4415
|
const resolvedDirection = direction ?? alignment ?? "row";
|
|
4416
4416
|
const displayText = labelProps?.text ?? labelText;
|
|
4417
|
-
return (jsxRuntime.jsx("div", { className: `cw-check ${styles$
|
|
4417
|
+
return (jsxRuntime.jsx("div", { className: `cw-check ${styles$k["check-container"]}${className ? ` ${className}` : ''}`, children: jsxRuntime.jsxs("label", { "data-direction": resolvedDirection, children: [jsxRuntime.jsx("input", { type: "checkbox", ...inputProps }), displayText && (jsxRuntime.jsx("span", { className: "cw-icon-text", children: displayText }))] }) }));
|
|
4418
4418
|
}
|
|
4419
4419
|
|
|
4420
4420
|
function CwCheckbox(CwCheckboxProps) {
|
|
@@ -4468,14 +4468,14 @@ function CwCheckboxGroup({ options, value = [], onChange, labelProps, groupLayou
|
|
|
4468
4468
|
?? (optionsAlignment === "column" ? "row" : "column");
|
|
4469
4469
|
// Each checkbox's box-to-text layout is the opposite of the options flow, mirroring CwRadioGroup.
|
|
4470
4470
|
const checkDirection = itemDirection === "row" ? "column" : "row";
|
|
4471
|
-
return (jsxRuntime.jsxs("div", { className: `cw-checkbox-group ${styles$
|
|
4471
|
+
return (jsxRuntime.jsxs("div", { className: `cw-checkbox-group ${styles$k.group}${className ? ` ${className}` : ""}`, "data-testid": dataTestId, "data-direction": labelProps ? groupDirection : "column", "data-align": groupLayout?.align, "data-gap": groupLayout?.gap, children: [labelProps && (jsxRuntime.jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxRuntime.jsx("div", { className: styles$k.options, role: "group", "aria-label": labelProps?.text, "aria-describedby": describedBy, "aria-required": required || undefined, "data-direction": itemDirection, "data-gap": itemLayout?.gap, children: options.map((option) => (jsxRuntime.jsx(CwCheck, { labelProps: { text: option.label }, checked: value.includes(option.value), disabled: disabled || option.disabled, direction: checkDirection, onChange: (e) => handleChange(option.value, e.currentTarget.checked) }, option.value))) }), feedbackMessages.map((feedbackItem, index) => (jsxRuntime.jsx("p", { id: feedbackIds[index], className: "cw-input-feedback", "data-color": feedbackItem.type, "aria-live": "polite", children: feedbackItem.message }, index)))] }));
|
|
4472
4472
|
}
|
|
4473
4473
|
|
|
4474
|
-
var styles$
|
|
4474
|
+
var styles$j = {"radio":"cw-radio-module__radio__5sh7a","group":"cw-radio-module__group__br-HM","options":"cw-radio-module__options__LSw7M"};
|
|
4475
4475
|
|
|
4476
4476
|
/** Single native radio input. Building block for CwRadioGroup. */
|
|
4477
4477
|
function CwRadio({ label, direction = "row", className, ...inputProps }) {
|
|
4478
|
-
return (jsxRuntime.jsxs("label", { className: `cw-radio ${styles$
|
|
4478
|
+
return (jsxRuntime.jsxs("label", { className: `cw-radio ${styles$j.radio}${className ? ` ${className}` : ""}`, "data-direction": direction, children: [jsxRuntime.jsx("input", { type: "radio", ...inputProps }), label && jsxRuntime.jsx("span", { children: label })] }));
|
|
4479
4479
|
}
|
|
4480
4480
|
|
|
4481
4481
|
/**
|
|
@@ -4502,7 +4502,7 @@ function CwRadioGroup({ options, value, onChange, labelProps, groupLayout, itemL
|
|
|
4502
4502
|
// (itemLayout column) reads best with the circle beside the text; a side-by-side row reads
|
|
4503
4503
|
// best with the circle above the text.
|
|
4504
4504
|
const radioDirection = itemLayout?.direction === "row" ? "column" : "row";
|
|
4505
|
-
return (jsxRuntime.jsxs("div", { className: `cw-radio-group ${styles$
|
|
4505
|
+
return (jsxRuntime.jsxs("div", { className: `cw-radio-group ${styles$j.group}${className ? ` ${className}` : ""}`, "data-testid": dataTestId, "data-direction": labelProps ? (groupLayout?.direction ?? "column") : "column", "data-align": groupLayout?.align, "data-gap": groupLayout?.gap, children: [labelProps && (jsxRuntime.jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxRuntime.jsx("div", { className: styles$j.options, role: "radiogroup", "aria-describedby": describedBy, "aria-labelledby": ariaLabelledby, "aria-label": ariaLabelledby ? undefined : (ariaLabel ?? labelProps?.text), "aria-required": required || undefined, "data-direction": itemLayout?.direction ?? "column", "data-gap": itemLayout?.gap, children: options.map((option) => (jsxRuntime.jsx(CwRadio, { name: groupName, label: option.label, direction: radioDirection, checked: value === option.value, required: required, disabled: disabled || option.disabled, onChange: () => onChange(option.value) }, option.value))) }), feedbackMessages.map((feedbackItem, index) => (jsxRuntime.jsx("p", { id: feedbackIds[index], className: "cw-input-feedback", "data-color": feedbackItem.type, "aria-live": "polite", children: feedbackItem.message }, index)))] }));
|
|
4506
4506
|
}
|
|
4507
4507
|
|
|
4508
4508
|
function CwToggle(props) {
|
|
@@ -4643,7 +4643,7 @@ function useDropdownPortal({ anchorRef, isOpen, onClose, positionStrategy = defa
|
|
|
4643
4643
|
return { panelRef, panelStyle: style, renderPanel };
|
|
4644
4644
|
}
|
|
4645
4645
|
|
|
4646
|
-
var styles$
|
|
4646
|
+
var styles$i = {"dropdown":"cw-dropdown-module__dropdown__EFZ9M cw-choice-dropdown-module__dropdown__P0b4j","option":"cw-dropdown-module__option__TuMdI cw-choice-dropdown-module__option__uR-ro","optionFocused":"cw-dropdown-module__optionFocused__7LGaC cw-choice-dropdown-module__optionFocused__BYMxj","optionSelected":"cw-dropdown-module__optionSelected__OiXIc cw-choice-dropdown-module__optionSelected__Lt8Xg","optionDisabled":"cw-dropdown-module__optionDisabled__lYM6w cw-choice-dropdown-module__optionDisabled__n3scM","emptyState":"cw-dropdown-module__emptyState__gp8KT cw-choice-dropdown-module__emptyState__HO0Y7","searchBox":"cw-dropdown-module__searchBox__Bx0JE cw-choice-dropdown-module__searchBox__0gox7","searchInput":"cw-dropdown-module__searchInput__JrxCK cw-choice-dropdown-module__searchInput__G5oRs","value":"cw-dropdown-module__value__i-7IN","colorCircle":"cw-dropdown-module__colorCircle__5kgiT cw-choice-dropdown-module__colorCircle__x7mI9"};
|
|
4647
4647
|
|
|
4648
4648
|
/**
|
|
4649
4649
|
* Custom dropdown component with colour-coded options, optional search filter, and full
|
|
@@ -4757,12 +4757,12 @@ function CwDropdown({ options, value, onChange, labelProps, alignProps, placehol
|
|
|
4757
4757
|
}
|
|
4758
4758
|
}, [isOpen, highlightedIndex, filteredOptions, handleOptionSelect]);
|
|
4759
4759
|
const flexDirection = alignProps?.flexDirection ?? "row";
|
|
4760
|
-
return (jsxRuntime.jsx("div", { ref: containerRef, className: `cw-dropdown${className ? ` ${className}` : ""}`, "data-direction": flexDirection, style: style, ...otherProps, children: jsxRuntime.jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && jsxRuntime.jsx(CwLabel, { id: labelId, ...labelProps }), jsxRuntime.jsxs("div", { className: styles$
|
|
4761
|
-
styles$
|
|
4762
|
-
value === option.value ? styles$
|
|
4763
|
-
highlightedIndex === index ? styles$
|
|
4764
|
-
option.disabled ? styles$
|
|
4765
|
-
].filter(Boolean).join(" "), onClick: () => !option.disabled && handleOptionSelect(option.value), onMouseEnter: () => setHighlightedIndex(index), role: "option", "aria-selected": value === option.value, "aria-disabled": option.disabled, children: [option.color && (jsxRuntime.jsx("span", { className: styles$
|
|
4760
|
+
return (jsxRuntime.jsx("div", { ref: containerRef, className: `cw-dropdown${className ? ` ${className}` : ""}`, "data-direction": flexDirection, style: style, ...otherProps, children: jsxRuntime.jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && jsxRuntime.jsx(CwLabel, { id: labelId, ...labelProps }), jsxRuntime.jsxs("div", { className: styles$i.value, children: [jsxRuntime.jsxs("div", { className: "cw-dropdown-wrapper", ref: wrapperRef, children: [jsxRuntime.jsxs("button", { type: "button", className: "cw-dropdown-trigger", onClick: handleTriggerClick, onKeyDown: handleKeyDown, disabled: disabled, "aria-haspopup": "listbox", "aria-expanded": isOpen, "aria-labelledby": labelProps ? `${labelId} ${valueId}` : undefined, "aria-label": labelProps ? undefined : (selectedOption?.label || placeholder), "aria-describedby": describedBy, children: [jsxRuntime.jsx("div", { className: "cw-dropdown-trigger-content", id: valueId, children: selectedOption ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [selectedOption.color && (jsxRuntime.jsx("span", { className: styles$i.colorCircle, style: { backgroundColor: selectedOption.color } })), jsxRuntime.jsx("span", { children: selectedOption.label })] })) : (jsxRuntime.jsx("span", { className: "cw-dropdown-placeholder", children: placeholder })) }), jsxRuntime.jsx(CwIcon, { iconId: "chevron-down", size: "small" })] }), renderPanel(jsxRuntime.jsxs("div", { ref: panelRef, className: styles$i.dropdown, style: panelStyle, role: "listbox", onKeyDown: handleKeyDown, children: [allowSearch && (jsxRuntime.jsx("div", { className: styles$i.searchBox, children: jsxRuntime.jsx("input", { ref: inputRef, type: "text", placeholder: "Search...", value: searchText, onChange: handleSearchChange, onKeyDown: handleKeyDown, className: styles$i.searchInput, autoComplete: "off" }) })), jsxRuntime.jsx("ul", { children: filteredOptions.length > 0 ? (filteredOptions.map((option, index) => (jsxRuntime.jsxs("li", { ref: highlightedIndex === index ? highlightedItemRef : null, className: [
|
|
4761
|
+
styles$i.option,
|
|
4762
|
+
value === option.value ? styles$i.optionSelected : "",
|
|
4763
|
+
highlightedIndex === index ? styles$i.optionFocused : "",
|
|
4764
|
+
option.disabled ? styles$i.optionDisabled : "",
|
|
4765
|
+
].filter(Boolean).join(" "), onClick: () => !option.disabled && handleOptionSelect(option.value), onMouseEnter: () => setHighlightedIndex(index), role: "option", "aria-selected": value === option.value, "aria-disabled": option.disabled, children: [option.color && (jsxRuntime.jsx("span", { className: styles$i.colorCircle, style: { backgroundColor: option.color } })), jsxRuntime.jsx("span", { children: option.label })] }, option.value)))) : (jsxRuntime.jsx("li", { className: styles$i.emptyState, children: "No options available" })) })] }))] }), feedbackMessages.map((item, index) => (jsxRuntime.jsx("p", { id: feedbackIds[index], className: "cw-input-feedback", "data-color": item.type, "aria-live": "polite", children: item.message }, index)))] })] }) }));
|
|
4766
4766
|
}
|
|
4767
4767
|
|
|
4768
4768
|
/**
|
|
@@ -4779,7 +4779,7 @@ function CwDropdownFilter(props) {
|
|
|
4779
4779
|
return (jsxRuntime.jsxs("div", { id: props.IdContent, className: containerClassName, children: [props.children, isVisible && (jsxRuntime.jsx("nav", { className: "cw-dropdown-menu", style: inlineStyles, onMouseLeave: props.onMouseLeave, id: props.idDropDownFilter, role: "menu", children: jsxRuntime.jsx("ul", { role: "none", children: props.DataSourceDropDownItem?.map((item) => (jsxRuntime.jsx("li", { id: item.IdDropDown_filter, role: "menuitem", children: item.dropDownFilter_desc }, item.IdDropDown_filter))) }) }))] }));
|
|
4780
4780
|
}
|
|
4781
4781
|
|
|
4782
|
-
var styles$
|
|
4782
|
+
var styles$h = {"container":"cw-popover-button-module__container__YSWQU","panel":"cw-popover-button-module__panel__C-BTn"};
|
|
4783
4783
|
|
|
4784
4784
|
/**
|
|
4785
4785
|
* Button that toggles a floating panel with arbitrary children.
|
|
@@ -4858,7 +4858,7 @@ function CwPopoverButton({ children, panelWidth = "auto", placement = "bottom-st
|
|
|
4858
4858
|
}
|
|
4859
4859
|
panel.showPopover();
|
|
4860
4860
|
}, [placement]);
|
|
4861
|
-
return (jsxRuntime.jsxs("div", { ref: containerRef, className: styles$
|
|
4861
|
+
return (jsxRuntime.jsxs("div", { ref: containerRef, className: styles$h.container, children: [jsxRuntime.jsx(CwButton, { ...buttonProps, onClick: handleClick }), jsxRuntime.jsx("div", { ref: panelCallbackRef, className: `${styles$h.panel}${panelClassName ? ` ${panelClassName}` : ""}`, style: { width: panelWidth }, children: children })] }));
|
|
4862
4862
|
}
|
|
4863
4863
|
|
|
4864
4864
|
function itemsToMultiFilterTags(items, nameKey, valueKey, category, primaryColor, onPrimaryColor) {
|
|
@@ -4876,13 +4876,13 @@ function itemsToMultiFilterTags(items, nameKey, valueKey, category, primaryColor
|
|
|
4876
4876
|
return result;
|
|
4877
4877
|
}
|
|
4878
4878
|
|
|
4879
|
-
var styles$
|
|
4879
|
+
var styles$g = {"cw-multifilter-tag":"cw-multi-filter-tag-module__cw-multifilter-tag__Epda-"};
|
|
4880
4880
|
|
|
4881
4881
|
const CwMultiFilterTag = props => {
|
|
4882
4882
|
const { ID, Name, Value, Category, Removable, PrimaryColor, Selectable, Selected } = props;
|
|
4883
4883
|
const backgroundColor = `rgb(${PrimaryColor.r},${PrimaryColor.g},${PrimaryColor.b})`;
|
|
4884
4884
|
const isOutlineMode = Selected && !Removable;
|
|
4885
|
-
return (jsxRuntime.jsx("li", { className: styles$
|
|
4885
|
+
return (jsxRuntime.jsx("li", { className: styles$g["cw-multifilter-tag"], id: ID, "data-value": Value, "data-category": Category, "data-selected": !!Selected, title: props.tooltip ?? Name, onClick: () => {
|
|
4886
4886
|
if (Selectable)
|
|
4887
4887
|
props.OnSelect?.(props);
|
|
4888
4888
|
}, children: jsxRuntime.jsx(CwChip, { label: Name, colorScheme: "custom", customColor: backgroundColor, variant: isOutlineMode ? "outline" : "soft", closable: Removable, onClose: () => props.OnRemove?.(props),
|
|
@@ -4892,7 +4892,7 @@ const CwMultiFilterTag = props => {
|
|
|
4892
4892
|
} : undefined }) }));
|
|
4893
4893
|
};
|
|
4894
4894
|
|
|
4895
|
-
var styles$
|
|
4895
|
+
var styles$f = {"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 cw-choice-input-box-module__inputBox__gHY9d"};
|
|
4896
4896
|
|
|
4897
4897
|
/**
|
|
4898
4898
|
* A multiple filter selector, a MULTI-SELECT even. Allows users to select and filter items based on tags.
|
|
@@ -5133,9 +5133,9 @@ const CwMultiFilter = ({ allTags, id, onChangeSelectedTags, selectedTags, placeh
|
|
|
5133
5133
|
inputRef.current?.focus();
|
|
5134
5134
|
}
|
|
5135
5135
|
};
|
|
5136
|
-
return (jsxRuntime.jsxs("form", { ref: componentRef, id: id, "data-testid": testId, className: `${styles$
|
|
5136
|
+
return (jsxRuntime.jsxs("form", { ref: componentRef, id: id, "data-testid": testId, className: `${styles$f["cw-multi-filter"]}${className ? ` ${className}` : ""}`, style: style, onSubmit: (e) => {
|
|
5137
5137
|
e.preventDefault();
|
|
5138
|
-
}, children: [jsxRuntime.jsxs("div", { onClick: handleDivClick, ref: wrapperRef, className: styles$
|
|
5138
|
+
}, children: [jsxRuntime.jsxs("div", { onClick: handleDivClick, ref: wrapperRef, className: styles$f["cw-multi-filter-search"], style: isPanelOpen ? { outline: "1px solid var(--cw-outline-variant)" } : {}, 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.currentTarget.value), autoComplete: "off", placeholder: selectedTags.size === 0 ? placeholder : "", onKeyDown: e => {
|
|
5139
5139
|
switch (e.key) {
|
|
5140
5140
|
case "Enter":
|
|
5141
5141
|
case "Tab": {
|
|
@@ -5162,7 +5162,7 @@ const CwMultiFilter = ({ allTags, id, onChangeSelectedTags, selectedTags, placeh
|
|
|
5162
5162
|
break;
|
|
5163
5163
|
}
|
|
5164
5164
|
}
|
|
5165
|
-
} })] }), selectedTags.size > 0 && (jsxRuntime.jsx(CwButton, { type: "button", variant: "icon", color: "neutral", icon: "close", onMouseDown: e => e.preventDefault(), onClick: (e) => { e.stopPropagation(); onChangeSelectedTags(new Set()); setIsPanelOpen(false); }, tabIndex: -1, "aria-label": "Clear all" }))] }), renderPanel(jsxRuntime.jsxs("section", { ref: panelRef, className: styles$
|
|
5165
|
+
} })] }), selectedTags.size > 0 && (jsxRuntime.jsx(CwButton, { type: "button", variant: "icon", color: "neutral", icon: "close", onMouseDown: e => e.preventDefault(), onClick: (e) => { e.stopPropagation(); onChangeSelectedTags(new Set()); setIsPanelOpen(false); }, tabIndex: -1, "aria-label": "Clear all" }))] }), renderPanel(jsxRuntime.jsxs("section", { ref: panelRef, className: styles$f["cw-multi-filter-catalog-container"], style: panelStyle, children: [jsxRuntime.jsxs("nav", { children: [jsxRuntime.jsx("button", { className: selectedCategory === "All" ? styles$f["category-selected"] : undefined, onClick: () => handleClickCategory("All"), children: allCategoriesLabel }), Array.from(categoriesMappedToTags().keys()).map(category => (jsxRuntime.jsx("button", { className: selectedCategory === category ? styles$f["category-selected"] : undefined, style: {
|
|
5166
5166
|
backgroundColor: getColor(category).primary,
|
|
5167
5167
|
color: getColor(category).onPrimary,
|
|
5168
5168
|
}, onClick: () => handleClickCategory(category), children: category }, category)))] }), jsxRuntime.jsx("ul", { children: displayTags
|
|
@@ -5188,7 +5188,7 @@ const CwMultiFilter = ({ allTags, id, onChangeSelectedTags, selectedTags, placeh
|
|
|
5188
5188
|
} }, props.ID))) })] }))] }));
|
|
5189
5189
|
};
|
|
5190
5190
|
|
|
5191
|
-
var styles$
|
|
5191
|
+
var styles$e = {"container":"cw-tag-selector-module__container__-do8i","value":"cw-tag-selector-module__value__TDAy3","inputBox":"cw-tag-selector-module__inputBox__3NGz9 cw-choice-input-box-module__inputBox__gHY9d","disabled":"cw-tag-selector-module__disabled__vbud9 cw-choice-input-box-module__disabled__TFAnC","dropdown":"cw-tag-selector-module__dropdown__X6Dob cw-choice-dropdown-module__dropdown__P0b4j","option":"cw-tag-selector-module__option__2UQ7g cw-choice-dropdown-module__option__uR-ro","optionFocused":"cw-tag-selector-module__optionFocused__8-lv8 cw-choice-dropdown-module__optionFocused__BYMxj","optionDot":"cw-tag-selector-module__optionDot__CdFlG cw-choice-dropdown-module__colorCircle__x7mI9","selectAll":"cw-tag-selector-module__selectAll__HyvMA"};
|
|
5192
5192
|
|
|
5193
5193
|
/**
|
|
5194
5194
|
* Multi-select input with chip tags and searchable dropdown.
|
|
@@ -5339,11 +5339,11 @@ function CwTagSelector({ id, options, value, onChange, labelProps, iconProps, al
|
|
|
5339
5339
|
const feedbackId = React.useId();
|
|
5340
5340
|
const feedbackIds = feedbackMessages.map((_, index) => `${feedbackId}-${index}`);
|
|
5341
5341
|
const describedBy = feedbackIds.join(" ") || undefined;
|
|
5342
|
-
return (jsxRuntime.jsxs("div", { ref: containerRef, className: `${styles$
|
|
5343
|
-
inputRef.current?.focus(); }, children: [selectedOptions.map(opt => (jsxRuntime.jsx(CwChip, { label: opt.label, colorScheme: opt.color ? "custom" : "info", customColor: opt.color, closable: !disabled, onClose: () => handleRemove(opt.value) }, opt.value))), jsxRuntime.jsx("input", { ref: inputRef, type: "text", value: search, onChange: handleInputChange, onFocus: handleInputFocus, onKeyDown: handleKeyDown, placeholder: selectedOptions.length === 0 ? placeholder : "", disabled: disabled, autoComplete: "off", "aria-describedby": describedBy }), value.length > 0 && !disabled && (jsxRuntime.jsx(CwButton, { type: "button", variant: "icon", color: "neutral", icon: "close", onMouseDown: (e) => e.preventDefault(), onClick: (e) => { e.stopPropagation(); onChange([]); setSearch(""); setOpen(false); }, tabIndex: -1, "aria-label": "Clear all" }))] }), feedbackMessages.map((fb, i) => (jsxRuntime.jsx("p", { id: feedbackIds[i], className: "cw-input-feedback", "data-color": fb.type, "aria-live": "polite", children: fb.message }, i)))] })] }), renderPanel((showSelectAll || filtered.length > 0) ? (jsxRuntime.jsxs("div", { ref: panelRef, className: styles$
|
|
5342
|
+
return (jsxRuntime.jsxs("div", { ref: containerRef, className: `${styles$e.container}${className ? ` ${className}` : ""}`, style: style, id: id, "data-direction": direction, children: [jsxRuntime.jsxs(CwAlign, { ...alignProps, children: [labelProps && (jsxRuntime.jsxs(CwLabel, { ...labelProps, children: [iconProps && jsxRuntime.jsx(CwIcon, { ...iconProps }), labelProps.text] })), jsxRuntime.jsxs("div", { className: styles$e.value, children: [jsxRuntime.jsxs("div", { className: `${styles$e.inputBox}${disabled ? ` ${styles$e.disabled}` : ""}`, ref: inputBoxRef, onClick: () => { if (!disabled)
|
|
5343
|
+
inputRef.current?.focus(); }, children: [selectedOptions.map(opt => (jsxRuntime.jsx(CwChip, { label: opt.label, colorScheme: opt.color ? "custom" : "info", customColor: opt.color, closable: !disabled, onClose: () => handleRemove(opt.value) }, opt.value))), jsxRuntime.jsx("input", { ref: inputRef, type: "text", value: search, onChange: handleInputChange, onFocus: handleInputFocus, onKeyDown: handleKeyDown, placeholder: selectedOptions.length === 0 ? placeholder : "", disabled: disabled, autoComplete: "off", "aria-describedby": describedBy }), value.length > 0 && !disabled && (jsxRuntime.jsx(CwButton, { type: "button", variant: "icon", color: "neutral", icon: "close", onMouseDown: (e) => e.preventDefault(), onClick: (e) => { e.stopPropagation(); onChange([]); setSearch(""); setOpen(false); }, tabIndex: -1, "aria-label": "Clear all" }))] }), feedbackMessages.map((fb, i) => (jsxRuntime.jsx("p", { id: feedbackIds[i], className: "cw-input-feedback", "data-color": fb.type, "aria-live": "polite", children: fb.message }, i)))] })] }), renderPanel((showSelectAll || filtered.length > 0) ? (jsxRuntime.jsxs("div", { ref: panelRef, className: styles$e.dropdown, style: panelStyle, children: [showSelectAll && !search && (jsxRuntime.jsxs("div", { className: styles$e.selectAll, onMouseDown: (e) => e.preventDefault(), onClick: handleToggleAll, children: [jsxRuntime.jsx("input", { type: "checkbox", checked: allSelected, readOnly: true, tabIndex: -1 }), allSelected ? "Deselect all" : "Select all"] })), jsxRuntime.jsx("ul", { ref: listRef, children: filtered.map((opt, idx) => (jsxRuntime.jsxs("li", { className: `${styles$e.option}${idx === focusedIndex ? ` ${styles$e.optionFocused}` : ""}`, onMouseDown: (e) => e.preventDefault(), onClick: () => handleSelect(opt.value), onMouseEnter: () => setFocusedIndex(idx), title: opt.tooltip, children: [opt.color && jsxRuntime.jsx("span", { className: styles$e.optionDot, style: { backgroundColor: opt.color } }), opt.label] }, opt.value))) })] })) : null)] }));
|
|
5344
5344
|
}
|
|
5345
5345
|
|
|
5346
|
-
var styles$
|
|
5346
|
+
var styles$d = {"tree-container":"cw-tree-view-module__tree-container__NN-HJ","tree-wrapper":"cw-tree-view-module__tree-wrapper__keBZI","tree-item":"cw-tree-view-module__tree-item__prE9N","tree-node":"cw-tree-view-module__tree-node__wfro9","selected":"cw-tree-view-module__selected__XJQ0w","non-selectable":"cw-tree-view-module__non-selectable__YuPSx","tree-children":"cw-tree-view-module__tree-children__ji8CZ","empty-state":"cw-tree-view-module__empty-state__yvRjo","tree-label":"cw-tree-view-module__tree-label__ss3Nf","tree-spacer":"cw-tree-view-module__tree-spacer__E9Ud2"};
|
|
5347
5347
|
|
|
5348
5348
|
function CwTreeView({ data, onSelect, allowParentSelection = false, selectedId: initialSelectedId = null, ...dataAttributes }) {
|
|
5349
5349
|
const [expanded, setExpanded] = React.useState(new Set());
|
|
@@ -5410,10 +5410,10 @@ function CwTreeView({ data, onSelect, allowParentSelection = false, selectedId:
|
|
|
5410
5410
|
const isLeaf = !hasChildren;
|
|
5411
5411
|
const isSelected = selectedId?.toString().toLowerCase() === node.id.toString().toLowerCase();
|
|
5412
5412
|
const selectable = allowParentSelection || isLeaf;
|
|
5413
|
-
return (jsxRuntime.jsxs("div", { className: styles$
|
|
5414
|
-
styles$
|
|
5415
|
-
isSelected && styles$
|
|
5416
|
-
!selectable && styles$
|
|
5413
|
+
return (jsxRuntime.jsxs("div", { className: styles$d["tree-item"], children: [jsxRuntime.jsxs("div", { className: [
|
|
5414
|
+
styles$d["tree-node"],
|
|
5415
|
+
isSelected && styles$d["selected"],
|
|
5416
|
+
!selectable && styles$d["non-selectable"]
|
|
5417
5417
|
].filter(Boolean).join(' '), style: { paddingLeft: `${depth * 24 + 8}px` }, onClick: () => {
|
|
5418
5418
|
if (selectable) {
|
|
5419
5419
|
setSelectedId(node.id);
|
|
@@ -5425,7 +5425,7 @@ function CwTreeView({ data, onSelect, allowParentSelection = false, selectedId:
|
|
|
5425
5425
|
}, children: [hasChildren && (jsxRuntime.jsx(CwButton, { variant: "icon", color: "neutral", icon: isExpanded ? "chevron-down" : "chevron-right", "aria-expanded": isExpanded, "aria-label": `${isExpanded ? "Collapse" : "Expand"} ${node.label}`, onClick: (event) => {
|
|
5426
5426
|
event.stopPropagation();
|
|
5427
5427
|
toggleExpand(node.id);
|
|
5428
|
-
} })), jsxRuntime.jsx("div", { className: `${styles$
|
|
5428
|
+
} })), jsxRuntime.jsx("div", { className: `${styles$d["tree-label"]} ${!hasChildren ? styles$d["tree-spacer"] : ''}`, children: node.label })] }), hasChildren && isExpanded && (jsxRuntime.jsx("div", { className: styles$d["tree-children"], children: node.children.map((child) => renderNode(child, depth + 1)) }))] }, node.id));
|
|
5429
5429
|
};
|
|
5430
5430
|
// Function to search for nodes that match the search term
|
|
5431
5431
|
const searchInTree = (nodes, searchTerm) => {
|
|
@@ -5449,7 +5449,7 @@ function CwTreeView({ data, onSelect, allowParentSelection = false, selectedId:
|
|
|
5449
5449
|
return results;
|
|
5450
5450
|
};
|
|
5451
5451
|
const filteredData = searchInTree(data, search);
|
|
5452
|
-
return (jsxRuntime.jsxs("div", { ...dataAttributes, className: `cw-tree-view ${styles$
|
|
5452
|
+
return (jsxRuntime.jsxs("div", { ...dataAttributes, className: `cw-tree-view ${styles$d["tree-container"]}`, children: [jsxRuntime.jsxs("header", { children: [jsxRuntime.jsx("input", { type: "text", placeholder: "Search in tree...", value: search, onChange: (e) => setSearch(e.target.value) }), search && (jsxRuntime.jsx(CwButton, { onClick: () => setSearch(""), type: "button", icon: "close", variant: "icon", color: "neutral" }))] }), jsxRuntime.jsx("div", { className: styles$d["tree-wrapper"], children: filteredData.length > 0 ? (filteredData.map((node) => renderNode(node))) : (jsxRuntime.jsxs("div", { className: styles$d["empty-state"], children: [jsxRuntime.jsx(CwIcon, { iconId: "comment" }), search ? "No results found" : "There are no items"] })) })] }));
|
|
5453
5453
|
}
|
|
5454
5454
|
|
|
5455
5455
|
const EXACT_PHRASE = /"([^"]+)"/g;
|
|
@@ -5517,7 +5517,7 @@ function sortNavItems(items, alphabetically) {
|
|
|
5517
5517
|
return [...items].sort((first, second) => first.label.localeCompare(second.label));
|
|
5518
5518
|
}
|
|
5519
5519
|
|
|
5520
|
-
var styles$
|
|
5520
|
+
var styles$c = {"menu":"cw-nav-menu-module__menu__LmeQg","expandIndicator":"cw-nav-menu-module__expandIndicator__TBSdQ","favorites":"cw-nav-menu-module__favorites__Esaqd","favoritesContent":"cw-nav-menu-module__favoritesContent__l5DGG","favoriteStar":"cw-nav-menu-module__favoriteStar__J-jzg","emptyMessage":"cw-nav-menu-module__emptyMessage__-CQNd","skeleton":"cw-nav-menu-module__skeleton__INQqF"};
|
|
5521
5521
|
|
|
5522
5522
|
const SKELETON_ROW_COUNT = 5;
|
|
5523
5523
|
const defaultRenderLink = (item, children) => (jsxRuntime.jsx("a", { href: item.href, children: children }));
|
|
@@ -5529,13 +5529,13 @@ function CwNavMenu({ items, loading = false, isActive, renderLink = defaultRende
|
|
|
5529
5529
|
const renderItem = (item, level) => {
|
|
5530
5530
|
const children = item.children;
|
|
5531
5531
|
if (children && children.length > 0) {
|
|
5532
|
-
return (jsxRuntime.jsx("li", { children: jsxRuntime.jsxs("details", { "data-level": level, open: hasActiveDescendant(item) || undefined, children: [jsxRuntime.jsxs("summary", { children: [item.icon && jsxRuntime.jsx(CwIcon, { iconId: item.icon, size: level >= 2 ? "small" : "medium" }), item.label, (showExpandIndicator || level >= 2) && (jsxRuntime.jsx(CwIcon, { iconId: "chevron-down", size: "small", className: styles$
|
|
5532
|
+
return (jsxRuntime.jsx("li", { children: jsxRuntime.jsxs("details", { "data-level": level, open: hasActiveDescendant(item) || undefined, children: [jsxRuntime.jsxs("summary", { children: [item.icon && jsxRuntime.jsx(CwIcon, { iconId: item.icon, size: level >= 2 ? "small" : "medium" }), item.label, (showExpandIndicator || level >= 2) && (jsxRuntime.jsx(CwIcon, { iconId: "chevron-down", size: "small", className: styles$c.expandIndicator }))] }), jsxRuntime.jsx("ul", { children: children.map(child => renderItem(child, level + 1)) })] }) }, item.id));
|
|
5533
5533
|
}
|
|
5534
|
-
return (jsxRuntime.jsx("li", { "data-active": isActive?.(item) ? "true" : undefined, children: renderLink(item, jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [item.icon && jsxRuntime.jsx(CwIcon, { iconId: item.icon, size: "medium" }), item.label, isFavorite?.(item) && jsxRuntime.jsx(CwIcon, { iconId: "star", size: "small", className: styles$
|
|
5534
|
+
return (jsxRuntime.jsx("li", { "data-active": isActive?.(item) ? "true" : undefined, children: renderLink(item, jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [item.icon && jsxRuntime.jsx(CwIcon, { iconId: item.icon, size: "medium" }), item.label, isFavorite?.(item) && jsxRuntime.jsx(CwIcon, { iconId: "star", size: "small", className: styles$c.favoriteStar })] })) }, item.id));
|
|
5535
5535
|
};
|
|
5536
|
-
return (jsxRuntime.jsxs("nav", { ...navProps, className: [styles$
|
|
5537
|
-
? Array.from({ length: SKELETON_ROW_COUNT }, (_unused, index) => (jsxRuntime.jsx("li", { className: styles$
|
|
5538
|
-
: visibleItems.map(item => renderItem(item, 1)) }), showEmptyMessage && jsxRuntime.jsx("p", { className: styles$
|
|
5536
|
+
return (jsxRuntime.jsxs("nav", { ...navProps, className: [styles$c.menu, className].filter(Boolean).join(" "), "aria-busy": loading || undefined, children: [favoritesSlot && (jsxRuntime.jsxs("details", { open: true, ...favoritesProps, className: [styles$c.favorites, favoritesProps?.className].filter(Boolean).join(" "), children: [jsxRuntime.jsxs("summary", { children: [jsxRuntime.jsx(CwIcon, { iconId: "star", size: "medium" }), favoritesLabel] }), jsxRuntime.jsx("div", { className: styles$c.favoritesContent, children: favoritesSlot })] })), beforeTreeSlot, jsxRuntime.jsx("ul", { children: loading
|
|
5537
|
+
? Array.from({ length: SKELETON_ROW_COUNT }, (_unused, index) => (jsxRuntime.jsx("li", { className: styles$c.skeleton, children: jsxRuntime.jsx(CwSkeleton, { height: "var(--cw-size-small)", radius: "var(--cw-border-radius-small)" }) }, index)))
|
|
5538
|
+
: visibleItems.map(item => renderItem(item, 1)) }), showEmptyMessage && jsxRuntime.jsx("p", { className: styles$c.emptyMessage, children: emptyMessage })] }));
|
|
5539
5539
|
}
|
|
5540
5540
|
|
|
5541
5541
|
var dropdownStyles = {"dropdown":"cw-choice-dropdown-module__dropdown__P0b4j","option":"cw-choice-dropdown-module__option__uR-ro","optionFocused":"cw-choice-dropdown-module__optionFocused__BYMxj"};
|
|
@@ -5701,7 +5701,7 @@ function CwSearchInput(optionsProps) {
|
|
|
5701
5701
|
return (jsxRuntime.jsxs("div", { className: `cw-search-input ${className || ""}`, style: style, ...otherProps, "data-direction": direction, children: [jsxRuntime.jsxs(CwAlign, { ...alignProps, children: [labelProps && (jsxRuntime.jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxRuntime.jsxs("div", { className: "cw-search-input-wrapper", ref: wrapperRef, children: [jsxRuntime.jsx("input", { ref: inputRef, id: id, type: "text", value: searchText, onChange: handleInputChange, onKeyDown: handleKeyDown, onFocus: handleInputFocus, onBlur: handleInputBlur, placeholder: placeholder, className: "cw-input-search", disabled: disabled, autoFocus: autoFocus, autoComplete: "off", role: "combobox", "aria-haspopup": "listbox", "aria-expanded": showDropdown && filteredOptions.length > 0, "aria-controls": listboxId, "aria-activedescendant": highlightedIndex >= 0 ? optionId(highlightedIndex) : undefined }), isLoading && (jsxRuntime.jsx("div", { className: "cw-search-input-loading", children: jsxRuntime.jsx(CwIcon, { iconId: "spinner" }) })), jsxRuntime.jsx("div", { className: "cw-search-input-icons", children: searchText && !disabled && !isLoading ? (jsxRuntime.jsx(CwButton, { type: "button", onClick: handleClearClick, "aria-label": "Clear search", variant: "icon", icon: "close", color: "neutral" })) : (iconProps ? jsxRuntime.jsx(CwIcon, { ...iconProps }) : jsxRuntime.jsx(CwIcon, { iconId: "search" })) })] })] }), renderPanel(jsxRuntime.jsx("div", { ref: panelRef, id: listboxId, className: dropdownStyles.dropdown, style: panelStyle, role: "listbox", children: jsxRuntime.jsx("ul", { children: filteredOptions.map((option, index) => (jsxRuntime.jsx("li", { id: optionId(index), className: `${dropdownStyles.option}${index === highlightedIndex ? ` ${dropdownStyles.optionFocused}` : ""}`, onClick: () => handleOptionSelect(option), onMouseDown: (e) => e.preventDefault(), onMouseEnter: () => setHighlightedIndex(index), role: "option", "aria-selected": index === highlightedIndex, children: renderOption ? renderOption(option) : option.description }, option.id))) }) }))] }));
|
|
5702
5702
|
}
|
|
5703
5703
|
|
|
5704
|
-
var styles$
|
|
5704
|
+
var styles$b = {"value":"cw-search-async-module__value__z53xT"};
|
|
5705
5705
|
|
|
5706
5706
|
/**
|
|
5707
5707
|
* Generic API-backed search input. Fetches matching options from a caller-supplied `onSearch`
|
|
@@ -5965,10 +5965,10 @@ const CwSearchAsync = ({ onSearch, onChange, value, onResolveInitial, onResolveS
|
|
|
5965
5965
|
return (jsxRuntime.jsxs("div", { ...dataAttributes, className: `cw-search-input ${className}`, style: {
|
|
5966
5966
|
...(width ? { width } : {}),
|
|
5967
5967
|
...(labelProps?.labelWidth ? { "--label-width": labelProps.labelWidth } : {}),
|
|
5968
|
-
}, "data-direction": direction, children: [jsxRuntime.jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsxRuntime.jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxRuntime.jsxs("div", { className: styles$
|
|
5968
|
+
}, "data-direction": direction, children: [jsxRuntime.jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsxRuntime.jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxRuntime.jsxs("div", { className: styles$b.value, children: [jsxRuntime.jsxs("div", { className: "cw-search-input-wrapper", ref: wrapperRef, children: [jsxRuntime.jsx("input", { ref: inputRef, type: "text", value: inputValue, onChange: handleInputChange, onKeyDown: handleKeyDown, onFocus: handleInputFocus, placeholder: isInitialLoading ? "Loading…" : placeholder, disabled: disabled, required: required, autoComplete: "off", role: "combobox", "aria-haspopup": "listbox", "aria-expanded": showDropdown, "aria-controls": listboxId, "aria-activedescendant": highlightedIndex >= 0 ? optionId(highlightedIndex) : undefined, "aria-describedby": describedBy, title: tooltipText }), (isLoading || isInitialLoading) && (jsxRuntime.jsx("div", { className: "cw-search-input-loading", children: jsxRuntime.jsx(CwIcon, { iconId: "spinner" }) })), jsxRuntime.jsx("div", { className: "cw-search-input-icons", children: inputValue && !disabled && !isInitialLoading ? (jsxRuntime.jsx(CwButton, { type: "button", onClick: handleClear, "aria-label": "Clear selection", variant: "icon", icon: "close", color: "neutral" })) : (icon ? jsxRuntime.jsx(CwIcon, { ...icon }) : jsxRuntime.jsx(CwIcon, { iconId: "search" })) })] }), feedbackMessages.map((feedbackItem, index) => (jsxRuntime.jsx("p", { id: feedbackIds[index], className: "cw-input-feedback", "data-color": feedbackItem.type, "aria-live": "polite", children: feedbackItem.message }, index)))] })] }), renderPanel(jsxRuntime.jsx("div", { ref: panelRef, id: listboxId, className: dropdownStyles.dropdown, style: panelStyle, role: "listbox", children: jsxRuntime.jsx("ul", { children: options.map((option, index) => (jsxRuntime.jsx("li", { id: optionId(index), className: `${dropdownStyles.option}${index === highlightedIndex ? ` ${dropdownStyles.optionFocused}` : ""}`, onClick: () => handleOptionSelect(option.id), onMouseDown: (e) => e.preventDefault(), onMouseEnter: () => setHighlightedIndex(index), role: "option", "aria-selected": index === highlightedIndex, children: option.subLabel ? (jsxRuntime.jsxs("span", { className: "cw-search-async-option-lines", children: [jsxRuntime.jsx("span", { children: option.label }), jsxRuntime.jsx("span", { className: "cw-search-async-option-sub", children: option.subLabel })] })) : (option.label) }, option.id))) }) }))] }));
|
|
5969
5969
|
};
|
|
5970
5970
|
|
|
5971
|
-
var styles$
|
|
5971
|
+
var styles$a = {"menu":"cw-context-menu-module__menu__tXmun","item":"cw-context-menu-module__item__-ko8L","arrow":"cw-context-menu-module__arrow__LHZmQ"};
|
|
5972
5972
|
|
|
5973
5973
|
const ARROW_SIZE = 8;
|
|
5974
5974
|
const SAFETY_MARGIN = 8;
|
|
@@ -6109,14 +6109,14 @@ const CwAnchoredMenu = ({ children, options, onSelect, placement = 'bottom', ope
|
|
|
6109
6109
|
document.removeEventListener("keydown", handleEscape);
|
|
6110
6110
|
};
|
|
6111
6111
|
}, [isOpen]);
|
|
6112
|
-
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { ref: triggerRef, onContextMenu: handleContextMenu, style: { display: "contents" }, children: children }), isOpen && normalizedOptions.length > 0 && reactDom.createPortal(jsxRuntime.jsxs("div", { ref: menuRef, className: styles$
|
|
6112
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { ref: triggerRef, onContextMenu: handleContextMenu, style: { display: "contents" }, children: children }), isOpen && normalizedOptions.length > 0 && reactDom.createPortal(jsxRuntime.jsxs("div", { ref: menuRef, className: styles$a["menu"], style: {
|
|
6113
6113
|
top: `${position.y}px`,
|
|
6114
6114
|
left: `${position.x}px`,
|
|
6115
|
-
}, "data-has-arrow": "true", children: [jsxRuntime.jsx("div", { ref: arrowRef, className: styles$
|
|
6115
|
+
}, "data-has-arrow": "true", children: [jsxRuntime.jsx("div", { ref: arrowRef, className: styles$a["arrow"], "data-placement": placement }), normalizedOptions.map(option => (jsxRuntime.jsxs("div", { onClick: (e) => {
|
|
6116
6116
|
e.stopPropagation();
|
|
6117
6117
|
e.preventDefault();
|
|
6118
6118
|
handleOptionClick(option);
|
|
6119
|
-
}, className: `${styles$
|
|
6119
|
+
}, className: `${styles$a["item"]} ${option.disabled ? styles$a["disabled"] : ""}`, "data-disabled": option.disabled, children: [option.icon && jsxRuntime.jsx("span", { className: styles$a["icon"], children: option.icon }), option.label] }, option.key)))] }), document.body)] }));
|
|
6120
6120
|
};
|
|
6121
6121
|
|
|
6122
6122
|
/**
|
|
@@ -6189,13 +6189,13 @@ const CwContextMenu = ({ children, options, offset = DEFAULT_OFFSET, onSelect })
|
|
|
6189
6189
|
document.removeEventListener("keydown", handleEscape);
|
|
6190
6190
|
};
|
|
6191
6191
|
}, [isOpen]);
|
|
6192
|
-
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { onContextMenu: handleContextMenu, children: children }), isOpen && reactDom.createPortal(jsxRuntime.jsx("div", { ref: menuRef, className: styles$
|
|
6192
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { onContextMenu: handleContextMenu, children: children }), isOpen && reactDom.createPortal(jsxRuntime.jsx("div", { ref: menuRef, className: styles$a["menu"], style: {
|
|
6193
6193
|
top: `${position.y}px`,
|
|
6194
6194
|
left: `${position.x}px`,
|
|
6195
|
-
}, children: options.map(option => (jsxRuntime.jsx("div", { onClick: () => handleOptionClick(option), className: styles$
|
|
6195
|
+
}, children: options.map(option => (jsxRuntime.jsx("div", { onClick: () => handleOptionClick(option), className: styles$a["item"], children: option }, option))) }), document.body)] }));
|
|
6196
6196
|
};
|
|
6197
6197
|
|
|
6198
|
-
var styles$
|
|
6198
|
+
var styles$9 = {"split":"cw-button-split-module__split__B2KbU","menu":"cw-button-split-module__menu__mKDlt","item":"cw-button-split-module__item__Aqw6f"};
|
|
6199
6199
|
|
|
6200
6200
|
/**
|
|
6201
6201
|
* CwButtonSplit - A primary action joined to a dropdown of alternative actions.
|
|
@@ -6221,7 +6221,7 @@ function CwButtonSplit({ text, icon, onClick, actions, onSelect, variant = "soli
|
|
|
6221
6221
|
onSelect(action.key);
|
|
6222
6222
|
event.currentTarget.closest("[popover]")?.hidePopover();
|
|
6223
6223
|
};
|
|
6224
|
-
return (jsxRuntime.jsxs("div", { className: styles$
|
|
6224
|
+
return (jsxRuntime.jsxs("div", { className: styles$9.split, "data-variant": variant, "data-testid": dataTestId, children: [jsxRuntime.jsx(CwButton, { text: text, icon: icon, variant: variant, color: color, size: size, onClick: onClick, disabled: disabled }), jsxRuntime.jsx(CwPopoverButton, { icon: "chevron-down", title: "More actions", variant: variant, color: color, size: size, disabled: disabled, placement: placement, children: jsxRuntime.jsx("div", { className: styles$9.menu, children: actions.map((action) => (jsxRuntime.jsx(CwButton, { variant: "ghost", color: "neutral", text: action.label, icon: action.icon, disabled: action.disabled, className: styles$9.item, onClick: handleSelect(action) }, action.key))) }) })] }));
|
|
6225
6225
|
}
|
|
6226
6226
|
|
|
6227
6227
|
/**
|
|
@@ -6289,7 +6289,7 @@ function useRovingSingleSelect(options, value, onChange) {
|
|
|
6289
6289
|
return { activeIndex, getSegmentProps };
|
|
6290
6290
|
}
|
|
6291
6291
|
|
|
6292
|
-
var styles$
|
|
6292
|
+
var styles$8 = {"switch":"cw-button-switch-module__switch__vdWV-","segment":"cw-button-switch-module__segment__JmVla","iconOnly":"cw-button-switch-module__iconOnly__-kCwj"};
|
|
6293
6293
|
|
|
6294
6294
|
/**
|
|
6295
6295
|
* CwButtonSwitch - Segmented single-select control: a strip of joined CwButtons.
|
|
@@ -6319,10 +6319,10 @@ var styles$5 = {"switch":"cw-button-switch-module__switch__vdWV-","segment":"cw-
|
|
|
6319
6319
|
*/
|
|
6320
6320
|
function CwButtonSwitch({ options, value, onChange, size = "medium", color = "primary", iconOnly = false, direction = "row", disabled = false, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, ...dataAttributes }) {
|
|
6321
6321
|
const { getSegmentProps } = useRovingSingleSelect(options, value, onChange);
|
|
6322
|
-
return (jsxRuntime.jsx("div", { ...dataAttributes, className: `cw-button-switch ${styles$
|
|
6322
|
+
return (jsxRuntime.jsx("div", { ...dataAttributes, className: `cw-button-switch ${styles$8.switch}`, role: "radiogroup", "aria-labelledby": ariaLabelledby, "aria-label": ariaLabelledby ? undefined : ariaLabel, "data-direction": direction, children: options.map((option, index) => {
|
|
6323
6323
|
const selected = option.value === value;
|
|
6324
6324
|
const segmentProps = getSegmentProps(index);
|
|
6325
|
-
return (jsxRuntime.jsx(CwButton, { text: iconOnly ? undefined : option.label, title: iconOnly ? option.label : undefined, icon: option.icon, size: size, color: color, variant: selected ? "solid" : "outline", role: "radio", "aria-checked": selected, disabled: disabled || option.disabled, onClick: () => onChange(option.value), className: `${styles$
|
|
6325
|
+
return (jsxRuntime.jsx(CwButton, { text: iconOnly ? undefined : option.label, title: iconOnly ? option.label : undefined, icon: option.icon, size: size, color: color, variant: selected ? "solid" : "outline", role: "radio", "aria-checked": selected, disabled: disabled || option.disabled, onClick: () => onChange(option.value), className: `${styles$8.segment}${iconOnly ? ` ${styles$8.iconOnly}` : ""}`, tabIndex: segmentProps.tabIndex, ref: segmentProps.ref, onKeyDown: segmentProps.onKeyDown }, option.value));
|
|
6326
6326
|
}) }));
|
|
6327
6327
|
}
|
|
6328
6328
|
|
|
@@ -6364,7 +6364,7 @@ function CwBtnCancel({ cw_btnOnclick, cw_btn_disabled, onClick, disabled, ...res
|
|
|
6364
6364
|
return (jsxRuntime.jsx(CwButton, { ...rest, variant: "icon", icon: "cancel", color: "danger", onClick: onClick ?? cw_btnOnclick, disabled: disabled ?? cw_btn_disabled, title: "Cancel" }));
|
|
6365
6365
|
}
|
|
6366
6366
|
|
|
6367
|
-
var styles$
|
|
6367
|
+
var styles$7 = {"value":"cw-pickers-base-module__value__BR67P","pickerWrapper":"cw-pickers-base-module__pickerWrapper__Fb9Zo","pickerIcons":"cw-pickers-base-module__pickerIcons__dyd2-","pickerPopup":"cw-pickers-base-module__pickerPopup__dkxJo"};
|
|
6368
6368
|
|
|
6369
6369
|
/**
|
|
6370
6370
|
* Renders a picker popup in a portal so it escapes overflow containers.
|
|
@@ -6604,7 +6604,7 @@ function CwDatePicker({ value, onChange, minDate, maxDate, disabledDates, disabl
|
|
|
6604
6604
|
...(maxDate ? [{ after: maxDate }] : []),
|
|
6605
6605
|
...(disabledMatcher ? [disabledMatcher] : []),
|
|
6606
6606
|
], [disabledDates, minDate, maxDate, disabledMatcher]);
|
|
6607
|
-
return (jsxRuntime.jsx("div", { ...dataAttributes, 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", { className: styles$
|
|
6607
|
+
return (jsxRuntime.jsx("div", { ...dataAttributes, 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", { className: styles$7.value, children: [jsxRuntime.jsxs("div", { ref: wrapperRef, className: styles$7.pickerWrapper, children: [jsxRuntime.jsx("input", { ref: inputRef, type: "text", value: inputValue, placeholder: placeholder, onChange: handleInputChange, onBlur: handleInputBlur, onClick: handleInputClick, onKeyDown: handleInputKeyDown, disabled: disabled, "aria-describedby": describedBy, required: required, size: displayFormat.length }), jsxRuntime.jsx("div", { className: styles$7.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$7.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, captionLayout: "dropdown", startMonth: dropdownStartMonth, endMonth: dropdownEndMonth, modifiers: {
|
|
6608
6608
|
today: new Date(),
|
|
6609
6609
|
}, modifiersClassNames: {
|
|
6610
6610
|
today: "rdp-day-today",
|
|
@@ -7253,7 +7253,7 @@ function CwDateRangePicker({ value, onChange, minDate, maxDate, disabledDates, d
|
|
|
7253
7253
|
}
|
|
7254
7254
|
return undefined;
|
|
7255
7255
|
}, [value]);
|
|
7256
|
-
return (jsxRuntime.jsx("div", { ...dataAttributes, ref: containerRef, className: `cw-rangepicker ${className || ""}`, children: jsxRuntime.jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsxRuntime.jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxRuntime.jsxs("div", { className: styles$
|
|
7256
|
+
return (jsxRuntime.jsx("div", { ...dataAttributes, ref: containerRef, className: `cw-rangepicker ${className || ""}`, children: jsxRuntime.jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsxRuntime.jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxRuntime.jsxs("div", { className: styles$7.value, children: [jsxRuntime.jsxs("div", { ref: wrapperRef, className: rangeStyles.rangeWrapper, children: [jsxRuntime.jsxs("div", { className: styles$7.pickerWrapper, children: [jsxRuntime.jsx("input", { ref: inputFromRef, type: "text", value: inputFromValue, placeholder: placeholderFrom, onChange: handleInputFromChange, onBlur: () => handleInputBlur('from'), onClick: handleInputFromClick, onKeyDown: (e) => handleInputKeyDown(e, 'from'), disabled: disabled, "aria-describedby": describedBy, required: required, size: displayFormat.length, "data-focused": focusedInput === 'from' }), jsxRuntime.jsx("div", { className: styles$7.pickerIcons, children: showClear && value?.from && !disabled ? (jsxRuntime.jsx(CwButton, { type: "button", variant: "icon", color: "neutral", icon: "close", onClick: handleClearFrom, tabIndex: -1, "aria-label": "Clear from date" })) : (jsxRuntime.jsx(CwIcon, { iconId: "calendar" })) })] }), jsxRuntime.jsx(CwIcon, { iconId: "arrow-right", size: "medium" }), jsxRuntime.jsxs("div", { className: styles$7.pickerWrapper, children: [jsxRuntime.jsx("input", { ref: inputToRef, type: "text", value: inputToValue, placeholder: placeholderTo, onChange: handleInputToChange, onBlur: () => handleInputBlur('to'), onClick: handleInputToClick, onKeyDown: (e) => handleInputKeyDown(e, 'to'), disabled: disabled, "aria-describedby": describedBy, required: required, size: displayFormat.length, "data-focused": focusedInput === 'to' }), jsxRuntime.jsx("div", { className: styles$7.pickerIcons, children: showClear && value?.to && !disabled ? (jsxRuntime.jsx(CwButton, { type: "button", variant: "icon", color: "neutral", icon: "close", onClick: handleClearTo, tabIndex: -1, "aria-label": "Clear to date" })) : (jsxRuntime.jsx(CwIcon, { iconId: "calendar" })) })] }), renderPopup(jsxRuntime.jsxs("div", { ref: popupRef, className: `${styles$7.pickerPopup} ${rangeStyles.rangePopup}`, style: popupStyle, children: [jsxRuntime.jsxs("div", { className: rangeStyles.rangeCalendarRow, children: [showPresets && presetsToRender.length > 0 && (jsxRuntime.jsx("div", { className: rangeStyles.presetList, children: presetsToRender.map((preset) => (jsxRuntime.jsx("button", { type: "button", onClick: (e) => handlePresetClick(preset, e), children: preset.label }, preset.key))) })), jsxRuntime.jsx(reactDayPicker.DayPicker, { mode: "range", selected: selectedRange, onSelect: handleRangeSelect, disabled: disabledDays, numberOfMonths: numberOfMonths, defaultMonth: defaultMonth || value?.from || undefined, modifiers: {
|
|
7257
7257
|
today: new Date(),
|
|
7258
7258
|
}, modifiersClassNames: {
|
|
7259
7259
|
today: "rdp-day-today",
|
|
@@ -7426,10 +7426,10 @@ function CwTimePicker({ value, onChange, interval = 15, minTime, maxTime, labelP
|
|
|
7426
7426
|
const displayValue = inputValue;
|
|
7427
7427
|
// If using native time input
|
|
7428
7428
|
if (useNative) {
|
|
7429
|
-
return (jsxRuntime.jsx("div", { ...dataAttributes, className: `cw-timepicker cw-timepicker-native ${className || ""}`, children: jsxRuntime.jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsxRuntime.jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxRuntime.jsxs("div", { className: styles$
|
|
7429
|
+
return (jsxRuntime.jsx("div", { ...dataAttributes, className: `cw-timepicker cw-timepicker-native ${className || ""}`, children: jsxRuntime.jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsxRuntime.jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxRuntime.jsxs("div", { className: styles$7.value, children: [jsxRuntime.jsx("div", { className: styles$7.pickerWrapper, children: jsxRuntime.jsx("input", { type: "time", value: displayValue, onChange: handleNativeChange, min: minTime, max: maxTime, step: interval * 60, disabled: disabled, required: required, placeholder: placeholder, "aria-describedby": describedBy, className: "cw-timepicker-native" }) }), feedbackElements] })] }) }));
|
|
7430
7430
|
}
|
|
7431
7431
|
// Custom time picker
|
|
7432
|
-
return (jsxRuntime.jsx("div", { ...dataAttributes, ref: containerRef, className: `cw-timepicker ${className || ""}`, children: jsxRuntime.jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsxRuntime.jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxRuntime.jsxs("div", { className: styles$
|
|
7432
|
+
return (jsxRuntime.jsx("div", { ...dataAttributes, ref: containerRef, className: `cw-timepicker ${className || ""}`, children: jsxRuntime.jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsxRuntime.jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxRuntime.jsxs("div", { className: styles$7.value, children: [jsxRuntime.jsxs("div", { ref: wrapperRef, className: `${styles$7.pickerWrapper} ${timeStyles.field}${showClear ? "" : ` ${timeStyles.narrow}`}`, children: [jsxRuntime.jsx("input", { ref: inputRef, type: "text", value: inputValue, placeholder: placeholder, onChange: handleInputChange, onBlur: handleInputBlur, onClick: handleInputClick, onKeyDown: handleInputKeyDown, disabled: disabled, "aria-describedby": describedBy, required: required, maxLength: 5 }), showClear && (jsxRuntime.jsx("div", { className: styles$7.pickerIcons, children: value && !disabled ? (jsxRuntime.jsx(CwButton, { type: "button", variant: "icon", icon: "close", color: "neutral", onClick: handleClear, tabIndex: -1, "aria-label": "Clear time" })) : (jsxRuntime.jsx(CwIcon, { iconId: "clock" })) })), renderPopup(jsxRuntime.jsx("div", { ref: popupRef, className: `${styles$7.pickerPopup} ${timeStyles.timePickerPopup}`, style: popupStyle, children: jsxRuntime.jsx("div", { className: timeStyles.timePickerList, children: timeOptions.map((time) => {
|
|
7433
7433
|
const isSelected = time === value;
|
|
7434
7434
|
return (jsxRuntime.jsx("button", { ref: isSelected ? selectedItemRef : null, type: "button", className: isSelected ? timeStyles.selected : "", onClick: () => handleTimeSelect(time), children: time }, time));
|
|
7435
7435
|
}) }) }))] }), feedbackElements] })] }) }));
|
|
@@ -7737,7 +7737,7 @@ function CwDateTimePicker({ value, onChange, minDateTime, maxDateTime, disabledD
|
|
|
7737
7737
|
const displayMaxDate = React.useMemo(() => normalizedMaxDateTime && timezoneOffset !== undefined
|
|
7738
7738
|
? getDateInOffset(normalizedMaxDateTime, timezoneOffset)
|
|
7739
7739
|
: normalizedMaxDateTime, [normalizedMaxDateTime, timezoneOffset]);
|
|
7740
|
-
return jsxRuntime.jsx("div", { ...dataAttributes, 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: styles$
|
|
7740
|
+
return jsxRuntime.jsx("div", { ...dataAttributes, 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: styles$7.value, children: [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 }))] }), feedbackMessages.map((feedbackItem, index) => (jsxRuntime.jsx("p", { id: feedbackIds[index], className: "cw-input-feedback", "data-color": feedbackItem.type, "aria-live": "polite", children: feedbackItem.message }, index)))] })] }) });
|
|
7741
7741
|
}
|
|
7742
7742
|
|
|
7743
7743
|
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"};
|
|
@@ -7993,7 +7993,7 @@ function CwDateTimePickerCompact({ value, onChange, minDateTime, maxDateTime, di
|
|
|
7993
7993
|
...(maxDateTime ? [{ after: maxDateTime }] : []),
|
|
7994
7994
|
...(disabledMatcher ? [disabledMatcher] : []),
|
|
7995
7995
|
], [disabledDates, minDateTime, maxDateTime, disabledMatcher]);
|
|
7996
|
-
return (jsxRuntime.jsx("div", { ...dataAttributes, 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", { className: styles$
|
|
7996
|
+
return (jsxRuntime.jsx("div", { ...dataAttributes, 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", { className: styles$7.value, children: [jsxRuntime.jsxs("div", { ref: wrapperRef, className: styles$7.pickerWrapper, children: [jsxRuntime.jsx("input", { ref: inputRef, type: "text", value: inputValue, placeholder: placeholder, onChange: handleInputChange, onBlur: handleInputBlur, onClick: handleInputClick, onKeyDown: handleInputKeyDown, disabled: disabled, "aria-describedby": describedBy, required: required, maxLength: 16 }), jsxRuntime.jsx("div", { className: styles$7.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$7.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, captionLayout: "dropdown", startMonth: dropdownStartMonth, endMonth: dropdownEndMonth, modifiers: {
|
|
7997
7997
|
today: new Date(),
|
|
7998
7998
|
}, modifiersClassNames: {
|
|
7999
7999
|
today: "rdp-day-today",
|
|
@@ -8228,7 +8228,7 @@ function CwDatePickerTemporal({ value, onChange, minDate, maxDate, disabledDates
|
|
|
8228
8228
|
const defaultMonthJsDate = defaultMonth
|
|
8229
8229
|
? new Date(defaultMonth.year, defaultMonth.month - 1, 1)
|
|
8230
8230
|
: selectedJsDate;
|
|
8231
|
-
return (jsxRuntime.jsx("div", { ...dataAttributes, 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", { className: styles$
|
|
8231
|
+
return (jsxRuntime.jsx("div", { ...dataAttributes, 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", { className: styles$7.value, children: [jsxRuntime.jsxs("div", { ref: wrapperRef, className: styles$7.pickerWrapper, children: [jsxRuntime.jsx("input", { ref: inputRef, type: "text", value: inputValue, placeholder: placeholder, onChange: handleInputChange, onBlur: handleInputBlur, onClick: handleInputClick, onKeyDown: handleInputKeyDown, disabled: disabled, "aria-describedby": describedBy, required: required, size: displayFormat.length }), jsxRuntime.jsx("div", { className: styles$7.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$7.pickerPopup, style: popupStyle, children: [jsxRuntime.jsx(reactDayPicker.DayPicker, { mode: "single", selected: selectedJsDate, defaultMonth: defaultMonthJsDate, onSelect: handleDaySelect, disabled: disabledDays, numberOfMonths: numberOfMonths, locale: locale$1, captionLayout: "dropdown", startMonth: dropdownStartMonth, endMonth: dropdownEndMonth, modifiers: {
|
|
8232
8232
|
today: new Date(),
|
|
8233
8233
|
}, modifiersClassNames: {
|
|
8234
8234
|
today: "rdp-day-today",
|
|
@@ -8529,7 +8529,7 @@ function CwDateTimePickerTemporal({ value, onChange, minDateTime, maxDateTime, d
|
|
|
8529
8529
|
minTime,
|
|
8530
8530
|
maxTime,
|
|
8531
8531
|
});
|
|
8532
|
-
return jsxRuntime.jsx("div", { ...dataAttributes, 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: styles$
|
|
8532
|
+
return jsxRuntime.jsx("div", { ...dataAttributes, 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: styles$7.value, children: [jsxRuntime.jsxs("div", { className: "cw-flex-row cw-align-left-center cw-gap-small", children: [jsxRuntime.jsx(CwDatePickerTemporal, { value: picker.date, onChange: picker.selectDate, minDate: picker.minDate, maxDate: picker.maxDate, disabledDates: disabledDates, disabledMatcher: disabledMatcher, placeholder: datePlaceholder, disabled: disabled, required: required, showClear: showClear, numberOfMonths: numberOfMonths, popupPosition: popupPosition, locale: locale$1 }), jsxRuntime.jsx(CwTimePicker, { value: picker.time, onChange: picker.selectTime, interval: timeInterval, minTime: picker.effectiveMinTime, maxTime: picker.effectiveMaxTime, placeholder: timePlaceholder, disabled: disabled, required: required, showClear: showClear, popupPosition: popupPosition }), showNowButton && (jsxRuntime.jsx(CwButton, { type: "button", variant: "outline", onClick: picker.selectNow, disabled: disabled, text: nowLabel }))] }), feedbackMessages.map((feedbackItem, index) => (jsxRuntime.jsx("p", { id: feedbackIds[index], className: "cw-input-feedback", "data-color": feedbackItem.type, "aria-live": "polite", children: feedbackItem.message }, index)))] })] }) });
|
|
8533
8533
|
}
|
|
8534
8534
|
|
|
8535
8535
|
// dateRangePresetsTemporal.ts
|
|
@@ -8981,7 +8981,7 @@ function CwDateRangePickerTemporal({ value, onChange, minDate, maxDate, disabled
|
|
|
8981
8981
|
const defaultMonthJsDate = defaultMonth
|
|
8982
8982
|
? new Date(defaultMonth.year, defaultMonth.month - 1, 1)
|
|
8983
8983
|
: (value?.from ? plainDateToJsDate(value.from) : undefined);
|
|
8984
|
-
return (jsxRuntime.jsx("div", { ...dataAttributes, ref: containerRef, className: `cw-rangepicker ${className || ""}`, children: jsxRuntime.jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsxRuntime.jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxRuntime.jsxs("div", { className: styles$
|
|
8984
|
+
return (jsxRuntime.jsx("div", { ...dataAttributes, ref: containerRef, className: `cw-rangepicker ${className || ""}`, children: jsxRuntime.jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsxRuntime.jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxRuntime.jsxs("div", { className: styles$7.value, children: [jsxRuntime.jsxs("div", { ref: wrapperRef, className: rangeStyles.rangeWrapper, children: [jsxRuntime.jsxs("div", { className: styles$7.pickerWrapper, children: [jsxRuntime.jsx("input", { ref: inputFromRef, type: "text", value: inputFromValue, placeholder: placeholderFrom, onChange: handleInputFromChange, onBlur: () => handleInputBlur('from'), onClick: handleInputFromClick, onKeyDown: (e) => handleInputKeyDown(e, 'from'), disabled: disabled, "aria-describedby": describedBy, required: required, size: displayFormat.length, "data-focused": focusedInput === 'from' }), jsxRuntime.jsx("div", { className: styles$7.pickerIcons, children: showClear && value?.from && !disabled ? (jsxRuntime.jsx(CwButton, { type: "button", variant: "icon", color: "neutral", icon: "close", onClick: handleClearFrom, tabIndex: -1, "aria-label": "Clear from date" })) : (jsxRuntime.jsx(CwIcon, { iconId: "calendar" })) })] }), jsxRuntime.jsx(CwIcon, { iconId: "arrow-right", size: "medium" }), jsxRuntime.jsxs("div", { className: styles$7.pickerWrapper, children: [jsxRuntime.jsx("input", { ref: inputToRef, type: "text", value: inputToValue, placeholder: placeholderTo, onChange: handleInputToChange, onBlur: () => handleInputBlur('to'), onClick: handleInputToClick, onKeyDown: (e) => handleInputKeyDown(e, 'to'), disabled: disabled, "aria-describedby": describedBy, required: required, size: displayFormat.length, "data-focused": focusedInput === 'to' }), jsxRuntime.jsx("div", { className: styles$7.pickerIcons, children: showClear && value?.to && !disabled ? (jsxRuntime.jsx(CwButton, { type: "button", variant: "icon", color: "neutral", icon: "close", onClick: handleClearTo, tabIndex: -1, "aria-label": "Clear to date" })) : (jsxRuntime.jsx(CwIcon, { iconId: "calendar" })) })] }), renderPopup(jsxRuntime.jsxs("div", { ref: popupRef, className: `${styles$7.pickerPopup} ${rangeStyles.rangePopup}`, style: popupStyle, children: [jsxRuntime.jsxs("div", { className: rangeStyles.rangeCalendarRow, children: [showPresets && presetsToRender.length > 0 && (jsxRuntime.jsx("div", { className: rangeStyles.presetList, children: presetsToRender.map((preset) => (jsxRuntime.jsx("button", { type: "button", onClick: (e) => handlePresetClick(preset, e), children: preset.label }, preset.key))) })), jsxRuntime.jsx(reactDayPicker.DayPicker, { mode: "range", selected: selectedRange, onSelect: handleRangeSelect, disabled: disabledDays, numberOfMonths: numberOfMonths, defaultMonth: defaultMonthJsDate, locale: locale$1, captionLayout: "dropdown", startMonth: dropdownStartMonth, endMonth: dropdownEndMonth, modifiers: {
|
|
8985
8985
|
today: new Date(),
|
|
8986
8986
|
}, modifiersClassNames: {
|
|
8987
8987
|
today: "rdp-day-today",
|
|
@@ -9128,7 +9128,7 @@ class OnClickContextMenu {
|
|
|
9128
9128
|
}
|
|
9129
9129
|
}
|
|
9130
9130
|
|
|
9131
|
-
var styles$
|
|
9131
|
+
var styles$6 = {"scheduler-header":"scheduler-header-module__scheduler-header__Y7SNV","scheduler-header-divisions":"scheduler-header-module__scheduler-header-divisions__1JrNe","scheduler-timeline-now":"scheduler-header-module__scheduler-timeline-now__naREv","scheduler-timeline-label":"scheduler-header-module__scheduler-timeline-label__mLVAp","scheduler-header-title":"scheduler-header-module__scheduler-header-title__Jzy94","title-cell":"scheduler-header-module__title-cell__mz7Ki","title":"scheduler-header-module__title__TuJVn","scheduler-dates":"scheduler-header-module__scheduler-dates__O7ePQ","current-day":"scheduler-header-module__current-day__FpHiv","scheduler-hours":"scheduler-header-module__scheduler-hours__ZDCVj"};
|
|
9132
9132
|
|
|
9133
9133
|
const getNow$1 = (isUtc) => {
|
|
9134
9134
|
let now = new Date();
|
|
@@ -9157,9 +9157,9 @@ const TimeLine = ({ left, top = "0px", height, color, width = "2px", zIndexNumbe
|
|
|
9157
9157
|
background: color,
|
|
9158
9158
|
pointerEvents: "none",
|
|
9159
9159
|
zIndex: zIndexNumber,
|
|
9160
|
-
}, className: styles$
|
|
9160
|
+
}, className: styles$6["scheduler-timeline-now"], children: isHeader && (jsxRuntime.jsx("span", { style: {
|
|
9161
9161
|
color: color,
|
|
9162
|
-
}, className: styles$
|
|
9162
|
+
}, className: styles$6["scheduler-timeline-label"], children: currentTime.toLocaleTimeString([], {
|
|
9163
9163
|
hour: '2-digit',
|
|
9164
9164
|
minute: '2-digit',
|
|
9165
9165
|
hour12: false
|
|
@@ -9186,17 +9186,17 @@ const SchedulerHeaderDivision = ({ topDivisions, bottomDivisions, classification
|
|
|
9186
9186
|
return (jsxRuntime.jsxs("div", { style: {
|
|
9187
9187
|
width: topWidth,
|
|
9188
9188
|
cursor: "pointer"
|
|
9189
|
-
}, children: [jsxRuntime.jsx("div", { className: `${styles$
|
|
9189
|
+
}, children: [jsxRuntime.jsx("div", { className: `${styles$6["scheduler-dates"]} ${isToday(startDate, endDate) ? styles$6["current-day"] : ""}`, children: jsxRuntime.jsx("span", { children: topDivisions }) }), jsxRuntime.jsx("div", { style: { display: "flex", justifyContent: "space-between", flex: 1 }, children: bottomDivisions.map((element) => (jsxRuntime.jsx("div", { className: styles$6["scheduler-hours"], children: jsxRuntime.jsx("span", { children: element }) }, classification + element + topDivisions))) })] }));
|
|
9190
9190
|
};
|
|
9191
9191
|
|
|
9192
9192
|
const HeaderTitle = ({ title, dividerText, width }) => {
|
|
9193
|
-
return (jsxRuntime.jsxs("div", { className: styles$
|
|
9193
|
+
return (jsxRuntime.jsxs("div", { className: styles$6["scheduler-header-title"], style: { width: width }, children: [jsxRuntime.jsx("div", { className: styles$6["title-cell"], children: jsxRuntime.jsx("span", { className: styles$6.title, children: title }) }), jsxRuntime.jsx("div", { children: jsxRuntime.jsx("span", { children: dividerText }) })] }));
|
|
9194
9194
|
};
|
|
9195
9195
|
|
|
9196
9196
|
const SchedulerHeader = ({ content, divisions, visibleDays, selectedDate, width, onEvent, timeLinePercentage, isUtc }) => {
|
|
9197
9197
|
const topWidth = (100 / visibleDays);
|
|
9198
9198
|
const { title, classification } = content;
|
|
9199
|
-
return (jsxRuntime.jsxs("div", { className: styles$
|
|
9199
|
+
return (jsxRuntime.jsxs("div", { className: styles$6["scheduler-header"], children: [jsxRuntime.jsx(HeaderTitle, { title: title, dividerText: classification, width: width }), jsxRuntime.jsxs("div", { className: styles$6["scheduler-header-divisions"], style: { flex: 1, display: "flex", position: "relative" }, onMouseDown: (e) => {
|
|
9200
9200
|
e.preventDefault();
|
|
9201
9201
|
const clickPercentage = getPercentageFromMouseEvent(e);
|
|
9202
9202
|
const date = dateFromPercentage(selectedDate, visibleDays, clickPercentage);
|
|
@@ -9228,7 +9228,7 @@ function getStartEnd(day) {
|
|
|
9228
9228
|
};
|
|
9229
9229
|
}
|
|
9230
9230
|
|
|
9231
|
-
var styles$
|
|
9231
|
+
var styles$5 = {"hide-scrollbar":"new-scheduler-module__hide-scrollbar__33GG9","scheduler-event":"new-scheduler-module__scheduler-event__gphwn","scheduler-event-container":"new-scheduler-module__scheduler-event-container__-h1xm","time-marker":"new-scheduler-module__time-marker__2BejU","underlay":"new-scheduler-module__underlay__RVCnV","label-halo":"new-scheduler-module__label-halo__84c7e","corner-marker":"new-scheduler-module__corner-marker__w7o90","color-bars":"new-scheduler-module__color-bars__MhMF7","scheduler-event-text":"new-scheduler-module__scheduler-event-text__zjvd7"};
|
|
9232
9232
|
|
|
9233
9233
|
const BackgroundEvent = ({ value, heightRem }) => {
|
|
9234
9234
|
const backColor = getHSLColor(value.color, value.alpha ?? 0.5);
|
|
@@ -9255,13 +9255,13 @@ const BackgroundEvent = ({ value, heightRem }) => {
|
|
|
9255
9255
|
padding: "0.25rem",
|
|
9256
9256
|
color: textColor,
|
|
9257
9257
|
alignItems: value.contentAlign ?? "center"
|
|
9258
|
-
}, className: styles$
|
|
9258
|
+
}, className: styles$5["scheduler-event-container"], children: [value.icons ? jsxRuntime.jsx("div", { style: {
|
|
9259
9259
|
display: "flex",
|
|
9260
9260
|
flexDirection: "row",
|
|
9261
9261
|
alignItems: "center",
|
|
9262
9262
|
whiteSpace: "nowrap",
|
|
9263
9263
|
color: textColor,
|
|
9264
|
-
}, children: value.icons }) : null, jsxRuntime.jsx("span", { className: styles$
|
|
9264
|
+
}, children: value.icons }) : null, jsxRuntime.jsx("span", { className: styles$5["scheduler-event-text"], children: value.name })] }) }) }) }) })) : null;
|
|
9265
9265
|
};
|
|
9266
9266
|
|
|
9267
9267
|
const eventIsVisible = (startDate, endDate, selectedDate, visibleDays) => {
|
|
@@ -9423,7 +9423,7 @@ const WeekEndLine = ({ left, top = "0px", height, widthPercentage }) => {
|
|
|
9423
9423
|
} }));
|
|
9424
9424
|
};
|
|
9425
9425
|
|
|
9426
|
-
var styles$
|
|
9426
|
+
var styles$4 = {"scheduler-row-header":"scheduler-row-module__scheduler-row-header__S-iv4"};
|
|
9427
9427
|
|
|
9428
9428
|
const DefaultRowHeader = ({ value, width, onEvent }) => {
|
|
9429
9429
|
return (jsxRuntime.jsxs("div", { style: {
|
|
@@ -9432,7 +9432,7 @@ const DefaultRowHeader = ({ value, width, onEvent }) => {
|
|
|
9432
9432
|
background: value.highlightColor,
|
|
9433
9433
|
}, onClick: (_) => {
|
|
9434
9434
|
onEvent(new OnClickRowHeader(value.rowId));
|
|
9435
|
-
}, className: styles$
|
|
9435
|
+
}, className: styles$4["scheduler-row-header"], children: [jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("strong", { children: value.title }), value.title2 && jsxRuntime.jsxs("span", { children: ["-", value.title2] }), " ", jsxRuntime.jsx("strong", { children: value.subtitle })] }), value.subtitle && jsxRuntime.jsxs("div", { style: { opacity: 0.5 }, children: [value.title3 && jsxRuntime.jsxs("span", { className: styles$4["scheduler-crewmember-functions"], children: ["(", value.title3, ")"] }), value.subtitle2 && jsxRuntime.jsxs("span", { children: ["-", value.subtitle2] })] })] }));
|
|
9436
9436
|
};
|
|
9437
9437
|
|
|
9438
9438
|
const EventSideDrag = ({ left = false, heightRem, onStartEvent }) => {
|
|
@@ -9522,7 +9522,7 @@ const SchedulerEvent = ({ value, heightRem, onEvent, maxEventHeightRem }) => {
|
|
|
9522
9522
|
height: `calc(${effectiveHeight}rem - 4px)`,
|
|
9523
9523
|
opacity: eventTransparent ? 0.5 : 1,
|
|
9524
9524
|
...value.style,
|
|
9525
|
-
}, className: styles$
|
|
9525
|
+
}, className: styles$5["scheduler-event"], "data-underlay": value.underlay ? true : undefined, children: [value.underlay && jsxRuntime.jsx("div", { className: styles$5["underlay"], children: value.underlay }), jsxRuntime.jsxs("div", { style: {
|
|
9526
9526
|
display: "flex",
|
|
9527
9527
|
flexDirection: "row",
|
|
9528
9528
|
alignItems: "stretch",
|
|
@@ -9533,13 +9533,13 @@ const SchedulerEvent = ({ value, heightRem, onEvent, maxEventHeightRem }) => {
|
|
|
9533
9533
|
height: "100%",
|
|
9534
9534
|
}, children: [value.isResizable && (jsxRuntime.jsx(EventSideDrag, { left: true, heightRem: effectiveHeight * 0.7, onStartEvent: () => {
|
|
9535
9535
|
onEvent(new OnLeftDragStart(value.id));
|
|
9536
|
-
} })), jsxRuntime.jsxs("div", { className: styles$
|
|
9536
|
+
} })), jsxRuntime.jsxs("div", { className: styles$5["scheduler-event-container"], children: [jsxRuntime.jsx("div", { style: {
|
|
9537
9537
|
display: "flex",
|
|
9538
9538
|
flexDirection: "row",
|
|
9539
9539
|
alignItems: "center",
|
|
9540
9540
|
whiteSpace: "nowrap",
|
|
9541
9541
|
color: textColor,
|
|
9542
|
-
}, children: value.icons }), jsxRuntime.jsx("span", { className: value.underlay ? styles$
|
|
9542
|
+
}, children: value.icons }), jsxRuntime.jsx("span", { className: value.underlay ? styles$5["label-halo"] : undefined, style: {
|
|
9543
9543
|
padding: "0.1rem",
|
|
9544
9544
|
color: textColor,
|
|
9545
9545
|
display: "flex",
|
|
@@ -9547,13 +9547,13 @@ const SchedulerEvent = ({ value, heightRem, onEvent, maxEventHeightRem }) => {
|
|
|
9547
9547
|
alignItems: "center",
|
|
9548
9548
|
width: "100%",
|
|
9549
9549
|
...(value.underlay && { "--label-halo-color": backColor }),
|
|
9550
|
-
}, children: value.rectangleColors && value.rectangleColors.length > 0 ? (jsxRuntime.jsx("div", { className: styles$
|
|
9550
|
+
}, children: value.rectangleColors && value.rectangleColors.length > 0 ? (jsxRuntime.jsx("div", { className: styles$5["color-bars"], children: value.rectangleColors.map((item, i) => (jsxRuntime.jsx("div", { style: { backgroundColor: item } }, value.id + "_" + i))) })) : (value.label ?? value.name) })] }), value.isResizable && (jsxRuntime.jsx(EventSideDrag, { heightRem: effectiveHeight * 0.7, onStartEvent: () => {
|
|
9551
9551
|
onEvent(new OnRightDragStart(value.id));
|
|
9552
|
-
} }))] }), value.primaryTimeMarkerColor && (jsxRuntime.jsx("div", { className: styles$
|
|
9552
|
+
} }))] }), value.primaryTimeMarkerColor && (jsxRuntime.jsx("div", { className: styles$5["time-marker"], style: {
|
|
9553
9553
|
backgroundColor: value.primaryTimeMarkerColor,
|
|
9554
|
-
} })), value.secondaryTimeMarkerColor && (jsxRuntime.jsx("div", { className: styles$
|
|
9554
|
+
} })), value.secondaryTimeMarkerColor && (jsxRuntime.jsx("div", { className: styles$5["time-marker"], style: {
|
|
9555
9555
|
backgroundColor: value.secondaryTimeMarkerColor,
|
|
9556
|
-
} }))] }) }) }, value.id) }), value.cornerMarker && (jsxRuntime.jsx("div", { className: styles$
|
|
9556
|
+
} }))] }) }) }, value.id) }), value.cornerMarker && (jsxRuntime.jsx("div", { className: styles$5["corner-marker"], style: {
|
|
9557
9557
|
top: `${topOffsetPx}px`,
|
|
9558
9558
|
left: `${value.left}%`,
|
|
9559
9559
|
width: `${value.width}%`,
|
|
@@ -10087,13 +10087,13 @@ const Scheduler = (props) => {
|
|
|
10087
10087
|
position: "sticky",
|
|
10088
10088
|
top: 0,
|
|
10089
10089
|
zIndex: 1,
|
|
10090
|
-
}, 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$
|
|
10090
|
+
}, 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$5["hide-scrollbar"], children: Row })] }) }));
|
|
10091
10091
|
};
|
|
10092
10092
|
|
|
10093
10093
|
class UiEvent {
|
|
10094
10094
|
}
|
|
10095
10095
|
|
|
10096
|
-
var styles = {"super-scheduler-row-header":"super-scheduler-module__super-scheduler-row-header__TTs4e","indicators":"super-scheduler-module__indicators__f4lIT","scheduler-crewmember-functions":"super-scheduler-module__scheduler-crewmember-functions__BS2hs"};
|
|
10096
|
+
var styles$3 = {"super-scheduler-row-header":"super-scheduler-module__super-scheduler-row-header__TTs4e","indicators":"super-scheduler-module__indicators__f4lIT","scheduler-crewmember-functions":"super-scheduler-module__scheduler-crewmember-functions__BS2hs"};
|
|
10097
10097
|
|
|
10098
10098
|
class OnPinRow {
|
|
10099
10099
|
id;
|
|
@@ -10121,15 +10121,15 @@ const PinRowHeader = ({ value, width, onEvent }) => {
|
|
|
10121
10121
|
flexShrink: 0,
|
|
10122
10122
|
height: "100%",
|
|
10123
10123
|
background: highlightColor
|
|
10124
|
-
}, className: styles["super-scheduler-row-header"], children: [jsxRuntime.jsx("button", { className: "cw-button-icon cwi-pin", "data-pinned": value.isPinned, onClick: (_) => {
|
|
10124
|
+
}, className: styles$3["super-scheduler-row-header"], children: [jsxRuntime.jsx("button", { className: "cw-button-icon cwi-pin", "data-pinned": value.isPinned, onClick: (_) => {
|
|
10125
10125
|
onEvent(value.isPinned ? new OnUnpinRow(value.rowId) : new OnPinRow(value.rowId));
|
|
10126
|
-
} }), jsxRuntime.jsx("div", { className: styles["indicators"], children: value.indicators ?? undefined }), jsxRuntime.jsxs("div", { style: {
|
|
10126
|
+
} }), jsxRuntime.jsx("div", { className: styles$3["indicators"], children: value.indicators ?? undefined }), jsxRuntime.jsxs("div", { style: {
|
|
10127
10127
|
display: "flex",
|
|
10128
10128
|
flexDirection: "column",
|
|
10129
10129
|
justifyContent: "center",
|
|
10130
10130
|
alignItems: "flex-start",
|
|
10131
10131
|
}, children: [jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("strong", { children: value.title }), value.title2 && jsxRuntime.jsxs("span", { children: ["-", value.title2] }), " ", jsxRuntime.jsx("strong", { children: value.subtitle })] }), value.subtitle && jsxRuntime.jsxs("div", { style: { opacity: 0.5 }, children: [value.title3 &&
|
|
10132
|
-
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);
|
|
10132
|
+
jsxRuntime.jsxs("span", { className: styles$3["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);
|
|
10133
10133
|
};
|
|
10134
10134
|
|
|
10135
10135
|
const SuperScheduler = ({ id, state, header, rows, events, headerColumnComp, pinnedOrderCategory, unPinnedOrderCategory, backgroundEvents, indicatorRows = [], contextMenuItems, onEvent, rowHeightRem = 1.75, rowHeaderWidth, maxEventHeightRem, EventComp = SchedulerEvent, }) => {
|
|
@@ -10141,6 +10141,2324 @@ const SuperScheduler = ({ id, state, header, rows, events, headerColumnComp, pin
|
|
|
10141
10141
|
}, 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: EventComp, RowTitleComp: headerColumnComp, rowHeightRem: rowHeightRem, rowHeaderWidth: rowHeaderWidth, maxEventHeightRem: maxEventHeightRem })] }));
|
|
10142
10142
|
};
|
|
10143
10143
|
|
|
10144
|
+
/** A toggle ref hides its item while `InitiallyHidden` XOR "flipped" is true. */
|
|
10145
|
+
function isToggleRefHidden(ref, flipped) {
|
|
10146
|
+
return ref.InitiallyHidden !== flipped.has(ref.Id);
|
|
10147
|
+
}
|
|
10148
|
+
/** An item, row or cell is visible only when every toggle it depends on is in its visible state. */
|
|
10149
|
+
function isRdlVisible(toggles, flipped) {
|
|
10150
|
+
if (!toggles || toggles.length === 0)
|
|
10151
|
+
return true;
|
|
10152
|
+
return toggles.every(ref => !isToggleRefHidden(ref, flipped));
|
|
10153
|
+
}
|
|
10154
|
+
/** The +/- state of a toggle textbox: the initial state inverted when its id was flipped. */
|
|
10155
|
+
function isToggleExpanded(textbox, flipped) {
|
|
10156
|
+
const initial = textbox.ToggleExpanded ?? false;
|
|
10157
|
+
return textbox.ToggleId ? initial !== flipped.has(textbox.ToggleId) : initial;
|
|
10158
|
+
}
|
|
10159
|
+
/**
|
|
10160
|
+
* The toggle ids an export or print sends so the file shows the drilldown state of the screen: the
|
|
10161
|
+
* ids flipped relative to the render's initial state (a toggle clicked twice is back to its initial
|
|
10162
|
+
* state and not sent), sorted for a stable request.
|
|
10163
|
+
*/
|
|
10164
|
+
function flippedToggleIds(flipped) {
|
|
10165
|
+
return [...flipped].sort();
|
|
10166
|
+
}
|
|
10167
|
+
/** The toggle ids to ask the server for after a click on toggle `id`: the flipped ids with `id` flipped once more. */
|
|
10168
|
+
function toggledIds(flipped, id) {
|
|
10169
|
+
return flippedToggleIds(flipToggle(flipped, id));
|
|
10170
|
+
}
|
|
10171
|
+
/** Returns a new set with `id` flipped (added when absent, removed when present). */
|
|
10172
|
+
function flipToggle(flipped, id) {
|
|
10173
|
+
const next = new Set(flipped);
|
|
10174
|
+
if (next.has(id)) {
|
|
10175
|
+
next.delete(id);
|
|
10176
|
+
}
|
|
10177
|
+
else {
|
|
10178
|
+
next.add(id);
|
|
10179
|
+
}
|
|
10180
|
+
return next;
|
|
10181
|
+
}
|
|
10182
|
+
|
|
10183
|
+
/** Number of visible rows among `span` rows starting at `start`, at least 1. */
|
|
10184
|
+
function visibleRowSpan(rowVisible, start, span) {
|
|
10185
|
+
const end = Math.min(rowVisible.length, start + Math.max(1, span));
|
|
10186
|
+
let count = 0;
|
|
10187
|
+
for (let i = start; i < end; i++) {
|
|
10188
|
+
if (rowVisible[i])
|
|
10189
|
+
count++;
|
|
10190
|
+
}
|
|
10191
|
+
return Math.max(1, count);
|
|
10192
|
+
}
|
|
10193
|
+
function sum(values, from, to) {
|
|
10194
|
+
let total = 0;
|
|
10195
|
+
for (let i = Math.max(0, from); i < Math.min(values.length, to); i++)
|
|
10196
|
+
total += values[i];
|
|
10197
|
+
return total;
|
|
10198
|
+
}
|
|
10199
|
+
function visibleHeight(rows, rowVisible, start, span) {
|
|
10200
|
+
const end = Math.min(rows.length, start + Math.max(1, span));
|
|
10201
|
+
let height = 0;
|
|
10202
|
+
for (let i = start; i < end; i++) {
|
|
10203
|
+
if (rowVisible[i])
|
|
10204
|
+
height += rows[i].Height;
|
|
10205
|
+
}
|
|
10206
|
+
return height;
|
|
10207
|
+
}
|
|
10208
|
+
/**
|
|
10209
|
+
* Lays a table out for the current toggle state: hidden rows take no space, cells are placed at
|
|
10210
|
+
* their column with ColSpan/RowSpan and a header cell's RowSpan is recounted over the visible rows.
|
|
10211
|
+
* Cells that start in a hidden row are not drawn.
|
|
10212
|
+
*/
|
|
10213
|
+
function layoutRdlTable(table, flipped) {
|
|
10214
|
+
const rows = table.Rows;
|
|
10215
|
+
const widths = table.ColumnWidths;
|
|
10216
|
+
const rowVisible = rows.map(row => isRdlVisible(row.Toggles, flipped));
|
|
10217
|
+
const rowTops = [];
|
|
10218
|
+
let top = 0;
|
|
10219
|
+
rows.forEach((row, index) => {
|
|
10220
|
+
rowTops.push(top);
|
|
10221
|
+
if (rowVisible[index])
|
|
10222
|
+
top += row.Height;
|
|
10223
|
+
});
|
|
10224
|
+
const cells = [];
|
|
10225
|
+
rows.forEach((row, rowIndex) => {
|
|
10226
|
+
if (!rowVisible[rowIndex])
|
|
10227
|
+
return;
|
|
10228
|
+
row.Cells.forEach((cell, cellIndex) => {
|
|
10229
|
+
const colSpan = Math.max(1, cell.ColSpan);
|
|
10230
|
+
cells.push({
|
|
10231
|
+
cell,
|
|
10232
|
+
rowIndex,
|
|
10233
|
+
cellIndex,
|
|
10234
|
+
left: sum(widths, 0, cell.Column),
|
|
10235
|
+
top: rowTops[rowIndex],
|
|
10236
|
+
width: sum(widths, cell.Column, cell.Column + colSpan),
|
|
10237
|
+
height: visibleHeight(rows, rowVisible, rowIndex, cell.RowSpan),
|
|
10238
|
+
rowSpan: visibleRowSpan(rowVisible, rowIndex, cell.RowSpan),
|
|
10239
|
+
hidden: !isRdlVisible(cell.Toggles, flipped)
|
|
10240
|
+
});
|
|
10241
|
+
});
|
|
10242
|
+
});
|
|
10243
|
+
return {
|
|
10244
|
+
width: sum(widths, 0, widths.length),
|
|
10245
|
+
height: top,
|
|
10246
|
+
rowTops,
|
|
10247
|
+
rowVisible,
|
|
10248
|
+
cells
|
|
10249
|
+
};
|
|
10250
|
+
}
|
|
10251
|
+
|
|
10252
|
+
/*
|
|
10253
|
+
* Find over the text of one page. Every run gets a key from its position in the render tree; the
|
|
10254
|
+
* renderer builds the same keys, so a match can be highlighted where the run is drawn.
|
|
10255
|
+
*/
|
|
10256
|
+
/** Key prefixes of the three page sections. */
|
|
10257
|
+
const RDL_SECTION_KEYS = { header: "h", body: "b", footer: "f" };
|
|
10258
|
+
function rdlItemKey(parentKey, index) {
|
|
10259
|
+
return `${parentKey}.${index}`;
|
|
10260
|
+
}
|
|
10261
|
+
function rdlCellKey(tableKey, rowIndex, cellIndex) {
|
|
10262
|
+
return `${tableKey}.r${rowIndex}c${cellIndex}`;
|
|
10263
|
+
}
|
|
10264
|
+
function rdlRunKey(textboxKey, paragraphIndex, runIndex) {
|
|
10265
|
+
return `${textboxKey}.p${paragraphIndex}r${runIndex}`;
|
|
10266
|
+
}
|
|
10267
|
+
function collectTextbox(textbox, key, out) {
|
|
10268
|
+
textbox.Paragraphs.forEach((paragraph, p) => {
|
|
10269
|
+
paragraph.Runs.forEach((run, r) => {
|
|
10270
|
+
if (run.Text)
|
|
10271
|
+
out.push({ key: rdlRunKey(key, p, r), text: run.Text });
|
|
10272
|
+
});
|
|
10273
|
+
});
|
|
10274
|
+
}
|
|
10275
|
+
function collectItem(item, key, flipped, out) {
|
|
10276
|
+
if (!isRdlVisible(item.Toggles, flipped))
|
|
10277
|
+
return;
|
|
10278
|
+
switch (item.Kind) {
|
|
10279
|
+
case "textbox":
|
|
10280
|
+
collectTextbox(item, key, out);
|
|
10281
|
+
break;
|
|
10282
|
+
case "rectangle":
|
|
10283
|
+
collectItems(item.Items, key, flipped, out);
|
|
10284
|
+
break;
|
|
10285
|
+
case "table": {
|
|
10286
|
+
const layout = layoutRdlTable(item, flipped);
|
|
10287
|
+
for (const box of layout.cells) {
|
|
10288
|
+
if (box.hidden || !box.cell.Content)
|
|
10289
|
+
continue;
|
|
10290
|
+
collectItem(box.cell.Content, rdlCellKey(key, box.rowIndex, box.cellIndex), flipped, out);
|
|
10291
|
+
}
|
|
10292
|
+
break;
|
|
10293
|
+
}
|
|
10294
|
+
}
|
|
10295
|
+
}
|
|
10296
|
+
function collectItems(items, parentKey, flipped, out) {
|
|
10297
|
+
items.forEach((item, index) => collectItem(item, rdlItemKey(parentKey, index), flipped, out));
|
|
10298
|
+
}
|
|
10299
|
+
/** Every visible text run of a page in document order: header, body, footer. */
|
|
10300
|
+
function collectPageText(page, flipped) {
|
|
10301
|
+
const out = [];
|
|
10302
|
+
if (page.Header)
|
|
10303
|
+
collectItems(page.Header.Items, RDL_SECTION_KEYS.header, flipped, out);
|
|
10304
|
+
collectItems(page.Items, RDL_SECTION_KEYS.body, flipped, out);
|
|
10305
|
+
if (page.Footer)
|
|
10306
|
+
collectItems(page.Footer.Items, RDL_SECTION_KEYS.footer, flipped, out);
|
|
10307
|
+
return out;
|
|
10308
|
+
}
|
|
10309
|
+
/**
|
|
10310
|
+
* The text lower-cased one character at a time, with each code unit of the result pointing back to the
|
|
10311
|
+
* original character it came from: lower-casing can change the length ("İ" becomes two code units outside
|
|
10312
|
+
* Turkish), and the highlights slice the original text.
|
|
10313
|
+
*/
|
|
10314
|
+
function lowerWithOffsets(text, locale) {
|
|
10315
|
+
let lower = "";
|
|
10316
|
+
const starts = [];
|
|
10317
|
+
const ends = [];
|
|
10318
|
+
let index = 0;
|
|
10319
|
+
for (const character of text) {
|
|
10320
|
+
const lowered = character.toLocaleLowerCase(locale);
|
|
10321
|
+
for (let unit = 0; unit < lowered.length; unit++) {
|
|
10322
|
+
starts.push(index);
|
|
10323
|
+
ends.push(index + character.length);
|
|
10324
|
+
}
|
|
10325
|
+
lower += lowered;
|
|
10326
|
+
index += character.length;
|
|
10327
|
+
}
|
|
10328
|
+
return { lower, starts, ends };
|
|
10329
|
+
}
|
|
10330
|
+
/**
|
|
10331
|
+
* Case-insensitive, non-overlapping matches of `query` in the runs, in run order, with the casing rules of
|
|
10332
|
+
* `locale` (default: the browser's, so "istanbul" finds "İSTANBUL" in Turkish). A match does not span two runs.
|
|
10333
|
+
*/
|
|
10334
|
+
function findInRuns(runs, query, locale) {
|
|
10335
|
+
const needle = query.trim().toLocaleLowerCase(locale);
|
|
10336
|
+
if (needle === "")
|
|
10337
|
+
return [];
|
|
10338
|
+
const matches = [];
|
|
10339
|
+
for (const run of runs) {
|
|
10340
|
+
const { lower, starts, ends } = lowerWithOffsets(run.text, locale);
|
|
10341
|
+
let from = 0;
|
|
10342
|
+
for (;;) {
|
|
10343
|
+
const at = lower.indexOf(needle, from);
|
|
10344
|
+
if (at < 0)
|
|
10345
|
+
break;
|
|
10346
|
+
const start = starts[at];
|
|
10347
|
+
matches.push({ key: run.key, start, length: ends[at + needle.length - 1] - start });
|
|
10348
|
+
from = at + needle.length;
|
|
10349
|
+
}
|
|
10350
|
+
}
|
|
10351
|
+
return matches;
|
|
10352
|
+
}
|
|
10353
|
+
/** Find over the visible text of one page. */
|
|
10354
|
+
function findInPage(page, query, flipped) {
|
|
10355
|
+
return findInRuns(collectPageText(page, flipped), query);
|
|
10356
|
+
}
|
|
10357
|
+
/**
|
|
10358
|
+
* The active match index: -1 without matches; the stored index clamped into the matches when it belongs to the
|
|
10359
|
+
* current page and query (a stored -1 from an empty search becomes 0 once a drilldown reveals matches); else 0.
|
|
10360
|
+
*/
|
|
10361
|
+
function activeMatchIndex(storedIndex, storedForCurrentSearch, count) {
|
|
10362
|
+
if (count <= 0)
|
|
10363
|
+
return -1;
|
|
10364
|
+
return storedForCurrentSearch ? Math.min(Math.max(0, storedIndex), count - 1) : 0;
|
|
10365
|
+
}
|
|
10366
|
+
/** The next match index after `current`, cycling; -1 when there are no matches. */
|
|
10367
|
+
function nextMatchIndex(current, count) {
|
|
10368
|
+
if (count <= 0)
|
|
10369
|
+
return -1;
|
|
10370
|
+
return current < 0 ? 0 : (current + 1) % count;
|
|
10371
|
+
}
|
|
10372
|
+
|
|
10373
|
+
/** CSS pixels per point: 96 px per inch, 72 pt per inch. */
|
|
10374
|
+
const PX_PER_PT = 96 / 72;
|
|
10375
|
+
/** Converts points to CSS pixels at a zoom factor (1 = 100%). */
|
|
10376
|
+
function ptToPx(pt, zoom = 1) {
|
|
10377
|
+
return pt * PX_PER_PT * zoom;
|
|
10378
|
+
}
|
|
10379
|
+
const POINTS_PER_UNIT = {
|
|
10380
|
+
pt: 1,
|
|
10381
|
+
px: 72 / 96,
|
|
10382
|
+
in: 72,
|
|
10383
|
+
cm: 72 / 2.54,
|
|
10384
|
+
mm: 72 / 25.4,
|
|
10385
|
+
pc: 12
|
|
10386
|
+
};
|
|
10387
|
+
// Linear: one digit run either side of the point, so a long non-matching value cannot backtrack quadratically.
|
|
10388
|
+
const SIZE_PATTERN = /^\s*(-?(?:\d+(?:\.\d*)?|\.\d+))\s*(pt|px|in|cm|mm|pc)?\s*$/i;
|
|
10389
|
+
/** No real RDL size comes near this; longer report-supplied strings are rejected before matching. */
|
|
10390
|
+
const MAX_SIZE_LENGTH = 32;
|
|
10391
|
+
/** Parses an RDL size ("10pt", "2.5mm", "1in", ...) into points; a bare number is points. Null when unparsable. */
|
|
10392
|
+
function parseRdlSize(value) {
|
|
10393
|
+
if (!value || value.length > MAX_SIZE_LENGTH)
|
|
10394
|
+
return null;
|
|
10395
|
+
const match = SIZE_PATTERN.exec(value);
|
|
10396
|
+
if (!match)
|
|
10397
|
+
return null;
|
|
10398
|
+
const unit = (match[2] ?? "pt").toLowerCase();
|
|
10399
|
+
return parseFloat(match[1]) * POINTS_PER_UNIT[unit];
|
|
10400
|
+
}
|
|
10401
|
+
/** An RDL size as CSS pixels at a zoom factor, or undefined when the size is missing or unparsable. */
|
|
10402
|
+
function rdlSizeToPx(value, zoom) {
|
|
10403
|
+
const pt = parseRdlSize(value);
|
|
10404
|
+
return pt === null ? undefined : ptToPx(pt, zoom);
|
|
10405
|
+
}
|
|
10406
|
+
|
|
10407
|
+
/** The zoom steps of the toolbar, 50% to 200%. */
|
|
10408
|
+
const REPORT_ZOOM_STEPS = [0.5, 0.75, 1, 1.25, 1.5, 2];
|
|
10409
|
+
const MIN_ZOOM = 0.1;
|
|
10410
|
+
const MAX_ZOOM = 4;
|
|
10411
|
+
/** Default margins of a standalone CwReportPage: a quarter inch, so content never touches the paper edge. */
|
|
10412
|
+
const DEFAULT_PAGE_MARGIN_PT = 18;
|
|
10413
|
+
/**
|
|
10414
|
+
* Margins of the sheet in the viewer: none. Like the old ReportViewer, the screen shows the content
|
|
10415
|
+
* (header, body, footer) without the RDL page margins; PDF and print come from the server with them.
|
|
10416
|
+
*/
|
|
10417
|
+
const SCREEN_PAGE_MARGINS = { top: 0, right: 0, bottom: 0, left: 0 };
|
|
10418
|
+
/** Width of the page content in points: the page's own `Width` (body plus items reaching past it) when the server sends it, else the body width. */
|
|
10419
|
+
function pageContentWidthPt(page, bodyWidth) {
|
|
10420
|
+
return page.Width && Number.isFinite(page.Width) && page.Width > 0 ? Math.max(page.Width, bodyWidth) : bodyWidth;
|
|
10421
|
+
}
|
|
10422
|
+
/** Size of the drawn sheet in points: margins + header + body + footer, as wide as the content so every item lies on it. */
|
|
10423
|
+
function pageSheetSizePt(page, bodyWidth, margins) {
|
|
10424
|
+
return {
|
|
10425
|
+
width: margins.left + pageContentWidthPt(page, bodyWidth) + margins.right,
|
|
10426
|
+
height: margins.top + (page.Header?.Height ?? 0) + page.Height + (page.Footer?.Height ?? 0) + margins.bottom
|
|
10427
|
+
};
|
|
10428
|
+
}
|
|
10429
|
+
/**
|
|
10430
|
+
* The zoom factor to draw with. "pageWidth" fits the sheet width into the available width,
|
|
10431
|
+
* "wholePage" fits the whole sheet; available sizes are CSS pixels. Clamped to 10%..400%.
|
|
10432
|
+
*/
|
|
10433
|
+
function resolveZoomFactor(zoom, sheetPt, available) {
|
|
10434
|
+
if (typeof zoom === "number")
|
|
10435
|
+
return clamp(zoom);
|
|
10436
|
+
const widthPx = ptToPx(sheetPt.width);
|
|
10437
|
+
const heightPx = ptToPx(sheetPt.height);
|
|
10438
|
+
if (widthPx <= 0 || available.width <= 0)
|
|
10439
|
+
return 1;
|
|
10440
|
+
const byWidth = available.width / widthPx;
|
|
10441
|
+
if (zoom === "pageWidth" || heightPx <= 0 || available.height <= 0)
|
|
10442
|
+
return clamp(byWidth);
|
|
10443
|
+
return clamp(Math.min(byWidth, available.height / heightPx));
|
|
10444
|
+
}
|
|
10445
|
+
function clamp(value) {
|
|
10446
|
+
return Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, value));
|
|
10447
|
+
}
|
|
10448
|
+
|
|
10449
|
+
/*
|
|
10450
|
+
* Maps the RDL style vocabulary to CSS. Everything here is report CONTENT: colors, fonts and borders
|
|
10451
|
+
* come from the report definition and are applied as given, not from the design system.
|
|
10452
|
+
*/
|
|
10453
|
+
const RDL_DEFAULT_FONT_FAMILY = "Arial";
|
|
10454
|
+
const RDL_DEFAULT_FONT_SIZE = "10pt";
|
|
10455
|
+
const RDL_DEFAULT_COLOR = "black";
|
|
10456
|
+
const FONT_WEIGHTS = {
|
|
10457
|
+
thin: 100,
|
|
10458
|
+
extralight: 200,
|
|
10459
|
+
light: 300,
|
|
10460
|
+
normal: 400,
|
|
10461
|
+
medium: 500,
|
|
10462
|
+
semibold: 600,
|
|
10463
|
+
bold: 700,
|
|
10464
|
+
extrabold: 800,
|
|
10465
|
+
heavy: 900
|
|
10466
|
+
};
|
|
10467
|
+
const BORDER_STYLES = {
|
|
10468
|
+
solid: "solid",
|
|
10469
|
+
dotted: "dotted",
|
|
10470
|
+
dashed: "dashed",
|
|
10471
|
+
double: "double",
|
|
10472
|
+
none: "none"
|
|
10473
|
+
};
|
|
10474
|
+
/** An RDL color as CSS: "Transparent" = transparent, names and #RRGGBB pass through, empty = undefined. */
|
|
10475
|
+
function rdlColor(value) {
|
|
10476
|
+
if (!value)
|
|
10477
|
+
return undefined;
|
|
10478
|
+
const trimmed = value.trim();
|
|
10479
|
+
if (trimmed === "")
|
|
10480
|
+
return undefined;
|
|
10481
|
+
return trimmed.toLowerCase() === "transparent" ? "transparent" : trimmed;
|
|
10482
|
+
}
|
|
10483
|
+
/** Thin..Heavy to 100..900; unknown values fall back to normal. */
|
|
10484
|
+
function rdlFontWeight(value) {
|
|
10485
|
+
if (!value)
|
|
10486
|
+
return 400;
|
|
10487
|
+
return FONT_WEIGHTS[value.toLowerCase()] ?? (/^\d{3}$/.test(value) ? parseInt(value, 10) : 400);
|
|
10488
|
+
}
|
|
10489
|
+
/** A border side as a CSS shorthand; Style None or missing = none. Width defaults to 1pt, color to black. */
|
|
10490
|
+
function rdlBorder(border, zoom) {
|
|
10491
|
+
if (!border)
|
|
10492
|
+
return undefined;
|
|
10493
|
+
const style = BORDER_STYLES[(border.Style ?? "none").toLowerCase()] ?? "solid";
|
|
10494
|
+
if (style === "none")
|
|
10495
|
+
return "none";
|
|
10496
|
+
const widthPt = parseRdlSize(border.Width) ?? 1;
|
|
10497
|
+
const widthPx = Math.max(1, ptToPx(widthPt, zoom));
|
|
10498
|
+
return `${widthPx}px ${style} ${rdlColor(border.Color) ?? RDL_DEFAULT_COLOR}`;
|
|
10499
|
+
}
|
|
10500
|
+
/**
|
|
10501
|
+
* The four borders of an item box as the custom properties its border overlay draws
|
|
10502
|
+
* (`--rdl-border-top` ... in cw-report-page.module.css). Like the report server, borders sit on
|
|
10503
|
+
* the box edge and take no space from the content, so text laid out by the server to just fit
|
|
10504
|
+
* still fits. Always all four sides, "none" when unset: custom properties inherit, and a nested
|
|
10505
|
+
* item must not draw its container's borders.
|
|
10506
|
+
*/
|
|
10507
|
+
function rdlBorderVars(style, zoom) {
|
|
10508
|
+
return {
|
|
10509
|
+
"--rdl-border-top": rdlBorder(style?.BorderTop, zoom) ?? "none",
|
|
10510
|
+
"--rdl-border-right": rdlBorder(style?.BorderRight, zoom) ?? "none",
|
|
10511
|
+
"--rdl-border-bottom": rdlBorder(style?.BorderBottom, zoom) ?? "none",
|
|
10512
|
+
"--rdl-border-left": rdlBorder(style?.BorderLeft, zoom) ?? "none"
|
|
10513
|
+
};
|
|
10514
|
+
}
|
|
10515
|
+
/** Background, borders (as overlay custom properties, see `rdlBorderVars`) and padding of an item box. */
|
|
10516
|
+
function rdlBoxStyle(style, zoom) {
|
|
10517
|
+
if (!style)
|
|
10518
|
+
return rdlBorderVars(null, zoom);
|
|
10519
|
+
return {
|
|
10520
|
+
...rdlBorderVars(style, zoom),
|
|
10521
|
+
backgroundColor: rdlColor(style.BackgroundColor),
|
|
10522
|
+
paddingTop: rdlSizeToPx(style.PaddingTop, zoom),
|
|
10523
|
+
paddingRight: rdlSizeToPx(style.PaddingRight, zoom),
|
|
10524
|
+
paddingBottom: rdlSizeToPx(style.PaddingBottom, zoom),
|
|
10525
|
+
paddingLeft: rdlSizeToPx(style.PaddingLeft, zoom)
|
|
10526
|
+
};
|
|
10527
|
+
}
|
|
10528
|
+
function pick(key, styles) {
|
|
10529
|
+
for (const style of styles) {
|
|
10530
|
+
const value = style?.[key];
|
|
10531
|
+
if (value)
|
|
10532
|
+
return value;
|
|
10533
|
+
}
|
|
10534
|
+
return null;
|
|
10535
|
+
}
|
|
10536
|
+
function resolveRdlTextStyle(run, paragraph, textbox) {
|
|
10537
|
+
const chain = [run, paragraph, textbox];
|
|
10538
|
+
return {
|
|
10539
|
+
color: pick("Color", chain) ?? RDL_DEFAULT_COLOR,
|
|
10540
|
+
fontFamily: pick("FontFamily", chain) ?? RDL_DEFAULT_FONT_FAMILY,
|
|
10541
|
+
fontSize: pick("FontSize", chain) ?? RDL_DEFAULT_FONT_SIZE,
|
|
10542
|
+
fontStyle: pick("FontStyle", chain),
|
|
10543
|
+
fontWeight: pick("FontWeight", chain),
|
|
10544
|
+
textDecoration: pick("TextDecoration", chain),
|
|
10545
|
+
lineHeight: pick("LineHeight", chain)
|
|
10546
|
+
};
|
|
10547
|
+
}
|
|
10548
|
+
/**
|
|
10549
|
+
* The text style of a paragraph element. Its font sets the strut every line box gets, so it takes
|
|
10550
|
+
* the font of its runs (they usually share one; when they differ, the first run's): a paragraph
|
|
10551
|
+
* left at the textbox default (Arial 10pt) around Segoe UI 8pt runs would make every line taller
|
|
10552
|
+
* than the server measured and the text would overflow its box. An RDL LineHeight on the paragraph
|
|
10553
|
+
* or textbox still wins; otherwise the run's LineHeight, if any, applies.
|
|
10554
|
+
*/
|
|
10555
|
+
function resolveParagraphTextStyle(runStyles, paragraph, textbox) {
|
|
10556
|
+
const own = resolveRdlTextStyle(null, paragraph, textbox);
|
|
10557
|
+
if (runStyles.length === 0)
|
|
10558
|
+
return own;
|
|
10559
|
+
const run = resolveRdlTextStyle(runStyles[0], paragraph, textbox);
|
|
10560
|
+
return {
|
|
10561
|
+
...own,
|
|
10562
|
+
fontFamily: run.fontFamily,
|
|
10563
|
+
fontSize: run.fontSize,
|
|
10564
|
+
fontStyle: run.fontStyle,
|
|
10565
|
+
fontWeight: run.fontWeight,
|
|
10566
|
+
lineHeight: own.lineHeight ?? run.lineHeight
|
|
10567
|
+
};
|
|
10568
|
+
}
|
|
10569
|
+
function textDecoration(value) {
|
|
10570
|
+
switch ((value ?? "").toLowerCase()) {
|
|
10571
|
+
case "underline": return "underline";
|
|
10572
|
+
case "linethrough": return "line-through";
|
|
10573
|
+
case "overline": return "overline";
|
|
10574
|
+
default: return undefined;
|
|
10575
|
+
}
|
|
10576
|
+
}
|
|
10577
|
+
/** An RDL LineHeight in pixels, else "normal"; an unparsable value also falls back to normal. */
|
|
10578
|
+
function rdlLineHeight(value, zoom) {
|
|
10579
|
+
const px = rdlSizeToPx(value, zoom);
|
|
10580
|
+
return px === undefined ? "normal" : `${px}px`;
|
|
10581
|
+
}
|
|
10582
|
+
/**
|
|
10583
|
+
* CSS of a resolved text style at a zoom factor. Without an RDL LineHeight the line height is the
|
|
10584
|
+
* font's natural one ("normal"), the spacing the server measures text with; set inline so no app
|
|
10585
|
+
* or base rule on p / span (125%, 1.15) can shrink the line box and clip large glyphs.
|
|
10586
|
+
*/
|
|
10587
|
+
function rdlTextCss(text, zoom) {
|
|
10588
|
+
return {
|
|
10589
|
+
// Transparent text is invisible, as in Reporting Services (reports use it to hide values).
|
|
10590
|
+
color: rdlColor(text.color),
|
|
10591
|
+
fontFamily: text.fontFamily,
|
|
10592
|
+
fontSize: rdlSizeToPx(text.fontSize, zoom) ?? ptToPx(10, zoom),
|
|
10593
|
+
fontStyle: (text.fontStyle ?? "").toLowerCase() === "italic" ? "italic" : undefined,
|
|
10594
|
+
fontWeight: text.fontWeight ? rdlFontWeight(text.fontWeight) : undefined,
|
|
10595
|
+
textDecoration: textDecoration(text.textDecoration),
|
|
10596
|
+
lineHeight: rdlLineHeight(text.lineHeight, zoom)
|
|
10597
|
+
};
|
|
10598
|
+
}
|
|
10599
|
+
/** TextAlign Left/Center/Right/General (General = left). */
|
|
10600
|
+
function rdlTextAlign(value) {
|
|
10601
|
+
switch ((value ?? "").toLowerCase()) {
|
|
10602
|
+
case "center": return "center";
|
|
10603
|
+
case "right": return "right";
|
|
10604
|
+
case "justify": return "justify";
|
|
10605
|
+
default: return "left";
|
|
10606
|
+
}
|
|
10607
|
+
}
|
|
10608
|
+
/**
|
|
10609
|
+
* VerticalAlign Top/Middle/Bottom as the justify-content of a column flex box. "safe": when the
|
|
10610
|
+
* text is taller than its box it falls back to the top, so the first line is never cut off.
|
|
10611
|
+
*/
|
|
10612
|
+
function rdlVerticalAlign(value) {
|
|
10613
|
+
switch ((value ?? "").toLowerCase()) {
|
|
10614
|
+
case "middle": return "safe center";
|
|
10615
|
+
case "bottom": return "safe flex-end";
|
|
10616
|
+
default: return "flex-start";
|
|
10617
|
+
}
|
|
10618
|
+
}
|
|
10619
|
+
/** The stroke of a line item: BorderTop, else BorderLeft, default 1pt solid black. */
|
|
10620
|
+
function rdlLineStroke(style, zoom) {
|
|
10621
|
+
const border = style?.BorderTop ?? style?.BorderLeft ?? null;
|
|
10622
|
+
const width = Math.max(1, ptToPx(parseRdlSize(border?.Width) ?? 1, zoom));
|
|
10623
|
+
const kind = (border?.Style ?? "solid").toLowerCase();
|
|
10624
|
+
const color = rdlColor(border?.Color) ?? RDL_DEFAULT_COLOR;
|
|
10625
|
+
if (kind === "none")
|
|
10626
|
+
return { color: "transparent", width };
|
|
10627
|
+
if (kind === "dotted")
|
|
10628
|
+
return { color, width, dash: `${width} ${width * 2}` };
|
|
10629
|
+
if (kind === "dashed")
|
|
10630
|
+
return { color, width, dash: `${width * 4} ${width * 2}` };
|
|
10631
|
+
return { color, width };
|
|
10632
|
+
}
|
|
10633
|
+
|
|
10634
|
+
/*
|
|
10635
|
+
* Pure chart math for the report charts. The hex values below are the report's chart palettes:
|
|
10636
|
+
* content colors (like the colors a report carries), not UI chrome, so no design tokens.
|
|
10637
|
+
*/
|
|
10638
|
+
/**
|
|
10639
|
+
* The Microsoft chart palettes by name, with the exact colors of the old ReportViewer engine.
|
|
10640
|
+
* The engine's semi-transparent palettes are #AARRGGBB; here they are CSS #RRGGBBAA.
|
|
10641
|
+
*/
|
|
10642
|
+
const PALETTES = {
|
|
10643
|
+
Default: [
|
|
10644
|
+
"#008000", "#0000FF", "#800080", "#00FF00", "#FF00FF", "#008080", "#FFFF00", "#808080",
|
|
10645
|
+
"#00FFFF", "#000080", "#800000", "#FF0000", "#808000", "#C0C0C0", "#FF6347", "#FFE4B5"
|
|
10646
|
+
],
|
|
10647
|
+
Grayscale: [
|
|
10648
|
+
"#C8C8C8", "#BDBDBD", "#B2B2B2", "#A7A7A7", "#9C9C9C", "#919191", "#868686", "#7B7B7B",
|
|
10649
|
+
"#707070", "#656565", "#5A5A5A", "#4F4F4F", "#444444", "#393939", "#2E2E2E", "#232323"
|
|
10650
|
+
],
|
|
10651
|
+
Excel: [
|
|
10652
|
+
"#9999FF", "#993366", "#FFFFCC", "#CCFFFF", "#660066", "#FF8080", "#0066CC", "#CCCCFF",
|
|
10653
|
+
"#000080", "#FF00FF", "#FFFF00", "#00FFFF", "#800080", "#800000", "#008080", "#0000FF"
|
|
10654
|
+
],
|
|
10655
|
+
Light: ["#E6E6FA", "#FFF0F5", "#FFDAB9", "#FFFACD", "#FFE4E1", "#F0FFF0", "#F0F8FF", "#F5F5F5", "#FAEBD7", "#E0FFFF"],
|
|
10656
|
+
Pastel: [
|
|
10657
|
+
"#87CEEB", "#32CD32", "#BA55D3", "#F08080", "#4682B4", "#9ACD32", "#40E0D0", "#FF69B4",
|
|
10658
|
+
"#F0E68C", "#D2B48C", "#8FBC8B", "#6495ED", "#DDA0DD", "#5F9EA0", "#FFDAB9", "#FFA07A"
|
|
10659
|
+
],
|
|
10660
|
+
EarthTones: [
|
|
10661
|
+
"#FF8000", "#B8860B", "#C04000", "#6B8E23", "#CD853F", "#C0C000", "#228B22", "#D2691E",
|
|
10662
|
+
"#808000", "#20B2AA", "#F4A460", "#00C000", "#8FBC8B", "#B22222", "#8B4513", "#C00000"
|
|
10663
|
+
],
|
|
10664
|
+
SemiTransparent: [
|
|
10665
|
+
"#FF000096", "#00FF0096", "#0000FF96", "#FFFF0096", "#00FFFF96", "#FF00FF96", "#AA781496", "#FF000050",
|
|
10666
|
+
"#00FF0050", "#0000FF50", "#FFFF0050", "#00FFFF50", "#FF00FF50", "#AA781450", "#64783296", "#285A9696"
|
|
10667
|
+
],
|
|
10668
|
+
Berry: ["#8A2BE2", "#BA55D3", "#4169E1", "#C71585", "#0000FF", "#8A2BE2", "#DA70D6", "#7B68EE", "#C000C0", "#0000CD", "#800080"],
|
|
10669
|
+
Chocolate: ["#A0522D", "#D2691E", "#8B0000", "#CD853F", "#A52A2A", "#F4A460", "#8B4513", "#C04000", "#B22222", "#B65C3A"],
|
|
10670
|
+
Fire: ["#FFD700", "#FF0000", "#FF1493", "#DC143C", "#FF8C00", "#FF00FF", "#FFFF00", "#FF4500", "#C71585", "#DDE221"],
|
|
10671
|
+
SeaGreen: ["#2E8B57", "#66CDAA", "#4682B4", "#008B8B", "#5F9EA0", "#3CB371", "#48D1CC", "#B0C4DE", "#8FBC8B", "#87CEEB"],
|
|
10672
|
+
BrightPastel: [
|
|
10673
|
+
"#418CF0", "#FCB441", "#E0400A", "#056492", "#BFBFBF", "#1A3B69", "#FFE382", "#129CDD",
|
|
10674
|
+
"#CA6B4B", "#005CDB", "#F3D288", "#506381", "#F1B9A8", "#E0830A", "#7893BE"
|
|
10675
|
+
],
|
|
10676
|
+
Pacific: [
|
|
10677
|
+
"#01B8AA", "#374649", "#FD625E", "#F2C80F", "#5F6B6D", "#8AD4EB", "#FE9666", "#A66999", "#3599B8", "#DFBFBF",
|
|
10678
|
+
"#4AC5BB", "#5F6B6D", "#FB8281", "#F4D25A", "#7F898A", "#A4DDEE", "#FDAB89", "#B687AC", "#28738A", "#A78F8F"
|
|
10679
|
+
],
|
|
10680
|
+
PacificLight: ["#4AC5BB", "#5F6B6D", "#FB8281", "#F4D25A", "#7F898A", "#A4DDEE", "#FDAB89", "#B687AC", "#28738A", "#A78F8F"],
|
|
10681
|
+
PacificSemiTransparent: [
|
|
10682
|
+
"#01B8AA99", "#37464999", "#FD625E99", "#F2C80F99", "#5F6B6D99", "#8AD4EB99", "#FE966699", "#A6699999", "#3599B899", "#DFBFBF99",
|
|
10683
|
+
"#4AC5BB99", "#5F6B6D99", "#FB828199", "#F4D25A99", "#7F898A99", "#A4DDEE99", "#FDAB8999", "#B687AC99", "#28738A99", "#A78F8F99"
|
|
10684
|
+
]
|
|
10685
|
+
};
|
|
10686
|
+
const PALETTES_BY_NAME = new Map(Object.entries(PALETTES).map(([name, colors]) => [name.toLowerCase(), colors]));
|
|
10687
|
+
/** The palette for an RDL palette name (any case); a missing or unknown name is "Default", the RDL default. */
|
|
10688
|
+
function chartPalette(name) {
|
|
10689
|
+
return PALETTES_BY_NAME.get((name ?? "").trim().toLowerCase()) ?? PALETTES.Default;
|
|
10690
|
+
}
|
|
10691
|
+
/** The color of series `index`: its own color, else the palette entry. */
|
|
10692
|
+
function seriesColor(series, index, palette) {
|
|
10693
|
+
return series.Color && series.Color.toLowerCase() !== "automatic" ? series.Color : palette[index % palette.length];
|
|
10694
|
+
}
|
|
10695
|
+
/** Steps of a value axis: about five, rounded to 1, 2, 5 or 10 times a power of ten. */
|
|
10696
|
+
const VALUE_AXIS_STEPS = 5;
|
|
10697
|
+
/** Drops the binary noise of step multiples (0.6000000000000001 -> 0.6). */
|
|
10698
|
+
function tidy$1(value) {
|
|
10699
|
+
return Number(value.toPrecision(12));
|
|
10700
|
+
}
|
|
10701
|
+
/**
|
|
10702
|
+
* bound / step, snapped to the nearest integer only when it is within a few ulps of it: 0.07 / 0.01 is
|
|
10703
|
+
* 7.000000000000001 in binary floating point and would otherwise add an axis step. Genuine non-multiples
|
|
10704
|
+
* (0.37 / 0.05) keep their fraction so the bounds still round outwards.
|
|
10705
|
+
*/
|
|
10706
|
+
function axisQuotient(bound, step) {
|
|
10707
|
+
const quotient = bound / step;
|
|
10708
|
+
const nearest = Math.round(quotient);
|
|
10709
|
+
const reconstructed = nearest * step;
|
|
10710
|
+
const tolerance = Number.EPSILON * 4 * Math.max(1, Math.abs(bound), Math.abs(reconstructed));
|
|
10711
|
+
return Math.abs(reconstructed - bound) <= tolerance ? nearest : quotient;
|
|
10712
|
+
}
|
|
10713
|
+
/** The nice step for a value range (> 0): range / 5 rounded to 1, 2, 5 or 10 times its magnitude. */
|
|
10714
|
+
function niceAxisStep(range) {
|
|
10715
|
+
const raw = range / VALUE_AXIS_STEPS;
|
|
10716
|
+
const magnitude = Math.pow(10, Math.floor(Math.log10(raw)));
|
|
10717
|
+
const residual = raw / magnitude;
|
|
10718
|
+
const nice = residual < 1.5 ? 1 : residual < 3.5 ? 2 : residual < 7.5 ? 5 : 10;
|
|
10719
|
+
return tidy$1(nice * magnitude);
|
|
10720
|
+
}
|
|
10721
|
+
/**
|
|
10722
|
+
* A value axis over the given values, the same rule as the server's PDF: the range always includes
|
|
10723
|
+
* 0, nulls are ignored, the step is `niceAxisStep(range)` and the bounds are rounded out to whole
|
|
10724
|
+
* steps (data max 113 gives 0..120 in steps of 20). All-null or all-zero data gives 0..1.
|
|
10725
|
+
*/
|
|
10726
|
+
function computeValueScale(values) {
|
|
10727
|
+
let min = 0;
|
|
10728
|
+
let max = 0;
|
|
10729
|
+
for (const value of values) {
|
|
10730
|
+
if (value === null || value === undefined || !Number.isFinite(value))
|
|
10731
|
+
continue;
|
|
10732
|
+
if (value < min)
|
|
10733
|
+
min = value;
|
|
10734
|
+
if (value > max)
|
|
10735
|
+
max = value;
|
|
10736
|
+
}
|
|
10737
|
+
if (min === max)
|
|
10738
|
+
max = min + 1;
|
|
10739
|
+
const step = niceAxisStep(max - min);
|
|
10740
|
+
const first = Math.floor(axisQuotient(min, step));
|
|
10741
|
+
const last = Math.ceil(axisQuotient(max, step));
|
|
10742
|
+
const ticks = [];
|
|
10743
|
+
for (let index = first; index <= last; index++)
|
|
10744
|
+
ticks.push(tidy$1(index * step));
|
|
10745
|
+
return { min: tidy$1(first * step), max: tidy$1(last * step), ticks };
|
|
10746
|
+
}
|
|
10747
|
+
/**
|
|
10748
|
+
* The server's category axis labels (`RdlRenderChart.CategoryAxisLabels`) along an axis that starts
|
|
10749
|
+
* at `start` with `band` pixels per category: position 0 is the first category's point (the middle
|
|
10750
|
+
* of its band), fractions fall between two categories. Labels outside the categories are dropped.
|
|
10751
|
+
*/
|
|
10752
|
+
function categoryAxisLabels(labels, categoryCount, start, band) {
|
|
10753
|
+
return labels
|
|
10754
|
+
.filter(label => Number.isFinite(label.Position) && label.Position >= -0.5 && label.Position <= categoryCount - 0.5)
|
|
10755
|
+
.map(label => ({ at: start + band * (label.Position + 0.5), text: label.Text }));
|
|
10756
|
+
}
|
|
10757
|
+
/**
|
|
10758
|
+
* Centers a label of `width` on `center`, moved inside [min, max] when it would cross an end: a
|
|
10759
|
+
* category label never runs past the value axis (and its origin label) or the plot's right edge.
|
|
10760
|
+
*/
|
|
10761
|
+
function placeAxisLabel(center, width, min, max) {
|
|
10762
|
+
if (center - width / 2 < min)
|
|
10763
|
+
return { x: min, anchor: "start" };
|
|
10764
|
+
if (center + width / 2 > max)
|
|
10765
|
+
return { x: max, anchor: "end" };
|
|
10766
|
+
return { x: center, anchor: "middle" };
|
|
10767
|
+
}
|
|
10768
|
+
/** Maps a value of the scale to a pixel coordinate between `from` (scale min) and `to` (scale max). */
|
|
10769
|
+
function scaleValue(value, scale, from, to) {
|
|
10770
|
+
const span = scale.max - scale.min;
|
|
10771
|
+
if (span === 0)
|
|
10772
|
+
return from;
|
|
10773
|
+
return from + ((value - scale.min) / span) * (to - from);
|
|
10774
|
+
}
|
|
10775
|
+
/** Splits a series into runs of consecutive non-null points: a null value leaves a gap in the line. */
|
|
10776
|
+
function lineSegments(values, x, y) {
|
|
10777
|
+
const segments = [];
|
|
10778
|
+
let current = [];
|
|
10779
|
+
values.forEach((value, index) => {
|
|
10780
|
+
if (value === null || value === undefined || !Number.isFinite(value)) {
|
|
10781
|
+
if (current.length > 0)
|
|
10782
|
+
segments.push(current);
|
|
10783
|
+
current = [];
|
|
10784
|
+
return;
|
|
10785
|
+
}
|
|
10786
|
+
current.push({ index, value, x: x(index), y: y(value) });
|
|
10787
|
+
});
|
|
10788
|
+
if (current.length > 0)
|
|
10789
|
+
segments.push(current);
|
|
10790
|
+
return segments;
|
|
10791
|
+
}
|
|
10792
|
+
/** SVG path of one segment: straight, stepped (horizontal then vertical) or smooth (Catmull-Rom as cubic Beziers). */
|
|
10793
|
+
function segmentPath(points, subtype) {
|
|
10794
|
+
if (points.length === 0)
|
|
10795
|
+
return "";
|
|
10796
|
+
const kind = (subtype ?? "").toLowerCase();
|
|
10797
|
+
let path = `M${points[0].x},${points[0].y}`;
|
|
10798
|
+
for (let i = 1; i < points.length; i++) {
|
|
10799
|
+
const prev = points[i - 1];
|
|
10800
|
+
const point = points[i];
|
|
10801
|
+
if (kind === "stepped") {
|
|
10802
|
+
path += ` H${point.x} V${point.y}`;
|
|
10803
|
+
}
|
|
10804
|
+
else if (kind === "smooth") {
|
|
10805
|
+
const before = points[i - 2] ?? prev;
|
|
10806
|
+
const after = points[i + 1] ?? point;
|
|
10807
|
+
const c1x = prev.x + (point.x - before.x) / 6;
|
|
10808
|
+
const c1y = prev.y + (point.y - before.y) / 6;
|
|
10809
|
+
const c2x = point.x - (after.x - prev.x) / 6;
|
|
10810
|
+
const c2y = point.y - (after.y - prev.y) / 6;
|
|
10811
|
+
path += ` C${c1x},${c1y} ${c2x},${c2y} ${point.x},${point.y}`;
|
|
10812
|
+
}
|
|
10813
|
+
else {
|
|
10814
|
+
path += ` L${point.x},${point.y}`;
|
|
10815
|
+
}
|
|
10816
|
+
}
|
|
10817
|
+
return path;
|
|
10818
|
+
}
|
|
10819
|
+
/** Pie slices of positive values (null, zero and negative values get no slice); angles in radians from 12 o'clock, clockwise. */
|
|
10820
|
+
function pieSlices(values) {
|
|
10821
|
+
const total = values.reduce((acc, value) => acc + (value !== null && value > 0 ? value : 0), 0);
|
|
10822
|
+
if (total <= 0)
|
|
10823
|
+
return [];
|
|
10824
|
+
const slices = [];
|
|
10825
|
+
let angle = 0;
|
|
10826
|
+
values.forEach((value, index) => {
|
|
10827
|
+
if (value === null || value <= 0)
|
|
10828
|
+
return;
|
|
10829
|
+
const fraction = value / total;
|
|
10830
|
+
const end = angle + fraction * Math.PI * 2;
|
|
10831
|
+
slices.push({ index, value, fraction, startAngle: angle, endAngle: end });
|
|
10832
|
+
angle = end;
|
|
10833
|
+
});
|
|
10834
|
+
return slices;
|
|
10835
|
+
}
|
|
10836
|
+
/** A point on a circle; angle in radians from 12 o'clock, clockwise. */
|
|
10837
|
+
function polar(cx, cy, radius, angle) {
|
|
10838
|
+
return { x: cx + radius * Math.sin(angle), y: cy - radius * Math.cos(angle) };
|
|
10839
|
+
}
|
|
10840
|
+
/** SVG path of an arc band (or a pie wedge when innerRadius is 0) between two angles. */
|
|
10841
|
+
function arcPath(cx, cy, outerRadius, innerRadius, start, end) {
|
|
10842
|
+
const sweep = end - start;
|
|
10843
|
+
if (sweep >= Math.PI * 2 - 1e-9) {
|
|
10844
|
+
const mid = start + Math.PI;
|
|
10845
|
+
return arcPath(cx, cy, outerRadius, innerRadius, start, mid) + " " + arcPath(cx, cy, outerRadius, innerRadius, mid, end);
|
|
10846
|
+
}
|
|
10847
|
+
const large = sweep > Math.PI ? 1 : 0;
|
|
10848
|
+
const o1 = polar(cx, cy, outerRadius, start);
|
|
10849
|
+
const o2 = polar(cx, cy, outerRadius, end);
|
|
10850
|
+
if (innerRadius <= 0) {
|
|
10851
|
+
return `M${cx},${cy} L${o1.x},${o1.y} A${outerRadius},${outerRadius} 0 ${large} 1 ${o2.x},${o2.y} Z`;
|
|
10852
|
+
}
|
|
10853
|
+
const i2 = polar(cx, cy, innerRadius, end);
|
|
10854
|
+
const i1 = polar(cx, cy, innerRadius, start);
|
|
10855
|
+
return `M${o1.x},${o1.y} A${outerRadius},${outerRadius} 0 ${large} 1 ${o2.x},${o2.y} L${i2.x},${i2.y} A${innerRadius},${innerRadius} 0 ${large} 0 ${i1.x},${i1.y} Z`;
|
|
10856
|
+
}
|
|
10857
|
+
/** Clamps a gauge value into the scale; null stays at the minimum. */
|
|
10858
|
+
function clampGaugeValue(value, minimum, maximum) {
|
|
10859
|
+
if (value === null || value === undefined || !Number.isFinite(value))
|
|
10860
|
+
return minimum;
|
|
10861
|
+
const low = Math.min(minimum, maximum);
|
|
10862
|
+
const high = Math.max(minimum, maximum);
|
|
10863
|
+
return Math.min(high, Math.max(low, value));
|
|
10864
|
+
}
|
|
10865
|
+
/** Formats an axis or data value compactly: integers without decimals, else up to 2 decimals. */
|
|
10866
|
+
function formatChartValue(value) {
|
|
10867
|
+
if (Number.isInteger(value))
|
|
10868
|
+
return value.toString();
|
|
10869
|
+
return (Math.round(value * 100) / 100).toString();
|
|
10870
|
+
}
|
|
10871
|
+
|
|
10872
|
+
/** More ticks than this on one scale is a broken interval, not a scale anyone can read. */
|
|
10873
|
+
const MAX_GAUGE_TICKS = 200;
|
|
10874
|
+
/**
|
|
10875
|
+
* The ellipse a state indicator fills: its percent box of the panel, stretched to an oval in a box
|
|
10876
|
+
* that is not square, like the old viewer. Null when the active state has no color or the box is empty.
|
|
10877
|
+
*/
|
|
10878
|
+
function stateIndicatorEllipse(indicator, panelWidth, panelHeight) {
|
|
10879
|
+
if (!indicator.Color)
|
|
10880
|
+
return null;
|
|
10881
|
+
const width = (Math.max(0, indicator.Width) / 100) * panelWidth;
|
|
10882
|
+
const height = (Math.max(0, indicator.Height) / 100) * panelHeight;
|
|
10883
|
+
if (width <= 0 || height <= 0)
|
|
10884
|
+
return null;
|
|
10885
|
+
const left = (indicator.Left / 100) * panelWidth;
|
|
10886
|
+
const top = (indicator.Top / 100) * panelHeight;
|
|
10887
|
+
return { cx: left + width / 2, cy: top + height / 2, rx: width / 2, ry: height / 2 };
|
|
10888
|
+
}
|
|
10889
|
+
/** The value span a Bar pointer covers: from the scale minimum to the value clamped into the scale. */
|
|
10890
|
+
function gaugeBarSpan(value, minimum, maximum) {
|
|
10891
|
+
return { from: minimum, to: clampGaugeValue(value, minimum, maximum) };
|
|
10892
|
+
}
|
|
10893
|
+
function tidy(value) {
|
|
10894
|
+
return Number(value.toPrecision(12));
|
|
10895
|
+
}
|
|
10896
|
+
/**
|
|
10897
|
+
* The major tick values of a gauge scale: every `interval` from the minimum up to the maximum (the
|
|
10898
|
+
* server computes it); without a usable interval the chart axis' nice step of the range is used.
|
|
10899
|
+
*/
|
|
10900
|
+
function gaugeTicks(minimum, maximum, interval) {
|
|
10901
|
+
const low = Math.min(minimum, maximum);
|
|
10902
|
+
const high = Math.max(minimum, maximum);
|
|
10903
|
+
if (!Number.isFinite(low) || !Number.isFinite(high))
|
|
10904
|
+
return [];
|
|
10905
|
+
if (high === low)
|
|
10906
|
+
return [tidy(low)];
|
|
10907
|
+
const step = interval !== null && interval !== undefined && Number.isFinite(interval) && interval > 0
|
|
10908
|
+
? interval
|
|
10909
|
+
: niceAxisStep(high - low);
|
|
10910
|
+
const count = Math.floor(tidy((high - low) / step));
|
|
10911
|
+
if (count > MAX_GAUGE_TICKS)
|
|
10912
|
+
return [tidy(low), tidy(high)];
|
|
10913
|
+
const ticks = [];
|
|
10914
|
+
for (let index = 0; index <= count; index++)
|
|
10915
|
+
ticks.push(tidy(low + index * step));
|
|
10916
|
+
return ticks;
|
|
10917
|
+
}
|
|
10918
|
+
/**
|
|
10919
|
+
* A thickness given in percent of the gauge's own size (for a horizontal linear gauge its height, as
|
|
10920
|
+
* the chart engine measures range and pointer widths), or the default fraction when it is missing.
|
|
10921
|
+
*/
|
|
10922
|
+
function gaugeThickness(percent, gaugeSize, defaultFraction) {
|
|
10923
|
+
const fraction = percent !== null && percent !== undefined && Number.isFinite(percent) && percent > 0 ? percent / 100 : defaultFraction;
|
|
10924
|
+
return gaugeSize * Math.min(1, fraction);
|
|
10925
|
+
}
|
|
10926
|
+
/** Cross (or none) is centred on the scale line, Inside lies above it, Outside below it. */
|
|
10927
|
+
function gaugeBandExtent(placement, thickness) {
|
|
10928
|
+
switch ((placement ?? "").toLowerCase()) {
|
|
10929
|
+
case "inside": return { up: thickness, down: 0 };
|
|
10930
|
+
case "outside": return { up: 0, down: thickness };
|
|
10931
|
+
default: return { up: thickness / 2, down: thickness / 2 };
|
|
10932
|
+
}
|
|
10933
|
+
}
|
|
10934
|
+
/**
|
|
10935
|
+
* The y of the scale line of a horizontal linear gauge `height` tall: the bands (ranges and pointers)
|
|
10936
|
+
* and the tick labels below them (`labelSpace`) are centred as one block.
|
|
10937
|
+
*/
|
|
10938
|
+
function linearGaugeScaleY(height, bands, labelSpace) {
|
|
10939
|
+
const up = Math.max(0, ...bands.map(band => band.up));
|
|
10940
|
+
const down = Math.max(0, ...bands.map(band => band.down));
|
|
10941
|
+
return Math.max(up, (height - (up + down + labelSpace)) / 2 + up);
|
|
10942
|
+
}
|
|
10943
|
+
|
|
10944
|
+
/** The order an "Auto" marker takes per series, as the chart engine cycles it. */
|
|
10945
|
+
const AUTO_SHAPES = ["square", "circle", "diamond", "triangle", "cross", "star4", "star5", "star6", "star10"];
|
|
10946
|
+
const SHAPES = {
|
|
10947
|
+
square: "square",
|
|
10948
|
+
circle: "circle",
|
|
10949
|
+
diamond: "diamond",
|
|
10950
|
+
triangle: "triangle",
|
|
10951
|
+
cross: "cross",
|
|
10952
|
+
star4: "star4",
|
|
10953
|
+
star5: "star5",
|
|
10954
|
+
star6: "star6",
|
|
10955
|
+
star10: "star10"
|
|
10956
|
+
};
|
|
10957
|
+
/** The marker size when the series sets none, as the server uses it. */
|
|
10958
|
+
const RDL_DEFAULT_MARKER_SIZE_PT = 5;
|
|
10959
|
+
/**
|
|
10960
|
+
* The marker a series draws, or null for none. The server resolves Auto to a shape, so a missing type
|
|
10961
|
+
* or None means no marker. Should an Auto (or an unknown name) still arrive, a scatter series cycles
|
|
10962
|
+
* the shapes per series, so its points stay visible; a line series then draws none.
|
|
10963
|
+
*/
|
|
10964
|
+
function seriesMarker(markerType, scatter, seriesIndex) {
|
|
10965
|
+
if (!markerType)
|
|
10966
|
+
return null;
|
|
10967
|
+
const name = markerType.trim().toLowerCase();
|
|
10968
|
+
if (name === "none" || name === "")
|
|
10969
|
+
return null;
|
|
10970
|
+
const shape = SHAPES[name];
|
|
10971
|
+
if (shape)
|
|
10972
|
+
return shape;
|
|
10973
|
+
return scatter ? AUTO_SHAPES[seriesIndex % AUTO_SHAPES.length] : null;
|
|
10974
|
+
}
|
|
10975
|
+
/** The marker size in pixels at a zoom (RDL size, default 5pt), at least 3 px so it stays visible. */
|
|
10976
|
+
function markerSizePx(markerSize, zoom) {
|
|
10977
|
+
return Math.max(3, ptToPx(parseRdlSize(markerSize) ?? RDL_DEFAULT_MARKER_SIZE_PT, zoom));
|
|
10978
|
+
}
|
|
10979
|
+
function round(value) {
|
|
10980
|
+
return Math.round(value * 100) / 100;
|
|
10981
|
+
}
|
|
10982
|
+
function polygon(points) {
|
|
10983
|
+
return `M${points.map(([x, y]) => `${round(x)},${round(y)}`).join(" L")} Z`;
|
|
10984
|
+
}
|
|
10985
|
+
function star(cx, cy, outer, points) {
|
|
10986
|
+
const inner = outer * (points <= 4 ? 0.35 : 0.45);
|
|
10987
|
+
const corners = [];
|
|
10988
|
+
for (let index = 0; index < points * 2; index++) {
|
|
10989
|
+
const radius = index % 2 === 0 ? outer : inner;
|
|
10990
|
+
const angle = -Math.PI / 2 + (index * Math.PI) / points;
|
|
10991
|
+
corners.push([cx + radius * Math.cos(angle), cy + radius * Math.sin(angle)]);
|
|
10992
|
+
}
|
|
10993
|
+
return polygon(corners);
|
|
10994
|
+
}
|
|
10995
|
+
/** SVG path of a marker of `size` (its width; an X has arms of that length along its diagonals) centered on (cx, cy); every shape is filled. */
|
|
10996
|
+
function markerPath(shape, cx, cy, size) {
|
|
10997
|
+
const r = size / 2;
|
|
10998
|
+
switch (shape) {
|
|
10999
|
+
case "square":
|
|
11000
|
+
return polygon([[cx - r, cy - r], [cx + r, cy - r], [cx + r, cy + r], [cx - r, cy + r]]);
|
|
11001
|
+
case "circle":
|
|
11002
|
+
return `M${round(cx - r)},${round(cy)} A${round(r)},${round(r)} 0 1 0 ${round(cx + r)},${round(cy)} A${round(r)},${round(r)} 0 1 0 ${round(cx - r)},${round(cy)} Z`;
|
|
11003
|
+
case "diamond":
|
|
11004
|
+
return polygon([[cx, cy - r], [cx + r, cy], [cx, cy + r], [cx - r, cy]]);
|
|
11005
|
+
case "triangle":
|
|
11006
|
+
return polygon([[cx, cy - r], [cx + r, cy + r], [cx - r, cy + r]]);
|
|
11007
|
+
case "cross": {
|
|
11008
|
+
// The chart engine's Cross is an X: a filled plus sign, its arms a third of the size thick, turned by 45 degrees.
|
|
11009
|
+
const t = size / 6;
|
|
11010
|
+
const plus = [
|
|
11011
|
+
[-t, -r], [t, -r], [t, -t], [r, -t], [r, t], [t, t],
|
|
11012
|
+
[t, r], [-t, r], [-t, t], [-r, t], [-r, -t], [-t, -t]
|
|
11013
|
+
];
|
|
11014
|
+
const turn = Math.SQRT1_2;
|
|
11015
|
+
return polygon(plus.map(([dx, dy]) => [cx + (dx - dy) * turn, cy + (dx + dy) * turn]));
|
|
11016
|
+
}
|
|
11017
|
+
case "star4": return star(cx, cy, r, 4);
|
|
11018
|
+
case "star5": return star(cx, cy, r, 5);
|
|
11019
|
+
case "star6": return star(cx, cy, r, 6);
|
|
11020
|
+
case "star10": return star(cx, cy, r, 10);
|
|
11021
|
+
}
|
|
11022
|
+
}
|
|
11023
|
+
const ALIGN = {
|
|
11024
|
+
left: "start",
|
|
11025
|
+
top: "start",
|
|
11026
|
+
center: "center",
|
|
11027
|
+
right: "end",
|
|
11028
|
+
bottom: "end"
|
|
11029
|
+
};
|
|
11030
|
+
/** The RDL default legend position: a column on the right, vertically centered (RightCenter). */
|
|
11031
|
+
const DEFAULT_LEGEND = { edge: "right", align: "center" };
|
|
11032
|
+
/**
|
|
11033
|
+
* The RDL ChartLegend Position as an edge and an alignment: the first word is the edge, the second
|
|
11034
|
+
* the alignment along it (TopLeft = top edge, left aligned; LeftTop = left edge, top aligned).
|
|
11035
|
+
* Missing or unknown = RightCenter, the RDL default.
|
|
11036
|
+
*/
|
|
11037
|
+
function legendPlacement(position) {
|
|
11038
|
+
const match = /^(top|bottom|left|right)(left|right|top|bottom|center)$/i.exec((position ?? "").trim());
|
|
11039
|
+
if (!match)
|
|
11040
|
+
return DEFAULT_LEGEND;
|
|
11041
|
+
const edge = match[1].toLowerCase();
|
|
11042
|
+
const along = match[2].toLowerCase();
|
|
11043
|
+
const horizontalEdge = edge === "top" || edge === "bottom";
|
|
11044
|
+
// "TopTop" or "LeftLeft" make no sense: the alignment must run along the edge.
|
|
11045
|
+
const valid = horizontalEdge ? ["left", "center", "right"].includes(along) : ["top", "center", "bottom"].includes(along);
|
|
11046
|
+
return { edge, align: valid ? ALIGN[along] : "center" };
|
|
11047
|
+
}
|
|
11048
|
+
|
|
11049
|
+
var styles$2 = {"chart":"cw-report-chart-module__chart__HEwvf","title":"cw-report-chart-module__title__zvCfx","axis":"cw-report-chart-module__axis__qwHaL","grid":"cw-report-chart-module__grid__xJMG8","tick":"cw-report-chart-module__tick__uQJeQ","muted":"cw-report-chart-module__muted__Nj1-j","hit":"cw-report-chart-module__hit__-u6vf","point":"cw-report-chart-module__point__M-GuI","slice":"cw-report-chart-module__slice__DEbOB","track":"cw-report-chart-module__track__NAVBX","needle":"cw-report-chart-module__needle__m5fJK","cap":"cw-report-chart-module__cap__zL7KT"};
|
|
11050
|
+
|
|
11051
|
+
/** Chart text size in points (scaled with the zoom). */
|
|
11052
|
+
const CHART_FONT_PT = 8;
|
|
11053
|
+
/** Rough width of one character of the chart font, as a share of the font size. */
|
|
11054
|
+
const CHAR_WIDTH = 0.6;
|
|
11055
|
+
function chartFontPx(zoom) {
|
|
11056
|
+
return ptToPx(CHART_FONT_PT, zoom);
|
|
11057
|
+
}
|
|
11058
|
+
function estimateTextWidth(text, fontPx) {
|
|
11059
|
+
return text.length * fontPx * CHAR_WIDTH;
|
|
11060
|
+
}
|
|
11061
|
+
/** Hover handlers that show the shared CwTooltipManager tooltip over an SVG element. */
|
|
11062
|
+
function useChartTooltip() {
|
|
11063
|
+
React.useEffect(() => {
|
|
11064
|
+
ensureTooltipManager();
|
|
11065
|
+
}, []);
|
|
11066
|
+
const show = React.useCallback((content) => (event) => {
|
|
11067
|
+
showTooltip(content, event.currentTarget, "top");
|
|
11068
|
+
}, []);
|
|
11069
|
+
const hide = React.useCallback(() => hideTooltip(), []);
|
|
11070
|
+
return { show, hide };
|
|
11071
|
+
}
|
|
11072
|
+
function tooltipContent(title, label, value) {
|
|
11073
|
+
return (jsxRuntime.jsxs("span", { children: [jsxRuntime.jsx("strong", { children: title }), jsxRuntime.jsx("br", {}), label, ": ", value] }));
|
|
11074
|
+
}
|
|
11075
|
+
/** Lays legend entries out in rows that wrap at `width`, each row aligned within `width` (centered by default). */
|
|
11076
|
+
function layoutLegend(entries, width, fontPx, align = "center") {
|
|
11077
|
+
if (entries.length === 0)
|
|
11078
|
+
return { height: 0, width: 0, items: [] };
|
|
11079
|
+
const swatch = fontPx;
|
|
11080
|
+
const gap = fontPx;
|
|
11081
|
+
const lineHeight = fontPx * 1.6;
|
|
11082
|
+
const rows = [[]];
|
|
11083
|
+
let rowWidth = 0;
|
|
11084
|
+
for (const entry of entries) {
|
|
11085
|
+
const itemWidth = swatch + fontPx * 0.4 + estimateTextWidth(entry.label, fontPx);
|
|
11086
|
+
if (rowWidth > 0 && rowWidth + gap + itemWidth > width) {
|
|
11087
|
+
rows.push([]);
|
|
11088
|
+
rowWidth = 0;
|
|
11089
|
+
}
|
|
11090
|
+
rowWidth += (rowWidth > 0 ? gap : 0) + itemWidth;
|
|
11091
|
+
rows[rows.length - 1].push({ entry, width: itemWidth });
|
|
11092
|
+
}
|
|
11093
|
+
const items = [];
|
|
11094
|
+
let widest = 0;
|
|
11095
|
+
rows.forEach((row, rowIndex) => {
|
|
11096
|
+
const total = row.reduce((acc, item) => acc + item.width, 0) + gap * (row.length - 1);
|
|
11097
|
+
widest = Math.max(widest, total);
|
|
11098
|
+
const free = Math.max(0, width - total);
|
|
11099
|
+
let x = align === "start" ? 0 : align === "end" ? free : free / 2;
|
|
11100
|
+
for (const item of row) {
|
|
11101
|
+
items.push({ entry: item.entry, x, y: rowIndex * lineHeight + lineHeight / 2 });
|
|
11102
|
+
x += item.width + gap;
|
|
11103
|
+
}
|
|
11104
|
+
});
|
|
11105
|
+
return { height: rows.length * lineHeight, width: widest, items };
|
|
11106
|
+
}
|
|
11107
|
+
function ChartLegend({ layout, top, left = 0, fontPx }) {
|
|
11108
|
+
return (jsxRuntime.jsx("g", { transform: `translate(${left}, ${top})`, children: layout.items.map((item, index) => (jsxRuntime.jsxs("g", { transform: `translate(${item.x}, ${item.y})`, children: [item.entry.marker
|
|
11109
|
+
? jsxRuntime.jsx("path", { d: markerPath(item.entry.marker, fontPx / 2, 0, fontPx), fill: item.entry.color })
|
|
11110
|
+
: jsxRuntime.jsx("rect", { x: 0, y: -fontPx / 2, width: fontPx, height: fontPx, fill: item.entry.color }), jsxRuntime.jsx("text", { x: fontPx * 1.4, dominantBaseline: "central", fontSize: fontPx, children: item.entry.label })] }, index))) }));
|
|
11111
|
+
}
|
|
11112
|
+
/** The chart titles, one line each, centered. Returns the height they take. */
|
|
11113
|
+
function ChartTitles({ titles, width, fontPx }) {
|
|
11114
|
+
return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: titles.map((title, index) => (jsxRuntime.jsx("text", { className: styles$2.title, x: width / 2, y: fontPx * 1.5 * (index + 1), textAnchor: "middle", fontSize: fontPx * 1.25, children: title }, index))) }));
|
|
11115
|
+
}
|
|
11116
|
+
function titlesHeight(titles, fontPx) {
|
|
11117
|
+
return titles.length === 0 ? 0 : fontPx * 1.5 * titles.length + fontPx * 0.5;
|
|
11118
|
+
}
|
|
11119
|
+
/** A centered message instead of the plot (dataset error or no rows). */
|
|
11120
|
+
function ChartMessage({ text, width, height, fontPx }) {
|
|
11121
|
+
return (jsxRuntime.jsx("text", { className: styles$2.muted, x: width / 2, y: height / 2, textAnchor: "middle", dominantBaseline: "central", fontSize: fontPx * 1.2, children: text }));
|
|
11122
|
+
}
|
|
11123
|
+
|
|
11124
|
+
/** 270 degree dial: from 7:30 to 4:30 on a clock face, in radians from 12 o'clock. */
|
|
11125
|
+
const DIAL_START = -Math.PI * 0.75;
|
|
11126
|
+
const DIAL_END = Math.PI * 0.75;
|
|
11127
|
+
/** Report content defaults, as the old viewer drew them: black pointers, the Default palette for ranges. */
|
|
11128
|
+
const POINTER_FALLBACK = "black";
|
|
11129
|
+
const RANGE_PALETTE = chartPalette(null);
|
|
11130
|
+
/** Thickness of a range / pointer without a Width, as a fraction of the gauge height (the chart engine defaults). */
|
|
11131
|
+
const RANGE_DEFAULT_WIDTH = 0.3;
|
|
11132
|
+
const POINTER_DEFAULT_WIDTH = 0.1;
|
|
11133
|
+
const EMPTY_SCALE = { Minimum: 0, Maximum: 100, Pointers: [], Ranges: [] };
|
|
11134
|
+
function isBarPointer(type) {
|
|
11135
|
+
return (type ?? "").toLowerCase() === "bar";
|
|
11136
|
+
}
|
|
11137
|
+
function rangeColor(range, index) {
|
|
11138
|
+
return range.Color || RANGE_PALETTE[index % RANGE_PALETTE.length];
|
|
11139
|
+
}
|
|
11140
|
+
function pointerColor(pointer) {
|
|
11141
|
+
return pointer.Color || POINTER_FALLBACK;
|
|
11142
|
+
}
|
|
11143
|
+
function RadialGauge({ gauge, width, height, fontPx, zoom }) {
|
|
11144
|
+
const scale = gauge.Scales[0] ?? EMPTY_SCALE;
|
|
11145
|
+
const { Minimum: min, Maximum: max } = scale;
|
|
11146
|
+
const radius = Math.max(0, Math.min(width / 2, height / 1.75) - fontPx * 0.5);
|
|
11147
|
+
const cx = width / 2;
|
|
11148
|
+
const cy = fontPx * 0.5 + radius;
|
|
11149
|
+
const band = radius * 0.15;
|
|
11150
|
+
const angle = (value) => scaleValue(clampGaugeValue(value, min, max), { min, max }, DIAL_START, DIAL_END);
|
|
11151
|
+
const minLabel = polar(cx, cy, radius - band * 2.5, DIAL_START);
|
|
11152
|
+
const maxLabel = polar(cx, cy, radius - band * 2.5, DIAL_END);
|
|
11153
|
+
const showLabels = scale.ShowLabels !== false;
|
|
11154
|
+
return (jsxRuntime.jsxs("g", { children: [jsxRuntime.jsx("path", { className: styles$2.track, d: arcPath(cx, cy, radius, radius - band, DIAL_START, DIAL_END) }), scale.Ranges.map((range, index) => (jsxRuntime.jsx("path", { d: arcPath(cx, cy, radius, radius - band, angle(range.Start ?? min), angle(range.End ?? max)), fill: rangeColor(range, index) }, `r${index}`))), scale.Pointers.map((pointer, index) => {
|
|
11155
|
+
if (isBarPointer(pointer.Type)) {
|
|
11156
|
+
const span = gaugeBarSpan(pointer.Value, min, max);
|
|
11157
|
+
return (jsxRuntime.jsx("path", { d: arcPath(cx, cy, radius - band * 1.2, radius - band * 1.9, angle(span.from), angle(span.to)), fill: pointerColor(pointer) }, `p${index}`));
|
|
11158
|
+
}
|
|
11159
|
+
const tip = polar(cx, cy, radius * 0.8, angle(pointer.Value ?? min));
|
|
11160
|
+
return (jsxRuntime.jsx("line", { className: styles$2.needle, x1: cx, y1: cy, x2: tip.x, y2: tip.y, style: pointer.Color ? { stroke: pointer.Color } : undefined, strokeWidth: Math.max(1.5, 2.5 * zoom) }, `p${index}`));
|
|
11161
|
+
}), jsxRuntime.jsx("circle", { className: styles$2.cap, cx: cx, cy: cy, r: Math.max(2, radius * 0.06) }), showLabels && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("text", { className: styles$2.tick, x: minLabel.x, y: minLabel.y, textAnchor: "middle", fontSize: fontPx, children: formatChartValue(min) }), jsxRuntime.jsx("text", { className: styles$2.tick, x: maxLabel.x, y: maxLabel.y, textAnchor: "middle", fontSize: fontPx, children: formatChartValue(max) })] }))] }));
|
|
11162
|
+
}
|
|
11163
|
+
/**
|
|
11164
|
+
* A horizontal linear gauge as the chart engine draws it: range bands and pointers are as thick as
|
|
11165
|
+
* their Width percent of the gauge height (ranges 30%, pointers 10% by default) around the scale line
|
|
11166
|
+
* (Cross centred, Inside above, Outside below), the ranges in their own colour at full opacity, a Bar
|
|
11167
|
+
* pointer on top of them from the minimum to the value, and the major ticks with labels below.
|
|
11168
|
+
*/
|
|
11169
|
+
function LinearGauge({ gauge, width, height, fontPx }) {
|
|
11170
|
+
const scale = gauge.Scales[0] ?? EMPTY_SCALE;
|
|
11171
|
+
const { Minimum: min, Maximum: max } = scale;
|
|
11172
|
+
const showLabels = scale.ShowLabels !== false;
|
|
11173
|
+
const left = fontPx;
|
|
11174
|
+
const right = width - fontPx;
|
|
11175
|
+
const labelSpace = showLabels ? fontPx * 2 : 0;
|
|
11176
|
+
const x = (value) => scaleValue(clampGaugeValue(value, min, max), { min, max }, left, right);
|
|
11177
|
+
const ticks = showLabels ? gaugeTicks(min, max, scale.Interval) : [];
|
|
11178
|
+
const ranges = scale.Ranges.map(range => {
|
|
11179
|
+
const thickness = gaugeThickness(range.Width, height, RANGE_DEFAULT_WIDTH);
|
|
11180
|
+
return { range, thickness, extent: gaugeBandExtent(range.Placement, thickness) };
|
|
11181
|
+
});
|
|
11182
|
+
const pointers = scale.Pointers.map(pointer => {
|
|
11183
|
+
const thickness = gaugeThickness(pointer.Width, height, POINTER_DEFAULT_WIDTH);
|
|
11184
|
+
return { pointer, thickness, extent: gaugeBandExtent(pointer.Placement, thickness) };
|
|
11185
|
+
});
|
|
11186
|
+
const extents = [...ranges, ...pointers].map(band => band.extent);
|
|
11187
|
+
const scaleY = linearGaugeScaleY(height, extents, labelSpace);
|
|
11188
|
+
const bandsBottom = scaleY + Math.max(0, ...extents.map(extent => extent.down));
|
|
11189
|
+
return (jsxRuntime.jsxs("g", { children: [ranges.map(({ range, thickness, extent }, index) => {
|
|
11190
|
+
const from = x(range.Start ?? min);
|
|
11191
|
+
const to = x(range.End ?? max);
|
|
11192
|
+
return (jsxRuntime.jsx("rect", { x: Math.min(from, to), y: scaleY - extent.up, width: Math.abs(to - from), height: thickness, fill: rangeColor(range, index) }, `r${index}`));
|
|
11193
|
+
}), jsxRuntime.jsx("line", { className: styles$2.axis, x1: left, x2: right, y1: scaleY, y2: scaleY }), pointers.map(({ pointer, thickness, extent }, index) => {
|
|
11194
|
+
if (isBarPointer(pointer.Type)) {
|
|
11195
|
+
const span = gaugeBarSpan(pointer.Value, min, max);
|
|
11196
|
+
const from = x(span.from);
|
|
11197
|
+
const to = x(span.to);
|
|
11198
|
+
return (jsxRuntime.jsx("rect", { x: Math.min(from, to), y: scaleY - extent.up, width: Math.abs(to - from), height: thickness, fill: pointerColor(pointer) }, `p${index}`));
|
|
11199
|
+
}
|
|
11200
|
+
// A marker: a triangle pointing at the scale line from below.
|
|
11201
|
+
const at = x(pointer.Value ?? min);
|
|
11202
|
+
const size = Math.max(thickness, fontPx * 0.6);
|
|
11203
|
+
return (jsxRuntime.jsx("path", { d: `M${at},${scaleY} l${-size / 2},${size} h${size} Z`, fill: pointerColor(pointer) }, `p${index}`));
|
|
11204
|
+
}), ticks.map(tick => (jsxRuntime.jsxs("g", { children: [jsxRuntime.jsx("line", { className: styles$2.axis, x1: x(tick), x2: x(tick), y1: bandsBottom, y2: bandsBottom + fontPx * 0.4 }), jsxRuntime.jsx("text", { className: styles$2.tick, x: x(tick), y: bandsBottom + fontPx * 1.5, textAnchor: "middle", fontSize: fontPx, children: formatChartValue(tick) })] }, tick)))] }));
|
|
11205
|
+
}
|
|
11206
|
+
/**
|
|
11207
|
+
* A gauge panel: its gauges side by side (RadialGauge = 270 degree dial with colored ranges and a
|
|
11208
|
+
* needle or bar per pointer, LinearGauge = horizontal track with range bands, a bar or marker per
|
|
11209
|
+
* pointer and the major ticks), the state indicators at their percent boxes and the panel labels
|
|
11210
|
+
* below. The pointer value is not printed; only the report's own labels are. Only the first scale of
|
|
11211
|
+
* a gauge is drawn.
|
|
11212
|
+
*/
|
|
11213
|
+
function CwReportGauge({ panel, width, height, zoom = 1 }) {
|
|
11214
|
+
const fontPx = chartFontPx(zoom);
|
|
11215
|
+
const lineHeight = fontPx * 1.5;
|
|
11216
|
+
const labelsHeight = panel.Labels.length * lineHeight;
|
|
11217
|
+
const gaugeHeight = Math.max(0, height - labelsHeight);
|
|
11218
|
+
const cellWidth = panel.Gauges.length > 0 ? width / panel.Gauges.length : width;
|
|
11219
|
+
return (jsxRuntime.jsxs("svg", { className: styles$2.chart, width: width, height: height, role: "img", "aria-label": panel.Name, children: [panel.Error ? (jsxRuntime.jsx(ChartMessage, { text: panel.Error, width: width, height: height, fontPx: fontPx })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [panel.Gauges.map((gauge, index) => {
|
|
11220
|
+
const drawProps = { gauge, width: cellWidth, height: gaugeHeight, fontPx, zoom };
|
|
11221
|
+
return (jsxRuntime.jsx("g", { transform: `translate(${cellWidth * index}, 0)`, children: gauge.Kind.toLowerCase() === "lineargauge" ? jsxRuntime.jsx(LinearGauge, { ...drawProps }) : jsxRuntime.jsx(RadialGauge, { ...drawProps }) }, index));
|
|
11222
|
+
}), (panel.StateIndicators ?? []).map((indicator, index) => {
|
|
11223
|
+
const ellipse = stateIndicatorEllipse(indicator, width, height);
|
|
11224
|
+
return ellipse && jsxRuntime.jsx("ellipse", { ...ellipse, fill: indicator.Color ?? undefined }, `s${index}`);
|
|
11225
|
+
})] })), panel.Labels.map((label, index) => (jsxRuntime.jsx("text", { x: width / 2, y: gaugeHeight + lineHeight * (index + 0.7), textAnchor: "middle", fontSize: fontPx, children: label }, index)))] }));
|
|
11226
|
+
}
|
|
11227
|
+
|
|
11228
|
+
/*
|
|
11229
|
+
* The .NET numeric format strings a report puts on a chart's value axis (<Format>), applied with the
|
|
11230
|
+
* report culture so the screen shows the same tick text as the server's PDF. Only the formats reports
|
|
11231
|
+
* actually use are covered; anything else returns null and the caller keeps its own formatting.
|
|
11232
|
+
*/
|
|
11233
|
+
/** .NET's default decimals of the N, F and P standard formats without a precision. */
|
|
11234
|
+
const STANDARD_DEFAULT_DECIMALS = 2;
|
|
11235
|
+
const STANDARD_FORMAT = /^([NnFfPp])(\d{1,2})?$/;
|
|
11236
|
+
/** 0, 0.00, 0.0#, #,##0, #,0.00, 0%, 0.0% ... (integer part of 0, # and one optional grouping comma). */
|
|
11237
|
+
const CUSTOM_FORMAT = /^(#*,?#*0+)(?:\.(0*)(#*))?(%?)$/;
|
|
11238
|
+
/**
|
|
11239
|
+
* Formats longer than this are not matched (the custom pattern backtracks quadratically on a long
|
|
11240
|
+
* non-matching string), and more decimals than this are left to the caller: Intl.NumberFormat throws
|
|
11241
|
+
* above 100 fraction digits (above 20 on older browsers), which would break the chart render.
|
|
11242
|
+
*/
|
|
11243
|
+
const MAX_FORMAT_LENGTH = 64;
|
|
11244
|
+
const MAX_FRACTION_DIGITS = 20;
|
|
11245
|
+
function parseFormat(format) {
|
|
11246
|
+
if (format.length > MAX_FORMAT_LENGTH)
|
|
11247
|
+
return null;
|
|
11248
|
+
const standard = STANDARD_FORMAT.exec(format);
|
|
11249
|
+
if (standard) {
|
|
11250
|
+
const kind = standard[1].toUpperCase();
|
|
11251
|
+
const digits = standard[2] === undefined ? STANDARD_DEFAULT_DECIMALS : parseInt(standard[2], 10);
|
|
11252
|
+
return {
|
|
11253
|
+
minimumFractionDigits: digits,
|
|
11254
|
+
maximumFractionDigits: digits,
|
|
11255
|
+
grouping: kind !== "F",
|
|
11256
|
+
percent: kind === "P" ? "standard" : null
|
|
11257
|
+
};
|
|
11258
|
+
}
|
|
11259
|
+
const custom = CUSTOM_FORMAT.exec(format);
|
|
11260
|
+
if (custom) {
|
|
11261
|
+
const required = custom[2]?.length ?? 0;
|
|
11262
|
+
const optional = custom[3]?.length ?? 0;
|
|
11263
|
+
return {
|
|
11264
|
+
minimumFractionDigits: required,
|
|
11265
|
+
maximumFractionDigits: required + optional,
|
|
11266
|
+
grouping: custom[1].includes(","),
|
|
11267
|
+
percent: custom[4] === "%" ? "custom" : null
|
|
11268
|
+
};
|
|
11269
|
+
}
|
|
11270
|
+
return null;
|
|
11271
|
+
}
|
|
11272
|
+
function numberFormat(locale, options) {
|
|
11273
|
+
try {
|
|
11274
|
+
return new Intl.NumberFormat(locale || "en-US", options);
|
|
11275
|
+
}
|
|
11276
|
+
catch {
|
|
11277
|
+
return new Intl.NumberFormat("en-US", options);
|
|
11278
|
+
}
|
|
11279
|
+
}
|
|
11280
|
+
/**
|
|
11281
|
+
* A value in a .NET numeric format ("N0", "F1", "P0", "0", "0.0", "#,##0.00", "0%", ...) and a culture
|
|
11282
|
+
* ("de-CH"; missing or unknown = invariant-like en-US). Null for a missing or unsupported format.
|
|
11283
|
+
* The standard "P" uses the culture's percent pattern (e.g. "12 %" in de-DE); a custom "%" appends the
|
|
11284
|
+
* sign directly, as .NET does.
|
|
11285
|
+
*/
|
|
11286
|
+
function formatRdlNumber(value, format, culture) {
|
|
11287
|
+
if (!format || !Number.isFinite(value))
|
|
11288
|
+
return null;
|
|
11289
|
+
const spec = parseFormat(format.trim());
|
|
11290
|
+
if (!spec || spec.maximumFractionDigits > MAX_FRACTION_DIGITS)
|
|
11291
|
+
return null;
|
|
11292
|
+
const digits = {
|
|
11293
|
+
minimumFractionDigits: spec.minimumFractionDigits,
|
|
11294
|
+
maximumFractionDigits: spec.maximumFractionDigits,
|
|
11295
|
+
useGrouping: spec.grouping
|
|
11296
|
+
};
|
|
11297
|
+
const locale = culture ?? undefined;
|
|
11298
|
+
if (spec.percent === "standard")
|
|
11299
|
+
return numberFormat(locale, { ...digits, style: "percent" }).format(value);
|
|
11300
|
+
if (spec.percent === "custom") {
|
|
11301
|
+
// The percent style scales by 100 without float noise (0.145 -> 14.5, not 14.499...); its own
|
|
11302
|
+
// sign and spacing are dropped for the plain "%" a custom format appends.
|
|
11303
|
+
const parts = numberFormat(locale, { ...digits, style: "percent" }).formatToParts(value);
|
|
11304
|
+
return `${parts.filter(part => part.type !== "percentSign" && part.type !== "literal").map(part => part.value).join("")}%`;
|
|
11305
|
+
}
|
|
11306
|
+
return numberFormat(locale, digits).format(value);
|
|
11307
|
+
}
|
|
11308
|
+
|
|
11309
|
+
function seriesKind(series) {
|
|
11310
|
+
switch (series.Type.toLowerCase()) {
|
|
11311
|
+
case "column": return "column";
|
|
11312
|
+
case "bar": return "bar";
|
|
11313
|
+
case "area": return "area";
|
|
11314
|
+
case "scatter": return "scatter";
|
|
11315
|
+
default: return "line";
|
|
11316
|
+
}
|
|
11317
|
+
}
|
|
11318
|
+
function truncate(text, maxWidth, fontPx) {
|
|
11319
|
+
if (estimateTextWidth(text, fontPx) <= maxWidth)
|
|
11320
|
+
return text;
|
|
11321
|
+
const chars = Math.max(1, Math.floor(maxWidth / (fontPx * 0.6)) - 1);
|
|
11322
|
+
return text.slice(0, chars) + "…";
|
|
11323
|
+
}
|
|
11324
|
+
/** Categories along the x axis, values up: lines, areas, columns and scatter points. */
|
|
11325
|
+
function VerticalPlot({ chart, colors, scale, fontPx, left, top, right, bottom, strokeWidth, zoom, tickLabel }) {
|
|
11326
|
+
const tooltip = useChartTooltip();
|
|
11327
|
+
const count = Math.max(1, chart.Categories.length);
|
|
11328
|
+
const band = (right - left) / count;
|
|
11329
|
+
const x = (index) => left + band * (index + 0.5);
|
|
11330
|
+
const y = (value) => scaleValue(value, scale, bottom, top);
|
|
11331
|
+
const baseline = y(Math.min(scale.max, Math.max(scale.min, 0)));
|
|
11332
|
+
const columns = chart.Series.map((series, index) => ({ series, index })).filter(({ series }) => {
|
|
11333
|
+
const kind = seriesKind(series);
|
|
11334
|
+
return kind === "column" || kind === "bar";
|
|
11335
|
+
});
|
|
11336
|
+
const groupWidth = band * 0.75;
|
|
11337
|
+
const columnWidth = columns.length > 0 ? groupWidth / columns.length : 0;
|
|
11338
|
+
const labelWidth = Math.max(...chart.Categories.map(label => estimateTextWidth(label, fontPx)), 0) + fontPx;
|
|
11339
|
+
const labelStep = Math.max(1, Math.ceil(Math.min(labelWidth, fontPx * 12) / band));
|
|
11340
|
+
const marks = [];
|
|
11341
|
+
const hits = [];
|
|
11342
|
+
columns.forEach(({ series, index }, columnIndex) => {
|
|
11343
|
+
series.Values.forEach((value, category) => {
|
|
11344
|
+
if (value === null || !Number.isFinite(value))
|
|
11345
|
+
return;
|
|
11346
|
+
const columnX = left + band * category + (band - groupWidth) / 2 + columnWidth * columnIndex;
|
|
11347
|
+
const valueY = y(value);
|
|
11348
|
+
const content = tooltipContent(series.Name, chart.Categories[category] ?? "", formatChartValue(value));
|
|
11349
|
+
marks.push(jsxRuntime.jsx("rect", { x: columnX, y: Math.min(valueY, baseline), width: Math.max(1, columnWidth - 1), height: Math.max(0, Math.abs(baseline - valueY)), fill: colors[index], onMouseEnter: tooltip.show(content), onMouseLeave: tooltip.hide }, `c${index}-${category}`));
|
|
11350
|
+
if (series.ShowDataLabels) {
|
|
11351
|
+
marks.push(jsxRuntime.jsx("text", { x: columnX + columnWidth / 2, y: valueY - fontPx * 0.4, textAnchor: "middle", fontSize: fontPx, children: formatChartValue(value) }, `cl${index}-${category}`));
|
|
11352
|
+
}
|
|
11353
|
+
});
|
|
11354
|
+
});
|
|
11355
|
+
chart.Series.forEach((series, index) => {
|
|
11356
|
+
const kind = seriesKind(series);
|
|
11357
|
+
if (kind !== "line" && kind !== "area" && kind !== "scatter")
|
|
11358
|
+
return;
|
|
11359
|
+
const marker = seriesMarker(series.MarkerType, kind === "scatter", index);
|
|
11360
|
+
const markerSize = markerSizePx(series.MarkerSize, zoom);
|
|
11361
|
+
const segments = lineSegments(series.Values, x, y);
|
|
11362
|
+
segments.forEach((segment, segmentIndex) => {
|
|
11363
|
+
if (kind !== "scatter")
|
|
11364
|
+
drawLine(segment, segmentIndex);
|
|
11365
|
+
for (const point of segment) {
|
|
11366
|
+
if (marker) {
|
|
11367
|
+
marks.push(jsxRuntime.jsx("path", { d: markerPath(marker, point.x, point.y, markerSize), fill: colors[index] }, `m${index}-${point.index}`));
|
|
11368
|
+
}
|
|
11369
|
+
if (series.ShowDataLabels) {
|
|
11370
|
+
marks.push(jsxRuntime.jsx("text", { x: point.x, y: point.y - Math.max(fontPx * 0.6, markerSize), textAnchor: "middle", fontSize: fontPx, children: formatChartValue(point.value) }, `ll${index}-${point.index}`));
|
|
11371
|
+
}
|
|
11372
|
+
const content = tooltipContent(series.Name, chart.Categories[point.index] ?? "", formatChartValue(point.value));
|
|
11373
|
+
hits.push(jsxRuntime.jsx("circle", { className: styles$2.hit, cx: point.x, cy: point.y, r: Math.max(4, Math.min(8, band / 3), markerSize / 2), onMouseEnter: tooltip.show(content), onMouseLeave: tooltip.hide }, `h${index}-${point.index}`));
|
|
11374
|
+
}
|
|
11375
|
+
});
|
|
11376
|
+
function drawLine(segment, segmentIndex) {
|
|
11377
|
+
const path = segmentPath(segment, series.Subtype);
|
|
11378
|
+
if (kind === "area") {
|
|
11379
|
+
const first = segment[0];
|
|
11380
|
+
const last = segment[segment.length - 1];
|
|
11381
|
+
marks.push(jsxRuntime.jsx("path", { d: `${path} L${last.x},${baseline} L${first.x},${baseline} Z`, fill: colors[index], fillOpacity: 0.45, stroke: "none" }, `a${index}-${segmentIndex}`));
|
|
11382
|
+
}
|
|
11383
|
+
marks.push(jsxRuntime.jsx("path", { d: path, fill: "none", stroke: colors[index], strokeWidth: strokeWidth }, `l${index}-${segmentIndex}`));
|
|
11384
|
+
// A lone point between gaps has no line to show it: a dot, unless the series draws markers.
|
|
11385
|
+
if (segment.length === 1 && !marker) {
|
|
11386
|
+
marks.push(jsxRuntime.jsx("circle", { className: styles$2.point, cx: segment[0].x, cy: segment[0].y, r: strokeWidth * 1.5, fill: colors[index] }, `p${index}-${segmentIndex}`));
|
|
11387
|
+
}
|
|
11388
|
+
}
|
|
11389
|
+
});
|
|
11390
|
+
const serverLabels = chart.CategoryAxisLabels ? categoryAxisLabels(chart.CategoryAxisLabels, count, left, band) : null;
|
|
11391
|
+
// Category gridlines run through the labelled categories: the server's labels, else every labelStep-th category.
|
|
11392
|
+
const categoryGrid = chart.CategoryAxisGridLines === false
|
|
11393
|
+
? []
|
|
11394
|
+
: serverLabels?.map(label => label.at) ?? chart.Categories.map((_label, index) => index).filter(index => index % labelStep === 0).map(x);
|
|
11395
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [categoryGrid.map((at, index) => jsxRuntime.jsx("line", { className: styles$2.grid, x1: at, x2: at, y1: top, y2: bottom }, `cg${index}`)), scale.ticks.map(tick => (jsxRuntime.jsxs("g", { children: [chart.ValueAxisGridLines !== false && jsxRuntime.jsx("line", { className: styles$2.grid, x1: left, x2: right, y1: y(tick), y2: y(tick) }), jsxRuntime.jsx("text", { className: styles$2.tick, x: left - fontPx * 0.4, y: y(tick), textAnchor: "end", dominantBaseline: "central", fontSize: fontPx, children: tickLabel(tick) })] }, tick))), jsxRuntime.jsx("line", { className: styles$2.axis, x1: left, x2: left, y1: top, y2: bottom }), jsxRuntime.jsx("line", { className: styles$2.axis, x1: left, x2: right, y1: baseline, y2: baseline }), serverLabels
|
|
11396
|
+
? serverLabels.map((label, index) => {
|
|
11397
|
+
const placement = placeAxisLabel(label.at, estimateTextWidth(label.text, fontPx), left, right);
|
|
11398
|
+
return (jsxRuntime.jsxs("g", { children: [jsxRuntime.jsx("line", { className: styles$2.axis, x1: label.at, x2: label.at, y1: bottom, y2: bottom + fontPx * 0.3 }), jsxRuntime.jsx("text", { className: styles$2.tick, x: placement.x, y: bottom + fontPx * 1.3, textAnchor: placement.anchor, fontSize: fontPx, children: label.text })] }, `c${index}`));
|
|
11399
|
+
})
|
|
11400
|
+
: chart.Categories.map((label, index) => {
|
|
11401
|
+
if (index % labelStep !== 0)
|
|
11402
|
+
return null;
|
|
11403
|
+
const text = truncate(label, band * labelStep - fontPx * 0.4, fontPx);
|
|
11404
|
+
const placement = placeAxisLabel(x(index), estimateTextWidth(text, fontPx), left, right);
|
|
11405
|
+
return (jsxRuntime.jsx("text", { className: styles$2.tick, x: placement.x, y: bottom + fontPx * 1.1, textAnchor: placement.anchor, fontSize: fontPx, children: text }, index));
|
|
11406
|
+
}), marks, hits] }));
|
|
11407
|
+
}
|
|
11408
|
+
/** Categories down the y axis, values to the right: horizontal bars. */
|
|
11409
|
+
function HorizontalPlot({ chart, colors, scale, fontPx, left, top, right, bottom, tickLabel }) {
|
|
11410
|
+
const tooltip = useChartTooltip();
|
|
11411
|
+
const count = Math.max(1, chart.Categories.length);
|
|
11412
|
+
const band = (bottom - top) / count;
|
|
11413
|
+
const x = (value) => scaleValue(value, scale, left, right);
|
|
11414
|
+
const baseline = x(Math.min(scale.max, Math.max(scale.min, 0)));
|
|
11415
|
+
const groupHeight = band * 0.75;
|
|
11416
|
+
const barHeight = chart.Series.length > 0 ? groupHeight / chart.Series.length : 0;
|
|
11417
|
+
const serverLabels = chart.CategoryAxisLabels ? categoryAxisLabels(chart.CategoryAxisLabels, count, top, band) : null;
|
|
11418
|
+
const categoryGrid = chart.CategoryAxisGridLines === false
|
|
11419
|
+
? []
|
|
11420
|
+
: serverLabels?.map(label => label.at) ?? chart.Categories.map((_label, index) => top + band * (index + 0.5));
|
|
11421
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [categoryGrid.map((at, index) => jsxRuntime.jsx("line", { className: styles$2.grid, x1: left, x2: right, y1: at, y2: at }, `cg${index}`)), scale.ticks.map(tick => (jsxRuntime.jsxs("g", { children: [chart.ValueAxisGridLines !== false && jsxRuntime.jsx("line", { className: styles$2.grid, x1: x(tick), x2: x(tick), y1: top, y2: bottom }), jsxRuntime.jsx("text", { className: styles$2.tick, x: x(tick), y: bottom + fontPx * 1.1, textAnchor: "middle", fontSize: fontPx, children: tickLabel(tick) })] }, tick))), jsxRuntime.jsx("line", { className: styles$2.axis, x1: baseline, x2: baseline, y1: top, y2: bottom }), jsxRuntime.jsx("line", { className: styles$2.axis, x1: left, x2: right, y1: bottom, y2: bottom }), serverLabels
|
|
11422
|
+
? serverLabels.map((label, index) => (jsxRuntime.jsxs("g", { children: [jsxRuntime.jsx("line", { className: styles$2.axis, x1: left - fontPx * 0.3, x2: left, y1: label.at, y2: label.at }), jsxRuntime.jsx("text", { className: styles$2.tick, x: left - fontPx * 0.4, y: label.at, textAnchor: "end", dominantBaseline: "central", fontSize: fontPx, children: truncate(label.text, left - fontPx, fontPx) })] }, `c${index}`)))
|
|
11423
|
+
: chart.Categories.map((label, index) => (jsxRuntime.jsx("text", { className: styles$2.tick, x: left - fontPx * 0.4, y: top + band * (index + 0.5), textAnchor: "end", dominantBaseline: "central", fontSize: fontPx, children: truncate(label, left - fontPx, fontPx) }, index))), chart.Series.map((series, index) => series.Values.map((value, category) => {
|
|
11424
|
+
if (value === null || !Number.isFinite(value))
|
|
11425
|
+
return null;
|
|
11426
|
+
const barY = top + band * category + (band - groupHeight) / 2 + barHeight * index;
|
|
11427
|
+
const valueX = x(value);
|
|
11428
|
+
const content = tooltipContent(series.Name, chart.Categories[category] ?? "", formatChartValue(value));
|
|
11429
|
+
return (jsxRuntime.jsxs("g", { children: [jsxRuntime.jsx("rect", { x: Math.min(valueX, baseline), y: barY, width: Math.max(0, Math.abs(valueX - baseline)), height: Math.max(1, barHeight - 1), fill: colors[index], onMouseEnter: tooltip.show(content), onMouseLeave: tooltip.hide }), series.ShowDataLabels && (jsxRuntime.jsx("text", { x: Math.max(valueX, baseline) + fontPx * 0.3, y: barY + barHeight / 2, dominantBaseline: "central", fontSize: fontPx, children: formatChartValue(value) }))] }, `${index}-${category}`));
|
|
11430
|
+
}))] }));
|
|
11431
|
+
}
|
|
11432
|
+
/**
|
|
11433
|
+
* A report chart over categories: Line (Smooth / Stepped), Area, Column and Scatter series share one
|
|
11434
|
+
* value axis; a chart made only of Bar series is drawn horizontally. Scatter series draw only their
|
|
11435
|
+
* markers, line series draw markers too when the report sets a marker type. Null values leave gaps.
|
|
11436
|
+
* The legend sits where the RDL legend Position says. Own SVG, no chart library; hover shows
|
|
11437
|
+
* category, series and value.
|
|
11438
|
+
*/
|
|
11439
|
+
function CwReportLineChart({ chart, width, height, zoom = 1, culture }) {
|
|
11440
|
+
const fontPx = chartFontPx(zoom);
|
|
11441
|
+
const palette = chartPalette(chart.Palette);
|
|
11442
|
+
const colors = chart.Series.map((series, index) => seriesColor(series, index, palette));
|
|
11443
|
+
const titleHeight = titlesHeight(chart.Titles, fontPx);
|
|
11444
|
+
// The legend on the side its RDL Position names: a side legend is a column at most 30% wide.
|
|
11445
|
+
const placement = legendPlacement(chart.LegendPosition);
|
|
11446
|
+
const sideLegend = placement.edge === "left" || placement.edge === "right";
|
|
11447
|
+
const legend = layoutLegend(chart.LegendHidden ? [] : chart.Series.map((series, index) => ({
|
|
11448
|
+
label: series.Name,
|
|
11449
|
+
color: colors[index],
|
|
11450
|
+
marker: seriesMarker(series.MarkerType, seriesKind(series) === "scatter", index)
|
|
11451
|
+
})), sideLegend ? width * 0.3 : width, fontPx, sideLegend ? "start" : placement.align);
|
|
11452
|
+
const legendHeight = legend.height > 0 ? legend.height + fontPx * 0.5 : 0;
|
|
11453
|
+
const legendWidth = legend.width > 0 ? legend.width + fontPx : 0;
|
|
11454
|
+
const legendSpace = {
|
|
11455
|
+
top: placement.edge === "top" ? legendHeight : 0,
|
|
11456
|
+
bottom: placement.edge === "bottom" ? legendHeight : 0,
|
|
11457
|
+
left: placement.edge === "left" ? legendWidth : 0,
|
|
11458
|
+
right: placement.edge === "right" ? legendWidth : 0
|
|
11459
|
+
};
|
|
11460
|
+
const message = chart.Error
|
|
11461
|
+
?? (chart.Categories.length === 0 || chart.Series.length === 0 ? (chart.NoRowsMessage ?? "No data") : null);
|
|
11462
|
+
if (message) {
|
|
11463
|
+
return (jsxRuntime.jsxs("svg", { className: styles$2.chart, width: width, height: height, role: "img", "aria-label": chart.Titles.join(" ") || chart.Name, children: [jsxRuntime.jsx(ChartTitles, { titles: chart.Titles, width: width, fontPx: fontPx }), jsxRuntime.jsx(ChartMessage, { text: message, width: width, height: height, fontPx: fontPx })] }));
|
|
11464
|
+
}
|
|
11465
|
+
const horizontal = chart.Series.every(series => seriesKind(series) === "bar");
|
|
11466
|
+
const scale = computeValueScale(chart.Series.flatMap(series => series.Values));
|
|
11467
|
+
// The axis <Format> with the report culture, as the server's PDF prints it; an unsupported format keeps the compact default.
|
|
11468
|
+
const tickLabel = (tick) => formatRdlNumber(tick, chart.ValueAxisFormat, culture) ?? formatChartValue(tick);
|
|
11469
|
+
const tickWidth = Math.max(...scale.ticks.map(tick => estimateTextWidth(tickLabel(tick), fontPx)));
|
|
11470
|
+
const categoryWidth = Math.min(width * 0.35, Math.max(...chart.Categories.map(label => estimateTextWidth(label, fontPx)), 0));
|
|
11471
|
+
const lineHeight = fontPx * 1.6;
|
|
11472
|
+
const valueTitleSpace = chart.ValueAxisTitle ? lineHeight : 0;
|
|
11473
|
+
const categoryTitleSpace = chart.CategoryAxisTitle ? lineHeight : 0;
|
|
11474
|
+
const left = legendSpace.left + (horizontal ? categoryTitleSpace + categoryWidth : valueTitleSpace + tickWidth) + fontPx * 0.8;
|
|
11475
|
+
const right = width - fontPx - legendSpace.right;
|
|
11476
|
+
const top = titleHeight + legendSpace.top + fontPx * 0.8;
|
|
11477
|
+
const bottom = height - legendSpace.bottom - lineHeight - (horizontal ? valueTitleSpace : categoryTitleSpace);
|
|
11478
|
+
const strokeWidth = Math.max(1, 1.5 * zoom);
|
|
11479
|
+
const plotProps = { chart, colors, scale, fontPx, left, top, right, bottom, strokeWidth, zoom, tickLabel };
|
|
11480
|
+
const verticalTitle = horizontal ? chart.CategoryAxisTitle : chart.ValueAxisTitle;
|
|
11481
|
+
const horizontalTitle = horizontal ? chart.ValueAxisTitle : chart.CategoryAxisTitle;
|
|
11482
|
+
const titleX = legendSpace.left + fontPx;
|
|
11483
|
+
const legendLeft = placement.edge === "left" ? fontPx * 0.4 : placement.edge === "right" ? width - legendWidth + fontPx * 0.4 : 0;
|
|
11484
|
+
const legendTop = (() => {
|
|
11485
|
+
switch (placement.edge) {
|
|
11486
|
+
case "top": return titleHeight;
|
|
11487
|
+
case "bottom": return height - legend.height;
|
|
11488
|
+
default:
|
|
11489
|
+
if (placement.align === "start")
|
|
11490
|
+
return top;
|
|
11491
|
+
if (placement.align === "end")
|
|
11492
|
+
return Math.max(top, bottom - legend.height);
|
|
11493
|
+
return Math.max(top, (top + bottom - legend.height) / 2);
|
|
11494
|
+
}
|
|
11495
|
+
})();
|
|
11496
|
+
return (jsxRuntime.jsxs("svg", { className: styles$2.chart, width: width, height: height, role: "img", "aria-label": chart.Titles.join(" ") || chart.Name, children: [jsxRuntime.jsx(ChartTitles, { titles: chart.Titles, width: width, fontPx: fontPx }), right - left > 10 && bottom - top > 10 && (horizontal ? jsxRuntime.jsx(HorizontalPlot, { ...plotProps }) : jsxRuntime.jsx(VerticalPlot, { ...plotProps })), verticalTitle && (jsxRuntime.jsx("text", { x: titleX, y: (top + bottom) / 2, textAnchor: "middle", dominantBaseline: "central", fontSize: fontPx, transform: `rotate(-90 ${titleX} ${(top + bottom) / 2})`, children: verticalTitle })), horizontalTitle && (jsxRuntime.jsx("text", { x: (left + right) / 2, y: bottom + lineHeight * 1.6, textAnchor: "middle", fontSize: fontPx, children: horizontalTitle })), jsxRuntime.jsx(ChartLegend, { layout: legend, top: legendTop, left: legendLeft, fontPx: fontPx })] }));
|
|
11497
|
+
}
|
|
11498
|
+
|
|
11499
|
+
/** A pie of the chart's first series over its categories (RDL series Type "Shape"). Own SVG. */
|
|
11500
|
+
function CwReportPieChart({ chart, width, height, zoom = 1 }) {
|
|
11501
|
+
const tooltip = useChartTooltip();
|
|
11502
|
+
const fontPx = chartFontPx(zoom);
|
|
11503
|
+
const palette = chartPalette(chart.Palette);
|
|
11504
|
+
const series = chart.Series[0];
|
|
11505
|
+
const colorOf = (index) => palette[index % palette.length];
|
|
11506
|
+
const titleHeight = titlesHeight(chart.Titles, fontPx);
|
|
11507
|
+
const legend = layoutLegend(chart.LegendHidden ? [] : chart.Categories.map((label, index) => ({ label, color: colorOf(index) })), width, fontPx);
|
|
11508
|
+
const legendHeight = legend.height > 0 ? legend.height + fontPx * 0.5 : 0;
|
|
11509
|
+
const slices = series ? pieSlices(series.Values) : [];
|
|
11510
|
+
const message = chart.Error ?? (slices.length === 0 ? (chart.NoRowsMessage ?? "No data") : null);
|
|
11511
|
+
const plotTop = titleHeight + fontPx * 0.5;
|
|
11512
|
+
const plotHeight = height - plotTop - legendHeight;
|
|
11513
|
+
const radius = Math.max(0, Math.min(width, plotHeight) / 2 - fontPx * 0.5);
|
|
11514
|
+
const cx = width / 2;
|
|
11515
|
+
const cy = plotTop + plotHeight / 2;
|
|
11516
|
+
return (jsxRuntime.jsxs("svg", { className: styles$2.chart, width: width, height: height, role: "img", "aria-label": chart.Titles.join(" ") || chart.Name, children: [jsxRuntime.jsx(ChartTitles, { titles: chart.Titles, width: width, fontPx: fontPx }), message ? (jsxRuntime.jsx(ChartMessage, { text: message, width: width, height: height, fontPx: fontPx })) : (slices.map(slice => {
|
|
11517
|
+
const category = chart.Categories[slice.index] ?? "";
|
|
11518
|
+
const percent = `${formatChartValue(slice.fraction * 100)}%`;
|
|
11519
|
+
const labelAt = polar(cx, cy, radius * 0.65, (slice.startAngle + slice.endAngle) / 2);
|
|
11520
|
+
return (jsxRuntime.jsxs("g", { children: [jsxRuntime.jsx("path", { className: styles$2.slice, d: arcPath(cx, cy, radius, 0, slice.startAngle, slice.endAngle), fill: colorOf(slice.index), onMouseEnter: tooltip.show(tooltipContent(series.Name, category, `${formatChartValue(slice.value)} (${percent})`)), onMouseLeave: tooltip.hide }), series.ShowDataLabels && slice.fraction >= 0.03 && (jsxRuntime.jsx("text", { x: labelAt.x, y: labelAt.y, textAnchor: "middle", dominantBaseline: "central", fontSize: fontPx, pointerEvents: "none", children: formatChartValue(slice.value) }))] }, slice.index));
|
|
11521
|
+
})), jsxRuntime.jsx(ChartLegend, { layout: legend, top: height - legend.height, fontPx: fontPx })] }));
|
|
11522
|
+
}
|
|
11523
|
+
|
|
11524
|
+
/** Indent of one list level, in points. */
|
|
11525
|
+
const RDL_LIST_INDENT_PT = 18;
|
|
11526
|
+
const BULLETS = ["•", "◦", "▪"];
|
|
11527
|
+
function listKind(paragraph) {
|
|
11528
|
+
switch ((paragraph.ListStyle ?? "").toLowerCase()) {
|
|
11529
|
+
case "bulleted": return "bulleted";
|
|
11530
|
+
case "numbered": return "numbered";
|
|
11531
|
+
default: return null;
|
|
11532
|
+
}
|
|
11533
|
+
}
|
|
11534
|
+
/**
|
|
11535
|
+
* The list marker of every paragraph (null = no list). Numbering counts consecutive numbered
|
|
11536
|
+
* paragraphs of the same level: a non-list paragraph resets every level, a paragraph at a level
|
|
11537
|
+
* resets the levels below it, and a bulleted paragraph resets the numbering of its own level.
|
|
11538
|
+
*/
|
|
11539
|
+
function computeListMarkers(paragraphs) {
|
|
11540
|
+
const counters = [];
|
|
11541
|
+
return paragraphs.map(paragraph => {
|
|
11542
|
+
const kind = listKind(paragraph);
|
|
11543
|
+
if (!kind) {
|
|
11544
|
+
counters.length = 0;
|
|
11545
|
+
return null;
|
|
11546
|
+
}
|
|
11547
|
+
const level = Math.max(1, paragraph.ListLevel);
|
|
11548
|
+
counters.length = Math.min(counters.length, level);
|
|
11549
|
+
if (kind === "bulleted") {
|
|
11550
|
+
counters[level - 1] = 0;
|
|
11551
|
+
return BULLETS[(level - 1) % BULLETS.length];
|
|
11552
|
+
}
|
|
11553
|
+
counters[level - 1] = (counters[level - 1] ?? 0) + 1;
|
|
11554
|
+
return `${counters[level - 1]}.`;
|
|
11555
|
+
});
|
|
11556
|
+
}
|
|
11557
|
+
/** Left indent of a paragraph in points: one list indent per level (a list paragraph has at least level 1). */
|
|
11558
|
+
function paragraphIndentPt(paragraph) {
|
|
11559
|
+
const level = listKind(paragraph) ? Math.max(1, paragraph.ListLevel) : Math.max(0, paragraph.ListLevel);
|
|
11560
|
+
return level * RDL_LIST_INDENT_PT;
|
|
11561
|
+
}
|
|
11562
|
+
|
|
11563
|
+
const EMPTY_FLIPPED = new Set();
|
|
11564
|
+
const EMPTY_HIGHLIGHTS = new Map();
|
|
11565
|
+
const ReportRenderContext = React.createContext({
|
|
11566
|
+
zoom: 1,
|
|
11567
|
+
flipped: EMPTY_FLIPPED,
|
|
11568
|
+
highlights: EMPTY_HIGHLIGHTS,
|
|
11569
|
+
activeMatchIndex: -1
|
|
11570
|
+
});
|
|
11571
|
+
function useReportRender() {
|
|
11572
|
+
return React.useContext(ReportRenderContext);
|
|
11573
|
+
}
|
|
11574
|
+
/** Groups the page's matches by run key. */
|
|
11575
|
+
function highlightsByRun(matches) {
|
|
11576
|
+
const map = new Map();
|
|
11577
|
+
matches.forEach((match, matchIndex) => {
|
|
11578
|
+
const list = map.get(match.key) ?? [];
|
|
11579
|
+
list.push({ start: match.start, length: match.length, matchIndex });
|
|
11580
|
+
map.set(match.key, list);
|
|
11581
|
+
});
|
|
11582
|
+
return map;
|
|
11583
|
+
}
|
|
11584
|
+
|
|
11585
|
+
var styles$1 = {"sheet":"cw-report-page-module__sheet__MAL8a","section":"cw-report-page-module__section__rN7A3","item":"cw-report-page-module__item__VZFw8","table":"cw-report-page-module__table__SAZVC","textbox":"cw-report-page-module__textbox__iwVwk cw-report-page-module__item__VZFw8","textRow":"cw-report-page-module__textRow__3WIRX","textBody":"cw-report-page-module__textBody__Jp1-1","paragraph":"cw-report-page-module__paragraph__d6dYI","marker":"cw-report-page-module__marker__geRQt","link":"cw-report-page-module__link__YFX1h","match":"cw-report-page-module__match__sWdSD","inlineButton":"cw-report-page-module__inlineButton__m05OC","rectangle":"cw-report-page-module__rectangle__2PYyK cw-report-page-module__item__VZFw8","line":"cw-report-page-module__line__Vfus-","image":"cw-report-page-module__image__VEp5H cw-report-page-module__item__VZFw8","tableMessage":"cw-report-page-module__tableMessage__kWsAj","chart":"cw-report-page-module__chart__PP9oM cw-report-page-module__item__VZFw8","placeholder":"cw-report-page-module__placeholder__FK8kO cw-report-page-module__item__VZFw8"};
|
|
11586
|
+
|
|
11587
|
+
/** Size of the drilldown / sort icons, in points: a little below the default 10pt text. */
|
|
11588
|
+
const INLINE_ICON_PT = 9;
|
|
11589
|
+
const SAFE_LINK = /^(https?:|mailto:)/i;
|
|
11590
|
+
function highlightedText(text, highlights, activeMatchIndex) {
|
|
11591
|
+
if (!highlights || highlights.length === 0)
|
|
11592
|
+
return text;
|
|
11593
|
+
const parts = [];
|
|
11594
|
+
let cursor = 0;
|
|
11595
|
+
for (const highlight of highlights) {
|
|
11596
|
+
if (highlight.start > cursor)
|
|
11597
|
+
parts.push(text.slice(cursor, highlight.start));
|
|
11598
|
+
const active = highlight.matchIndex === activeMatchIndex;
|
|
11599
|
+
parts.push(jsxRuntime.jsx("mark", { className: styles$1.match, "data-active": active ? "true" : undefined, "data-active-match": active ? "true" : undefined, children: text.slice(highlight.start, highlight.start + highlight.length) }, highlight.matchIndex));
|
|
11600
|
+
cursor = highlight.start + highlight.length;
|
|
11601
|
+
}
|
|
11602
|
+
if (cursor < text.length)
|
|
11603
|
+
parts.push(text.slice(cursor));
|
|
11604
|
+
return parts;
|
|
11605
|
+
}
|
|
11606
|
+
function ReportRun({ run, runKey, paragraphStyle, textboxStyle }) {
|
|
11607
|
+
const { zoom, highlights, activeMatchIndex } = useReportRender();
|
|
11608
|
+
const css = rdlTextCss(resolveRdlTextStyle(run.Style, paragraphStyle, textboxStyle), zoom);
|
|
11609
|
+
const content = highlightedText(run.Text, highlights.get(runKey), activeMatchIndex);
|
|
11610
|
+
if (run.Href && SAFE_LINK.test(run.Href)) {
|
|
11611
|
+
return (jsxRuntime.jsx("a", { className: styles$1.link, href: run.Href, target: "_blank", rel: "noopener noreferrer", style: css, children: content }));
|
|
11612
|
+
}
|
|
11613
|
+
return jsxRuntime.jsx("span", { style: css, children: content });
|
|
11614
|
+
}
|
|
11615
|
+
function iconSizeStyle(zoom) {
|
|
11616
|
+
return { "--btn-size": `${ptToPx(INLINE_ICON_PT, zoom)}px` };
|
|
11617
|
+
}
|
|
11618
|
+
function sortIcon(direction) {
|
|
11619
|
+
switch ((direction ?? "").toLowerCase()) {
|
|
11620
|
+
case "ascending": return "sort-asc";
|
|
11621
|
+
case "descending": return "sort-desc";
|
|
11622
|
+
default: return "sortable";
|
|
11623
|
+
}
|
|
11624
|
+
}
|
|
11625
|
+
/** A textbox: paragraphs as blocks, runs as inline spans, plain text only (never HTML). */
|
|
11626
|
+
function ReportTextbox({ textbox, itemKey, frameStyle }) {
|
|
11627
|
+
const { zoom, flipped, onToggle, onSort } = useReportRender();
|
|
11628
|
+
const markers = computeListMarkers(textbox.Paragraphs);
|
|
11629
|
+
const paragraphs = textbox.Paragraphs.map((paragraph, p) => {
|
|
11630
|
+
const indent = ptToPx(paragraphIndentPt(paragraph), zoom);
|
|
11631
|
+
const marker = markers[p];
|
|
11632
|
+
const firstRun = paragraph.Runs[0];
|
|
11633
|
+
const paragraphCss = {
|
|
11634
|
+
...rdlTextCss(resolveParagraphTextStyle(paragraph.Runs.map(run => run.Style), paragraph.Style, textbox.Style), zoom),
|
|
11635
|
+
textAlign: rdlTextAlign(paragraph.Style?.TextAlign ?? textbox.Style?.TextAlign),
|
|
11636
|
+
paddingLeft: indent || undefined
|
|
11637
|
+
};
|
|
11638
|
+
return (jsxRuntime.jsxs("p", { className: styles$1.paragraph, style: paragraphCss, children: [marker !== null && (jsxRuntime.jsx("span", { className: styles$1.marker, style: {
|
|
11639
|
+
...rdlTextCss(resolveRdlTextStyle(firstRun?.Style, paragraph.Style, textbox.Style), zoom),
|
|
11640
|
+
left: indent - ptToPx(RDL_LIST_INDENT_PT, zoom),
|
|
11641
|
+
width: ptToPx(RDL_LIST_INDENT_PT, zoom)
|
|
11642
|
+
}, children: marker })), paragraph.Runs.map((run, r) => (jsxRuntime.jsx(ReportRun, { run: run, runKey: rdlRunKey(itemKey, p, r), paragraphStyle: paragraph.Style, textboxStyle: textbox.Style }, r)))] }, p));
|
|
11643
|
+
});
|
|
11644
|
+
const boxCss = {
|
|
11645
|
+
...frameStyle,
|
|
11646
|
+
...rdlBoxStyle(textbox.Style, zoom),
|
|
11647
|
+
color: rdlColor(textbox.Style?.Color),
|
|
11648
|
+
justifyContent: rdlVerticalAlign(textbox.Style?.VerticalAlign)
|
|
11649
|
+
};
|
|
11650
|
+
const toggleId = textbox.ToggleId;
|
|
11651
|
+
const sortId = textbox.SortId;
|
|
11652
|
+
if (!toggleId && !sortId) {
|
|
11653
|
+
return jsxRuntime.jsx("div", { className: styles$1.textbox, style: boxCss, children: paragraphs });
|
|
11654
|
+
}
|
|
11655
|
+
const expanded = isToggleExpanded(textbox, flipped);
|
|
11656
|
+
return (jsxRuntime.jsx("div", { className: styles$1.textbox, style: boxCss, children: jsxRuntime.jsxs("div", { className: styles$1.textRow, children: [toggleId && (jsxRuntime.jsx(CwButton, { variant: "icon", size: "small", icon: expanded ? "minus" : "plus", className: styles$1.inlineButton, style: iconSizeStyle(zoom), "aria-label": expanded ? "Collapse" : "Expand", "aria-expanded": expanded, "data-toggle-id": toggleId, onClick: () => onToggle?.(toggleId) })), jsxRuntime.jsx("div", { className: styles$1.textBody, children: paragraphs }), sortId && (jsxRuntime.jsx(CwButton, { variant: "icon", size: "small", icon: sortIcon(textbox.SortDirection), className: styles$1.inlineButton, style: iconSizeStyle(zoom), "aria-label": "Sort", onClick: () => onSort?.(sortId) }))] }) }));
|
|
11657
|
+
}
|
|
11658
|
+
|
|
11659
|
+
function frameOf(item) {
|
|
11660
|
+
return { top: item.Top, left: item.Left, width: item.Width, height: item.Height };
|
|
11661
|
+
}
|
|
11662
|
+
function frameCss(frame, zoom) {
|
|
11663
|
+
return {
|
|
11664
|
+
top: ptToPx(frame.top, zoom),
|
|
11665
|
+
left: ptToPx(frame.left, zoom),
|
|
11666
|
+
width: ptToPx(frame.width, zoom),
|
|
11667
|
+
height: ptToPx(frame.height, zoom)
|
|
11668
|
+
};
|
|
11669
|
+
}
|
|
11670
|
+
const SAFE_IMAGE_URL = /^https?:/i;
|
|
11671
|
+
function ReportRectangle({ item, itemKey, frame }) {
|
|
11672
|
+
const { zoom } = useReportRender();
|
|
11673
|
+
return (jsxRuntime.jsx("div", { className: styles$1.rectangle, style: { ...frameCss(frame, zoom), ...rdlBoxStyle(item.Style, zoom) }, children: jsxRuntime.jsx(ReportItems, { items: item.Items, parentKey: itemKey }) }));
|
|
11674
|
+
}
|
|
11675
|
+
function ReportLine({ item, frame }) {
|
|
11676
|
+
const { zoom } = useReportRender();
|
|
11677
|
+
const stroke = rdlLineStroke(item.Style, zoom);
|
|
11678
|
+
const x1 = ptToPx(frame.left, zoom);
|
|
11679
|
+
const y1 = ptToPx(frame.top, zoom);
|
|
11680
|
+
const x2 = ptToPx(frame.left + frame.width, zoom);
|
|
11681
|
+
const y2 = ptToPx(frame.top + frame.height, zoom);
|
|
11682
|
+
const pad = stroke.width;
|
|
11683
|
+
const left = Math.min(x1, x2) - pad;
|
|
11684
|
+
const top = Math.min(y1, y2) - pad;
|
|
11685
|
+
const width = Math.abs(x2 - x1) + pad * 2;
|
|
11686
|
+
const height = Math.abs(y2 - y1) + pad * 2;
|
|
11687
|
+
return (jsxRuntime.jsx("svg", { className: styles$1.line, style: { left, top, width, height }, width: width, height: height, "aria-hidden": "true", children: jsxRuntime.jsx("line", { x1: x1 - left, y1: y1 - top, x2: x2 - left, y2: y2 - top, stroke: stroke.color, strokeWidth: stroke.width, strokeDasharray: stroke.dash }) }));
|
|
11688
|
+
}
|
|
11689
|
+
function imageSizing(sizing) {
|
|
11690
|
+
switch (sizing.toLowerCase()) {
|
|
11691
|
+
case "fit": return "fit";
|
|
11692
|
+
case "fitproportional": return "proportional";
|
|
11693
|
+
default: return "natural";
|
|
11694
|
+
}
|
|
11695
|
+
}
|
|
11696
|
+
function ReportImage({ item, frame }) {
|
|
11697
|
+
const { zoom, getImageUrl } = useReportRender();
|
|
11698
|
+
const src = item.Url && SAFE_IMAGE_URL.test(item.Url)
|
|
11699
|
+
? item.Url
|
|
11700
|
+
: item.ImageId && getImageUrl ? getImageUrl(item.ImageId) : null;
|
|
11701
|
+
const sizing = imageSizing(item.Sizing);
|
|
11702
|
+
return (jsxRuntime.jsx("div", { className: styles$1.image, style: { ...frameCss(frame, zoom), ...rdlBoxStyle(item.Style, zoom) }, children: src && (jsxRuntime.jsx("img", { src: src, alt: item.Name, referrerPolicy: "no-referrer", "data-sizing": sizing, style: sizing === "natural" ? { transform: `scale(${zoom})` } : undefined })) }));
|
|
11703
|
+
}
|
|
11704
|
+
function ReportTable({ item, itemKey, frame }) {
|
|
11705
|
+
const { zoom, flipped } = useReportRender();
|
|
11706
|
+
const message = item.Error ?? (item.Rows.length === 0 ? item.NoRowsMessage : null);
|
|
11707
|
+
if (message) {
|
|
11708
|
+
const textCss = rdlTextCss(resolveRdlTextStyle(null, null, item.Style), zoom);
|
|
11709
|
+
return (jsxRuntime.jsx("div", { className: styles$1.table, style: { ...frameCss(frame, zoom), height: undefined, ...rdlBoxStyle(item.Style, zoom) }, children: item.Error
|
|
11710
|
+
? jsxRuntime.jsx(CwNote, { color: "danger", variant: "soft", fontSize: "small", content: item.Error })
|
|
11711
|
+
: jsxRuntime.jsx("p", { className: `${styles$1.tableMessage} cw-margin-none`, style: textCss, children: message }) }));
|
|
11712
|
+
}
|
|
11713
|
+
const layout = layoutRdlTable(item, flipped);
|
|
11714
|
+
return (jsxRuntime.jsx("div", { className: styles$1.table, style: { ...frameCss({ ...frame, width: layout.width, height: layout.height }, zoom), ...rdlBoxStyle(item.Style, zoom) }, children: layout.cells.map(box => {
|
|
11715
|
+
const content = box.cell.Content;
|
|
11716
|
+
if (!content)
|
|
11717
|
+
return null;
|
|
11718
|
+
const cellFrame = { top: box.top, left: box.left, width: box.width, height: box.height };
|
|
11719
|
+
const key = `${box.rowIndex}-${box.cellIndex}`;
|
|
11720
|
+
if (box.hidden) {
|
|
11721
|
+
// Drawn empty: the cell keeps its borders and background so the grid stays closed.
|
|
11722
|
+
return jsxRuntime.jsx("div", { className: styles$1.item, style: { ...frameCss(cellFrame, zoom), ...rdlBoxStyle(content.Style, zoom) } }, key);
|
|
11723
|
+
}
|
|
11724
|
+
return jsxRuntime.jsx(ReportItem, { item: content, itemKey: rdlCellKey(itemKey, box.rowIndex, box.cellIndex), frame: cellFrame }, key);
|
|
11725
|
+
}) }));
|
|
11726
|
+
}
|
|
11727
|
+
function ReportChart({ item, frame }) {
|
|
11728
|
+
const { zoom, culture } = useReportRender();
|
|
11729
|
+
const width = ptToPx(frame.width, zoom);
|
|
11730
|
+
const height = ptToPx(frame.height, zoom);
|
|
11731
|
+
const pie = item.Series[0]?.Type.toLowerCase() === "shape";
|
|
11732
|
+
return (jsxRuntime.jsx("div", { className: styles$1.chart, style: { ...frameCss(frame, zoom), ...rdlBoxStyle(item.Style, zoom) }, children: pie
|
|
11733
|
+
? jsxRuntime.jsx(CwReportPieChart, { chart: item, width: width, height: height, zoom: zoom })
|
|
11734
|
+
: jsxRuntime.jsx(CwReportLineChart, { chart: item, width: width, height: height, zoom: zoom, culture: culture }) }));
|
|
11735
|
+
}
|
|
11736
|
+
function ReportGauge({ item, frame }) {
|
|
11737
|
+
const { zoom } = useReportRender();
|
|
11738
|
+
return (jsxRuntime.jsx("div", { className: styles$1.chart, style: { ...frameCss(frame, zoom), ...rdlBoxStyle(item.Style, zoom) }, children: jsxRuntime.jsx(CwReportGauge, { panel: item, width: ptToPx(frame.width, zoom), height: ptToPx(frame.height, zoom), zoom: zoom }) }));
|
|
11739
|
+
}
|
|
11740
|
+
function ReportPlaceholder({ item, frame }) {
|
|
11741
|
+
const { zoom } = useReportRender();
|
|
11742
|
+
return (jsxRuntime.jsx(CwTooltipNew, { content: `${item.Element}: ${item.Reason}`, position: "top", children: jsxRuntime.jsx("div", { className: styles$1.placeholder, style: frameCss(frame, zoom), "aria-label": `${item.Element}: ${item.Reason}`, children: item.Element }) }));
|
|
11743
|
+
}
|
|
11744
|
+
/** One render-tree item, absolutely positioned in its container; nothing when its toggles hide it. */
|
|
11745
|
+
function ReportItem({ item, itemKey, frame }) {
|
|
11746
|
+
const { zoom, flipped } = useReportRender();
|
|
11747
|
+
if (!isRdlVisible(item.Toggles, flipped))
|
|
11748
|
+
return null;
|
|
11749
|
+
const box = frame ?? frameOf(item);
|
|
11750
|
+
switch (item.Kind) {
|
|
11751
|
+
case "textbox": return jsxRuntime.jsx(ReportTextbox, { textbox: item, itemKey: itemKey, frameStyle: frameCss(box, zoom) });
|
|
11752
|
+
case "rectangle": return jsxRuntime.jsx(ReportRectangle, { item: item, itemKey: itemKey, frame: box });
|
|
11753
|
+
case "line": return jsxRuntime.jsx(ReportLine, { item: item, frame: box });
|
|
11754
|
+
case "image": return jsxRuntime.jsx(ReportImage, { item: item, frame: box });
|
|
11755
|
+
case "table": return jsxRuntime.jsx(ReportTable, { item: item, itemKey: itemKey, frame: box });
|
|
11756
|
+
case "chart": return jsxRuntime.jsx(ReportChart, { item: item, frame: box });
|
|
11757
|
+
case "gauge": return jsxRuntime.jsx(ReportGauge, { item: item, frame: box });
|
|
11758
|
+
case "placeholder": return jsxRuntime.jsx(ReportPlaceholder, { item: item, frame: box });
|
|
11759
|
+
default: return null;
|
|
11760
|
+
}
|
|
11761
|
+
}
|
|
11762
|
+
function ReportItems({ items, parentKey }) {
|
|
11763
|
+
return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: items.map((item, index) => (jsxRuntime.jsx(ReportItem, { item: item, itemKey: rdlItemKey(parentKey, index) }, index))) }));
|
|
11764
|
+
}
|
|
11765
|
+
|
|
11766
|
+
const NO_FLIPS$1 = new Set();
|
|
11767
|
+
const NO_MATCHES = [];
|
|
11768
|
+
const DEFAULT_MARGINS = {
|
|
11769
|
+
top: DEFAULT_PAGE_MARGIN_PT,
|
|
11770
|
+
right: DEFAULT_PAGE_MARGIN_PT,
|
|
11771
|
+
bottom: DEFAULT_PAGE_MARGIN_PT,
|
|
11772
|
+
left: DEFAULT_PAGE_MARGIN_PT
|
|
11773
|
+
};
|
|
11774
|
+
function Band({ section, sectionKey, zoom, bodyWidth, as: Element }) {
|
|
11775
|
+
return (jsxRuntime.jsx(Element, { className: styles$1.section, style: { ...rdlBoxStyle(section.Style, zoom), width: ptToPx(bodyWidth, zoom), height: ptToPx(section.Height, zoom) }, children: jsxRuntime.jsx(ReportItems, { items: section.Items, parentKey: sectionKey }) }));
|
|
11776
|
+
}
|
|
11777
|
+
/**
|
|
11778
|
+
* One page of a rendered RDL report drawn as a sheet of paper: page header, body and page footer,
|
|
11779
|
+
* every item absolutely positioned in points scaled by the zoom. Presentational: toggle state,
|
|
11780
|
+
* sorting, images and find matches come from the host.
|
|
11781
|
+
*/
|
|
11782
|
+
function CwReportPage({ page, bodyWidth, zoom = 1, culture, margins = DEFAULT_MARGINS, flippedToggles = NO_FLIPS$1, onToggle, onSort, getImageUrl, findMatches = NO_MATCHES, activeMatchIndex = -1, className }) {
|
|
11783
|
+
const sheetRef = React.useRef(null);
|
|
11784
|
+
const highlights = React.useMemo(() => highlightsByRun(findMatches), [findMatches]);
|
|
11785
|
+
const context = React.useMemo(() => ({
|
|
11786
|
+
zoom,
|
|
11787
|
+
culture,
|
|
11788
|
+
flipped: flippedToggles,
|
|
11789
|
+
onToggle,
|
|
11790
|
+
onSort,
|
|
11791
|
+
getImageUrl,
|
|
11792
|
+
highlights,
|
|
11793
|
+
activeMatchIndex
|
|
11794
|
+
}), [zoom, culture, flippedToggles, onToggle, onSort, getImageUrl, highlights, activeMatchIndex]);
|
|
11795
|
+
React.useEffect(() => {
|
|
11796
|
+
if (activeMatchIndex < 0)
|
|
11797
|
+
return;
|
|
11798
|
+
const active = sheetRef.current?.querySelector("[data-active-match='true']");
|
|
11799
|
+
active?.scrollIntoView?.({ block: "center", inline: "nearest" });
|
|
11800
|
+
}, [activeMatchIndex, findMatches]);
|
|
11801
|
+
const sheet = pageSheetSizePt(page, bodyWidth, margins);
|
|
11802
|
+
return (jsxRuntime.jsx(ReportRenderContext.Provider, { value: context, children: jsxRuntime.jsxs("div", { ref: sheetRef, className: `${styles$1.sheet}${className ? ` ${className}` : ""}`, style: {
|
|
11803
|
+
width: ptToPx(sheet.width, zoom),
|
|
11804
|
+
height: ptToPx(sheet.height, zoom),
|
|
11805
|
+
padding: `${ptToPx(margins.top, zoom)}px ${ptToPx(margins.right, zoom)}px ${ptToPx(margins.bottom, zoom)}px ${ptToPx(margins.left, zoom)}px`
|
|
11806
|
+
}, "data-page": page.Number, children: [page.Header && jsxRuntime.jsx(Band, { section: page.Header, sectionKey: RDL_SECTION_KEYS.header, zoom: zoom, bodyWidth: bodyWidth, as: "header" }), jsxRuntime.jsx("div", { className: styles$1.section, style: { width: ptToPx(bodyWidth, zoom), height: ptToPx(page.Height, zoom) }, children: jsxRuntime.jsx(ReportItems, { items: page.Items, parentKey: RDL_SECTION_KEYS.body }) }), page.Footer && jsxRuntime.jsx(Band, { section: page.Footer, sectionKey: RDL_SECTION_KEYS.footer, zoom: zoom, bodyWidth: bodyWidth, as: "footer" })] }) }));
|
|
11807
|
+
}
|
|
11808
|
+
|
|
11809
|
+
const DATE_PREFIX = /^(\d{4}-\d{2}-\d{2})(?:[T ](\d{2}):(\d{2})(?::(\d{2}))?)?/;
|
|
11810
|
+
/** Reads the date and time of an ISO 8601 text (any offset is ignored, the wall clock is kept). Null when it is not a date. */
|
|
11811
|
+
function parseRdlDateTime(text) {
|
|
11812
|
+
const match = DATE_PREFIX.exec(text.trim());
|
|
11813
|
+
if (!match)
|
|
11814
|
+
return null;
|
|
11815
|
+
try {
|
|
11816
|
+
return Temporal.PlainDateTime.from(`${match[1]}T${match[2] ?? "00"}:${match[3] ?? "00"}:${match[4] ?? "00"}`);
|
|
11817
|
+
}
|
|
11818
|
+
catch {
|
|
11819
|
+
return null;
|
|
11820
|
+
}
|
|
11821
|
+
}
|
|
11822
|
+
/** A date-time as invariant text "YYYY-MM-DDTHH:mm:ss". */
|
|
11823
|
+
function formatRdlDateTime(value) {
|
|
11824
|
+
return value.toString({ smallestUnit: "second", calendarName: "never" });
|
|
11825
|
+
}
|
|
11826
|
+
/** A typed API value as the text of an option or an editor (null stays null). */
|
|
11827
|
+
function rdlValueToText(value) {
|
|
11828
|
+
if (value === null || value === undefined)
|
|
11829
|
+
return null;
|
|
11830
|
+
if (typeof value === "boolean")
|
|
11831
|
+
return value ? "true" : "false";
|
|
11832
|
+
if (typeof value === "number")
|
|
11833
|
+
return numberToInvariant(value);
|
|
11834
|
+
return value;
|
|
11835
|
+
}
|
|
11836
|
+
function numberToInvariant(value) {
|
|
11837
|
+
return Number.isFinite(value) ? value.toString() : "";
|
|
11838
|
+
}
|
|
11839
|
+
function isDateTime(parameter) {
|
|
11840
|
+
return parameter.DataType.toLowerCase() === "datetime" || parameter.InputType.toLowerCase() === "datetime";
|
|
11841
|
+
}
|
|
11842
|
+
function isBoolean(parameter) {
|
|
11843
|
+
return parameter.DataType.toLowerCase() === "boolean" || parameter.InputType.toLowerCase() === "boolean";
|
|
11844
|
+
}
|
|
11845
|
+
function isTextInput(parameter) {
|
|
11846
|
+
return parameter.InputType.toLowerCase() === "textbox";
|
|
11847
|
+
}
|
|
11848
|
+
function isNumeric(parameter) {
|
|
11849
|
+
const type = parameter.DataType.toLowerCase();
|
|
11850
|
+
return type === "integer" || type === "float";
|
|
11851
|
+
}
|
|
11852
|
+
function toEditorScalar(parameter, value) {
|
|
11853
|
+
if (isDateTime(parameter) && parameter.InputType.toLowerCase() !== "dropdownlist") {
|
|
11854
|
+
const text = String(value);
|
|
11855
|
+
return parseRdlDateTime(text) ?? text;
|
|
11856
|
+
}
|
|
11857
|
+
if (isBoolean(parameter) && parameter.InputType.toLowerCase() !== "dropdownlist") {
|
|
11858
|
+
return typeof value === "boolean" ? value : String(value).toLowerCase() === "true";
|
|
11859
|
+
}
|
|
11860
|
+
return rdlValueToText(value) ?? "";
|
|
11861
|
+
}
|
|
11862
|
+
/** The editor state of one parameter from the values the server sent; a NULL value is an empty field. */
|
|
11863
|
+
function initialParameterEdit(parameter) {
|
|
11864
|
+
const scalars = (parameter.Values ?? [])
|
|
11865
|
+
.filter((value) => value !== null && value !== undefined)
|
|
11866
|
+
.map(value => toEditorScalar(parameter, value));
|
|
11867
|
+
return { values: parameter.MultiValue ? scalars : scalars.slice(0, 1) };
|
|
11868
|
+
}
|
|
11869
|
+
/** The values that count: a multi-value text drops its empty lines, a single value keeps only the first. */
|
|
11870
|
+
function enteredValues(parameter, edit) {
|
|
11871
|
+
const values = edit.values.filter(value => value !== "");
|
|
11872
|
+
return parameter.MultiValue ? values : values.slice(0, 1);
|
|
11873
|
+
}
|
|
11874
|
+
/** The editor state of a whole form. */
|
|
11875
|
+
function initialParameterEdits(parameters) {
|
|
11876
|
+
const edits = {};
|
|
11877
|
+
for (const parameter of parameters)
|
|
11878
|
+
edits[parameter.Name] = initialParameterEdit(parameter);
|
|
11879
|
+
return edits;
|
|
11880
|
+
}
|
|
11881
|
+
/** One editor value as invariant text. */
|
|
11882
|
+
function editorScalarToInvariant(parameter, value) {
|
|
11883
|
+
if (typeof value === "boolean")
|
|
11884
|
+
return value ? "true" : "false";
|
|
11885
|
+
if (typeof value !== "string")
|
|
11886
|
+
return formatRdlDateTime(value);
|
|
11887
|
+
if (isNumeric(parameter))
|
|
11888
|
+
return value.trim().replace(",", ".");
|
|
11889
|
+
return value;
|
|
11890
|
+
}
|
|
11891
|
+
/**
|
|
11892
|
+
* The values the API takes (RdlParameterValuesRequest.Values): every parameter, each as an array of
|
|
11893
|
+
* invariant text. An empty field is [null] when the parameter is nullable, [""] for a single text,
|
|
11894
|
+
* else []. A parameter without an edit sends the values the server gave it.
|
|
11895
|
+
*
|
|
11896
|
+
* A nullable boolean whose value is NULL shows neither True nor False and keeps sending NULL until
|
|
11897
|
+
* the user picks one; from then on it sends "true" / "false" (a radio group cannot be emptied again).
|
|
11898
|
+
*/
|
|
11899
|
+
function toRdlParameterValues(parameters, edits) {
|
|
11900
|
+
const result = {};
|
|
11901
|
+
for (const parameter of parameters) {
|
|
11902
|
+
const edit = edits[parameter.Name] ?? initialParameterEdit(parameter);
|
|
11903
|
+
const texts = enteredValues(parameter, edit).map(value => editorScalarToInvariant(parameter, value));
|
|
11904
|
+
if (texts.length > 0) {
|
|
11905
|
+
result[parameter.Name] = texts;
|
|
11906
|
+
}
|
|
11907
|
+
else if (parameter.Nullable) {
|
|
11908
|
+
result[parameter.Name] = [null];
|
|
11909
|
+
}
|
|
11910
|
+
else {
|
|
11911
|
+
result[parameter.Name] = !parameter.MultiValue && isTextInput(parameter) ? [""] : [];
|
|
11912
|
+
}
|
|
11913
|
+
}
|
|
11914
|
+
return result;
|
|
11915
|
+
}
|
|
11916
|
+
/** True when any other parameter lists `name` in its DependsOn: a change must re-resolve the form. */
|
|
11917
|
+
function hasDependentParameters(parameters, name) {
|
|
11918
|
+
return parameters.some(parameter => parameter.Name !== name && parameter.DependsOn.includes(name));
|
|
11919
|
+
}
|
|
11920
|
+
/** Whether the user edits the parameter (hidden and server-filled parameters are not editable). */
|
|
11921
|
+
function isEditableParameter(parameter) {
|
|
11922
|
+
return !parameter.Hidden && !parameter.ServerValue;
|
|
11923
|
+
}
|
|
11924
|
+
/** Whether the form has anything for the user to fill in (some parameter is neither hidden nor server-filled). */
|
|
11925
|
+
function hasEditableParameters(parameters) {
|
|
11926
|
+
return parameters.some(isEditableParameter);
|
|
11927
|
+
}
|
|
11928
|
+
/**
|
|
11929
|
+
* Client-side checks before "View report": a value is required unless the parameter is nullable
|
|
11930
|
+
* (empty = NULL) or allows blank text (AllowBlank), and numbers / dates must parse. Hidden and
|
|
11931
|
+
* server-filled parameters are the server's business. Returns parameter name to message.
|
|
11932
|
+
*/
|
|
11933
|
+
function validateParameterEdits(parameters, edits) {
|
|
11934
|
+
const errors = {};
|
|
11935
|
+
for (const parameter of parameters) {
|
|
11936
|
+
if (!isEditableParameter(parameter))
|
|
11937
|
+
continue;
|
|
11938
|
+
const edit = edits[parameter.Name] ?? initialParameterEdit(parameter);
|
|
11939
|
+
const values = enteredValues(parameter, edit);
|
|
11940
|
+
if (values.length === 0) {
|
|
11941
|
+
const blankAllowed = parameter.Nullable
|
|
11942
|
+
|| (parameter.AllowBlank && !parameter.MultiValue && isTextInput(parameter) && !isNumeric(parameter));
|
|
11943
|
+
if (!blankAllowed)
|
|
11944
|
+
errors[parameter.Name] = parameter.MultiValue ? "Select at least one value." : "A value is required.";
|
|
11945
|
+
continue;
|
|
11946
|
+
}
|
|
11947
|
+
if (isNumeric(parameter) && parameter.InputType.toLowerCase() !== "dropdownlist") {
|
|
11948
|
+
const integer = parameter.DataType.toLowerCase() === "integer";
|
|
11949
|
+
const invalid = values.some(value => {
|
|
11950
|
+
const text = editorScalarToInvariant(parameter, value);
|
|
11951
|
+
return integer ? !/^-?\d+$/.test(text) : !/^-?(\d+\.?\d*|\.\d+)$/.test(text);
|
|
11952
|
+
});
|
|
11953
|
+
if (invalid)
|
|
11954
|
+
errors[parameter.Name] = integer ? "Enter a whole number." : "Enter a number.";
|
|
11955
|
+
continue;
|
|
11956
|
+
}
|
|
11957
|
+
if (isDateTime(parameter) && parameter.InputType.toLowerCase() !== "dropdownlist"
|
|
11958
|
+
&& values.some(value => typeof value === "string")) {
|
|
11959
|
+
errors[parameter.Name] = "Enter a valid date.";
|
|
11960
|
+
}
|
|
11961
|
+
}
|
|
11962
|
+
return errors;
|
|
11963
|
+
}
|
|
11964
|
+
|
|
11965
|
+
var styles = {"viewer":"cw-report-viewer-module__viewer__SYHSE","sidebar":"cw-report-viewer-module__sidebar__1ZSM2","resizer":"cw-report-viewer-module__resizer__JdnQI","main":"cw-report-viewer-module__main__qsPXX","parameterForm":"cw-report-viewer-module__parameterForm__H01sS","parameterFields":"cw-report-viewer-module__parameterFields__D2BuK","parameterActions":"cw-report-viewer-module__parameterActions__01RD4","viewButton":"cw-report-viewer-module__viewButton__1CMHL","parameterGrid":"cw-report-viewer-module__parameterGrid__Jyn0M","toolbar":"cw-report-viewer-module__toolbar__9gKXV","reportName":"cw-report-viewer-module__reportName__g5nsm","findStatus":"cw-report-viewer-module__findStatus__4gLGb","warningsButton":"cw-report-viewer-module__warningsButton__GIKjH","warningsPanel":"cw-report-viewer-module__warningsPanel__rFHBP","warningList":"cw-report-viewer-module__warningList__Ouk7x","menu":"cw-report-viewer-module__menu__ufxUU","menuItem":"cw-report-viewer-module__menuItem__hBmdD","canvas":"cw-report-viewer-module__canvas__9KuUT","canvasNote":"cw-report-viewer-module__canvasNote__K9XP0","sheetSkeleton":"cw-report-viewer-module__sheetSkeleton__DsSaA","emptyState":"cw-report-viewer-module__emptyState__pJyQ1"};
|
|
11966
|
+
|
|
11967
|
+
/** Above this many options a single-select dropdown gets a search box. */
|
|
11968
|
+
const SEARCH_THRESHOLD = 10;
|
|
11969
|
+
const LABEL_LAYOUT = { flexDirection: "column" };
|
|
11970
|
+
const BOOLEAN_OPTIONS = [
|
|
11971
|
+
{ value: "true", label: "True" },
|
|
11972
|
+
{ value: "false", label: "False" }
|
|
11973
|
+
];
|
|
11974
|
+
function scalarText(value) {
|
|
11975
|
+
if (value === undefined)
|
|
11976
|
+
return "";
|
|
11977
|
+
if (typeof value === "boolean")
|
|
11978
|
+
return value ? "true" : "false";
|
|
11979
|
+
return typeof value === "string" ? value : value.toString();
|
|
11980
|
+
}
|
|
11981
|
+
function feedbackFor(parameter, clientError) {
|
|
11982
|
+
const messages = [];
|
|
11983
|
+
if (parameter.ValidValuesError)
|
|
11984
|
+
messages.push({ type: "danger", message: parameter.ValidValuesError });
|
|
11985
|
+
if (parameter.ValueError)
|
|
11986
|
+
messages.push({ type: "danger", message: parameter.ValueError });
|
|
11987
|
+
if (clientError)
|
|
11988
|
+
messages.push({ type: "danger", message: clientError });
|
|
11989
|
+
return messages.length > 0 ? messages : undefined;
|
|
11990
|
+
}
|
|
11991
|
+
function ParameterField({ parameter, edit, error, disabled, setEdit, commit }) {
|
|
11992
|
+
const label = { text: parameter.Prompt || parameter.Name };
|
|
11993
|
+
const feedback = feedbackFor(parameter, error);
|
|
11994
|
+
const testId = `report-parameter-${parameter.Name}`;
|
|
11995
|
+
// A NULL valid value is no option of its own: leaving the field empty is NULL.
|
|
11996
|
+
const options = parameter.ValidValues
|
|
11997
|
+
.filter(option => option.Value !== null && option.Value !== undefined)
|
|
11998
|
+
.map(option => ({ value: rdlValueToText(option.Value) ?? "", label: option.Label }));
|
|
11999
|
+
switch (parameter.InputType.toLowerCase()) {
|
|
12000
|
+
case "dropdownlist":
|
|
12001
|
+
if (parameter.MultiValue) {
|
|
12002
|
+
return (jsxRuntime.jsx(CwTagSelector, { id: testId, labelProps: label, alignProps: LABEL_LAYOUT, options: options, value: edit.values.map(scalarText), onChange: values => setEdit({ values }, true), showSelectAll: true, disabled: disabled, feedback: feedback }));
|
|
12003
|
+
}
|
|
12004
|
+
return (jsxRuntime.jsx(CwDropdown, { "data-testid": testId, labelProps: label, alignProps: LABEL_LAYOUT, options: options, value: scalarText(edit.values[0]), onChange: value => setEdit({ values: value === undefined ? [] : [String(value)] }, true), allowSearch: options.length > SEARCH_THRESHOLD, disabled: disabled, feedback: feedback }));
|
|
12005
|
+
case "datetime": {
|
|
12006
|
+
const value = edit.values[0];
|
|
12007
|
+
return (jsxRuntime.jsx(CwDateTimePickerTemporal, { "data-testid": testId, labelProps: label, alignProps: LABEL_LAYOUT, value: value instanceof Temporal.PlainDateTime ? value : undefined, onChange: dateTime => setEdit({ values: dateTime ? [dateTime] : [] }, true), disabled: disabled, feedback: feedback }));
|
|
12008
|
+
}
|
|
12009
|
+
case "boolean":
|
|
12010
|
+
return (jsxRuntime.jsx(CwRadioGroup, { "data-testid": testId, labelProps: label, groupLayout: { direction: "row" }, options: BOOLEAN_OPTIONS, value: edit.values.length > 0 ? scalarText(edit.values[0]) : undefined, onChange: value => setEdit({ values: [value === "true"] }, true), disabled: disabled, feedback: feedback }));
|
|
12011
|
+
case "integer":
|
|
12012
|
+
case "float":
|
|
12013
|
+
return (jsxRuntime.jsx(CwDigit, { "data-testid": testId, labelProps: label, alignProps: LABEL_LAYOUT, step: parameter.InputType.toLowerCase() === "integer" ? 1 : "any", value: scalarText(edit.values[0]), onChange: e => {
|
|
12014
|
+
const text = e.currentTarget.value;
|
|
12015
|
+
setEdit({ values: text === "" ? [] : [text] }, false);
|
|
12016
|
+
}, onBlur: commit, disabled: disabled, feedback: feedback }));
|
|
12017
|
+
default:
|
|
12018
|
+
if (parameter.MultiValue) {
|
|
12019
|
+
return (jsxRuntime.jsx(CwTextArea, { "data-testid": testId, labelProps: label, alignProps: LABEL_LAYOUT, value: edit.values.map(scalarText).join("\n"), onChange: e => {
|
|
12020
|
+
// Keep empty lines while typing (Enter must survive the re-render); they are dropped on serialization.
|
|
12021
|
+
setEdit({ values: e.currentTarget.value.split("\n") }, false);
|
|
12022
|
+
}, onBlur: commit, disabled: disabled, feedback: feedback ?? { type: "neutral", message: "One value per line." } }));
|
|
12023
|
+
}
|
|
12024
|
+
return (jsxRuntime.jsx(CwInputText, { "data-testid": testId, labelProps: label, alignProps: LABEL_LAYOUT, value: scalarText(edit.values[0]), onChange: e => {
|
|
12025
|
+
const text = e.currentTarget.value;
|
|
12026
|
+
setEdit({ values: [text] }, false);
|
|
12027
|
+
}, onBlur: commit, disabled: disabled, feedback: feedback }));
|
|
12028
|
+
}
|
|
12029
|
+
}
|
|
12030
|
+
/**
|
|
12031
|
+
* The report parameter form in the server's order: dropdowns (multi-value as tag selector with
|
|
12032
|
+
* select-all), date-time pickers, True/False, numbers and text. There is no NULL control: an empty
|
|
12033
|
+
* field of a nullable parameter is sent as NULL (see rdlParameterValues), as in the old WinOps
|
|
12034
|
+
* report page. Hidden parameters are not drawn; server-filled ones are read-only. Presentational:
|
|
12035
|
+
* the host owns the editor state (`useReportParameterForm`); a change to a parameter others depend
|
|
12036
|
+
* on calls its `onResolve`, "View report" its `onRender` with the values as invariant text
|
|
12037
|
+
* (`toRdlParameterValues`).
|
|
12038
|
+
*
|
|
12039
|
+
* The form fills its parent's height when the parent gives it one: the fields scroll and the
|
|
12040
|
+
* "View report" footer stays pinned at the bottom. In an unbounded parent it simply grows.
|
|
12041
|
+
*/
|
|
12042
|
+
function CwReportParameterForm({ controller, loading = false, rendering = false }) {
|
|
12043
|
+
const { form } = controller;
|
|
12044
|
+
const visible = form?.Parameters.filter(parameter => !parameter.Hidden) ?? [];
|
|
12045
|
+
return (jsxRuntime.jsxs("form", { className: styles.parameterForm, "data-testid": "report-parameter-form", onSubmit: e => {
|
|
12046
|
+
e.preventDefault();
|
|
12047
|
+
controller.submit();
|
|
12048
|
+
}, children: [jsxRuntime.jsx("div", { className: styles.parameterFields, children: form ? (jsxRuntime.jsx(CwLoading, { isLoading: loading, size: "small", children: visible.length > 0 && (jsxRuntime.jsx("div", { className: `${styles.parameterGrid} cw-grid-base-auto`, children: visible.map(parameter => (jsxRuntime.jsx(ParameterField, { parameter: parameter, edit: controller.edits[parameter.Name] ?? { values: [] }, error: controller.errors[parameter.Name], disabled: loading || !!parameter.ServerValue, setEdit: (edit, commit) => controller.setEdit(parameter.Name, edit, commit), commit: () => controller.commit(parameter.Name) }, parameter.Name))) })) })) : (jsxRuntime.jsxs("div", { className: `${styles.parameterGrid} cw-grid-base-auto`, "aria-busy": "true", children: [jsxRuntime.jsx(CwSkeleton, { variant: "rect", height: "3.5rem" }), jsxRuntime.jsx(CwSkeleton, { variant: "rect", height: "3.5rem" }), jsxRuntime.jsx(CwSkeleton, { variant: "rect", height: "3.5rem" })] })) }), jsxRuntime.jsx("footer", { className: styles.parameterActions, children: jsxRuntime.jsx(CwButton, { type: "submit", text: "View report", icon: "file-view", className: styles.viewButton, disabled: !form || loading || rendering, "data-testid": "report-view-button" }) })] }));
|
|
12049
|
+
}
|
|
12050
|
+
|
|
12051
|
+
const REPORT_EXPORT_LABELS = {
|
|
12052
|
+
pdf: "PDF",
|
|
12053
|
+
xlsx: "Excel",
|
|
12054
|
+
docx: "Word"
|
|
12055
|
+
};
|
|
12056
|
+
/** A render with warnings asks before exporting or printing; without warnings the action runs at once. */
|
|
12057
|
+
function needsOutputConfirmation(warnings) {
|
|
12058
|
+
return (warnings?.length ?? 0) > 0;
|
|
12059
|
+
}
|
|
12060
|
+
/** Texts of the confirmation shown before an export or print of a render with warnings. */
|
|
12061
|
+
function outputConfirmation(action, warningCount) {
|
|
12062
|
+
const count = `${warningCount} warning${warningCount === 1 ? "" : "s"}`;
|
|
12063
|
+
if (action.kind === "print") {
|
|
12064
|
+
return {
|
|
12065
|
+
headline: "Print report",
|
|
12066
|
+
message: `This report has ${count}. The printout may not match the report exactly.`,
|
|
12067
|
+
confirmText: "Print anyway"
|
|
12068
|
+
};
|
|
12069
|
+
}
|
|
12070
|
+
const label = REPORT_EXPORT_LABELS[action.format];
|
|
12071
|
+
return {
|
|
12072
|
+
headline: `Export as ${label}`,
|
|
12073
|
+
message: `This report has ${count}. The ${label} export may not match the report exactly.`,
|
|
12074
|
+
confirmText: "Export anyway"
|
|
12075
|
+
};
|
|
12076
|
+
}
|
|
12077
|
+
|
|
12078
|
+
const EXPORTS = [
|
|
12079
|
+
{ format: "pdf", label: REPORT_EXPORT_LABELS.pdf, icon: "file-pdf" },
|
|
12080
|
+
{ format: "xlsx", label: REPORT_EXPORT_LABELS.xlsx, icon: "table" },
|
|
12081
|
+
{ format: "docx", label: REPORT_EXPORT_LABELS.docx, icon: "document" }
|
|
12082
|
+
];
|
|
12083
|
+
function zoomToValue(zoom) {
|
|
12084
|
+
return typeof zoom === "number" ? String(zoom) : zoom;
|
|
12085
|
+
}
|
|
12086
|
+
function valueToZoom(value) {
|
|
12087
|
+
if (value === "pageWidth" || value === "wholePage")
|
|
12088
|
+
return value;
|
|
12089
|
+
const factor = parseFloat(value);
|
|
12090
|
+
return Number.isFinite(factor) && factor > 0 ? factor : 1;
|
|
12091
|
+
}
|
|
12092
|
+
function closePopover(element) {
|
|
12093
|
+
element.closest("[popover]")?.hidePopover();
|
|
12094
|
+
}
|
|
12095
|
+
function PageInput({ pageNumber, pageCount, onPage, disabled }) {
|
|
12096
|
+
const [draft, setDraft] = React.useState({ page: pageNumber, text: String(pageNumber) });
|
|
12097
|
+
const text = draft.page === pageNumber ? draft.text : String(pageNumber);
|
|
12098
|
+
const commit = () => {
|
|
12099
|
+
const parsed = parseInt(text, 10);
|
|
12100
|
+
const target = Number.isFinite(parsed) ? Math.min(Math.max(1, parsed), Math.max(1, pageCount)) : pageNumber;
|
|
12101
|
+
setDraft({ page: pageNumber, text: String(target) });
|
|
12102
|
+
if (target !== pageNumber)
|
|
12103
|
+
onPage(target);
|
|
12104
|
+
};
|
|
12105
|
+
return (jsxRuntime.jsx(CwDigit, { "data-size": "tiny", "aria-label": "Page", min: 1, max: Math.max(1, pageCount), value: text, disabled: disabled, onChange: e => {
|
|
12106
|
+
const value = e.currentTarget.value;
|
|
12107
|
+
setDraft({ page: pageNumber, text: value });
|
|
12108
|
+
}, onKeyDown: (e) => {
|
|
12109
|
+
if (e.key === "Enter")
|
|
12110
|
+
commit();
|
|
12111
|
+
}, onBlur: commit }));
|
|
12112
|
+
}
|
|
12113
|
+
/**
|
|
12114
|
+
* The report viewer toolbar, like the old ReportViewer: page navigation, refresh, back, zoom,
|
|
12115
|
+
* find on the current page, export (PDF / Excel / Word) and print. Warnings stay invisible until
|
|
12116
|
+
* there are some (Dark Cockpit). Presentational: every action is a callback.
|
|
12117
|
+
*/
|
|
12118
|
+
function CwReportToolbar({ pageNumber, pageCount, onPage, onRefresh, onBack, zoom, onZoomChange, findText, onFindTextChange, onFindNext, findMatchCount, findMatchIndex, onExport, onPrint, warnings = [], onToggleParameters, parametersVisible = true, disabled = false, reportName }) {
|
|
12119
|
+
const firstPage = disabled || pageNumber <= 1;
|
|
12120
|
+
const lastPage = disabled || pageNumber >= pageCount;
|
|
12121
|
+
const findStatus = findText.trim() === ""
|
|
12122
|
+
? ""
|
|
12123
|
+
: findMatchCount === 0 ? "No matches" : `${Math.max(0, findMatchIndex) + 1} of ${findMatchCount}`;
|
|
12124
|
+
return (jsxRuntime.jsxs("header", { className: `${styles.toolbar} cw-flex-row cw-align-left-center cw-flex-wrap cw-gap-small`, "data-testid": "report-toolbar", children: [onBack && jsxRuntime.jsx(CwButton, { variant: "icon", icon: "arrow-left", title: "Back", onClick: onBack }), onToggleParameters && (jsxRuntime.jsx(CwButton, { variant: "icon", icon: "filter-list", title: parametersVisible ? "Hide parameters" : "Show parameters", pressed: parametersVisible, onClick: onToggleParameters })), jsxRuntime.jsxs("nav", { className: "cw-flex-row cw-align-left-center cw-gap-tiny", "aria-label": "Pages", children: [jsxRuntime.jsx(CwButton, { variant: "icon", icon: "chevron-left-double", title: "First page", disabled: firstPage, onClick: () => onPage(1) }), jsxRuntime.jsx(CwButton, { variant: "icon", icon: "chevron-left", title: "Previous page", disabled: firstPage, onClick: () => onPage(pageNumber - 1) }), jsxRuntime.jsx(PageInput, { pageNumber: pageNumber, pageCount: pageCount, onPage: onPage, disabled: disabled }), jsxRuntime.jsxs("span", { className: "cw-font-size-small", children: ["of ", disabled ? "-" : pageCount] }), jsxRuntime.jsx(CwButton, { variant: "icon", icon: "chevron-right", title: "Next page", disabled: lastPage, onClick: () => onPage(pageNumber + 1) }), jsxRuntime.jsx(CwButton, { variant: "icon", icon: "chevron-right-double", title: "Last page", disabled: lastPage, onClick: () => onPage(pageCount) })] }), onRefresh && jsxRuntime.jsx(CwButton, { variant: "icon", icon: "refresh", title: "Refresh", disabled: disabled, onClick: onRefresh }), jsxRuntime.jsxs(CwSelect, { "aria-label": "Zoom", value: zoomToValue(zoom), handleChange: value => onZoomChange(valueToZoom(value)), children: [REPORT_ZOOM_STEPS.map(step => (jsxRuntime.jsx(CwOption, { value: String(step), text: `${Math.round(step * 100)}%` }, step))), jsxRuntime.jsx(CwOption, { value: "pageWidth", text: "Page width" }), jsxRuntime.jsx(CwOption, { value: "wholePage", text: "Whole page" })] }), jsxRuntime.jsxs("div", { className: "cw-flex-row cw-align-left-center cw-gap-small", children: [jsxRuntime.jsx(CwInputText, { "aria-label": "Find text", placeholder: "Find", value: findText, disabled: disabled, onChange: e => onFindTextChange(e.currentTarget.value), onKeyDown: (e) => {
|
|
12125
|
+
if (e.key === "Enter")
|
|
12126
|
+
onFindNext();
|
|
12127
|
+
}, buttonProps: {
|
|
12128
|
+
text: "Next",
|
|
12129
|
+
variant: "outline",
|
|
12130
|
+
disabled: disabled || findMatchCount === 0,
|
|
12131
|
+
onClick: onFindNext
|
|
12132
|
+
} }), jsxRuntime.jsx("span", { className: `${styles.findStatus} cw-font-size-small`, "aria-live": "polite", children: findStatus })] }), jsxRuntime.jsx("div", { className: styles.reportName, children: reportName && (jsxRuntime.jsx(CwTooltipNew, { content: reportName, position: "bottom", children: jsxRuntime.jsx("strong", { className: "cw-text-ellipsis", "data-testid": "report-name", children: reportName }) })) }), warnings.length > 0 && (jsxRuntime.jsx(CwPopoverButton, { variant: "icon", icon: "warning", title: "Warnings", badge: { count: warnings.length, color: "warning" }, className: styles.warningsButton, placement: "bottom-end", panelClassName: styles.warningsPanel, children: jsxRuntime.jsx("ul", { className: styles.warningList, children: warnings.map((warning, index) => jsxRuntime.jsx("li", { children: warning }, index)) }) })), jsxRuntime.jsx(CwPopoverButton, { icon: "download", title: "Export", variant: "icon", disabled: disabled, placement: "bottom-end", children: jsxRuntime.jsx("div", { className: styles.menu, children: EXPORTS.map(item => (jsxRuntime.jsx(CwButton, { variant: "ghost", color: "neutral", text: item.label, icon: item.icon, className: styles.menuItem, onClick: e => {
|
|
12133
|
+
closePopover(e.currentTarget);
|
|
12134
|
+
onExport(item.format);
|
|
12135
|
+
} }, item.format))) }) }), jsxRuntime.jsx(CwButton, { variant: "icon", icon: "print", title: "Print", disabled: disabled, onClick: onPrint })] }));
|
|
12136
|
+
}
|
|
12137
|
+
|
|
12138
|
+
/** Width change of one arrow key press on the sidebar resize handle, in CSS pixels. */
|
|
12139
|
+
const SIDEBAR_KEY_STEP = 16;
|
|
12140
|
+
/** Keeps a sidebar width inside [min, max]; a max below min collapses to min. */
|
|
12141
|
+
function clampSidebarWidth(width, min, max) {
|
|
12142
|
+
const upper = Math.max(min, max);
|
|
12143
|
+
if (!Number.isFinite(width))
|
|
12144
|
+
return min;
|
|
12145
|
+
return Math.min(Math.max(width, min), upper);
|
|
12146
|
+
}
|
|
12147
|
+
/**
|
|
12148
|
+
* The sidebar width after a key press on the resize handle: arrows step, Home / End jump to the
|
|
12149
|
+
* bounds. Null for keys the handle does not use, so they keep their default behaviour.
|
|
12150
|
+
*/
|
|
12151
|
+
function sidebarWidthForKey(key, current, min, max, step = SIDEBAR_KEY_STEP) {
|
|
12152
|
+
switch (key) {
|
|
12153
|
+
case "ArrowLeft":
|
|
12154
|
+
return clampSidebarWidth(current - step, min, max);
|
|
12155
|
+
case "ArrowRight":
|
|
12156
|
+
return clampSidebarWidth(current + step, min, max);
|
|
12157
|
+
case "Home":
|
|
12158
|
+
return clampSidebarWidth(min, min, max);
|
|
12159
|
+
case "End":
|
|
12160
|
+
return clampSidebarWidth(max, min, max);
|
|
12161
|
+
default:
|
|
12162
|
+
return null;
|
|
12163
|
+
}
|
|
12164
|
+
}
|
|
12165
|
+
/**
|
|
12166
|
+
* Whether the viewer shows the parameter sidebar. While the form loads for the first time it shows
|
|
12167
|
+
* the skeleton; once loaded only when the user has something to fill in. A report whose parameters
|
|
12168
|
+
* are all hidden or server-filled (or that has none) gets no sidebar.
|
|
12169
|
+
*/
|
|
12170
|
+
function showsParameterSidebar(parameters, loadingParameters) {
|
|
12171
|
+
return parameters ? hasEditableParameters(parameters.Parameters) : loadingParameters;
|
|
12172
|
+
}
|
|
12173
|
+
|
|
12174
|
+
/**
|
|
12175
|
+
* Width of a left sidebar resized by dragging the handle on its right edge, like the master panel
|
|
12176
|
+
* of CwMasterDetail. The handle captures the pointer, so the drag keeps going outside it; the
|
|
12177
|
+
* keyboard steps the width for users who cannot drag.
|
|
12178
|
+
*/
|
|
12179
|
+
function useSidebarResize(initialWidth, minWidth, maxWidth) {
|
|
12180
|
+
const [storedWidth, setStoredWidth] = React.useState(initialWidth);
|
|
12181
|
+
const [resizing, setResizing] = React.useState(false);
|
|
12182
|
+
const drag = React.useRef(null);
|
|
12183
|
+
const width = clampSidebarWidth(storedWidth, minWidth, maxWidth);
|
|
12184
|
+
const onPointerDown = React.useCallback((e) => {
|
|
12185
|
+
if (e.button !== 0)
|
|
12186
|
+
return;
|
|
12187
|
+
e.preventDefault();
|
|
12188
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
12189
|
+
drag.current = { startX: e.clientX, startWidth: width };
|
|
12190
|
+
setResizing(true);
|
|
12191
|
+
}, [width]);
|
|
12192
|
+
const onPointerMove = React.useCallback((e) => {
|
|
12193
|
+
const start = drag.current;
|
|
12194
|
+
if (!start)
|
|
12195
|
+
return;
|
|
12196
|
+
setStoredWidth(clampSidebarWidth(start.startWidth + e.clientX - start.startX, minWidth, maxWidth));
|
|
12197
|
+
}, [minWidth, maxWidth]);
|
|
12198
|
+
const endDrag = React.useCallback((e) => {
|
|
12199
|
+
if (!drag.current)
|
|
12200
|
+
return;
|
|
12201
|
+
drag.current = null;
|
|
12202
|
+
if (e.currentTarget.hasPointerCapture(e.pointerId))
|
|
12203
|
+
e.currentTarget.releasePointerCapture(e.pointerId);
|
|
12204
|
+
setResizing(false);
|
|
12205
|
+
}, []);
|
|
12206
|
+
const onKeyDown = React.useCallback((e) => {
|
|
12207
|
+
const next = sidebarWidthForKey(e.key, width, minWidth, maxWidth);
|
|
12208
|
+
if (next === null)
|
|
12209
|
+
return;
|
|
12210
|
+
e.preventDefault();
|
|
12211
|
+
setStoredWidth(next);
|
|
12212
|
+
}, [width, minWidth, maxWidth]);
|
|
12213
|
+
return {
|
|
12214
|
+
width,
|
|
12215
|
+
resizing,
|
|
12216
|
+
handleProps: { onPointerDown, onPointerMove, onPointerUp: endDrag, onPointerCancel: endDrag, onKeyDown }
|
|
12217
|
+
};
|
|
12218
|
+
}
|
|
12219
|
+
|
|
12220
|
+
/** Space the canvas keeps around the sheet when fitting it (its padding on both sides), in CSS pixels. */
|
|
12221
|
+
const CANVAS_GUTTER = 32;
|
|
12222
|
+
function useElementSize(ref) {
|
|
12223
|
+
const [size, setSize] = React.useState({ width: 0, height: 0 });
|
|
12224
|
+
React.useEffect(() => {
|
|
12225
|
+
const element = ref.current;
|
|
12226
|
+
if (!element || typeof ResizeObserver === "undefined")
|
|
12227
|
+
return;
|
|
12228
|
+
const observer = new ResizeObserver(() => {
|
|
12229
|
+
setSize({ width: element.clientWidth, height: element.clientHeight });
|
|
12230
|
+
});
|
|
12231
|
+
observer.observe(element);
|
|
12232
|
+
return () => observer.disconnect();
|
|
12233
|
+
}, [ref]);
|
|
12234
|
+
return size;
|
|
12235
|
+
}
|
|
12236
|
+
/**
|
|
12237
|
+
* The RDL report viewer: a resizable, collapsible parameter sidebar on the left, the toolbar and
|
|
12238
|
+
* the page on a neutral canvas on the right, like the old ReportViewer. The sidebar only exists
|
|
12239
|
+
* when the user has a parameter to fill in. Presentational: it fetches nothing and owns no report
|
|
12240
|
+
* state. The host calls `useReportViewerState` and `useReportParameterForm` and passes their results
|
|
12241
|
+
* as `state` and `parameterForm`; every request goes out through the callbacks given to those hooks
|
|
12242
|
+
* (or `onSort` / `onRefresh` / `onBack`) and comes back as their arguments.
|
|
12243
|
+
*/
|
|
12244
|
+
function CwReportViewer({ definition, state, parameterForm, loadingParameters = false, loadingRender = false, error, onSort, onRefresh, onBack, getImageUrl, sidebarWidth = 320, minSidebarWidth = 240, maxSidebarWidth = 600, resizable = true, className }) {
|
|
12245
|
+
const { result } = state;
|
|
12246
|
+
const parameters = parameterForm.form;
|
|
12247
|
+
const sidebar = useSidebarResize(sidebarWidth, minSidebarWidth, maxSidebarWidth);
|
|
12248
|
+
const canvasRef = React.useRef(null);
|
|
12249
|
+
const canvasSize = useElementSize(canvasRef);
|
|
12250
|
+
const hasSidebar = showsParameterSidebar(parameters, loadingParameters);
|
|
12251
|
+
const sidebarOpen = hasSidebar && state.parametersVisible;
|
|
12252
|
+
const warnings = result?.Warnings ?? [];
|
|
12253
|
+
const reportName = result?.Name || definition?.Name || null;
|
|
12254
|
+
const confirmation = state.pendingOutput ? outputConfirmation(state.pendingOutput, warnings.length) : null;
|
|
12255
|
+
/** Where the clicked toggle sat in the canvas, so the re-rendered page keeps it in the same place. */
|
|
12256
|
+
const toggleAnchorRef = React.useRef(null);
|
|
12257
|
+
const handleToggle = (toggleId) => {
|
|
12258
|
+
if (state.serverToggles) {
|
|
12259
|
+
const canvas = canvasRef.current;
|
|
12260
|
+
const button = canvas?.querySelector(`[data-toggle-id="${CSS.escape(toggleId)}"]`);
|
|
12261
|
+
toggleAnchorRef.current = canvas && button
|
|
12262
|
+
? { toggleId, offset: button.getBoundingClientRect().top - canvas.getBoundingClientRect().top }
|
|
12263
|
+
: null;
|
|
12264
|
+
}
|
|
12265
|
+
state.toggle(toggleId);
|
|
12266
|
+
};
|
|
12267
|
+
React.useLayoutEffect(() => {
|
|
12268
|
+
const anchor = toggleAnchorRef.current;
|
|
12269
|
+
const canvas = canvasRef.current;
|
|
12270
|
+
if (!anchor || !canvas || !result)
|
|
12271
|
+
return;
|
|
12272
|
+
toggleAnchorRef.current = null;
|
|
12273
|
+
const button = canvas.querySelector(`[data-toggle-id="${CSS.escape(anchor.toggleId)}"]`);
|
|
12274
|
+
if (button)
|
|
12275
|
+
canvas.scrollTop += button.getBoundingClientRect().top - canvas.getBoundingClientRect().top - anchor.offset;
|
|
12276
|
+
}, [result]);
|
|
12277
|
+
// Without a sidebar (nothing to fill in) the form's "View report" lives on the canvas instead.
|
|
12278
|
+
const runButton = !hasSidebar && parameters && !result ? (jsxRuntime.jsx(CwButton, { text: "View report", icon: "file-view", disabled: loadingParameters || loadingRender, onClick: parameterForm.submit, "data-testid": "report-view-button" })) : null;
|
|
12279
|
+
const zoomFactor = result
|
|
12280
|
+
? resolveZoomFactor(state.zoom, pageSheetSizePt(result.Page, result.BodyWidth, SCREEN_PAGE_MARGINS), { width: canvasSize.width - CANVAS_GUTTER, height: canvasSize.height - CANVAS_GUTTER })
|
|
12281
|
+
: 1;
|
|
12282
|
+
const renderCanvas = () => {
|
|
12283
|
+
if (error) {
|
|
12284
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(CwNote, { color: "danger", variant: "soft", icon: "warning", content: error, className: styles.canvasNote }), runButton] }));
|
|
12285
|
+
}
|
|
12286
|
+
if (!result) {
|
|
12287
|
+
if (loadingRender) {
|
|
12288
|
+
return (jsxRuntime.jsx("div", { className: styles.sheetSkeleton, "aria-busy": "true", children: jsxRuntime.jsx(CwSkeleton, { variant: "text", count: 14 }) }));
|
|
12289
|
+
}
|
|
12290
|
+
return (jsxRuntime.jsxs("div", { className: `${styles.emptyState} cw-flex-column cw-align-center-center cw-gap-small`, children: [jsxRuntime.jsx(CwIcon, { iconId: "file-report", size: "huge", color: "neutral" }), jsxRuntime.jsx("p", { className: "cw-margin-none", children: hasSidebar ? "Set the parameters and select View report." : "Select View report to run the report." }), runButton] }));
|
|
12291
|
+
}
|
|
12292
|
+
return (jsxRuntime.jsx(CwLoading, { isLoading: loadingRender, children: jsxRuntime.jsx(CwReportPage, { page: result.Page, bodyWidth: result.BodyWidth, zoom: zoomFactor, culture: result.Culture, margins: SCREEN_PAGE_MARGINS, flippedToggles: state.drawnFlips, onToggle: handleToggle, onSort: onSort, getImageUrl: getImageUrl, findMatches: state.findMatches, activeMatchIndex: state.findIndex }) }));
|
|
12293
|
+
};
|
|
12294
|
+
return (jsxRuntime.jsxs("section", { className: `${styles.viewer}${className ? ` ${className}` : ""}`, "aria-label": result?.Name ?? definition?.Name ?? "Report", "data-resizing": sidebar.resizing || undefined, "data-testid": "report-viewer", children: [sidebarOpen && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("aside", { className: styles.sidebar, style: { width: sidebar.width }, "aria-label": "Report parameters", "data-testid": "report-parameter-sidebar", children: jsxRuntime.jsx(CwReportParameterForm, { controller: parameterForm, loading: loadingParameters, rendering: loadingRender }) }), resizable && (jsxRuntime.jsx("div", { className: styles.resizer, role: "separator", "aria-orientation": "vertical", "aria-label": "Resize parameters", "aria-valuenow": sidebar.width, "aria-valuemin": minSidebarWidth, "aria-valuemax": Math.max(minSidebarWidth, maxSidebarWidth), tabIndex: 0, "data-resizing": sidebar.resizing, ...sidebar.handleProps }))] })), jsxRuntime.jsxs("div", { className: styles.main, children: [jsxRuntime.jsx(CwReportToolbar, { pageNumber: result?.Page.Number ?? 1, pageCount: result?.PageCount ?? 1, onPage: state.goToPage, onRefresh: onRefresh, onBack: onBack, zoom: state.zoom, onZoomChange: state.setZoom, findText: state.findText, onFindTextChange: state.setFindText, onFindNext: state.findNext, findMatchCount: state.findMatches.length, findMatchIndex: state.findIndex, onExport: format => state.requestOutput({ kind: "export", format }), onPrint: () => state.requestOutput({ kind: "print" }), warnings: warnings, reportName: reportName, onToggleParameters: hasSidebar ? state.toggleParameters : undefined, parametersVisible: state.parametersVisible, disabled: !result || loadingRender || !!error }), jsxRuntime.jsx("div", { ref: canvasRef, className: styles.canvas, "data-testid": "report-canvas", children: renderCanvas() })] }), confirmation && (jsxRuntime.jsx(CwDialog, { open: true, headline: confirmation.headline, width: "28rem", onClose: state.cancelOutput, customFooter: [
|
|
12295
|
+
jsxRuntime.jsx(CwButton, { text: "Cancel", variant: "outline", onClick: state.cancelOutput }, "cancel"),
|
|
12296
|
+
jsxRuntime.jsx(CwButton, { text: confirmation.confirmText, onClick: state.confirmOutput, "data-testid": "report-output-confirm" }, "confirm")
|
|
12297
|
+
], "data-testid": "report-output-dialog", children: jsxRuntime.jsx("p", { className: "cw-margin-none", children: confirmation.message }) })), state.permissionDialogOpen && (jsxRuntime.jsx(CwDialog, { open: true, headline: "SQL ERROR", width: "30rem", onClose: state.closePermissionDialog, customFooter: [jsxRuntime.jsx(CwButton, { text: "OK", onClick: state.closePermissionDialog }, "ok")], "data-testid": "report-permission-dialog", children: jsxRuntime.jsx("p", { className: "cw-margin-none", children: "You do not have the database permission this report needs, so some of its data could not be read. Ask an administrator to grant access to the report data." }) }))] }));
|
|
12298
|
+
}
|
|
12299
|
+
|
|
12300
|
+
const NO_FLIPS = new Set();
|
|
12301
|
+
/**
|
|
12302
|
+
* Client-side state of the report viewer around one render result, owned by the host and handed to
|
|
12303
|
+
* `CwReportViewer` as `state`. The drilldown toggles belong to a render: a new RenderId (new render,
|
|
12304
|
+
* sort) starts with none flipped, paging and server views of the same render keep them. With
|
|
12305
|
+
* `onView` the host re-renders the page for the flipped toggles, so the page and find use it as drawn
|
|
12306
|
+
* (`drawnFlips` = none). The find position belongs to the query and the page shown. The permission
|
|
12307
|
+
* dialog is dismissed per render, and a pending export / print question is dropped by a new render.
|
|
12308
|
+
*/
|
|
12309
|
+
function useReportViewerState(result, options) {
|
|
12310
|
+
const { onPage, onView, onExport, onPrint, initialZoom = 1 } = options;
|
|
12311
|
+
const renderId = result?.RenderId ?? null;
|
|
12312
|
+
const page = result?.Page ?? null;
|
|
12313
|
+
const pageNumber = page?.Number ?? 1;
|
|
12314
|
+
const warnings = result?.Warnings;
|
|
12315
|
+
const serverToggles = !!onView;
|
|
12316
|
+
const [zoom, setZoom] = React.useState(initialZoom);
|
|
12317
|
+
const [toggles, setToggles] = React.useState({ renderId: null, flipped: NO_FLIPS });
|
|
12318
|
+
const [findText, setFindText] = React.useState("");
|
|
12319
|
+
const [find, setFind] = React.useState({ key: "", index: 0 });
|
|
12320
|
+
const [parametersVisible, setParametersVisible] = React.useState(true);
|
|
12321
|
+
const [dismissedRenderId, setDismissedRenderId] = React.useState(null);
|
|
12322
|
+
const [output, setOutput] = React.useState(null);
|
|
12323
|
+
const flipped = toggles.renderId === renderId ? toggles.flipped : NO_FLIPS;
|
|
12324
|
+
const toggle = React.useCallback((toggleId) => {
|
|
12325
|
+
setToggles(previous => ({
|
|
12326
|
+
renderId,
|
|
12327
|
+
flipped: flipToggle(previous.renderId === renderId ? previous.flipped : NO_FLIPS, toggleId)
|
|
12328
|
+
}));
|
|
12329
|
+
if (onView)
|
|
12330
|
+
onView(toggledIds(flipped, toggleId), pageNumber);
|
|
12331
|
+
}, [renderId, onView, flipped, pageNumber]);
|
|
12332
|
+
const setFlipped = React.useCallback((toggleIds) => {
|
|
12333
|
+
setToggles({ renderId, flipped: new Set(toggleIds) });
|
|
12334
|
+
}, [renderId]);
|
|
12335
|
+
const goToPage = React.useCallback((target) => {
|
|
12336
|
+
if (onView && flipped.size > 0) {
|
|
12337
|
+
onView(flippedToggleIds(flipped), target);
|
|
12338
|
+
}
|
|
12339
|
+
else {
|
|
12340
|
+
onPage(target);
|
|
12341
|
+
}
|
|
12342
|
+
}, [onView, onPage, flipped]);
|
|
12343
|
+
// With server toggles the page arrives already drawn for the flipped toggles, so nothing is flipped again here.
|
|
12344
|
+
const drawnFlips = serverToggles ? NO_FLIPS : flipped;
|
|
12345
|
+
const findMatches = React.useMemo(() => (page ? findInPage(page, findText, drawnFlips) : []), [page, findText, drawnFlips]);
|
|
12346
|
+
const findKey = `${renderId ?? ""}|${page?.Number ?? 0}|${findText}`;
|
|
12347
|
+
const findIndex = activeMatchIndex(find.index, find.key === findKey, findMatches.length);
|
|
12348
|
+
const findNext = React.useCallback(() => {
|
|
12349
|
+
setFind({ key: findKey, index: nextMatchIndex(findIndex, findMatches.length) });
|
|
12350
|
+
}, [findKey, findIndex, findMatches.length]);
|
|
12351
|
+
const toggleParameters = React.useCallback(() => setParametersVisible(visible => !visible), []);
|
|
12352
|
+
const closePermissionDialog = React.useCallback(() => setDismissedRenderId(renderId), [renderId]);
|
|
12353
|
+
// A new render (refresh, sort) drops a question asked about the previous one.
|
|
12354
|
+
const pendingOutput = output && output.renderId === renderId ? output.action : null;
|
|
12355
|
+
const runOutput = React.useCallback((action) => {
|
|
12356
|
+
const toggleIds = flippedToggleIds(flipped);
|
|
12357
|
+
if (action.kind === "print") {
|
|
12358
|
+
onPrint(toggleIds);
|
|
12359
|
+
}
|
|
12360
|
+
else {
|
|
12361
|
+
onExport(action.format, toggleIds);
|
|
12362
|
+
}
|
|
12363
|
+
}, [flipped, onPrint, onExport]);
|
|
12364
|
+
const requestOutput = React.useCallback((action) => {
|
|
12365
|
+
if (needsOutputConfirmation(warnings)) {
|
|
12366
|
+
setOutput({ renderId, action });
|
|
12367
|
+
}
|
|
12368
|
+
else {
|
|
12369
|
+
runOutput(action);
|
|
12370
|
+
}
|
|
12371
|
+
}, [warnings, renderId, runOutput]);
|
|
12372
|
+
const confirmOutput = React.useCallback(() => {
|
|
12373
|
+
setOutput(null);
|
|
12374
|
+
if (pendingOutput)
|
|
12375
|
+
runOutput(pendingOutput);
|
|
12376
|
+
}, [pendingOutput, runOutput]);
|
|
12377
|
+
const cancelOutput = React.useCallback(() => setOutput(null), []);
|
|
12378
|
+
return {
|
|
12379
|
+
result,
|
|
12380
|
+
serverToggles,
|
|
12381
|
+
zoom,
|
|
12382
|
+
setZoom,
|
|
12383
|
+
flipped,
|
|
12384
|
+
drawnFlips,
|
|
12385
|
+
toggle,
|
|
12386
|
+
setFlipped,
|
|
12387
|
+
goToPage,
|
|
12388
|
+
findText,
|
|
12389
|
+
setFindText,
|
|
12390
|
+
findMatches,
|
|
12391
|
+
findIndex,
|
|
12392
|
+
findNext,
|
|
12393
|
+
parametersVisible,
|
|
12394
|
+
toggleParameters,
|
|
12395
|
+
permissionDialogOpen: !!result?.PermissionDenied && dismissedRenderId !== renderId,
|
|
12396
|
+
closePermissionDialog,
|
|
12397
|
+
pendingOutput,
|
|
12398
|
+
requestOutput,
|
|
12399
|
+
confirmOutput,
|
|
12400
|
+
cancelOutput
|
|
12401
|
+
};
|
|
12402
|
+
}
|
|
12403
|
+
|
|
12404
|
+
function stateFor(form) {
|
|
12405
|
+
return { source: form, edits: initialParameterEdits(form?.Parameters ?? []), pending: new Set(), showErrors: false };
|
|
12406
|
+
}
|
|
12407
|
+
/**
|
|
12408
|
+
* Editor state of the report parameter form, owned by the host and handed to `CwReportParameterForm`
|
|
12409
|
+
* as `controller`. A new form object from the host (after a resolve) resets the editors to the values
|
|
12410
|
+
* it carries; the server echoes the submitted values, so nothing is lost.
|
|
12411
|
+
*/
|
|
12412
|
+
function useReportParameterForm(form, options) {
|
|
12413
|
+
const { onResolve, onRender } = options;
|
|
12414
|
+
const [state, setState] = React.useState(() => stateFor(form));
|
|
12415
|
+
const current = state.source === form ? state : stateFor(form);
|
|
12416
|
+
if (current !== state)
|
|
12417
|
+
setState(current);
|
|
12418
|
+
const parameters = form?.Parameters ?? [];
|
|
12419
|
+
const setEdit = React.useCallback((name, edit, commit) => {
|
|
12420
|
+
const params = form?.Parameters ?? [];
|
|
12421
|
+
const edits = { ...current.edits, [name]: edit };
|
|
12422
|
+
const pending = new Set(current.pending);
|
|
12423
|
+
if (commit) {
|
|
12424
|
+
pending.delete(name);
|
|
12425
|
+
}
|
|
12426
|
+
else {
|
|
12427
|
+
pending.add(name);
|
|
12428
|
+
}
|
|
12429
|
+
setState({ ...current, edits, pending });
|
|
12430
|
+
if (commit && hasDependentParameters(params, name))
|
|
12431
|
+
onResolve(toRdlParameterValues(params, edits));
|
|
12432
|
+
}, [current, form, onResolve]);
|
|
12433
|
+
const commit = React.useCallback((name) => {
|
|
12434
|
+
if (!current.pending.has(name))
|
|
12435
|
+
return;
|
|
12436
|
+
const params = form?.Parameters ?? [];
|
|
12437
|
+
const pending = new Set(current.pending);
|
|
12438
|
+
pending.delete(name);
|
|
12439
|
+
setState({ ...current, pending });
|
|
12440
|
+
if (hasDependentParameters(params, name))
|
|
12441
|
+
onResolve(toRdlParameterValues(params, current.edits));
|
|
12442
|
+
}, [current, form, onResolve]);
|
|
12443
|
+
const submit = React.useCallback(() => {
|
|
12444
|
+
const params = form?.Parameters ?? [];
|
|
12445
|
+
const errors = validateParameterEdits(params, current.edits);
|
|
12446
|
+
if (Object.keys(errors).length > 0) {
|
|
12447
|
+
setState({ ...current, showErrors: true });
|
|
12448
|
+
return;
|
|
12449
|
+
}
|
|
12450
|
+
onRender(toRdlParameterValues(params, current.edits));
|
|
12451
|
+
}, [current, form, onRender]);
|
|
12452
|
+
return {
|
|
12453
|
+
form,
|
|
12454
|
+
edits: current.edits,
|
|
12455
|
+
errors: current.showErrors ? validateParameterEdits(parameters, current.edits) : {},
|
|
12456
|
+
setEdit,
|
|
12457
|
+
commit,
|
|
12458
|
+
submit
|
|
12459
|
+
};
|
|
12460
|
+
}
|
|
12461
|
+
|
|
10144
12462
|
/**
|
|
10145
12463
|
* API-backed airport search input for WinOps.
|
|
10146
12464
|
*
|
|
@@ -10644,7 +12962,14 @@ exports.CwOption = CwOption;
|
|
|
10644
12962
|
exports.CwPopoverButton = CwPopoverButton;
|
|
10645
12963
|
exports.CwProgress = CwProgress;
|
|
10646
12964
|
exports.CwRadioGroup = CwRadioGroup;
|
|
12965
|
+
exports.CwReportGauge = CwReportGauge;
|
|
12966
|
+
exports.CwReportLineChart = CwReportLineChart;
|
|
10647
12967
|
exports.CwReportModal = CwReportModal;
|
|
12968
|
+
exports.CwReportPage = CwReportPage;
|
|
12969
|
+
exports.CwReportParameterForm = CwReportParameterForm;
|
|
12970
|
+
exports.CwReportPieChart = CwReportPieChart;
|
|
12971
|
+
exports.CwReportToolbar = CwReportToolbar;
|
|
12972
|
+
exports.CwReportViewer = CwReportViewer;
|
|
10648
12973
|
exports.CwSearchAsync = CwSearchAsync;
|
|
10649
12974
|
exports.CwSearchInput = CwSearchInput;
|
|
10650
12975
|
exports.CwSelect = CwSelect;
|
|
@@ -10690,6 +13015,7 @@ exports.OnStartClickHeaderEvent = OnStartClickHeaderEvent;
|
|
|
10690
13015
|
exports.OnUnpinRow = OnUnpinRow;
|
|
10691
13016
|
exports.PRESET_LIBRARY_TEMPORAL = PRESET_LIBRARY_TEMPORAL;
|
|
10692
13017
|
exports.PinRowHeader = PinRowHeader;
|
|
13018
|
+
exports.REPORT_ZOOM_STEPS = REPORT_ZOOM_STEPS;
|
|
10693
13019
|
exports.Scheduler = Scheduler;
|
|
10694
13020
|
exports.SchedulerEvent = SchedulerEvent;
|
|
10695
13021
|
exports.SuperScheduler = SuperScheduler;
|
|
@@ -10700,13 +13026,20 @@ exports.configureCwLoading = configureCwLoading;
|
|
|
10700
13026
|
exports.eventIsVisible = eventIsVisible;
|
|
10701
13027
|
exports.filterNavItems = filterNavItems;
|
|
10702
13028
|
exports.filterVisibleEvents = filterVisibleEvents;
|
|
13029
|
+
exports.findInPage = findInPage;
|
|
13030
|
+
exports.flipToggle = flipToggle;
|
|
10703
13031
|
exports.getCwLoadingDefaults = getCwLoadingDefaults;
|
|
10704
13032
|
exports.getDefaultDivisions = getDefaultDivisions;
|
|
10705
13033
|
exports.getEventSizes = getEventSizes;
|
|
10706
13034
|
exports.getPresetsByKeysTemporal = getPresetsByKeysTemporal;
|
|
13035
|
+
exports.initialParameterEdits = initialParameterEdits;
|
|
13036
|
+
exports.isRdlVisible = isRdlVisible;
|
|
10707
13037
|
exports.itemsToMultiFilterTags = itemsToMultiFilterTags;
|
|
10708
13038
|
exports.sortNavItems = sortNavItems;
|
|
13039
|
+
exports.toRdlParameterValues = toRdlParameterValues;
|
|
10709
13040
|
exports.useCwMessage = useCwMessage;
|
|
13041
|
+
exports.useReportParameterForm = useReportParameterForm;
|
|
13042
|
+
exports.useReportViewerState = useReportViewerState;
|
|
10710
13043
|
exports.useSortableList = useSortableList;
|
|
10711
13044
|
exports.useSortableTable = useSortableTable;
|
|
10712
13045
|
exports.useTooltipConstraint = useTooltipConstraint;
|