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