@cwellt_software/cwellt-reactjs-lib 1.6.2 → 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 +2522 -166
- package/dist/index.css +1 -1
- package/dist/index.d.ts +697 -5
- package/dist/index.es.js +2508 -167
- package/dist/src/components/control/choice/tag-selector/CwTagSelector.d.ts.map +1 -1
- package/dist/src/components/control/choice/tag-selector/CwTagSelector.stories.d.ts +2 -0
- package/dist/src/components/control/choice/tag-selector/CwTagSelector.stories.d.ts.map +1 -1
- 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/custom/scheduler-new/presentation/Scheduler.stories.d.ts +18 -1
- package/dist/src/components/custom/scheduler-new/presentation/Scheduler.stories.d.ts.map +1 -1
- package/dist/src/components/custom/scheduler-new/presentation/components/row/DefaultRowHeader.d.ts +2 -0
- package/dist/src/components/custom/scheduler-new/presentation/components/row/DefaultRowHeader.d.ts.map +1 -1
- package/dist/src/components/custom/scheduler-new/presentation/components/row/Event.d.ts +15 -0
- package/dist/src/components/custom/scheduler-new/presentation/components/row/Event.d.ts.map +1 -1
- package/dist/src/components/custom/super-scheduler/PinRowHeader.d.ts +0 -1
- package/dist/src/components/custom/super-scheduler/PinRowHeader.d.ts.map +1 -1
- package/dist/src/components/display/data/table/CwTable.d.ts.map +1 -1
- package/dist/src/components/display/data/table/CwTable.stories.d.ts +2 -0
- package/dist/src/components/display/data/table/CwTable.stories.d.ts.map +1 -1
- 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/components/layout/card/CwCard.d.ts +2 -2
- package/dist/src/components/layout/card/CwCard.d.ts.map +1 -1
- package/dist/src/components/layout/card/CwCard.stories.d.ts +4 -2
- package/dist/src/components/layout/card/CwCard.stories.d.ts.map +1 -1
- package/dist/src/index.d.ts +16 -1
- 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 +1 -1
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
|
|
@@ -1945,8 +1945,8 @@ var styles$t = {"card":"cw-card-module__card__HJUT0","clickable":"cw-card-module
|
|
|
1945
1945
|
* onDelete={confirmDeletion}
|
|
1946
1946
|
* footer="Last updated: 2 days ago"
|
|
1947
1947
|
* chips={[
|
|
1948
|
-
* { label: 'Important',
|
|
1949
|
-
* { label: 'Custom',
|
|
1948
|
+
* { label: 'Important', color: 'danger' },
|
|
1949
|
+
* { label: 'Custom', color: 'custom', customColor: '#8a2be2' }
|
|
1950
1950
|
* ]}
|
|
1951
1951
|
* >
|
|
1952
1952
|
* <p>This is the main content of the card.</p>
|
|
@@ -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
|
/*
|
|
@@ -2201,9 +2201,6 @@ function CwTable({ columns, data, pagination = false, pageSizeOptions = [5, 10,
|
|
|
2201
2201
|
return acc;
|
|
2202
2202
|
}, {}));
|
|
2203
2203
|
const [selectedKeys, setSelectedKeys] = useState(new Set(rowSelection?.selectedRowKeys || []));
|
|
2204
|
-
useEffect(() => {
|
|
2205
|
-
setCurrentPage(1);
|
|
2206
|
-
}, [data]);
|
|
2207
2204
|
useEffect(() => {
|
|
2208
2205
|
setSelectedKeys(new Set(rowSelection?.selectedRowKeys || []));
|
|
2209
2206
|
}, [rowSelection?.selectedRowKeys]);
|
|
@@ -2289,20 +2286,27 @@ function CwTable({ columns, data, pagination = false, pageSizeOptions = [5, 10,
|
|
|
2289
2286
|
return newSet;
|
|
2290
2287
|
});
|
|
2291
2288
|
}, [rowSelection, rowKey, sortedData]);
|
|
2289
|
+
const totalPages = useMemo(() => {
|
|
2290
|
+
return pagination ? Math.ceil(sortedData.length / localItemsPerPage) : 1;
|
|
2291
|
+
}, [sortedData, localItemsPerPage, pagination]);
|
|
2292
|
+
// Keep the page when data changes (e.g. a row was edited); only clamp it when it no longer exists.
|
|
2293
|
+
// Derived in render so a shrunk data set never shows an empty page for a frame.
|
|
2294
|
+
const page = Math.min(currentPage, Math.max(1, totalPages));
|
|
2295
|
+
useEffect(() => {
|
|
2296
|
+
if (page !== currentPage)
|
|
2297
|
+
setCurrentPage(page);
|
|
2298
|
+
}, [page, currentPage]);
|
|
2292
2299
|
const paginatedData = useMemo(() => {
|
|
2293
2300
|
if (!sortedData)
|
|
2294
2301
|
return [];
|
|
2295
2302
|
if (!pagination)
|
|
2296
2303
|
return sortedData;
|
|
2297
|
-
const start = (
|
|
2304
|
+
const start = (page - 1) * localItemsPerPage;
|
|
2298
2305
|
return sortedData.slice(start, start + localItemsPerPage);
|
|
2299
|
-
}, [sortedData,
|
|
2300
|
-
const
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
const handlePageChange = (page) => {
|
|
2304
|
-
if (page >= 1 && page <= totalPages)
|
|
2305
|
-
setCurrentPage(page);
|
|
2306
|
+
}, [sortedData, page, localItemsPerPage, pagination]);
|
|
2307
|
+
const handlePageChange = (target) => {
|
|
2308
|
+
if (target >= 1 && target <= totalPages)
|
|
2309
|
+
setCurrentPage(target);
|
|
2306
2310
|
};
|
|
2307
2311
|
const startResize = (e, key) => {
|
|
2308
2312
|
e.preventDefault();
|
|
@@ -2363,7 +2367,7 @@ function CwTable({ columns, data, pagination = false, pageSizeOptions = [5, 10,
|
|
|
2363
2367
|
? "sortable"
|
|
2364
2368
|
: sortConfig.direction === "asc"
|
|
2365
2369
|
? "sortable-asc"
|
|
2366
|
-
: "sortable-desc" }))] }), jsx("span", { onMouseDown: (e) => startResize(e, col.key), className: "th-column-resizer" })] }, col.key)))] }) }), jsx("tbody", { children: renderTableBody() })] }), pagination && data.length > pageSizeOptions[0] && (jsxs("footer", { className: "cw-table-pagination", children: [jsxs("div", { className: "cw-table-pagination-size", children: [jsx(CwIcon, { iconId: "list" }), jsx("select", { value: localItemsPerPage, onChange: handleItemsPerPageChange, children: pageSizeOptions.map(size => (jsx("option", { value: size, children: pageLabel ? `${size} / ${pageLabel}` : size }, size))) })] }), jsxs("div", { className: "cw-table-pagination-nav", children: [jsx(CwButton, { onClick: () => handlePageChange(1), disabled:
|
|
2370
|
+
: "sortable-desc" }))] }), jsx("span", { onMouseDown: (e) => startResize(e, col.key), className: "th-column-resizer" })] }, col.key)))] }) }), jsx("tbody", { children: renderTableBody() })] }), pagination && data.length > pageSizeOptions[0] && (jsxs("footer", { className: "cw-table-pagination", children: [jsxs("div", { className: "cw-table-pagination-size", children: [jsx(CwIcon, { iconId: "list" }), jsx("select", { value: localItemsPerPage, onChange: handleItemsPerPageChange, children: pageSizeOptions.map(size => (jsx("option", { value: size, children: pageLabel ? `${size} / ${pageLabel}` : size }, size))) })] }), jsxs("div", { className: "cw-table-pagination-nav", children: [jsx(CwButton, { onClick: () => handlePageChange(1), disabled: page === 1 || totalPages <= 1, variant: "icon", icon: "chevron-left-double" }), jsx(CwButton, { onClick: () => handlePageChange(page - 1), disabled: page === 1 || totalPages <= 1, variant: "icon", icon: "chevron-left" }), jsx("input", { type: "text", inputMode: "numeric", value: totalPages === 0 ? 0 : page, disabled: totalPages === 0, onChange: (e) => {
|
|
2367
2371
|
const value = parseInt(e.target.value, 10);
|
|
2368
2372
|
if (!isNaN(value))
|
|
2369
2373
|
handlePageChange(value);
|
|
@@ -2372,10 +2376,10 @@ function CwTable({ columns, data, pagination = false, pageSizeOptions = [5, 10,
|
|
|
2372
2376
|
if (isNaN(value) || value < 1 || value > totalPages) {
|
|
2373
2377
|
handlePageChange(1);
|
|
2374
2378
|
}
|
|
2375
|
-
}, min: 1, max: totalPages }), jsx("span", { children: "/" }), jsx("span", { children: totalPages }), jsx(CwButton, { onClick: () => handlePageChange(
|
|
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" })] })] }))] }));
|
|
2376
2380
|
}
|
|
2377
2381
|
|
|
2378
|
-
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"};
|
|
2379
2383
|
|
|
2380
2384
|
/**
|
|
2381
2385
|
* Hook that provides state management for CwSortableTable.
|
|
@@ -2438,7 +2442,7 @@ function useSortableTable(initialItems) {
|
|
|
2438
2442
|
}
|
|
2439
2443
|
|
|
2440
2444
|
// ─── Drop indicator row ───────────────────────────────────────────────────────
|
|
2441
|
-
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", {}) }) }));
|
|
2442
2446
|
// ─── Loading skeleton ─────────────────────────────────────────────────────────
|
|
2443
2447
|
// Plain td cells (no local class): the ghost cells get the same global cw-table
|
|
2444
2448
|
// padding as real body cells, so row heights match the loaded rows exactly.
|
|
@@ -2541,11 +2545,11 @@ function CwSortableTable({ columns, data, rowKey = "key", onReorder, loading = f
|
|
|
2541
2545
|
return items.map((item, index) => {
|
|
2542
2546
|
const key = item[rowKey];
|
|
2543
2547
|
const isDragging = draggedItem?.[rowKey] === key;
|
|
2544
|
-
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)));
|
|
2545
2549
|
});
|
|
2546
2550
|
};
|
|
2547
2551
|
// ── Render ──────────────────────────────────────────────────────────────
|
|
2548
|
-
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) })] }))] }));
|
|
2549
2553
|
}
|
|
2550
2554
|
|
|
2551
2555
|
/**
|
|
@@ -2746,7 +2750,7 @@ function CwTableServerSide({ columns, data, totalItems, pagination = false, page
|
|
|
2746
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" })] })] }))] }));
|
|
2747
2751
|
}
|
|
2748
2752
|
|
|
2749
|
-
var styles$
|
|
2753
|
+
var styles$s = {"cw-tabs":"cw-tabs-module__cw-tabs__1pmji","cw-tabs-content":"cw-tabs-module__cw-tabs-content__HTp8d"};
|
|
2750
2754
|
|
|
2751
2755
|
const BADGE_COLOR_SCHEMES = ['primary', 'danger', 'warning', 'success', 'info', 'neutral'];
|
|
2752
2756
|
function isBadgeColorScheme(value) {
|
|
@@ -2800,11 +2804,11 @@ function CwTabs(CwTabsProps) {
|
|
|
2800
2804
|
const tabsListStyle = position === 'left' && CwTabsProps.tabsListWidth
|
|
2801
2805
|
? { minWidth: CwTabsProps.tabsListWidth }
|
|
2802
2806
|
: undefined;
|
|
2803
|
-
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 => {
|
|
2804
2808
|
const badgeScheme = tab.badgeColor && isBadgeColorScheme(tab.badgeColor) ? tab.badgeColor : undefined;
|
|
2805
2809
|
const rawBadgeColor = tab.badgeColor && !badgeScheme ? tab.badgeColor : undefined;
|
|
2806
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));
|
|
2807
|
-
}) }), 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 })] }));
|
|
2808
2812
|
}
|
|
2809
2813
|
|
|
2810
2814
|
/**
|
|
@@ -2832,7 +2836,7 @@ const CwExpandable = ({ briefing, onToggle, onOpen, onClose, children, ...detail
|
|
|
2832
2836
|
return (jsxs("details", { ref: myRef, ...detailsProps, className: "cw-expandable", children: [jsx("summary", { children: briefing }), children && jsx("section", { children: children })] }));
|
|
2833
2837
|
};
|
|
2834
2838
|
|
|
2835
|
-
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"};
|
|
2836
2840
|
|
|
2837
2841
|
/*
|
|
2838
2842
|
* Fixed rem widths for the ghosts: the master rows and detail panel size their
|
|
@@ -2876,12 +2880,12 @@ const CwMasterDetail = ({ id, className, style, items, selectedKey, onSelect, de
|
|
|
2876
2880
|
document.body.style.userSelect = '';
|
|
2877
2881
|
};
|
|
2878
2882
|
}, [isResizing, minMasterWidth, maxMasterWidth]);
|
|
2879
|
-
const rootClassName = [styles$
|
|
2883
|
+
const rootClassName = [styles$r['cw-master-detail'], className].filter(Boolean).join(' ');
|
|
2880
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
|
|
2881
|
-
? Array.from({ length: skeletonRows }, (_unused, rowIndex) => (jsxs("li", { "aria-hidden": "true", children: [jsx(CwSkeleton, { variant: "circle", height: "2rem" }), jsxs("div", { className: styles$
|
|
2882
|
-
: 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
|
|
2883
2887
|
? detailContent
|
|
2884
|
-
: (jsx("div", { className: styles$
|
|
2888
|
+
: (jsx("div", { className: styles$r['detail-empty'], children: emptyDetail ?? 'Select an item to view details' })) })] }));
|
|
2885
2889
|
};
|
|
2886
2890
|
|
|
2887
2891
|
/*
|
|
@@ -2921,7 +2925,7 @@ const CwKeyValueList = ({ items, className = "", emptyValue = "-", direction = "
|
|
|
2921
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() }));
|
|
2922
2926
|
};
|
|
2923
2927
|
|
|
2924
|
-
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"};
|
|
2925
2929
|
|
|
2926
2930
|
function CwSortableList({ items, onReorder, renderItem, className = '', movedItems = new Set(), emptyMessage = "No elements to show", readOnly = false, maxHeight = "300px", }) {
|
|
2927
2931
|
const [draggedItem, setDraggedItem] = useState(null);
|
|
@@ -2984,15 +2988,15 @@ function CwSortableList({ items, onReorder, renderItem, className = '', movedIte
|
|
|
2984
2988
|
}
|
|
2985
2989
|
};
|
|
2986
2990
|
if (items.length === 0) {
|
|
2987
|
-
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 }) }));
|
|
2988
2992
|
}
|
|
2989
|
-
return (jsx("div", { className: `${styles$
|
|
2993
|
+
return (jsx("div", { className: `${styles$q.sortableList} ${className}`, style: { maxHeight }, children: items.map((item, index) => {
|
|
2990
2994
|
const { title, extraContent, actions, showHandle = true, expandedContent } = renderItem(item);
|
|
2991
2995
|
const isDragging = draggedItem?.id === item.id;
|
|
2992
2996
|
const isMoved = movedItems.has(item.id);
|
|
2993
2997
|
const canExpand = Boolean(expandedContent);
|
|
2994
2998
|
const isExpanded = expandedItems.has(item.id);
|
|
2995
|
-
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));
|
|
2996
3000
|
}) }));
|
|
2997
3001
|
}
|
|
2998
3002
|
|
|
@@ -3119,7 +3123,7 @@ const CwTableGrouped = props => {
|
|
|
3119
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", {})] }) }));
|
|
3120
3124
|
};
|
|
3121
3125
|
|
|
3122
|
-
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"};
|
|
3123
3127
|
|
|
3124
3128
|
const DEFAULT_LABELS$1 = {
|
|
3125
3129
|
uploadDisabled: 'Upload disabled',
|
|
@@ -3305,12 +3309,12 @@ function CwFileUploadMultiple(fileUploadProps) {
|
|
|
3305
3309
|
}
|
|
3306
3310
|
}
|
|
3307
3311
|
};
|
|
3308
|
-
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: () => {
|
|
3309
3313
|
setExistingFile(undefined);
|
|
3310
3314
|
if (fileUploadProps.onSelect) {
|
|
3311
3315
|
fileUploadProps.onSelect(null);
|
|
3312
3316
|
}
|
|
3313
|
-
}, 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: () => {
|
|
3314
3318
|
let files = fileInputRef.current?.files ?? null;
|
|
3315
3319
|
if (!files) {
|
|
3316
3320
|
const dataTransfer = new DataTransfer();
|
|
@@ -3318,7 +3322,7 @@ function CwFileUploadMultiple(fileUploadProps) {
|
|
|
3318
3322
|
files = dataTransfer.files;
|
|
3319
3323
|
}
|
|
3320
3324
|
fileUploadProps.onConfirm?.(files);
|
|
3321
|
-
}, 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 }))] }))] }));
|
|
3322
3326
|
}
|
|
3323
3327
|
|
|
3324
3328
|
/**
|
|
@@ -3371,7 +3375,7 @@ function CwDigit(props) {
|
|
|
3371
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)))] }));
|
|
3372
3376
|
}
|
|
3373
3377
|
|
|
3374
|
-
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"};
|
|
3375
3379
|
|
|
3376
3380
|
const CwColorPicker = ({ initialColor, onChange, previewText = "Color preview" }) => {
|
|
3377
3381
|
const [rgb, setRgb] = useState({ r: 255, g: 255, b: 255 });
|
|
@@ -3574,13 +3578,13 @@ const CwColorPicker = ({ initialColor, onChange, previewText = "Color preview" }
|
|
|
3574
3578
|
// Calculate background color for the main area based on current hue
|
|
3575
3579
|
const hueColor = hsvToRgb(hsv.h, 100, 100);
|
|
3576
3580
|
const hueHex = rgbToHex(hueColor.r, hueColor.g, hueColor.b);
|
|
3577
|
-
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: {
|
|
3578
3582
|
left: `${hsv.s}%`,
|
|
3579
3583
|
top: `${100 - hsv.v}%`
|
|
3580
3584
|
} })] })] })] }));
|
|
3581
3585
|
};
|
|
3582
3586
|
|
|
3583
|
-
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"};
|
|
3584
3588
|
|
|
3585
3589
|
const CwInputColor = ({ value, onChange, previewText = "Color preview", disabled = false, readOnly = false, width = '4rem', height = '2rem', labelProps, layoutProps, className, style, }) => {
|
|
3586
3590
|
const [isOpen, setIsOpen] = useState(false);
|
|
@@ -3713,11 +3717,11 @@ const CwInputColor = ({ value, onChange, previewText = "Color preview", disabled
|
|
|
3713
3717
|
break;
|
|
3714
3718
|
}
|
|
3715
3719
|
};
|
|
3716
|
-
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: {
|
|
3717
3721
|
width,
|
|
3718
3722
|
height,
|
|
3719
3723
|
backgroundColor: value,
|
|
3720
|
-
}, "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)] }));
|
|
3721
3725
|
};
|
|
3722
3726
|
|
|
3723
3727
|
/**
|
|
@@ -3972,7 +3976,7 @@ function CwInputDateText(props) {
|
|
|
3972
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 })] }) }));
|
|
3973
3977
|
}
|
|
3974
3978
|
|
|
3975
|
-
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"};
|
|
3976
3980
|
|
|
3977
3981
|
const DEFAULT_LABELS = {
|
|
3978
3982
|
dropImageHere: "Drop image here",
|
|
@@ -4051,7 +4055,7 @@ function CwInputImage(props) {
|
|
|
4051
4055
|
const file = e.currentTarget.files?.[0];
|
|
4052
4056
|
void handleFile(file ?? null);
|
|
4053
4057
|
};
|
|
4054
|
-
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 })] }));
|
|
4055
4059
|
}
|
|
4056
4060
|
|
|
4057
4061
|
/**
|
|
@@ -4183,7 +4187,7 @@ const CwImageArea = forwardRef((props, ref) => {
|
|
|
4183
4187
|
});
|
|
4184
4188
|
CwImageArea.displayName = "CwImageArea";
|
|
4185
4189
|
|
|
4186
|
-
var styles$
|
|
4190
|
+
var styles$l = {"container":"cw-weekday-selector-module__container__pwcvx","days":"cw-weekday-selector-module__days__Dd3RH"};
|
|
4187
4191
|
|
|
4188
4192
|
/* 2024-01-01 was a Monday. Formatted in UTC so a negative browser offset cannot shift a day. */
|
|
4189
4193
|
const MONDAY_UTC = Date.UTC(2024, 0, 1);
|
|
@@ -4381,16 +4385,16 @@ const CwWeekdaySelector = ({ value = "mtwrfsu", onChange, disabled = false, labe
|
|
|
4381
4385
|
// Points at the label element rather than copying its text, which also covers a labelProps that
|
|
4382
4386
|
// carries children instead of `text`.
|
|
4383
4387
|
const labelId = labelProps?.id ?? `${feedbackId}-label`;
|
|
4384
|
-
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)))] }));
|
|
4385
4389
|
};
|
|
4386
4390
|
|
|
4387
|
-
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"};
|
|
4388
4392
|
|
|
4389
4393
|
function CwCheck(props) {
|
|
4390
4394
|
const { labelProps, labelText, direction, alignment, className, ...inputProps } = props;
|
|
4391
4395
|
const resolvedDirection = direction ?? alignment ?? "row";
|
|
4392
4396
|
const displayText = labelProps?.text ?? labelText;
|
|
4393
|
-
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 }))] }) }));
|
|
4394
4398
|
}
|
|
4395
4399
|
|
|
4396
4400
|
function CwCheckbox(CwCheckboxProps) {
|
|
@@ -4444,14 +4448,14 @@ function CwCheckboxGroup({ options, value = [], onChange, labelProps, groupLayou
|
|
|
4444
4448
|
?? (optionsAlignment === "column" ? "row" : "column");
|
|
4445
4449
|
// Each checkbox's box-to-text layout is the opposite of the options flow, mirroring CwRadioGroup.
|
|
4446
4450
|
const checkDirection = itemDirection === "row" ? "column" : "row";
|
|
4447
|
-
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)))] }));
|
|
4448
4452
|
}
|
|
4449
4453
|
|
|
4450
|
-
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"};
|
|
4451
4455
|
|
|
4452
4456
|
/** Single native radio input. Building block for CwRadioGroup. */
|
|
4453
4457
|
function CwRadio({ label, direction = "row", className, ...inputProps }) {
|
|
4454
|
-
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 })] }));
|
|
4455
4459
|
}
|
|
4456
4460
|
|
|
4457
4461
|
/**
|
|
@@ -4478,7 +4482,7 @@ function CwRadioGroup({ options, value, onChange, labelProps, groupLayout, itemL
|
|
|
4478
4482
|
// (itemLayout column) reads best with the circle beside the text; a side-by-side row reads
|
|
4479
4483
|
// best with the circle above the text.
|
|
4480
4484
|
const radioDirection = itemLayout?.direction === "row" ? "column" : "row";
|
|
4481
|
-
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)))] }));
|
|
4482
4486
|
}
|
|
4483
4487
|
|
|
4484
4488
|
function CwToggle(props) {
|
|
@@ -4619,7 +4623,7 @@ function useDropdownPortal({ anchorRef, isOpen, onClose, positionStrategy = defa
|
|
|
4619
4623
|
return { panelRef, panelStyle: style, renderPanel };
|
|
4620
4624
|
}
|
|
4621
4625
|
|
|
4622
|
-
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"};
|
|
4623
4627
|
|
|
4624
4628
|
/**
|
|
4625
4629
|
* Custom dropdown component with colour-coded options, optional search filter, and full
|
|
@@ -4733,12 +4737,12 @@ function CwDropdown({ options, value, onChange, labelProps, alignProps, placehol
|
|
|
4733
4737
|
}
|
|
4734
4738
|
}, [isOpen, highlightedIndex, filteredOptions, handleOptionSelect]);
|
|
4735
4739
|
const flexDirection = alignProps?.flexDirection ?? "row";
|
|
4736
|
-
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$
|
|
4737
|
-
styles$
|
|
4738
|
-
value === option.value ? styles$
|
|
4739
|
-
highlightedIndex === index ? styles$
|
|
4740
|
-
option.disabled ? styles$
|
|
4741
|
-
].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)))] })] }) }));
|
|
4742
4746
|
}
|
|
4743
4747
|
|
|
4744
4748
|
/**
|
|
@@ -4755,7 +4759,7 @@ function CwDropdownFilter(props) {
|
|
|
4755
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))) }) }))] }));
|
|
4756
4760
|
}
|
|
4757
4761
|
|
|
4758
|
-
var styles$
|
|
4762
|
+
var styles$h = {"container":"cw-popover-button-module__container__YSWQU","panel":"cw-popover-button-module__panel__C-BTn"};
|
|
4759
4763
|
|
|
4760
4764
|
/**
|
|
4761
4765
|
* Button that toggles a floating panel with arbitrary children.
|
|
@@ -4834,7 +4838,7 @@ function CwPopoverButton({ children, panelWidth = "auto", placement = "bottom-st
|
|
|
4834
4838
|
}
|
|
4835
4839
|
panel.showPopover();
|
|
4836
4840
|
}, [placement]);
|
|
4837
|
-
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 })] }));
|
|
4838
4842
|
}
|
|
4839
4843
|
|
|
4840
4844
|
function itemsToMultiFilterTags(items, nameKey, valueKey, category, primaryColor, onPrimaryColor) {
|
|
@@ -4852,13 +4856,13 @@ function itemsToMultiFilterTags(items, nameKey, valueKey, category, primaryColor
|
|
|
4852
4856
|
return result;
|
|
4853
4857
|
}
|
|
4854
4858
|
|
|
4855
|
-
var styles$
|
|
4859
|
+
var styles$g = {"cw-multifilter-tag":"cw-multi-filter-tag-module__cw-multifilter-tag__Epda-"};
|
|
4856
4860
|
|
|
4857
4861
|
const CwMultiFilterTag = props => {
|
|
4858
4862
|
const { ID, Name, Value, Category, Removable, PrimaryColor, Selectable, Selected } = props;
|
|
4859
4863
|
const backgroundColor = `rgb(${PrimaryColor.r},${PrimaryColor.g},${PrimaryColor.b})`;
|
|
4860
4864
|
const isOutlineMode = Selected && !Removable;
|
|
4861
|
-
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: () => {
|
|
4862
4866
|
if (Selectable)
|
|
4863
4867
|
props.OnSelect?.(props);
|
|
4864
4868
|
}, children: jsx(CwChip, { label: Name, colorScheme: "custom", customColor: backgroundColor, variant: isOutlineMode ? "outline" : "soft", closable: Removable, onClose: () => props.OnRemove?.(props),
|
|
@@ -4868,7 +4872,7 @@ const CwMultiFilterTag = props => {
|
|
|
4868
4872
|
} : undefined }) }));
|
|
4869
4873
|
};
|
|
4870
4874
|
|
|
4871
|
-
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"};
|
|
4872
4876
|
|
|
4873
4877
|
/**
|
|
4874
4878
|
* A multiple filter selector, a MULTI-SELECT even. Allows users to select and filter items based on tags.
|
|
@@ -5109,9 +5113,9 @@ const CwMultiFilter = ({ allTags, id, onChangeSelectedTags, selectedTags, placeh
|
|
|
5109
5113
|
inputRef.current?.focus();
|
|
5110
5114
|
}
|
|
5111
5115
|
};
|
|
5112
|
-
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) => {
|
|
5113
5117
|
e.preventDefault();
|
|
5114
|
-
}, 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 => {
|
|
5115
5119
|
switch (e.key) {
|
|
5116
5120
|
case "Enter":
|
|
5117
5121
|
case "Tab": {
|
|
@@ -5138,7 +5142,7 @@ const CwMultiFilter = ({ allTags, id, onChangeSelectedTags, selectedTags, placeh
|
|
|
5138
5142
|
break;
|
|
5139
5143
|
}
|
|
5140
5144
|
}
|
|
5141
|
-
} })] }), 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: {
|
|
5142
5146
|
backgroundColor: getColor(category).primary,
|
|
5143
5147
|
color: getColor(category).onPrimary,
|
|
5144
5148
|
}, onClick: () => handleClickCategory(category), children: category }, category)))] }), jsx("ul", { children: displayTags
|
|
@@ -5164,7 +5168,7 @@ const CwMultiFilter = ({ allTags, id, onChangeSelectedTags, selectedTags, placeh
|
|
|
5164
5168
|
} }, props.ID))) })] }))] }));
|
|
5165
5169
|
};
|
|
5166
5170
|
|
|
5167
|
-
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"};
|
|
5168
5172
|
|
|
5169
5173
|
/**
|
|
5170
5174
|
* Multi-select input with chip tags and searchable dropdown.
|
|
@@ -5202,6 +5206,11 @@ function CwTagSelector({ id, options, value, onChange, labelProps, iconProps, al
|
|
|
5202
5206
|
isOpen: open,
|
|
5203
5207
|
onClose: () => { setOpen(false); setFocusedIndex(-1); },
|
|
5204
5208
|
});
|
|
5209
|
+
// Disabling while the panel is open closes it (state adjusted during render, no effect pass).
|
|
5210
|
+
if (disabled && open) {
|
|
5211
|
+
setOpen(false);
|
|
5212
|
+
setFocusedIndex(-1);
|
|
5213
|
+
}
|
|
5205
5214
|
const selectedSet = useMemo(() => new Set(value), [value]);
|
|
5206
5215
|
const filtered = useMemo(() => {
|
|
5207
5216
|
if (!search)
|
|
@@ -5211,17 +5220,23 @@ function CwTagSelector({ id, options, value, onChange, labelProps, iconProps, al
|
|
|
5211
5220
|
}, [options, selectedSet, search]);
|
|
5212
5221
|
const selectedOptions = useMemo(() => value.map(v => options.find(o => o.value === v)).filter(Boolean), [value, options]);
|
|
5213
5222
|
const handleSelect = useCallback((optionValue) => {
|
|
5223
|
+
if (disabled)
|
|
5224
|
+
return;
|
|
5214
5225
|
onChange([...value, optionValue]);
|
|
5215
5226
|
setSearch("");
|
|
5216
5227
|
setFocusedIndex(-1);
|
|
5217
5228
|
inputRef.current?.focus();
|
|
5218
|
-
}, [value, onChange]);
|
|
5229
|
+
}, [disabled, value, onChange]);
|
|
5219
5230
|
const handleRemove = useCallback((optionValue) => {
|
|
5231
|
+
if (disabled)
|
|
5232
|
+
return;
|
|
5220
5233
|
onChange(value.filter(v => v !== optionValue));
|
|
5221
5234
|
inputRef.current?.focus();
|
|
5222
|
-
}, [value, onChange]);
|
|
5235
|
+
}, [disabled, value, onChange]);
|
|
5223
5236
|
const allSelected = options.length > 0 && options.every(o => selectedSet.has(o.value));
|
|
5224
5237
|
const handleToggleAll = useCallback(() => {
|
|
5238
|
+
if (disabled)
|
|
5239
|
+
return;
|
|
5225
5240
|
if (allSelected) {
|
|
5226
5241
|
onChange([]);
|
|
5227
5242
|
}
|
|
@@ -5231,7 +5246,7 @@ function CwTagSelector({ id, options, value, onChange, labelProps, iconProps, al
|
|
|
5231
5246
|
setSearch("");
|
|
5232
5247
|
setFocusedIndex(-1);
|
|
5233
5248
|
inputRef.current?.focus();
|
|
5234
|
-
}, [allSelected, options, onChange]);
|
|
5249
|
+
}, [disabled, allSelected, options, onChange]);
|
|
5235
5250
|
const handleInputChange = useCallback((e) => {
|
|
5236
5251
|
setSearch(e.currentTarget.value);
|
|
5237
5252
|
if (!open)
|
|
@@ -5243,6 +5258,8 @@ function CwTagSelector({ id, options, value, onChange, labelProps, iconProps, al
|
|
|
5243
5258
|
setOpen(true);
|
|
5244
5259
|
}, [disabled]);
|
|
5245
5260
|
const handleKeyDown = useCallback((e) => {
|
|
5261
|
+
if (disabled)
|
|
5262
|
+
return;
|
|
5246
5263
|
if (e.key === "Backspace" && !search && value.length > 0) {
|
|
5247
5264
|
onChange(value.slice(0, -1));
|
|
5248
5265
|
return;
|
|
@@ -5287,7 +5304,7 @@ function CwTagSelector({ id, options, value, onChange, labelProps, iconProps, al
|
|
|
5287
5304
|
e.preventDefault();
|
|
5288
5305
|
setFocusedIndex(prev => (prev > 0 ? prev - 1 : 0));
|
|
5289
5306
|
}
|
|
5290
|
-
}, [open, search, value, filtered, focusedIndex, onChange, handleSelect]);
|
|
5307
|
+
}, [disabled, open, search, value, filtered, focusedIndex, onChange, handleSelect]);
|
|
5291
5308
|
// Scroll focused item into view
|
|
5292
5309
|
useEffect(() => {
|
|
5293
5310
|
if (focusedIndex >= 0 && listRef.current) {
|
|
@@ -5302,11 +5319,11 @@ function CwTagSelector({ id, options, value, onChange, labelProps, iconProps, al
|
|
|
5302
5319
|
const feedbackId = useId();
|
|
5303
5320
|
const feedbackIds = feedbackMessages.map((_, index) => `${feedbackId}-${index}`);
|
|
5304
5321
|
const describedBy = feedbackIds.join(" ") || undefined;
|
|
5305
|
-
return (jsxs("div", { ref: containerRef, className: `${styles$
|
|
5306
|
-
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)] }));
|
|
5307
5324
|
}
|
|
5308
5325
|
|
|
5309
|
-
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"};
|
|
5310
5327
|
|
|
5311
5328
|
function CwTreeView({ data, onSelect, allowParentSelection = false, selectedId: initialSelectedId = null, ...dataAttributes }) {
|
|
5312
5329
|
const [expanded, setExpanded] = useState(new Set());
|
|
@@ -5373,10 +5390,10 @@ function CwTreeView({ data, onSelect, allowParentSelection = false, selectedId:
|
|
|
5373
5390
|
const isLeaf = !hasChildren;
|
|
5374
5391
|
const isSelected = selectedId?.toString().toLowerCase() === node.id.toString().toLowerCase();
|
|
5375
5392
|
const selectable = allowParentSelection || isLeaf;
|
|
5376
|
-
return (jsxs("div", { className: styles$
|
|
5377
|
-
styles$
|
|
5378
|
-
isSelected && styles$
|
|
5379
|
-
!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"]
|
|
5380
5397
|
].filter(Boolean).join(' '), style: { paddingLeft: `${depth * 24 + 8}px` }, onClick: () => {
|
|
5381
5398
|
if (selectable) {
|
|
5382
5399
|
setSelectedId(node.id);
|
|
@@ -5388,7 +5405,7 @@ function CwTreeView({ data, onSelect, allowParentSelection = false, selectedId:
|
|
|
5388
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) => {
|
|
5389
5406
|
event.stopPropagation();
|
|
5390
5407
|
toggleExpand(node.id);
|
|
5391
|
-
} })), 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));
|
|
5392
5409
|
};
|
|
5393
5410
|
// Function to search for nodes that match the search term
|
|
5394
5411
|
const searchInTree = (nodes, searchTerm) => {
|
|
@@ -5412,7 +5429,7 @@ function CwTreeView({ data, onSelect, allowParentSelection = false, selectedId:
|
|
|
5412
5429
|
return results;
|
|
5413
5430
|
};
|
|
5414
5431
|
const filteredData = searchInTree(data, search);
|
|
5415
|
-
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"] })) })] }));
|
|
5416
5433
|
}
|
|
5417
5434
|
|
|
5418
5435
|
const EXACT_PHRASE = /"([^"]+)"/g;
|
|
@@ -5480,7 +5497,7 @@ function sortNavItems(items, alphabetically) {
|
|
|
5480
5497
|
return [...items].sort((first, second) => first.label.localeCompare(second.label));
|
|
5481
5498
|
}
|
|
5482
5499
|
|
|
5483
|
-
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"};
|
|
5484
5501
|
|
|
5485
5502
|
const SKELETON_ROW_COUNT = 5;
|
|
5486
5503
|
const defaultRenderLink = (item, children) => (jsx("a", { href: item.href, children: children }));
|
|
@@ -5492,13 +5509,13 @@ function CwNavMenu({ items, loading = false, isActive, renderLink = defaultRende
|
|
|
5492
5509
|
const renderItem = (item, level) => {
|
|
5493
5510
|
const children = item.children;
|
|
5494
5511
|
if (children && children.length > 0) {
|
|
5495
|
-
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));
|
|
5496
5513
|
}
|
|
5497
|
-
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));
|
|
5498
5515
|
};
|
|
5499
|
-
return (jsxs("nav", { ...navProps, className: [styles$
|
|
5500
|
-
? Array.from({ length: SKELETON_ROW_COUNT }, (_unused, index) => (jsx("li", { className: styles$
|
|
5501
|
-
: 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 })] }));
|
|
5502
5519
|
}
|
|
5503
5520
|
|
|
5504
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"};
|
|
@@ -5664,7 +5681,7 @@ function CwSearchInput(optionsProps) {
|
|
|
5664
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))) }) }))] }));
|
|
5665
5682
|
}
|
|
5666
5683
|
|
|
5667
|
-
var styles$
|
|
5684
|
+
var styles$b = {"value":"cw-search-async-module__value__z53xT"};
|
|
5668
5685
|
|
|
5669
5686
|
/**
|
|
5670
5687
|
* Generic API-backed search input. Fetches matching options from a caller-supplied `onSearch`
|
|
@@ -5928,10 +5945,10 @@ const CwSearchAsync = ({ onSearch, onChange, value, onResolveInitial, onResolveS
|
|
|
5928
5945
|
return (jsxs("div", { ...dataAttributes, className: `cw-search-input ${className}`, style: {
|
|
5929
5946
|
...(width ? { width } : {}),
|
|
5930
5947
|
...(labelProps?.labelWidth ? { "--label-width": labelProps.labelWidth } : {}),
|
|
5931
|
-
}, "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))) }) }))] }));
|
|
5932
5949
|
};
|
|
5933
5950
|
|
|
5934
|
-
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"};
|
|
5935
5952
|
|
|
5936
5953
|
const ARROW_SIZE = 8;
|
|
5937
5954
|
const SAFETY_MARGIN = 8;
|
|
@@ -6072,14 +6089,14 @@ const CwAnchoredMenu = ({ children, options, onSelect, placement = 'bottom', ope
|
|
|
6072
6089
|
document.removeEventListener("keydown", handleEscape);
|
|
6073
6090
|
};
|
|
6074
6091
|
}, [isOpen]);
|
|
6075
|
-
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: {
|
|
6076
6093
|
top: `${position.y}px`,
|
|
6077
6094
|
left: `${position.x}px`,
|
|
6078
|
-
}, "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) => {
|
|
6079
6096
|
e.stopPropagation();
|
|
6080
6097
|
e.preventDefault();
|
|
6081
6098
|
handleOptionClick(option);
|
|
6082
|
-
}, 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)] }));
|
|
6083
6100
|
};
|
|
6084
6101
|
|
|
6085
6102
|
/**
|
|
@@ -6152,13 +6169,13 @@ const CwContextMenu = ({ children, options, offset = DEFAULT_OFFSET, onSelect })
|
|
|
6152
6169
|
document.removeEventListener("keydown", handleEscape);
|
|
6153
6170
|
};
|
|
6154
6171
|
}, [isOpen]);
|
|
6155
|
-
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: {
|
|
6156
6173
|
top: `${position.y}px`,
|
|
6157
6174
|
left: `${position.x}px`,
|
|
6158
|
-
}, 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)] }));
|
|
6159
6176
|
};
|
|
6160
6177
|
|
|
6161
|
-
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"};
|
|
6162
6179
|
|
|
6163
6180
|
/**
|
|
6164
6181
|
* CwButtonSplit - A primary action joined to a dropdown of alternative actions.
|
|
@@ -6184,7 +6201,7 @@ function CwButtonSplit({ text, icon, onClick, actions, onSelect, variant = "soli
|
|
|
6184
6201
|
onSelect(action.key);
|
|
6185
6202
|
event.currentTarget.closest("[popover]")?.hidePopover();
|
|
6186
6203
|
};
|
|
6187
|
-
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))) }) })] }));
|
|
6188
6205
|
}
|
|
6189
6206
|
|
|
6190
6207
|
/**
|
|
@@ -6252,7 +6269,7 @@ function useRovingSingleSelect(options, value, onChange) {
|
|
|
6252
6269
|
return { activeIndex, getSegmentProps };
|
|
6253
6270
|
}
|
|
6254
6271
|
|
|
6255
|
-
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"};
|
|
6256
6273
|
|
|
6257
6274
|
/**
|
|
6258
6275
|
* CwButtonSwitch - Segmented single-select control: a strip of joined CwButtons.
|
|
@@ -6282,10 +6299,10 @@ var styles$5 = {"switch":"cw-button-switch-module__switch__vdWV-","segment":"cw-
|
|
|
6282
6299
|
*/
|
|
6283
6300
|
function CwButtonSwitch({ options, value, onChange, size = "medium", color = "primary", iconOnly = false, direction = "row", disabled = false, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, ...dataAttributes }) {
|
|
6284
6301
|
const { getSegmentProps } = useRovingSingleSelect(options, value, onChange);
|
|
6285
|
-
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) => {
|
|
6286
6303
|
const selected = option.value === value;
|
|
6287
6304
|
const segmentProps = getSegmentProps(index);
|
|
6288
|
-
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));
|
|
6289
6306
|
}) }));
|
|
6290
6307
|
}
|
|
6291
6308
|
|
|
@@ -6327,7 +6344,7 @@ function CwBtnCancel({ cw_btnOnclick, cw_btn_disabled, onClick, disabled, ...res
|
|
|
6327
6344
|
return (jsx(CwButton, { ...rest, variant: "icon", icon: "cancel", color: "danger", onClick: onClick ?? cw_btnOnclick, disabled: disabled ?? cw_btn_disabled, title: "Cancel" }));
|
|
6328
6345
|
}
|
|
6329
6346
|
|
|
6330
|
-
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"};
|
|
6331
6348
|
|
|
6332
6349
|
/**
|
|
6333
6350
|
* Renders a picker popup in a portal so it escapes overflow containers.
|
|
@@ -6567,7 +6584,7 @@ function CwDatePicker({ value, onChange, minDate, maxDate, disabledDates, disabl
|
|
|
6567
6584
|
...(maxDate ? [{ after: maxDate }] : []),
|
|
6568
6585
|
...(disabledMatcher ? [disabledMatcher] : []),
|
|
6569
6586
|
], [disabledDates, minDate, maxDate, disabledMatcher]);
|
|
6570
|
-
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: {
|
|
6571
6588
|
today: new Date(),
|
|
6572
6589
|
}, modifiersClassNames: {
|
|
6573
6590
|
today: "rdp-day-today",
|
|
@@ -7216,7 +7233,7 @@ function CwDateRangePicker({ value, onChange, minDate, maxDate, disabledDates, d
|
|
|
7216
7233
|
}
|
|
7217
7234
|
return undefined;
|
|
7218
7235
|
}, [value]);
|
|
7219
|
-
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: {
|
|
7220
7237
|
today: new Date(),
|
|
7221
7238
|
}, modifiersClassNames: {
|
|
7222
7239
|
today: "rdp-day-today",
|
|
@@ -7389,10 +7406,10 @@ function CwTimePicker({ value, onChange, interval = 15, minTime, maxTime, labelP
|
|
|
7389
7406
|
const displayValue = inputValue;
|
|
7390
7407
|
// If using native time input
|
|
7391
7408
|
if (useNative) {
|
|
7392
|
-
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] })] }) }));
|
|
7393
7410
|
}
|
|
7394
7411
|
// Custom time picker
|
|
7395
|
-
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) => {
|
|
7396
7413
|
const isSelected = time === value;
|
|
7397
7414
|
return (jsx("button", { ref: isSelected ? selectedItemRef : null, type: "button", className: isSelected ? timeStyles.selected : "", onClick: () => handleTimeSelect(time), children: time }, time));
|
|
7398
7415
|
}) }) }))] }), feedbackElements] })] }) }));
|
|
@@ -7700,7 +7717,7 @@ function CwDateTimePicker({ value, onChange, minDateTime, maxDateTime, disabledD
|
|
|
7700
7717
|
const displayMaxDate = useMemo(() => normalizedMaxDateTime && timezoneOffset !== undefined
|
|
7701
7718
|
? getDateInOffset(normalizedMaxDateTime, timezoneOffset)
|
|
7702
7719
|
: normalizedMaxDateTime, [normalizedMaxDateTime, timezoneOffset]);
|
|
7703
|
-
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)))] })] }) });
|
|
7704
7721
|
}
|
|
7705
7722
|
|
|
7706
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"};
|
|
@@ -7956,7 +7973,7 @@ function CwDateTimePickerCompact({ value, onChange, minDateTime, maxDateTime, di
|
|
|
7956
7973
|
...(maxDateTime ? [{ after: maxDateTime }] : []),
|
|
7957
7974
|
...(disabledMatcher ? [disabledMatcher] : []),
|
|
7958
7975
|
], [disabledDates, minDateTime, maxDateTime, disabledMatcher]);
|
|
7959
|
-
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: {
|
|
7960
7977
|
today: new Date(),
|
|
7961
7978
|
}, modifiersClassNames: {
|
|
7962
7979
|
today: "rdp-day-today",
|
|
@@ -8191,7 +8208,7 @@ function CwDatePickerTemporal({ value, onChange, minDate, maxDate, disabledDates
|
|
|
8191
8208
|
const defaultMonthJsDate = defaultMonth
|
|
8192
8209
|
? new Date(defaultMonth.year, defaultMonth.month - 1, 1)
|
|
8193
8210
|
: selectedJsDate;
|
|
8194
|
-
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: {
|
|
8195
8212
|
today: new Date(),
|
|
8196
8213
|
}, modifiersClassNames: {
|
|
8197
8214
|
today: "rdp-day-today",
|
|
@@ -8492,7 +8509,7 @@ function CwDateTimePickerTemporal({ value, onChange, minDateTime, maxDateTime, d
|
|
|
8492
8509
|
minTime,
|
|
8493
8510
|
maxTime,
|
|
8494
8511
|
});
|
|
8495
|
-
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)))] })] }) });
|
|
8496
8513
|
}
|
|
8497
8514
|
|
|
8498
8515
|
// dateRangePresetsTemporal.ts
|
|
@@ -8944,7 +8961,7 @@ function CwDateRangePickerTemporal({ value, onChange, minDate, maxDate, disabled
|
|
|
8944
8961
|
const defaultMonthJsDate = defaultMonth
|
|
8945
8962
|
? new Date(defaultMonth.year, defaultMonth.month - 1, 1)
|
|
8946
8963
|
: (value?.from ? plainDateToJsDate(value.from) : undefined);
|
|
8947
|
-
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: {
|
|
8948
8965
|
today: new Date(),
|
|
8949
8966
|
}, modifiersClassNames: {
|
|
8950
8967
|
today: "rdp-day-today",
|
|
@@ -9091,7 +9108,7 @@ class OnClickContextMenu {
|
|
|
9091
9108
|
}
|
|
9092
9109
|
}
|
|
9093
9110
|
|
|
9094
|
-
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"};
|
|
9095
9112
|
|
|
9096
9113
|
const getNow$1 = (isUtc) => {
|
|
9097
9114
|
let now = new Date();
|
|
@@ -9120,9 +9137,9 @@ const TimeLine = ({ left, top = "0px", height, color, width = "2px", zIndexNumbe
|
|
|
9120
9137
|
background: color,
|
|
9121
9138
|
pointerEvents: "none",
|
|
9122
9139
|
zIndex: zIndexNumber,
|
|
9123
|
-
}, className: styles$
|
|
9140
|
+
}, className: styles$6["scheduler-timeline-now"], children: isHeader && (jsx("span", { style: {
|
|
9124
9141
|
color: color,
|
|
9125
|
-
}, className: styles$
|
|
9142
|
+
}, className: styles$6["scheduler-timeline-label"], children: currentTime.toLocaleTimeString([], {
|
|
9126
9143
|
hour: '2-digit',
|
|
9127
9144
|
minute: '2-digit',
|
|
9128
9145
|
hour12: false
|
|
@@ -9149,17 +9166,17 @@ const SchedulerHeaderDivision = ({ topDivisions, bottomDivisions, classification
|
|
|
9149
9166
|
return (jsxs("div", { style: {
|
|
9150
9167
|
width: topWidth,
|
|
9151
9168
|
cursor: "pointer"
|
|
9152
|
-
}, 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))) })] }));
|
|
9153
9170
|
};
|
|
9154
9171
|
|
|
9155
9172
|
const HeaderTitle = ({ title, dividerText, width }) => {
|
|
9156
|
-
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 }) })] }));
|
|
9157
9174
|
};
|
|
9158
9175
|
|
|
9159
9176
|
const SchedulerHeader = ({ content, divisions, visibleDays, selectedDate, width, onEvent, timeLinePercentage, isUtc }) => {
|
|
9160
9177
|
const topWidth = (100 / visibleDays);
|
|
9161
9178
|
const { title, classification } = content;
|
|
9162
|
-
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) => {
|
|
9163
9180
|
e.preventDefault();
|
|
9164
9181
|
const clickPercentage = getPercentageFromMouseEvent(e);
|
|
9165
9182
|
const date = dateFromPercentage(selectedDate, visibleDays, clickPercentage);
|
|
@@ -9191,7 +9208,7 @@ function getStartEnd(day) {
|
|
|
9191
9208
|
};
|
|
9192
9209
|
}
|
|
9193
9210
|
|
|
9194
|
-
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"};
|
|
9195
9212
|
|
|
9196
9213
|
const BackgroundEvent = ({ value, heightRem }) => {
|
|
9197
9214
|
const backColor = getHSLColor(value.color, value.alpha ?? 0.5);
|
|
@@ -9218,13 +9235,13 @@ const BackgroundEvent = ({ value, heightRem }) => {
|
|
|
9218
9235
|
padding: "0.25rem",
|
|
9219
9236
|
color: textColor,
|
|
9220
9237
|
alignItems: value.contentAlign ?? "center"
|
|
9221
|
-
}, className: styles$
|
|
9238
|
+
}, className: styles$5["scheduler-event-container"], children: [value.icons ? jsx("div", { style: {
|
|
9222
9239
|
display: "flex",
|
|
9223
9240
|
flexDirection: "row",
|
|
9224
9241
|
alignItems: "center",
|
|
9225
9242
|
whiteSpace: "nowrap",
|
|
9226
9243
|
color: textColor,
|
|
9227
|
-
}, 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;
|
|
9228
9245
|
};
|
|
9229
9246
|
|
|
9230
9247
|
const eventIsVisible = (startDate, endDate, selectedDate, visibleDays) => {
|
|
@@ -9386,16 +9403,16 @@ const WeekEndLine = ({ left, top = "0px", height, widthPercentage }) => {
|
|
|
9386
9403
|
} }));
|
|
9387
9404
|
};
|
|
9388
9405
|
|
|
9389
|
-
var styles$
|
|
9406
|
+
var styles$4 = {"scheduler-row-header":"scheduler-row-module__scheduler-row-header__S-iv4"};
|
|
9390
9407
|
|
|
9391
9408
|
const DefaultRowHeader = ({ value, width, onEvent }) => {
|
|
9392
9409
|
return (jsxs("div", { style: {
|
|
9393
9410
|
width: width,
|
|
9394
9411
|
flexShrink: 0,
|
|
9395
|
-
|
|
9412
|
+
background: value.highlightColor,
|
|
9396
9413
|
}, onClick: (_) => {
|
|
9397
9414
|
onEvent(new OnClickRowHeader(value.rowId));
|
|
9398
|
-
}, 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] })] })] }));
|
|
9399
9416
|
};
|
|
9400
9417
|
|
|
9401
9418
|
const EventSideDrag = ({ left = false, heightRem, onStartEvent }) => {
|
|
@@ -9485,7 +9502,7 @@ const SchedulerEvent = ({ value, heightRem, onEvent, maxEventHeightRem }) => {
|
|
|
9485
9502
|
height: `calc(${effectiveHeight}rem - 4px)`,
|
|
9486
9503
|
opacity: eventTransparent ? 0.5 : 1,
|
|
9487
9504
|
...value.style,
|
|
9488
|
-
}, 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: {
|
|
9489
9506
|
display: "flex",
|
|
9490
9507
|
flexDirection: "row",
|
|
9491
9508
|
alignItems: "stretch",
|
|
@@ -9496,26 +9513,32 @@ const SchedulerEvent = ({ value, heightRem, onEvent, maxEventHeightRem }) => {
|
|
|
9496
9513
|
height: "100%",
|
|
9497
9514
|
}, children: [value.isResizable && (jsx(EventSideDrag, { left: true, heightRem: effectiveHeight * 0.7, onStartEvent: () => {
|
|
9498
9515
|
onEvent(new OnLeftDragStart(value.id));
|
|
9499
|
-
} })), jsxs("div", { className: styles$
|
|
9516
|
+
} })), jsxs("div", { className: styles$5["scheduler-event-container"], children: [jsx("div", { style: {
|
|
9500
9517
|
display: "flex",
|
|
9501
9518
|
flexDirection: "row",
|
|
9502
9519
|
alignItems: "center",
|
|
9503
9520
|
whiteSpace: "nowrap",
|
|
9504
9521
|
color: textColor,
|
|
9505
|
-
}, children: value.icons }), jsx("span", { style: {
|
|
9522
|
+
}, children: value.icons }), jsx("span", { className: value.underlay ? styles$5["label-halo"] : undefined, style: {
|
|
9506
9523
|
padding: "0.1rem",
|
|
9507
9524
|
color: textColor,
|
|
9508
9525
|
display: "flex",
|
|
9509
9526
|
flex: 1,
|
|
9510
9527
|
alignItems: "center",
|
|
9511
9528
|
width: "100%",
|
|
9512
|
-
|
|
9529
|
+
...(value.underlay && { "--label-halo-color": backColor }),
|
|
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: () => {
|
|
9513
9531
|
onEvent(new OnRightDragStart(value.id));
|
|
9514
|
-
} }))] }), value.primaryTimeMarkerColor && (jsx("div", { className: styles$
|
|
9532
|
+
} }))] }), value.primaryTimeMarkerColor && (jsx("div", { className: styles$5["time-marker"], style: {
|
|
9515
9533
|
backgroundColor: value.primaryTimeMarkerColor,
|
|
9516
|
-
} })), value.secondaryTimeMarkerColor && (jsx("div", { className: styles$
|
|
9534
|
+
} })), value.secondaryTimeMarkerColor && (jsx("div", { className: styles$5["time-marker"], style: {
|
|
9517
9535
|
backgroundColor: value.secondaryTimeMarkerColor,
|
|
9518
|
-
} }))] }) }) }, value.id) })]
|
|
9536
|
+
} }))] }) }) }, value.id) }), value.cornerMarker && (jsx("div", { className: styles$5["corner-marker"], style: {
|
|
9537
|
+
top: `${topOffsetPx}px`,
|
|
9538
|
+
left: `${value.left}%`,
|
|
9539
|
+
width: `${value.width}%`,
|
|
9540
|
+
height: `calc(${effectiveHeight}rem - 4px)`,
|
|
9541
|
+
}, children: jsx("span", { "data-interactive": value.cornerMarker.tooltip ? true : undefined, "data-pulse": value.cornerMarker.pulse ? true : undefined, children: value.cornerMarker.tooltip ? (jsx(CwTooltipNew, { content: value.cornerMarker.tooltip, position: "top", showDelay: 200, hide: isOnDrag, displayInline: true, children: jsx(CwBadge, { color: value.cornerMarker.color, size: "medium" }) })) : (jsx(CwBadge, { color: value.cornerMarker.color, size: "medium" })) }) }))] }));
|
|
9519
9542
|
};
|
|
9520
9543
|
|
|
9521
9544
|
const SchedulerRow = memo((props) => {
|
|
@@ -10044,13 +10067,13 @@ const Scheduler = (props) => {
|
|
|
10044
10067
|
position: "sticky",
|
|
10045
10068
|
top: 0,
|
|
10046
10069
|
zIndex: 1,
|
|
10047
|
-
}, 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 })] }) }));
|
|
10048
10071
|
};
|
|
10049
10072
|
|
|
10050
10073
|
class UiEvent {
|
|
10051
10074
|
}
|
|
10052
10075
|
|
|
10053
|
-
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"};
|
|
10054
10077
|
|
|
10055
10078
|
class OnPinRow {
|
|
10056
10079
|
id;
|
|
@@ -10078,15 +10101,15 @@ const PinRowHeader = ({ value, width, onEvent }) => {
|
|
|
10078
10101
|
flexShrink: 0,
|
|
10079
10102
|
height: "100%",
|
|
10080
10103
|
background: highlightColor
|
|
10081
|
-
}, 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: (_) => {
|
|
10082
10105
|
onEvent(value.isPinned ? new OnUnpinRow(value.rowId) : new OnPinRow(value.rowId));
|
|
10083
|
-
} }), 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: {
|
|
10084
10107
|
display: "flex",
|
|
10085
10108
|
flexDirection: "column",
|
|
10086
10109
|
justifyContent: "center",
|
|
10087
10110
|
alignItems: "flex-start",
|
|
10088
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 &&
|
|
10089
|
-
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);
|
|
10090
10113
|
};
|
|
10091
10114
|
|
|
10092
10115
|
const SuperScheduler = ({ id, state, header, rows, events, headerColumnComp, pinnedOrderCategory, unPinnedOrderCategory, backgroundEvents, indicatorRows = [], contextMenuItems, onEvent, rowHeightRem = 1.75, rowHeaderWidth, maxEventHeightRem, EventComp = SchedulerEvent, }) => {
|
|
@@ -10098,6 +10121,2324 @@ const SuperScheduler = ({ id, state, header, rows, events, headerColumnComp, pin
|
|
|
10098
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 })] }));
|
|
10099
10122
|
};
|
|
10100
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
|
+
|
|
10101
12442
|
/**
|
|
10102
12443
|
* API-backed airport search input for WinOps.
|
|
10103
12444
|
*
|
|
@@ -10532,4 +12873,4 @@ const CwFindCrewmember = ({ handleChange, placeHolder = "Search crew…", requir
|
|
|
10532
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))) }) }))] }));
|
|
10533
12874
|
};
|
|
10534
12875
|
|
|
10535
|
-
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 };
|