@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.es.js
CHANGED
|
@@ -174,7 +174,7 @@ function getContrastColor(color) {
|
|
|
174
174
|
return `hsl(${hsl.h}, ${contrastS}%, ${targetL}%)`;
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
var styles$
|
|
177
|
+
var styles$D = {"cw-tooltip-content":"cw-tooltip-module__cw-tooltip-content__RZczd","cw-tooltip":"cw-tooltip-module__cw-tooltip__1KYig"};
|
|
178
178
|
|
|
179
179
|
// Constants
|
|
180
180
|
const margin$1 = 16;
|
|
@@ -464,7 +464,7 @@ const CwTooltipManager = () => {
|
|
|
464
464
|
if (!state.isVisible || !state.content) {
|
|
465
465
|
return null;
|
|
466
466
|
}
|
|
467
|
-
return createPortal(jsx("div", { ref: tooltipRef, className: styles$
|
|
467
|
+
return createPortal(jsx("div", { ref: tooltipRef, className: styles$D["cw-tooltip-content"], "data-position": actualPosition, "data-visible": state.isVisible, "data-inline": state.displayInline, style: {
|
|
468
468
|
top: `${tooltipPosition.top}px`,
|
|
469
469
|
left: `${tooltipPosition.left}px`,
|
|
470
470
|
opacity: (isPositioned && !isMeasuring) ? 1 : 0,
|
|
@@ -545,7 +545,7 @@ const CwTooltipNew = ({ children, content = null, position = "right", dissapears
|
|
|
545
545
|
useEffect(() => {
|
|
546
546
|
ensureTooltipManager();
|
|
547
547
|
}, []);
|
|
548
|
-
return (jsx("div", { ref: containerRef, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, style: overlayStyle, "data-inline": displayInline, className: styles$
|
|
548
|
+
return (jsx("div", { ref: containerRef, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, style: overlayStyle, "data-inline": displayInline, className: styles$D["cw-tooltip"], children: children }));
|
|
549
549
|
};
|
|
550
550
|
|
|
551
551
|
const SVG_ICONS = {
|
|
@@ -665,7 +665,7 @@ function resolveBadgeAria(label, loading, hasText) {
|
|
|
665
665
|
return hasText ? {} : { 'aria-hidden': 'true' };
|
|
666
666
|
}
|
|
667
667
|
|
|
668
|
-
var styles$
|
|
668
|
+
var styles$C = {"badge":"cw-badge-module__badge__PKlak"};
|
|
669
669
|
|
|
670
670
|
/**
|
|
671
671
|
* CwBadge - the small coloured circle with a number, used for alert and notification counts.
|
|
@@ -701,7 +701,7 @@ const CwBadge = ({ count, content, color = 'info', variant = 'solid', size = 'sm
|
|
|
701
701
|
? formatBadgeCount(count, max)
|
|
702
702
|
: undefined;
|
|
703
703
|
const isDot = !loading && !hasContent && count === undefined;
|
|
704
|
-
const badgeClassNames = ['cw-badge', styles$
|
|
704
|
+
const badgeClassNames = ['cw-badge', styles$C.badge, className].filter(Boolean).join(' ');
|
|
705
705
|
return (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 ? jsx(CwIcon, { iconId: "spinner", size: "small" }) : hasContent ? content : countText }));
|
|
706
706
|
};
|
|
707
707
|
|
|
@@ -1089,7 +1089,7 @@ function CwLoading({ isLoading = false, text = '', size = 'regular', iconPositio
|
|
|
1089
1089
|
return (jsxs("div", { "aria-busy": isLoading, className: "cw-loading-container", "data-children": !!children, children: [overlay, jsx("div", { className: "cw-loading-disabled-content", children: children })] }));
|
|
1090
1090
|
}
|
|
1091
1091
|
|
|
1092
|
-
var styles$
|
|
1092
|
+
var styles$B = {"skeleton":"cw-skeleton-module__skeleton__Jvx2x","group":"cw-skeleton-module__group__Cm8AU"};
|
|
1093
1093
|
|
|
1094
1094
|
/*
|
|
1095
1095
|
* Internal helper for skeleton allocations (CwSkeleton, tables, cards).
|
|
@@ -1131,8 +1131,8 @@ function CwSkeleton({ variant = 'text', width, height, count = 1, radius, gap })
|
|
|
1131
1131
|
};
|
|
1132
1132
|
// The un-hashed cw-skeleton / cw-skeleton-group classes carry no styles in the
|
|
1133
1133
|
// library - they are stable public hooks for exceptional external overrides.
|
|
1134
|
-
const skeletonClassName = ['cw-skeleton', styles$
|
|
1135
|
-
const groupClassName = ['cw-skeleton-group', styles$
|
|
1134
|
+
const skeletonClassName = ['cw-skeleton', styles$B.skeleton].filter(Boolean).join(' ');
|
|
1135
|
+
const groupClassName = ['cw-skeleton-group', styles$B.group].filter(Boolean).join(' ');
|
|
1136
1136
|
const lineCount = normalizeSkeletonCount(count, 1);
|
|
1137
1137
|
if (lineCount <= 1) {
|
|
1138
1138
|
return (jsx("span", { className: skeletonClassName, "data-variant": variant, "aria-hidden": "true", style: style }));
|
|
@@ -1144,7 +1144,7 @@ function CwSkeleton({ variant = 'text', width, height, count = 1, radius, gap })
|
|
|
1144
1144
|
return (jsx("span", { className: groupClassName, "aria-hidden": "true", style: groupStyle, children: skeletonElements }));
|
|
1145
1145
|
}
|
|
1146
1146
|
|
|
1147
|
-
var styles$
|
|
1147
|
+
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"};
|
|
1148
1148
|
|
|
1149
1149
|
/**
|
|
1150
1150
|
* CwProgress - a determinate or indeterminate progress bar.
|
|
@@ -1168,17 +1168,17 @@ function CwProgress({ value, indeterminate = false, label, rightLabel, footer, s
|
|
|
1168
1168
|
const valueContent = rightLabel ?? (percent !== undefined ? `${percent}%` : '');
|
|
1169
1169
|
// The un-hashed cw-progress class carries no styles in the library - a stable
|
|
1170
1170
|
// public hook for exceptional external overrides only.
|
|
1171
|
-
const rootClassName = ['cw-progress', styles$
|
|
1172
|
-
const labelSpan = label ? jsx("span", { className: styles$
|
|
1173
|
-
const valueLabel = showValue ? (jsx("span", { className: styles$
|
|
1174
|
-
return (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) && (jsxs("header", { className: styles$
|
|
1171
|
+
const rootClassName = ['cw-progress', styles$A.progress, className].filter(Boolean).join(' ');
|
|
1172
|
+
const labelSpan = label ? jsx("span", { className: styles$A.label, id: labelId, children: label }) : null;
|
|
1173
|
+
const valueLabel = showValue ? (jsx("span", { className: styles$A.value, "data-mono": monoValue || undefined, children: valueContent })) : null;
|
|
1174
|
+
return (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) && (jsxs("header", { className: styles$A.header, children: [labelSpan, valueLabel] })), layout === 'row' && labelSpan, jsx("div", { className: styles$A.track, role: "progressbar", ...(label && { 'aria-labelledby': labelId }), ...(!indeterminate && percent !== undefined && {
|
|
1175
1175
|
'aria-valuenow': percent,
|
|
1176
1176
|
'aria-valuemin': 0,
|
|
1177
1177
|
'aria-valuemax': 100
|
|
1178
|
-
}), children: jsx("div", { className: styles$
|
|
1178
|
+
}), children: jsx("div", { className: styles$A.fill, style: !indeterminate && clamped !== undefined ? { width: `${clamped}%` } : undefined }) }), layout === 'row' && valueLabel, footer != null && jsx("footer", { className: styles$A.footer, children: footer })] }));
|
|
1179
1179
|
}
|
|
1180
1180
|
|
|
1181
|
-
var styles$
|
|
1181
|
+
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"};
|
|
1182
1182
|
|
|
1183
1183
|
// Constants moved outside to prevent recreation
|
|
1184
1184
|
const margin = 16;
|
|
@@ -1305,12 +1305,12 @@ const CwGenericTooltip = ({ children, content = null, position = defaultPosition
|
|
|
1305
1305
|
const { setTooltipTimeout, clearTooltipTimeout } = useTooltipDelay(() => {
|
|
1306
1306
|
setIsVisible(true);
|
|
1307
1307
|
}, showDelay);
|
|
1308
|
-
const { position: tooltipPosition, actualPosition } = useTooltipPosition(isVisible, containerRef, position, styles$
|
|
1308
|
+
const { position: tooltipPosition, actualPosition } = useTooltipPosition(isVisible, containerRef, position, styles$z["cw-generic-tooltip-content"]);
|
|
1309
1309
|
// Memoize tooltip content creation
|
|
1310
1310
|
const tooltipContent = useMemo(() => {
|
|
1311
1311
|
if (hide || !isVisible || !content)
|
|
1312
1312
|
return null;
|
|
1313
|
-
return createPortal(jsx("div", { className: styles$
|
|
1313
|
+
return createPortal(jsx("div", { className: styles$z["cw-generic-tooltip-content"], "data-position": actualPosition, "data-visible": isVisible, "data-inline": displayInline, style: {
|
|
1314
1314
|
position: 'fixed',
|
|
1315
1315
|
top: `${tooltipPosition.top}px`,
|
|
1316
1316
|
left: `${tooltipPosition.left}px`,
|
|
@@ -1325,7 +1325,7 @@ const CwGenericTooltip = ({ children, content = null, position = defaultPosition
|
|
|
1325
1325
|
clearTooltipTimeout();
|
|
1326
1326
|
setIsVisible(false);
|
|
1327
1327
|
}, [clearTooltipTimeout]);
|
|
1328
|
-
return (jsxs("div", { ref: containerRef, className: styles$
|
|
1328
|
+
return (jsxs("div", { ref: containerRef, className: styles$z["cw-generic-tooltip"], onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, style: overlayStyle, "data-inline": displayInline, children: [tooltipContent, children] }));
|
|
1329
1329
|
};
|
|
1330
1330
|
|
|
1331
1331
|
const CwButton = React__default.forwardRef(function CwButton({ text, variant = 'solid', color = 'primary', size = 'medium', iconPosition = 'left', pressed, className = '', icon = "", title, tooltipPosition = "bottom", badge, children, ...buttonProps }, ref) {
|
|
@@ -1393,7 +1393,7 @@ function CwModal(custModalProps) {
|
|
|
1393
1393
|
return (jsx("div", { children: custModalProps.modalState && (jsxs("div", { className: custModalProps.classNameModalOverlay + " cwelltModalOverlay", children: [jsx("div", { className: "cwelltModalOverlayBg", onClick: custModalProps.onCloseModal }), jsx(Draggable, { disabled: isModalDisabled, axis: "both", nodeRef: draggableRef, children: jsxs("div", { className: custModalProps.classNameModal + " cwelltContainerModal", ref: draggableRef, style: widthModalDef !== "40em" ? modalStyle.widthCustomStyle : modalStyle.widthDefStyle, children: [jsxs("div", { className: "cwelltModalHeader", onMouseOver: cwelltOnMouseOverModal, onMouseOut: cwelltOnMouseOutModal, children: [jsx("div", { className: "cwelltModalTitle", children: custModalProps.titleModal }), jsx("button", { className: "cwelltBtnCloseModal", onClick: custModalProps.onCloseModal, children: jsx("span", { className: "cwelltCloseIcon" }) })] }), jsx("div", { className: "cwelltModalBody", children: jsx("div", { className: "cwelltContainerModalBody", style: { position: "relative" }, children: custModalProps.children }) }), isHide === false ? (jsx("footer", { className: "legacy-modal-footer", onMouseOver: cwelltOnMouseOverModal, onMouseOut: cwelltOnMouseOutModal, children: jsx(CwButton, { variant: "icon", icon: "save", title: "Save", onClick: custModalProps.onSaveModal, form: custModalProps.formSaveModal, hidden: custModalProps.HideBtnModal, type: custModalProps.HtmlSubmitModal }) })) : (jsx("div", { style: { display: "none" } }))] }) })] })) }));
|
|
1394
1394
|
}
|
|
1395
1395
|
|
|
1396
|
-
var styles$
|
|
1396
|
+
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"};
|
|
1397
1397
|
|
|
1398
1398
|
/**
|
|
1399
1399
|
* Small anchored confirmation for a destructive action.
|
|
@@ -1462,15 +1462,15 @@ function CwConfirmationPopup(props) {
|
|
|
1462
1462
|
onCancel();
|
|
1463
1463
|
}
|
|
1464
1464
|
};
|
|
1465
|
-
return (jsxs(Fragment, { children: [renderChildren(), isOpen && (jsx("div", { className: styles$
|
|
1465
|
+
return (jsxs(Fragment, { children: [renderChildren(), isOpen && (jsx("div", { className: styles$y.overlayPositioned, onClick: handleOverlayClick, children: jsxs("div", { ref: popupRef, className: styles$y.popup, style: {
|
|
1466
1466
|
position: 'absolute',
|
|
1467
1467
|
top: `${position.top}px`,
|
|
1468
1468
|
left: `${position.left}px`,
|
|
1469
1469
|
transform: 'none'
|
|
1470
|
-
}, children: [jsx("p", { className: styles$
|
|
1470
|
+
}, children: [jsx("p", { className: styles$y.message, children: message }), jsxs("div", { className: styles$y.buttons, children: [jsx("button", { className: `${styles$y.button} ${styles$y.confirmButton}`, onClick: onConfirm, children: confirmText }), jsx("button", { className: `${styles$y.button} ${styles$y.cancelButton}`, onClick: onCancel, children: cancelText })] })] }) }))] }));
|
|
1471
1471
|
}
|
|
1472
1472
|
|
|
1473
|
-
var styles$
|
|
1473
|
+
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"};
|
|
1474
1474
|
|
|
1475
1475
|
// Helper function to parse size values
|
|
1476
1476
|
const parseSize = (size) => {
|
|
@@ -1743,7 +1743,7 @@ const CwDialog = props => {
|
|
|
1743
1743
|
}
|
|
1744
1744
|
}
|
|
1745
1745
|
}, [open]);
|
|
1746
|
-
const header = useMemo(() => (jsxs("header", { onMouseDown: handleMouseDown, children: [jsx("span", { children: headline }), customHeader || (jsx("button", { className: styles$
|
|
1746
|
+
const header = useMemo(() => (jsxs("header", { onMouseDown: handleMouseDown, children: [jsx("span", { children: headline }), customHeader || (jsx("button", { className: styles$x["cw-dialog-button-close"], onClick: onClose }))] })), [handleMouseDown, headline, customHeader, onClose]);
|
|
1747
1747
|
const content = useMemo(() => (jsx("section", { children: children })), [children]);
|
|
1748
1748
|
const footer = useMemo(() => (jsx("footer", { children: customFooter || (jsx(CwButton, { variant: "icon", icon: "save", title: "Save", onClick: onSave, tooltipPosition: "top", disabled: disableSave, "data-testid": "cw-dialog-save" })) })), [customFooter, onSave, disableSave]);
|
|
1749
1749
|
const resizeHandles = useMemo(() => size.autoHeight
|
|
@@ -1770,7 +1770,7 @@ const CwDialog = props => {
|
|
|
1770
1770
|
: `${convertFromPx(size.height, size.heightUnit)}${size.heightUnit}`;
|
|
1771
1771
|
return { displayWidth, displayHeight };
|
|
1772
1772
|
}, [size.width, size.height, size.widthUnit, size.heightUnit, size.autoHeight]);
|
|
1773
|
-
const dialogContent = (jsx("div", { "data-has-scrim": hasScrim, className: styles$
|
|
1773
|
+
const dialogContent = (jsx("div", { "data-has-scrim": hasScrim, className: styles$x["cw-dialog-main"], onMouseDown: handleScrimMouseDown, onMouseUp: handleScrimMouseUpOrLeave, onMouseLeave: handleScrimMouseUpOrLeave, "data-pressing": isPressingScrim, children: jsxs("dialog", { ...domProps, ref: dialogRef, style: {
|
|
1774
1774
|
left: `${position.x}px`,
|
|
1775
1775
|
top: `${position.y}px`,
|
|
1776
1776
|
width: displayDimensions.displayWidth,
|
|
@@ -1932,7 +1932,7 @@ function CwAlign(props) {
|
|
|
1932
1932
|
}, children: props.children }));
|
|
1933
1933
|
}
|
|
1934
1934
|
|
|
1935
|
-
var styles$
|
|
1935
|
+
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"};
|
|
1936
1936
|
|
|
1937
1937
|
/**
|
|
1938
1938
|
* CwCard - A simple card component for displaying content in a contained format
|
|
@@ -1958,10 +1958,10 @@ const CwCard = ({ id, title, subtitle, alignment = 'center', children, footer, o
|
|
|
1958
1958
|
// Construct class names using CSS modules
|
|
1959
1959
|
const cardClassNames = [
|
|
1960
1960
|
'cw-card',
|
|
1961
|
-
styles$
|
|
1961
|
+
styles$w.card,
|
|
1962
1962
|
className,
|
|
1963
|
-
clickable ? styles$
|
|
1964
|
-
disabled ? styles$
|
|
1963
|
+
clickable ? styles$w.clickable : '',
|
|
1964
|
+
disabled ? styles$w.disabled : '',
|
|
1965
1965
|
].filter(Boolean).join(' ');
|
|
1966
1966
|
// Handle click event when card is clickable
|
|
1967
1967
|
const handleClick = () => {
|
|
@@ -1979,15 +1979,15 @@ const CwCard = ({ id, title, subtitle, alignment = 'center', children, footer, o
|
|
|
1979
1979
|
const hasFooter = hasChips || footer || (hasActions && resolvedDirection === "row");
|
|
1980
1980
|
// Actions component to reuse. While loading, each provided action renders a circular
|
|
1981
1981
|
// ghost sized to the CwButton icon footprint (2rem) - no interactive buttons.
|
|
1982
|
-
const ActionsComponent = hasActions && !disabled ? (jsx("div", { className: styles$
|
|
1982
|
+
const ActionsComponent = hasActions && !disabled ? (jsx("div", { className: styles$w.actions, children: isLoading ? (jsxs(Fragment, { children: [extraActions && jsx(CwSkeleton, { variant: "circle", height: "2rem" }), onEdit && jsx(CwSkeleton, { variant: "circle", height: "2rem" }), onDelete && jsx(CwSkeleton, { variant: "circle", height: "2rem" })] })) : (jsxs(Fragment, { children: [extraActions ?? null, onEdit && jsx(CwButton, { variant: "icon", icon: "edit", onClick: onEdit }), onDelete && jsx(CwButton, { variant: "icon", icon: "delete", color: "danger", onClick: onDelete })] })) })) : null;
|
|
1983
1983
|
return (jsxs("div", { ...(id && { id }), className: cardClassNames, ...(style && { style }), ...(clickable && !isLoading && {
|
|
1984
1984
|
role: 'button',
|
|
1985
1985
|
tabIndex: 0,
|
|
1986
1986
|
onClick: handleClick,
|
|
1987
|
-
}), "data-variant": variant, "data-direction": resolvedDirection, "aria-busy": isLoading || undefined, children: [isLoading ? (jsxs("header", { children: [jsx("div", { className: styles$
|
|
1987
|
+
}), "data-variant": variant, "data-direction": resolvedDirection, "aria-busy": isLoading || undefined, children: [isLoading ? (jsxs("header", { children: [jsx("div", { className: styles$w.headerContent, "data-align": resolvedAlign, children: jsx(CwSkeleton, { width: "8rem", height: "1.5em" }) }), resolvedDirection === "column" && ActionsComponent] })) : (hasHeader && (jsxs("header", { children: [(title || subtitle) && (jsxs("div", { className: styles$w.headerContent, "data-align": resolvedAlign, children: [title && jsx("h5", { children: title }), subtitle && jsx("strong", { children: subtitle })] })), resolvedDirection === "column" && ActionsComponent] }))), jsx("div", { className: styles$w.content, children: isLoading ? jsx(CwSkeleton, { count: normalizeSkeletonCount(skeletonLines, 3) }) : children }), hasFooter && (isLoading ? (jsx("footer", { children: jsx("div", { className: styles$w.footerTags, children: jsx(CwSkeleton, { variant: "rect", width: "4rem", height: "1.5em", radius: "1rem" }) }) })) : (jsxs("footer", { children: [hasChips && (jsx("div", { className: styles$w.footerTags, children: chips.map((chip, index) => (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 && (jsx("div", { className: styles$w.footerContent, children: footer }))] }))), resolvedDirection === "row" && ActionsComponent] }));
|
|
1988
1988
|
};
|
|
1989
1989
|
|
|
1990
|
-
var styles$
|
|
1990
|
+
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"};
|
|
1991
1991
|
|
|
1992
1992
|
/**
|
|
1993
1993
|
* Option to display in the CwSelect
|
|
@@ -2082,10 +2082,10 @@ function CwCardList({ items, renderCard, pageSize = 10, layout = 'grid', default
|
|
|
2082
2082
|
icon: sortDirection === 'asc' ? 'arrow-up' : 'arrow-down',
|
|
2083
2083
|
onClick: () => setSortDirection(prev => prev === 'asc' ? 'desc' : 'asc'),
|
|
2084
2084
|
title: `Sort ${sortDirection === 'asc' ? 'descending' : 'ascending'}`,
|
|
2085
|
-
}, value: sortKey, onChange: (e) => handleSortChange(e.target.value), children: sortOptions.map(option => (jsx(CwOption, { value: option.key, children: option.label }, option.key))) }) })), isLoading ? (jsx("div", { className: `${styles$
|
|
2085
|
+
}, value: sortKey, onChange: (e) => handleSortChange(e.target.value), children: sortOptions.map(option => (jsx(CwOption, { value: option.key, children: option.label }, option.key))) }) })), isLoading ? (jsx("div", { className: `${styles$v.cardContainer}`, "data-layout": layout, style: gridStyle, "aria-busy": true, children: Array.from({ length: normalizeSkeletonCount(skeletonCards, 6) }, (_, index) => (jsx(CwCard, { isLoading: true, children: null }, `skeleton_${index}`))) })) : sortedItems.length === 0 ? (jsx("div", { className: styles$v.emptyState, children: emptyState || jsx("p", { children: "No items to display" }) })) : (jsxs(Fragment, { children: [jsx("div", { className: `${styles$v.cardContainer}`, "data-layout": layout, style: gridStyle, children: visibleItems.map((item, index) => (jsx(React__default.Fragment, { children: renderCard(item, index) }, keyExtractor ? keyExtractor(item, index) : index))) }), totalPages > 1 && (jsxs("div", { className: styles$v.pagination, children: [jsx(CwButton, { variant: "icon", icon: "chevron-left", disabled: currentPage === 1, onClick: () => setCurrentPage(p => Math.max(1, p - 1)) }), jsxs("span", { className: styles$v.pageInfo, children: [currentPage, " / ", totalPages] }), jsx(CwButton, { variant: "icon", icon: "chevron-right", disabled: currentPage === totalPages, onClick: () => setCurrentPage(p => Math.min(totalPages, p + 1)) })] }))] }))] }));
|
|
2086
2086
|
}
|
|
2087
2087
|
|
|
2088
|
-
var styles$
|
|
2088
|
+
var styles$u = {"cw-accordion":"cw-accordion-module__cw-accordion__ErvlW","cw-accordion-body":"cw-accordion-module__cw-accordion-body__xlI8b"};
|
|
2089
2089
|
|
|
2090
2090
|
/**
|
|
2091
2091
|
* Legacy collapsible section with its own header markup.
|
|
@@ -2100,7 +2100,7 @@ function CwAccordionContainer(CwelltAccordionContainerProps) {
|
|
|
2100
2100
|
setVisible_accordionBody(!isVisible_accordionBody);
|
|
2101
2101
|
};
|
|
2102
2102
|
// #endregion
|
|
2103
|
-
return (jsxs("div", { className: styles$
|
|
2103
|
+
return (jsxs("div", { className: styles$u["cw-accordion"], style: CwelltAccordionContainerProps.style, "data-open": isVisible_accordionBody, children: [jsxs("header", { onClick: () => showAccordionBody(), children: [jsx("div", { children: CwelltAccordionContainerProps.desc_text }), jsx("button", { className: "cw-button-icon" })] }), jsx("div", { className: styles$u["cw-accordion-body"], children: CwelltAccordionContainerProps.children })] }));
|
|
2104
2104
|
}
|
|
2105
2105
|
|
|
2106
2106
|
/*
|
|
@@ -2379,7 +2379,7 @@ function CwTable({ columns, data, pagination = false, pageSizeOptions = [5, 10,
|
|
|
2379
2379
|
}, min: 1, max: totalPages }), jsx("span", { children: "/" }), jsx("span", { children: totalPages }), jsx(CwButton, { onClick: () => handlePageChange(page + 1), disabled: page === totalPages || totalPages <= 1, variant: "icon", icon: "chevron-right" }), jsx(CwButton, { onClick: () => handlePageChange(totalPages), disabled: page === totalPages || totalPages <= 1, variant: "icon", icon: "chevron-right-double" })] })] }))] }));
|
|
2380
2380
|
}
|
|
2381
2381
|
|
|
2382
|
-
var styles$
|
|
2382
|
+
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"};
|
|
2383
2383
|
|
|
2384
2384
|
/**
|
|
2385
2385
|
* Hook that provides state management for CwSortableTable.
|
|
@@ -2442,7 +2442,7 @@ function useSortableTable(initialItems) {
|
|
|
2442
2442
|
}
|
|
2443
2443
|
|
|
2444
2444
|
// ─── Drop indicator row ───────────────────────────────────────────────────────
|
|
2445
|
-
const DropIndicatorRow = ({ colSpan }) => (jsx("tr", { className: styles$
|
|
2445
|
+
const DropIndicatorRow = ({ colSpan }) => (jsx("tr", { className: styles$t.dropIndicator, "aria-hidden": true, children: jsx("td", { colSpan: colSpan, children: jsx("div", {}) }) }));
|
|
2446
2446
|
// ─── Loading skeleton ─────────────────────────────────────────────────────────
|
|
2447
2447
|
// Plain td cells (no local class): the ghost cells get the same global cw-table
|
|
2448
2448
|
// padding as real body cells, so row heights match the loaded rows exactly.
|
|
@@ -2545,11 +2545,11 @@ function CwSortableTable({ columns, data, rowKey = "key", onReorder, loading = f
|
|
|
2545
2545
|
return items.map((item, index) => {
|
|
2546
2546
|
const key = item[rowKey];
|
|
2547
2547
|
const isDragging = draggedItem?.[rowKey] === key;
|
|
2548
|
-
return (jsxs(React__default.Fragment, { children: [dropIndicatorIndex === index && (jsx(DropIndicatorRow, { colSpan: totalCols })), jsxs("tr", { draggable: true, onDragStart: (e) => handleDragStart(e, item), onDragOver: (e) => handleDragOver(e, index), onDragEnd: handleDragEnd, className: isDragging ? styles$
|
|
2548
|
+
return (jsxs(React__default.Fragment, { children: [dropIndicatorIndex === index && (jsx(DropIndicatorRow, { colSpan: totalCols })), jsxs("tr", { draggable: true, onDragStart: (e) => handleDragStart(e, item), onDragOver: (e) => handleDragOver(e, index), onDragEnd: handleDragEnd, className: isDragging ? styles$t.dragging : "", children: [showHandle && (jsx("td", { className: "cw-table-col-action", children: jsx(CwIcon, { iconId: "grip-dots", color: "neutral", style: { opacity: 0.75 } }) })), columns.map(col => (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 && (jsx(DropIndicatorRow, { colSpan: totalCols }))] }, String(key)));
|
|
2549
2549
|
});
|
|
2550
2550
|
};
|
|
2551
2551
|
// ── Render ──────────────────────────────────────────────────────────────
|
|
2552
|
-
return (jsxs("div", { className: `cw-table-container ${className ?? ""}`, style: containerStyle, "aria-busy": loading || undefined, children: [jsxs("table", { className: `cw-table cw-sortable-table ${tableClassName ?? ""}`, style: { width: "100%" }, children: [jsx("thead", { children: jsxs("tr", { children: [showHandle && jsx("th", { className: "cw-table-col-action" }), columns.map(col => (jsx("th", { className: col.className ?? "", style: col.width ? { width: col.width, minWidth: 50 } : undefined, children: col.title }, col.key)))] }) }), jsx("tbody", { children: renderBody() })] }), !loading && hasChanges && (jsxs("footer", { className: styles$
|
|
2552
|
+
return (jsxs("div", { className: `cw-table-container ${className ?? ""}`, style: containerStyle, "aria-busy": loading || undefined, children: [jsxs("table", { className: `cw-table cw-sortable-table ${tableClassName ?? ""}`, style: { width: "100%" }, children: [jsx("thead", { children: jsxs("tr", { children: [showHandle && jsx("th", { className: "cw-table-col-action" }), columns.map(col => (jsx("th", { className: col.className ?? "", style: col.width ? { width: col.width, minWidth: 50 } : undefined, children: col.title }, col.key)))] }) }), jsx("tbody", { children: renderBody() })] }), !loading && hasChanges && (jsxs("footer", { className: styles$t.saveBar, children: [jsx(CwButton, { variant: "outline", color: "neutral", icon: "undo", text: discardLabel, onClick: handleDiscard }), jsx(CwButton, { variant: "solid", color: "primary", icon: "save", text: saveLabel, onClick: () => handleSave(onReorder) })] }))] }));
|
|
2553
2553
|
}
|
|
2554
2554
|
|
|
2555
2555
|
/**
|
|
@@ -2750,7 +2750,7 @@ function CwTableServerSide({ columns, data, totalItems, pagination = false, page
|
|
|
2750
2750
|
}, min: 1, max: totalPages }), jsx("span", { children: "/" }), jsx("span", { children: totalPages }), jsx(CwButton, { onClick: () => handlePageChange(state.page + 1), disabled: state.page === totalPages || totalPages <= 1, variant: "icon", icon: "chevron-right" }), jsx(CwButton, { onClick: () => handlePageChange(totalPages), disabled: state.page === totalPages || totalPages <= 1, variant: "icon", icon: "chevron-right-double" })] })] }))] }));
|
|
2751
2751
|
}
|
|
2752
2752
|
|
|
2753
|
-
var styles$
|
|
2753
|
+
var styles$s = {"cw-tabs":"cw-tabs-module__cw-tabs__1pmji","cw-tabs-content":"cw-tabs-module__cw-tabs-content__HTp8d"};
|
|
2754
2754
|
|
|
2755
2755
|
const BADGE_COLOR_SCHEMES = ['primary', 'danger', 'warning', 'success', 'info', 'neutral'];
|
|
2756
2756
|
function isBadgeColorScheme(value) {
|
|
@@ -2804,11 +2804,11 @@ function CwTabs(CwTabsProps) {
|
|
|
2804
2804
|
const tabsListStyle = position === 'left' && CwTabsProps.tabsListWidth
|
|
2805
2805
|
? { minWidth: CwTabsProps.tabsListWidth }
|
|
2806
2806
|
: undefined;
|
|
2807
|
-
return (jsxs("div", { id: CwTabsProps.id, "data-testid": CwTabsProps.testId, className: styles$
|
|
2807
|
+
return (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: [jsx("ul", { style: tabsListStyle, children: CwTabsProps.tabs.map(tab => {
|
|
2808
2808
|
const badgeScheme = tab.badgeColor && isBadgeColorScheme(tab.badgeColor) ? tab.badgeColor : undefined;
|
|
2809
2809
|
const rawBadgeColor = tab.badgeColor && !badgeScheme ? tab.badgeColor : undefined;
|
|
2810
2810
|
return (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: [jsx(TabIcon, { icon: tab.icon }), tab.title, (tab.badge !== undefined || tab.badgeColor) && (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));
|
|
2811
|
-
}) }), jsx("div", { className: styles$
|
|
2811
|
+
}) }), 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 })] }));
|
|
2812
2812
|
}
|
|
2813
2813
|
|
|
2814
2814
|
/**
|
|
@@ -2836,7 +2836,7 @@ const CwExpandable = ({ briefing, onToggle, onOpen, onClose, children, ...detail
|
|
|
2836
2836
|
return (jsxs("details", { ref: myRef, ...detailsProps, className: "cw-expandable", children: [jsx("summary", { children: briefing }), children && jsx("section", { children: children })] }));
|
|
2837
2837
|
};
|
|
2838
2838
|
|
|
2839
|
-
var styles$
|
|
2839
|
+
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"};
|
|
2840
2840
|
|
|
2841
2841
|
/*
|
|
2842
2842
|
* Fixed rem widths for the ghosts: the master rows and detail panel size their
|
|
@@ -2880,12 +2880,12 @@ const CwMasterDetail = ({ id, className, style, items, selectedKey, onSelect, de
|
|
|
2880
2880
|
document.body.style.userSelect = '';
|
|
2881
2881
|
};
|
|
2882
2882
|
}, [isResizing, minMasterWidth, maxMasterWidth]);
|
|
2883
|
-
const rootClassName = [styles$
|
|
2883
|
+
const rootClassName = [styles$r['cw-master-detail'], className].filter(Boolean).join(' ');
|
|
2884
2884
|
return (jsxs("div", { id: id, ref: containerRef, className: rootClassName, style: style, "aria-busy": loading || undefined, children: [jsxs("aside", { style: { width: masterWidth, minWidth: masterWidth }, children: [showSearch && (jsx("search", { children: jsx("input", { type: "text", placeholder: searchPlaceholder, onChange: (e) => onSearch?.(e.target.value) }) })), masterHeader && (jsx("header", { children: masterHeader })), jsx("ul", { children: loading
|
|
2885
|
-
? Array.from({ length: skeletonRows }, (_unused, rowIndex) => (jsxs("li", { "aria-hidden": "true", children: [jsx(CwSkeleton, { variant: "circle", height: "2rem" }), jsxs("div", { className: styles$
|
|
2886
|
-
: items.map((item) => (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 && (jsx("footer", { children: masterFooter }))] }), resizable && (jsx("div", { className: styles$
|
|
2885
|
+
? Array.from({ length: skeletonRows }, (_unused, rowIndex) => (jsxs("li", { "aria-hidden": "true", children: [jsx(CwSkeleton, { variant: "circle", height: "2rem" }), jsxs("div", { className: styles$r.skeletonRow, children: [jsx(CwSkeleton, { variant: "text", width: MASTER_TITLE_WIDTHS[rowIndex % MASTER_TITLE_WIDTHS.length] }), jsx(CwSkeleton, { variant: "text", width: MASTER_SUBTITLE_WIDTH })] })] }, `skeleton_${rowIndex}`)))
|
|
2886
|
+
: items.map((item) => (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 && (jsx("footer", { children: masterFooter }))] }), resizable && (jsx("div", { className: styles$r.resizer, "data-resizing": isResizing, onMouseDown: handleMouseDown })), jsx("section", { className: styles$r.detail, children: loading ? (jsxs("div", { className: styles$r.detailSkeleton, "aria-hidden": "true", children: [jsx(CwSkeleton, { variant: "text", width: "14rem", height: "1.5em" }), jsx(CwSkeleton, { variant: "text", width: "22rem" }), jsx(CwSkeleton, { variant: "text", width: "18rem" }), jsx(CwSkeleton, { variant: "text", width: "20rem" })] })) : selectedKey && detailContent
|
|
2887
2887
|
? detailContent
|
|
2888
|
-
: (jsx("div", { className: styles$
|
|
2888
|
+
: (jsx("div", { className: styles$r['detail-empty'], children: emptyDetail ?? 'Select an item to view details' })) })] }));
|
|
2889
2889
|
};
|
|
2890
2890
|
|
|
2891
2891
|
/*
|
|
@@ -2925,7 +2925,7 @@ const CwKeyValueList = ({ items, className = "", emptyValue = "-", direction = "
|
|
|
2925
2925
|
return (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() }));
|
|
2926
2926
|
};
|
|
2927
2927
|
|
|
2928
|
-
var styles$
|
|
2928
|
+
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"};
|
|
2929
2929
|
|
|
2930
2930
|
function CwSortableList({ items, onReorder, renderItem, className = '', movedItems = new Set(), emptyMessage = "No elements to show", readOnly = false, maxHeight = "300px", }) {
|
|
2931
2931
|
const [draggedItem, setDraggedItem] = useState(null);
|
|
@@ -2988,15 +2988,15 @@ function CwSortableList({ items, onReorder, renderItem, className = '', movedIte
|
|
|
2988
2988
|
}
|
|
2989
2989
|
};
|
|
2990
2990
|
if (items.length === 0) {
|
|
2991
|
-
return (jsx("div", { className: `${styles$
|
|
2991
|
+
return (jsx("div", { className: `${styles$q.sortableList} ${styles$q.emptyState} ${className}`, children: jsx("div", { className: styles$q.emptyMessage, children: emptyMessage }) }));
|
|
2992
2992
|
}
|
|
2993
|
-
return (jsx("div", { className: `${styles$
|
|
2993
|
+
return (jsx("div", { className: `${styles$q.sortableList} ${className}`, style: { maxHeight }, children: items.map((item, index) => {
|
|
2994
2994
|
const { title, extraContent, actions, showHandle = true, expandedContent } = renderItem(item);
|
|
2995
2995
|
const isDragging = draggedItem?.id === item.id;
|
|
2996
2996
|
const isMoved = movedItems.has(item.id);
|
|
2997
2997
|
const canExpand = Boolean(expandedContent);
|
|
2998
2998
|
const isExpanded = expandedItems.has(item.id);
|
|
2999
|
-
return (jsxs(React__default.Fragment, { children: [dropIndicatorIndex === index && jsx("div", { className: styles$
|
|
2999
|
+
return (jsxs(React__default.Fragment, { children: [dropIndicatorIndex === index && jsx("div", { className: styles$q.dropIndicator }), 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: [jsxs("header", { children: [canExpand ? (jsx(CwButton, { variant: "icon", onClick: () => toggleExpanded(item.id), type: "button", title: isExpanded ? "Collapse" : "Expand", icon: isExpanded ? "chevron-up" : "chevron-down" })) : showHandle ? (jsx("div", { className: styles$q.sortableHandle, children: jsx(CwIcon, { iconId: "grip-dots" }) })) : null, jsxs("div", { className: styles$q.sortableContent, children: [jsx("div", { className: styles$q.sortableTitle, children: title }), extraContent && jsx("div", { className: styles$q.sortableExtraContent, children: extraContent })] }), jsx("div", { className: styles$q.sortableActions, children: actions })] }), isExpanded && expandedContent && (jsx("div", { className: styles$q.expandedContent, children: expandedContent }))] }), !readOnly && dropIndicatorIndex === items.length && index === items.length - 1 && (jsx("div", { className: styles$q.dropIndicator }))] }, item.id));
|
|
3000
3000
|
}) }));
|
|
3001
3001
|
}
|
|
3002
3002
|
|
|
@@ -3123,7 +3123,7 @@ const CwTableGrouped = props => {
|
|
|
3123
3123
|
group.rows.map((child, j) => (jsx("tr", { "data-row": true, "data-row-last-of-group": j == group.rows.length - 1, children: child.cells.map((cell, k) => (jsx("td", { children: cell.content }, k))) }, j)))] }, i))) }), jsx("tfoot", {})] }) }));
|
|
3124
3124
|
};
|
|
3125
3125
|
|
|
3126
|
-
var styles$
|
|
3126
|
+
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"};
|
|
3127
3127
|
|
|
3128
3128
|
const DEFAULT_LABELS$1 = {
|
|
3129
3129
|
uploadDisabled: 'Upload disabled',
|
|
@@ -3309,12 +3309,12 @@ function CwFileUploadMultiple(fileUploadProps) {
|
|
|
3309
3309
|
}
|
|
3310
3310
|
}
|
|
3311
3311
|
};
|
|
3312
|
-
return (jsxs("div", { className: `${styles$
|
|
3312
|
+
return (jsxs("div", { className: `${styles$p.fileUploadContainer} ${fileUploadProps.className}`, children: [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 ? (jsxs("div", { className: `${styles$p.uploadArea} ${fileUploadProps.disabled ? styles$p.uploadAreaDisabled : ''}`, onDragOver: handleDragOver, onDrop: handleDrop, onClick: !fileUploadProps.disabled ? handleButtonClick : undefined, children: [jsx(CwIcon, { iconId: "upload" }), jsx("p", { className: `${styles$p.uploadTitle}`, children: fileUploadProps.disabled ? labels.uploadDisabled : labels.clickToUpload }), jsxs("p", { className: `${styles$p.uploadSubtitle}`, children: [fileUploadProps.accept ? labels.acceptedFiles(fileUploadProps.accept) : labels.allTypesAccepted, !fileUploadProps.multiple && ` ${labels.singleFileOnly}`] })] })) : selectedFiles.length === 0 && existingFile ? (jsxs("div", { className: styles$p.filesContainer, children: [jsxs("div", { className: styles$p.fileItem, children: [jsxs("div", { className: styles$p.fileIcon, children: [jsx(CwIcon, { iconId: "page" }), jsx("span", { className: styles$p.fileExtension, children: getFileExtension(existingFile) })] }), jsx("div", { className: styles$p.fileInfo, children: jsx("p", { className: styles$p.fileName, children: existingFile }) }), jsx(CwButton, { variant: "icon", icon: "close", color: "neutral", onClick: () => {
|
|
3313
3313
|
setExistingFile(undefined);
|
|
3314
3314
|
if (fileUploadProps.onSelect) {
|
|
3315
3315
|
fileUploadProps.onSelect(null);
|
|
3316
3316
|
}
|
|
3317
|
-
}, disabled: fileUploadProps.disabled, className: styles$
|
|
3317
|
+
}, disabled: fileUploadProps.disabled, className: styles$p.smallButton })] }), jsx(CwButton, { text: labels.changeFile, icon: "refresh", onClick: handleButtonClick, disabled: fileUploadProps.disabled })] })) : (jsxs("div", { className: styles$p.filesContainer, children: [jsxs("header", { className: "cw-flex-row cw-align-between-center cw-flex-wrap", children: [jsx("small", { className: styles$p.filesCount, children: fileUploadProps.statusText ?? labels.filesSelected(selectedFiles.length) }), jsxs("div", { className: "cw-flex-row", children: [fileUploadProps.onConfirm && selectedFiles.length > 0 && (jsx(CwButton, { onClick: () => {
|
|
3318
3318
|
let files = fileInputRef.current?.files ?? null;
|
|
3319
3319
|
if (!files) {
|
|
3320
3320
|
const dataTransfer = new DataTransfer();
|
|
@@ -3322,7 +3322,7 @@ function CwFileUploadMultiple(fileUploadProps) {
|
|
|
3322
3322
|
files = dataTransfer.files;
|
|
3323
3323
|
}
|
|
3324
3324
|
fileUploadProps.onConfirm?.(files);
|
|
3325
|
-
}, disabled: fileUploadProps.disabled || fileUploadProps.confirmDisabled, icon: "upload", text: fileUploadProps.confirmLabel ?? labels.upload })), !fileUploadProps.hideClearAll && (jsx(CwButton, { onClick: removeAllFiles, disabled: fileUploadProps.disabled, color: "danger", variant: "outline", icon: "delete", text: labels.clearAll }))] })] }), selectedFiles.map((file, index) => (jsxs("div", { className: styles$
|
|
3325
|
+
}, disabled: fileUploadProps.disabled || fileUploadProps.confirmDisabled, icon: "upload", text: fileUploadProps.confirmLabel ?? labels.upload })), !fileUploadProps.hideClearAll && (jsx(CwButton, { onClick: removeAllFiles, disabled: fileUploadProps.disabled, color: "danger", variant: "outline", icon: "delete", text: labels.clearAll }))] })] }), selectedFiles.map((file, index) => (jsxs("div", { className: styles$p.fileItem, children: [jsxs("div", { className: styles$p.fileIcon, children: [jsx(CwIcon, { iconId: "page" }), jsx("span", { className: styles$p.fileExtension, children: getFileExtension(file.name) })] }), jsxs("div", { className: styles$p.fileInfo, children: [jsx("p", { className: styles$p.fileName, children: file.name }), jsxs("p", { className: styles$p.fileSize, children: [(file.size / 1024).toFixed(1), " KB"] })] }), jsx(CwButton, { variant: "icon", icon: "close", color: "neutral", onClick: () => removeFile(index), className: styles$p.smallButton })] }, `${file.name}-${file.size}`))), fileUploadProps.multiple && (jsx(CwButton, { text: labels.addMoreFiles, icon: "plus", variant: "outline", onClick: handleButtonClick, disabled: fileUploadProps.disabled })), !fileUploadProps.multiple && (jsx(CwButton, { text: labels.changeFile, icon: "refresh", onClick: handleButtonClick, disabled: fileUploadProps.disabled }))] }))] }));
|
|
3326
3326
|
}
|
|
3327
3327
|
|
|
3328
3328
|
/**
|
|
@@ -3375,7 +3375,7 @@ function CwDigit(props) {
|
|
|
3375
3375
|
return (jsxs("div", { className: "cw-input-text", "data-direction": labelProps ? flexDirection : "column", "data-feedback-position": feedbackPosition, children: [jsxs(CwAlign, { ...alignProps, itemProp: inputProps.required === true ? "required" : "", children: [labelProps && (jsxs(CwLabel, { ...labelProps, children: [iconProps && jsx(CwIcon, { ...iconProps }), labelProps.text] })), jsxs("div", { className: "cw-input-button", children: [jsx("input", { type: "number", ...inputProps, "aria-describedby": describedBy }), buttonProps && jsx(CwButton, { ...buttonProps })] })] }), feedbackMessages.map((feedbackItem, index) => (jsx("p", { id: feedbackIds[index], className: "cw-input-info", "data-color": feedbackItem.type, "aria-live": "polite", children: feedbackItem.message }, index)))] }));
|
|
3376
3376
|
}
|
|
3377
3377
|
|
|
3378
|
-
var styles$
|
|
3378
|
+
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"};
|
|
3379
3379
|
|
|
3380
3380
|
const CwColorPicker = ({ initialColor, onChange, previewText = "Color preview" }) => {
|
|
3381
3381
|
const [rgb, setRgb] = useState({ r: 255, g: 255, b: 255 });
|
|
@@ -3578,13 +3578,13 @@ const CwColorPicker = ({ initialColor, onChange, previewText = "Color preview" }
|
|
|
3578
3578
|
// Calculate background color for the main area based on current hue
|
|
3579
3579
|
const hueColor = hsvToRgb(hsv.h, 100, 100);
|
|
3580
3580
|
const hueHex = rgbToHex(hueColor.r, hueColor.g, hueColor.b);
|
|
3581
|
-
return (jsxs("div", { className: "cw-flex-column cw-gap-small", children: [jsxs("div", { className: "cw-flex-row cw-align-between-center cw-gap-small", children: [jsx("div", { className: styles$
|
|
3581
|
+
return (jsxs("div", { className: "cw-flex-column cw-gap-small", children: [jsxs("div", { className: "cw-flex-row cw-align-between-center cw-gap-small", children: [jsx("div", { className: styles$o.colorPreview, style: { backgroundColor: hexColor, color: getContrastColor(hexColor) }, children: previewText }), jsx("button", { type: "button", className: "cw-button", onClick: toggleInputMode, style: { minWidth: "3ch" }, children: inputMode === 'hex' ? 'RGB' : 'HEX' })] }), jsx("div", { className: "color-inputs-row", children: inputMode === 'hex' ? (jsx("input", { type: "text", value: hexColor, onChange: handleHexChange })) : (jsxs("div", { className: "cw-grid-base-3 cw-gap-small", children: [jsxs("div", { children: [jsx("label", { children: "R:" }), jsx("input", { type: "number", name: "r", min: "0", max: "255", value: rgb.r, onChange: handleRgbChange })] }), jsxs("div", { children: [jsx("label", { children: "G:" }), jsx("input", { type: "number", name: "g", min: "0", max: "255", value: rgb.g, onChange: handleRgbChange })] }), jsxs("div", { children: [jsx("label", { children: "B:" }), jsx("input", { type: "number", name: "b", min: "0", max: "255", value: rgb.b, onChange: handleRgbChange })] })] })) }), jsxs("div", { className: styles$o.colorPickerInteractiveArea, children: [jsx("div", { className: styles$o.hueBar, ref: hueBarRef, onClick: handleHueBarClick, children: jsx("div", { className: styles$o.hueBarSlider, style: { top: `${(360 - hsv.h) / 360 * 100}%` } }) }), jsxs("div", { className: styles$o.colorArea, ref: colorAreaRef, onClick: handleColorAreaClick, children: [jsxs("div", { className: styles$o.colorAreaOverlay, children: [jsx("div", { className: styles$o.hueBackground, style: { backgroundColor: hueHex } }), jsx("div", { className: styles$o.whiteGradient }), jsx("div", { className: styles$o.blackGradient })] }), jsx("div", { className: styles$o.colorAreaCursor, style: {
|
|
3582
3582
|
left: `${hsv.s}%`,
|
|
3583
3583
|
top: `${100 - hsv.v}%`
|
|
3584
3584
|
} })] })] })] }));
|
|
3585
3585
|
};
|
|
3586
3586
|
|
|
3587
|
-
var styles$
|
|
3587
|
+
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"};
|
|
3588
3588
|
|
|
3589
3589
|
const CwInputColor = ({ value, onChange, previewText = "Color preview", disabled = false, readOnly = false, width = '4rem', height = '2rem', labelProps, layoutProps, className, style, }) => {
|
|
3590
3590
|
const [isOpen, setIsOpen] = useState(false);
|
|
@@ -3717,11 +3717,11 @@ const CwInputColor = ({ value, onChange, previewText = "Color preview", disabled
|
|
|
3717
3717
|
break;
|
|
3718
3718
|
}
|
|
3719
3719
|
};
|
|
3720
|
-
return (jsxs("div", { className: `cw-input-color ${styles$
|
|
3720
|
+
return (jsxs("div", { className: `cw-input-color ${styles$n.container}${className ? ` ${className}` : ''}`, style: style, "data-direction": layoutProps?.direction ?? "row", "data-align": layoutProps?.align, children: [labelProps && (jsx(CwLabel, { ...labelProps })), jsx("div", { ref: containerRef, onClick: handleToggle, className: `${styles$n.selectColor} ${disabled ? styles$n.disabled : ''} ${readOnly ? styles$n.readOnly : ''}`, style: {
|
|
3721
3721
|
width,
|
|
3722
3722
|
height,
|
|
3723
3723
|
backgroundColor: value,
|
|
3724
|
-
}, "aria-label": "Open color picker", "aria-expanded": isOpen, "aria-haspopup": "dialog", "aria-readonly": readOnly, role: "button", tabIndex: disabled ? -1 : 0, onKeyDown: handleKeyDown }), isOpen && createPortal(jsx("div", { ref: dropdownRef, className: styles$
|
|
3724
|
+
}, "aria-label": "Open color picker", "aria-expanded": isOpen, "aria-haspopup": "dialog", "aria-readonly": readOnly, role: "button", tabIndex: disabled ? -1 : 0, onKeyDown: handleKeyDown }), isOpen && createPortal(jsx("div", { ref: dropdownRef, className: styles$n.colorDropdown, style: dropdownStyle, role: "dialog", "aria-modal": "true", "aria-label": "Color picker", children: jsx(CwColorPicker, { initialColor: value, onChange: handleColorChange, previewText: previewText }) }), document.body)] }));
|
|
3725
3725
|
};
|
|
3726
3726
|
|
|
3727
3727
|
/**
|
|
@@ -3976,7 +3976,7 @@ function CwInputDateText(props) {
|
|
|
3976
3976
|
return (jsx("div", { className: "cw-input-date-text", children: jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsxs(CwLabel, { ...labelProps, children: [iconProps && jsx(CwIcon, { ...iconProps }), labelProps.text] })), jsxs("div", { children: [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 }), jsxs("span", { children: [jsx(CwIcon, { iconId: "calendar" }), jsx("input", { type: "date", disabled: disabled, onChange: onChangeInputDate, readOnly: readOnly, ref: dateInputRef, tabIndex: -1, value: date, ...inputProps })] })] }), buttonProps && jsx(CwButton, { ...buttonProps })] }) }));
|
|
3977
3977
|
}
|
|
3978
3978
|
|
|
3979
|
-
var styles$
|
|
3979
|
+
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"};
|
|
3980
3980
|
|
|
3981
3981
|
const DEFAULT_LABELS = {
|
|
3982
3982
|
dropImageHere: "Drop image here",
|
|
@@ -4055,7 +4055,7 @@ function CwInputImage(props) {
|
|
|
4055
4055
|
const file = e.currentTarget.files?.[0];
|
|
4056
4056
|
void handleFile(file ?? null);
|
|
4057
4057
|
};
|
|
4058
|
-
return (jsxs("div", { className: `cw-input-image ${styles$
|
|
4058
|
+
return (jsxs("div", { className: `cw-input-image ${styles$m.container}`, "data-size": size, children: [jsxs("div", { onDragOver: (e) => e.preventDefault(), onDrop: handleDrop, className: styles$m.dropZone, children: [jsx(CwIcon, { iconId: "upload" }), jsxs("div", { className: "cw-flex-column cw-gap-tiny", children: [jsx("p", { className: styles$m.title, children: labels.dropImageHere }), jsx("p", { className: styles$m.subtitle, children: labels.acceptedFormats(allowedFormats.join(", ").replace(/image\//g, "")) })] })] }), jsxs("div", { className: "cw-flex-row cw-align-center-center cw-gap-small", children: [jsx(CwButton, { onClick: onPasteButtonClick, icon: "paste", text: labels.paste, size: size }), jsx(CwButton, { onClick: () => inputRef.current?.click(), icon: "folder-open", text: labels.browse, size: size })] }), jsx("input", { ref: inputRef, type: "file", accept: allowedFormats.join(","), onChange: handleFileChange, className: styles$m.hiddenInput, ...inputProps })] }));
|
|
4059
4059
|
}
|
|
4060
4060
|
|
|
4061
4061
|
/**
|
|
@@ -4187,7 +4187,7 @@ const CwImageArea = forwardRef((props, ref) => {
|
|
|
4187
4187
|
});
|
|
4188
4188
|
CwImageArea.displayName = "CwImageArea";
|
|
4189
4189
|
|
|
4190
|
-
var styles$
|
|
4190
|
+
var styles$l = {"container":"cw-weekday-selector-module__container__pwcvx","days":"cw-weekday-selector-module__days__Dd3RH"};
|
|
4191
4191
|
|
|
4192
4192
|
/* 2024-01-01 was a Monday. Formatted in UTC so a negative browser offset cannot shift a day. */
|
|
4193
4193
|
const MONDAY_UTC = Date.UTC(2024, 0, 1);
|
|
@@ -4385,16 +4385,16 @@ const CwWeekdaySelector = ({ value = "mtwrfsu", onChange, disabled = false, labe
|
|
|
4385
4385
|
// Points at the label element rather than copying its text, which also covers a labelProps that
|
|
4386
4386
|
// carries children instead of `text`.
|
|
4387
4387
|
const labelId = labelProps?.id ?? `${feedbackId}-label`;
|
|
4388
|
-
return (jsxs("div", { ...dataAttributes, className: `cw-weekday-selector ${styles$
|
|
4388
|
+
return (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 && jsx(CwLabel, { id: labelId, ...labelProps }), jsx("div", { role: "group", className: styles$l.days, "aria-labelledby": labelProps ? labelId : undefined, "aria-describedby": describedBy, children: Weekdays.getFullWeek().map(day => (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) => (jsx("p", { id: feedbackIds[index], className: "cw-input-feedback", "data-color": feedbackItem.type, "aria-live": "polite", children: feedbackItem.message }, index)))] }));
|
|
4389
4389
|
};
|
|
4390
4390
|
|
|
4391
|
-
var styles$
|
|
4391
|
+
var styles$k = {"check-container":"cw-check-module__check-container__4nscZ","group":"cw-check-module__group__6JCQo","options":"cw-check-module__options__-g9LF"};
|
|
4392
4392
|
|
|
4393
4393
|
function CwCheck(props) {
|
|
4394
4394
|
const { labelProps, labelText, direction, alignment, className, ...inputProps } = props;
|
|
4395
4395
|
const resolvedDirection = direction ?? alignment ?? "row";
|
|
4396
4396
|
const displayText = labelProps?.text ?? labelText;
|
|
4397
|
-
return (jsx("div", { className: `cw-check ${styles$
|
|
4397
|
+
return (jsx("div", { className: `cw-check ${styles$k["check-container"]}${className ? ` ${className}` : ''}`, children: jsxs("label", { "data-direction": resolvedDirection, children: [jsx("input", { type: "checkbox", ...inputProps }), displayText && (jsx("span", { className: "cw-icon-text", children: displayText }))] }) }));
|
|
4398
4398
|
}
|
|
4399
4399
|
|
|
4400
4400
|
function CwCheckbox(CwCheckboxProps) {
|
|
@@ -4448,14 +4448,14 @@ function CwCheckboxGroup({ options, value = [], onChange, labelProps, groupLayou
|
|
|
4448
4448
|
?? (optionsAlignment === "column" ? "row" : "column");
|
|
4449
4449
|
// Each checkbox's box-to-text layout is the opposite of the options flow, mirroring CwRadioGroup.
|
|
4450
4450
|
const checkDirection = itemDirection === "row" ? "column" : "row";
|
|
4451
|
-
return (jsxs("div", { className: `cw-checkbox-group ${styles$
|
|
4451
|
+
return (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 && (jsx(CwLabel, { ...labelProps, children: labelProps.text })), 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) => (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) => (jsx("p", { id: feedbackIds[index], className: "cw-input-feedback", "data-color": feedbackItem.type, "aria-live": "polite", children: feedbackItem.message }, index)))] }));
|
|
4452
4452
|
}
|
|
4453
4453
|
|
|
4454
|
-
var styles$
|
|
4454
|
+
var styles$j = {"radio":"cw-radio-module__radio__5sh7a","group":"cw-radio-module__group__br-HM","options":"cw-radio-module__options__LSw7M"};
|
|
4455
4455
|
|
|
4456
4456
|
/** Single native radio input. Building block for CwRadioGroup. */
|
|
4457
4457
|
function CwRadio({ label, direction = "row", className, ...inputProps }) {
|
|
4458
|
-
return (jsxs("label", { className: `cw-radio ${styles$
|
|
4458
|
+
return (jsxs("label", { className: `cw-radio ${styles$j.radio}${className ? ` ${className}` : ""}`, "data-direction": direction, children: [jsx("input", { type: "radio", ...inputProps }), label && jsx("span", { children: label })] }));
|
|
4459
4459
|
}
|
|
4460
4460
|
|
|
4461
4461
|
/**
|
|
@@ -4482,7 +4482,7 @@ function CwRadioGroup({ options, value, onChange, labelProps, groupLayout, itemL
|
|
|
4482
4482
|
// (itemLayout column) reads best with the circle beside the text; a side-by-side row reads
|
|
4483
4483
|
// best with the circle above the text.
|
|
4484
4484
|
const radioDirection = itemLayout?.direction === "row" ? "column" : "row";
|
|
4485
|
-
return (jsxs("div", { className: `cw-radio-group ${styles$
|
|
4485
|
+
return (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 && (jsx(CwLabel, { ...labelProps, children: labelProps.text })), 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) => (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) => (jsx("p", { id: feedbackIds[index], className: "cw-input-feedback", "data-color": feedbackItem.type, "aria-live": "polite", children: feedbackItem.message }, index)))] }));
|
|
4486
4486
|
}
|
|
4487
4487
|
|
|
4488
4488
|
function CwToggle(props) {
|
|
@@ -4623,7 +4623,7 @@ function useDropdownPortal({ anchorRef, isOpen, onClose, positionStrategy = defa
|
|
|
4623
4623
|
return { panelRef, panelStyle: style, renderPanel };
|
|
4624
4624
|
}
|
|
4625
4625
|
|
|
4626
|
-
var styles$
|
|
4626
|
+
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"};
|
|
4627
4627
|
|
|
4628
4628
|
/**
|
|
4629
4629
|
* Custom dropdown component with colour-coded options, optional search filter, and full
|
|
@@ -4737,12 +4737,12 @@ function CwDropdown({ options, value, onChange, labelProps, alignProps, placehol
|
|
|
4737
4737
|
}
|
|
4738
4738
|
}, [isOpen, highlightedIndex, filteredOptions, handleOptionSelect]);
|
|
4739
4739
|
const flexDirection = alignProps?.flexDirection ?? "row";
|
|
4740
|
-
return (jsx("div", { ref: containerRef, className: `cw-dropdown${className ? ` ${className}` : ""}`, "data-direction": flexDirection, style: style, ...otherProps, children: jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && jsx(CwLabel, { id: labelId, ...labelProps }), jsxs("div", { className: styles$
|
|
4741
|
-
styles$
|
|
4742
|
-
value === option.value ? styles$
|
|
4743
|
-
highlightedIndex === index ? styles$
|
|
4744
|
-
option.disabled ? styles$
|
|
4745
|
-
].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 && (jsx("span", { className: styles$
|
|
4740
|
+
return (jsx("div", { ref: containerRef, className: `cw-dropdown${className ? ` ${className}` : ""}`, "data-direction": flexDirection, style: style, ...otherProps, children: jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && jsx(CwLabel, { id: labelId, ...labelProps }), jsxs("div", { className: styles$i.value, children: [jsxs("div", { className: "cw-dropdown-wrapper", ref: wrapperRef, children: [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: [jsx("div", { className: "cw-dropdown-trigger-content", id: valueId, children: selectedOption ? (jsxs(Fragment, { children: [selectedOption.color && (jsx("span", { className: styles$i.colorCircle, style: { backgroundColor: selectedOption.color } })), jsx("span", { children: selectedOption.label })] })) : (jsx("span", { className: "cw-dropdown-placeholder", children: placeholder })) }), jsx(CwIcon, { iconId: "chevron-down", size: "small" })] }), renderPanel(jsxs("div", { ref: panelRef, className: styles$i.dropdown, style: panelStyle, role: "listbox", onKeyDown: handleKeyDown, children: [allowSearch && (jsx("div", { className: styles$i.searchBox, children: jsx("input", { ref: inputRef, type: "text", placeholder: "Search...", value: searchText, onChange: handleSearchChange, onKeyDown: handleKeyDown, className: styles$i.searchInput, autoComplete: "off" }) })), jsx("ul", { children: filteredOptions.length > 0 ? (filteredOptions.map((option, index) => (jsxs("li", { ref: highlightedIndex === index ? highlightedItemRef : null, className: [
|
|
4741
|
+
styles$i.option,
|
|
4742
|
+
value === option.value ? styles$i.optionSelected : "",
|
|
4743
|
+
highlightedIndex === index ? styles$i.optionFocused : "",
|
|
4744
|
+
option.disabled ? styles$i.optionDisabled : "",
|
|
4745
|
+
].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 && (jsx("span", { className: styles$i.colorCircle, style: { backgroundColor: option.color } })), jsx("span", { children: option.label })] }, option.value)))) : (jsx("li", { className: styles$i.emptyState, children: "No options available" })) })] }))] }), feedbackMessages.map((item, index) => (jsx("p", { id: feedbackIds[index], className: "cw-input-feedback", "data-color": item.type, "aria-live": "polite", children: item.message }, index)))] })] }) }));
|
|
4746
4746
|
}
|
|
4747
4747
|
|
|
4748
4748
|
/**
|
|
@@ -4759,7 +4759,7 @@ function CwDropdownFilter(props) {
|
|
|
4759
4759
|
return (jsxs("div", { id: props.IdContent, className: containerClassName, children: [props.children, isVisible && (jsx("nav", { className: "cw-dropdown-menu", style: inlineStyles, onMouseLeave: props.onMouseLeave, id: props.idDropDownFilter, role: "menu", children: jsx("ul", { role: "none", children: props.DataSourceDropDownItem?.map((item) => (jsx("li", { id: item.IdDropDown_filter, role: "menuitem", children: item.dropDownFilter_desc }, item.IdDropDown_filter))) }) }))] }));
|
|
4760
4760
|
}
|
|
4761
4761
|
|
|
4762
|
-
var styles$
|
|
4762
|
+
var styles$h = {"container":"cw-popover-button-module__container__YSWQU","panel":"cw-popover-button-module__panel__C-BTn"};
|
|
4763
4763
|
|
|
4764
4764
|
/**
|
|
4765
4765
|
* Button that toggles a floating panel with arbitrary children.
|
|
@@ -4838,7 +4838,7 @@ function CwPopoverButton({ children, panelWidth = "auto", placement = "bottom-st
|
|
|
4838
4838
|
}
|
|
4839
4839
|
panel.showPopover();
|
|
4840
4840
|
}, [placement]);
|
|
4841
|
-
return (jsxs("div", { ref: containerRef, className: styles$
|
|
4841
|
+
return (jsxs("div", { ref: containerRef, className: styles$h.container, children: [jsx(CwButton, { ...buttonProps, onClick: handleClick }), jsx("div", { ref: panelCallbackRef, className: `${styles$h.panel}${panelClassName ? ` ${panelClassName}` : ""}`, style: { width: panelWidth }, children: children })] }));
|
|
4842
4842
|
}
|
|
4843
4843
|
|
|
4844
4844
|
function itemsToMultiFilterTags(items, nameKey, valueKey, category, primaryColor, onPrimaryColor) {
|
|
@@ -4856,13 +4856,13 @@ function itemsToMultiFilterTags(items, nameKey, valueKey, category, primaryColor
|
|
|
4856
4856
|
return result;
|
|
4857
4857
|
}
|
|
4858
4858
|
|
|
4859
|
-
var styles$
|
|
4859
|
+
var styles$g = {"cw-multifilter-tag":"cw-multi-filter-tag-module__cw-multifilter-tag__Epda-"};
|
|
4860
4860
|
|
|
4861
4861
|
const CwMultiFilterTag = props => {
|
|
4862
4862
|
const { ID, Name, Value, Category, Removable, PrimaryColor, Selectable, Selected } = props;
|
|
4863
4863
|
const backgroundColor = `rgb(${PrimaryColor.r},${PrimaryColor.g},${PrimaryColor.b})`;
|
|
4864
4864
|
const isOutlineMode = Selected && !Removable;
|
|
4865
|
-
return (jsx("li", { className: styles$
|
|
4865
|
+
return (jsx("li", { className: styles$g["cw-multifilter-tag"], id: ID, "data-value": Value, "data-category": Category, "data-selected": !!Selected, title: props.tooltip ?? Name, onClick: () => {
|
|
4866
4866
|
if (Selectable)
|
|
4867
4867
|
props.OnSelect?.(props);
|
|
4868
4868
|
}, children: jsx(CwChip, { label: Name, colorScheme: "custom", customColor: backgroundColor, variant: isOutlineMode ? "outline" : "soft", closable: Removable, onClose: () => props.OnRemove?.(props),
|
|
@@ -4872,7 +4872,7 @@ const CwMultiFilterTag = props => {
|
|
|
4872
4872
|
} : undefined }) }));
|
|
4873
4873
|
};
|
|
4874
4874
|
|
|
4875
|
-
var styles$
|
|
4875
|
+
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"};
|
|
4876
4876
|
|
|
4877
4877
|
/**
|
|
4878
4878
|
* A multiple filter selector, a MULTI-SELECT even. Allows users to select and filter items based on tags.
|
|
@@ -5113,9 +5113,9 @@ const CwMultiFilter = ({ allTags, id, onChangeSelectedTags, selectedTags, placeh
|
|
|
5113
5113
|
inputRef.current?.focus();
|
|
5114
5114
|
}
|
|
5115
5115
|
};
|
|
5116
|
-
return (jsxs("form", { ref: componentRef, id: id, "data-testid": testId, className: `${styles$
|
|
5116
|
+
return (jsxs("form", { ref: componentRef, id: id, "data-testid": testId, className: `${styles$f["cw-multi-filter"]}${className ? ` ${className}` : ""}`, style: style, onSubmit: (e) => {
|
|
5117
5117
|
e.preventDefault();
|
|
5118
|
-
}, children: [jsxs("div", { onClick: handleDivClick, ref: wrapperRef, className: styles$
|
|
5118
|
+
}, children: [jsxs("div", { onClick: handleDivClick, ref: wrapperRef, className: styles$f["cw-multi-filter-search"], style: isPanelOpen ? { outline: "1px solid var(--cw-outline-variant)" } : {}, children: [jsxs("ul", { id: id + "_selected_filters", children: [Array.from(selectedTags).map(tag => (createElement(CwMultiFilterTag, { ...tag, key: tag.ID, Selectable: false, Removable: true, OnRemove: () => removeTag(tag.ID) }))), 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 => {
|
|
5119
5119
|
switch (e.key) {
|
|
5120
5120
|
case "Enter":
|
|
5121
5121
|
case "Tab": {
|
|
@@ -5142,7 +5142,7 @@ const CwMultiFilter = ({ allTags, id, onChangeSelectedTags, selectedTags, placeh
|
|
|
5142
5142
|
break;
|
|
5143
5143
|
}
|
|
5144
5144
|
}
|
|
5145
|
-
} })] }), selectedTags.size > 0 && (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(jsxs("section", { ref: panelRef, className: styles$
|
|
5145
|
+
} })] }), selectedTags.size > 0 && (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(jsxs("section", { ref: panelRef, className: styles$f["cw-multi-filter-catalog-container"], style: panelStyle, children: [jsxs("nav", { children: [jsx("button", { className: selectedCategory === "All" ? styles$f["category-selected"] : undefined, onClick: () => handleClickCategory("All"), children: allCategoriesLabel }), Array.from(categoriesMappedToTags().keys()).map(category => (jsx("button", { className: selectedCategory === category ? styles$f["category-selected"] : undefined, style: {
|
|
5146
5146
|
backgroundColor: getColor(category).primary,
|
|
5147
5147
|
color: getColor(category).onPrimary,
|
|
5148
5148
|
}, onClick: () => handleClickCategory(category), children: category }, category)))] }), jsx("ul", { children: displayTags
|
|
@@ -5168,7 +5168,7 @@ const CwMultiFilter = ({ allTags, id, onChangeSelectedTags, selectedTags, placeh
|
|
|
5168
5168
|
} }, props.ID))) })] }))] }));
|
|
5169
5169
|
};
|
|
5170
5170
|
|
|
5171
|
-
var styles$
|
|
5171
|
+
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"};
|
|
5172
5172
|
|
|
5173
5173
|
/**
|
|
5174
5174
|
* Multi-select input with chip tags and searchable dropdown.
|
|
@@ -5319,11 +5319,11 @@ function CwTagSelector({ id, options, value, onChange, labelProps, iconProps, al
|
|
|
5319
5319
|
const feedbackId = useId();
|
|
5320
5320
|
const feedbackIds = feedbackMessages.map((_, index) => `${feedbackId}-${index}`);
|
|
5321
5321
|
const describedBy = feedbackIds.join(" ") || undefined;
|
|
5322
|
-
return (jsxs("div", { ref: containerRef, className: `${styles$
|
|
5323
|
-
inputRef.current?.focus(); }, children: [selectedOptions.map(opt => (jsx(CwChip, { label: opt.label, colorScheme: opt.color ? "custom" : "info", customColor: opt.color, closable: !disabled, onClose: () => handleRemove(opt.value) }, opt.value))), 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 && (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) => (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) ? (jsxs("div", { ref: panelRef, className: styles$
|
|
5322
|
+
return (jsxs("div", { ref: containerRef, className: `${styles$e.container}${className ? ` ${className}` : ""}`, style: style, id: id, "data-direction": direction, children: [jsxs(CwAlign, { ...alignProps, children: [labelProps && (jsxs(CwLabel, { ...labelProps, children: [iconProps && jsx(CwIcon, { ...iconProps }), labelProps.text] })), jsxs("div", { className: styles$e.value, children: [jsxs("div", { className: `${styles$e.inputBox}${disabled ? ` ${styles$e.disabled}` : ""}`, ref: inputBoxRef, onClick: () => { if (!disabled)
|
|
5323
|
+
inputRef.current?.focus(); }, children: [selectedOptions.map(opt => (jsx(CwChip, { label: opt.label, colorScheme: opt.color ? "custom" : "info", customColor: opt.color, closable: !disabled, onClose: () => handleRemove(opt.value) }, opt.value))), 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 && (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) => (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) ? (jsxs("div", { ref: panelRef, className: styles$e.dropdown, style: panelStyle, children: [showSelectAll && !search && (jsxs("div", { className: styles$e.selectAll, onMouseDown: (e) => e.preventDefault(), onClick: handleToggleAll, children: [jsx("input", { type: "checkbox", checked: allSelected, readOnly: true, tabIndex: -1 }), allSelected ? "Deselect all" : "Select all"] })), jsx("ul", { ref: listRef, children: filtered.map((opt, idx) => (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 && jsx("span", { className: styles$e.optionDot, style: { backgroundColor: opt.color } }), opt.label] }, opt.value))) })] })) : null)] }));
|
|
5324
5324
|
}
|
|
5325
5325
|
|
|
5326
|
-
var styles$
|
|
5326
|
+
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"};
|
|
5327
5327
|
|
|
5328
5328
|
function CwTreeView({ data, onSelect, allowParentSelection = false, selectedId: initialSelectedId = null, ...dataAttributes }) {
|
|
5329
5329
|
const [expanded, setExpanded] = useState(new Set());
|
|
@@ -5390,10 +5390,10 @@ function CwTreeView({ data, onSelect, allowParentSelection = false, selectedId:
|
|
|
5390
5390
|
const isLeaf = !hasChildren;
|
|
5391
5391
|
const isSelected = selectedId?.toString().toLowerCase() === node.id.toString().toLowerCase();
|
|
5392
5392
|
const selectable = allowParentSelection || isLeaf;
|
|
5393
|
-
return (jsxs("div", { className: styles$
|
|
5394
|
-
styles$
|
|
5395
|
-
isSelected && styles$
|
|
5396
|
-
!selectable && styles$
|
|
5393
|
+
return (jsxs("div", { className: styles$d["tree-item"], children: [jsxs("div", { className: [
|
|
5394
|
+
styles$d["tree-node"],
|
|
5395
|
+
isSelected && styles$d["selected"],
|
|
5396
|
+
!selectable && styles$d["non-selectable"]
|
|
5397
5397
|
].filter(Boolean).join(' '), style: { paddingLeft: `${depth * 24 + 8}px` }, onClick: () => {
|
|
5398
5398
|
if (selectable) {
|
|
5399
5399
|
setSelectedId(node.id);
|
|
@@ -5405,7 +5405,7 @@ function CwTreeView({ data, onSelect, allowParentSelection = false, selectedId:
|
|
|
5405
5405
|
}, children: [hasChildren && (jsx(CwButton, { variant: "icon", color: "neutral", icon: isExpanded ? "chevron-down" : "chevron-right", "aria-expanded": isExpanded, "aria-label": `${isExpanded ? "Collapse" : "Expand"} ${node.label}`, onClick: (event) => {
|
|
5406
5406
|
event.stopPropagation();
|
|
5407
5407
|
toggleExpand(node.id);
|
|
5408
|
-
} })), jsx("div", { className: `${styles$
|
|
5408
|
+
} })), jsx("div", { className: `${styles$d["tree-label"]} ${!hasChildren ? styles$d["tree-spacer"] : ''}`, children: node.label })] }), hasChildren && isExpanded && (jsx("div", { className: styles$d["tree-children"], children: node.children.map((child) => renderNode(child, depth + 1)) }))] }, node.id));
|
|
5409
5409
|
};
|
|
5410
5410
|
// Function to search for nodes that match the search term
|
|
5411
5411
|
const searchInTree = (nodes, searchTerm) => {
|
|
@@ -5429,7 +5429,7 @@ function CwTreeView({ data, onSelect, allowParentSelection = false, selectedId:
|
|
|
5429
5429
|
return results;
|
|
5430
5430
|
};
|
|
5431
5431
|
const filteredData = searchInTree(data, search);
|
|
5432
|
-
return (jsxs("div", { ...dataAttributes, className: `cw-tree-view ${styles$
|
|
5432
|
+
return (jsxs("div", { ...dataAttributes, className: `cw-tree-view ${styles$d["tree-container"]}`, children: [jsxs("header", { children: [jsx("input", { type: "text", placeholder: "Search in tree...", value: search, onChange: (e) => setSearch(e.target.value) }), search && (jsx(CwButton, { onClick: () => setSearch(""), type: "button", icon: "close", variant: "icon", color: "neutral" }))] }), jsx("div", { className: styles$d["tree-wrapper"], children: filteredData.length > 0 ? (filteredData.map((node) => renderNode(node))) : (jsxs("div", { className: styles$d["empty-state"], children: [jsx(CwIcon, { iconId: "comment" }), search ? "No results found" : "There are no items"] })) })] }));
|
|
5433
5433
|
}
|
|
5434
5434
|
|
|
5435
5435
|
const EXACT_PHRASE = /"([^"]+)"/g;
|
|
@@ -5497,7 +5497,7 @@ function sortNavItems(items, alphabetically) {
|
|
|
5497
5497
|
return [...items].sort((first, second) => first.label.localeCompare(second.label));
|
|
5498
5498
|
}
|
|
5499
5499
|
|
|
5500
|
-
var styles$
|
|
5500
|
+
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"};
|
|
5501
5501
|
|
|
5502
5502
|
const SKELETON_ROW_COUNT = 5;
|
|
5503
5503
|
const defaultRenderLink = (item, children) => (jsx("a", { href: item.href, children: children }));
|
|
@@ -5509,13 +5509,13 @@ function CwNavMenu({ items, loading = false, isActive, renderLink = defaultRende
|
|
|
5509
5509
|
const renderItem = (item, level) => {
|
|
5510
5510
|
const children = item.children;
|
|
5511
5511
|
if (children && children.length > 0) {
|
|
5512
|
-
return (jsx("li", { children: jsxs("details", { "data-level": level, open: hasActiveDescendant(item) || undefined, children: [jsxs("summary", { children: [item.icon && jsx(CwIcon, { iconId: item.icon, size: level >= 2 ? "small" : "medium" }), item.label, (showExpandIndicator || level >= 2) && (jsx(CwIcon, { iconId: "chevron-down", size: "small", className: styles$
|
|
5512
|
+
return (jsx("li", { children: jsxs("details", { "data-level": level, open: hasActiveDescendant(item) || undefined, children: [jsxs("summary", { children: [item.icon && jsx(CwIcon, { iconId: item.icon, size: level >= 2 ? "small" : "medium" }), item.label, (showExpandIndicator || level >= 2) && (jsx(CwIcon, { iconId: "chevron-down", size: "small", className: styles$c.expandIndicator }))] }), jsx("ul", { children: children.map(child => renderItem(child, level + 1)) })] }) }, item.id));
|
|
5513
5513
|
}
|
|
5514
|
-
return (jsx("li", { "data-active": isActive?.(item) ? "true" : undefined, children: renderLink(item, jsxs(Fragment, { children: [item.icon && jsx(CwIcon, { iconId: item.icon, size: "medium" }), item.label, isFavorite?.(item) && jsx(CwIcon, { iconId: "star", size: "small", className: styles$
|
|
5514
|
+
return (jsx("li", { "data-active": isActive?.(item) ? "true" : undefined, children: renderLink(item, jsxs(Fragment, { children: [item.icon && jsx(CwIcon, { iconId: item.icon, size: "medium" }), item.label, isFavorite?.(item) && jsx(CwIcon, { iconId: "star", size: "small", className: styles$c.favoriteStar })] })) }, item.id));
|
|
5515
5515
|
};
|
|
5516
|
-
return (jsxs("nav", { ...navProps, className: [styles$
|
|
5517
|
-
? Array.from({ length: SKELETON_ROW_COUNT }, (_unused, index) => (jsx("li", { className: styles$
|
|
5518
|
-
: visibleItems.map(item => renderItem(item, 1)) }), showEmptyMessage && jsx("p", { className: styles$
|
|
5516
|
+
return (jsxs("nav", { ...navProps, className: [styles$c.menu, className].filter(Boolean).join(" "), "aria-busy": loading || undefined, children: [favoritesSlot && (jsxs("details", { open: true, ...favoritesProps, className: [styles$c.favorites, favoritesProps?.className].filter(Boolean).join(" "), children: [jsxs("summary", { children: [jsx(CwIcon, { iconId: "star", size: "medium" }), favoritesLabel] }), jsx("div", { className: styles$c.favoritesContent, children: favoritesSlot })] })), beforeTreeSlot, jsx("ul", { children: loading
|
|
5517
|
+
? Array.from({ length: SKELETON_ROW_COUNT }, (_unused, index) => (jsx("li", { className: styles$c.skeleton, children: jsx(CwSkeleton, { height: "var(--cw-size-small)", radius: "var(--cw-border-radius-small)" }) }, index)))
|
|
5518
|
+
: visibleItems.map(item => renderItem(item, 1)) }), showEmptyMessage && jsx("p", { className: styles$c.emptyMessage, children: emptyMessage })] }));
|
|
5519
5519
|
}
|
|
5520
5520
|
|
|
5521
5521
|
var dropdownStyles = {"dropdown":"cw-choice-dropdown-module__dropdown__P0b4j","option":"cw-choice-dropdown-module__option__uR-ro","optionFocused":"cw-choice-dropdown-module__optionFocused__BYMxj"};
|
|
@@ -5681,7 +5681,7 @@ function CwSearchInput(optionsProps) {
|
|
|
5681
5681
|
return (jsxs("div", { className: `cw-search-input ${className || ""}`, style: style, ...otherProps, "data-direction": direction, children: [jsxs(CwAlign, { ...alignProps, children: [labelProps && (jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxs("div", { className: "cw-search-input-wrapper", ref: wrapperRef, children: [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 && (jsx("div", { className: "cw-search-input-loading", children: jsx(CwIcon, { iconId: "spinner" }) })), jsx("div", { className: "cw-search-input-icons", children: searchText && !disabled && !isLoading ? (jsx(CwButton, { type: "button", onClick: handleClearClick, "aria-label": "Clear search", variant: "icon", icon: "close", color: "neutral" })) : (iconProps ? jsx(CwIcon, { ...iconProps }) : jsx(CwIcon, { iconId: "search" })) })] })] }), renderPanel(jsx("div", { ref: panelRef, id: listboxId, className: dropdownStyles.dropdown, style: panelStyle, role: "listbox", children: jsx("ul", { children: filteredOptions.map((option, index) => (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))) }) }))] }));
|
|
5682
5682
|
}
|
|
5683
5683
|
|
|
5684
|
-
var styles$
|
|
5684
|
+
var styles$b = {"value":"cw-search-async-module__value__z53xT"};
|
|
5685
5685
|
|
|
5686
5686
|
/**
|
|
5687
5687
|
* Generic API-backed search input. Fetches matching options from a caller-supplied `onSearch`
|
|
@@ -5945,10 +5945,10 @@ const CwSearchAsync = ({ onSearch, onChange, value, onResolveInitial, onResolveS
|
|
|
5945
5945
|
return (jsxs("div", { ...dataAttributes, className: `cw-search-input ${className}`, style: {
|
|
5946
5946
|
...(width ? { width } : {}),
|
|
5947
5947
|
...(labelProps?.labelWidth ? { "--label-width": labelProps.labelWidth } : {}),
|
|
5948
|
-
}, "data-direction": direction, children: [jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxs("div", { className: styles$
|
|
5948
|
+
}, "data-direction": direction, children: [jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxs("div", { className: styles$b.value, children: [jsxs("div", { className: "cw-search-input-wrapper", ref: wrapperRef, children: [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) && (jsx("div", { className: "cw-search-input-loading", children: jsx(CwIcon, { iconId: "spinner" }) })), jsx("div", { className: "cw-search-input-icons", children: inputValue && !disabled && !isInitialLoading ? (jsx(CwButton, { type: "button", onClick: handleClear, "aria-label": "Clear selection", variant: "icon", icon: "close", color: "neutral" })) : (icon ? jsx(CwIcon, { ...icon }) : jsx(CwIcon, { iconId: "search" })) })] }), feedbackMessages.map((feedbackItem, index) => (jsx("p", { id: feedbackIds[index], className: "cw-input-feedback", "data-color": feedbackItem.type, "aria-live": "polite", children: feedbackItem.message }, index)))] })] }), renderPanel(jsx("div", { ref: panelRef, id: listboxId, className: dropdownStyles.dropdown, style: panelStyle, role: "listbox", children: jsx("ul", { children: options.map((option, index) => (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 ? (jsxs("span", { className: "cw-search-async-option-lines", children: [jsx("span", { children: option.label }), jsx("span", { className: "cw-search-async-option-sub", children: option.subLabel })] })) : (option.label) }, option.id))) }) }))] }));
|
|
5949
5949
|
};
|
|
5950
5950
|
|
|
5951
|
-
var styles$
|
|
5951
|
+
var styles$a = {"menu":"cw-context-menu-module__menu__tXmun","item":"cw-context-menu-module__item__-ko8L","arrow":"cw-context-menu-module__arrow__LHZmQ"};
|
|
5952
5952
|
|
|
5953
5953
|
const ARROW_SIZE = 8;
|
|
5954
5954
|
const SAFETY_MARGIN = 8;
|
|
@@ -6089,14 +6089,14 @@ const CwAnchoredMenu = ({ children, options, onSelect, placement = 'bottom', ope
|
|
|
6089
6089
|
document.removeEventListener("keydown", handleEscape);
|
|
6090
6090
|
};
|
|
6091
6091
|
}, [isOpen]);
|
|
6092
|
-
return (jsxs(Fragment, { children: [jsx("div", { ref: triggerRef, onContextMenu: handleContextMenu, style: { display: "contents" }, children: children }), isOpen && normalizedOptions.length > 0 && createPortal(jsxs("div", { ref: menuRef, className: styles$
|
|
6092
|
+
return (jsxs(Fragment, { children: [jsx("div", { ref: triggerRef, onContextMenu: handleContextMenu, style: { display: "contents" }, children: children }), isOpen && normalizedOptions.length > 0 && createPortal(jsxs("div", { ref: menuRef, className: styles$a["menu"], style: {
|
|
6093
6093
|
top: `${position.y}px`,
|
|
6094
6094
|
left: `${position.x}px`,
|
|
6095
|
-
}, "data-has-arrow": "true", children: [jsx("div", { ref: arrowRef, className: styles$
|
|
6095
|
+
}, "data-has-arrow": "true", children: [jsx("div", { ref: arrowRef, className: styles$a["arrow"], "data-placement": placement }), normalizedOptions.map(option => (jsxs("div", { onClick: (e) => {
|
|
6096
6096
|
e.stopPropagation();
|
|
6097
6097
|
e.preventDefault();
|
|
6098
6098
|
handleOptionClick(option);
|
|
6099
|
-
}, className: `${styles$
|
|
6099
|
+
}, className: `${styles$a["item"]} ${option.disabled ? styles$a["disabled"] : ""}`, "data-disabled": option.disabled, children: [option.icon && jsx("span", { className: styles$a["icon"], children: option.icon }), option.label] }, option.key)))] }), document.body)] }));
|
|
6100
6100
|
};
|
|
6101
6101
|
|
|
6102
6102
|
/**
|
|
@@ -6169,13 +6169,13 @@ const CwContextMenu = ({ children, options, offset = DEFAULT_OFFSET, onSelect })
|
|
|
6169
6169
|
document.removeEventListener("keydown", handleEscape);
|
|
6170
6170
|
};
|
|
6171
6171
|
}, [isOpen]);
|
|
6172
|
-
return (jsxs(Fragment, { children: [jsx("div", { onContextMenu: handleContextMenu, children: children }), isOpen && createPortal(jsx("div", { ref: menuRef, className: styles$
|
|
6172
|
+
return (jsxs(Fragment, { children: [jsx("div", { onContextMenu: handleContextMenu, children: children }), isOpen && createPortal(jsx("div", { ref: menuRef, className: styles$a["menu"], style: {
|
|
6173
6173
|
top: `${position.y}px`,
|
|
6174
6174
|
left: `${position.x}px`,
|
|
6175
|
-
}, children: options.map(option => (jsx("div", { onClick: () => handleOptionClick(option), className: styles$
|
|
6175
|
+
}, children: options.map(option => (jsx("div", { onClick: () => handleOptionClick(option), className: styles$a["item"], children: option }, option))) }), document.body)] }));
|
|
6176
6176
|
};
|
|
6177
6177
|
|
|
6178
|
-
var styles$
|
|
6178
|
+
var styles$9 = {"split":"cw-button-split-module__split__B2KbU","menu":"cw-button-split-module__menu__mKDlt","item":"cw-button-split-module__item__Aqw6f"};
|
|
6179
6179
|
|
|
6180
6180
|
/**
|
|
6181
6181
|
* CwButtonSplit - A primary action joined to a dropdown of alternative actions.
|
|
@@ -6201,7 +6201,7 @@ function CwButtonSplit({ text, icon, onClick, actions, onSelect, variant = "soli
|
|
|
6201
6201
|
onSelect(action.key);
|
|
6202
6202
|
event.currentTarget.closest("[popover]")?.hidePopover();
|
|
6203
6203
|
};
|
|
6204
|
-
return (jsxs("div", { className: styles$
|
|
6204
|
+
return (jsxs("div", { className: styles$9.split, "data-variant": variant, "data-testid": dataTestId, children: [jsx(CwButton, { text: text, icon: icon, variant: variant, color: color, size: size, onClick: onClick, disabled: disabled }), jsx(CwPopoverButton, { icon: "chevron-down", title: "More actions", variant: variant, color: color, size: size, disabled: disabled, placement: placement, children: jsx("div", { className: styles$9.menu, children: actions.map((action) => (jsx(CwButton, { variant: "ghost", color: "neutral", text: action.label, icon: action.icon, disabled: action.disabled, className: styles$9.item, onClick: handleSelect(action) }, action.key))) }) })] }));
|
|
6205
6205
|
}
|
|
6206
6206
|
|
|
6207
6207
|
/**
|
|
@@ -6269,7 +6269,7 @@ function useRovingSingleSelect(options, value, onChange) {
|
|
|
6269
6269
|
return { activeIndex, getSegmentProps };
|
|
6270
6270
|
}
|
|
6271
6271
|
|
|
6272
|
-
var styles$
|
|
6272
|
+
var styles$8 = {"switch":"cw-button-switch-module__switch__vdWV-","segment":"cw-button-switch-module__segment__JmVla","iconOnly":"cw-button-switch-module__iconOnly__-kCwj"};
|
|
6273
6273
|
|
|
6274
6274
|
/**
|
|
6275
6275
|
* CwButtonSwitch - Segmented single-select control: a strip of joined CwButtons.
|
|
@@ -6299,10 +6299,10 @@ var styles$5 = {"switch":"cw-button-switch-module__switch__vdWV-","segment":"cw-
|
|
|
6299
6299
|
*/
|
|
6300
6300
|
function CwButtonSwitch({ options, value, onChange, size = "medium", color = "primary", iconOnly = false, direction = "row", disabled = false, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, ...dataAttributes }) {
|
|
6301
6301
|
const { getSegmentProps } = useRovingSingleSelect(options, value, onChange);
|
|
6302
|
-
return (jsx("div", { ...dataAttributes, className: `cw-button-switch ${styles$
|
|
6302
|
+
return (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) => {
|
|
6303
6303
|
const selected = option.value === value;
|
|
6304
6304
|
const segmentProps = getSegmentProps(index);
|
|
6305
|
-
return (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$
|
|
6305
|
+
return (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));
|
|
6306
6306
|
}) }));
|
|
6307
6307
|
}
|
|
6308
6308
|
|
|
@@ -6344,7 +6344,7 @@ function CwBtnCancel({ cw_btnOnclick, cw_btn_disabled, onClick, disabled, ...res
|
|
|
6344
6344
|
return (jsx(CwButton, { ...rest, variant: "icon", icon: "cancel", color: "danger", onClick: onClick ?? cw_btnOnclick, disabled: disabled ?? cw_btn_disabled, title: "Cancel" }));
|
|
6345
6345
|
}
|
|
6346
6346
|
|
|
6347
|
-
var styles$
|
|
6347
|
+
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"};
|
|
6348
6348
|
|
|
6349
6349
|
/**
|
|
6350
6350
|
* Renders a picker popup in a portal so it escapes overflow containers.
|
|
@@ -6584,7 +6584,7 @@ function CwDatePicker({ value, onChange, minDate, maxDate, disabledDates, disabl
|
|
|
6584
6584
|
...(maxDate ? [{ after: maxDate }] : []),
|
|
6585
6585
|
...(disabledMatcher ? [disabledMatcher] : []),
|
|
6586
6586
|
], [disabledDates, minDate, maxDate, disabledMatcher]);
|
|
6587
|
-
return (jsx("div", { ...dataAttributes, ref: containerRef, className: `cw-datepicker ${className || ""}`, children: jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxs("div", { className: styles$
|
|
6587
|
+
return (jsx("div", { ...dataAttributes, ref: containerRef, className: `cw-datepicker ${className || ""}`, children: jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxs("div", { className: styles$7.value, children: [jsxs("div", { ref: wrapperRef, className: styles$7.pickerWrapper, children: [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 }), jsx("div", { className: styles$7.pickerIcons, children: showClear && value && !disabled ? (jsx(CwButton, { type: "button", variant: "icon", color: "neutral", icon: "close", onClick: handleClear, tabIndex: -1, "aria-label": "Clear date" })) : (jsx(CwIcon, { iconId: "calendar" })) }), renderPopup(jsxs("div", { ref: popupRef, className: styles$7.pickerPopup, style: popupStyle, children: [jsx(DayPicker, { mode: "single", selected: value || undefined, defaultMonth: defaultMonth || value || undefined, onSelect: handleDaySelect, disabled: disabledDays, numberOfMonths: numberOfMonths, locale: locale, captionLayout: "dropdown", startMonth: dropdownStartMonth, endMonth: dropdownEndMonth, modifiers: {
|
|
6588
6588
|
today: new Date(),
|
|
6589
6589
|
}, modifiersClassNames: {
|
|
6590
6590
|
today: "rdp-day-today",
|
|
@@ -7233,7 +7233,7 @@ function CwDateRangePicker({ value, onChange, minDate, maxDate, disabledDates, d
|
|
|
7233
7233
|
}
|
|
7234
7234
|
return undefined;
|
|
7235
7235
|
}, [value]);
|
|
7236
|
-
return (jsx("div", { ...dataAttributes, ref: containerRef, className: `cw-rangepicker ${className || ""}`, children: jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxs("div", { className: styles$
|
|
7236
|
+
return (jsx("div", { ...dataAttributes, ref: containerRef, className: `cw-rangepicker ${className || ""}`, children: jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxs("div", { className: styles$7.value, children: [jsxs("div", { ref: wrapperRef, className: rangeStyles.rangeWrapper, children: [jsxs("div", { className: styles$7.pickerWrapper, children: [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' }), jsx("div", { className: styles$7.pickerIcons, children: showClear && value?.from && !disabled ? (jsx(CwButton, { type: "button", variant: "icon", color: "neutral", icon: "close", onClick: handleClearFrom, tabIndex: -1, "aria-label": "Clear from date" })) : (jsx(CwIcon, { iconId: "calendar" })) })] }), jsx(CwIcon, { iconId: "arrow-right", size: "medium" }), jsxs("div", { className: styles$7.pickerWrapper, children: [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' }), jsx("div", { className: styles$7.pickerIcons, children: showClear && value?.to && !disabled ? (jsx(CwButton, { type: "button", variant: "icon", color: "neutral", icon: "close", onClick: handleClearTo, tabIndex: -1, "aria-label": "Clear to date" })) : (jsx(CwIcon, { iconId: "calendar" })) })] }), renderPopup(jsxs("div", { ref: popupRef, className: `${styles$7.pickerPopup} ${rangeStyles.rangePopup}`, style: popupStyle, children: [jsxs("div", { className: rangeStyles.rangeCalendarRow, children: [showPresets && presetsToRender.length > 0 && (jsx("div", { className: rangeStyles.presetList, children: presetsToRender.map((preset) => (jsx("button", { type: "button", onClick: (e) => handlePresetClick(preset, e), children: preset.label }, preset.key))) })), jsx(DayPicker, { mode: "range", selected: selectedRange, onSelect: handleRangeSelect, disabled: disabledDays, numberOfMonths: numberOfMonths, defaultMonth: defaultMonth || value?.from || undefined, modifiers: {
|
|
7237
7237
|
today: new Date(),
|
|
7238
7238
|
}, modifiersClassNames: {
|
|
7239
7239
|
today: "rdp-day-today",
|
|
@@ -7406,10 +7406,10 @@ function CwTimePicker({ value, onChange, interval = 15, minTime, maxTime, labelP
|
|
|
7406
7406
|
const displayValue = inputValue;
|
|
7407
7407
|
// If using native time input
|
|
7408
7408
|
if (useNative) {
|
|
7409
|
-
return (jsx("div", { ...dataAttributes, className: `cw-timepicker cw-timepicker-native ${className || ""}`, children: jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxs("div", { className: styles$
|
|
7409
|
+
return (jsx("div", { ...dataAttributes, className: `cw-timepicker cw-timepicker-native ${className || ""}`, children: jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxs("div", { className: styles$7.value, children: [jsx("div", { className: styles$7.pickerWrapper, children: 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] })] }) }));
|
|
7410
7410
|
}
|
|
7411
7411
|
// Custom time picker
|
|
7412
|
-
return (jsx("div", { ...dataAttributes, ref: containerRef, className: `cw-timepicker ${className || ""}`, children: jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxs("div", { className: styles$
|
|
7412
|
+
return (jsx("div", { ...dataAttributes, ref: containerRef, className: `cw-timepicker ${className || ""}`, children: jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxs("div", { className: styles$7.value, children: [jsxs("div", { ref: wrapperRef, className: `${styles$7.pickerWrapper} ${timeStyles.field}${showClear ? "" : ` ${timeStyles.narrow}`}`, children: [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 && (jsx("div", { className: styles$7.pickerIcons, children: value && !disabled ? (jsx(CwButton, { type: "button", variant: "icon", icon: "close", color: "neutral", onClick: handleClear, tabIndex: -1, "aria-label": "Clear time" })) : (jsx(CwIcon, { iconId: "clock" })) })), renderPopup(jsx("div", { ref: popupRef, className: `${styles$7.pickerPopup} ${timeStyles.timePickerPopup}`, style: popupStyle, children: jsx("div", { className: timeStyles.timePickerList, children: timeOptions.map((time) => {
|
|
7413
7413
|
const isSelected = time === value;
|
|
7414
7414
|
return (jsx("button", { ref: isSelected ? selectedItemRef : null, type: "button", className: isSelected ? timeStyles.selected : "", onClick: () => handleTimeSelect(time), children: time }, time));
|
|
7415
7415
|
}) }) }))] }), feedbackElements] })] }) }));
|
|
@@ -7717,7 +7717,7 @@ function CwDateTimePicker({ value, onChange, minDateTime, maxDateTime, disabledD
|
|
|
7717
7717
|
const displayMaxDate = useMemo(() => normalizedMaxDateTime && timezoneOffset !== undefined
|
|
7718
7718
|
? getDateInOffset(normalizedMaxDateTime, timezoneOffset)
|
|
7719
7719
|
: normalizedMaxDateTime, [normalizedMaxDateTime, timezoneOffset]);
|
|
7720
|
-
return jsx("div", { ...dataAttributes, className: `cw-datetimepicker cw-datetimepicker-separate ${className || ""}`, children: jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxs("div", { className: styles$
|
|
7720
|
+
return jsx("div", { ...dataAttributes, className: `cw-datetimepicker cw-datetimepicker-separate ${className || ""}`, children: jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxs("div", { className: styles$7.value, children: [jsxs("div", { className: "cw-flex-row cw-align-left-center cw-gap-small", children: [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 }), 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 && (jsx(CwButton, { type: "button", variant: "outline", onClick: handleNowClick, disabled: disabled, text: nowLabel }))] }), feedbackMessages.map((feedbackItem, index) => (jsx("p", { id: feedbackIds[index], className: "cw-input-feedback", "data-color": feedbackItem.type, "aria-live": "polite", children: feedbackItem.message }, index)))] })] }) });
|
|
7721
7721
|
}
|
|
7722
7722
|
|
|
7723
7723
|
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"};
|
|
@@ -7973,7 +7973,7 @@ function CwDateTimePickerCompact({ value, onChange, minDateTime, maxDateTime, di
|
|
|
7973
7973
|
...(maxDateTime ? [{ after: maxDateTime }] : []),
|
|
7974
7974
|
...(disabledMatcher ? [disabledMatcher] : []),
|
|
7975
7975
|
], [disabledDates, minDateTime, maxDateTime, disabledMatcher]);
|
|
7976
|
-
return (jsx("div", { ...dataAttributes, ref: containerRef, className: `cw-datetimepicker ${className || ""}`, children: jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxs("div", { className: styles$
|
|
7976
|
+
return (jsx("div", { ...dataAttributes, ref: containerRef, className: `cw-datetimepicker ${className || ""}`, children: jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxs("div", { className: styles$7.value, children: [jsxs("div", { ref: wrapperRef, className: styles$7.pickerWrapper, children: [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 }), jsx("div", { className: styles$7.pickerIcons, children: showClear && value && !disabled ? (jsx(CwButton, { type: "button", variant: "icon", color: "neutral", icon: "close", onClick: handleClear, tabIndex: -1, "aria-label": "Clear datetime" })) : (jsx(CwIcon, { iconId: "calendar-time" })) }), renderPopup(jsxs("div", { ref: popupRef, className: `${styles$7.pickerPopup} ${compactStyles.compactPopup}`, style: popupStyle, children: [jsxs("div", { className: compactStyles.calendarWrapper, children: [jsx(DayPicker, { mode: "single", selected: selectedDate, onSelect: handleDaySelect, defaultMonth: selectedDate, disabled: disabledDays, numberOfMonths: numberOfMonths, locale: locale, captionLayout: "dropdown", startMonth: dropdownStartMonth, endMonth: dropdownEndMonth, modifiers: {
|
|
7977
7977
|
today: new Date(),
|
|
7978
7978
|
}, modifiersClassNames: {
|
|
7979
7979
|
today: "rdp-day-today",
|
|
@@ -8208,7 +8208,7 @@ function CwDatePickerTemporal({ value, onChange, minDate, maxDate, disabledDates
|
|
|
8208
8208
|
const defaultMonthJsDate = defaultMonth
|
|
8209
8209
|
? new Date(defaultMonth.year, defaultMonth.month - 1, 1)
|
|
8210
8210
|
: selectedJsDate;
|
|
8211
|
-
return (jsx("div", { ...dataAttributes, ref: containerRef, className: `cw-datepicker ${className || ""}`, children: jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxs("div", { className: styles$
|
|
8211
|
+
return (jsx("div", { ...dataAttributes, ref: containerRef, className: `cw-datepicker ${className || ""}`, children: jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxs("div", { className: styles$7.value, children: [jsxs("div", { ref: wrapperRef, className: styles$7.pickerWrapper, children: [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 }), jsx("div", { className: styles$7.pickerIcons, children: showClear && value && !disabled ? (jsx(CwButton, { type: "button", variant: "icon", color: "neutral", icon: "close", onClick: handleClear, tabIndex: -1, "aria-label": "Clear date" })) : (jsx(CwIcon, { iconId: "calendar" })) }), renderPopup(jsxs("div", { ref: popupRef, className: styles$7.pickerPopup, style: popupStyle, children: [jsx(DayPicker, { mode: "single", selected: selectedJsDate, defaultMonth: defaultMonthJsDate, onSelect: handleDaySelect, disabled: disabledDays, numberOfMonths: numberOfMonths, locale: locale, captionLayout: "dropdown", startMonth: dropdownStartMonth, endMonth: dropdownEndMonth, modifiers: {
|
|
8212
8212
|
today: new Date(),
|
|
8213
8213
|
}, modifiersClassNames: {
|
|
8214
8214
|
today: "rdp-day-today",
|
|
@@ -8509,7 +8509,7 @@ function CwDateTimePickerTemporal({ value, onChange, minDateTime, maxDateTime, d
|
|
|
8509
8509
|
minTime,
|
|
8510
8510
|
maxTime,
|
|
8511
8511
|
});
|
|
8512
|
-
return jsx("div", { ...dataAttributes, className: `cw-datetimepicker cw-datetimepicker-separate ${className || ""}`, children: jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxs("div", { className: styles$
|
|
8512
|
+
return jsx("div", { ...dataAttributes, className: `cw-datetimepicker cw-datetimepicker-separate ${className || ""}`, children: jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxs("div", { className: styles$7.value, children: [jsxs("div", { className: "cw-flex-row cw-align-left-center cw-gap-small", children: [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 }), 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 && (jsx(CwButton, { type: "button", variant: "outline", onClick: picker.selectNow, disabled: disabled, text: nowLabel }))] }), feedbackMessages.map((feedbackItem, index) => (jsx("p", { id: feedbackIds[index], className: "cw-input-feedback", "data-color": feedbackItem.type, "aria-live": "polite", children: feedbackItem.message }, index)))] })] }) });
|
|
8513
8513
|
}
|
|
8514
8514
|
|
|
8515
8515
|
// dateRangePresetsTemporal.ts
|
|
@@ -8961,7 +8961,7 @@ function CwDateRangePickerTemporal({ value, onChange, minDate, maxDate, disabled
|
|
|
8961
8961
|
const defaultMonthJsDate = defaultMonth
|
|
8962
8962
|
? new Date(defaultMonth.year, defaultMonth.month - 1, 1)
|
|
8963
8963
|
: (value?.from ? plainDateToJsDate(value.from) : undefined);
|
|
8964
|
-
return (jsx("div", { ...dataAttributes, ref: containerRef, className: `cw-rangepicker ${className || ""}`, children: jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxs("div", { className: styles$
|
|
8964
|
+
return (jsx("div", { ...dataAttributes, ref: containerRef, className: `cw-rangepicker ${className || ""}`, children: jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxs("div", { className: styles$7.value, children: [jsxs("div", { ref: wrapperRef, className: rangeStyles.rangeWrapper, children: [jsxs("div", { className: styles$7.pickerWrapper, children: [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' }), jsx("div", { className: styles$7.pickerIcons, children: showClear && value?.from && !disabled ? (jsx(CwButton, { type: "button", variant: "icon", color: "neutral", icon: "close", onClick: handleClearFrom, tabIndex: -1, "aria-label": "Clear from date" })) : (jsx(CwIcon, { iconId: "calendar" })) })] }), jsx(CwIcon, { iconId: "arrow-right", size: "medium" }), jsxs("div", { className: styles$7.pickerWrapper, children: [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' }), jsx("div", { className: styles$7.pickerIcons, children: showClear && value?.to && !disabled ? (jsx(CwButton, { type: "button", variant: "icon", color: "neutral", icon: "close", onClick: handleClearTo, tabIndex: -1, "aria-label": "Clear to date" })) : (jsx(CwIcon, { iconId: "calendar" })) })] }), renderPopup(jsxs("div", { ref: popupRef, className: `${styles$7.pickerPopup} ${rangeStyles.rangePopup}`, style: popupStyle, children: [jsxs("div", { className: rangeStyles.rangeCalendarRow, children: [showPresets && presetsToRender.length > 0 && (jsx("div", { className: rangeStyles.presetList, children: presetsToRender.map((preset) => (jsx("button", { type: "button", onClick: (e) => handlePresetClick(preset, e), children: preset.label }, preset.key))) })), jsx(DayPicker, { mode: "range", selected: selectedRange, onSelect: handleRangeSelect, disabled: disabledDays, numberOfMonths: numberOfMonths, defaultMonth: defaultMonthJsDate, locale: locale, captionLayout: "dropdown", startMonth: dropdownStartMonth, endMonth: dropdownEndMonth, modifiers: {
|
|
8965
8965
|
today: new Date(),
|
|
8966
8966
|
}, modifiersClassNames: {
|
|
8967
8967
|
today: "rdp-day-today",
|
|
@@ -9108,7 +9108,7 @@ class OnClickContextMenu {
|
|
|
9108
9108
|
}
|
|
9109
9109
|
}
|
|
9110
9110
|
|
|
9111
|
-
var styles$
|
|
9111
|
+
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"};
|
|
9112
9112
|
|
|
9113
9113
|
const getNow$1 = (isUtc) => {
|
|
9114
9114
|
let now = new Date();
|
|
@@ -9137,9 +9137,9 @@ const TimeLine = ({ left, top = "0px", height, color, width = "2px", zIndexNumbe
|
|
|
9137
9137
|
background: color,
|
|
9138
9138
|
pointerEvents: "none",
|
|
9139
9139
|
zIndex: zIndexNumber,
|
|
9140
|
-
}, className: styles$
|
|
9140
|
+
}, className: styles$6["scheduler-timeline-now"], children: isHeader && (jsx("span", { style: {
|
|
9141
9141
|
color: color,
|
|
9142
|
-
}, className: styles$
|
|
9142
|
+
}, className: styles$6["scheduler-timeline-label"], children: currentTime.toLocaleTimeString([], {
|
|
9143
9143
|
hour: '2-digit',
|
|
9144
9144
|
minute: '2-digit',
|
|
9145
9145
|
hour12: false
|
|
@@ -9166,17 +9166,17 @@ const SchedulerHeaderDivision = ({ topDivisions, bottomDivisions, classification
|
|
|
9166
9166
|
return (jsxs("div", { style: {
|
|
9167
9167
|
width: topWidth,
|
|
9168
9168
|
cursor: "pointer"
|
|
9169
|
-
}, children: [jsx("div", { className: `${styles$
|
|
9169
|
+
}, children: [jsx("div", { className: `${styles$6["scheduler-dates"]} ${isToday(startDate, endDate) ? styles$6["current-day"] : ""}`, children: jsx("span", { children: topDivisions }) }), jsx("div", { style: { display: "flex", justifyContent: "space-between", flex: 1 }, children: bottomDivisions.map((element) => (jsx("div", { className: styles$6["scheduler-hours"], children: jsx("span", { children: element }) }, classification + element + topDivisions))) })] }));
|
|
9170
9170
|
};
|
|
9171
9171
|
|
|
9172
9172
|
const HeaderTitle = ({ title, dividerText, width }) => {
|
|
9173
|
-
return (jsxs("div", { className: styles$
|
|
9173
|
+
return (jsxs("div", { className: styles$6["scheduler-header-title"], style: { width: width }, children: [jsx("div", { className: styles$6["title-cell"], children: jsx("span", { className: styles$6.title, children: title }) }), jsx("div", { children: jsx("span", { children: dividerText }) })] }));
|
|
9174
9174
|
};
|
|
9175
9175
|
|
|
9176
9176
|
const SchedulerHeader = ({ content, divisions, visibleDays, selectedDate, width, onEvent, timeLinePercentage, isUtc }) => {
|
|
9177
9177
|
const topWidth = (100 / visibleDays);
|
|
9178
9178
|
const { title, classification } = content;
|
|
9179
|
-
return (jsxs("div", { className: styles$
|
|
9179
|
+
return (jsxs("div", { className: styles$6["scheduler-header"], children: [jsx(HeaderTitle, { title: title, dividerText: classification, width: width }), jsxs("div", { className: styles$6["scheduler-header-divisions"], style: { flex: 1, display: "flex", position: "relative" }, onMouseDown: (e) => {
|
|
9180
9180
|
e.preventDefault();
|
|
9181
9181
|
const clickPercentage = getPercentageFromMouseEvent(e);
|
|
9182
9182
|
const date = dateFromPercentage(selectedDate, visibleDays, clickPercentage);
|
|
@@ -9208,7 +9208,7 @@ function getStartEnd(day) {
|
|
|
9208
9208
|
};
|
|
9209
9209
|
}
|
|
9210
9210
|
|
|
9211
|
-
var styles$
|
|
9211
|
+
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"};
|
|
9212
9212
|
|
|
9213
9213
|
const BackgroundEvent = ({ value, heightRem }) => {
|
|
9214
9214
|
const backColor = getHSLColor(value.color, value.alpha ?? 0.5);
|
|
@@ -9235,13 +9235,13 @@ const BackgroundEvent = ({ value, heightRem }) => {
|
|
|
9235
9235
|
padding: "0.25rem",
|
|
9236
9236
|
color: textColor,
|
|
9237
9237
|
alignItems: value.contentAlign ?? "center"
|
|
9238
|
-
}, className: styles$
|
|
9238
|
+
}, className: styles$5["scheduler-event-container"], children: [value.icons ? jsx("div", { style: {
|
|
9239
9239
|
display: "flex",
|
|
9240
9240
|
flexDirection: "row",
|
|
9241
9241
|
alignItems: "center",
|
|
9242
9242
|
whiteSpace: "nowrap",
|
|
9243
9243
|
color: textColor,
|
|
9244
|
-
}, children: value.icons }) : null, jsx("span", { className: styles$
|
|
9244
|
+
}, children: value.icons }) : null, jsx("span", { className: styles$5["scheduler-event-text"], children: value.name })] }) }) }) }) })) : null;
|
|
9245
9245
|
};
|
|
9246
9246
|
|
|
9247
9247
|
const eventIsVisible = (startDate, endDate, selectedDate, visibleDays) => {
|
|
@@ -9403,7 +9403,7 @@ const WeekEndLine = ({ left, top = "0px", height, widthPercentage }) => {
|
|
|
9403
9403
|
} }));
|
|
9404
9404
|
};
|
|
9405
9405
|
|
|
9406
|
-
var styles$
|
|
9406
|
+
var styles$4 = {"scheduler-row-header":"scheduler-row-module__scheduler-row-header__S-iv4"};
|
|
9407
9407
|
|
|
9408
9408
|
const DefaultRowHeader = ({ value, width, onEvent }) => {
|
|
9409
9409
|
return (jsxs("div", { style: {
|
|
@@ -9412,7 +9412,7 @@ const DefaultRowHeader = ({ value, width, onEvent }) => {
|
|
|
9412
9412
|
background: value.highlightColor,
|
|
9413
9413
|
}, onClick: (_) => {
|
|
9414
9414
|
onEvent(new OnClickRowHeader(value.rowId));
|
|
9415
|
-
}, className: styles$
|
|
9415
|
+
}, className: styles$4["scheduler-row-header"], children: [jsxs("div", { children: [jsx("strong", { children: value.title }), value.title2 && jsxs("span", { children: ["-", value.title2] }), " ", jsx("strong", { children: value.subtitle })] }), value.subtitle && jsxs("div", { style: { opacity: 0.5 }, children: [value.title3 && jsxs("span", { className: styles$4["scheduler-crewmember-functions"], children: ["(", value.title3, ")"] }), value.subtitle2 && jsxs("span", { children: ["-", value.subtitle2] })] })] }));
|
|
9416
9416
|
};
|
|
9417
9417
|
|
|
9418
9418
|
const EventSideDrag = ({ left = false, heightRem, onStartEvent }) => {
|
|
@@ -9502,7 +9502,7 @@ const SchedulerEvent = ({ value, heightRem, onEvent, maxEventHeightRem }) => {
|
|
|
9502
9502
|
height: `calc(${effectiveHeight}rem - 4px)`,
|
|
9503
9503
|
opacity: eventTransparent ? 0.5 : 1,
|
|
9504
9504
|
...value.style,
|
|
9505
|
-
}, className: styles$
|
|
9505
|
+
}, className: styles$5["scheduler-event"], "data-underlay": value.underlay ? true : undefined, children: [value.underlay && jsx("div", { className: styles$5["underlay"], children: value.underlay }), jsxs("div", { style: {
|
|
9506
9506
|
display: "flex",
|
|
9507
9507
|
flexDirection: "row",
|
|
9508
9508
|
alignItems: "stretch",
|
|
@@ -9513,13 +9513,13 @@ const SchedulerEvent = ({ value, heightRem, onEvent, maxEventHeightRem }) => {
|
|
|
9513
9513
|
height: "100%",
|
|
9514
9514
|
}, children: [value.isResizable && (jsx(EventSideDrag, { left: true, heightRem: effectiveHeight * 0.7, onStartEvent: () => {
|
|
9515
9515
|
onEvent(new OnLeftDragStart(value.id));
|
|
9516
|
-
} })), jsxs("div", { className: styles$
|
|
9516
|
+
} })), jsxs("div", { className: styles$5["scheduler-event-container"], children: [jsx("div", { style: {
|
|
9517
9517
|
display: "flex",
|
|
9518
9518
|
flexDirection: "row",
|
|
9519
9519
|
alignItems: "center",
|
|
9520
9520
|
whiteSpace: "nowrap",
|
|
9521
9521
|
color: textColor,
|
|
9522
|
-
}, children: value.icons }), jsx("span", { className: value.underlay ? styles$
|
|
9522
|
+
}, children: value.icons }), jsx("span", { className: value.underlay ? styles$5["label-halo"] : undefined, style: {
|
|
9523
9523
|
padding: "0.1rem",
|
|
9524
9524
|
color: textColor,
|
|
9525
9525
|
display: "flex",
|
|
@@ -9527,13 +9527,13 @@ const SchedulerEvent = ({ value, heightRem, onEvent, maxEventHeightRem }) => {
|
|
|
9527
9527
|
alignItems: "center",
|
|
9528
9528
|
width: "100%",
|
|
9529
9529
|
...(value.underlay && { "--label-halo-color": backColor }),
|
|
9530
|
-
}, children: value.rectangleColors && value.rectangleColors.length > 0 ? (jsx("div", { className: styles$
|
|
9530
|
+
}, children: value.rectangleColors && value.rectangleColors.length > 0 ? (jsx("div", { className: styles$5["color-bars"], children: value.rectangleColors.map((item, i) => (jsx("div", { style: { backgroundColor: item } }, value.id + "_" + i))) })) : (value.label ?? value.name) })] }), value.isResizable && (jsx(EventSideDrag, { heightRem: effectiveHeight * 0.7, onStartEvent: () => {
|
|
9531
9531
|
onEvent(new OnRightDragStart(value.id));
|
|
9532
|
-
} }))] }), value.primaryTimeMarkerColor && (jsx("div", { className: styles$
|
|
9532
|
+
} }))] }), value.primaryTimeMarkerColor && (jsx("div", { className: styles$5["time-marker"], style: {
|
|
9533
9533
|
backgroundColor: value.primaryTimeMarkerColor,
|
|
9534
|
-
} })), value.secondaryTimeMarkerColor && (jsx("div", { className: styles$
|
|
9534
|
+
} })), value.secondaryTimeMarkerColor && (jsx("div", { className: styles$5["time-marker"], style: {
|
|
9535
9535
|
backgroundColor: value.secondaryTimeMarkerColor,
|
|
9536
|
-
} }))] }) }) }, value.id) }), value.cornerMarker && (jsx("div", { className: styles$
|
|
9536
|
+
} }))] }) }) }, value.id) }), value.cornerMarker && (jsx("div", { className: styles$5["corner-marker"], style: {
|
|
9537
9537
|
top: `${topOffsetPx}px`,
|
|
9538
9538
|
left: `${value.left}%`,
|
|
9539
9539
|
width: `${value.width}%`,
|
|
@@ -10067,13 +10067,13 @@ const Scheduler = (props) => {
|
|
|
10067
10067
|
position: "sticky",
|
|
10068
10068
|
top: 0,
|
|
10069
10069
|
zIndex: 1,
|
|
10070
|
-
}, children: jsx(SchedulerHeader, { ...header }) })), jsx(VariableSizeList, { height: schedulerContentHeight * 16, itemCount: rows.length, itemSize: getItemSize, itemData: itemData, width: "100%", style: { overflowX: "hidden" }, ref: instanceRef, className: styles$
|
|
10070
|
+
}, children: jsx(SchedulerHeader, { ...header }) })), jsx(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 })] }) }));
|
|
10071
10071
|
};
|
|
10072
10072
|
|
|
10073
10073
|
class UiEvent {
|
|
10074
10074
|
}
|
|
10075
10075
|
|
|
10076
|
-
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"};
|
|
10076
|
+
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"};
|
|
10077
10077
|
|
|
10078
10078
|
class OnPinRow {
|
|
10079
10079
|
id;
|
|
@@ -10101,15 +10101,15 @@ const PinRowHeader = ({ value, width, onEvent }) => {
|
|
|
10101
10101
|
flexShrink: 0,
|
|
10102
10102
|
height: "100%",
|
|
10103
10103
|
background: highlightColor
|
|
10104
|
-
}, className: styles["super-scheduler-row-header"], children: [jsx("button", { className: "cw-button-icon cwi-pin", "data-pinned": value.isPinned, onClick: (_) => {
|
|
10104
|
+
}, className: styles$3["super-scheduler-row-header"], children: [jsx("button", { className: "cw-button-icon cwi-pin", "data-pinned": value.isPinned, onClick: (_) => {
|
|
10105
10105
|
onEvent(value.isPinned ? new OnUnpinRow(value.rowId) : new OnPinRow(value.rowId));
|
|
10106
|
-
} }), jsx("div", { className: styles["indicators"], children: value.indicators ?? undefined }), jsxs("div", { style: {
|
|
10106
|
+
} }), jsx("div", { className: styles$3["indicators"], children: value.indicators ?? undefined }), jsxs("div", { style: {
|
|
10107
10107
|
display: "flex",
|
|
10108
10108
|
flexDirection: "column",
|
|
10109
10109
|
justifyContent: "center",
|
|
10110
10110
|
alignItems: "flex-start",
|
|
10111
10111
|
}, children: [jsxs("div", { children: [jsx("strong", { children: value.title }), value.title2 && jsxs("span", { children: ["-", value.title2] }), " ", jsx("strong", { children: value.subtitle })] }), value.subtitle && jsxs("div", { style: { opacity: 0.5 }, children: [value.title3 &&
|
|
10112
|
-
jsxs("span", { className: styles["scheduler-crewmember-functions"], children: ["(", value.title3, ")"] }), value.subtitle2 && jsxs("span", { children: ["-", value.subtitle2] })] })] }), isLoading ? jsx("span", { className: "cwi-icons cwi-spinner" }) : undefined] }) }, value.rowId) }, value.rowId);
|
|
10112
|
+
jsxs("span", { className: styles$3["scheduler-crewmember-functions"], children: ["(", value.title3, ")"] }), value.subtitle2 && jsxs("span", { children: ["-", value.subtitle2] })] })] }), isLoading ? jsx("span", { className: "cwi-icons cwi-spinner" }) : undefined] }) }, value.rowId) }, value.rowId);
|
|
10113
10113
|
};
|
|
10114
10114
|
|
|
10115
10115
|
const SuperScheduler = ({ id, state, header, rows, events, headerColumnComp, pinnedOrderCategory, unPinnedOrderCategory, backgroundEvents, indicatorRows = [], contextMenuItems, onEvent, rowHeightRem = 1.75, rowHeaderWidth, maxEventHeightRem, EventComp = SchedulerEvent, }) => {
|
|
@@ -10121,6 +10121,2324 @@ const SuperScheduler = ({ id, state, header, rows, events, headerColumnComp, pin
|
|
|
10121
10121
|
}, children: "Clear pinned" }) })] })), 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 })] }));
|
|
10122
10122
|
};
|
|
10123
10123
|
|
|
10124
|
+
/** A toggle ref hides its item while `InitiallyHidden` XOR "flipped" is true. */
|
|
10125
|
+
function isToggleRefHidden(ref, flipped) {
|
|
10126
|
+
return ref.InitiallyHidden !== flipped.has(ref.Id);
|
|
10127
|
+
}
|
|
10128
|
+
/** An item, row or cell is visible only when every toggle it depends on is in its visible state. */
|
|
10129
|
+
function isRdlVisible(toggles, flipped) {
|
|
10130
|
+
if (!toggles || toggles.length === 0)
|
|
10131
|
+
return true;
|
|
10132
|
+
return toggles.every(ref => !isToggleRefHidden(ref, flipped));
|
|
10133
|
+
}
|
|
10134
|
+
/** The +/- state of a toggle textbox: the initial state inverted when its id was flipped. */
|
|
10135
|
+
function isToggleExpanded(textbox, flipped) {
|
|
10136
|
+
const initial = textbox.ToggleExpanded ?? false;
|
|
10137
|
+
return textbox.ToggleId ? initial !== flipped.has(textbox.ToggleId) : initial;
|
|
10138
|
+
}
|
|
10139
|
+
/**
|
|
10140
|
+
* The toggle ids an export or print sends so the file shows the drilldown state of the screen: the
|
|
10141
|
+
* ids flipped relative to the render's initial state (a toggle clicked twice is back to its initial
|
|
10142
|
+
* state and not sent), sorted for a stable request.
|
|
10143
|
+
*/
|
|
10144
|
+
function flippedToggleIds(flipped) {
|
|
10145
|
+
return [...flipped].sort();
|
|
10146
|
+
}
|
|
10147
|
+
/** The toggle ids to ask the server for after a click on toggle `id`: the flipped ids with `id` flipped once more. */
|
|
10148
|
+
function toggledIds(flipped, id) {
|
|
10149
|
+
return flippedToggleIds(flipToggle(flipped, id));
|
|
10150
|
+
}
|
|
10151
|
+
/** Returns a new set with `id` flipped (added when absent, removed when present). */
|
|
10152
|
+
function flipToggle(flipped, id) {
|
|
10153
|
+
const next = new Set(flipped);
|
|
10154
|
+
if (next.has(id)) {
|
|
10155
|
+
next.delete(id);
|
|
10156
|
+
}
|
|
10157
|
+
else {
|
|
10158
|
+
next.add(id);
|
|
10159
|
+
}
|
|
10160
|
+
return next;
|
|
10161
|
+
}
|
|
10162
|
+
|
|
10163
|
+
/** Number of visible rows among `span` rows starting at `start`, at least 1. */
|
|
10164
|
+
function visibleRowSpan(rowVisible, start, span) {
|
|
10165
|
+
const end = Math.min(rowVisible.length, start + Math.max(1, span));
|
|
10166
|
+
let count = 0;
|
|
10167
|
+
for (let i = start; i < end; i++) {
|
|
10168
|
+
if (rowVisible[i])
|
|
10169
|
+
count++;
|
|
10170
|
+
}
|
|
10171
|
+
return Math.max(1, count);
|
|
10172
|
+
}
|
|
10173
|
+
function sum(values, from, to) {
|
|
10174
|
+
let total = 0;
|
|
10175
|
+
for (let i = Math.max(0, from); i < Math.min(values.length, to); i++)
|
|
10176
|
+
total += values[i];
|
|
10177
|
+
return total;
|
|
10178
|
+
}
|
|
10179
|
+
function visibleHeight(rows, rowVisible, start, span) {
|
|
10180
|
+
const end = Math.min(rows.length, start + Math.max(1, span));
|
|
10181
|
+
let height = 0;
|
|
10182
|
+
for (let i = start; i < end; i++) {
|
|
10183
|
+
if (rowVisible[i])
|
|
10184
|
+
height += rows[i].Height;
|
|
10185
|
+
}
|
|
10186
|
+
return height;
|
|
10187
|
+
}
|
|
10188
|
+
/**
|
|
10189
|
+
* Lays a table out for the current toggle state: hidden rows take no space, cells are placed at
|
|
10190
|
+
* their column with ColSpan/RowSpan and a header cell's RowSpan is recounted over the visible rows.
|
|
10191
|
+
* Cells that start in a hidden row are not drawn.
|
|
10192
|
+
*/
|
|
10193
|
+
function layoutRdlTable(table, flipped) {
|
|
10194
|
+
const rows = table.Rows;
|
|
10195
|
+
const widths = table.ColumnWidths;
|
|
10196
|
+
const rowVisible = rows.map(row => isRdlVisible(row.Toggles, flipped));
|
|
10197
|
+
const rowTops = [];
|
|
10198
|
+
let top = 0;
|
|
10199
|
+
rows.forEach((row, index) => {
|
|
10200
|
+
rowTops.push(top);
|
|
10201
|
+
if (rowVisible[index])
|
|
10202
|
+
top += row.Height;
|
|
10203
|
+
});
|
|
10204
|
+
const cells = [];
|
|
10205
|
+
rows.forEach((row, rowIndex) => {
|
|
10206
|
+
if (!rowVisible[rowIndex])
|
|
10207
|
+
return;
|
|
10208
|
+
row.Cells.forEach((cell, cellIndex) => {
|
|
10209
|
+
const colSpan = Math.max(1, cell.ColSpan);
|
|
10210
|
+
cells.push({
|
|
10211
|
+
cell,
|
|
10212
|
+
rowIndex,
|
|
10213
|
+
cellIndex,
|
|
10214
|
+
left: sum(widths, 0, cell.Column),
|
|
10215
|
+
top: rowTops[rowIndex],
|
|
10216
|
+
width: sum(widths, cell.Column, cell.Column + colSpan),
|
|
10217
|
+
height: visibleHeight(rows, rowVisible, rowIndex, cell.RowSpan),
|
|
10218
|
+
rowSpan: visibleRowSpan(rowVisible, rowIndex, cell.RowSpan),
|
|
10219
|
+
hidden: !isRdlVisible(cell.Toggles, flipped)
|
|
10220
|
+
});
|
|
10221
|
+
});
|
|
10222
|
+
});
|
|
10223
|
+
return {
|
|
10224
|
+
width: sum(widths, 0, widths.length),
|
|
10225
|
+
height: top,
|
|
10226
|
+
rowTops,
|
|
10227
|
+
rowVisible,
|
|
10228
|
+
cells
|
|
10229
|
+
};
|
|
10230
|
+
}
|
|
10231
|
+
|
|
10232
|
+
/*
|
|
10233
|
+
* Find over the text of one page. Every run gets a key from its position in the render tree; the
|
|
10234
|
+
* renderer builds the same keys, so a match can be highlighted where the run is drawn.
|
|
10235
|
+
*/
|
|
10236
|
+
/** Key prefixes of the three page sections. */
|
|
10237
|
+
const RDL_SECTION_KEYS = { header: "h", body: "b", footer: "f" };
|
|
10238
|
+
function rdlItemKey(parentKey, index) {
|
|
10239
|
+
return `${parentKey}.${index}`;
|
|
10240
|
+
}
|
|
10241
|
+
function rdlCellKey(tableKey, rowIndex, cellIndex) {
|
|
10242
|
+
return `${tableKey}.r${rowIndex}c${cellIndex}`;
|
|
10243
|
+
}
|
|
10244
|
+
function rdlRunKey(textboxKey, paragraphIndex, runIndex) {
|
|
10245
|
+
return `${textboxKey}.p${paragraphIndex}r${runIndex}`;
|
|
10246
|
+
}
|
|
10247
|
+
function collectTextbox(textbox, key, out) {
|
|
10248
|
+
textbox.Paragraphs.forEach((paragraph, p) => {
|
|
10249
|
+
paragraph.Runs.forEach((run, r) => {
|
|
10250
|
+
if (run.Text)
|
|
10251
|
+
out.push({ key: rdlRunKey(key, p, r), text: run.Text });
|
|
10252
|
+
});
|
|
10253
|
+
});
|
|
10254
|
+
}
|
|
10255
|
+
function collectItem(item, key, flipped, out) {
|
|
10256
|
+
if (!isRdlVisible(item.Toggles, flipped))
|
|
10257
|
+
return;
|
|
10258
|
+
switch (item.Kind) {
|
|
10259
|
+
case "textbox":
|
|
10260
|
+
collectTextbox(item, key, out);
|
|
10261
|
+
break;
|
|
10262
|
+
case "rectangle":
|
|
10263
|
+
collectItems(item.Items, key, flipped, out);
|
|
10264
|
+
break;
|
|
10265
|
+
case "table": {
|
|
10266
|
+
const layout = layoutRdlTable(item, flipped);
|
|
10267
|
+
for (const box of layout.cells) {
|
|
10268
|
+
if (box.hidden || !box.cell.Content)
|
|
10269
|
+
continue;
|
|
10270
|
+
collectItem(box.cell.Content, rdlCellKey(key, box.rowIndex, box.cellIndex), flipped, out);
|
|
10271
|
+
}
|
|
10272
|
+
break;
|
|
10273
|
+
}
|
|
10274
|
+
}
|
|
10275
|
+
}
|
|
10276
|
+
function collectItems(items, parentKey, flipped, out) {
|
|
10277
|
+
items.forEach((item, index) => collectItem(item, rdlItemKey(parentKey, index), flipped, out));
|
|
10278
|
+
}
|
|
10279
|
+
/** Every visible text run of a page in document order: header, body, footer. */
|
|
10280
|
+
function collectPageText(page, flipped) {
|
|
10281
|
+
const out = [];
|
|
10282
|
+
if (page.Header)
|
|
10283
|
+
collectItems(page.Header.Items, RDL_SECTION_KEYS.header, flipped, out);
|
|
10284
|
+
collectItems(page.Items, RDL_SECTION_KEYS.body, flipped, out);
|
|
10285
|
+
if (page.Footer)
|
|
10286
|
+
collectItems(page.Footer.Items, RDL_SECTION_KEYS.footer, flipped, out);
|
|
10287
|
+
return out;
|
|
10288
|
+
}
|
|
10289
|
+
/**
|
|
10290
|
+
* The text lower-cased one character at a time, with each code unit of the result pointing back to the
|
|
10291
|
+
* original character it came from: lower-casing can change the length ("İ" becomes two code units outside
|
|
10292
|
+
* Turkish), and the highlights slice the original text.
|
|
10293
|
+
*/
|
|
10294
|
+
function lowerWithOffsets(text, locale) {
|
|
10295
|
+
let lower = "";
|
|
10296
|
+
const starts = [];
|
|
10297
|
+
const ends = [];
|
|
10298
|
+
let index = 0;
|
|
10299
|
+
for (const character of text) {
|
|
10300
|
+
const lowered = character.toLocaleLowerCase(locale);
|
|
10301
|
+
for (let unit = 0; unit < lowered.length; unit++) {
|
|
10302
|
+
starts.push(index);
|
|
10303
|
+
ends.push(index + character.length);
|
|
10304
|
+
}
|
|
10305
|
+
lower += lowered;
|
|
10306
|
+
index += character.length;
|
|
10307
|
+
}
|
|
10308
|
+
return { lower, starts, ends };
|
|
10309
|
+
}
|
|
10310
|
+
/**
|
|
10311
|
+
* Case-insensitive, non-overlapping matches of `query` in the runs, in run order, with the casing rules of
|
|
10312
|
+
* `locale` (default: the browser's, so "istanbul" finds "İSTANBUL" in Turkish). A match does not span two runs.
|
|
10313
|
+
*/
|
|
10314
|
+
function findInRuns(runs, query, locale) {
|
|
10315
|
+
const needle = query.trim().toLocaleLowerCase(locale);
|
|
10316
|
+
if (needle === "")
|
|
10317
|
+
return [];
|
|
10318
|
+
const matches = [];
|
|
10319
|
+
for (const run of runs) {
|
|
10320
|
+
const { lower, starts, ends } = lowerWithOffsets(run.text, locale);
|
|
10321
|
+
let from = 0;
|
|
10322
|
+
for (;;) {
|
|
10323
|
+
const at = lower.indexOf(needle, from);
|
|
10324
|
+
if (at < 0)
|
|
10325
|
+
break;
|
|
10326
|
+
const start = starts[at];
|
|
10327
|
+
matches.push({ key: run.key, start, length: ends[at + needle.length - 1] - start });
|
|
10328
|
+
from = at + needle.length;
|
|
10329
|
+
}
|
|
10330
|
+
}
|
|
10331
|
+
return matches;
|
|
10332
|
+
}
|
|
10333
|
+
/** Find over the visible text of one page. */
|
|
10334
|
+
function findInPage(page, query, flipped) {
|
|
10335
|
+
return findInRuns(collectPageText(page, flipped), query);
|
|
10336
|
+
}
|
|
10337
|
+
/**
|
|
10338
|
+
* The active match index: -1 without matches; the stored index clamped into the matches when it belongs to the
|
|
10339
|
+
* current page and query (a stored -1 from an empty search becomes 0 once a drilldown reveals matches); else 0.
|
|
10340
|
+
*/
|
|
10341
|
+
function activeMatchIndex(storedIndex, storedForCurrentSearch, count) {
|
|
10342
|
+
if (count <= 0)
|
|
10343
|
+
return -1;
|
|
10344
|
+
return storedForCurrentSearch ? Math.min(Math.max(0, storedIndex), count - 1) : 0;
|
|
10345
|
+
}
|
|
10346
|
+
/** The next match index after `current`, cycling; -1 when there are no matches. */
|
|
10347
|
+
function nextMatchIndex(current, count) {
|
|
10348
|
+
if (count <= 0)
|
|
10349
|
+
return -1;
|
|
10350
|
+
return current < 0 ? 0 : (current + 1) % count;
|
|
10351
|
+
}
|
|
10352
|
+
|
|
10353
|
+
/** CSS pixels per point: 96 px per inch, 72 pt per inch. */
|
|
10354
|
+
const PX_PER_PT = 96 / 72;
|
|
10355
|
+
/** Converts points to CSS pixels at a zoom factor (1 = 100%). */
|
|
10356
|
+
function ptToPx(pt, zoom = 1) {
|
|
10357
|
+
return pt * PX_PER_PT * zoom;
|
|
10358
|
+
}
|
|
10359
|
+
const POINTS_PER_UNIT = {
|
|
10360
|
+
pt: 1,
|
|
10361
|
+
px: 72 / 96,
|
|
10362
|
+
in: 72,
|
|
10363
|
+
cm: 72 / 2.54,
|
|
10364
|
+
mm: 72 / 25.4,
|
|
10365
|
+
pc: 12
|
|
10366
|
+
};
|
|
10367
|
+
// Linear: one digit run either side of the point, so a long non-matching value cannot backtrack quadratically.
|
|
10368
|
+
const SIZE_PATTERN = /^\s*(-?(?:\d+(?:\.\d*)?|\.\d+))\s*(pt|px|in|cm|mm|pc)?\s*$/i;
|
|
10369
|
+
/** No real RDL size comes near this; longer report-supplied strings are rejected before matching. */
|
|
10370
|
+
const MAX_SIZE_LENGTH = 32;
|
|
10371
|
+
/** Parses an RDL size ("10pt", "2.5mm", "1in", ...) into points; a bare number is points. Null when unparsable. */
|
|
10372
|
+
function parseRdlSize(value) {
|
|
10373
|
+
if (!value || value.length > MAX_SIZE_LENGTH)
|
|
10374
|
+
return null;
|
|
10375
|
+
const match = SIZE_PATTERN.exec(value);
|
|
10376
|
+
if (!match)
|
|
10377
|
+
return null;
|
|
10378
|
+
const unit = (match[2] ?? "pt").toLowerCase();
|
|
10379
|
+
return parseFloat(match[1]) * POINTS_PER_UNIT[unit];
|
|
10380
|
+
}
|
|
10381
|
+
/** An RDL size as CSS pixels at a zoom factor, or undefined when the size is missing or unparsable. */
|
|
10382
|
+
function rdlSizeToPx(value, zoom) {
|
|
10383
|
+
const pt = parseRdlSize(value);
|
|
10384
|
+
return pt === null ? undefined : ptToPx(pt, zoom);
|
|
10385
|
+
}
|
|
10386
|
+
|
|
10387
|
+
/** The zoom steps of the toolbar, 50% to 200%. */
|
|
10388
|
+
const REPORT_ZOOM_STEPS = [0.5, 0.75, 1, 1.25, 1.5, 2];
|
|
10389
|
+
const MIN_ZOOM = 0.1;
|
|
10390
|
+
const MAX_ZOOM = 4;
|
|
10391
|
+
/** Default margins of a standalone CwReportPage: a quarter inch, so content never touches the paper edge. */
|
|
10392
|
+
const DEFAULT_PAGE_MARGIN_PT = 18;
|
|
10393
|
+
/**
|
|
10394
|
+
* Margins of the sheet in the viewer: none. Like the old ReportViewer, the screen shows the content
|
|
10395
|
+
* (header, body, footer) without the RDL page margins; PDF and print come from the server with them.
|
|
10396
|
+
*/
|
|
10397
|
+
const SCREEN_PAGE_MARGINS = { top: 0, right: 0, bottom: 0, left: 0 };
|
|
10398
|
+
/** 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. */
|
|
10399
|
+
function pageContentWidthPt(page, bodyWidth) {
|
|
10400
|
+
return page.Width && Number.isFinite(page.Width) && page.Width > 0 ? Math.max(page.Width, bodyWidth) : bodyWidth;
|
|
10401
|
+
}
|
|
10402
|
+
/** Size of the drawn sheet in points: margins + header + body + footer, as wide as the content so every item lies on it. */
|
|
10403
|
+
function pageSheetSizePt(page, bodyWidth, margins) {
|
|
10404
|
+
return {
|
|
10405
|
+
width: margins.left + pageContentWidthPt(page, bodyWidth) + margins.right,
|
|
10406
|
+
height: margins.top + (page.Header?.Height ?? 0) + page.Height + (page.Footer?.Height ?? 0) + margins.bottom
|
|
10407
|
+
};
|
|
10408
|
+
}
|
|
10409
|
+
/**
|
|
10410
|
+
* The zoom factor to draw with. "pageWidth" fits the sheet width into the available width,
|
|
10411
|
+
* "wholePage" fits the whole sheet; available sizes are CSS pixels. Clamped to 10%..400%.
|
|
10412
|
+
*/
|
|
10413
|
+
function resolveZoomFactor(zoom, sheetPt, available) {
|
|
10414
|
+
if (typeof zoom === "number")
|
|
10415
|
+
return clamp(zoom);
|
|
10416
|
+
const widthPx = ptToPx(sheetPt.width);
|
|
10417
|
+
const heightPx = ptToPx(sheetPt.height);
|
|
10418
|
+
if (widthPx <= 0 || available.width <= 0)
|
|
10419
|
+
return 1;
|
|
10420
|
+
const byWidth = available.width / widthPx;
|
|
10421
|
+
if (zoom === "pageWidth" || heightPx <= 0 || available.height <= 0)
|
|
10422
|
+
return clamp(byWidth);
|
|
10423
|
+
return clamp(Math.min(byWidth, available.height / heightPx));
|
|
10424
|
+
}
|
|
10425
|
+
function clamp(value) {
|
|
10426
|
+
return Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, value));
|
|
10427
|
+
}
|
|
10428
|
+
|
|
10429
|
+
/*
|
|
10430
|
+
* Maps the RDL style vocabulary to CSS. Everything here is report CONTENT: colors, fonts and borders
|
|
10431
|
+
* come from the report definition and are applied as given, not from the design system.
|
|
10432
|
+
*/
|
|
10433
|
+
const RDL_DEFAULT_FONT_FAMILY = "Arial";
|
|
10434
|
+
const RDL_DEFAULT_FONT_SIZE = "10pt";
|
|
10435
|
+
const RDL_DEFAULT_COLOR = "black";
|
|
10436
|
+
const FONT_WEIGHTS = {
|
|
10437
|
+
thin: 100,
|
|
10438
|
+
extralight: 200,
|
|
10439
|
+
light: 300,
|
|
10440
|
+
normal: 400,
|
|
10441
|
+
medium: 500,
|
|
10442
|
+
semibold: 600,
|
|
10443
|
+
bold: 700,
|
|
10444
|
+
extrabold: 800,
|
|
10445
|
+
heavy: 900
|
|
10446
|
+
};
|
|
10447
|
+
const BORDER_STYLES = {
|
|
10448
|
+
solid: "solid",
|
|
10449
|
+
dotted: "dotted",
|
|
10450
|
+
dashed: "dashed",
|
|
10451
|
+
double: "double",
|
|
10452
|
+
none: "none"
|
|
10453
|
+
};
|
|
10454
|
+
/** An RDL color as CSS: "Transparent" = transparent, names and #RRGGBB pass through, empty = undefined. */
|
|
10455
|
+
function rdlColor(value) {
|
|
10456
|
+
if (!value)
|
|
10457
|
+
return undefined;
|
|
10458
|
+
const trimmed = value.trim();
|
|
10459
|
+
if (trimmed === "")
|
|
10460
|
+
return undefined;
|
|
10461
|
+
return trimmed.toLowerCase() === "transparent" ? "transparent" : trimmed;
|
|
10462
|
+
}
|
|
10463
|
+
/** Thin..Heavy to 100..900; unknown values fall back to normal. */
|
|
10464
|
+
function rdlFontWeight(value) {
|
|
10465
|
+
if (!value)
|
|
10466
|
+
return 400;
|
|
10467
|
+
return FONT_WEIGHTS[value.toLowerCase()] ?? (/^\d{3}$/.test(value) ? parseInt(value, 10) : 400);
|
|
10468
|
+
}
|
|
10469
|
+
/** A border side as a CSS shorthand; Style None or missing = none. Width defaults to 1pt, color to black. */
|
|
10470
|
+
function rdlBorder(border, zoom) {
|
|
10471
|
+
if (!border)
|
|
10472
|
+
return undefined;
|
|
10473
|
+
const style = BORDER_STYLES[(border.Style ?? "none").toLowerCase()] ?? "solid";
|
|
10474
|
+
if (style === "none")
|
|
10475
|
+
return "none";
|
|
10476
|
+
const widthPt = parseRdlSize(border.Width) ?? 1;
|
|
10477
|
+
const widthPx = Math.max(1, ptToPx(widthPt, zoom));
|
|
10478
|
+
return `${widthPx}px ${style} ${rdlColor(border.Color) ?? RDL_DEFAULT_COLOR}`;
|
|
10479
|
+
}
|
|
10480
|
+
/**
|
|
10481
|
+
* The four borders of an item box as the custom properties its border overlay draws
|
|
10482
|
+
* (`--rdl-border-top` ... in cw-report-page.module.css). Like the report server, borders sit on
|
|
10483
|
+
* the box edge and take no space from the content, so text laid out by the server to just fit
|
|
10484
|
+
* still fits. Always all four sides, "none" when unset: custom properties inherit, and a nested
|
|
10485
|
+
* item must not draw its container's borders.
|
|
10486
|
+
*/
|
|
10487
|
+
function rdlBorderVars(style, zoom) {
|
|
10488
|
+
return {
|
|
10489
|
+
"--rdl-border-top": rdlBorder(style?.BorderTop, zoom) ?? "none",
|
|
10490
|
+
"--rdl-border-right": rdlBorder(style?.BorderRight, zoom) ?? "none",
|
|
10491
|
+
"--rdl-border-bottom": rdlBorder(style?.BorderBottom, zoom) ?? "none",
|
|
10492
|
+
"--rdl-border-left": rdlBorder(style?.BorderLeft, zoom) ?? "none"
|
|
10493
|
+
};
|
|
10494
|
+
}
|
|
10495
|
+
/** Background, borders (as overlay custom properties, see `rdlBorderVars`) and padding of an item box. */
|
|
10496
|
+
function rdlBoxStyle(style, zoom) {
|
|
10497
|
+
if (!style)
|
|
10498
|
+
return rdlBorderVars(null, zoom);
|
|
10499
|
+
return {
|
|
10500
|
+
...rdlBorderVars(style, zoom),
|
|
10501
|
+
backgroundColor: rdlColor(style.BackgroundColor),
|
|
10502
|
+
paddingTop: rdlSizeToPx(style.PaddingTop, zoom),
|
|
10503
|
+
paddingRight: rdlSizeToPx(style.PaddingRight, zoom),
|
|
10504
|
+
paddingBottom: rdlSizeToPx(style.PaddingBottom, zoom),
|
|
10505
|
+
paddingLeft: rdlSizeToPx(style.PaddingLeft, zoom)
|
|
10506
|
+
};
|
|
10507
|
+
}
|
|
10508
|
+
function pick(key, styles) {
|
|
10509
|
+
for (const style of styles) {
|
|
10510
|
+
const value = style?.[key];
|
|
10511
|
+
if (value)
|
|
10512
|
+
return value;
|
|
10513
|
+
}
|
|
10514
|
+
return null;
|
|
10515
|
+
}
|
|
10516
|
+
function resolveRdlTextStyle(run, paragraph, textbox) {
|
|
10517
|
+
const chain = [run, paragraph, textbox];
|
|
10518
|
+
return {
|
|
10519
|
+
color: pick("Color", chain) ?? RDL_DEFAULT_COLOR,
|
|
10520
|
+
fontFamily: pick("FontFamily", chain) ?? RDL_DEFAULT_FONT_FAMILY,
|
|
10521
|
+
fontSize: pick("FontSize", chain) ?? RDL_DEFAULT_FONT_SIZE,
|
|
10522
|
+
fontStyle: pick("FontStyle", chain),
|
|
10523
|
+
fontWeight: pick("FontWeight", chain),
|
|
10524
|
+
textDecoration: pick("TextDecoration", chain),
|
|
10525
|
+
lineHeight: pick("LineHeight", chain)
|
|
10526
|
+
};
|
|
10527
|
+
}
|
|
10528
|
+
/**
|
|
10529
|
+
* The text style of a paragraph element. Its font sets the strut every line box gets, so it takes
|
|
10530
|
+
* the font of its runs (they usually share one; when they differ, the first run's): a paragraph
|
|
10531
|
+
* left at the textbox default (Arial 10pt) around Segoe UI 8pt runs would make every line taller
|
|
10532
|
+
* than the server measured and the text would overflow its box. An RDL LineHeight on the paragraph
|
|
10533
|
+
* or textbox still wins; otherwise the run's LineHeight, if any, applies.
|
|
10534
|
+
*/
|
|
10535
|
+
function resolveParagraphTextStyle(runStyles, paragraph, textbox) {
|
|
10536
|
+
const own = resolveRdlTextStyle(null, paragraph, textbox);
|
|
10537
|
+
if (runStyles.length === 0)
|
|
10538
|
+
return own;
|
|
10539
|
+
const run = resolveRdlTextStyle(runStyles[0], paragraph, textbox);
|
|
10540
|
+
return {
|
|
10541
|
+
...own,
|
|
10542
|
+
fontFamily: run.fontFamily,
|
|
10543
|
+
fontSize: run.fontSize,
|
|
10544
|
+
fontStyle: run.fontStyle,
|
|
10545
|
+
fontWeight: run.fontWeight,
|
|
10546
|
+
lineHeight: own.lineHeight ?? run.lineHeight
|
|
10547
|
+
};
|
|
10548
|
+
}
|
|
10549
|
+
function textDecoration(value) {
|
|
10550
|
+
switch ((value ?? "").toLowerCase()) {
|
|
10551
|
+
case "underline": return "underline";
|
|
10552
|
+
case "linethrough": return "line-through";
|
|
10553
|
+
case "overline": return "overline";
|
|
10554
|
+
default: return undefined;
|
|
10555
|
+
}
|
|
10556
|
+
}
|
|
10557
|
+
/** An RDL LineHeight in pixels, else "normal"; an unparsable value also falls back to normal. */
|
|
10558
|
+
function rdlLineHeight(value, zoom) {
|
|
10559
|
+
const px = rdlSizeToPx(value, zoom);
|
|
10560
|
+
return px === undefined ? "normal" : `${px}px`;
|
|
10561
|
+
}
|
|
10562
|
+
/**
|
|
10563
|
+
* CSS of a resolved text style at a zoom factor. Without an RDL LineHeight the line height is the
|
|
10564
|
+
* font's natural one ("normal"), the spacing the server measures text with; set inline so no app
|
|
10565
|
+
* or base rule on p / span (125%, 1.15) can shrink the line box and clip large glyphs.
|
|
10566
|
+
*/
|
|
10567
|
+
function rdlTextCss(text, zoom) {
|
|
10568
|
+
return {
|
|
10569
|
+
// Transparent text is invisible, as in Reporting Services (reports use it to hide values).
|
|
10570
|
+
color: rdlColor(text.color),
|
|
10571
|
+
fontFamily: text.fontFamily,
|
|
10572
|
+
fontSize: rdlSizeToPx(text.fontSize, zoom) ?? ptToPx(10, zoom),
|
|
10573
|
+
fontStyle: (text.fontStyle ?? "").toLowerCase() === "italic" ? "italic" : undefined,
|
|
10574
|
+
fontWeight: text.fontWeight ? rdlFontWeight(text.fontWeight) : undefined,
|
|
10575
|
+
textDecoration: textDecoration(text.textDecoration),
|
|
10576
|
+
lineHeight: rdlLineHeight(text.lineHeight, zoom)
|
|
10577
|
+
};
|
|
10578
|
+
}
|
|
10579
|
+
/** TextAlign Left/Center/Right/General (General = left). */
|
|
10580
|
+
function rdlTextAlign(value) {
|
|
10581
|
+
switch ((value ?? "").toLowerCase()) {
|
|
10582
|
+
case "center": return "center";
|
|
10583
|
+
case "right": return "right";
|
|
10584
|
+
case "justify": return "justify";
|
|
10585
|
+
default: return "left";
|
|
10586
|
+
}
|
|
10587
|
+
}
|
|
10588
|
+
/**
|
|
10589
|
+
* VerticalAlign Top/Middle/Bottom as the justify-content of a column flex box. "safe": when the
|
|
10590
|
+
* text is taller than its box it falls back to the top, so the first line is never cut off.
|
|
10591
|
+
*/
|
|
10592
|
+
function rdlVerticalAlign(value) {
|
|
10593
|
+
switch ((value ?? "").toLowerCase()) {
|
|
10594
|
+
case "middle": return "safe center";
|
|
10595
|
+
case "bottom": return "safe flex-end";
|
|
10596
|
+
default: return "flex-start";
|
|
10597
|
+
}
|
|
10598
|
+
}
|
|
10599
|
+
/** The stroke of a line item: BorderTop, else BorderLeft, default 1pt solid black. */
|
|
10600
|
+
function rdlLineStroke(style, zoom) {
|
|
10601
|
+
const border = style?.BorderTop ?? style?.BorderLeft ?? null;
|
|
10602
|
+
const width = Math.max(1, ptToPx(parseRdlSize(border?.Width) ?? 1, zoom));
|
|
10603
|
+
const kind = (border?.Style ?? "solid").toLowerCase();
|
|
10604
|
+
const color = rdlColor(border?.Color) ?? RDL_DEFAULT_COLOR;
|
|
10605
|
+
if (kind === "none")
|
|
10606
|
+
return { color: "transparent", width };
|
|
10607
|
+
if (kind === "dotted")
|
|
10608
|
+
return { color, width, dash: `${width} ${width * 2}` };
|
|
10609
|
+
if (kind === "dashed")
|
|
10610
|
+
return { color, width, dash: `${width * 4} ${width * 2}` };
|
|
10611
|
+
return { color, width };
|
|
10612
|
+
}
|
|
10613
|
+
|
|
10614
|
+
/*
|
|
10615
|
+
* Pure chart math for the report charts. The hex values below are the report's chart palettes:
|
|
10616
|
+
* content colors (like the colors a report carries), not UI chrome, so no design tokens.
|
|
10617
|
+
*/
|
|
10618
|
+
/**
|
|
10619
|
+
* The Microsoft chart palettes by name, with the exact colors of the old ReportViewer engine.
|
|
10620
|
+
* The engine's semi-transparent palettes are #AARRGGBB; here they are CSS #RRGGBBAA.
|
|
10621
|
+
*/
|
|
10622
|
+
const PALETTES = {
|
|
10623
|
+
Default: [
|
|
10624
|
+
"#008000", "#0000FF", "#800080", "#00FF00", "#FF00FF", "#008080", "#FFFF00", "#808080",
|
|
10625
|
+
"#00FFFF", "#000080", "#800000", "#FF0000", "#808000", "#C0C0C0", "#FF6347", "#FFE4B5"
|
|
10626
|
+
],
|
|
10627
|
+
Grayscale: [
|
|
10628
|
+
"#C8C8C8", "#BDBDBD", "#B2B2B2", "#A7A7A7", "#9C9C9C", "#919191", "#868686", "#7B7B7B",
|
|
10629
|
+
"#707070", "#656565", "#5A5A5A", "#4F4F4F", "#444444", "#393939", "#2E2E2E", "#232323"
|
|
10630
|
+
],
|
|
10631
|
+
Excel: [
|
|
10632
|
+
"#9999FF", "#993366", "#FFFFCC", "#CCFFFF", "#660066", "#FF8080", "#0066CC", "#CCCCFF",
|
|
10633
|
+
"#000080", "#FF00FF", "#FFFF00", "#00FFFF", "#800080", "#800000", "#008080", "#0000FF"
|
|
10634
|
+
],
|
|
10635
|
+
Light: ["#E6E6FA", "#FFF0F5", "#FFDAB9", "#FFFACD", "#FFE4E1", "#F0FFF0", "#F0F8FF", "#F5F5F5", "#FAEBD7", "#E0FFFF"],
|
|
10636
|
+
Pastel: [
|
|
10637
|
+
"#87CEEB", "#32CD32", "#BA55D3", "#F08080", "#4682B4", "#9ACD32", "#40E0D0", "#FF69B4",
|
|
10638
|
+
"#F0E68C", "#D2B48C", "#8FBC8B", "#6495ED", "#DDA0DD", "#5F9EA0", "#FFDAB9", "#FFA07A"
|
|
10639
|
+
],
|
|
10640
|
+
EarthTones: [
|
|
10641
|
+
"#FF8000", "#B8860B", "#C04000", "#6B8E23", "#CD853F", "#C0C000", "#228B22", "#D2691E",
|
|
10642
|
+
"#808000", "#20B2AA", "#F4A460", "#00C000", "#8FBC8B", "#B22222", "#8B4513", "#C00000"
|
|
10643
|
+
],
|
|
10644
|
+
SemiTransparent: [
|
|
10645
|
+
"#FF000096", "#00FF0096", "#0000FF96", "#FFFF0096", "#00FFFF96", "#FF00FF96", "#AA781496", "#FF000050",
|
|
10646
|
+
"#00FF0050", "#0000FF50", "#FFFF0050", "#00FFFF50", "#FF00FF50", "#AA781450", "#64783296", "#285A9696"
|
|
10647
|
+
],
|
|
10648
|
+
Berry: ["#8A2BE2", "#BA55D3", "#4169E1", "#C71585", "#0000FF", "#8A2BE2", "#DA70D6", "#7B68EE", "#C000C0", "#0000CD", "#800080"],
|
|
10649
|
+
Chocolate: ["#A0522D", "#D2691E", "#8B0000", "#CD853F", "#A52A2A", "#F4A460", "#8B4513", "#C04000", "#B22222", "#B65C3A"],
|
|
10650
|
+
Fire: ["#FFD700", "#FF0000", "#FF1493", "#DC143C", "#FF8C00", "#FF00FF", "#FFFF00", "#FF4500", "#C71585", "#DDE221"],
|
|
10651
|
+
SeaGreen: ["#2E8B57", "#66CDAA", "#4682B4", "#008B8B", "#5F9EA0", "#3CB371", "#48D1CC", "#B0C4DE", "#8FBC8B", "#87CEEB"],
|
|
10652
|
+
BrightPastel: [
|
|
10653
|
+
"#418CF0", "#FCB441", "#E0400A", "#056492", "#BFBFBF", "#1A3B69", "#FFE382", "#129CDD",
|
|
10654
|
+
"#CA6B4B", "#005CDB", "#F3D288", "#506381", "#F1B9A8", "#E0830A", "#7893BE"
|
|
10655
|
+
],
|
|
10656
|
+
Pacific: [
|
|
10657
|
+
"#01B8AA", "#374649", "#FD625E", "#F2C80F", "#5F6B6D", "#8AD4EB", "#FE9666", "#A66999", "#3599B8", "#DFBFBF",
|
|
10658
|
+
"#4AC5BB", "#5F6B6D", "#FB8281", "#F4D25A", "#7F898A", "#A4DDEE", "#FDAB89", "#B687AC", "#28738A", "#A78F8F"
|
|
10659
|
+
],
|
|
10660
|
+
PacificLight: ["#4AC5BB", "#5F6B6D", "#FB8281", "#F4D25A", "#7F898A", "#A4DDEE", "#FDAB89", "#B687AC", "#28738A", "#A78F8F"],
|
|
10661
|
+
PacificSemiTransparent: [
|
|
10662
|
+
"#01B8AA99", "#37464999", "#FD625E99", "#F2C80F99", "#5F6B6D99", "#8AD4EB99", "#FE966699", "#A6699999", "#3599B899", "#DFBFBF99",
|
|
10663
|
+
"#4AC5BB99", "#5F6B6D99", "#FB828199", "#F4D25A99", "#7F898A99", "#A4DDEE99", "#FDAB8999", "#B687AC99", "#28738A99", "#A78F8F99"
|
|
10664
|
+
]
|
|
10665
|
+
};
|
|
10666
|
+
const PALETTES_BY_NAME = new Map(Object.entries(PALETTES).map(([name, colors]) => [name.toLowerCase(), colors]));
|
|
10667
|
+
/** The palette for an RDL palette name (any case); a missing or unknown name is "Default", the RDL default. */
|
|
10668
|
+
function chartPalette(name) {
|
|
10669
|
+
return PALETTES_BY_NAME.get((name ?? "").trim().toLowerCase()) ?? PALETTES.Default;
|
|
10670
|
+
}
|
|
10671
|
+
/** The color of series `index`: its own color, else the palette entry. */
|
|
10672
|
+
function seriesColor(series, index, palette) {
|
|
10673
|
+
return series.Color && series.Color.toLowerCase() !== "automatic" ? series.Color : palette[index % palette.length];
|
|
10674
|
+
}
|
|
10675
|
+
/** Steps of a value axis: about five, rounded to 1, 2, 5 or 10 times a power of ten. */
|
|
10676
|
+
const VALUE_AXIS_STEPS = 5;
|
|
10677
|
+
/** Drops the binary noise of step multiples (0.6000000000000001 -> 0.6). */
|
|
10678
|
+
function tidy$1(value) {
|
|
10679
|
+
return Number(value.toPrecision(12));
|
|
10680
|
+
}
|
|
10681
|
+
/**
|
|
10682
|
+
* bound / step, snapped to the nearest integer only when it is within a few ulps of it: 0.07 / 0.01 is
|
|
10683
|
+
* 7.000000000000001 in binary floating point and would otherwise add an axis step. Genuine non-multiples
|
|
10684
|
+
* (0.37 / 0.05) keep their fraction so the bounds still round outwards.
|
|
10685
|
+
*/
|
|
10686
|
+
function axisQuotient(bound, step) {
|
|
10687
|
+
const quotient = bound / step;
|
|
10688
|
+
const nearest = Math.round(quotient);
|
|
10689
|
+
const reconstructed = nearest * step;
|
|
10690
|
+
const tolerance = Number.EPSILON * 4 * Math.max(1, Math.abs(bound), Math.abs(reconstructed));
|
|
10691
|
+
return Math.abs(reconstructed - bound) <= tolerance ? nearest : quotient;
|
|
10692
|
+
}
|
|
10693
|
+
/** The nice step for a value range (> 0): range / 5 rounded to 1, 2, 5 or 10 times its magnitude. */
|
|
10694
|
+
function niceAxisStep(range) {
|
|
10695
|
+
const raw = range / VALUE_AXIS_STEPS;
|
|
10696
|
+
const magnitude = Math.pow(10, Math.floor(Math.log10(raw)));
|
|
10697
|
+
const residual = raw / magnitude;
|
|
10698
|
+
const nice = residual < 1.5 ? 1 : residual < 3.5 ? 2 : residual < 7.5 ? 5 : 10;
|
|
10699
|
+
return tidy$1(nice * magnitude);
|
|
10700
|
+
}
|
|
10701
|
+
/**
|
|
10702
|
+
* A value axis over the given values, the same rule as the server's PDF: the range always includes
|
|
10703
|
+
* 0, nulls are ignored, the step is `niceAxisStep(range)` and the bounds are rounded out to whole
|
|
10704
|
+
* steps (data max 113 gives 0..120 in steps of 20). All-null or all-zero data gives 0..1.
|
|
10705
|
+
*/
|
|
10706
|
+
function computeValueScale(values) {
|
|
10707
|
+
let min = 0;
|
|
10708
|
+
let max = 0;
|
|
10709
|
+
for (const value of values) {
|
|
10710
|
+
if (value === null || value === undefined || !Number.isFinite(value))
|
|
10711
|
+
continue;
|
|
10712
|
+
if (value < min)
|
|
10713
|
+
min = value;
|
|
10714
|
+
if (value > max)
|
|
10715
|
+
max = value;
|
|
10716
|
+
}
|
|
10717
|
+
if (min === max)
|
|
10718
|
+
max = min + 1;
|
|
10719
|
+
const step = niceAxisStep(max - min);
|
|
10720
|
+
const first = Math.floor(axisQuotient(min, step));
|
|
10721
|
+
const last = Math.ceil(axisQuotient(max, step));
|
|
10722
|
+
const ticks = [];
|
|
10723
|
+
for (let index = first; index <= last; index++)
|
|
10724
|
+
ticks.push(tidy$1(index * step));
|
|
10725
|
+
return { min: tidy$1(first * step), max: tidy$1(last * step), ticks };
|
|
10726
|
+
}
|
|
10727
|
+
/**
|
|
10728
|
+
* The server's category axis labels (`RdlRenderChart.CategoryAxisLabels`) along an axis that starts
|
|
10729
|
+
* at `start` with `band` pixels per category: position 0 is the first category's point (the middle
|
|
10730
|
+
* of its band), fractions fall between two categories. Labels outside the categories are dropped.
|
|
10731
|
+
*/
|
|
10732
|
+
function categoryAxisLabels(labels, categoryCount, start, band) {
|
|
10733
|
+
return labels
|
|
10734
|
+
.filter(label => Number.isFinite(label.Position) && label.Position >= -0.5 && label.Position <= categoryCount - 0.5)
|
|
10735
|
+
.map(label => ({ at: start + band * (label.Position + 0.5), text: label.Text }));
|
|
10736
|
+
}
|
|
10737
|
+
/**
|
|
10738
|
+
* Centers a label of `width` on `center`, moved inside [min, max] when it would cross an end: a
|
|
10739
|
+
* category label never runs past the value axis (and its origin label) or the plot's right edge.
|
|
10740
|
+
*/
|
|
10741
|
+
function placeAxisLabel(center, width, min, max) {
|
|
10742
|
+
if (center - width / 2 < min)
|
|
10743
|
+
return { x: min, anchor: "start" };
|
|
10744
|
+
if (center + width / 2 > max)
|
|
10745
|
+
return { x: max, anchor: "end" };
|
|
10746
|
+
return { x: center, anchor: "middle" };
|
|
10747
|
+
}
|
|
10748
|
+
/** Maps a value of the scale to a pixel coordinate between `from` (scale min) and `to` (scale max). */
|
|
10749
|
+
function scaleValue(value, scale, from, to) {
|
|
10750
|
+
const span = scale.max - scale.min;
|
|
10751
|
+
if (span === 0)
|
|
10752
|
+
return from;
|
|
10753
|
+
return from + ((value - scale.min) / span) * (to - from);
|
|
10754
|
+
}
|
|
10755
|
+
/** Splits a series into runs of consecutive non-null points: a null value leaves a gap in the line. */
|
|
10756
|
+
function lineSegments(values, x, y) {
|
|
10757
|
+
const segments = [];
|
|
10758
|
+
let current = [];
|
|
10759
|
+
values.forEach((value, index) => {
|
|
10760
|
+
if (value === null || value === undefined || !Number.isFinite(value)) {
|
|
10761
|
+
if (current.length > 0)
|
|
10762
|
+
segments.push(current);
|
|
10763
|
+
current = [];
|
|
10764
|
+
return;
|
|
10765
|
+
}
|
|
10766
|
+
current.push({ index, value, x: x(index), y: y(value) });
|
|
10767
|
+
});
|
|
10768
|
+
if (current.length > 0)
|
|
10769
|
+
segments.push(current);
|
|
10770
|
+
return segments;
|
|
10771
|
+
}
|
|
10772
|
+
/** SVG path of one segment: straight, stepped (horizontal then vertical) or smooth (Catmull-Rom as cubic Beziers). */
|
|
10773
|
+
function segmentPath(points, subtype) {
|
|
10774
|
+
if (points.length === 0)
|
|
10775
|
+
return "";
|
|
10776
|
+
const kind = (subtype ?? "").toLowerCase();
|
|
10777
|
+
let path = `M${points[0].x},${points[0].y}`;
|
|
10778
|
+
for (let i = 1; i < points.length; i++) {
|
|
10779
|
+
const prev = points[i - 1];
|
|
10780
|
+
const point = points[i];
|
|
10781
|
+
if (kind === "stepped") {
|
|
10782
|
+
path += ` H${point.x} V${point.y}`;
|
|
10783
|
+
}
|
|
10784
|
+
else if (kind === "smooth") {
|
|
10785
|
+
const before = points[i - 2] ?? prev;
|
|
10786
|
+
const after = points[i + 1] ?? point;
|
|
10787
|
+
const c1x = prev.x + (point.x - before.x) / 6;
|
|
10788
|
+
const c1y = prev.y + (point.y - before.y) / 6;
|
|
10789
|
+
const c2x = point.x - (after.x - prev.x) / 6;
|
|
10790
|
+
const c2y = point.y - (after.y - prev.y) / 6;
|
|
10791
|
+
path += ` C${c1x},${c1y} ${c2x},${c2y} ${point.x},${point.y}`;
|
|
10792
|
+
}
|
|
10793
|
+
else {
|
|
10794
|
+
path += ` L${point.x},${point.y}`;
|
|
10795
|
+
}
|
|
10796
|
+
}
|
|
10797
|
+
return path;
|
|
10798
|
+
}
|
|
10799
|
+
/** Pie slices of positive values (null, zero and negative values get no slice); angles in radians from 12 o'clock, clockwise. */
|
|
10800
|
+
function pieSlices(values) {
|
|
10801
|
+
const total = values.reduce((acc, value) => acc + (value !== null && value > 0 ? value : 0), 0);
|
|
10802
|
+
if (total <= 0)
|
|
10803
|
+
return [];
|
|
10804
|
+
const slices = [];
|
|
10805
|
+
let angle = 0;
|
|
10806
|
+
values.forEach((value, index) => {
|
|
10807
|
+
if (value === null || value <= 0)
|
|
10808
|
+
return;
|
|
10809
|
+
const fraction = value / total;
|
|
10810
|
+
const end = angle + fraction * Math.PI * 2;
|
|
10811
|
+
slices.push({ index, value, fraction, startAngle: angle, endAngle: end });
|
|
10812
|
+
angle = end;
|
|
10813
|
+
});
|
|
10814
|
+
return slices;
|
|
10815
|
+
}
|
|
10816
|
+
/** A point on a circle; angle in radians from 12 o'clock, clockwise. */
|
|
10817
|
+
function polar(cx, cy, radius, angle) {
|
|
10818
|
+
return { x: cx + radius * Math.sin(angle), y: cy - radius * Math.cos(angle) };
|
|
10819
|
+
}
|
|
10820
|
+
/** SVG path of an arc band (or a pie wedge when innerRadius is 0) between two angles. */
|
|
10821
|
+
function arcPath(cx, cy, outerRadius, innerRadius, start, end) {
|
|
10822
|
+
const sweep = end - start;
|
|
10823
|
+
if (sweep >= Math.PI * 2 - 1e-9) {
|
|
10824
|
+
const mid = start + Math.PI;
|
|
10825
|
+
return arcPath(cx, cy, outerRadius, innerRadius, start, mid) + " " + arcPath(cx, cy, outerRadius, innerRadius, mid, end);
|
|
10826
|
+
}
|
|
10827
|
+
const large = sweep > Math.PI ? 1 : 0;
|
|
10828
|
+
const o1 = polar(cx, cy, outerRadius, start);
|
|
10829
|
+
const o2 = polar(cx, cy, outerRadius, end);
|
|
10830
|
+
if (innerRadius <= 0) {
|
|
10831
|
+
return `M${cx},${cy} L${o1.x},${o1.y} A${outerRadius},${outerRadius} 0 ${large} 1 ${o2.x},${o2.y} Z`;
|
|
10832
|
+
}
|
|
10833
|
+
const i2 = polar(cx, cy, innerRadius, end);
|
|
10834
|
+
const i1 = polar(cx, cy, innerRadius, start);
|
|
10835
|
+
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`;
|
|
10836
|
+
}
|
|
10837
|
+
/** Clamps a gauge value into the scale; null stays at the minimum. */
|
|
10838
|
+
function clampGaugeValue(value, minimum, maximum) {
|
|
10839
|
+
if (value === null || value === undefined || !Number.isFinite(value))
|
|
10840
|
+
return minimum;
|
|
10841
|
+
const low = Math.min(minimum, maximum);
|
|
10842
|
+
const high = Math.max(minimum, maximum);
|
|
10843
|
+
return Math.min(high, Math.max(low, value));
|
|
10844
|
+
}
|
|
10845
|
+
/** Formats an axis or data value compactly: integers without decimals, else up to 2 decimals. */
|
|
10846
|
+
function formatChartValue(value) {
|
|
10847
|
+
if (Number.isInteger(value))
|
|
10848
|
+
return value.toString();
|
|
10849
|
+
return (Math.round(value * 100) / 100).toString();
|
|
10850
|
+
}
|
|
10851
|
+
|
|
10852
|
+
/** More ticks than this on one scale is a broken interval, not a scale anyone can read. */
|
|
10853
|
+
const MAX_GAUGE_TICKS = 200;
|
|
10854
|
+
/**
|
|
10855
|
+
* The ellipse a state indicator fills: its percent box of the panel, stretched to an oval in a box
|
|
10856
|
+
* that is not square, like the old viewer. Null when the active state has no color or the box is empty.
|
|
10857
|
+
*/
|
|
10858
|
+
function stateIndicatorEllipse(indicator, panelWidth, panelHeight) {
|
|
10859
|
+
if (!indicator.Color)
|
|
10860
|
+
return null;
|
|
10861
|
+
const width = (Math.max(0, indicator.Width) / 100) * panelWidth;
|
|
10862
|
+
const height = (Math.max(0, indicator.Height) / 100) * panelHeight;
|
|
10863
|
+
if (width <= 0 || height <= 0)
|
|
10864
|
+
return null;
|
|
10865
|
+
const left = (indicator.Left / 100) * panelWidth;
|
|
10866
|
+
const top = (indicator.Top / 100) * panelHeight;
|
|
10867
|
+
return { cx: left + width / 2, cy: top + height / 2, rx: width / 2, ry: height / 2 };
|
|
10868
|
+
}
|
|
10869
|
+
/** The value span a Bar pointer covers: from the scale minimum to the value clamped into the scale. */
|
|
10870
|
+
function gaugeBarSpan(value, minimum, maximum) {
|
|
10871
|
+
return { from: minimum, to: clampGaugeValue(value, minimum, maximum) };
|
|
10872
|
+
}
|
|
10873
|
+
function tidy(value) {
|
|
10874
|
+
return Number(value.toPrecision(12));
|
|
10875
|
+
}
|
|
10876
|
+
/**
|
|
10877
|
+
* The major tick values of a gauge scale: every `interval` from the minimum up to the maximum (the
|
|
10878
|
+
* server computes it); without a usable interval the chart axis' nice step of the range is used.
|
|
10879
|
+
*/
|
|
10880
|
+
function gaugeTicks(minimum, maximum, interval) {
|
|
10881
|
+
const low = Math.min(minimum, maximum);
|
|
10882
|
+
const high = Math.max(minimum, maximum);
|
|
10883
|
+
if (!Number.isFinite(low) || !Number.isFinite(high))
|
|
10884
|
+
return [];
|
|
10885
|
+
if (high === low)
|
|
10886
|
+
return [tidy(low)];
|
|
10887
|
+
const step = interval !== null && interval !== undefined && Number.isFinite(interval) && interval > 0
|
|
10888
|
+
? interval
|
|
10889
|
+
: niceAxisStep(high - low);
|
|
10890
|
+
const count = Math.floor(tidy((high - low) / step));
|
|
10891
|
+
if (count > MAX_GAUGE_TICKS)
|
|
10892
|
+
return [tidy(low), tidy(high)];
|
|
10893
|
+
const ticks = [];
|
|
10894
|
+
for (let index = 0; index <= count; index++)
|
|
10895
|
+
ticks.push(tidy(low + index * step));
|
|
10896
|
+
return ticks;
|
|
10897
|
+
}
|
|
10898
|
+
/**
|
|
10899
|
+
* A thickness given in percent of the gauge's own size (for a horizontal linear gauge its height, as
|
|
10900
|
+
* the chart engine measures range and pointer widths), or the default fraction when it is missing.
|
|
10901
|
+
*/
|
|
10902
|
+
function gaugeThickness(percent, gaugeSize, defaultFraction) {
|
|
10903
|
+
const fraction = percent !== null && percent !== undefined && Number.isFinite(percent) && percent > 0 ? percent / 100 : defaultFraction;
|
|
10904
|
+
return gaugeSize * Math.min(1, fraction);
|
|
10905
|
+
}
|
|
10906
|
+
/** Cross (or none) is centred on the scale line, Inside lies above it, Outside below it. */
|
|
10907
|
+
function gaugeBandExtent(placement, thickness) {
|
|
10908
|
+
switch ((placement ?? "").toLowerCase()) {
|
|
10909
|
+
case "inside": return { up: thickness, down: 0 };
|
|
10910
|
+
case "outside": return { up: 0, down: thickness };
|
|
10911
|
+
default: return { up: thickness / 2, down: thickness / 2 };
|
|
10912
|
+
}
|
|
10913
|
+
}
|
|
10914
|
+
/**
|
|
10915
|
+
* The y of the scale line of a horizontal linear gauge `height` tall: the bands (ranges and pointers)
|
|
10916
|
+
* and the tick labels below them (`labelSpace`) are centred as one block.
|
|
10917
|
+
*/
|
|
10918
|
+
function linearGaugeScaleY(height, bands, labelSpace) {
|
|
10919
|
+
const up = Math.max(0, ...bands.map(band => band.up));
|
|
10920
|
+
const down = Math.max(0, ...bands.map(band => band.down));
|
|
10921
|
+
return Math.max(up, (height - (up + down + labelSpace)) / 2 + up);
|
|
10922
|
+
}
|
|
10923
|
+
|
|
10924
|
+
/** The order an "Auto" marker takes per series, as the chart engine cycles it. */
|
|
10925
|
+
const AUTO_SHAPES = ["square", "circle", "diamond", "triangle", "cross", "star4", "star5", "star6", "star10"];
|
|
10926
|
+
const SHAPES = {
|
|
10927
|
+
square: "square",
|
|
10928
|
+
circle: "circle",
|
|
10929
|
+
diamond: "diamond",
|
|
10930
|
+
triangle: "triangle",
|
|
10931
|
+
cross: "cross",
|
|
10932
|
+
star4: "star4",
|
|
10933
|
+
star5: "star5",
|
|
10934
|
+
star6: "star6",
|
|
10935
|
+
star10: "star10"
|
|
10936
|
+
};
|
|
10937
|
+
/** The marker size when the series sets none, as the server uses it. */
|
|
10938
|
+
const RDL_DEFAULT_MARKER_SIZE_PT = 5;
|
|
10939
|
+
/**
|
|
10940
|
+
* The marker a series draws, or null for none. The server resolves Auto to a shape, so a missing type
|
|
10941
|
+
* or None means no marker. Should an Auto (or an unknown name) still arrive, a scatter series cycles
|
|
10942
|
+
* the shapes per series, so its points stay visible; a line series then draws none.
|
|
10943
|
+
*/
|
|
10944
|
+
function seriesMarker(markerType, scatter, seriesIndex) {
|
|
10945
|
+
if (!markerType)
|
|
10946
|
+
return null;
|
|
10947
|
+
const name = markerType.trim().toLowerCase();
|
|
10948
|
+
if (name === "none" || name === "")
|
|
10949
|
+
return null;
|
|
10950
|
+
const shape = SHAPES[name];
|
|
10951
|
+
if (shape)
|
|
10952
|
+
return shape;
|
|
10953
|
+
return scatter ? AUTO_SHAPES[seriesIndex % AUTO_SHAPES.length] : null;
|
|
10954
|
+
}
|
|
10955
|
+
/** The marker size in pixels at a zoom (RDL size, default 5pt), at least 3 px so it stays visible. */
|
|
10956
|
+
function markerSizePx(markerSize, zoom) {
|
|
10957
|
+
return Math.max(3, ptToPx(parseRdlSize(markerSize) ?? RDL_DEFAULT_MARKER_SIZE_PT, zoom));
|
|
10958
|
+
}
|
|
10959
|
+
function round(value) {
|
|
10960
|
+
return Math.round(value * 100) / 100;
|
|
10961
|
+
}
|
|
10962
|
+
function polygon(points) {
|
|
10963
|
+
return `M${points.map(([x, y]) => `${round(x)},${round(y)}`).join(" L")} Z`;
|
|
10964
|
+
}
|
|
10965
|
+
function star(cx, cy, outer, points) {
|
|
10966
|
+
const inner = outer * (points <= 4 ? 0.35 : 0.45);
|
|
10967
|
+
const corners = [];
|
|
10968
|
+
for (let index = 0; index < points * 2; index++) {
|
|
10969
|
+
const radius = index % 2 === 0 ? outer : inner;
|
|
10970
|
+
const angle = -Math.PI / 2 + (index * Math.PI) / points;
|
|
10971
|
+
corners.push([cx + radius * Math.cos(angle), cy + radius * Math.sin(angle)]);
|
|
10972
|
+
}
|
|
10973
|
+
return polygon(corners);
|
|
10974
|
+
}
|
|
10975
|
+
/** 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. */
|
|
10976
|
+
function markerPath(shape, cx, cy, size) {
|
|
10977
|
+
const r = size / 2;
|
|
10978
|
+
switch (shape) {
|
|
10979
|
+
case "square":
|
|
10980
|
+
return polygon([[cx - r, cy - r], [cx + r, cy - r], [cx + r, cy + r], [cx - r, cy + r]]);
|
|
10981
|
+
case "circle":
|
|
10982
|
+
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`;
|
|
10983
|
+
case "diamond":
|
|
10984
|
+
return polygon([[cx, cy - r], [cx + r, cy], [cx, cy + r], [cx - r, cy]]);
|
|
10985
|
+
case "triangle":
|
|
10986
|
+
return polygon([[cx, cy - r], [cx + r, cy + r], [cx - r, cy + r]]);
|
|
10987
|
+
case "cross": {
|
|
10988
|
+
// The chart engine's Cross is an X: a filled plus sign, its arms a third of the size thick, turned by 45 degrees.
|
|
10989
|
+
const t = size / 6;
|
|
10990
|
+
const plus = [
|
|
10991
|
+
[-t, -r], [t, -r], [t, -t], [r, -t], [r, t], [t, t],
|
|
10992
|
+
[t, r], [-t, r], [-t, t], [-r, t], [-r, -t], [-t, -t]
|
|
10993
|
+
];
|
|
10994
|
+
const turn = Math.SQRT1_2;
|
|
10995
|
+
return polygon(plus.map(([dx, dy]) => [cx + (dx - dy) * turn, cy + (dx + dy) * turn]));
|
|
10996
|
+
}
|
|
10997
|
+
case "star4": return star(cx, cy, r, 4);
|
|
10998
|
+
case "star5": return star(cx, cy, r, 5);
|
|
10999
|
+
case "star6": return star(cx, cy, r, 6);
|
|
11000
|
+
case "star10": return star(cx, cy, r, 10);
|
|
11001
|
+
}
|
|
11002
|
+
}
|
|
11003
|
+
const ALIGN = {
|
|
11004
|
+
left: "start",
|
|
11005
|
+
top: "start",
|
|
11006
|
+
center: "center",
|
|
11007
|
+
right: "end",
|
|
11008
|
+
bottom: "end"
|
|
11009
|
+
};
|
|
11010
|
+
/** The RDL default legend position: a column on the right, vertically centered (RightCenter). */
|
|
11011
|
+
const DEFAULT_LEGEND = { edge: "right", align: "center" };
|
|
11012
|
+
/**
|
|
11013
|
+
* The RDL ChartLegend Position as an edge and an alignment: the first word is the edge, the second
|
|
11014
|
+
* the alignment along it (TopLeft = top edge, left aligned; LeftTop = left edge, top aligned).
|
|
11015
|
+
* Missing or unknown = RightCenter, the RDL default.
|
|
11016
|
+
*/
|
|
11017
|
+
function legendPlacement(position) {
|
|
11018
|
+
const match = /^(top|bottom|left|right)(left|right|top|bottom|center)$/i.exec((position ?? "").trim());
|
|
11019
|
+
if (!match)
|
|
11020
|
+
return DEFAULT_LEGEND;
|
|
11021
|
+
const edge = match[1].toLowerCase();
|
|
11022
|
+
const along = match[2].toLowerCase();
|
|
11023
|
+
const horizontalEdge = edge === "top" || edge === "bottom";
|
|
11024
|
+
// "TopTop" or "LeftLeft" make no sense: the alignment must run along the edge.
|
|
11025
|
+
const valid = horizontalEdge ? ["left", "center", "right"].includes(along) : ["top", "center", "bottom"].includes(along);
|
|
11026
|
+
return { edge, align: valid ? ALIGN[along] : "center" };
|
|
11027
|
+
}
|
|
11028
|
+
|
|
11029
|
+
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"};
|
|
11030
|
+
|
|
11031
|
+
/** Chart text size in points (scaled with the zoom). */
|
|
11032
|
+
const CHART_FONT_PT = 8;
|
|
11033
|
+
/** Rough width of one character of the chart font, as a share of the font size. */
|
|
11034
|
+
const CHAR_WIDTH = 0.6;
|
|
11035
|
+
function chartFontPx(zoom) {
|
|
11036
|
+
return ptToPx(CHART_FONT_PT, zoom);
|
|
11037
|
+
}
|
|
11038
|
+
function estimateTextWidth(text, fontPx) {
|
|
11039
|
+
return text.length * fontPx * CHAR_WIDTH;
|
|
11040
|
+
}
|
|
11041
|
+
/** Hover handlers that show the shared CwTooltipManager tooltip over an SVG element. */
|
|
11042
|
+
function useChartTooltip() {
|
|
11043
|
+
useEffect(() => {
|
|
11044
|
+
ensureTooltipManager();
|
|
11045
|
+
}, []);
|
|
11046
|
+
const show = useCallback((content) => (event) => {
|
|
11047
|
+
showTooltip(content, event.currentTarget, "top");
|
|
11048
|
+
}, []);
|
|
11049
|
+
const hide = useCallback(() => hideTooltip(), []);
|
|
11050
|
+
return { show, hide };
|
|
11051
|
+
}
|
|
11052
|
+
function tooltipContent(title, label, value) {
|
|
11053
|
+
return (jsxs("span", { children: [jsx("strong", { children: title }), jsx("br", {}), label, ": ", value] }));
|
|
11054
|
+
}
|
|
11055
|
+
/** Lays legend entries out in rows that wrap at `width`, each row aligned within `width` (centered by default). */
|
|
11056
|
+
function layoutLegend(entries, width, fontPx, align = "center") {
|
|
11057
|
+
if (entries.length === 0)
|
|
11058
|
+
return { height: 0, width: 0, items: [] };
|
|
11059
|
+
const swatch = fontPx;
|
|
11060
|
+
const gap = fontPx;
|
|
11061
|
+
const lineHeight = fontPx * 1.6;
|
|
11062
|
+
const rows = [[]];
|
|
11063
|
+
let rowWidth = 0;
|
|
11064
|
+
for (const entry of entries) {
|
|
11065
|
+
const itemWidth = swatch + fontPx * 0.4 + estimateTextWidth(entry.label, fontPx);
|
|
11066
|
+
if (rowWidth > 0 && rowWidth + gap + itemWidth > width) {
|
|
11067
|
+
rows.push([]);
|
|
11068
|
+
rowWidth = 0;
|
|
11069
|
+
}
|
|
11070
|
+
rowWidth += (rowWidth > 0 ? gap : 0) + itemWidth;
|
|
11071
|
+
rows[rows.length - 1].push({ entry, width: itemWidth });
|
|
11072
|
+
}
|
|
11073
|
+
const items = [];
|
|
11074
|
+
let widest = 0;
|
|
11075
|
+
rows.forEach((row, rowIndex) => {
|
|
11076
|
+
const total = row.reduce((acc, item) => acc + item.width, 0) + gap * (row.length - 1);
|
|
11077
|
+
widest = Math.max(widest, total);
|
|
11078
|
+
const free = Math.max(0, width - total);
|
|
11079
|
+
let x = align === "start" ? 0 : align === "end" ? free : free / 2;
|
|
11080
|
+
for (const item of row) {
|
|
11081
|
+
items.push({ entry: item.entry, x, y: rowIndex * lineHeight + lineHeight / 2 });
|
|
11082
|
+
x += item.width + gap;
|
|
11083
|
+
}
|
|
11084
|
+
});
|
|
11085
|
+
return { height: rows.length * lineHeight, width: widest, items };
|
|
11086
|
+
}
|
|
11087
|
+
function ChartLegend({ layout, top, left = 0, fontPx }) {
|
|
11088
|
+
return (jsx("g", { transform: `translate(${left}, ${top})`, children: layout.items.map((item, index) => (jsxs("g", { transform: `translate(${item.x}, ${item.y})`, children: [item.entry.marker
|
|
11089
|
+
? jsx("path", { d: markerPath(item.entry.marker, fontPx / 2, 0, fontPx), fill: item.entry.color })
|
|
11090
|
+
: jsx("rect", { x: 0, y: -fontPx / 2, width: fontPx, height: fontPx, fill: item.entry.color }), jsx("text", { x: fontPx * 1.4, dominantBaseline: "central", fontSize: fontPx, children: item.entry.label })] }, index))) }));
|
|
11091
|
+
}
|
|
11092
|
+
/** The chart titles, one line each, centered. Returns the height they take. */
|
|
11093
|
+
function ChartTitles({ titles, width, fontPx }) {
|
|
11094
|
+
return (jsx(Fragment, { children: titles.map((title, index) => (jsx("text", { className: styles$2.title, x: width / 2, y: fontPx * 1.5 * (index + 1), textAnchor: "middle", fontSize: fontPx * 1.25, children: title }, index))) }));
|
|
11095
|
+
}
|
|
11096
|
+
function titlesHeight(titles, fontPx) {
|
|
11097
|
+
return titles.length === 0 ? 0 : fontPx * 1.5 * titles.length + fontPx * 0.5;
|
|
11098
|
+
}
|
|
11099
|
+
/** A centered message instead of the plot (dataset error or no rows). */
|
|
11100
|
+
function ChartMessage({ text, width, height, fontPx }) {
|
|
11101
|
+
return (jsx("text", { className: styles$2.muted, x: width / 2, y: height / 2, textAnchor: "middle", dominantBaseline: "central", fontSize: fontPx * 1.2, children: text }));
|
|
11102
|
+
}
|
|
11103
|
+
|
|
11104
|
+
/** 270 degree dial: from 7:30 to 4:30 on a clock face, in radians from 12 o'clock. */
|
|
11105
|
+
const DIAL_START = -Math.PI * 0.75;
|
|
11106
|
+
const DIAL_END = Math.PI * 0.75;
|
|
11107
|
+
/** Report content defaults, as the old viewer drew them: black pointers, the Default palette for ranges. */
|
|
11108
|
+
const POINTER_FALLBACK = "black";
|
|
11109
|
+
const RANGE_PALETTE = chartPalette(null);
|
|
11110
|
+
/** Thickness of a range / pointer without a Width, as a fraction of the gauge height (the chart engine defaults). */
|
|
11111
|
+
const RANGE_DEFAULT_WIDTH = 0.3;
|
|
11112
|
+
const POINTER_DEFAULT_WIDTH = 0.1;
|
|
11113
|
+
const EMPTY_SCALE = { Minimum: 0, Maximum: 100, Pointers: [], Ranges: [] };
|
|
11114
|
+
function isBarPointer(type) {
|
|
11115
|
+
return (type ?? "").toLowerCase() === "bar";
|
|
11116
|
+
}
|
|
11117
|
+
function rangeColor(range, index) {
|
|
11118
|
+
return range.Color || RANGE_PALETTE[index % RANGE_PALETTE.length];
|
|
11119
|
+
}
|
|
11120
|
+
function pointerColor(pointer) {
|
|
11121
|
+
return pointer.Color || POINTER_FALLBACK;
|
|
11122
|
+
}
|
|
11123
|
+
function RadialGauge({ gauge, width, height, fontPx, zoom }) {
|
|
11124
|
+
const scale = gauge.Scales[0] ?? EMPTY_SCALE;
|
|
11125
|
+
const { Minimum: min, Maximum: max } = scale;
|
|
11126
|
+
const radius = Math.max(0, Math.min(width / 2, height / 1.75) - fontPx * 0.5);
|
|
11127
|
+
const cx = width / 2;
|
|
11128
|
+
const cy = fontPx * 0.5 + radius;
|
|
11129
|
+
const band = radius * 0.15;
|
|
11130
|
+
const angle = (value) => scaleValue(clampGaugeValue(value, min, max), { min, max }, DIAL_START, DIAL_END);
|
|
11131
|
+
const minLabel = polar(cx, cy, radius - band * 2.5, DIAL_START);
|
|
11132
|
+
const maxLabel = polar(cx, cy, radius - band * 2.5, DIAL_END);
|
|
11133
|
+
const showLabels = scale.ShowLabels !== false;
|
|
11134
|
+
return (jsxs("g", { children: [jsx("path", { className: styles$2.track, d: arcPath(cx, cy, radius, radius - band, DIAL_START, DIAL_END) }), scale.Ranges.map((range, index) => (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) => {
|
|
11135
|
+
if (isBarPointer(pointer.Type)) {
|
|
11136
|
+
const span = gaugeBarSpan(pointer.Value, min, max);
|
|
11137
|
+
return (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}`));
|
|
11138
|
+
}
|
|
11139
|
+
const tip = polar(cx, cy, radius * 0.8, angle(pointer.Value ?? min));
|
|
11140
|
+
return (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}`));
|
|
11141
|
+
}), jsx("circle", { className: styles$2.cap, cx: cx, cy: cy, r: Math.max(2, radius * 0.06) }), showLabels && (jsxs(Fragment, { children: [jsx("text", { className: styles$2.tick, x: minLabel.x, y: minLabel.y, textAnchor: "middle", fontSize: fontPx, children: formatChartValue(min) }), jsx("text", { className: styles$2.tick, x: maxLabel.x, y: maxLabel.y, textAnchor: "middle", fontSize: fontPx, children: formatChartValue(max) })] }))] }));
|
|
11142
|
+
}
|
|
11143
|
+
/**
|
|
11144
|
+
* A horizontal linear gauge as the chart engine draws it: range bands and pointers are as thick as
|
|
11145
|
+
* their Width percent of the gauge height (ranges 30%, pointers 10% by default) around the scale line
|
|
11146
|
+
* (Cross centred, Inside above, Outside below), the ranges in their own colour at full opacity, a Bar
|
|
11147
|
+
* pointer on top of them from the minimum to the value, and the major ticks with labels below.
|
|
11148
|
+
*/
|
|
11149
|
+
function LinearGauge({ gauge, width, height, fontPx }) {
|
|
11150
|
+
const scale = gauge.Scales[0] ?? EMPTY_SCALE;
|
|
11151
|
+
const { Minimum: min, Maximum: max } = scale;
|
|
11152
|
+
const showLabels = scale.ShowLabels !== false;
|
|
11153
|
+
const left = fontPx;
|
|
11154
|
+
const right = width - fontPx;
|
|
11155
|
+
const labelSpace = showLabels ? fontPx * 2 : 0;
|
|
11156
|
+
const x = (value) => scaleValue(clampGaugeValue(value, min, max), { min, max }, left, right);
|
|
11157
|
+
const ticks = showLabels ? gaugeTicks(min, max, scale.Interval) : [];
|
|
11158
|
+
const ranges = scale.Ranges.map(range => {
|
|
11159
|
+
const thickness = gaugeThickness(range.Width, height, RANGE_DEFAULT_WIDTH);
|
|
11160
|
+
return { range, thickness, extent: gaugeBandExtent(range.Placement, thickness) };
|
|
11161
|
+
});
|
|
11162
|
+
const pointers = scale.Pointers.map(pointer => {
|
|
11163
|
+
const thickness = gaugeThickness(pointer.Width, height, POINTER_DEFAULT_WIDTH);
|
|
11164
|
+
return { pointer, thickness, extent: gaugeBandExtent(pointer.Placement, thickness) };
|
|
11165
|
+
});
|
|
11166
|
+
const extents = [...ranges, ...pointers].map(band => band.extent);
|
|
11167
|
+
const scaleY = linearGaugeScaleY(height, extents, labelSpace);
|
|
11168
|
+
const bandsBottom = scaleY + Math.max(0, ...extents.map(extent => extent.down));
|
|
11169
|
+
return (jsxs("g", { children: [ranges.map(({ range, thickness, extent }, index) => {
|
|
11170
|
+
const from = x(range.Start ?? min);
|
|
11171
|
+
const to = x(range.End ?? max);
|
|
11172
|
+
return (jsx("rect", { x: Math.min(from, to), y: scaleY - extent.up, width: Math.abs(to - from), height: thickness, fill: rangeColor(range, index) }, `r${index}`));
|
|
11173
|
+
}), jsx("line", { className: styles$2.axis, x1: left, x2: right, y1: scaleY, y2: scaleY }), pointers.map(({ pointer, thickness, extent }, index) => {
|
|
11174
|
+
if (isBarPointer(pointer.Type)) {
|
|
11175
|
+
const span = gaugeBarSpan(pointer.Value, min, max);
|
|
11176
|
+
const from = x(span.from);
|
|
11177
|
+
const to = x(span.to);
|
|
11178
|
+
return (jsx("rect", { x: Math.min(from, to), y: scaleY - extent.up, width: Math.abs(to - from), height: thickness, fill: pointerColor(pointer) }, `p${index}`));
|
|
11179
|
+
}
|
|
11180
|
+
// A marker: a triangle pointing at the scale line from below.
|
|
11181
|
+
const at = x(pointer.Value ?? min);
|
|
11182
|
+
const size = Math.max(thickness, fontPx * 0.6);
|
|
11183
|
+
return (jsx("path", { d: `M${at},${scaleY} l${-size / 2},${size} h${size} Z`, fill: pointerColor(pointer) }, `p${index}`));
|
|
11184
|
+
}), ticks.map(tick => (jsxs("g", { children: [jsx("line", { className: styles$2.axis, x1: x(tick), x2: x(tick), y1: bandsBottom, y2: bandsBottom + fontPx * 0.4 }), jsx("text", { className: styles$2.tick, x: x(tick), y: bandsBottom + fontPx * 1.5, textAnchor: "middle", fontSize: fontPx, children: formatChartValue(tick) })] }, tick)))] }));
|
|
11185
|
+
}
|
|
11186
|
+
/**
|
|
11187
|
+
* A gauge panel: its gauges side by side (RadialGauge = 270 degree dial with colored ranges and a
|
|
11188
|
+
* needle or bar per pointer, LinearGauge = horizontal track with range bands, a bar or marker per
|
|
11189
|
+
* pointer and the major ticks), the state indicators at their percent boxes and the panel labels
|
|
11190
|
+
* below. The pointer value is not printed; only the report's own labels are. Only the first scale of
|
|
11191
|
+
* a gauge is drawn.
|
|
11192
|
+
*/
|
|
11193
|
+
function CwReportGauge({ panel, width, height, zoom = 1 }) {
|
|
11194
|
+
const fontPx = chartFontPx(zoom);
|
|
11195
|
+
const lineHeight = fontPx * 1.5;
|
|
11196
|
+
const labelsHeight = panel.Labels.length * lineHeight;
|
|
11197
|
+
const gaugeHeight = Math.max(0, height - labelsHeight);
|
|
11198
|
+
const cellWidth = panel.Gauges.length > 0 ? width / panel.Gauges.length : width;
|
|
11199
|
+
return (jsxs("svg", { className: styles$2.chart, width: width, height: height, role: "img", "aria-label": panel.Name, children: [panel.Error ? (jsx(ChartMessage, { text: panel.Error, width: width, height: height, fontPx: fontPx })) : (jsxs(Fragment, { children: [panel.Gauges.map((gauge, index) => {
|
|
11200
|
+
const drawProps = { gauge, width: cellWidth, height: gaugeHeight, fontPx, zoom };
|
|
11201
|
+
return (jsx("g", { transform: `translate(${cellWidth * index}, 0)`, children: gauge.Kind.toLowerCase() === "lineargauge" ? jsx(LinearGauge, { ...drawProps }) : jsx(RadialGauge, { ...drawProps }) }, index));
|
|
11202
|
+
}), (panel.StateIndicators ?? []).map((indicator, index) => {
|
|
11203
|
+
const ellipse = stateIndicatorEllipse(indicator, width, height);
|
|
11204
|
+
return ellipse && jsx("ellipse", { ...ellipse, fill: indicator.Color ?? undefined }, `s${index}`);
|
|
11205
|
+
})] })), panel.Labels.map((label, index) => (jsx("text", { x: width / 2, y: gaugeHeight + lineHeight * (index + 0.7), textAnchor: "middle", fontSize: fontPx, children: label }, index)))] }));
|
|
11206
|
+
}
|
|
11207
|
+
|
|
11208
|
+
/*
|
|
11209
|
+
* The .NET numeric format strings a report puts on a chart's value axis (<Format>), applied with the
|
|
11210
|
+
* report culture so the screen shows the same tick text as the server's PDF. Only the formats reports
|
|
11211
|
+
* actually use are covered; anything else returns null and the caller keeps its own formatting.
|
|
11212
|
+
*/
|
|
11213
|
+
/** .NET's default decimals of the N, F and P standard formats without a precision. */
|
|
11214
|
+
const STANDARD_DEFAULT_DECIMALS = 2;
|
|
11215
|
+
const STANDARD_FORMAT = /^([NnFfPp])(\d{1,2})?$/;
|
|
11216
|
+
/** 0, 0.00, 0.0#, #,##0, #,0.00, 0%, 0.0% ... (integer part of 0, # and one optional grouping comma). */
|
|
11217
|
+
const CUSTOM_FORMAT = /^(#*,?#*0+)(?:\.(0*)(#*))?(%?)$/;
|
|
11218
|
+
/**
|
|
11219
|
+
* Formats longer than this are not matched (the custom pattern backtracks quadratically on a long
|
|
11220
|
+
* non-matching string), and more decimals than this are left to the caller: Intl.NumberFormat throws
|
|
11221
|
+
* above 100 fraction digits (above 20 on older browsers), which would break the chart render.
|
|
11222
|
+
*/
|
|
11223
|
+
const MAX_FORMAT_LENGTH = 64;
|
|
11224
|
+
const MAX_FRACTION_DIGITS = 20;
|
|
11225
|
+
function parseFormat(format) {
|
|
11226
|
+
if (format.length > MAX_FORMAT_LENGTH)
|
|
11227
|
+
return null;
|
|
11228
|
+
const standard = STANDARD_FORMAT.exec(format);
|
|
11229
|
+
if (standard) {
|
|
11230
|
+
const kind = standard[1].toUpperCase();
|
|
11231
|
+
const digits = standard[2] === undefined ? STANDARD_DEFAULT_DECIMALS : parseInt(standard[2], 10);
|
|
11232
|
+
return {
|
|
11233
|
+
minimumFractionDigits: digits,
|
|
11234
|
+
maximumFractionDigits: digits,
|
|
11235
|
+
grouping: kind !== "F",
|
|
11236
|
+
percent: kind === "P" ? "standard" : null
|
|
11237
|
+
};
|
|
11238
|
+
}
|
|
11239
|
+
const custom = CUSTOM_FORMAT.exec(format);
|
|
11240
|
+
if (custom) {
|
|
11241
|
+
const required = custom[2]?.length ?? 0;
|
|
11242
|
+
const optional = custom[3]?.length ?? 0;
|
|
11243
|
+
return {
|
|
11244
|
+
minimumFractionDigits: required,
|
|
11245
|
+
maximumFractionDigits: required + optional,
|
|
11246
|
+
grouping: custom[1].includes(","),
|
|
11247
|
+
percent: custom[4] === "%" ? "custom" : null
|
|
11248
|
+
};
|
|
11249
|
+
}
|
|
11250
|
+
return null;
|
|
11251
|
+
}
|
|
11252
|
+
function numberFormat(locale, options) {
|
|
11253
|
+
try {
|
|
11254
|
+
return new Intl.NumberFormat(locale || "en-US", options);
|
|
11255
|
+
}
|
|
11256
|
+
catch {
|
|
11257
|
+
return new Intl.NumberFormat("en-US", options);
|
|
11258
|
+
}
|
|
11259
|
+
}
|
|
11260
|
+
/**
|
|
11261
|
+
* A value in a .NET numeric format ("N0", "F1", "P0", "0", "0.0", "#,##0.00", "0%", ...) and a culture
|
|
11262
|
+
* ("de-CH"; missing or unknown = invariant-like en-US). Null for a missing or unsupported format.
|
|
11263
|
+
* The standard "P" uses the culture's percent pattern (e.g. "12 %" in de-DE); a custom "%" appends the
|
|
11264
|
+
* sign directly, as .NET does.
|
|
11265
|
+
*/
|
|
11266
|
+
function formatRdlNumber(value, format, culture) {
|
|
11267
|
+
if (!format || !Number.isFinite(value))
|
|
11268
|
+
return null;
|
|
11269
|
+
const spec = parseFormat(format.trim());
|
|
11270
|
+
if (!spec || spec.maximumFractionDigits > MAX_FRACTION_DIGITS)
|
|
11271
|
+
return null;
|
|
11272
|
+
const digits = {
|
|
11273
|
+
minimumFractionDigits: spec.minimumFractionDigits,
|
|
11274
|
+
maximumFractionDigits: spec.maximumFractionDigits,
|
|
11275
|
+
useGrouping: spec.grouping
|
|
11276
|
+
};
|
|
11277
|
+
const locale = culture ?? undefined;
|
|
11278
|
+
if (spec.percent === "standard")
|
|
11279
|
+
return numberFormat(locale, { ...digits, style: "percent" }).format(value);
|
|
11280
|
+
if (spec.percent === "custom") {
|
|
11281
|
+
// The percent style scales by 100 without float noise (0.145 -> 14.5, not 14.499...); its own
|
|
11282
|
+
// sign and spacing are dropped for the plain "%" a custom format appends.
|
|
11283
|
+
const parts = numberFormat(locale, { ...digits, style: "percent" }).formatToParts(value);
|
|
11284
|
+
return `${parts.filter(part => part.type !== "percentSign" && part.type !== "literal").map(part => part.value).join("")}%`;
|
|
11285
|
+
}
|
|
11286
|
+
return numberFormat(locale, digits).format(value);
|
|
11287
|
+
}
|
|
11288
|
+
|
|
11289
|
+
function seriesKind(series) {
|
|
11290
|
+
switch (series.Type.toLowerCase()) {
|
|
11291
|
+
case "column": return "column";
|
|
11292
|
+
case "bar": return "bar";
|
|
11293
|
+
case "area": return "area";
|
|
11294
|
+
case "scatter": return "scatter";
|
|
11295
|
+
default: return "line";
|
|
11296
|
+
}
|
|
11297
|
+
}
|
|
11298
|
+
function truncate(text, maxWidth, fontPx) {
|
|
11299
|
+
if (estimateTextWidth(text, fontPx) <= maxWidth)
|
|
11300
|
+
return text;
|
|
11301
|
+
const chars = Math.max(1, Math.floor(maxWidth / (fontPx * 0.6)) - 1);
|
|
11302
|
+
return text.slice(0, chars) + "…";
|
|
11303
|
+
}
|
|
11304
|
+
/** Categories along the x axis, values up: lines, areas, columns and scatter points. */
|
|
11305
|
+
function VerticalPlot({ chart, colors, scale, fontPx, left, top, right, bottom, strokeWidth, zoom, tickLabel }) {
|
|
11306
|
+
const tooltip = useChartTooltip();
|
|
11307
|
+
const count = Math.max(1, chart.Categories.length);
|
|
11308
|
+
const band = (right - left) / count;
|
|
11309
|
+
const x = (index) => left + band * (index + 0.5);
|
|
11310
|
+
const y = (value) => scaleValue(value, scale, bottom, top);
|
|
11311
|
+
const baseline = y(Math.min(scale.max, Math.max(scale.min, 0)));
|
|
11312
|
+
const columns = chart.Series.map((series, index) => ({ series, index })).filter(({ series }) => {
|
|
11313
|
+
const kind = seriesKind(series);
|
|
11314
|
+
return kind === "column" || kind === "bar";
|
|
11315
|
+
});
|
|
11316
|
+
const groupWidth = band * 0.75;
|
|
11317
|
+
const columnWidth = columns.length > 0 ? groupWidth / columns.length : 0;
|
|
11318
|
+
const labelWidth = Math.max(...chart.Categories.map(label => estimateTextWidth(label, fontPx)), 0) + fontPx;
|
|
11319
|
+
const labelStep = Math.max(1, Math.ceil(Math.min(labelWidth, fontPx * 12) / band));
|
|
11320
|
+
const marks = [];
|
|
11321
|
+
const hits = [];
|
|
11322
|
+
columns.forEach(({ series, index }, columnIndex) => {
|
|
11323
|
+
series.Values.forEach((value, category) => {
|
|
11324
|
+
if (value === null || !Number.isFinite(value))
|
|
11325
|
+
return;
|
|
11326
|
+
const columnX = left + band * category + (band - groupWidth) / 2 + columnWidth * columnIndex;
|
|
11327
|
+
const valueY = y(value);
|
|
11328
|
+
const content = tooltipContent(series.Name, chart.Categories[category] ?? "", formatChartValue(value));
|
|
11329
|
+
marks.push(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}`));
|
|
11330
|
+
if (series.ShowDataLabels) {
|
|
11331
|
+
marks.push(jsx("text", { x: columnX + columnWidth / 2, y: valueY - fontPx * 0.4, textAnchor: "middle", fontSize: fontPx, children: formatChartValue(value) }, `cl${index}-${category}`));
|
|
11332
|
+
}
|
|
11333
|
+
});
|
|
11334
|
+
});
|
|
11335
|
+
chart.Series.forEach((series, index) => {
|
|
11336
|
+
const kind = seriesKind(series);
|
|
11337
|
+
if (kind !== "line" && kind !== "area" && kind !== "scatter")
|
|
11338
|
+
return;
|
|
11339
|
+
const marker = seriesMarker(series.MarkerType, kind === "scatter", index);
|
|
11340
|
+
const markerSize = markerSizePx(series.MarkerSize, zoom);
|
|
11341
|
+
const segments = lineSegments(series.Values, x, y);
|
|
11342
|
+
segments.forEach((segment, segmentIndex) => {
|
|
11343
|
+
if (kind !== "scatter")
|
|
11344
|
+
drawLine(segment, segmentIndex);
|
|
11345
|
+
for (const point of segment) {
|
|
11346
|
+
if (marker) {
|
|
11347
|
+
marks.push(jsx("path", { d: markerPath(marker, point.x, point.y, markerSize), fill: colors[index] }, `m${index}-${point.index}`));
|
|
11348
|
+
}
|
|
11349
|
+
if (series.ShowDataLabels) {
|
|
11350
|
+
marks.push(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}`));
|
|
11351
|
+
}
|
|
11352
|
+
const content = tooltipContent(series.Name, chart.Categories[point.index] ?? "", formatChartValue(point.value));
|
|
11353
|
+
hits.push(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}`));
|
|
11354
|
+
}
|
|
11355
|
+
});
|
|
11356
|
+
function drawLine(segment, segmentIndex) {
|
|
11357
|
+
const path = segmentPath(segment, series.Subtype);
|
|
11358
|
+
if (kind === "area") {
|
|
11359
|
+
const first = segment[0];
|
|
11360
|
+
const last = segment[segment.length - 1];
|
|
11361
|
+
marks.push(jsx("path", { d: `${path} L${last.x},${baseline} L${first.x},${baseline} Z`, fill: colors[index], fillOpacity: 0.45, stroke: "none" }, `a${index}-${segmentIndex}`));
|
|
11362
|
+
}
|
|
11363
|
+
marks.push(jsx("path", { d: path, fill: "none", stroke: colors[index], strokeWidth: strokeWidth }, `l${index}-${segmentIndex}`));
|
|
11364
|
+
// A lone point between gaps has no line to show it: a dot, unless the series draws markers.
|
|
11365
|
+
if (segment.length === 1 && !marker) {
|
|
11366
|
+
marks.push(jsx("circle", { className: styles$2.point, cx: segment[0].x, cy: segment[0].y, r: strokeWidth * 1.5, fill: colors[index] }, `p${index}-${segmentIndex}`));
|
|
11367
|
+
}
|
|
11368
|
+
}
|
|
11369
|
+
});
|
|
11370
|
+
const serverLabels = chart.CategoryAxisLabels ? categoryAxisLabels(chart.CategoryAxisLabels, count, left, band) : null;
|
|
11371
|
+
// Category gridlines run through the labelled categories: the server's labels, else every labelStep-th category.
|
|
11372
|
+
const categoryGrid = chart.CategoryAxisGridLines === false
|
|
11373
|
+
? []
|
|
11374
|
+
: serverLabels?.map(label => label.at) ?? chart.Categories.map((_label, index) => index).filter(index => index % labelStep === 0).map(x);
|
|
11375
|
+
return (jsxs(Fragment, { children: [categoryGrid.map((at, index) => jsx("line", { className: styles$2.grid, x1: at, x2: at, y1: top, y2: bottom }, `cg${index}`)), scale.ticks.map(tick => (jsxs("g", { children: [chart.ValueAxisGridLines !== false && jsx("line", { className: styles$2.grid, x1: left, x2: right, y1: y(tick), y2: y(tick) }), jsx("text", { className: styles$2.tick, x: left - fontPx * 0.4, y: y(tick), textAnchor: "end", dominantBaseline: "central", fontSize: fontPx, children: tickLabel(tick) })] }, tick))), jsx("line", { className: styles$2.axis, x1: left, x2: left, y1: top, y2: bottom }), jsx("line", { className: styles$2.axis, x1: left, x2: right, y1: baseline, y2: baseline }), serverLabels
|
|
11376
|
+
? serverLabels.map((label, index) => {
|
|
11377
|
+
const placement = placeAxisLabel(label.at, estimateTextWidth(label.text, fontPx), left, right);
|
|
11378
|
+
return (jsxs("g", { children: [jsx("line", { className: styles$2.axis, x1: label.at, x2: label.at, y1: bottom, y2: bottom + fontPx * 0.3 }), jsx("text", { className: styles$2.tick, x: placement.x, y: bottom + fontPx * 1.3, textAnchor: placement.anchor, fontSize: fontPx, children: label.text })] }, `c${index}`));
|
|
11379
|
+
})
|
|
11380
|
+
: chart.Categories.map((label, index) => {
|
|
11381
|
+
if (index % labelStep !== 0)
|
|
11382
|
+
return null;
|
|
11383
|
+
const text = truncate(label, band * labelStep - fontPx * 0.4, fontPx);
|
|
11384
|
+
const placement = placeAxisLabel(x(index), estimateTextWidth(text, fontPx), left, right);
|
|
11385
|
+
return (jsx("text", { className: styles$2.tick, x: placement.x, y: bottom + fontPx * 1.1, textAnchor: placement.anchor, fontSize: fontPx, children: text }, index));
|
|
11386
|
+
}), marks, hits] }));
|
|
11387
|
+
}
|
|
11388
|
+
/** Categories down the y axis, values to the right: horizontal bars. */
|
|
11389
|
+
function HorizontalPlot({ chart, colors, scale, fontPx, left, top, right, bottom, tickLabel }) {
|
|
11390
|
+
const tooltip = useChartTooltip();
|
|
11391
|
+
const count = Math.max(1, chart.Categories.length);
|
|
11392
|
+
const band = (bottom - top) / count;
|
|
11393
|
+
const x = (value) => scaleValue(value, scale, left, right);
|
|
11394
|
+
const baseline = x(Math.min(scale.max, Math.max(scale.min, 0)));
|
|
11395
|
+
const groupHeight = band * 0.75;
|
|
11396
|
+
const barHeight = chart.Series.length > 0 ? groupHeight / chart.Series.length : 0;
|
|
11397
|
+
const serverLabels = chart.CategoryAxisLabels ? categoryAxisLabels(chart.CategoryAxisLabels, count, top, band) : null;
|
|
11398
|
+
const categoryGrid = chart.CategoryAxisGridLines === false
|
|
11399
|
+
? []
|
|
11400
|
+
: serverLabels?.map(label => label.at) ?? chart.Categories.map((_label, index) => top + band * (index + 0.5));
|
|
11401
|
+
return (jsxs(Fragment, { children: [categoryGrid.map((at, index) => jsx("line", { className: styles$2.grid, x1: left, x2: right, y1: at, y2: at }, `cg${index}`)), scale.ticks.map(tick => (jsxs("g", { children: [chart.ValueAxisGridLines !== false && jsx("line", { className: styles$2.grid, x1: x(tick), x2: x(tick), y1: top, y2: bottom }), jsx("text", { className: styles$2.tick, x: x(tick), y: bottom + fontPx * 1.1, textAnchor: "middle", fontSize: fontPx, children: tickLabel(tick) })] }, tick))), jsx("line", { className: styles$2.axis, x1: baseline, x2: baseline, y1: top, y2: bottom }), jsx("line", { className: styles$2.axis, x1: left, x2: right, y1: bottom, y2: bottom }), serverLabels
|
|
11402
|
+
? serverLabels.map((label, index) => (jsxs("g", { children: [jsx("line", { className: styles$2.axis, x1: left - fontPx * 0.3, x2: left, y1: label.at, y2: label.at }), 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}`)))
|
|
11403
|
+
: chart.Categories.map((label, index) => (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) => {
|
|
11404
|
+
if (value === null || !Number.isFinite(value))
|
|
11405
|
+
return null;
|
|
11406
|
+
const barY = top + band * category + (band - groupHeight) / 2 + barHeight * index;
|
|
11407
|
+
const valueX = x(value);
|
|
11408
|
+
const content = tooltipContent(series.Name, chart.Categories[category] ?? "", formatChartValue(value));
|
|
11409
|
+
return (jsxs("g", { children: [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 && (jsx("text", { x: Math.max(valueX, baseline) + fontPx * 0.3, y: barY + barHeight / 2, dominantBaseline: "central", fontSize: fontPx, children: formatChartValue(value) }))] }, `${index}-${category}`));
|
|
11410
|
+
}))] }));
|
|
11411
|
+
}
|
|
11412
|
+
/**
|
|
11413
|
+
* A report chart over categories: Line (Smooth / Stepped), Area, Column and Scatter series share one
|
|
11414
|
+
* value axis; a chart made only of Bar series is drawn horizontally. Scatter series draw only their
|
|
11415
|
+
* markers, line series draw markers too when the report sets a marker type. Null values leave gaps.
|
|
11416
|
+
* The legend sits where the RDL legend Position says. Own SVG, no chart library; hover shows
|
|
11417
|
+
* category, series and value.
|
|
11418
|
+
*/
|
|
11419
|
+
function CwReportLineChart({ chart, width, height, zoom = 1, culture }) {
|
|
11420
|
+
const fontPx = chartFontPx(zoom);
|
|
11421
|
+
const palette = chartPalette(chart.Palette);
|
|
11422
|
+
const colors = chart.Series.map((series, index) => seriesColor(series, index, palette));
|
|
11423
|
+
const titleHeight = titlesHeight(chart.Titles, fontPx);
|
|
11424
|
+
// The legend on the side its RDL Position names: a side legend is a column at most 30% wide.
|
|
11425
|
+
const placement = legendPlacement(chart.LegendPosition);
|
|
11426
|
+
const sideLegend = placement.edge === "left" || placement.edge === "right";
|
|
11427
|
+
const legend = layoutLegend(chart.LegendHidden ? [] : chart.Series.map((series, index) => ({
|
|
11428
|
+
label: series.Name,
|
|
11429
|
+
color: colors[index],
|
|
11430
|
+
marker: seriesMarker(series.MarkerType, seriesKind(series) === "scatter", index)
|
|
11431
|
+
})), sideLegend ? width * 0.3 : width, fontPx, sideLegend ? "start" : placement.align);
|
|
11432
|
+
const legendHeight = legend.height > 0 ? legend.height + fontPx * 0.5 : 0;
|
|
11433
|
+
const legendWidth = legend.width > 0 ? legend.width + fontPx : 0;
|
|
11434
|
+
const legendSpace = {
|
|
11435
|
+
top: placement.edge === "top" ? legendHeight : 0,
|
|
11436
|
+
bottom: placement.edge === "bottom" ? legendHeight : 0,
|
|
11437
|
+
left: placement.edge === "left" ? legendWidth : 0,
|
|
11438
|
+
right: placement.edge === "right" ? legendWidth : 0
|
|
11439
|
+
};
|
|
11440
|
+
const message = chart.Error
|
|
11441
|
+
?? (chart.Categories.length === 0 || chart.Series.length === 0 ? (chart.NoRowsMessage ?? "No data") : null);
|
|
11442
|
+
if (message) {
|
|
11443
|
+
return (jsxs("svg", { className: styles$2.chart, width: width, height: height, role: "img", "aria-label": chart.Titles.join(" ") || chart.Name, children: [jsx(ChartTitles, { titles: chart.Titles, width: width, fontPx: fontPx }), jsx(ChartMessage, { text: message, width: width, height: height, fontPx: fontPx })] }));
|
|
11444
|
+
}
|
|
11445
|
+
const horizontal = chart.Series.every(series => seriesKind(series) === "bar");
|
|
11446
|
+
const scale = computeValueScale(chart.Series.flatMap(series => series.Values));
|
|
11447
|
+
// The axis <Format> with the report culture, as the server's PDF prints it; an unsupported format keeps the compact default.
|
|
11448
|
+
const tickLabel = (tick) => formatRdlNumber(tick, chart.ValueAxisFormat, culture) ?? formatChartValue(tick);
|
|
11449
|
+
const tickWidth = Math.max(...scale.ticks.map(tick => estimateTextWidth(tickLabel(tick), fontPx)));
|
|
11450
|
+
const categoryWidth = Math.min(width * 0.35, Math.max(...chart.Categories.map(label => estimateTextWidth(label, fontPx)), 0));
|
|
11451
|
+
const lineHeight = fontPx * 1.6;
|
|
11452
|
+
const valueTitleSpace = chart.ValueAxisTitle ? lineHeight : 0;
|
|
11453
|
+
const categoryTitleSpace = chart.CategoryAxisTitle ? lineHeight : 0;
|
|
11454
|
+
const left = legendSpace.left + (horizontal ? categoryTitleSpace + categoryWidth : valueTitleSpace + tickWidth) + fontPx * 0.8;
|
|
11455
|
+
const right = width - fontPx - legendSpace.right;
|
|
11456
|
+
const top = titleHeight + legendSpace.top + fontPx * 0.8;
|
|
11457
|
+
const bottom = height - legendSpace.bottom - lineHeight - (horizontal ? valueTitleSpace : categoryTitleSpace);
|
|
11458
|
+
const strokeWidth = Math.max(1, 1.5 * zoom);
|
|
11459
|
+
const plotProps = { chart, colors, scale, fontPx, left, top, right, bottom, strokeWidth, zoom, tickLabel };
|
|
11460
|
+
const verticalTitle = horizontal ? chart.CategoryAxisTitle : chart.ValueAxisTitle;
|
|
11461
|
+
const horizontalTitle = horizontal ? chart.ValueAxisTitle : chart.CategoryAxisTitle;
|
|
11462
|
+
const titleX = legendSpace.left + fontPx;
|
|
11463
|
+
const legendLeft = placement.edge === "left" ? fontPx * 0.4 : placement.edge === "right" ? width - legendWidth + fontPx * 0.4 : 0;
|
|
11464
|
+
const legendTop = (() => {
|
|
11465
|
+
switch (placement.edge) {
|
|
11466
|
+
case "top": return titleHeight;
|
|
11467
|
+
case "bottom": return height - legend.height;
|
|
11468
|
+
default:
|
|
11469
|
+
if (placement.align === "start")
|
|
11470
|
+
return top;
|
|
11471
|
+
if (placement.align === "end")
|
|
11472
|
+
return Math.max(top, bottom - legend.height);
|
|
11473
|
+
return Math.max(top, (top + bottom - legend.height) / 2);
|
|
11474
|
+
}
|
|
11475
|
+
})();
|
|
11476
|
+
return (jsxs("svg", { className: styles$2.chart, width: width, height: height, role: "img", "aria-label": chart.Titles.join(" ") || chart.Name, children: [jsx(ChartTitles, { titles: chart.Titles, width: width, fontPx: fontPx }), right - left > 10 && bottom - top > 10 && (horizontal ? jsx(HorizontalPlot, { ...plotProps }) : jsx(VerticalPlot, { ...plotProps })), verticalTitle && (jsx("text", { x: titleX, y: (top + bottom) / 2, textAnchor: "middle", dominantBaseline: "central", fontSize: fontPx, transform: `rotate(-90 ${titleX} ${(top + bottom) / 2})`, children: verticalTitle })), horizontalTitle && (jsx("text", { x: (left + right) / 2, y: bottom + lineHeight * 1.6, textAnchor: "middle", fontSize: fontPx, children: horizontalTitle })), jsx(ChartLegend, { layout: legend, top: legendTop, left: legendLeft, fontPx: fontPx })] }));
|
|
11477
|
+
}
|
|
11478
|
+
|
|
11479
|
+
/** A pie of the chart's first series over its categories (RDL series Type "Shape"). Own SVG. */
|
|
11480
|
+
function CwReportPieChart({ chart, width, height, zoom = 1 }) {
|
|
11481
|
+
const tooltip = useChartTooltip();
|
|
11482
|
+
const fontPx = chartFontPx(zoom);
|
|
11483
|
+
const palette = chartPalette(chart.Palette);
|
|
11484
|
+
const series = chart.Series[0];
|
|
11485
|
+
const colorOf = (index) => palette[index % palette.length];
|
|
11486
|
+
const titleHeight = titlesHeight(chart.Titles, fontPx);
|
|
11487
|
+
const legend = layoutLegend(chart.LegendHidden ? [] : chart.Categories.map((label, index) => ({ label, color: colorOf(index) })), width, fontPx);
|
|
11488
|
+
const legendHeight = legend.height > 0 ? legend.height + fontPx * 0.5 : 0;
|
|
11489
|
+
const slices = series ? pieSlices(series.Values) : [];
|
|
11490
|
+
const message = chart.Error ?? (slices.length === 0 ? (chart.NoRowsMessage ?? "No data") : null);
|
|
11491
|
+
const plotTop = titleHeight + fontPx * 0.5;
|
|
11492
|
+
const plotHeight = height - plotTop - legendHeight;
|
|
11493
|
+
const radius = Math.max(0, Math.min(width, plotHeight) / 2 - fontPx * 0.5);
|
|
11494
|
+
const cx = width / 2;
|
|
11495
|
+
const cy = plotTop + plotHeight / 2;
|
|
11496
|
+
return (jsxs("svg", { className: styles$2.chart, width: width, height: height, role: "img", "aria-label": chart.Titles.join(" ") || chart.Name, children: [jsx(ChartTitles, { titles: chart.Titles, width: width, fontPx: fontPx }), message ? (jsx(ChartMessage, { text: message, width: width, height: height, fontPx: fontPx })) : (slices.map(slice => {
|
|
11497
|
+
const category = chart.Categories[slice.index] ?? "";
|
|
11498
|
+
const percent = `${formatChartValue(slice.fraction * 100)}%`;
|
|
11499
|
+
const labelAt = polar(cx, cy, radius * 0.65, (slice.startAngle + slice.endAngle) / 2);
|
|
11500
|
+
return (jsxs("g", { children: [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 && (jsx("text", { x: labelAt.x, y: labelAt.y, textAnchor: "middle", dominantBaseline: "central", fontSize: fontPx, pointerEvents: "none", children: formatChartValue(slice.value) }))] }, slice.index));
|
|
11501
|
+
})), jsx(ChartLegend, { layout: legend, top: height - legend.height, fontPx: fontPx })] }));
|
|
11502
|
+
}
|
|
11503
|
+
|
|
11504
|
+
/** Indent of one list level, in points. */
|
|
11505
|
+
const RDL_LIST_INDENT_PT = 18;
|
|
11506
|
+
const BULLETS = ["•", "◦", "▪"];
|
|
11507
|
+
function listKind(paragraph) {
|
|
11508
|
+
switch ((paragraph.ListStyle ?? "").toLowerCase()) {
|
|
11509
|
+
case "bulleted": return "bulleted";
|
|
11510
|
+
case "numbered": return "numbered";
|
|
11511
|
+
default: return null;
|
|
11512
|
+
}
|
|
11513
|
+
}
|
|
11514
|
+
/**
|
|
11515
|
+
* The list marker of every paragraph (null = no list). Numbering counts consecutive numbered
|
|
11516
|
+
* paragraphs of the same level: a non-list paragraph resets every level, a paragraph at a level
|
|
11517
|
+
* resets the levels below it, and a bulleted paragraph resets the numbering of its own level.
|
|
11518
|
+
*/
|
|
11519
|
+
function computeListMarkers(paragraphs) {
|
|
11520
|
+
const counters = [];
|
|
11521
|
+
return paragraphs.map(paragraph => {
|
|
11522
|
+
const kind = listKind(paragraph);
|
|
11523
|
+
if (!kind) {
|
|
11524
|
+
counters.length = 0;
|
|
11525
|
+
return null;
|
|
11526
|
+
}
|
|
11527
|
+
const level = Math.max(1, paragraph.ListLevel);
|
|
11528
|
+
counters.length = Math.min(counters.length, level);
|
|
11529
|
+
if (kind === "bulleted") {
|
|
11530
|
+
counters[level - 1] = 0;
|
|
11531
|
+
return BULLETS[(level - 1) % BULLETS.length];
|
|
11532
|
+
}
|
|
11533
|
+
counters[level - 1] = (counters[level - 1] ?? 0) + 1;
|
|
11534
|
+
return `${counters[level - 1]}.`;
|
|
11535
|
+
});
|
|
11536
|
+
}
|
|
11537
|
+
/** Left indent of a paragraph in points: one list indent per level (a list paragraph has at least level 1). */
|
|
11538
|
+
function paragraphIndentPt(paragraph) {
|
|
11539
|
+
const level = listKind(paragraph) ? Math.max(1, paragraph.ListLevel) : Math.max(0, paragraph.ListLevel);
|
|
11540
|
+
return level * RDL_LIST_INDENT_PT;
|
|
11541
|
+
}
|
|
11542
|
+
|
|
11543
|
+
const EMPTY_FLIPPED = new Set();
|
|
11544
|
+
const EMPTY_HIGHLIGHTS = new Map();
|
|
11545
|
+
const ReportRenderContext = createContext({
|
|
11546
|
+
zoom: 1,
|
|
11547
|
+
flipped: EMPTY_FLIPPED,
|
|
11548
|
+
highlights: EMPTY_HIGHLIGHTS,
|
|
11549
|
+
activeMatchIndex: -1
|
|
11550
|
+
});
|
|
11551
|
+
function useReportRender() {
|
|
11552
|
+
return useContext(ReportRenderContext);
|
|
11553
|
+
}
|
|
11554
|
+
/** Groups the page's matches by run key. */
|
|
11555
|
+
function highlightsByRun(matches) {
|
|
11556
|
+
const map = new Map();
|
|
11557
|
+
matches.forEach((match, matchIndex) => {
|
|
11558
|
+
const list = map.get(match.key) ?? [];
|
|
11559
|
+
list.push({ start: match.start, length: match.length, matchIndex });
|
|
11560
|
+
map.set(match.key, list);
|
|
11561
|
+
});
|
|
11562
|
+
return map;
|
|
11563
|
+
}
|
|
11564
|
+
|
|
11565
|
+
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"};
|
|
11566
|
+
|
|
11567
|
+
/** Size of the drilldown / sort icons, in points: a little below the default 10pt text. */
|
|
11568
|
+
const INLINE_ICON_PT = 9;
|
|
11569
|
+
const SAFE_LINK = /^(https?:|mailto:)/i;
|
|
11570
|
+
function highlightedText(text, highlights, activeMatchIndex) {
|
|
11571
|
+
if (!highlights || highlights.length === 0)
|
|
11572
|
+
return text;
|
|
11573
|
+
const parts = [];
|
|
11574
|
+
let cursor = 0;
|
|
11575
|
+
for (const highlight of highlights) {
|
|
11576
|
+
if (highlight.start > cursor)
|
|
11577
|
+
parts.push(text.slice(cursor, highlight.start));
|
|
11578
|
+
const active = highlight.matchIndex === activeMatchIndex;
|
|
11579
|
+
parts.push(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));
|
|
11580
|
+
cursor = highlight.start + highlight.length;
|
|
11581
|
+
}
|
|
11582
|
+
if (cursor < text.length)
|
|
11583
|
+
parts.push(text.slice(cursor));
|
|
11584
|
+
return parts;
|
|
11585
|
+
}
|
|
11586
|
+
function ReportRun({ run, runKey, paragraphStyle, textboxStyle }) {
|
|
11587
|
+
const { zoom, highlights, activeMatchIndex } = useReportRender();
|
|
11588
|
+
const css = rdlTextCss(resolveRdlTextStyle(run.Style, paragraphStyle, textboxStyle), zoom);
|
|
11589
|
+
const content = highlightedText(run.Text, highlights.get(runKey), activeMatchIndex);
|
|
11590
|
+
if (run.Href && SAFE_LINK.test(run.Href)) {
|
|
11591
|
+
return (jsx("a", { className: styles$1.link, href: run.Href, target: "_blank", rel: "noopener noreferrer", style: css, children: content }));
|
|
11592
|
+
}
|
|
11593
|
+
return jsx("span", { style: css, children: content });
|
|
11594
|
+
}
|
|
11595
|
+
function iconSizeStyle(zoom) {
|
|
11596
|
+
return { "--btn-size": `${ptToPx(INLINE_ICON_PT, zoom)}px` };
|
|
11597
|
+
}
|
|
11598
|
+
function sortIcon(direction) {
|
|
11599
|
+
switch ((direction ?? "").toLowerCase()) {
|
|
11600
|
+
case "ascending": return "sort-asc";
|
|
11601
|
+
case "descending": return "sort-desc";
|
|
11602
|
+
default: return "sortable";
|
|
11603
|
+
}
|
|
11604
|
+
}
|
|
11605
|
+
/** A textbox: paragraphs as blocks, runs as inline spans, plain text only (never HTML). */
|
|
11606
|
+
function ReportTextbox({ textbox, itemKey, frameStyle }) {
|
|
11607
|
+
const { zoom, flipped, onToggle, onSort } = useReportRender();
|
|
11608
|
+
const markers = computeListMarkers(textbox.Paragraphs);
|
|
11609
|
+
const paragraphs = textbox.Paragraphs.map((paragraph, p) => {
|
|
11610
|
+
const indent = ptToPx(paragraphIndentPt(paragraph), zoom);
|
|
11611
|
+
const marker = markers[p];
|
|
11612
|
+
const firstRun = paragraph.Runs[0];
|
|
11613
|
+
const paragraphCss = {
|
|
11614
|
+
...rdlTextCss(resolveParagraphTextStyle(paragraph.Runs.map(run => run.Style), paragraph.Style, textbox.Style), zoom),
|
|
11615
|
+
textAlign: rdlTextAlign(paragraph.Style?.TextAlign ?? textbox.Style?.TextAlign),
|
|
11616
|
+
paddingLeft: indent || undefined
|
|
11617
|
+
};
|
|
11618
|
+
return (jsxs("p", { className: styles$1.paragraph, style: paragraphCss, children: [marker !== null && (jsx("span", { className: styles$1.marker, style: {
|
|
11619
|
+
...rdlTextCss(resolveRdlTextStyle(firstRun?.Style, paragraph.Style, textbox.Style), zoom),
|
|
11620
|
+
left: indent - ptToPx(RDL_LIST_INDENT_PT, zoom),
|
|
11621
|
+
width: ptToPx(RDL_LIST_INDENT_PT, zoom)
|
|
11622
|
+
}, children: marker })), paragraph.Runs.map((run, r) => (jsx(ReportRun, { run: run, runKey: rdlRunKey(itemKey, p, r), paragraphStyle: paragraph.Style, textboxStyle: textbox.Style }, r)))] }, p));
|
|
11623
|
+
});
|
|
11624
|
+
const boxCss = {
|
|
11625
|
+
...frameStyle,
|
|
11626
|
+
...rdlBoxStyle(textbox.Style, zoom),
|
|
11627
|
+
color: rdlColor(textbox.Style?.Color),
|
|
11628
|
+
justifyContent: rdlVerticalAlign(textbox.Style?.VerticalAlign)
|
|
11629
|
+
};
|
|
11630
|
+
const toggleId = textbox.ToggleId;
|
|
11631
|
+
const sortId = textbox.SortId;
|
|
11632
|
+
if (!toggleId && !sortId) {
|
|
11633
|
+
return jsx("div", { className: styles$1.textbox, style: boxCss, children: paragraphs });
|
|
11634
|
+
}
|
|
11635
|
+
const expanded = isToggleExpanded(textbox, flipped);
|
|
11636
|
+
return (jsx("div", { className: styles$1.textbox, style: boxCss, children: jsxs("div", { className: styles$1.textRow, children: [toggleId && (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) })), jsx("div", { className: styles$1.textBody, children: paragraphs }), sortId && (jsx(CwButton, { variant: "icon", size: "small", icon: sortIcon(textbox.SortDirection), className: styles$1.inlineButton, style: iconSizeStyle(zoom), "aria-label": "Sort", onClick: () => onSort?.(sortId) }))] }) }));
|
|
11637
|
+
}
|
|
11638
|
+
|
|
11639
|
+
function frameOf(item) {
|
|
11640
|
+
return { top: item.Top, left: item.Left, width: item.Width, height: item.Height };
|
|
11641
|
+
}
|
|
11642
|
+
function frameCss(frame, zoom) {
|
|
11643
|
+
return {
|
|
11644
|
+
top: ptToPx(frame.top, zoom),
|
|
11645
|
+
left: ptToPx(frame.left, zoom),
|
|
11646
|
+
width: ptToPx(frame.width, zoom),
|
|
11647
|
+
height: ptToPx(frame.height, zoom)
|
|
11648
|
+
};
|
|
11649
|
+
}
|
|
11650
|
+
const SAFE_IMAGE_URL = /^https?:/i;
|
|
11651
|
+
function ReportRectangle({ item, itemKey, frame }) {
|
|
11652
|
+
const { zoom } = useReportRender();
|
|
11653
|
+
return (jsx("div", { className: styles$1.rectangle, style: { ...frameCss(frame, zoom), ...rdlBoxStyle(item.Style, zoom) }, children: jsx(ReportItems, { items: item.Items, parentKey: itemKey }) }));
|
|
11654
|
+
}
|
|
11655
|
+
function ReportLine({ item, frame }) {
|
|
11656
|
+
const { zoom } = useReportRender();
|
|
11657
|
+
const stroke = rdlLineStroke(item.Style, zoom);
|
|
11658
|
+
const x1 = ptToPx(frame.left, zoom);
|
|
11659
|
+
const y1 = ptToPx(frame.top, zoom);
|
|
11660
|
+
const x2 = ptToPx(frame.left + frame.width, zoom);
|
|
11661
|
+
const y2 = ptToPx(frame.top + frame.height, zoom);
|
|
11662
|
+
const pad = stroke.width;
|
|
11663
|
+
const left = Math.min(x1, x2) - pad;
|
|
11664
|
+
const top = Math.min(y1, y2) - pad;
|
|
11665
|
+
const width = Math.abs(x2 - x1) + pad * 2;
|
|
11666
|
+
const height = Math.abs(y2 - y1) + pad * 2;
|
|
11667
|
+
return (jsx("svg", { className: styles$1.line, style: { left, top, width, height }, width: width, height: height, "aria-hidden": "true", children: jsx("line", { x1: x1 - left, y1: y1 - top, x2: x2 - left, y2: y2 - top, stroke: stroke.color, strokeWidth: stroke.width, strokeDasharray: stroke.dash }) }));
|
|
11668
|
+
}
|
|
11669
|
+
function imageSizing(sizing) {
|
|
11670
|
+
switch (sizing.toLowerCase()) {
|
|
11671
|
+
case "fit": return "fit";
|
|
11672
|
+
case "fitproportional": return "proportional";
|
|
11673
|
+
default: return "natural";
|
|
11674
|
+
}
|
|
11675
|
+
}
|
|
11676
|
+
function ReportImage({ item, frame }) {
|
|
11677
|
+
const { zoom, getImageUrl } = useReportRender();
|
|
11678
|
+
const src = item.Url && SAFE_IMAGE_URL.test(item.Url)
|
|
11679
|
+
? item.Url
|
|
11680
|
+
: item.ImageId && getImageUrl ? getImageUrl(item.ImageId) : null;
|
|
11681
|
+
const sizing = imageSizing(item.Sizing);
|
|
11682
|
+
return (jsx("div", { className: styles$1.image, style: { ...frameCss(frame, zoom), ...rdlBoxStyle(item.Style, zoom) }, children: src && (jsx("img", { src: src, alt: item.Name, referrerPolicy: "no-referrer", "data-sizing": sizing, style: sizing === "natural" ? { transform: `scale(${zoom})` } : undefined })) }));
|
|
11683
|
+
}
|
|
11684
|
+
function ReportTable({ item, itemKey, frame }) {
|
|
11685
|
+
const { zoom, flipped } = useReportRender();
|
|
11686
|
+
const message = item.Error ?? (item.Rows.length === 0 ? item.NoRowsMessage : null);
|
|
11687
|
+
if (message) {
|
|
11688
|
+
const textCss = rdlTextCss(resolveRdlTextStyle(null, null, item.Style), zoom);
|
|
11689
|
+
return (jsx("div", { className: styles$1.table, style: { ...frameCss(frame, zoom), height: undefined, ...rdlBoxStyle(item.Style, zoom) }, children: item.Error
|
|
11690
|
+
? jsx(CwNote, { color: "danger", variant: "soft", fontSize: "small", content: item.Error })
|
|
11691
|
+
: jsx("p", { className: `${styles$1.tableMessage} cw-margin-none`, style: textCss, children: message }) }));
|
|
11692
|
+
}
|
|
11693
|
+
const layout = layoutRdlTable(item, flipped);
|
|
11694
|
+
return (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 => {
|
|
11695
|
+
const content = box.cell.Content;
|
|
11696
|
+
if (!content)
|
|
11697
|
+
return null;
|
|
11698
|
+
const cellFrame = { top: box.top, left: box.left, width: box.width, height: box.height };
|
|
11699
|
+
const key = `${box.rowIndex}-${box.cellIndex}`;
|
|
11700
|
+
if (box.hidden) {
|
|
11701
|
+
// Drawn empty: the cell keeps its borders and background so the grid stays closed.
|
|
11702
|
+
return jsx("div", { className: styles$1.item, style: { ...frameCss(cellFrame, zoom), ...rdlBoxStyle(content.Style, zoom) } }, key);
|
|
11703
|
+
}
|
|
11704
|
+
return jsx(ReportItem, { item: content, itemKey: rdlCellKey(itemKey, box.rowIndex, box.cellIndex), frame: cellFrame }, key);
|
|
11705
|
+
}) }));
|
|
11706
|
+
}
|
|
11707
|
+
function ReportChart({ item, frame }) {
|
|
11708
|
+
const { zoom, culture } = useReportRender();
|
|
11709
|
+
const width = ptToPx(frame.width, zoom);
|
|
11710
|
+
const height = ptToPx(frame.height, zoom);
|
|
11711
|
+
const pie = item.Series[0]?.Type.toLowerCase() === "shape";
|
|
11712
|
+
return (jsx("div", { className: styles$1.chart, style: { ...frameCss(frame, zoom), ...rdlBoxStyle(item.Style, zoom) }, children: pie
|
|
11713
|
+
? jsx(CwReportPieChart, { chart: item, width: width, height: height, zoom: zoom })
|
|
11714
|
+
: jsx(CwReportLineChart, { chart: item, width: width, height: height, zoom: zoom, culture: culture }) }));
|
|
11715
|
+
}
|
|
11716
|
+
function ReportGauge({ item, frame }) {
|
|
11717
|
+
const { zoom } = useReportRender();
|
|
11718
|
+
return (jsx("div", { className: styles$1.chart, style: { ...frameCss(frame, zoom), ...rdlBoxStyle(item.Style, zoom) }, children: jsx(CwReportGauge, { panel: item, width: ptToPx(frame.width, zoom), height: ptToPx(frame.height, zoom), zoom: zoom }) }));
|
|
11719
|
+
}
|
|
11720
|
+
function ReportPlaceholder({ item, frame }) {
|
|
11721
|
+
const { zoom } = useReportRender();
|
|
11722
|
+
return (jsx(CwTooltipNew, { content: `${item.Element}: ${item.Reason}`, position: "top", children: jsx("div", { className: styles$1.placeholder, style: frameCss(frame, zoom), "aria-label": `${item.Element}: ${item.Reason}`, children: item.Element }) }));
|
|
11723
|
+
}
|
|
11724
|
+
/** One render-tree item, absolutely positioned in its container; nothing when its toggles hide it. */
|
|
11725
|
+
function ReportItem({ item, itemKey, frame }) {
|
|
11726
|
+
const { zoom, flipped } = useReportRender();
|
|
11727
|
+
if (!isRdlVisible(item.Toggles, flipped))
|
|
11728
|
+
return null;
|
|
11729
|
+
const box = frame ?? frameOf(item);
|
|
11730
|
+
switch (item.Kind) {
|
|
11731
|
+
case "textbox": return jsx(ReportTextbox, { textbox: item, itemKey: itemKey, frameStyle: frameCss(box, zoom) });
|
|
11732
|
+
case "rectangle": return jsx(ReportRectangle, { item: item, itemKey: itemKey, frame: box });
|
|
11733
|
+
case "line": return jsx(ReportLine, { item: item, frame: box });
|
|
11734
|
+
case "image": return jsx(ReportImage, { item: item, frame: box });
|
|
11735
|
+
case "table": return jsx(ReportTable, { item: item, itemKey: itemKey, frame: box });
|
|
11736
|
+
case "chart": return jsx(ReportChart, { item: item, frame: box });
|
|
11737
|
+
case "gauge": return jsx(ReportGauge, { item: item, frame: box });
|
|
11738
|
+
case "placeholder": return jsx(ReportPlaceholder, { item: item, frame: box });
|
|
11739
|
+
default: return null;
|
|
11740
|
+
}
|
|
11741
|
+
}
|
|
11742
|
+
function ReportItems({ items, parentKey }) {
|
|
11743
|
+
return (jsx(Fragment, { children: items.map((item, index) => (jsx(ReportItem, { item: item, itemKey: rdlItemKey(parentKey, index) }, index))) }));
|
|
11744
|
+
}
|
|
11745
|
+
|
|
11746
|
+
const NO_FLIPS$1 = new Set();
|
|
11747
|
+
const NO_MATCHES = [];
|
|
11748
|
+
const DEFAULT_MARGINS = {
|
|
11749
|
+
top: DEFAULT_PAGE_MARGIN_PT,
|
|
11750
|
+
right: DEFAULT_PAGE_MARGIN_PT,
|
|
11751
|
+
bottom: DEFAULT_PAGE_MARGIN_PT,
|
|
11752
|
+
left: DEFAULT_PAGE_MARGIN_PT
|
|
11753
|
+
};
|
|
11754
|
+
function Band({ section, sectionKey, zoom, bodyWidth, as: Element }) {
|
|
11755
|
+
return (jsx(Element, { className: styles$1.section, style: { ...rdlBoxStyle(section.Style, zoom), width: ptToPx(bodyWidth, zoom), height: ptToPx(section.Height, zoom) }, children: jsx(ReportItems, { items: section.Items, parentKey: sectionKey }) }));
|
|
11756
|
+
}
|
|
11757
|
+
/**
|
|
11758
|
+
* One page of a rendered RDL report drawn as a sheet of paper: page header, body and page footer,
|
|
11759
|
+
* every item absolutely positioned in points scaled by the zoom. Presentational: toggle state,
|
|
11760
|
+
* sorting, images and find matches come from the host.
|
|
11761
|
+
*/
|
|
11762
|
+
function CwReportPage({ page, bodyWidth, zoom = 1, culture, margins = DEFAULT_MARGINS, flippedToggles = NO_FLIPS$1, onToggle, onSort, getImageUrl, findMatches = NO_MATCHES, activeMatchIndex = -1, className }) {
|
|
11763
|
+
const sheetRef = useRef(null);
|
|
11764
|
+
const highlights = useMemo(() => highlightsByRun(findMatches), [findMatches]);
|
|
11765
|
+
const context = useMemo(() => ({
|
|
11766
|
+
zoom,
|
|
11767
|
+
culture,
|
|
11768
|
+
flipped: flippedToggles,
|
|
11769
|
+
onToggle,
|
|
11770
|
+
onSort,
|
|
11771
|
+
getImageUrl,
|
|
11772
|
+
highlights,
|
|
11773
|
+
activeMatchIndex
|
|
11774
|
+
}), [zoom, culture, flippedToggles, onToggle, onSort, getImageUrl, highlights, activeMatchIndex]);
|
|
11775
|
+
useEffect(() => {
|
|
11776
|
+
if (activeMatchIndex < 0)
|
|
11777
|
+
return;
|
|
11778
|
+
const active = sheetRef.current?.querySelector("[data-active-match='true']");
|
|
11779
|
+
active?.scrollIntoView?.({ block: "center", inline: "nearest" });
|
|
11780
|
+
}, [activeMatchIndex, findMatches]);
|
|
11781
|
+
const sheet = pageSheetSizePt(page, bodyWidth, margins);
|
|
11782
|
+
return (jsx(ReportRenderContext.Provider, { value: context, children: jsxs("div", { ref: sheetRef, className: `${styles$1.sheet}${className ? ` ${className}` : ""}`, style: {
|
|
11783
|
+
width: ptToPx(sheet.width, zoom),
|
|
11784
|
+
height: ptToPx(sheet.height, zoom),
|
|
11785
|
+
padding: `${ptToPx(margins.top, zoom)}px ${ptToPx(margins.right, zoom)}px ${ptToPx(margins.bottom, zoom)}px ${ptToPx(margins.left, zoom)}px`
|
|
11786
|
+
}, "data-page": page.Number, children: [page.Header && jsx(Band, { section: page.Header, sectionKey: RDL_SECTION_KEYS.header, zoom: zoom, bodyWidth: bodyWidth, as: "header" }), jsx("div", { className: styles$1.section, style: { width: ptToPx(bodyWidth, zoom), height: ptToPx(page.Height, zoom) }, children: jsx(ReportItems, { items: page.Items, parentKey: RDL_SECTION_KEYS.body }) }), page.Footer && jsx(Band, { section: page.Footer, sectionKey: RDL_SECTION_KEYS.footer, zoom: zoom, bodyWidth: bodyWidth, as: "footer" })] }) }));
|
|
11787
|
+
}
|
|
11788
|
+
|
|
11789
|
+
const DATE_PREFIX = /^(\d{4}-\d{2}-\d{2})(?:[T ](\d{2}):(\d{2})(?::(\d{2}))?)?/;
|
|
11790
|
+
/** 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. */
|
|
11791
|
+
function parseRdlDateTime(text) {
|
|
11792
|
+
const match = DATE_PREFIX.exec(text.trim());
|
|
11793
|
+
if (!match)
|
|
11794
|
+
return null;
|
|
11795
|
+
try {
|
|
11796
|
+
return Temporal.PlainDateTime.from(`${match[1]}T${match[2] ?? "00"}:${match[3] ?? "00"}:${match[4] ?? "00"}`);
|
|
11797
|
+
}
|
|
11798
|
+
catch {
|
|
11799
|
+
return null;
|
|
11800
|
+
}
|
|
11801
|
+
}
|
|
11802
|
+
/** A date-time as invariant text "YYYY-MM-DDTHH:mm:ss". */
|
|
11803
|
+
function formatRdlDateTime(value) {
|
|
11804
|
+
return value.toString({ smallestUnit: "second", calendarName: "never" });
|
|
11805
|
+
}
|
|
11806
|
+
/** A typed API value as the text of an option or an editor (null stays null). */
|
|
11807
|
+
function rdlValueToText(value) {
|
|
11808
|
+
if (value === null || value === undefined)
|
|
11809
|
+
return null;
|
|
11810
|
+
if (typeof value === "boolean")
|
|
11811
|
+
return value ? "true" : "false";
|
|
11812
|
+
if (typeof value === "number")
|
|
11813
|
+
return numberToInvariant(value);
|
|
11814
|
+
return value;
|
|
11815
|
+
}
|
|
11816
|
+
function numberToInvariant(value) {
|
|
11817
|
+
return Number.isFinite(value) ? value.toString() : "";
|
|
11818
|
+
}
|
|
11819
|
+
function isDateTime(parameter) {
|
|
11820
|
+
return parameter.DataType.toLowerCase() === "datetime" || parameter.InputType.toLowerCase() === "datetime";
|
|
11821
|
+
}
|
|
11822
|
+
function isBoolean(parameter) {
|
|
11823
|
+
return parameter.DataType.toLowerCase() === "boolean" || parameter.InputType.toLowerCase() === "boolean";
|
|
11824
|
+
}
|
|
11825
|
+
function isTextInput(parameter) {
|
|
11826
|
+
return parameter.InputType.toLowerCase() === "textbox";
|
|
11827
|
+
}
|
|
11828
|
+
function isNumeric(parameter) {
|
|
11829
|
+
const type = parameter.DataType.toLowerCase();
|
|
11830
|
+
return type === "integer" || type === "float";
|
|
11831
|
+
}
|
|
11832
|
+
function toEditorScalar(parameter, value) {
|
|
11833
|
+
if (isDateTime(parameter) && parameter.InputType.toLowerCase() !== "dropdownlist") {
|
|
11834
|
+
const text = String(value);
|
|
11835
|
+
return parseRdlDateTime(text) ?? text;
|
|
11836
|
+
}
|
|
11837
|
+
if (isBoolean(parameter) && parameter.InputType.toLowerCase() !== "dropdownlist") {
|
|
11838
|
+
return typeof value === "boolean" ? value : String(value).toLowerCase() === "true";
|
|
11839
|
+
}
|
|
11840
|
+
return rdlValueToText(value) ?? "";
|
|
11841
|
+
}
|
|
11842
|
+
/** The editor state of one parameter from the values the server sent; a NULL value is an empty field. */
|
|
11843
|
+
function initialParameterEdit(parameter) {
|
|
11844
|
+
const scalars = (parameter.Values ?? [])
|
|
11845
|
+
.filter((value) => value !== null && value !== undefined)
|
|
11846
|
+
.map(value => toEditorScalar(parameter, value));
|
|
11847
|
+
return { values: parameter.MultiValue ? scalars : scalars.slice(0, 1) };
|
|
11848
|
+
}
|
|
11849
|
+
/** The values that count: a multi-value text drops its empty lines, a single value keeps only the first. */
|
|
11850
|
+
function enteredValues(parameter, edit) {
|
|
11851
|
+
const values = edit.values.filter(value => value !== "");
|
|
11852
|
+
return parameter.MultiValue ? values : values.slice(0, 1);
|
|
11853
|
+
}
|
|
11854
|
+
/** The editor state of a whole form. */
|
|
11855
|
+
function initialParameterEdits(parameters) {
|
|
11856
|
+
const edits = {};
|
|
11857
|
+
for (const parameter of parameters)
|
|
11858
|
+
edits[parameter.Name] = initialParameterEdit(parameter);
|
|
11859
|
+
return edits;
|
|
11860
|
+
}
|
|
11861
|
+
/** One editor value as invariant text. */
|
|
11862
|
+
function editorScalarToInvariant(parameter, value) {
|
|
11863
|
+
if (typeof value === "boolean")
|
|
11864
|
+
return value ? "true" : "false";
|
|
11865
|
+
if (typeof value !== "string")
|
|
11866
|
+
return formatRdlDateTime(value);
|
|
11867
|
+
if (isNumeric(parameter))
|
|
11868
|
+
return value.trim().replace(",", ".");
|
|
11869
|
+
return value;
|
|
11870
|
+
}
|
|
11871
|
+
/**
|
|
11872
|
+
* The values the API takes (RdlParameterValuesRequest.Values): every parameter, each as an array of
|
|
11873
|
+
* invariant text. An empty field is [null] when the parameter is nullable, [""] for a single text,
|
|
11874
|
+
* else []. A parameter without an edit sends the values the server gave it.
|
|
11875
|
+
*
|
|
11876
|
+
* A nullable boolean whose value is NULL shows neither True nor False and keeps sending NULL until
|
|
11877
|
+
* the user picks one; from then on it sends "true" / "false" (a radio group cannot be emptied again).
|
|
11878
|
+
*/
|
|
11879
|
+
function toRdlParameterValues(parameters, edits) {
|
|
11880
|
+
const result = {};
|
|
11881
|
+
for (const parameter of parameters) {
|
|
11882
|
+
const edit = edits[parameter.Name] ?? initialParameterEdit(parameter);
|
|
11883
|
+
const texts = enteredValues(parameter, edit).map(value => editorScalarToInvariant(parameter, value));
|
|
11884
|
+
if (texts.length > 0) {
|
|
11885
|
+
result[parameter.Name] = texts;
|
|
11886
|
+
}
|
|
11887
|
+
else if (parameter.Nullable) {
|
|
11888
|
+
result[parameter.Name] = [null];
|
|
11889
|
+
}
|
|
11890
|
+
else {
|
|
11891
|
+
result[parameter.Name] = !parameter.MultiValue && isTextInput(parameter) ? [""] : [];
|
|
11892
|
+
}
|
|
11893
|
+
}
|
|
11894
|
+
return result;
|
|
11895
|
+
}
|
|
11896
|
+
/** True when any other parameter lists `name` in its DependsOn: a change must re-resolve the form. */
|
|
11897
|
+
function hasDependentParameters(parameters, name) {
|
|
11898
|
+
return parameters.some(parameter => parameter.Name !== name && parameter.DependsOn.includes(name));
|
|
11899
|
+
}
|
|
11900
|
+
/** Whether the user edits the parameter (hidden and server-filled parameters are not editable). */
|
|
11901
|
+
function isEditableParameter(parameter) {
|
|
11902
|
+
return !parameter.Hidden && !parameter.ServerValue;
|
|
11903
|
+
}
|
|
11904
|
+
/** Whether the form has anything for the user to fill in (some parameter is neither hidden nor server-filled). */
|
|
11905
|
+
function hasEditableParameters(parameters) {
|
|
11906
|
+
return parameters.some(isEditableParameter);
|
|
11907
|
+
}
|
|
11908
|
+
/**
|
|
11909
|
+
* Client-side checks before "View report": a value is required unless the parameter is nullable
|
|
11910
|
+
* (empty = NULL) or allows blank text (AllowBlank), and numbers / dates must parse. Hidden and
|
|
11911
|
+
* server-filled parameters are the server's business. Returns parameter name to message.
|
|
11912
|
+
*/
|
|
11913
|
+
function validateParameterEdits(parameters, edits) {
|
|
11914
|
+
const errors = {};
|
|
11915
|
+
for (const parameter of parameters) {
|
|
11916
|
+
if (!isEditableParameter(parameter))
|
|
11917
|
+
continue;
|
|
11918
|
+
const edit = edits[parameter.Name] ?? initialParameterEdit(parameter);
|
|
11919
|
+
const values = enteredValues(parameter, edit);
|
|
11920
|
+
if (values.length === 0) {
|
|
11921
|
+
const blankAllowed = parameter.Nullable
|
|
11922
|
+
|| (parameter.AllowBlank && !parameter.MultiValue && isTextInput(parameter) && !isNumeric(parameter));
|
|
11923
|
+
if (!blankAllowed)
|
|
11924
|
+
errors[parameter.Name] = parameter.MultiValue ? "Select at least one value." : "A value is required.";
|
|
11925
|
+
continue;
|
|
11926
|
+
}
|
|
11927
|
+
if (isNumeric(parameter) && parameter.InputType.toLowerCase() !== "dropdownlist") {
|
|
11928
|
+
const integer = parameter.DataType.toLowerCase() === "integer";
|
|
11929
|
+
const invalid = values.some(value => {
|
|
11930
|
+
const text = editorScalarToInvariant(parameter, value);
|
|
11931
|
+
return integer ? !/^-?\d+$/.test(text) : !/^-?(\d+\.?\d*|\.\d+)$/.test(text);
|
|
11932
|
+
});
|
|
11933
|
+
if (invalid)
|
|
11934
|
+
errors[parameter.Name] = integer ? "Enter a whole number." : "Enter a number.";
|
|
11935
|
+
continue;
|
|
11936
|
+
}
|
|
11937
|
+
if (isDateTime(parameter) && parameter.InputType.toLowerCase() !== "dropdownlist"
|
|
11938
|
+
&& values.some(value => typeof value === "string")) {
|
|
11939
|
+
errors[parameter.Name] = "Enter a valid date.";
|
|
11940
|
+
}
|
|
11941
|
+
}
|
|
11942
|
+
return errors;
|
|
11943
|
+
}
|
|
11944
|
+
|
|
11945
|
+
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"};
|
|
11946
|
+
|
|
11947
|
+
/** Above this many options a single-select dropdown gets a search box. */
|
|
11948
|
+
const SEARCH_THRESHOLD = 10;
|
|
11949
|
+
const LABEL_LAYOUT = { flexDirection: "column" };
|
|
11950
|
+
const BOOLEAN_OPTIONS = [
|
|
11951
|
+
{ value: "true", label: "True" },
|
|
11952
|
+
{ value: "false", label: "False" }
|
|
11953
|
+
];
|
|
11954
|
+
function scalarText(value) {
|
|
11955
|
+
if (value === undefined)
|
|
11956
|
+
return "";
|
|
11957
|
+
if (typeof value === "boolean")
|
|
11958
|
+
return value ? "true" : "false";
|
|
11959
|
+
return typeof value === "string" ? value : value.toString();
|
|
11960
|
+
}
|
|
11961
|
+
function feedbackFor(parameter, clientError) {
|
|
11962
|
+
const messages = [];
|
|
11963
|
+
if (parameter.ValidValuesError)
|
|
11964
|
+
messages.push({ type: "danger", message: parameter.ValidValuesError });
|
|
11965
|
+
if (parameter.ValueError)
|
|
11966
|
+
messages.push({ type: "danger", message: parameter.ValueError });
|
|
11967
|
+
if (clientError)
|
|
11968
|
+
messages.push({ type: "danger", message: clientError });
|
|
11969
|
+
return messages.length > 0 ? messages : undefined;
|
|
11970
|
+
}
|
|
11971
|
+
function ParameterField({ parameter, edit, error, disabled, setEdit, commit }) {
|
|
11972
|
+
const label = { text: parameter.Prompt || parameter.Name };
|
|
11973
|
+
const feedback = feedbackFor(parameter, error);
|
|
11974
|
+
const testId = `report-parameter-${parameter.Name}`;
|
|
11975
|
+
// A NULL valid value is no option of its own: leaving the field empty is NULL.
|
|
11976
|
+
const options = parameter.ValidValues
|
|
11977
|
+
.filter(option => option.Value !== null && option.Value !== undefined)
|
|
11978
|
+
.map(option => ({ value: rdlValueToText(option.Value) ?? "", label: option.Label }));
|
|
11979
|
+
switch (parameter.InputType.toLowerCase()) {
|
|
11980
|
+
case "dropdownlist":
|
|
11981
|
+
if (parameter.MultiValue) {
|
|
11982
|
+
return (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 }));
|
|
11983
|
+
}
|
|
11984
|
+
return (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 }));
|
|
11985
|
+
case "datetime": {
|
|
11986
|
+
const value = edit.values[0];
|
|
11987
|
+
return (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 }));
|
|
11988
|
+
}
|
|
11989
|
+
case "boolean":
|
|
11990
|
+
return (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 }));
|
|
11991
|
+
case "integer":
|
|
11992
|
+
case "float":
|
|
11993
|
+
return (jsx(CwDigit, { "data-testid": testId, labelProps: label, alignProps: LABEL_LAYOUT, step: parameter.InputType.toLowerCase() === "integer" ? 1 : "any", value: scalarText(edit.values[0]), onChange: e => {
|
|
11994
|
+
const text = e.currentTarget.value;
|
|
11995
|
+
setEdit({ values: text === "" ? [] : [text] }, false);
|
|
11996
|
+
}, onBlur: commit, disabled: disabled, feedback: feedback }));
|
|
11997
|
+
default:
|
|
11998
|
+
if (parameter.MultiValue) {
|
|
11999
|
+
return (jsx(CwTextArea, { "data-testid": testId, labelProps: label, alignProps: LABEL_LAYOUT, value: edit.values.map(scalarText).join("\n"), onChange: e => {
|
|
12000
|
+
// Keep empty lines while typing (Enter must survive the re-render); they are dropped on serialization.
|
|
12001
|
+
setEdit({ values: e.currentTarget.value.split("\n") }, false);
|
|
12002
|
+
}, onBlur: commit, disabled: disabled, feedback: feedback ?? { type: "neutral", message: "One value per line." } }));
|
|
12003
|
+
}
|
|
12004
|
+
return (jsx(CwInputText, { "data-testid": testId, labelProps: label, alignProps: LABEL_LAYOUT, value: scalarText(edit.values[0]), onChange: e => {
|
|
12005
|
+
const text = e.currentTarget.value;
|
|
12006
|
+
setEdit({ values: [text] }, false);
|
|
12007
|
+
}, onBlur: commit, disabled: disabled, feedback: feedback }));
|
|
12008
|
+
}
|
|
12009
|
+
}
|
|
12010
|
+
/**
|
|
12011
|
+
* The report parameter form in the server's order: dropdowns (multi-value as tag selector with
|
|
12012
|
+
* select-all), date-time pickers, True/False, numbers and text. There is no NULL control: an empty
|
|
12013
|
+
* field of a nullable parameter is sent as NULL (see rdlParameterValues), as in the old WinOps
|
|
12014
|
+
* report page. Hidden parameters are not drawn; server-filled ones are read-only. Presentational:
|
|
12015
|
+
* the host owns the editor state (`useReportParameterForm`); a change to a parameter others depend
|
|
12016
|
+
* on calls its `onResolve`, "View report" its `onRender` with the values as invariant text
|
|
12017
|
+
* (`toRdlParameterValues`).
|
|
12018
|
+
*
|
|
12019
|
+
* The form fills its parent's height when the parent gives it one: the fields scroll and the
|
|
12020
|
+
* "View report" footer stays pinned at the bottom. In an unbounded parent it simply grows.
|
|
12021
|
+
*/
|
|
12022
|
+
function CwReportParameterForm({ controller, loading = false, rendering = false }) {
|
|
12023
|
+
const { form } = controller;
|
|
12024
|
+
const visible = form?.Parameters.filter(parameter => !parameter.Hidden) ?? [];
|
|
12025
|
+
return (jsxs("form", { className: styles.parameterForm, "data-testid": "report-parameter-form", onSubmit: e => {
|
|
12026
|
+
e.preventDefault();
|
|
12027
|
+
controller.submit();
|
|
12028
|
+
}, children: [jsx("div", { className: styles.parameterFields, children: form ? (jsx(CwLoading, { isLoading: loading, size: "small", children: visible.length > 0 && (jsx("div", { className: `${styles.parameterGrid} cw-grid-base-auto`, children: visible.map(parameter => (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))) })) })) : (jsxs("div", { className: `${styles.parameterGrid} cw-grid-base-auto`, "aria-busy": "true", children: [jsx(CwSkeleton, { variant: "rect", height: "3.5rem" }), jsx(CwSkeleton, { variant: "rect", height: "3.5rem" }), jsx(CwSkeleton, { variant: "rect", height: "3.5rem" })] })) }), jsx("footer", { className: styles.parameterActions, children: jsx(CwButton, { type: "submit", text: "View report", icon: "file-view", className: styles.viewButton, disabled: !form || loading || rendering, "data-testid": "report-view-button" }) })] }));
|
|
12029
|
+
}
|
|
12030
|
+
|
|
12031
|
+
const REPORT_EXPORT_LABELS = {
|
|
12032
|
+
pdf: "PDF",
|
|
12033
|
+
xlsx: "Excel",
|
|
12034
|
+
docx: "Word"
|
|
12035
|
+
};
|
|
12036
|
+
/** A render with warnings asks before exporting or printing; without warnings the action runs at once. */
|
|
12037
|
+
function needsOutputConfirmation(warnings) {
|
|
12038
|
+
return (warnings?.length ?? 0) > 0;
|
|
12039
|
+
}
|
|
12040
|
+
/** Texts of the confirmation shown before an export or print of a render with warnings. */
|
|
12041
|
+
function outputConfirmation(action, warningCount) {
|
|
12042
|
+
const count = `${warningCount} warning${warningCount === 1 ? "" : "s"}`;
|
|
12043
|
+
if (action.kind === "print") {
|
|
12044
|
+
return {
|
|
12045
|
+
headline: "Print report",
|
|
12046
|
+
message: `This report has ${count}. The printout may not match the report exactly.`,
|
|
12047
|
+
confirmText: "Print anyway"
|
|
12048
|
+
};
|
|
12049
|
+
}
|
|
12050
|
+
const label = REPORT_EXPORT_LABELS[action.format];
|
|
12051
|
+
return {
|
|
12052
|
+
headline: `Export as ${label}`,
|
|
12053
|
+
message: `This report has ${count}. The ${label} export may not match the report exactly.`,
|
|
12054
|
+
confirmText: "Export anyway"
|
|
12055
|
+
};
|
|
12056
|
+
}
|
|
12057
|
+
|
|
12058
|
+
const EXPORTS = [
|
|
12059
|
+
{ format: "pdf", label: REPORT_EXPORT_LABELS.pdf, icon: "file-pdf" },
|
|
12060
|
+
{ format: "xlsx", label: REPORT_EXPORT_LABELS.xlsx, icon: "table" },
|
|
12061
|
+
{ format: "docx", label: REPORT_EXPORT_LABELS.docx, icon: "document" }
|
|
12062
|
+
];
|
|
12063
|
+
function zoomToValue(zoom) {
|
|
12064
|
+
return typeof zoom === "number" ? String(zoom) : zoom;
|
|
12065
|
+
}
|
|
12066
|
+
function valueToZoom(value) {
|
|
12067
|
+
if (value === "pageWidth" || value === "wholePage")
|
|
12068
|
+
return value;
|
|
12069
|
+
const factor = parseFloat(value);
|
|
12070
|
+
return Number.isFinite(factor) && factor > 0 ? factor : 1;
|
|
12071
|
+
}
|
|
12072
|
+
function closePopover(element) {
|
|
12073
|
+
element.closest("[popover]")?.hidePopover();
|
|
12074
|
+
}
|
|
12075
|
+
function PageInput({ pageNumber, pageCount, onPage, disabled }) {
|
|
12076
|
+
const [draft, setDraft] = useState({ page: pageNumber, text: String(pageNumber) });
|
|
12077
|
+
const text = draft.page === pageNumber ? draft.text : String(pageNumber);
|
|
12078
|
+
const commit = () => {
|
|
12079
|
+
const parsed = parseInt(text, 10);
|
|
12080
|
+
const target = Number.isFinite(parsed) ? Math.min(Math.max(1, parsed), Math.max(1, pageCount)) : pageNumber;
|
|
12081
|
+
setDraft({ page: pageNumber, text: String(target) });
|
|
12082
|
+
if (target !== pageNumber)
|
|
12083
|
+
onPage(target);
|
|
12084
|
+
};
|
|
12085
|
+
return (jsx(CwDigit, { "data-size": "tiny", "aria-label": "Page", min: 1, max: Math.max(1, pageCount), value: text, disabled: disabled, onChange: e => {
|
|
12086
|
+
const value = e.currentTarget.value;
|
|
12087
|
+
setDraft({ page: pageNumber, text: value });
|
|
12088
|
+
}, onKeyDown: (e) => {
|
|
12089
|
+
if (e.key === "Enter")
|
|
12090
|
+
commit();
|
|
12091
|
+
}, onBlur: commit }));
|
|
12092
|
+
}
|
|
12093
|
+
/**
|
|
12094
|
+
* The report viewer toolbar, like the old ReportViewer: page navigation, refresh, back, zoom,
|
|
12095
|
+
* find on the current page, export (PDF / Excel / Word) and print. Warnings stay invisible until
|
|
12096
|
+
* there are some (Dark Cockpit). Presentational: every action is a callback.
|
|
12097
|
+
*/
|
|
12098
|
+
function CwReportToolbar({ pageNumber, pageCount, onPage, onRefresh, onBack, zoom, onZoomChange, findText, onFindTextChange, onFindNext, findMatchCount, findMatchIndex, onExport, onPrint, warnings = [], onToggleParameters, parametersVisible = true, disabled = false, reportName }) {
|
|
12099
|
+
const firstPage = disabled || pageNumber <= 1;
|
|
12100
|
+
const lastPage = disabled || pageNumber >= pageCount;
|
|
12101
|
+
const findStatus = findText.trim() === ""
|
|
12102
|
+
? ""
|
|
12103
|
+
: findMatchCount === 0 ? "No matches" : `${Math.max(0, findMatchIndex) + 1} of ${findMatchCount}`;
|
|
12104
|
+
return (jsxs("header", { className: `${styles.toolbar} cw-flex-row cw-align-left-center cw-flex-wrap cw-gap-small`, "data-testid": "report-toolbar", children: [onBack && jsx(CwButton, { variant: "icon", icon: "arrow-left", title: "Back", onClick: onBack }), onToggleParameters && (jsx(CwButton, { variant: "icon", icon: "filter-list", title: parametersVisible ? "Hide parameters" : "Show parameters", pressed: parametersVisible, onClick: onToggleParameters })), jsxs("nav", { className: "cw-flex-row cw-align-left-center cw-gap-tiny", "aria-label": "Pages", children: [jsx(CwButton, { variant: "icon", icon: "chevron-left-double", title: "First page", disabled: firstPage, onClick: () => onPage(1) }), jsx(CwButton, { variant: "icon", icon: "chevron-left", title: "Previous page", disabled: firstPage, onClick: () => onPage(pageNumber - 1) }), jsx(PageInput, { pageNumber: pageNumber, pageCount: pageCount, onPage: onPage, disabled: disabled }), jsxs("span", { className: "cw-font-size-small", children: ["of ", disabled ? "-" : pageCount] }), jsx(CwButton, { variant: "icon", icon: "chevron-right", title: "Next page", disabled: lastPage, onClick: () => onPage(pageNumber + 1) }), jsx(CwButton, { variant: "icon", icon: "chevron-right-double", title: "Last page", disabled: lastPage, onClick: () => onPage(pageCount) })] }), onRefresh && jsx(CwButton, { variant: "icon", icon: "refresh", title: "Refresh", disabled: disabled, onClick: onRefresh }), jsxs(CwSelect, { "aria-label": "Zoom", value: zoomToValue(zoom), handleChange: value => onZoomChange(valueToZoom(value)), children: [REPORT_ZOOM_STEPS.map(step => (jsx(CwOption, { value: String(step), text: `${Math.round(step * 100)}%` }, step))), jsx(CwOption, { value: "pageWidth", text: "Page width" }), jsx(CwOption, { value: "wholePage", text: "Whole page" })] }), jsxs("div", { className: "cw-flex-row cw-align-left-center cw-gap-small", children: [jsx(CwInputText, { "aria-label": "Find text", placeholder: "Find", value: findText, disabled: disabled, onChange: e => onFindTextChange(e.currentTarget.value), onKeyDown: (e) => {
|
|
12105
|
+
if (e.key === "Enter")
|
|
12106
|
+
onFindNext();
|
|
12107
|
+
}, buttonProps: {
|
|
12108
|
+
text: "Next",
|
|
12109
|
+
variant: "outline",
|
|
12110
|
+
disabled: disabled || findMatchCount === 0,
|
|
12111
|
+
onClick: onFindNext
|
|
12112
|
+
} }), jsx("span", { className: `${styles.findStatus} cw-font-size-small`, "aria-live": "polite", children: findStatus })] }), jsx("div", { className: styles.reportName, children: reportName && (jsx(CwTooltipNew, { content: reportName, position: "bottom", children: jsx("strong", { className: "cw-text-ellipsis", "data-testid": "report-name", children: reportName }) })) }), warnings.length > 0 && (jsx(CwPopoverButton, { variant: "icon", icon: "warning", title: "Warnings", badge: { count: warnings.length, color: "warning" }, className: styles.warningsButton, placement: "bottom-end", panelClassName: styles.warningsPanel, children: jsx("ul", { className: styles.warningList, children: warnings.map((warning, index) => jsx("li", { children: warning }, index)) }) })), jsx(CwPopoverButton, { icon: "download", title: "Export", variant: "icon", disabled: disabled, placement: "bottom-end", children: jsx("div", { className: styles.menu, children: EXPORTS.map(item => (jsx(CwButton, { variant: "ghost", color: "neutral", text: item.label, icon: item.icon, className: styles.menuItem, onClick: e => {
|
|
12113
|
+
closePopover(e.currentTarget);
|
|
12114
|
+
onExport(item.format);
|
|
12115
|
+
} }, item.format))) }) }), jsx(CwButton, { variant: "icon", icon: "print", title: "Print", disabled: disabled, onClick: onPrint })] }));
|
|
12116
|
+
}
|
|
12117
|
+
|
|
12118
|
+
/** Width change of one arrow key press on the sidebar resize handle, in CSS pixels. */
|
|
12119
|
+
const SIDEBAR_KEY_STEP = 16;
|
|
12120
|
+
/** Keeps a sidebar width inside [min, max]; a max below min collapses to min. */
|
|
12121
|
+
function clampSidebarWidth(width, min, max) {
|
|
12122
|
+
const upper = Math.max(min, max);
|
|
12123
|
+
if (!Number.isFinite(width))
|
|
12124
|
+
return min;
|
|
12125
|
+
return Math.min(Math.max(width, min), upper);
|
|
12126
|
+
}
|
|
12127
|
+
/**
|
|
12128
|
+
* The sidebar width after a key press on the resize handle: arrows step, Home / End jump to the
|
|
12129
|
+
* bounds. Null for keys the handle does not use, so they keep their default behaviour.
|
|
12130
|
+
*/
|
|
12131
|
+
function sidebarWidthForKey(key, current, min, max, step = SIDEBAR_KEY_STEP) {
|
|
12132
|
+
switch (key) {
|
|
12133
|
+
case "ArrowLeft":
|
|
12134
|
+
return clampSidebarWidth(current - step, min, max);
|
|
12135
|
+
case "ArrowRight":
|
|
12136
|
+
return clampSidebarWidth(current + step, min, max);
|
|
12137
|
+
case "Home":
|
|
12138
|
+
return clampSidebarWidth(min, min, max);
|
|
12139
|
+
case "End":
|
|
12140
|
+
return clampSidebarWidth(max, min, max);
|
|
12141
|
+
default:
|
|
12142
|
+
return null;
|
|
12143
|
+
}
|
|
12144
|
+
}
|
|
12145
|
+
/**
|
|
12146
|
+
* Whether the viewer shows the parameter sidebar. While the form loads for the first time it shows
|
|
12147
|
+
* the skeleton; once loaded only when the user has something to fill in. A report whose parameters
|
|
12148
|
+
* are all hidden or server-filled (or that has none) gets no sidebar.
|
|
12149
|
+
*/
|
|
12150
|
+
function showsParameterSidebar(parameters, loadingParameters) {
|
|
12151
|
+
return parameters ? hasEditableParameters(parameters.Parameters) : loadingParameters;
|
|
12152
|
+
}
|
|
12153
|
+
|
|
12154
|
+
/**
|
|
12155
|
+
* Width of a left sidebar resized by dragging the handle on its right edge, like the master panel
|
|
12156
|
+
* of CwMasterDetail. The handle captures the pointer, so the drag keeps going outside it; the
|
|
12157
|
+
* keyboard steps the width for users who cannot drag.
|
|
12158
|
+
*/
|
|
12159
|
+
function useSidebarResize(initialWidth, minWidth, maxWidth) {
|
|
12160
|
+
const [storedWidth, setStoredWidth] = useState(initialWidth);
|
|
12161
|
+
const [resizing, setResizing] = useState(false);
|
|
12162
|
+
const drag = useRef(null);
|
|
12163
|
+
const width = clampSidebarWidth(storedWidth, minWidth, maxWidth);
|
|
12164
|
+
const onPointerDown = useCallback((e) => {
|
|
12165
|
+
if (e.button !== 0)
|
|
12166
|
+
return;
|
|
12167
|
+
e.preventDefault();
|
|
12168
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
12169
|
+
drag.current = { startX: e.clientX, startWidth: width };
|
|
12170
|
+
setResizing(true);
|
|
12171
|
+
}, [width]);
|
|
12172
|
+
const onPointerMove = useCallback((e) => {
|
|
12173
|
+
const start = drag.current;
|
|
12174
|
+
if (!start)
|
|
12175
|
+
return;
|
|
12176
|
+
setStoredWidth(clampSidebarWidth(start.startWidth + e.clientX - start.startX, minWidth, maxWidth));
|
|
12177
|
+
}, [minWidth, maxWidth]);
|
|
12178
|
+
const endDrag = useCallback((e) => {
|
|
12179
|
+
if (!drag.current)
|
|
12180
|
+
return;
|
|
12181
|
+
drag.current = null;
|
|
12182
|
+
if (e.currentTarget.hasPointerCapture(e.pointerId))
|
|
12183
|
+
e.currentTarget.releasePointerCapture(e.pointerId);
|
|
12184
|
+
setResizing(false);
|
|
12185
|
+
}, []);
|
|
12186
|
+
const onKeyDown = useCallback((e) => {
|
|
12187
|
+
const next = sidebarWidthForKey(e.key, width, minWidth, maxWidth);
|
|
12188
|
+
if (next === null)
|
|
12189
|
+
return;
|
|
12190
|
+
e.preventDefault();
|
|
12191
|
+
setStoredWidth(next);
|
|
12192
|
+
}, [width, minWidth, maxWidth]);
|
|
12193
|
+
return {
|
|
12194
|
+
width,
|
|
12195
|
+
resizing,
|
|
12196
|
+
handleProps: { onPointerDown, onPointerMove, onPointerUp: endDrag, onPointerCancel: endDrag, onKeyDown }
|
|
12197
|
+
};
|
|
12198
|
+
}
|
|
12199
|
+
|
|
12200
|
+
/** Space the canvas keeps around the sheet when fitting it (its padding on both sides), in CSS pixels. */
|
|
12201
|
+
const CANVAS_GUTTER = 32;
|
|
12202
|
+
function useElementSize(ref) {
|
|
12203
|
+
const [size, setSize] = useState({ width: 0, height: 0 });
|
|
12204
|
+
useEffect(() => {
|
|
12205
|
+
const element = ref.current;
|
|
12206
|
+
if (!element || typeof ResizeObserver === "undefined")
|
|
12207
|
+
return;
|
|
12208
|
+
const observer = new ResizeObserver(() => {
|
|
12209
|
+
setSize({ width: element.clientWidth, height: element.clientHeight });
|
|
12210
|
+
});
|
|
12211
|
+
observer.observe(element);
|
|
12212
|
+
return () => observer.disconnect();
|
|
12213
|
+
}, [ref]);
|
|
12214
|
+
return size;
|
|
12215
|
+
}
|
|
12216
|
+
/**
|
|
12217
|
+
* The RDL report viewer: a resizable, collapsible parameter sidebar on the left, the toolbar and
|
|
12218
|
+
* the page on a neutral canvas on the right, like the old ReportViewer. The sidebar only exists
|
|
12219
|
+
* when the user has a parameter to fill in. Presentational: it fetches nothing and owns no report
|
|
12220
|
+
* state. The host calls `useReportViewerState` and `useReportParameterForm` and passes their results
|
|
12221
|
+
* as `state` and `parameterForm`; every request goes out through the callbacks given to those hooks
|
|
12222
|
+
* (or `onSort` / `onRefresh` / `onBack`) and comes back as their arguments.
|
|
12223
|
+
*/
|
|
12224
|
+
function CwReportViewer({ definition, state, parameterForm, loadingParameters = false, loadingRender = false, error, onSort, onRefresh, onBack, getImageUrl, sidebarWidth = 320, minSidebarWidth = 240, maxSidebarWidth = 600, resizable = true, className }) {
|
|
12225
|
+
const { result } = state;
|
|
12226
|
+
const parameters = parameterForm.form;
|
|
12227
|
+
const sidebar = useSidebarResize(sidebarWidth, minSidebarWidth, maxSidebarWidth);
|
|
12228
|
+
const canvasRef = useRef(null);
|
|
12229
|
+
const canvasSize = useElementSize(canvasRef);
|
|
12230
|
+
const hasSidebar = showsParameterSidebar(parameters, loadingParameters);
|
|
12231
|
+
const sidebarOpen = hasSidebar && state.parametersVisible;
|
|
12232
|
+
const warnings = result?.Warnings ?? [];
|
|
12233
|
+
const reportName = result?.Name || definition?.Name || null;
|
|
12234
|
+
const confirmation = state.pendingOutput ? outputConfirmation(state.pendingOutput, warnings.length) : null;
|
|
12235
|
+
/** Where the clicked toggle sat in the canvas, so the re-rendered page keeps it in the same place. */
|
|
12236
|
+
const toggleAnchorRef = useRef(null);
|
|
12237
|
+
const handleToggle = (toggleId) => {
|
|
12238
|
+
if (state.serverToggles) {
|
|
12239
|
+
const canvas = canvasRef.current;
|
|
12240
|
+
const button = canvas?.querySelector(`[data-toggle-id="${CSS.escape(toggleId)}"]`);
|
|
12241
|
+
toggleAnchorRef.current = canvas && button
|
|
12242
|
+
? { toggleId, offset: button.getBoundingClientRect().top - canvas.getBoundingClientRect().top }
|
|
12243
|
+
: null;
|
|
12244
|
+
}
|
|
12245
|
+
state.toggle(toggleId);
|
|
12246
|
+
};
|
|
12247
|
+
useLayoutEffect(() => {
|
|
12248
|
+
const anchor = toggleAnchorRef.current;
|
|
12249
|
+
const canvas = canvasRef.current;
|
|
12250
|
+
if (!anchor || !canvas || !result)
|
|
12251
|
+
return;
|
|
12252
|
+
toggleAnchorRef.current = null;
|
|
12253
|
+
const button = canvas.querySelector(`[data-toggle-id="${CSS.escape(anchor.toggleId)}"]`);
|
|
12254
|
+
if (button)
|
|
12255
|
+
canvas.scrollTop += button.getBoundingClientRect().top - canvas.getBoundingClientRect().top - anchor.offset;
|
|
12256
|
+
}, [result]);
|
|
12257
|
+
// Without a sidebar (nothing to fill in) the form's "View report" lives on the canvas instead.
|
|
12258
|
+
const runButton = !hasSidebar && parameters && !result ? (jsx(CwButton, { text: "View report", icon: "file-view", disabled: loadingParameters || loadingRender, onClick: parameterForm.submit, "data-testid": "report-view-button" })) : null;
|
|
12259
|
+
const zoomFactor = result
|
|
12260
|
+
? resolveZoomFactor(state.zoom, pageSheetSizePt(result.Page, result.BodyWidth, SCREEN_PAGE_MARGINS), { width: canvasSize.width - CANVAS_GUTTER, height: canvasSize.height - CANVAS_GUTTER })
|
|
12261
|
+
: 1;
|
|
12262
|
+
const renderCanvas = () => {
|
|
12263
|
+
if (error) {
|
|
12264
|
+
return (jsxs(Fragment, { children: [jsx(CwNote, { color: "danger", variant: "soft", icon: "warning", content: error, className: styles.canvasNote }), runButton] }));
|
|
12265
|
+
}
|
|
12266
|
+
if (!result) {
|
|
12267
|
+
if (loadingRender) {
|
|
12268
|
+
return (jsx("div", { className: styles.sheetSkeleton, "aria-busy": "true", children: jsx(CwSkeleton, { variant: "text", count: 14 }) }));
|
|
12269
|
+
}
|
|
12270
|
+
return (jsxs("div", { className: `${styles.emptyState} cw-flex-column cw-align-center-center cw-gap-small`, children: [jsx(CwIcon, { iconId: "file-report", size: "huge", color: "neutral" }), jsx("p", { className: "cw-margin-none", children: hasSidebar ? "Set the parameters and select View report." : "Select View report to run the report." }), runButton] }));
|
|
12271
|
+
}
|
|
12272
|
+
return (jsx(CwLoading, { isLoading: loadingRender, children: 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 }) }));
|
|
12273
|
+
};
|
|
12274
|
+
return (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 && (jsxs(Fragment, { children: [jsx("aside", { className: styles.sidebar, style: { width: sidebar.width }, "aria-label": "Report parameters", "data-testid": "report-parameter-sidebar", children: jsx(CwReportParameterForm, { controller: parameterForm, loading: loadingParameters, rendering: loadingRender }) }), resizable && (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 }))] })), jsxs("div", { className: styles.main, children: [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 }), jsx("div", { ref: canvasRef, className: styles.canvas, "data-testid": "report-canvas", children: renderCanvas() })] }), confirmation && (jsx(CwDialog, { open: true, headline: confirmation.headline, width: "28rem", onClose: state.cancelOutput, customFooter: [
|
|
12275
|
+
jsx(CwButton, { text: "Cancel", variant: "outline", onClick: state.cancelOutput }, "cancel"),
|
|
12276
|
+
jsx(CwButton, { text: confirmation.confirmText, onClick: state.confirmOutput, "data-testid": "report-output-confirm" }, "confirm")
|
|
12277
|
+
], "data-testid": "report-output-dialog", children: jsx("p", { className: "cw-margin-none", children: confirmation.message }) })), state.permissionDialogOpen && (jsx(CwDialog, { open: true, headline: "SQL ERROR", width: "30rem", onClose: state.closePermissionDialog, customFooter: [jsx(CwButton, { text: "OK", onClick: state.closePermissionDialog }, "ok")], "data-testid": "report-permission-dialog", children: 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." }) }))] }));
|
|
12278
|
+
}
|
|
12279
|
+
|
|
12280
|
+
const NO_FLIPS = new Set();
|
|
12281
|
+
/**
|
|
12282
|
+
* Client-side state of the report viewer around one render result, owned by the host and handed to
|
|
12283
|
+
* `CwReportViewer` as `state`. The drilldown toggles belong to a render: a new RenderId (new render,
|
|
12284
|
+
* sort) starts with none flipped, paging and server views of the same render keep them. With
|
|
12285
|
+
* `onView` the host re-renders the page for the flipped toggles, so the page and find use it as drawn
|
|
12286
|
+
* (`drawnFlips` = none). The find position belongs to the query and the page shown. The permission
|
|
12287
|
+
* dialog is dismissed per render, and a pending export / print question is dropped by a new render.
|
|
12288
|
+
*/
|
|
12289
|
+
function useReportViewerState(result, options) {
|
|
12290
|
+
const { onPage, onView, onExport, onPrint, initialZoom = 1 } = options;
|
|
12291
|
+
const renderId = result?.RenderId ?? null;
|
|
12292
|
+
const page = result?.Page ?? null;
|
|
12293
|
+
const pageNumber = page?.Number ?? 1;
|
|
12294
|
+
const warnings = result?.Warnings;
|
|
12295
|
+
const serverToggles = !!onView;
|
|
12296
|
+
const [zoom, setZoom] = useState(initialZoom);
|
|
12297
|
+
const [toggles, setToggles] = useState({ renderId: null, flipped: NO_FLIPS });
|
|
12298
|
+
const [findText, setFindText] = useState("");
|
|
12299
|
+
const [find, setFind] = useState({ key: "", index: 0 });
|
|
12300
|
+
const [parametersVisible, setParametersVisible] = useState(true);
|
|
12301
|
+
const [dismissedRenderId, setDismissedRenderId] = useState(null);
|
|
12302
|
+
const [output, setOutput] = useState(null);
|
|
12303
|
+
const flipped = toggles.renderId === renderId ? toggles.flipped : NO_FLIPS;
|
|
12304
|
+
const toggle = useCallback((toggleId) => {
|
|
12305
|
+
setToggles(previous => ({
|
|
12306
|
+
renderId,
|
|
12307
|
+
flipped: flipToggle(previous.renderId === renderId ? previous.flipped : NO_FLIPS, toggleId)
|
|
12308
|
+
}));
|
|
12309
|
+
if (onView)
|
|
12310
|
+
onView(toggledIds(flipped, toggleId), pageNumber);
|
|
12311
|
+
}, [renderId, onView, flipped, pageNumber]);
|
|
12312
|
+
const setFlipped = useCallback((toggleIds) => {
|
|
12313
|
+
setToggles({ renderId, flipped: new Set(toggleIds) });
|
|
12314
|
+
}, [renderId]);
|
|
12315
|
+
const goToPage = useCallback((target) => {
|
|
12316
|
+
if (onView && flipped.size > 0) {
|
|
12317
|
+
onView(flippedToggleIds(flipped), target);
|
|
12318
|
+
}
|
|
12319
|
+
else {
|
|
12320
|
+
onPage(target);
|
|
12321
|
+
}
|
|
12322
|
+
}, [onView, onPage, flipped]);
|
|
12323
|
+
// With server toggles the page arrives already drawn for the flipped toggles, so nothing is flipped again here.
|
|
12324
|
+
const drawnFlips = serverToggles ? NO_FLIPS : flipped;
|
|
12325
|
+
const findMatches = useMemo(() => (page ? findInPage(page, findText, drawnFlips) : []), [page, findText, drawnFlips]);
|
|
12326
|
+
const findKey = `${renderId ?? ""}|${page?.Number ?? 0}|${findText}`;
|
|
12327
|
+
const findIndex = activeMatchIndex(find.index, find.key === findKey, findMatches.length);
|
|
12328
|
+
const findNext = useCallback(() => {
|
|
12329
|
+
setFind({ key: findKey, index: nextMatchIndex(findIndex, findMatches.length) });
|
|
12330
|
+
}, [findKey, findIndex, findMatches.length]);
|
|
12331
|
+
const toggleParameters = useCallback(() => setParametersVisible(visible => !visible), []);
|
|
12332
|
+
const closePermissionDialog = useCallback(() => setDismissedRenderId(renderId), [renderId]);
|
|
12333
|
+
// A new render (refresh, sort) drops a question asked about the previous one.
|
|
12334
|
+
const pendingOutput = output && output.renderId === renderId ? output.action : null;
|
|
12335
|
+
const runOutput = useCallback((action) => {
|
|
12336
|
+
const toggleIds = flippedToggleIds(flipped);
|
|
12337
|
+
if (action.kind === "print") {
|
|
12338
|
+
onPrint(toggleIds);
|
|
12339
|
+
}
|
|
12340
|
+
else {
|
|
12341
|
+
onExport(action.format, toggleIds);
|
|
12342
|
+
}
|
|
12343
|
+
}, [flipped, onPrint, onExport]);
|
|
12344
|
+
const requestOutput = useCallback((action) => {
|
|
12345
|
+
if (needsOutputConfirmation(warnings)) {
|
|
12346
|
+
setOutput({ renderId, action });
|
|
12347
|
+
}
|
|
12348
|
+
else {
|
|
12349
|
+
runOutput(action);
|
|
12350
|
+
}
|
|
12351
|
+
}, [warnings, renderId, runOutput]);
|
|
12352
|
+
const confirmOutput = useCallback(() => {
|
|
12353
|
+
setOutput(null);
|
|
12354
|
+
if (pendingOutput)
|
|
12355
|
+
runOutput(pendingOutput);
|
|
12356
|
+
}, [pendingOutput, runOutput]);
|
|
12357
|
+
const cancelOutput = useCallback(() => setOutput(null), []);
|
|
12358
|
+
return {
|
|
12359
|
+
result,
|
|
12360
|
+
serverToggles,
|
|
12361
|
+
zoom,
|
|
12362
|
+
setZoom,
|
|
12363
|
+
flipped,
|
|
12364
|
+
drawnFlips,
|
|
12365
|
+
toggle,
|
|
12366
|
+
setFlipped,
|
|
12367
|
+
goToPage,
|
|
12368
|
+
findText,
|
|
12369
|
+
setFindText,
|
|
12370
|
+
findMatches,
|
|
12371
|
+
findIndex,
|
|
12372
|
+
findNext,
|
|
12373
|
+
parametersVisible,
|
|
12374
|
+
toggleParameters,
|
|
12375
|
+
permissionDialogOpen: !!result?.PermissionDenied && dismissedRenderId !== renderId,
|
|
12376
|
+
closePermissionDialog,
|
|
12377
|
+
pendingOutput,
|
|
12378
|
+
requestOutput,
|
|
12379
|
+
confirmOutput,
|
|
12380
|
+
cancelOutput
|
|
12381
|
+
};
|
|
12382
|
+
}
|
|
12383
|
+
|
|
12384
|
+
function stateFor(form) {
|
|
12385
|
+
return { source: form, edits: initialParameterEdits(form?.Parameters ?? []), pending: new Set(), showErrors: false };
|
|
12386
|
+
}
|
|
12387
|
+
/**
|
|
12388
|
+
* Editor state of the report parameter form, owned by the host and handed to `CwReportParameterForm`
|
|
12389
|
+
* as `controller`. A new form object from the host (after a resolve) resets the editors to the values
|
|
12390
|
+
* it carries; the server echoes the submitted values, so nothing is lost.
|
|
12391
|
+
*/
|
|
12392
|
+
function useReportParameterForm(form, options) {
|
|
12393
|
+
const { onResolve, onRender } = options;
|
|
12394
|
+
const [state, setState] = useState(() => stateFor(form));
|
|
12395
|
+
const current = state.source === form ? state : stateFor(form);
|
|
12396
|
+
if (current !== state)
|
|
12397
|
+
setState(current);
|
|
12398
|
+
const parameters = form?.Parameters ?? [];
|
|
12399
|
+
const setEdit = useCallback((name, edit, commit) => {
|
|
12400
|
+
const params = form?.Parameters ?? [];
|
|
12401
|
+
const edits = { ...current.edits, [name]: edit };
|
|
12402
|
+
const pending = new Set(current.pending);
|
|
12403
|
+
if (commit) {
|
|
12404
|
+
pending.delete(name);
|
|
12405
|
+
}
|
|
12406
|
+
else {
|
|
12407
|
+
pending.add(name);
|
|
12408
|
+
}
|
|
12409
|
+
setState({ ...current, edits, pending });
|
|
12410
|
+
if (commit && hasDependentParameters(params, name))
|
|
12411
|
+
onResolve(toRdlParameterValues(params, edits));
|
|
12412
|
+
}, [current, form, onResolve]);
|
|
12413
|
+
const commit = useCallback((name) => {
|
|
12414
|
+
if (!current.pending.has(name))
|
|
12415
|
+
return;
|
|
12416
|
+
const params = form?.Parameters ?? [];
|
|
12417
|
+
const pending = new Set(current.pending);
|
|
12418
|
+
pending.delete(name);
|
|
12419
|
+
setState({ ...current, pending });
|
|
12420
|
+
if (hasDependentParameters(params, name))
|
|
12421
|
+
onResolve(toRdlParameterValues(params, current.edits));
|
|
12422
|
+
}, [current, form, onResolve]);
|
|
12423
|
+
const submit = useCallback(() => {
|
|
12424
|
+
const params = form?.Parameters ?? [];
|
|
12425
|
+
const errors = validateParameterEdits(params, current.edits);
|
|
12426
|
+
if (Object.keys(errors).length > 0) {
|
|
12427
|
+
setState({ ...current, showErrors: true });
|
|
12428
|
+
return;
|
|
12429
|
+
}
|
|
12430
|
+
onRender(toRdlParameterValues(params, current.edits));
|
|
12431
|
+
}, [current, form, onRender]);
|
|
12432
|
+
return {
|
|
12433
|
+
form,
|
|
12434
|
+
edits: current.edits,
|
|
12435
|
+
errors: current.showErrors ? validateParameterEdits(parameters, current.edits) : {},
|
|
12436
|
+
setEdit,
|
|
12437
|
+
commit,
|
|
12438
|
+
submit
|
|
12439
|
+
};
|
|
12440
|
+
}
|
|
12441
|
+
|
|
10124
12442
|
/**
|
|
10125
12443
|
* API-backed airport search input for WinOps.
|
|
10126
12444
|
*
|
|
@@ -10555,4 +12873,4 @@ const CwFindCrewmember = ({ handleChange, placeHolder = "Search crew…", requir
|
|
|
10555
12873
|
}, "data-direction": direction, children: [jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxs("div", { className: "cw-search-input-wrapper", ref: wrapperRef, children: [jsx("input", { ref: inputRef, type: "text", value: inputValue, onChange: handleInputChange, onKeyDown: handleKeyDown, onFocus: handleInputFocus, placeholder: isInitialLoading ? "Loading…" : placeHolder, disabled: disabled, required: required, autoComplete: "off", "aria-expanded": showDropdown, "aria-haspopup": "listbox", role: "combobox", title: tooltipText }), (isLoading || isInitialLoading) && (jsx("div", { className: "cw-search-input-loading", children: jsx(CwIcon, { iconId: "spinner" }) })), jsx("div", { className: "cw-search-input-icons", children: inputValue && !disabled && !isInitialLoading ? (jsx(CwButton, { type: "button", onClick: handleClear, "aria-label": "Clear selected crewmember", variant: "icon", icon: "close", color: "neutral" })) : (jsx(CwIcon, { iconId: "person" })) })] })] }), renderPanel(jsx("div", { ref: panelRef, className: dropdownStyles.dropdown, style: panelStyle, role: "listbox", children: jsx("ul", { children: options.map((option, index) => (jsx("li", { className: `${dropdownStyles.option}${index === highlightedIndex ? ` ${dropdownStyles.optionFocused}` : ""}`, onClick: () => handleOptionSelect(option.value), onMouseDown: (e) => e.preventDefault(), onMouseEnter: () => setHighlightedIndex(index), role: "option", "aria-selected": index === highlightedIndex, children: option.text }, option.value))) }) }))] }));
|
|
10556
12874
|
};
|
|
10557
12875
|
|
|
10558
|
-
export { CwAccordionContainer, CwAlign, CwAnchoredMenu, CwBadge, CwBtnApprove, CwBtnCancel, CwBtnDelete, CwBtnEdit, CwBtnSave, CwButton, CwButtonSplit, CwButtonSwitch, CwCard, CwCardList, CwCheck, CwCheckbox, CwCheckboxGroup, CwChip, CwColorPicker, CwConfirmationPopup, CwContextMenu, CwDatePicker, CwDatePickerTemporal, CwDateRangePicker, CwDateRangePickerTemporal, CwDateTimePicker, CwDateTimePickerCompact, CwDateTimePickerTemporal, CwDialog, CwDigit, CwDisplayMessage, CwDropdown, CwDropdownFilter, CwExpandable, CwFileUploadMultiple, CwFindAirport, CwFindCrewmember, CwGenericTooltip, CwHeadingMain, CwHeadingSecond, CwIcon, CwImageArea, CwImageGallery, CwImageZoom, CwInput, CwInputColor, CwInputDateText, CwInputDatetime, CwInputImage, CwInputNumber, CwInputPhone, CwInputText, CwKeyValueList, CwLabel, CwLoading, CwMasterDetail, CwMessage, CwMessageManager, CwMessageType, CwModal, CwModalHover, CwModalReportFunctional, CwMultiFilter, CwMultiFilterTag, CwNavMenu, CwNote, CwOption, CwPopoverButton, CwProgress, CwRadioGroup, CwReportModal, CwSearchAsync, CwSearchInput, CwSelect, CwSelectList, CwSelectListItems, CwSkeleton, CwSortableList, CwSortableTable, CwTable, CwTableGrouped, CwTableServerSide, CwTabs, CwTagSelector, CwTextArea, CwTime, CwTimePicker, CwToggle, CwTooltipManager, CwTooltipNew, CwTreeView, CwWeekdaySelector, DefaultRowHeader, OnClearPinned, OnClickContextMenu, OnClickEvent, OnClickRowEvent, OnClickRowHeader, OnClickUtc, OnDoubleClickEvent, OnDoubleClickRowEvent, OnDragEvent, OnDropCtrlEvent, OnDropEvent, OnEndClickHeaderEvent, OnLeftDragStart, OnMultiClickEvent, OnPinRow, OnRangeClickEvent, OnRightClickEvent, OnRightClickRow, OnRightDragStart, OnStartClickHeaderEvent, OnUnpinRow, PRESET_LIBRARY_TEMPORAL, PinRowHeader, Scheduler, SchedulerEvent, SuperScheduler, TooltipConstraintContext, UiEvent, Weekdays, configureCwLoading, eventIsVisible, filterNavItems, filterVisibleEvents, getCwLoadingDefaults, getDefaultDivisions, getEventSizes, getPresetsByKeysTemporal, itemsToMultiFilterTags, sortNavItems, useCwMessage, useSortableList, useSortableTable, useTooltipConstraint };
|
|
12876
|
+
export { CwAccordionContainer, CwAlign, CwAnchoredMenu, CwBadge, CwBtnApprove, CwBtnCancel, CwBtnDelete, CwBtnEdit, CwBtnSave, CwButton, CwButtonSplit, CwButtonSwitch, CwCard, CwCardList, CwCheck, CwCheckbox, CwCheckboxGroup, CwChip, CwColorPicker, CwConfirmationPopup, CwContextMenu, CwDatePicker, CwDatePickerTemporal, CwDateRangePicker, CwDateRangePickerTemporal, CwDateTimePicker, CwDateTimePickerCompact, CwDateTimePickerTemporal, CwDialog, CwDigit, CwDisplayMessage, CwDropdown, CwDropdownFilter, CwExpandable, CwFileUploadMultiple, CwFindAirport, CwFindCrewmember, CwGenericTooltip, CwHeadingMain, CwHeadingSecond, CwIcon, CwImageArea, CwImageGallery, CwImageZoom, CwInput, CwInputColor, CwInputDateText, CwInputDatetime, CwInputImage, CwInputNumber, CwInputPhone, CwInputText, CwKeyValueList, CwLabel, CwLoading, CwMasterDetail, CwMessage, CwMessageManager, CwMessageType, CwModal, CwModalHover, CwModalReportFunctional, CwMultiFilter, CwMultiFilterTag, CwNavMenu, CwNote, CwOption, CwPopoverButton, CwProgress, CwRadioGroup, CwReportGauge, CwReportLineChart, CwReportModal, CwReportPage, CwReportParameterForm, CwReportPieChart, CwReportToolbar, CwReportViewer, CwSearchAsync, CwSearchInput, CwSelect, CwSelectList, CwSelectListItems, CwSkeleton, CwSortableList, CwSortableTable, CwTable, CwTableGrouped, CwTableServerSide, CwTabs, CwTagSelector, CwTextArea, CwTime, CwTimePicker, CwToggle, CwTooltipManager, CwTooltipNew, CwTreeView, CwWeekdaySelector, DefaultRowHeader, OnClearPinned, OnClickContextMenu, OnClickEvent, OnClickRowEvent, OnClickRowHeader, OnClickUtc, OnDoubleClickEvent, OnDoubleClickRowEvent, OnDragEvent, OnDropCtrlEvent, OnDropEvent, OnEndClickHeaderEvent, OnLeftDragStart, OnMultiClickEvent, OnPinRow, OnRangeClickEvent, OnRightClickEvent, OnRightClickRow, OnRightDragStart, OnStartClickHeaderEvent, OnUnpinRow, PRESET_LIBRARY_TEMPORAL, PinRowHeader, REPORT_ZOOM_STEPS, Scheduler, SchedulerEvent, SuperScheduler, TooltipConstraintContext, UiEvent, Weekdays, configureCwLoading, eventIsVisible, filterNavItems, filterVisibleEvents, findInPage, flipToggle, getCwLoadingDefaults, getDefaultDivisions, getEventSizes, getPresetsByKeysTemporal, initialParameterEdits, isRdlVisible, itemsToMultiFilterTags, sortNavItems, toRdlParameterValues, useCwMessage, useReportParameterForm, useReportViewerState, useSortableList, useSortableTable, useTooltipConstraint };
|