@cwellt_software/cwellt-reactjs-lib 1.4.0 → 1.4.2
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 +586 -213
- package/dist/index.css +2 -2
- package/dist/index.d.ts +205 -5
- package/dist/index.es.js +585 -214
- package/dist/src/common/hooks/useDropdownPortal.d.ts +86 -0
- package/dist/src/common/hooks/useDropdownPortal.d.ts.map +1 -0
- package/dist/src/components/control/action/search/CwSearch.d.ts +22 -0
- package/dist/src/components/control/action/search/CwSearch.d.ts.map +1 -1
- package/dist/src/components/control/choice/multi-filter/CwMultiFilter.d.ts.map +1 -1
- package/dist/src/components/control/choice/option/CwOption.d.ts +3 -1
- package/dist/src/components/control/choice/option/CwOption.d.ts.map +1 -1
- package/dist/src/components/control/choice/select/CwDropdown.d.ts +56 -0
- package/dist/src/components/control/choice/select/CwDropdown.d.ts.map +1 -0
- package/dist/src/components/control/choice/select/CwSelect.d.ts.map +1 -1
- package/dist/src/components/control/choice/tag-selector/CwTagSelector.d.ts +68 -0
- package/dist/src/components/control/choice/tag-selector/CwTagSelector.d.ts.map +1 -0
- package/dist/src/components/control/input/new-dates/CwDatePickerTemporal.d.ts +48 -0
- package/dist/src/components/control/input/new-dates/CwDatePickerTemporal.d.ts.map +1 -0
- package/dist/src/components/control/input/new-dates/datePickerHelpers.d.ts +20 -0
- package/dist/src/components/control/input/new-dates/datePickerHelpers.d.ts.map +1 -0
- package/dist/src/components/control/input/new-dates/usePickerPopup.d.ts +7 -8
- package/dist/src/components/control/input/new-dates/usePickerPopup.d.ts.map +1 -1
- package/dist/src/components/custom/find-airport/CwFindAirportComp.d.ts +36 -1
- package/dist/src/components/custom/find-airport/CwFindAirportComp.d.ts.map +1 -1
- package/dist/src/components/custom/find-crewmember/CwFindCrewmemberComp.d.ts +26 -0
- package/dist/src/components/custom/find-crewmember/CwFindCrewmemberComp.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-serverside/CwTableServerSide.d.ts +2 -1
- package/dist/src/components/display/data/table-serverside/CwTableServerSide.d.ts.map +1 -1
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/playground/PlaygroundApp.d.ts.map +1 -1
- package/dist/src/playground/pages/DatePickerTemporalPage.d.ts +3 -0
- package/dist/src/playground/pages/DatePickerTemporalPage.d.ts.map +1 -0
- package/dist/src/playground/pages/SelectorsPage.d.ts +3 -0
- package/dist/src/playground/pages/SelectorsPage.d.ts.map +1 -0
- package/dist/test/components/control/input/new-dates/datePickerHelpers.test.d.ts +2 -0
- package/dist/test/components/control/input/new-dates/datePickerHelpers.test.d.ts.map +1 -0
- package/package.json +1 -1
package/dist/index.es.js
CHANGED
|
@@ -632,7 +632,7 @@ function CwLoadingSmall(CwelltLoadingAppointements) {
|
|
|
632
632
|
return (jsx("div", { children: CwelltLoadingAppointements.isLoading === true ? (jsx("div", { className: "cw-loading-container", children: jsx("div", { className: "cw-loading" }) })) : (jsx("div", {})) }));
|
|
633
633
|
}
|
|
634
634
|
|
|
635
|
-
var styles$
|
|
635
|
+
var styles$q = {"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"};
|
|
636
636
|
|
|
637
637
|
// Constants moved outside to prevent recreation
|
|
638
638
|
const margin$1 = 16;
|
|
@@ -755,12 +755,12 @@ const CwGenericTooltip = ({ children, content = null, position = defaultPosition
|
|
|
755
755
|
const { setTooltipTimeout, clearTooltipTimeout } = useTooltipDelay(() => {
|
|
756
756
|
setIsVisible(true);
|
|
757
757
|
}, showDelay);
|
|
758
|
-
const { position: tooltipPosition, actualPosition } = useTooltipPosition(isVisible, containerRef, position, styles$
|
|
758
|
+
const { position: tooltipPosition, actualPosition } = useTooltipPosition(isVisible, containerRef, position, styles$q["cw-generic-tooltip-content"]);
|
|
759
759
|
// Memoize tooltip content creation
|
|
760
760
|
const tooltipContent = useMemo(() => {
|
|
761
761
|
if (hide || !isVisible || !content)
|
|
762
762
|
return null;
|
|
763
|
-
return createPortal(jsx("div", { className: styles$
|
|
763
|
+
return createPortal(jsx("div", { className: styles$q["cw-generic-tooltip-content"], "data-position": actualPosition, "data-visible": isVisible, "data-inline": displayInline, style: {
|
|
764
764
|
position: 'fixed',
|
|
765
765
|
top: `${tooltipPosition.top}px`,
|
|
766
766
|
left: `${tooltipPosition.left}px`,
|
|
@@ -775,10 +775,10 @@ const CwGenericTooltip = ({ children, content = null, position = defaultPosition
|
|
|
775
775
|
clearTooltipTimeout();
|
|
776
776
|
setIsVisible(false);
|
|
777
777
|
}, [clearTooltipTimeout]);
|
|
778
|
-
return (jsxs("div", { ref: containerRef, className: styles$
|
|
778
|
+
return (jsxs("div", { ref: containerRef, className: styles$q["cw-generic-tooltip"], onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, style: overlayStyle, "data-inline": displayInline, children: [tooltipContent, children] }));
|
|
779
779
|
};
|
|
780
780
|
|
|
781
|
-
var styles$
|
|
781
|
+
var styles$p = {"cw-tooltip-content":"cw-tooltip-module__cw-tooltip-content__RZczd","cw-tooltip":"cw-tooltip-module__cw-tooltip__1KYig"};
|
|
782
782
|
|
|
783
783
|
// Constants
|
|
784
784
|
const margin = 16;
|
|
@@ -993,7 +993,7 @@ const CwTooltipManager = () => {
|
|
|
993
993
|
if (!state.isVisible || !state.content) {
|
|
994
994
|
return null;
|
|
995
995
|
}
|
|
996
|
-
return createPortal(jsx("div", { ref: tooltipRef, className: styles$
|
|
996
|
+
return createPortal(jsx("div", { ref: tooltipRef, className: styles$p["cw-tooltip-content"], "data-position": actualPosition, "data-visible": state.isVisible, style: {
|
|
997
997
|
top: `${tooltipPosition.top}px`,
|
|
998
998
|
left: `${tooltipPosition.left}px`,
|
|
999
999
|
opacity: (isPositioned && !isMeasuring) ? 1 : 0,
|
|
@@ -1061,7 +1061,7 @@ const CwTooltipNew = ({ children, content = null, position = "right", dissapears
|
|
|
1061
1061
|
useEffect(() => {
|
|
1062
1062
|
ensureTooltipManager();
|
|
1063
1063
|
}, []);
|
|
1064
|
-
return (jsx("div", { ref: containerRef, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, style: overlayStyle, "data-inline": displayInline, className: styles$
|
|
1064
|
+
return (jsx("div", { ref: containerRef, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, style: overlayStyle, "data-inline": displayInline, className: styles$p["cw-tooltip"], children: children }));
|
|
1065
1065
|
};
|
|
1066
1066
|
|
|
1067
1067
|
function CwButton({ text, variant = 'solid', color = 'primary', className = '', icon = "", title, tooltipPosition = "bottom", children, ...buttonProps }) {
|
|
@@ -1114,7 +1114,7 @@ function CwModal(custModalProps) {
|
|
|
1114
1114
|
return (jsx("div", { children: custModalProps.modalState && (jsxs("div", { className: custModalProps.classNameModalOverlay + " cwelltModalOverlay", children: [jsx("div", { className: "cwelltModalOverlayBg", onClick: custModalProps.onCloseModal }), jsx(Draggable, { disabled: isModalDisabled, axis: "both", nodeRef: draggableRef, children: jsxs("div", { className: custModalProps.classNameModal + " cwelltContainerModal", ref: draggableRef, style: widthModalDef !== "40em" ? modalStyle.widthCustomStyle : modalStyle.widthDefStyle, children: [jsxs("div", { className: "cwelltModalHeader", onMouseOver: cwelltOnMouseOverModal, onMouseOut: cwelltOnMouseOutModal, children: [jsx("div", { className: "cwelltModalTitle", children: custModalProps.titleModal }), jsx("button", { className: "cwelltBtnCloseModal", onClick: custModalProps.onCloseModal, children: jsx("span", { className: "cwelltCloseIcon" }) })] }), jsx("div", { className: "cwelltModalBody", children: jsx("div", { className: "cwelltContainerModalBody", style: { position: "relative" }, children: custModalProps.children }) }), isHide === false ? (jsx("footer", { className: "legacy-modal-footer", onMouseOver: cwelltOnMouseOverModal, onMouseOut: cwelltOnMouseOutModal, children: jsx(CwButton, { variant: "icon", icon: "save", title: "Save", onClick: custModalProps.onSaveModal, form: custModalProps.formSaveModal, hidden: custModalProps.HideBtnModal, type: custModalProps.HtmlSubmitModal }) })) : (jsx("div", { style: { display: "none" } }))] }) })] })) }));
|
|
1115
1115
|
}
|
|
1116
1116
|
|
|
1117
|
-
var styles$
|
|
1117
|
+
var styles$o = {"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"};
|
|
1118
1118
|
|
|
1119
1119
|
function CwConfirmationPopup(props) {
|
|
1120
1120
|
const { isOpen, onConfirm, onCancel, message = "Are you sure you want to proceed?", confirmText = "Confirm", cancelText = "Cancel", placement = 'bottom', children } = props;
|
|
@@ -1178,15 +1178,15 @@ function CwConfirmationPopup(props) {
|
|
|
1178
1178
|
onCancel();
|
|
1179
1179
|
}
|
|
1180
1180
|
};
|
|
1181
|
-
return (jsxs(Fragment, { children: [renderChildren(), isOpen && (jsx("div", { className: styles$
|
|
1181
|
+
return (jsxs(Fragment, { children: [renderChildren(), isOpen && (jsx("div", { className: styles$o.overlayPositioned, onClick: handleOverlayClick, children: jsxs("div", { ref: popupRef, className: styles$o.popup, style: {
|
|
1182
1182
|
position: 'absolute',
|
|
1183
1183
|
top: `${position.top}px`,
|
|
1184
1184
|
left: `${position.left}px`,
|
|
1185
1185
|
transform: 'none'
|
|
1186
|
-
}, children: [jsx("p", { className: styles$
|
|
1186
|
+
}, children: [jsx("p", { className: styles$o.message, children: message }), jsxs("div", { className: styles$o.buttons, children: [jsx("button", { className: `${styles$o.button} ${styles$o.confirmButton}`, onClick: onConfirm, children: confirmText }), jsx("button", { className: `${styles$o.button} ${styles$o.cancelButton}`, onClick: onCancel, children: cancelText })] })] }) }))] }));
|
|
1187
1187
|
}
|
|
1188
1188
|
|
|
1189
|
-
var styles$
|
|
1189
|
+
var styles$n = {"cw-dialog-main":"cw-dialog-module__cw-dialog-main__cHxHt","cw-dialog-button-close":"cw-dialog-module__cw-dialog-button-close__9GRd8"};
|
|
1190
1190
|
|
|
1191
1191
|
// Helper function to parse size values
|
|
1192
1192
|
const parseSize = (size) => {
|
|
@@ -1459,7 +1459,7 @@ const CwDialog = props => {
|
|
|
1459
1459
|
}
|
|
1460
1460
|
}
|
|
1461
1461
|
}, [open]);
|
|
1462
|
-
const header = useMemo(() => (jsxs("header", { onMouseDown: handleMouseDown, children: [jsx("span", { children: headline }), customHeader || (jsx("button", { className: styles$
|
|
1462
|
+
const header = useMemo(() => (jsxs("header", { onMouseDown: handleMouseDown, children: [jsx("span", { children: headline }), customHeader || (jsx("button", { className: styles$n["cw-dialog-button-close"], onClick: onClose }))] })), [handleMouseDown, headline, customHeader, onClose]);
|
|
1463
1463
|
const content = useMemo(() => (jsx("section", { children: children })), [children]);
|
|
1464
1464
|
const footer = useMemo(() => (jsx("footer", { children: customFooter || (jsx(CwButton, { variant: "icon", icon: "save", title: "Save", onClick: onSave, tooltipPosition: "top", disabled: disableSave, "data-testid": "cw-dialog-save" })) })), [customFooter, onSave, disableSave]);
|
|
1465
1465
|
const resizeHandles = useMemo(() => size.autoHeight
|
|
@@ -1486,7 +1486,7 @@ const CwDialog = props => {
|
|
|
1486
1486
|
: `${convertFromPx(size.height, size.heightUnit)}${size.heightUnit}`;
|
|
1487
1487
|
return { displayWidth, displayHeight };
|
|
1488
1488
|
}, [size.width, size.height, size.widthUnit, size.heightUnit, size.autoHeight]);
|
|
1489
|
-
const dialogContent = (jsx("div", { "data-has-scrim": hasScrim, className: styles$
|
|
1489
|
+
const dialogContent = (jsx("div", { "data-has-scrim": hasScrim, className: styles$n["cw-dialog-main"], onMouseDown: handleScrimMouseDown, onMouseUp: handleScrimMouseUpOrLeave, onMouseLeave: handleScrimMouseUpOrLeave, "data-pressing": isPressingScrim, children: jsxs("dialog", { ...domProps, ref: dialogRef, style: {
|
|
1490
1490
|
left: `${position.x}px`,
|
|
1491
1491
|
top: `${position.y}px`,
|
|
1492
1492
|
width: displayDimensions.displayWidth,
|
|
@@ -1689,7 +1689,7 @@ function CwAlign(props) {
|
|
|
1689
1689
|
}, children: props.children }));
|
|
1690
1690
|
}
|
|
1691
1691
|
|
|
1692
|
-
var styles$
|
|
1692
|
+
var styles$m = {"card":"cw-card-module__card__HJUT0","clickable":"cw-card-module__clickable__Y-V3X","disabled":"cw-card-module__disabled__0wHh1","loading":"cw-card-module__loading__-fzlx","content":"cw-card-module__content__ma9qy","headerContent":"cw-card-module__headerContent__x4Jfl","footerTags":"cw-card-module__footerTags__80sSW","loadingOverlay":"cw-card-module__loadingOverlay__8-zVV"};
|
|
1693
1693
|
|
|
1694
1694
|
/**
|
|
1695
1695
|
* CwCard - A simple card component for displaying content in a contained format
|
|
@@ -1713,11 +1713,11 @@ const CwCard = ({ id, title, subtitle, alignment = 'center', children, footer, o
|
|
|
1713
1713
|
// Construct class names using CSS modules
|
|
1714
1714
|
const cardClassNames = [
|
|
1715
1715
|
'cw-card',
|
|
1716
|
-
styles$
|
|
1716
|
+
styles$m.card,
|
|
1717
1717
|
className,
|
|
1718
|
-
clickable ? styles$
|
|
1719
|
-
disabled ? styles$
|
|
1720
|
-
isLoading ? styles$
|
|
1718
|
+
clickable ? styles$m.clickable : '',
|
|
1719
|
+
disabled ? styles$m.disabled : '',
|
|
1720
|
+
isLoading ? styles$m.loading : '',
|
|
1721
1721
|
].filter(Boolean).join(' ');
|
|
1722
1722
|
// Handle click event when card is clickable
|
|
1723
1723
|
const handleClick = () => {
|
|
@@ -1734,15 +1734,15 @@ const CwCard = ({ id, title, subtitle, alignment = 'center', children, footer, o
|
|
|
1734
1734
|
// Determine if footer should be rendered
|
|
1735
1735
|
const hasFooter = hasChips || footer || (hasActions && direction === "row");
|
|
1736
1736
|
// Actions component to reuse
|
|
1737
|
-
const ActionsComponent = hasActions && !disabled ? (jsxs("div", { className: styles$
|
|
1737
|
+
const ActionsComponent = hasActions && !disabled ? (jsxs("div", { className: styles$m.actions, children: [extraActions ?? null, onEdit && jsx(CwButton, { variant: "icon", icon: "edit", onClick: onEdit }), onDelete && jsx(CwButton, { variant: "icon", icon: "delete", color: "danger", onClick: onDelete })] })) : null;
|
|
1738
1738
|
return (jsxs("div", { ...(id && { id }), className: cardClassNames, ...(style && { style }), ...(clickable && {
|
|
1739
1739
|
role: 'button',
|
|
1740
1740
|
tabIndex: 0,
|
|
1741
1741
|
onClick: handleClick,
|
|
1742
|
-
}), "data-variant": variant, "data-direction": direction, children: [hasHeader && (jsxs("header", { children: [(title || subtitle) && (jsxs("div", { className: styles$
|
|
1742
|
+
}), "data-variant": variant, "data-direction": direction, children: [hasHeader && (jsxs("header", { children: [(title || subtitle) && (jsxs("div", { className: styles$m.headerContent, "data-alignment": alignment, children: [title && jsx("h5", { children: title }), subtitle && jsx("strong", { children: subtitle })] })), direction === "column" && ActionsComponent] })), jsx("div", { className: styles$m.content, children: children }), hasFooter && (jsxs("footer", { children: [hasChips && (jsx("div", { className: styles$m.footerTags, children: chips.map((chip, index) => (jsx(CwChip, { label: chip.label, colorScheme: chip.colorScheme, className: styles$m.chip, ...(chip.customColor && { customColor: chip.customColor }), ...(chip.variant && { variant: chip.variant }), ...(chip.icon && { icon: chip.icon }) }, index))) })), footer && (jsx("div", { className: styles$m.footerContent, children: footer }))] })), direction === "row" && ActionsComponent, isLoading && (jsx("div", { className: styles$m.loadingOverlay, children: jsx(CwLoading, { isLoading: isLoading, size: "small" }) }))] }));
|
|
1743
1743
|
};
|
|
1744
1744
|
|
|
1745
|
-
var styles$
|
|
1745
|
+
var styles$l = {"cardContainer":"cw-card-list-module__cardContainer__l3YEh","pagination":"cw-card-list-module__pagination__5Ay78","pageInfo":"cw-card-list-module__pageInfo__uiMel","loading":"cw-card-list-module__loading__cYpND","emptyState":"cw-card-list-module__emptyState__RlqiS","sortControls":"cw-card-list-module__sortControls__mWgZA"};
|
|
1746
1746
|
|
|
1747
1747
|
function CwCardList({ items, renderCard, pageSize = 10, layout = 'grid', defaultCardWidth = 320, cardGap = 16, isLoading = false, emptyState, sortOptions = [], defaultSortKey, ...htmlProps }) {
|
|
1748
1748
|
const [currentPage, setCurrentPage] = useState(1);
|
|
@@ -1776,10 +1776,10 @@ function CwCardList({ items, renderCard, pageSize = 10, layout = 'grid', default
|
|
|
1776
1776
|
'--card-width': `${defaultCardWidth}px`,
|
|
1777
1777
|
'--card-gap': `${cardGap}px`
|
|
1778
1778
|
};
|
|
1779
|
-
return (jsxs("div", { ...htmlProps, children: [sortOptions.length > 0 && (jsxs("div", { className: styles$
|
|
1779
|
+
return (jsxs("div", { ...htmlProps, children: [sortOptions.length > 0 && (jsxs("div", { className: styles$l.sortControls, children: [jsx("label", { htmlFor: "cardlist-sort-select", children: "Sort by:" }), jsx("select", { id: "cardlist-sort-select", value: sortKey, onChange: (e) => handleSortChange(e.target.value), className: styles$l.sortSelect, children: sortOptions.map(option => (jsx("option", { value: option.key, children: option.label }, option.key))) }), jsx("button", { onClick: () => setSortDirection(prev => prev === 'asc' ? 'desc' : 'asc'), className: `cw-button-icon ${sortDirection === 'asc' ? 'cwi-arrow-up' : 'cwi-arrow-down'}`, title: `Sort ${sortDirection === 'asc' ? 'descending' : 'ascending'}` })] })), isLoading ? (jsx("div", { className: styles$l.loading, children: jsx(CwLoading, { isLoading: isLoading }) })) : sortedItems.length === 0 ? (jsx("div", { className: styles$l.emptyState, children: emptyState || jsx("p", { children: "No items to display" }) })) : (jsxs(Fragment, { children: [jsx("div", { className: `${styles$l.cardContainer}`, "data-layout": layout, style: gridStyle, children: visibleItems.map((item, index) => (jsx("div", { className: styles$l.cardWrapper, children: renderCard(item, index) }, index))) }), totalPages > 1 && (jsxs("div", { className: styles$l.pagination, children: [jsx("button", { disabled: currentPage === 1, onClick: () => setCurrentPage(p => Math.max(1, p - 1)), className: "cw-button-icon cwi-chevron-left" }), jsxs("span", { className: styles$l.pageInfo, children: [currentPage, " of ", totalPages] }), jsx("button", { disabled: currentPage === totalPages, onClick: () => setCurrentPage(p => Math.min(totalPages, p + 1)), className: "cw-button-icon cwi-chevron-right" })] }))] }))] }));
|
|
1780
1780
|
}
|
|
1781
1781
|
|
|
1782
|
-
var styles$
|
|
1782
|
+
var styles$k = {"cw-accordion":"cw-accordion-module__cw-accordion__ErvlW","cw-accordion-body":"cw-accordion-module__cw-accordion-body__xlI8b"};
|
|
1783
1783
|
|
|
1784
1784
|
/**
|
|
1785
1785
|
*
|
|
@@ -1796,7 +1796,7 @@ function CwAccordionContainer(CwelltAccordionContainerProps) {
|
|
|
1796
1796
|
setVisible_accordionBody(!isVisible_accordionBody);
|
|
1797
1797
|
};
|
|
1798
1798
|
// #endregion
|
|
1799
|
-
return (jsxs("div", { className: styles$
|
|
1799
|
+
return (jsxs("div", { className: styles$k["cw-accordion"], style: CwelltAccordionContainerProps.style, "data-open": isVisible_accordionBody, children: [jsxs("header", { onClick: () => showAccordionBody(), children: [jsx("div", { children: CwelltAccordionContainerProps.desc_text }), jsx("button", { className: "cw-button-icon" })] }), jsx("div", { className: styles$k["cw-accordion-body"], children: CwelltAccordionContainerProps.children })] }));
|
|
1800
1800
|
}
|
|
1801
1801
|
|
|
1802
1802
|
/**
|
|
@@ -2000,15 +2000,6 @@ function CwTable({ columns, data, pagination = false, pageSizeOptions = [5, 10,
|
|
|
2000
2000
|
window.addEventListener("mousemove", onMouseMove);
|
|
2001
2001
|
window.addEventListener("mouseup", onMouseUp);
|
|
2002
2002
|
};
|
|
2003
|
-
const scrollContainerStyle = useMemo(() => {
|
|
2004
|
-
if (stickyHeader || scrollHeight) {
|
|
2005
|
-
return {
|
|
2006
|
-
maxHeight: scrollHeight ?? 300,
|
|
2007
|
-
overflowY: "auto"
|
|
2008
|
-
};
|
|
2009
|
-
}
|
|
2010
|
-
return {}; // no height or scroll
|
|
2011
|
-
}, [stickyHeader, scrollHeight]);
|
|
2012
2003
|
const getColSpan = () => columns.length + (expandedRowRender ? 1 : 0);
|
|
2013
2004
|
const hasClassNameRow = (item) => {
|
|
2014
2005
|
return typeof item === "object" && item !== null && "classNameRow" in item;
|
|
@@ -2037,23 +2028,21 @@ function CwTable({ columns, data, pagination = false, pageSizeOptions = [5, 10,
|
|
|
2037
2028
|
: "cwi-chevron-right"}` }) })), columns.map(col => (jsx("td", { className: col.className ?? "", children: col.render ? col.render(item) : col.dataIndex ? String(item[col.dataIndex] ?? "") : "" }, `${itemKey}_${col.key}`)))] }), expandedRowRender && expandedRowKey === itemKey && (jsx("tr", { className: "cw-table-row-expanded", children: jsx("td", { colSpan: getColSpan(), children: expandedRowRender(item) }) }))] }, String(itemKey)));
|
|
2038
2029
|
});
|
|
2039
2030
|
};
|
|
2040
|
-
return (jsxs("div", { id: id, "data-testid": testId, className: `cw-table-container ${classNameContainer ?? ""}`, style:
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
? "sortable"
|
|
2054
|
-
:
|
|
2055
|
-
? "sortable-asc"
|
|
2056
|
-
: "sortable-desc" }))] }), jsx("span", { onMouseDown: (e) => startResize(e, col.key), className: "th-column-resizer" })] }, col.key)))] }) }), jsx("tbody", { children: renderTableBody() })] }) }), pagination && data.length > pageSizeOptions[0] && (jsxs("footer", { className: "cw-table-pagination", children: [jsxs("div", { className: "cw-table-pagination-size", children: [jsx(CwIcon, { iconId: "list" }), jsx("select", { value: localItemsPerPage, onChange: handleItemsPerPageChange, children: pageSizeOptions.map(size => (jsx("option", { value: size, children: pageLabel ? `${size} / ${pageLabel}` : size }, size))) })] }), jsxs("div", { className: "cw-table-pagination-nav", children: [jsx(CwButton, { onClick: () => handlePageChange(1), disabled: currentPage === 1 || totalPages === 1, variant: "icon", icon: "chevron-left-double" }), jsx(CwButton, { onClick: () => handlePageChange(currentPage - 1), disabled: currentPage === 1 || totalPages === 1, variant: "icon", icon: "chevron-left" }), jsx("input", { type: "text", inputMode: "numeric", value: currentPage, onChange: (e) => {
|
|
2031
|
+
return (jsxs("div", { id: id, "data-testid": testId, className: `cw-table-container${stickyHeader || scrollHeight ? " cw-table-scroll" : ""} ${classNameContainer ?? ""}`, style: { ...(scrollHeight ? { "--table-height": typeof scrollHeight === "number" ? `${scrollHeight}px` : scrollHeight } : {}), ...style }, children: [jsxs("table", { className: `cw-table ${className ?? ""}`, style: { width: "100%" }, children: [jsx("thead", { children: jsxs("tr", { children: [expandedRowRender && jsx("th", {}), columns.map(col => (jsxs("th", { onClick: () => col.sortable && col.dataIndex && handleSort(col.dataIndex), className: `${col.className ?? ""} ${sortConfig.key === col.dataIndex
|
|
2032
|
+
? sortConfig.direction
|
|
2033
|
+
: ""}`.trim(), style: {
|
|
2034
|
+
cursor: col.sortable ? "pointer" : "default",
|
|
2035
|
+
userSelect: "none",
|
|
2036
|
+
width: columnWidths[col.key] ?? col.width,
|
|
2037
|
+
...((col.width || columnWidths[col.key]) && {
|
|
2038
|
+
minWidth: 50,
|
|
2039
|
+
maxWidth: columnWidths[col.key] ?? col.width
|
|
2040
|
+
}),
|
|
2041
|
+
}, children: [jsxs("div", { className: "cw-flex-row cw-align-between-center", children: [jsx("span", { children: col.title }), col.sortable && col.dataIndex && (jsx(CwIcon, { style: { fontSize: "1.25em" }, iconId: sortConfig.key !== col.dataIndex
|
|
2042
|
+
? "sortable"
|
|
2043
|
+
: sortConfig.direction === "asc"
|
|
2044
|
+
? "sortable-asc"
|
|
2045
|
+
: "sortable-desc" }))] }), jsx("span", { onMouseDown: (e) => startResize(e, col.key), className: "th-column-resizer" })] }, col.key)))] }) }), jsx("tbody", { children: renderTableBody() })] }), pagination && data.length > pageSizeOptions[0] && (jsxs("footer", { className: "cw-table-pagination", children: [jsxs("div", { className: "cw-table-pagination-size", children: [jsx(CwIcon, { iconId: "list" }), jsx("select", { value: localItemsPerPage, onChange: handleItemsPerPageChange, children: pageSizeOptions.map(size => (jsx("option", { value: size, children: pageLabel ? `${size} / ${pageLabel}` : size }, size))) })] }), jsxs("div", { className: "cw-table-pagination-nav", children: [jsx(CwButton, { onClick: () => handlePageChange(1), disabled: currentPage === 1 || totalPages === 1, variant: "icon", icon: "chevron-left-double" }), jsx(CwButton, { onClick: () => handlePageChange(currentPage - 1), disabled: currentPage === 1 || totalPages === 1, variant: "icon", icon: "chevron-left" }), jsx("input", { type: "text", inputMode: "numeric", value: currentPage, onChange: (e) => {
|
|
2057
2046
|
const value = parseInt(e.target.value, 10);
|
|
2058
2047
|
if (!isNaN(value))
|
|
2059
2048
|
handlePageChange(value);
|
|
@@ -2065,7 +2054,7 @@ function CwTable({ columns, data, pagination = false, pageSizeOptions = [5, 10,
|
|
|
2065
2054
|
}, min: 1, max: totalPages }), jsx("span", { children: "/" }), jsx("span", { children: totalPages }), jsx(CwButton, { onClick: () => handlePageChange(currentPage + 1), disabled: currentPage === totalPages || totalPages === 1, variant: "icon", icon: "chevron-right" }), jsx(CwButton, { onClick: () => handlePageChange(totalPages), disabled: currentPage === totalPages || totalPages === 1, variant: "icon", icon: "chevron-right-double" })] })] }))] }));
|
|
2066
2055
|
}
|
|
2067
2056
|
|
|
2068
|
-
var styles$
|
|
2057
|
+
var styles$j = {"dropIndicator":"cw-sortable-table-module__dropIndicator__ov-Jz","dragging":"cw-sortable-table-module__dragging__MrLrz","skeletonRow":"cw-sortable-table-module__skeletonRow__vyD0M","skeleton":"cw-sortable-table-module__skeleton__QGXAD","saveBar":"cw-sortable-table-module__saveBar__3OdoZ cw-flex-row cw-align-center-center cw-gap-small"};
|
|
2069
2058
|
|
|
2070
2059
|
/**
|
|
2071
2060
|
* Hook that provides state management for CwSortableTable.
|
|
@@ -2128,9 +2117,9 @@ function useSortableTable(initialItems) {
|
|
|
2128
2117
|
}
|
|
2129
2118
|
|
|
2130
2119
|
// ─── Drop indicator row ───────────────────────────────────────────────────────
|
|
2131
|
-
const DropIndicatorRow = ({ colSpan }) => (jsx("tr", { className: styles$
|
|
2120
|
+
const DropIndicatorRow = ({ colSpan }) => (jsx("tr", { className: styles$j.dropIndicator, "aria-hidden": true, children: jsx("td", { colSpan: colSpan, children: jsx("div", {}) }) }));
|
|
2132
2121
|
// ─── Loading skeleton ─────────────────────────────────────────────────────────
|
|
2133
|
-
const LoadingRows = ({ colSpan }) => (jsx(Fragment, { children: Array.from({ length: 4 }).map((_, i) => (jsx("tr", { className: styles$
|
|
2122
|
+
const LoadingRows = ({ colSpan }) => (jsx(Fragment, { children: Array.from({ length: 4 }).map((_, i) => (jsx("tr", { className: styles$j.skeletonRow, children: jsx("td", { colSpan: colSpan, children: jsx("div", { className: styles$j.skeleton }) }) }, i))) }));
|
|
2134
2123
|
// ─── Component ────────────────────────────────────────────────────────────────
|
|
2135
2124
|
/**
|
|
2136
2125
|
* CwSortableTable
|
|
@@ -2217,11 +2206,11 @@ function CwSortableTable({ columns, data, rowKey = "key", onReorder, loading = f
|
|
|
2217
2206
|
return items.map((item, index) => {
|
|
2218
2207
|
const key = item[rowKey];
|
|
2219
2208
|
const isDragging = draggedItem?.[rowKey] === key;
|
|
2220
|
-
return (jsxs(React__default.Fragment, { children: [dropIndicatorIndex === index && (jsx(DropIndicatorRow, { colSpan: totalCols })), jsxs("tr", { draggable: true, onDragStart: (e) => handleDragStart(e, item), onDragOver: (e) => handleDragOver(e, index), onDragEnd: handleDragEnd, className: isDragging ? styles$
|
|
2209
|
+
return (jsxs(React__default.Fragment, { children: [dropIndicatorIndex === index && (jsx(DropIndicatorRow, { colSpan: totalCols })), jsxs("tr", { draggable: true, onDragStart: (e) => handleDragStart(e, item), onDragOver: (e) => handleDragOver(e, index), onDragEnd: handleDragEnd, className: isDragging ? styles$j.dragging : "", children: [showHandle && (jsx("td", { className: "cw-table-col-action", children: jsx(CwIcon, { iconId: "grip-dots", color: "neutral", style: { opacity: 0.75 } }) })), columns.map(col => (jsx("td", { className: col.className ?? "", style: col.width ? { width: col.width } : undefined, children: renderCell(item, col) }, `${String(key)}_${col.key}`)))] }), index === items.length - 1 && dropIndicatorIndex === items.length && (jsx(DropIndicatorRow, { colSpan: totalCols }))] }, String(key)));
|
|
2221
2210
|
});
|
|
2222
2211
|
};
|
|
2223
2212
|
// ── Render ──────────────────────────────────────────────────────────────
|
|
2224
|
-
return (jsxs("div", { className: `cw-table-container ${className ?? ""}`, style: containerStyle, children: [jsxs("table", { className: `cw-table cw-sortable-table ${tableClassName ?? ""}`, style: { width: "100%" }, children: [jsx("thead", { children: jsxs("tr", { children: [showHandle && jsx("th", { className: "cw-table-col-action" }), columns.map(col => (jsx("th", { className: col.className ?? "", style: col.width ? { width: col.width, minWidth: 50 } : undefined, children: col.title }, col.key)))] }) }), jsx("tbody", { children: renderBody() })] }), hasChanges && (jsxs("footer", { className: styles$
|
|
2213
|
+
return (jsxs("div", { className: `cw-table-container ${className ?? ""}`, style: containerStyle, children: [jsxs("table", { className: `cw-table cw-sortable-table ${tableClassName ?? ""}`, style: { width: "100%" }, children: [jsx("thead", { children: jsxs("tr", { children: [showHandle && jsx("th", { className: "cw-table-col-action" }), columns.map(col => (jsx("th", { className: col.className ?? "", style: col.width ? { width: col.width, minWidth: 50 } : undefined, children: col.title }, col.key)))] }) }), jsx("tbody", { children: renderBody() })] }), hasChanges && (jsxs("footer", { className: styles$j.saveBar, children: [jsx(CwButton, { variant: "outline", color: "neutral", icon: "undo", text: discardLabel, onClick: handleDiscard }), jsx(CwButton, { variant: "solid", color: "primary", icon: "save", text: saveLabel, onClick: () => handleSave(onReorder) })] }))] }));
|
|
2225
2214
|
}
|
|
2226
2215
|
|
|
2227
2216
|
/**
|
|
@@ -2277,7 +2266,7 @@ function CwSortableTable({ columns, data, rowKey = "key", onReorder, loading = f
|
|
|
2277
2266
|
*
|
|
2278
2267
|
* @returns React component
|
|
2279
2268
|
*/
|
|
2280
|
-
function CwTableServerSide({ columns, data, totalItems, pagination = false, pageSizeOptions = [5, 10, 20, 50], expandedRowRender, onExpand, className, classNameRow, style, classNameContainer, id, testId, textNoData = "No data available at the moment", rowKey = "key", loading = false, scrollHeight, stickyHeader = false, rowSelection, onChange, serverState, }) {
|
|
2269
|
+
function CwTableServerSide({ columns, data, totalItems, pagination = false, pageSizeOptions = [5, 10, 20, 50], pageLabel = "page", expandedRowRender, onExpand, className, classNameRow, style, classNameContainer, id, testId, textNoData = "No data available at the moment", rowKey = "key", loading = false, scrollHeight, stickyHeader = false, rowSelection, onChange, serverState, }) {
|
|
2281
2270
|
const [internalState, setInternalState] = useState({
|
|
2282
2271
|
page: 1,
|
|
2283
2272
|
pageSize: pageSizeOptions[0],
|
|
@@ -2426,10 +2415,10 @@ function CwTableServerSide({ columns, data, totalItems, pagination = false, page
|
|
|
2426
2415
|
if (isNaN(value) || value < 1 || value > totalPages) {
|
|
2427
2416
|
handlePageChange(1);
|
|
2428
2417
|
}
|
|
2429
|
-
}, min: 1, max: totalPages }), jsxs("span", { children: ["of ", totalPages] }), jsx("button", { onClick: () => handlePageChange(state.page + 1), disabled: state.page === totalPages || totalPages === 1, className: "cw-button-icon cwi-chevron-right", title: "Next" }), jsx("button", { onClick: () => handlePageChange(totalPages), disabled: state.page === totalPages || totalPages === 1, className: "cw-button-icon cwi-chevron-right-double", title: "Last" }), jsx("select", { value: state.pageSize, onChange: handleItemsPerPageChange, children: pageSizeOptions.map((size) => (
|
|
2418
|
+
}, min: 1, max: totalPages }), jsxs("span", { children: ["of ", totalPages] }), jsx("button", { onClick: () => handlePageChange(state.page + 1), disabled: state.page === totalPages || totalPages === 1, className: "cw-button-icon cwi-chevron-right", title: "Next" }), jsx("button", { onClick: () => handlePageChange(totalPages), disabled: state.page === totalPages || totalPages === 1, className: "cw-button-icon cwi-chevron-right-double", title: "Last" }), jsx("select", { value: state.pageSize, onChange: handleItemsPerPageChange, children: pageSizeOptions.map((size) => (jsx("option", { value: size, children: pageLabel ? `${size} / ${pageLabel}` : size }, size))) })] }))] }));
|
|
2430
2419
|
}
|
|
2431
2420
|
|
|
2432
|
-
var styles$
|
|
2421
|
+
var styles$i = {"cw-tabs":"cw-tabs-module__cw-tabs__1pmji","badge":"cw-tabs-module__badge__AmVxW","cw-tabs-content":"cw-tabs-module__cw-tabs-content__HTp8d"};
|
|
2433
2422
|
|
|
2434
2423
|
const TabIcon = ({ icon }) => {
|
|
2435
2424
|
if (!icon)
|
|
@@ -2479,7 +2468,7 @@ function CwTabs(CwTabsProps) {
|
|
|
2479
2468
|
const tabsListStyle = position === 'left' && CwTabsProps.tabsListWidth
|
|
2480
2469
|
? { minWidth: CwTabsProps.tabsListWidth }
|
|
2481
2470
|
: undefined;
|
|
2482
|
-
return (jsxs("div", { id: CwTabsProps.id, "data-testid": CwTabsProps.testId, className: styles$
|
|
2471
|
+
return (jsxs("div", { id: CwTabsProps.id, "data-testid": CwTabsProps.testId, className: styles$i['cw-tabs'] + (CwTabsProps.className ? ` ${CwTabsProps.className}` : "") + "", style: CwTabsProps.style, "data-tabs-position": position, children: [jsx("ul", { style: tabsListStyle, children: CwTabsProps.tabs.map(tab => (jsxs("li", { className: `${tab.key === activeTab ? "cw-tab-active" : ""}`, onClick: () => handleTabClick(tab), "data-active": tab.key === activeTab, "data-tab-key": tab.key, children: [jsx(TabIcon, { icon: tab.icon }), tab.title, (tab.badge !== undefined || tab.badgeColor) && (jsx("span", { className: styles$i['badge'], style: tab.badgeColor ? { backgroundColor: tab.badgeColor, color: getContrastColor(tab.badgeColor) } : undefined, children: tab.badge }))] }, tab.key))) }), jsx("div", { className: styles$i['cw-tabs-content'], "data-active-tab-key": activeTab ?? undefined, children: activeTab !== null && CwTabsProps.tabs.find(tab => tab.key === activeTab)?.content })] }));
|
|
2483
2472
|
}
|
|
2484
2473
|
|
|
2485
2474
|
/**
|
|
@@ -2503,10 +2492,10 @@ const CwExpandable = ({ briefing, onToggle, onOpen, onClose, children, ...detail
|
|
|
2503
2492
|
onClose?.(ev);
|
|
2504
2493
|
});
|
|
2505
2494
|
}, [onClose, onOpen, onToggle]);
|
|
2506
|
-
return (
|
|
2495
|
+
return (jsxs("details", { ref: myRef, ...detailsProps, className: "cw-expandable", children: [jsx("summary", { children: briefing }), children && jsx("section", { children: children })] }));
|
|
2507
2496
|
};
|
|
2508
2497
|
|
|
2509
|
-
var styles$
|
|
2498
|
+
var styles$h = {"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","detail-empty":"cw-master-detail-module__detail-empty__KFScK"};
|
|
2510
2499
|
|
|
2511
2500
|
const CwMasterDetail = ({ id, className, style, items, selectedKey, onSelect, detailContent, emptyDetail, searchPlaceholder = 'Search...', onSearch, masterHeader, masterFooter, masterWidth: initialWidth = 320, minMasterWidth = 240, maxMasterWidth = 500, resizable = true, showSearch = true, }) => {
|
|
2512
2501
|
const [masterWidth, setMasterWidth] = useState(initialWidth);
|
|
@@ -2542,10 +2531,10 @@ const CwMasterDetail = ({ id, className, style, items, selectedKey, onSelect, de
|
|
|
2542
2531
|
document.body.style.userSelect = '';
|
|
2543
2532
|
};
|
|
2544
2533
|
}, [isResizing, minMasterWidth, maxMasterWidth]);
|
|
2545
|
-
const rootClassName = [styles$
|
|
2546
|
-
return (jsxs("div", { id: id, ref: containerRef, className: rootClassName, style: style, children: [jsxs("aside", { style: { width: masterWidth, minWidth: masterWidth }, children: [showSearch && (jsx("search", { children: jsx("input", { type: "text", placeholder: searchPlaceholder, onChange: (e) => onSearch?.(e.target.value) }) })), masterHeader && (jsx("header", { children: masterHeader })), jsx("ul", { children: items.map((item) => (jsx("li", { "data-master-key": item.key, "data-selected": item.key === selectedKey, "data-disabled": item.disabled, onClick: () => onSelect?.(item.key), children: item.content }, item.key))) }), masterFooter && (jsx("footer", { children: masterFooter }))] }), resizable && (jsx("div", { className: styles$
|
|
2534
|
+
const rootClassName = [styles$h['cw-master-detail'], className].filter(Boolean).join(' ');
|
|
2535
|
+
return (jsxs("div", { id: id, ref: containerRef, className: rootClassName, style: style, children: [jsxs("aside", { style: { width: masterWidth, minWidth: masterWidth }, children: [showSearch && (jsx("search", { children: jsx("input", { type: "text", placeholder: searchPlaceholder, onChange: (e) => onSearch?.(e.target.value) }) })), masterHeader && (jsx("header", { children: masterHeader })), jsx("ul", { children: items.map((item) => (jsx("li", { "data-master-key": item.key, "data-selected": item.key === selectedKey, "data-disabled": item.disabled, onClick: () => onSelect?.(item.key), children: item.content }, item.key))) }), masterFooter && (jsx("footer", { children: masterFooter }))] }), resizable && (jsx("div", { className: styles$h.resizer, "data-resizing": isResizing, onMouseDown: handleMouseDown })), jsx("section", { className: styles$h.detail, children: selectedKey && detailContent
|
|
2547
2536
|
? detailContent
|
|
2548
|
-
: (jsx("div", { className: styles$
|
|
2537
|
+
: (jsx("div", { className: styles$h['detail-empty'], children: emptyDetail ?? 'Select an item to view details' })) })] }));
|
|
2549
2538
|
};
|
|
2550
2539
|
|
|
2551
2540
|
/**
|
|
@@ -2569,7 +2558,7 @@ const CwKeyValueList = ({ items, className = "", emptyValue = "-", direction = "
|
|
|
2569
2558
|
: emptyValue })] }, item.key))) }));
|
|
2570
2559
|
};
|
|
2571
2560
|
|
|
2572
|
-
var styles$
|
|
2561
|
+
var styles$g = {"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"};
|
|
2573
2562
|
|
|
2574
2563
|
function CwSortableList({ items, onReorder, renderItem, className = '', movedItems = new Set(), emptyMessage = "No elements to show", readOnly = false, maxHeight = "300px", }) {
|
|
2575
2564
|
const [draggedItem, setDraggedItem] = useState(null);
|
|
@@ -2632,15 +2621,15 @@ function CwSortableList({ items, onReorder, renderItem, className = '', movedIte
|
|
|
2632
2621
|
}
|
|
2633
2622
|
};
|
|
2634
2623
|
if (items.length === 0) {
|
|
2635
|
-
return (jsx("div", { className: `${styles$
|
|
2624
|
+
return (jsx("div", { className: `${styles$g.sortableList} ${styles$g.emptyState} ${className}`, children: jsx("div", { className: styles$g.emptyMessage, children: emptyMessage }) }));
|
|
2636
2625
|
}
|
|
2637
|
-
return (jsx("div", { className: `${styles$
|
|
2626
|
+
return (jsx("div", { className: `${styles$g.sortableList} ${className}`, style: { maxHeight }, children: items.map((item, index) => {
|
|
2638
2627
|
const { title, extraContent, actions, showHandle = true, expandedContent } = renderItem(item);
|
|
2639
2628
|
const isDragging = draggedItem?.id === item.id;
|
|
2640
2629
|
const isMoved = movedItems.has(item.id);
|
|
2641
2630
|
const canExpand = Boolean(expandedContent);
|
|
2642
2631
|
const isExpanded = expandedItems.has(item.id);
|
|
2643
|
-
return (jsxs(React__default.Fragment, { children: [dropIndicatorIndex === index && jsx("div", { className: styles$
|
|
2632
|
+
return (jsxs(React__default.Fragment, { children: [dropIndicatorIndex === index && jsx("div", { className: styles$g.dropIndicator }), jsxs("div", { className: `${styles$g.sortableItem} ${isDragging ? styles$g.dragging : ''} ${isMoved ? styles$g.moved : ''} ${readOnly ? styles$g.readOnly : ''}`, draggable: !readOnly, onDragStart: (e) => handleDragStart(e, item), onDragOver: (e) => handleDragOver(e, index), onDragEnd: handleDragEnd, children: [jsxs("header", { children: [canExpand ? (jsx(CwButton, { variant: "icon", onClick: () => toggleExpanded(item.id), type: "button", title: isExpanded ? "Collapse" : "Expand", icon: isExpanded ? "chevron-up" : "chevron-down" })) : showHandle ? (jsx("div", { className: styles$g.sortableHandle, children: jsx(CwIcon, { iconId: "grip-dots" }) })) : null, jsxs("div", { className: styles$g.sortableContent, children: [jsx("div", { className: styles$g.sortableTitle, children: title }), extraContent && jsx("div", { className: styles$g.sortableExtraContent, children: extraContent })] }), jsx("div", { className: styles$g.sortableActions, children: actions })] }), isExpanded && expandedContent && (jsx("div", { className: styles$g.expandedContent, children: expandedContent }))] }), !readOnly && dropIndicatorIndex === items.length && index === items.length - 1 && (jsx("div", { className: styles$g.dropIndicator }))] }, item.id));
|
|
2644
2633
|
}) }));
|
|
2645
2634
|
}
|
|
2646
2635
|
|
|
@@ -2817,7 +2806,7 @@ function CwFileUpload(fileUploadProps) {
|
|
|
2817
2806
|
return (jsxs("div", { children: [jsxs("div", { className: "row", children: [jsx("input", { className: "cw-button", type: "file", accept: fileUploadProps.accept, readOnly: true, placeholder: "No file selected...", onChange: handleFileChange }), previewURL && (jsx("div", { className: "row", children: jsx("img", { src: previewURL, alt: "Preview", style: { maxWidth: "200px", maxHeight: "200px" } }) }))] }), error && jsx("div", { className: "row error", children: error }), jsx("div", { className: "row", children: jsxs("label", { children: ["Please note: File/image has to be in ", fileUploadProps.acceptString, " format", fileUploadProps.sizeString && `, ${fileUploadProps.sizeString}`] }) })] }));
|
|
2818
2807
|
}
|
|
2819
2808
|
|
|
2820
|
-
var styles$
|
|
2809
|
+
var styles$f = {"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"};
|
|
2821
2810
|
|
|
2822
2811
|
const DEFAULT_LABELS = {
|
|
2823
2812
|
uploadDisabled: 'Upload disabled',
|
|
@@ -2984,12 +2973,12 @@ function CwFileUploadMultiple(fileUploadProps) {
|
|
|
2984
2973
|
}
|
|
2985
2974
|
}
|
|
2986
2975
|
};
|
|
2987
|
-
return (jsxs("div", { className: `${styles$
|
|
2976
|
+
return (jsxs("div", { className: `${styles$f.fileUploadContainer} ${fileUploadProps.className}`, children: [jsx("input", { ref: fileInputRef, type: "file", name: fileUploadProps.name, accept: fileUploadProps.accept, multiple: fileUploadProps.multiple, onChange: handleFileSelectInternal, disabled: fileUploadProps.disabled, "aria-label": "files", className: styles$f.hiddenInput }), selectedFiles.length === 0 && !existingFile ? (jsxs("div", { className: `${styles$f.uploadArea} ${fileUploadProps.disabled ? styles$f.uploadAreaDisabled : ''}`, onDragOver: handleDragOver, onDrop: handleDrop, onClick: !fileUploadProps.disabled ? handleButtonClick : undefined, children: [jsx(CwIcon, { iconId: "upload" }), jsx("p", { className: `${styles$f.uploadTitle}`, children: fileUploadProps.disabled ? labels.uploadDisabled : labels.clickToUpload }), jsxs("p", { className: `${styles$f.uploadSubtitle}`, children: [fileUploadProps.accept ? labels.acceptedFiles(fileUploadProps.accept) : labels.allTypesAccepted, !fileUploadProps.multiple && ` ${labels.singleFileOnly}`] })] })) : selectedFiles.length === 0 && existingFile ? (jsxs("div", { className: styles$f.filesContainer, children: [jsxs("div", { className: styles$f.fileItem, children: [jsxs("div", { className: styles$f.fileIcon, children: [jsx(CwIcon, { iconId: "page" }), jsx("span", { className: styles$f.fileExtension, children: getFileExtension(existingFile) })] }), jsx("div", { className: styles$f.fileInfo, children: jsx("p", { className: styles$f.fileName, children: existingFile }) }), jsx(CwButton, { variant: "icon", icon: "close", color: "neutral", onClick: () => {
|
|
2988
2977
|
setExistingFile(undefined);
|
|
2989
2978
|
if (fileUploadProps.onSelect) {
|
|
2990
2979
|
fileUploadProps.onSelect(null);
|
|
2991
2980
|
}
|
|
2992
|
-
}, disabled: fileUploadProps.disabled, className: styles$
|
|
2981
|
+
}, disabled: fileUploadProps.disabled, className: styles$f.smallButton })] }), jsx(CwButton, { text: labels.changeFile, icon: "refresh", onClick: handleButtonClick, disabled: fileUploadProps.disabled })] })) : (jsxs("div", { className: styles$f.filesContainer, children: [jsxs("div", { className: "cw-flex-row cw-align-between-center", children: [jsx("small", { className: styles$f.filesCount, children: labels.filesSelected(selectedFiles.length) }), jsx(CwButton, { onClick: removeAllFiles, disabled: fileUploadProps.disabled, color: "danger", variant: "outline", icon: "delete", text: labels.clearAll })] }), selectedFiles.map((file, index) => (jsxs("div", { className: styles$f.fileItem, children: [jsxs("div", { className: styles$f.fileIcon, children: [jsx(CwIcon, { iconId: "page" }), jsx("span", { className: styles$f.fileExtension, children: getFileExtension(file.name) })] }), jsxs("div", { className: styles$f.fileInfo, children: [jsx("p", { className: styles$f.fileName, children: file.name }), jsxs("p", { className: styles$f.fileSize, children: [(file.size / 1024).toFixed(1), " KB"] })] }), jsx(CwButton, { variant: "icon", icon: "close", color: "neutral", onClick: () => removeFile(index), className: styles$f.smallButton })] }, index))), fileUploadProps.multiple && (jsx(CwButton, { text: labels.addMoreFiles, icon: "plus", variant: "outline", onClick: handleButtonClick, disabled: fileUploadProps.disabled })), !fileUploadProps.multiple && (jsx(CwButton, { text: labels.changeFile, icon: "refresh", onClick: handleButtonClick, disabled: fileUploadProps.disabled }))] }))] }));
|
|
2993
2982
|
}
|
|
2994
2983
|
|
|
2995
2984
|
function CwInput(CwInputProps) {
|
|
@@ -3030,7 +3019,7 @@ function CwDigit(props) {
|
|
|
3030
3019
|
return (jsx("div", { className: "cw-input-text", children: jsxs(CwAlign, { ...alignProps, itemProp: inputProps.required === true ? "required" : "", children: [labelProps && (jsxs(CwLabel, { ...labelProps, children: [iconProps && jsx(CwIcon, { ...iconProps }), labelProps.text] })), jsx("input", { type: "number", ...inputProps }), buttonProps && jsx(CwButton, { ...buttonProps })] }) }));
|
|
3031
3020
|
}
|
|
3032
3021
|
|
|
3033
|
-
var styles$
|
|
3022
|
+
var styles$e = {"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"};
|
|
3034
3023
|
|
|
3035
3024
|
const CwColorPicker = ({ initialColor, onChange, previewText = "Color preview" }) => {
|
|
3036
3025
|
const [rgb, setRgb] = useState({ r: 255, g: 255, b: 255 });
|
|
@@ -3233,13 +3222,13 @@ const CwColorPicker = ({ initialColor, onChange, previewText = "Color preview" }
|
|
|
3233
3222
|
// Calculate background color for the main area based on current hue
|
|
3234
3223
|
const hueColor = hsvToRgb(hsv.h, 100, 100);
|
|
3235
3224
|
const hueHex = rgbToHex(hueColor.r, hueColor.g, hueColor.b);
|
|
3236
|
-
return (jsxs("div", { className: "cw-flex-column cw-gap-small", children: [jsxs("div", { className: "cw-flex-row cw-align-between-center cw-gap-small", children: [jsx("div", { className: styles$
|
|
3225
|
+
return (jsxs("div", { className: "cw-flex-column cw-gap-small", children: [jsxs("div", { className: "cw-flex-row cw-align-between-center cw-gap-small", children: [jsx("div", { className: styles$e.colorPreview, style: { backgroundColor: hexColor, color: getContrastColor(hexColor) }, children: previewText }), jsx("button", { type: "button", className: "cw-button", onClick: toggleInputMode, style: { minWidth: "3ch" }, children: inputMode === 'hex' ? 'RGB' : 'HEX' })] }), jsx("div", { className: "color-inputs-row", children: inputMode === 'hex' ? (jsx("input", { type: "text", value: hexColor, onChange: handleHexChange })) : (jsxs("div", { className: "cw-grid-base-3 cw-gap-small", children: [jsxs("div", { children: [jsx("label", { children: "R:" }), jsx("input", { type: "number", name: "r", min: "0", max: "255", value: rgb.r, onChange: handleRgbChange })] }), jsxs("div", { children: [jsx("label", { children: "G:" }), jsx("input", { type: "number", name: "g", min: "0", max: "255", value: rgb.g, onChange: handleRgbChange })] }), jsxs("div", { children: [jsx("label", { children: "B:" }), jsx("input", { type: "number", name: "b", min: "0", max: "255", value: rgb.b, onChange: handleRgbChange })] })] })) }), jsxs("div", { className: styles$e.colorPickerInteractiveArea, children: [jsx("div", { className: styles$e.hueBar, ref: hueBarRef, onClick: handleHueBarClick, children: jsx("div", { className: styles$e.hueBarSlider, style: { top: `${(360 - hsv.h) / 360 * 100}%` } }) }), jsxs("div", { className: styles$e.colorArea, ref: colorAreaRef, onClick: handleColorAreaClick, children: [jsxs("div", { className: styles$e.colorAreaOverlay, children: [jsx("div", { className: styles$e.hueBackground, style: { backgroundColor: hueHex } }), jsx("div", { className: styles$e.whiteGradient }), jsx("div", { className: styles$e.blackGradient })] }), jsx("div", { className: styles$e.colorAreaCursor, style: {
|
|
3237
3226
|
left: `${hsv.s}%`,
|
|
3238
3227
|
top: `${100 - hsv.v}%`
|
|
3239
3228
|
} })] })] })] }));
|
|
3240
3229
|
};
|
|
3241
3230
|
|
|
3242
|
-
var styles$
|
|
3231
|
+
var styles$d = {"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"};
|
|
3243
3232
|
|
|
3244
3233
|
const CwInputColor = ({ value, onChange, previewText = "Color preview", disabled = false, readOnly = false, width = '4rem', height = '2rem', labelProps, layoutProps, }) => {
|
|
3245
3234
|
const [isOpen, setIsOpen] = useState(false);
|
|
@@ -3372,11 +3361,11 @@ const CwInputColor = ({ value, onChange, previewText = "Color preview", disabled
|
|
|
3372
3361
|
break;
|
|
3373
3362
|
}
|
|
3374
3363
|
};
|
|
3375
|
-
return (jsxs("div", { className: `cw-input-color ${styles$
|
|
3364
|
+
return (jsxs("div", { className: `cw-input-color ${styles$d.container}`, "data-direction": layoutProps?.direction ?? "row", "data-align": layoutProps?.align, children: [labelProps && (jsx(CwLabel, { ...labelProps })), jsx("div", { ref: containerRef, onClick: handleToggle, className: `${styles$d.selectColor} ${disabled ? styles$d.disabled : ''} ${readOnly ? styles$d.readOnly : ''}`, style: {
|
|
3376
3365
|
width,
|
|
3377
3366
|
height,
|
|
3378
3367
|
backgroundColor: value,
|
|
3379
|
-
}, "aria-label": "Open color picker", "aria-expanded": isOpen, "aria-haspopup": "dialog", "aria-readonly": readOnly, role: "button", tabIndex: disabled ? -1 : 0, onKeyDown: handleKeyDown }), isOpen && createPortal(jsx("div", { ref: dropdownRef, className: styles$
|
|
3368
|
+
}, "aria-label": "Open color picker", "aria-expanded": isOpen, "aria-haspopup": "dialog", "aria-readonly": readOnly, role: "button", tabIndex: disabled ? -1 : 0, onKeyDown: handleKeyDown }), isOpen && createPortal(jsx("div", { ref: dropdownRef, className: styles$d.colorDropdown, style: dropdownStyle, role: "dialog", "aria-modal": "true", "aria-label": "Color picker", children: jsx(CwColorPicker, { initialColor: value, onChange: handleColorChange, previewText: previewText }) }), document.body)] }));
|
|
3380
3369
|
};
|
|
3381
3370
|
|
|
3382
3371
|
/**
|
|
@@ -4017,7 +4006,7 @@ const CwImageArea = forwardRef((props, ref) => {
|
|
|
4017
4006
|
});
|
|
4018
4007
|
CwImageArea.displayName = "CwImageArea";
|
|
4019
4008
|
|
|
4020
|
-
var styles$
|
|
4009
|
+
var styles$c = {"cw-weekday-selector":"cw-weekday-selector-module__cw-weekday-selector__Iz4GZ"};
|
|
4021
4010
|
|
|
4022
4011
|
/**
|
|
4023
4012
|
* This class represents a week where days can be selected or unselected
|
|
@@ -4157,15 +4146,15 @@ const CwWeekdaySelector = ({ value = "", onChange, disabled = false }) => {
|
|
|
4157
4146
|
setSelectedDays(newWeekdays);
|
|
4158
4147
|
onChange?.(newWeekdays.toString());
|
|
4159
4148
|
};
|
|
4160
|
-
return (jsx("div", { className: styles$
|
|
4149
|
+
return (jsx("div", { className: styles$c["cw-weekday-selector"], children: Weekdays.getFullWeek().map(day => (jsx("input", { type: "checkbox", "data-day": day, checked: selectedDays.toArray().includes(day), onChange: (e) => handleChange(day, e.target.checked), disabled: disabled }, day))) }));
|
|
4161
4150
|
};
|
|
4162
4151
|
|
|
4163
|
-
var styles$
|
|
4152
|
+
var styles$b = {"check-container":"cw-check-module__check-container__4nscZ","checkbox-group-container":"cw-check-module__checkbox-group-container__WMNbG"};
|
|
4164
4153
|
|
|
4165
4154
|
function CwCheck(props) {
|
|
4166
4155
|
const { labelProps, labelText, iconProps, alignment = "row", className, ...inputProps } = props;
|
|
4167
4156
|
const displayText = labelText || labelProps?.text;
|
|
4168
|
-
return (jsx("div", { className: `cw-check ${styles$
|
|
4157
|
+
return (jsx("div", { className: `cw-check ${styles$b["check-container"]}${className ? ` ${className}` : ''}`, children: jsxs("label", { "data-direction": alignment, children: [jsx("input", { type: "checkbox", ...inputProps }), displayText && (jsxs("span", { className: "cw-icon-text", children: [iconProps && jsx(CwIcon, { ...iconProps }), displayText] }))] }) }));
|
|
4169
4158
|
}
|
|
4170
4159
|
|
|
4171
4160
|
function CwCheckbox(CwCheckboxProps) {
|
|
@@ -4197,7 +4186,7 @@ function CwCheckboxGroup({ options, value = [], onChange, labelProps, alignProps
|
|
|
4197
4186
|
: [];
|
|
4198
4187
|
const flexDirection = alignProps?.flexDirection ?? "row";
|
|
4199
4188
|
const dataDirection = { 'data-direction': flexDirection };
|
|
4200
|
-
return (jsxs("div", { className: `cw-checkbox-group ${styles$
|
|
4189
|
+
return (jsxs("div", { className: `cw-checkbox-group ${styles$b["checkbox-group-container"]}${className ? ` ${className}` : ''}`, ...dataDirection, children: [jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsx("div", { className: `${optionsAlignment === "row" ? "cw-flex-column cw-gap-small" : "cw-flex-row cw-flex-wrap"}`, children: options.map((option) => (jsx(CwCheck, { labelText: option.label, checked: value.includes(option.value), disabled: disabled || option.disabled, alignment: optionsAlignment, onChange: (e) => handleChange(option.value, e.target.checked) }, option.value))) })] }), feedbackMessages.map((feedbackItem, index) => (jsx("p", { className: "cw-input-info", "data-color": feedbackItem.type, children: feedbackItem.message }, index)))] }));
|
|
4201
4190
|
}
|
|
4202
4191
|
|
|
4203
4192
|
function CwToggle(props) {
|
|
@@ -4215,13 +4204,16 @@ function CwToggle(props) {
|
|
|
4215
4204
|
* @example
|
|
4216
4205
|
* <CwSelect>
|
|
4217
4206
|
* <CwOption>A</CwOption>
|
|
4218
|
-
* <CwOption>
|
|
4207
|
+
* <CwOption color="#4a90d9">Blue option</CwOption>
|
|
4219
4208
|
* <CwOption>C</CwOption>
|
|
4220
4209
|
* </CwSelect>
|
|
4221
4210
|
*/
|
|
4222
4211
|
function CwOption(props) {
|
|
4223
|
-
const { text, ...optionProps } = props;
|
|
4224
|
-
|
|
4212
|
+
const { text, color, style, ...optionProps } = props;
|
|
4213
|
+
const mergedStyle = color
|
|
4214
|
+
? { ...style, backgroundColor: color, color: getContrastColor(color) }
|
|
4215
|
+
: style;
|
|
4216
|
+
return (jsx("option", { className: "cw-option", style: mergedStyle, ...optionProps, children: optionProps.children ?? text }));
|
|
4225
4217
|
}
|
|
4226
4218
|
|
|
4227
4219
|
/**
|
|
@@ -4255,7 +4247,248 @@ function CwSelect(props) {
|
|
|
4255
4247
|
: [];
|
|
4256
4248
|
const flexDirection = alignProps?.flexDirection ?? "row";
|
|
4257
4249
|
const dataDirection = { 'data-direction': flexDirection };
|
|
4258
|
-
|
|
4250
|
+
const { backgroundColor, color: textColor, ...wrapperStyle } = style ?? {};
|
|
4251
|
+
return (jsxs("div", { className: `cw-select${className ? ` ${className}` : ''}`, ...dataDirection, style: wrapperStyle, children: [jsxs(CwAlign, { ...alignProps, itemProp: selectProps.required === true ? "required" : "", children: [labelProps && (jsxs(CwLabel, { ...labelProps, children: [iconProps && jsx(CwIcon, { ...iconProps }), labelProps.text] })), jsxs("div", { className: "cw-flex-row cw-gap-small cw-flex-grow", children: [jsxs("select", { ...selectProps, style: backgroundColor ? { backgroundColor, color: textColor } : undefined, children: [placeholder && jsx(CwOption, { value: "", children: placeholder }), children] }), buttonProps && jsx(CwButton, { ...buttonProps })] })] }), feedbackMessages.map((feedbackItem, index) => (jsx("p", { className: "cw-input-info", "data-color": feedbackItem.type, children: feedbackItem.message }, index)))] }));
|
|
4252
|
+
}
|
|
4253
|
+
|
|
4254
|
+
// ─── Default strategy: below anchor, match width, flip above if needed ────────
|
|
4255
|
+
function defaultPositionStrategy(anchorRect, panelRect) {
|
|
4256
|
+
const panelH = panelRect?.height ?? 200;
|
|
4257
|
+
const spaceBelow = window.innerHeight - anchorRect.bottom;
|
|
4258
|
+
const top = spaceBelow >= panelH + 4
|
|
4259
|
+
? anchorRect.bottom + 4
|
|
4260
|
+
: Math.max(4, anchorRect.top - panelH);
|
|
4261
|
+
const width = anchorRect.width;
|
|
4262
|
+
const left = Math.max(4, Math.min(anchorRect.left, window.innerWidth - width - 4));
|
|
4263
|
+
return { top, left, width };
|
|
4264
|
+
}
|
|
4265
|
+
// ─── Hook ─────────────────────────────────────────────────────────────────────
|
|
4266
|
+
/**
|
|
4267
|
+
* Generic portal-based dropdown panel hook.
|
|
4268
|
+
*
|
|
4269
|
+
* Renders the panel as a direct child of `<body>` via `createPortal` so it
|
|
4270
|
+
* escapes any `overflow: hidden/auto` ancestor (dialogs, tabs, cards, etc.).
|
|
4271
|
+
* Positions the panel with `position: fixed` computed from the anchor element's
|
|
4272
|
+
* `getBoundingClientRect()`, and repositions on scroll and resize.
|
|
4273
|
+
*
|
|
4274
|
+
* The fix pattern is the same as {@link usePickerPopup} used by `CwDatePicker`.
|
|
4275
|
+
*
|
|
4276
|
+
* > **Future migration note:** Once the project's minimum browser baseline
|
|
4277
|
+
* > reaches Firefox 147 / Safari 26 (CSS Anchor Positioning — Baseline 2026),
|
|
4278
|
+
* > this hook can be replaced with `popover="manual"` + CSS `anchor()` with
|
|
4279
|
+
* > zero JS positioning.
|
|
4280
|
+
*
|
|
4281
|
+
* @example
|
|
4282
|
+
* ```tsx
|
|
4283
|
+
* const wrapperRef = useRef<HTMLDivElement>(null);
|
|
4284
|
+
* const { panelRef, panelStyle, renderPanel } = useDropdownPortal({
|
|
4285
|
+
* anchorRef: wrapperRef,
|
|
4286
|
+
* isOpen,
|
|
4287
|
+
* onClose: () => setIsOpen(false),
|
|
4288
|
+
* });
|
|
4289
|
+
*
|
|
4290
|
+
* return (
|
|
4291
|
+
* <div ref={wrapperRef}>
|
|
4292
|
+
* <input ... />
|
|
4293
|
+
* {renderPanel(
|
|
4294
|
+
* <div ref={panelRef} style={panelStyle} className={styles.dropdown}>
|
|
4295
|
+
* {options}
|
|
4296
|
+
* </div>
|
|
4297
|
+
* )}
|
|
4298
|
+
* </div>
|
|
4299
|
+
* );
|
|
4300
|
+
* ```
|
|
4301
|
+
*/
|
|
4302
|
+
function useDropdownPortal({ anchorRef, isOpen, onClose, positionStrategy = defaultPositionStrategy, }) {
|
|
4303
|
+
const panelRef = useRef(null);
|
|
4304
|
+
const [style, setStyle] = useState({
|
|
4305
|
+
position: "fixed",
|
|
4306
|
+
top: 0,
|
|
4307
|
+
left: 0,
|
|
4308
|
+
width: "auto",
|
|
4309
|
+
zIndex: 1000,
|
|
4310
|
+
right: "auto",
|
|
4311
|
+
marginLeft: 0,
|
|
4312
|
+
});
|
|
4313
|
+
const reposition = useCallback(() => {
|
|
4314
|
+
const anchor = anchorRef.current;
|
|
4315
|
+
if (!anchor)
|
|
4316
|
+
return;
|
|
4317
|
+
const anchorRect = anchor.getBoundingClientRect();
|
|
4318
|
+
const panelRect = panelRef.current?.getBoundingClientRect() ?? null;
|
|
4319
|
+
const { top, left, width } = positionStrategy(anchorRect, panelRect);
|
|
4320
|
+
setStyle({
|
|
4321
|
+
position: "fixed",
|
|
4322
|
+
top,
|
|
4323
|
+
left,
|
|
4324
|
+
width: width ?? "auto",
|
|
4325
|
+
zIndex: 1000,
|
|
4326
|
+
right: "auto",
|
|
4327
|
+
marginLeft: 0,
|
|
4328
|
+
});
|
|
4329
|
+
}, [anchorRef, positionStrategy]);
|
|
4330
|
+
// Reposition when opened; re-measure after first paint to get real panel size
|
|
4331
|
+
useEffect(() => {
|
|
4332
|
+
if (!isOpen)
|
|
4333
|
+
return;
|
|
4334
|
+
reposition();
|
|
4335
|
+
const raf = requestAnimationFrame(reposition);
|
|
4336
|
+
const handleUpdate = () => reposition();
|
|
4337
|
+
window.addEventListener("resize", handleUpdate);
|
|
4338
|
+
// Capture phase catches scroll on all scrollable ancestors
|
|
4339
|
+
window.addEventListener("scroll", handleUpdate, true);
|
|
4340
|
+
return () => {
|
|
4341
|
+
cancelAnimationFrame(raf);
|
|
4342
|
+
window.removeEventListener("resize", handleUpdate);
|
|
4343
|
+
window.removeEventListener("scroll", handleUpdate, true);
|
|
4344
|
+
};
|
|
4345
|
+
}, [isOpen, reposition]);
|
|
4346
|
+
// Close when the anchor scrolls out of view in any overflow container
|
|
4347
|
+
useEffect(() => {
|
|
4348
|
+
if (!isOpen)
|
|
4349
|
+
return;
|
|
4350
|
+
const anchor = anchorRef.current;
|
|
4351
|
+
if (!anchor)
|
|
4352
|
+
return;
|
|
4353
|
+
const observer = new IntersectionObserver(([entry]) => { if (!entry.isIntersecting)
|
|
4354
|
+
onClose(); }, { threshold: 0 });
|
|
4355
|
+
observer.observe(anchor);
|
|
4356
|
+
return () => observer.disconnect();
|
|
4357
|
+
}, [isOpen, anchorRef, onClose]);
|
|
4358
|
+
// Close on outside click (anchor + panel are both considered "inside")
|
|
4359
|
+
useEffect(() => {
|
|
4360
|
+
if (!isOpen)
|
|
4361
|
+
return;
|
|
4362
|
+
const handleMouseDown = (e) => {
|
|
4363
|
+
const target = e.target;
|
|
4364
|
+
if (anchorRef.current?.contains(target) || panelRef.current?.contains(target))
|
|
4365
|
+
return;
|
|
4366
|
+
onClose();
|
|
4367
|
+
};
|
|
4368
|
+
document.addEventListener("mousedown", handleMouseDown);
|
|
4369
|
+
return () => document.removeEventListener("mousedown", handleMouseDown);
|
|
4370
|
+
}, [isOpen, onClose, anchorRef, panelRef]);
|
|
4371
|
+
const renderPanel = useCallback((children) => {
|
|
4372
|
+
if (!isOpen)
|
|
4373
|
+
return null;
|
|
4374
|
+
return createPortal(children, document.body);
|
|
4375
|
+
}, [isOpen]);
|
|
4376
|
+
return { panelRef, panelStyle: style, renderPanel };
|
|
4377
|
+
}
|
|
4378
|
+
|
|
4379
|
+
var dropdownStyles = {"dropdown":"cw-choice-dropdown-module__dropdown__P0b4j","option":"cw-choice-dropdown-module__option__uR-ro","optionFocused":"cw-choice-dropdown-module__optionFocused__BYMxj","optionSelected":"cw-choice-dropdown-module__optionSelected__Lt8Xg","optionDisabled":"cw-choice-dropdown-module__optionDisabled__n3scM","searchBox":"cw-choice-dropdown-module__searchBox__0gox7","searchInput":"cw-choice-dropdown-module__searchInput__G5oRs","emptyState":"cw-choice-dropdown-module__emptyState__HO0Y7","colorCircle":"cw-choice-dropdown-module__colorCircle__x7mI9"};
|
|
4380
|
+
|
|
4381
|
+
/**
|
|
4382
|
+
* Custom dropdown component with colour-coded options, optional search filter, and full
|
|
4383
|
+
* keyboard navigation (↑ ↓ Enter Esc Tab).
|
|
4384
|
+
*
|
|
4385
|
+
* Use `CwDropdown` instead of the native `<select>` when options need colour indicators or
|
|
4386
|
+
* when inline search filtering is required.
|
|
4387
|
+
*
|
|
4388
|
+
* @example
|
|
4389
|
+
* <CwDropdown
|
|
4390
|
+
* options={[{ value: 'a', label: 'Option A', color: '#1976D2' }]}
|
|
4391
|
+
* value={selected}
|
|
4392
|
+
* onChange={setSelected}
|
|
4393
|
+
* labelProps={{ text: 'My Label' }}
|
|
4394
|
+
* />
|
|
4395
|
+
*/
|
|
4396
|
+
function CwDropdown({ options, value, onChange, labelProps, alignProps, placeholder = "Select...", allowSearch = false, disabled = false, required = false, feedback, className, style, ...otherProps }) {
|
|
4397
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
4398
|
+
const [searchText, setSearchText] = useState("");
|
|
4399
|
+
const [highlightedIndex, setHighlightedIndex] = useState(-1);
|
|
4400
|
+
const containerRef = useRef(null);
|
|
4401
|
+
const wrapperRef = useRef(null);
|
|
4402
|
+
const inputRef = useRef(null);
|
|
4403
|
+
const highlightedItemRef = useRef(null);
|
|
4404
|
+
const { panelRef, panelStyle, renderPanel } = useDropdownPortal({
|
|
4405
|
+
anchorRef: wrapperRef,
|
|
4406
|
+
isOpen,
|
|
4407
|
+
onClose: () => { setIsOpen(false); setSearchText(""); setHighlightedIndex(-1); },
|
|
4408
|
+
});
|
|
4409
|
+
const selectedOption = options.find(opt => opt.value === value);
|
|
4410
|
+
// Filter options based on search
|
|
4411
|
+
const filteredOptions = searchText
|
|
4412
|
+
? options.filter(opt => opt.label.toLowerCase().includes(searchText.toLowerCase()))
|
|
4413
|
+
: options;
|
|
4414
|
+
// Feedback handling
|
|
4415
|
+
const feedbackMessages = feedback
|
|
4416
|
+
? Array.isArray(feedback) ? feedback : [feedback]
|
|
4417
|
+
: [];
|
|
4418
|
+
// Scroll highlighted item into view
|
|
4419
|
+
useEffect(() => {
|
|
4420
|
+
if (isOpen && highlightedItemRef.current) {
|
|
4421
|
+
highlightedItemRef.current.scrollIntoView({
|
|
4422
|
+
block: "nearest",
|
|
4423
|
+
behavior: "smooth",
|
|
4424
|
+
});
|
|
4425
|
+
}
|
|
4426
|
+
}, [highlightedIndex, isOpen]);
|
|
4427
|
+
const handleTriggerClick = useCallback(() => {
|
|
4428
|
+
if (!disabled) {
|
|
4429
|
+
setIsOpen(!isOpen);
|
|
4430
|
+
setHighlightedIndex(-1);
|
|
4431
|
+
if (!isOpen && allowSearch) {
|
|
4432
|
+
setTimeout(() => inputRef.current?.focus(), 0);
|
|
4433
|
+
}
|
|
4434
|
+
}
|
|
4435
|
+
}, [isOpen, disabled, allowSearch]);
|
|
4436
|
+
const handleOptionSelect = useCallback((optionValue) => {
|
|
4437
|
+
const selected = options.find(opt => opt.value === optionValue);
|
|
4438
|
+
if (selected && !selected.disabled) {
|
|
4439
|
+
onChange?.(optionValue);
|
|
4440
|
+
setIsOpen(false);
|
|
4441
|
+
setSearchText("");
|
|
4442
|
+
setHighlightedIndex(-1);
|
|
4443
|
+
}
|
|
4444
|
+
}, [options, onChange]);
|
|
4445
|
+
const handleSearchChange = useCallback((e) => {
|
|
4446
|
+
setSearchText(e.target.value);
|
|
4447
|
+
setHighlightedIndex(-1);
|
|
4448
|
+
}, []);
|
|
4449
|
+
const handleKeyDown = useCallback((e) => {
|
|
4450
|
+
if (!isOpen) {
|
|
4451
|
+
if (e.key === "Enter" || e.key === " " || e.key === "ArrowDown") {
|
|
4452
|
+
e.preventDefault();
|
|
4453
|
+
setIsOpen(true);
|
|
4454
|
+
}
|
|
4455
|
+
return;
|
|
4456
|
+
}
|
|
4457
|
+
switch (e.key) {
|
|
4458
|
+
case "ArrowDown":
|
|
4459
|
+
e.preventDefault();
|
|
4460
|
+
setHighlightedIndex(prev => prev < filteredOptions.length - 1 ? prev + 1 : 0);
|
|
4461
|
+
break;
|
|
4462
|
+
case "ArrowUp":
|
|
4463
|
+
e.preventDefault();
|
|
4464
|
+
setHighlightedIndex(prev => prev > 0 ? prev - 1 : filteredOptions.length - 1);
|
|
4465
|
+
break;
|
|
4466
|
+
case "Enter":
|
|
4467
|
+
e.preventDefault();
|
|
4468
|
+
if (highlightedIndex >= 0 && highlightedIndex < filteredOptions.length) {
|
|
4469
|
+
handleOptionSelect(filteredOptions[highlightedIndex].value);
|
|
4470
|
+
}
|
|
4471
|
+
break;
|
|
4472
|
+
case "Escape":
|
|
4473
|
+
e.preventDefault();
|
|
4474
|
+
setIsOpen(false);
|
|
4475
|
+
setSearchText("");
|
|
4476
|
+
setHighlightedIndex(-1);
|
|
4477
|
+
break;
|
|
4478
|
+
case "Tab":
|
|
4479
|
+
setIsOpen(false);
|
|
4480
|
+
setSearchText("");
|
|
4481
|
+
setHighlightedIndex(-1);
|
|
4482
|
+
break;
|
|
4483
|
+
}
|
|
4484
|
+
}, [isOpen, highlightedIndex, filteredOptions, handleOptionSelect]);
|
|
4485
|
+
const flexDirection = alignProps?.flexDirection ?? "row";
|
|
4486
|
+
return (jsxs("div", { ref: containerRef, className: `cw-dropdown${className ? ` ${className}` : ""}`, "data-direction": flexDirection, style: style, ...otherProps, children: [jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxs("div", { className: "cw-dropdown-wrapper", ref: wrapperRef, children: [jsx("button", { type: "button", className: "cw-dropdown-trigger", onClick: handleTriggerClick, onKeyDown: handleKeyDown, disabled: disabled, "aria-haspopup": "listbox", "aria-expanded": isOpen, "aria-label": selectedOption?.label || placeholder, children: jsx("div", { className: "cw-dropdown-trigger-content", children: selectedOption ? (jsxs(Fragment, { children: [selectedOption.color && (jsx("span", { className: dropdownStyles.colorCircle, style: { backgroundColor: selectedOption.color } })), jsx("span", { children: selectedOption.label })] })) : (jsx("span", { className: "cw-dropdown-placeholder", children: placeholder })) }) }), jsx("div", { style: { display: "flex", alignItems: "center", paddingRight: "0.5rem", flexShrink: 0, cursor: disabled ? "not-allowed" : "pointer" }, onClick: handleTriggerClick, children: jsx(CwIcon, { iconId: "chevron-down", size: "small" }) }), renderPanel(jsxs("div", { ref: panelRef, className: dropdownStyles.dropdown, style: panelStyle, role: "listbox", onKeyDown: handleKeyDown, children: [allowSearch && (jsx("div", { className: dropdownStyles.searchBox, children: jsx("input", { ref: inputRef, type: "text", placeholder: "Search...", value: searchText, onChange: handleSearchChange, onKeyDown: handleKeyDown, className: dropdownStyles.searchInput, autoComplete: "off" }) })), jsx("ul", { children: filteredOptions.length > 0 ? (filteredOptions.map((option, index) => (jsxs("li", { ref: highlightedIndex === index ? highlightedItemRef : null, className: [
|
|
4487
|
+
dropdownStyles.option,
|
|
4488
|
+
value === option.value ? dropdownStyles.optionSelected : "",
|
|
4489
|
+
highlightedIndex === index ? dropdownStyles.optionFocused : "",
|
|
4490
|
+
option.disabled ? dropdownStyles.optionDisabled : "",
|
|
4491
|
+
].filter(Boolean).join(" "), onClick: () => !option.disabled && handleOptionSelect(option.value), onMouseEnter: () => setHighlightedIndex(index), role: "option", "aria-selected": value === option.value, "aria-disabled": option.disabled, children: [option.color && (jsx("span", { className: dropdownStyles.colorCircle, style: { backgroundColor: option.color } })), jsx("span", { children: option.label })] }, option.value)))) : (jsx("li", { className: dropdownStyles.emptyState, children: "No options available" })) })] }))] })] }), feedbackMessages.map((item, index) => (jsx("p", { className: "cw-input-info", "data-color": item.type, children: item.message }, index)))] }));
|
|
4259
4492
|
}
|
|
4260
4493
|
|
|
4261
4494
|
function CwDropdownFilter(props) {
|
|
@@ -4268,7 +4501,7 @@ function CwDropdownFilter(props) {
|
|
|
4268
4501
|
return (jsxs("div", { id: props.IdContent, className: containerClassName, children: [props.children, isVisible && (jsx("nav", { className: "cw-dropdown-menu", style: inlineStyles, onMouseLeave: props.onMouseLeave, id: props.idDropDownFilter, role: "menu", children: jsx("ul", { role: "none", children: props.DataSourceDropDownItem?.map((item) => (jsx("li", { id: item.IdDropDown_filter, role: "menuitem", children: item.dropDownFilter_desc }, item.IdDropDown_filter))) }) }))] }));
|
|
4269
4502
|
}
|
|
4270
4503
|
|
|
4271
|
-
var styles$
|
|
4504
|
+
var styles$a = {"container":"cw-popover-button-module__container__YSWQU","panel":"cw-popover-button-module__panel__C-BTn"};
|
|
4272
4505
|
|
|
4273
4506
|
/**
|
|
4274
4507
|
* Button that toggles a floating panel with arbitrary children.
|
|
@@ -4315,7 +4548,7 @@ function CwPopoverButton({ children, panelWidth = "auto", placement = "bottom-st
|
|
|
4315
4548
|
}
|
|
4316
4549
|
panel.togglePopover();
|
|
4317
4550
|
}, [placement]);
|
|
4318
|
-
return (jsxs("div", { ref: containerRef, className: styles$
|
|
4551
|
+
return (jsxs("div", { ref: containerRef, className: styles$a.container, children: [jsx(CwButton, { ...buttonProps, onClick: handleClick }), jsx("div", { ref: panelCallbackRef, className: `${styles$a.panel}${panelClassName ? ` ${panelClassName}` : ""}`, style: { width: panelWidth }, children: children })] }));
|
|
4319
4552
|
}
|
|
4320
4553
|
|
|
4321
4554
|
function itemsToMultiFilterTags(items, nameKey, valueKey, category, primaryColor, onPrimaryColor) {
|
|
@@ -4333,13 +4566,13 @@ function itemsToMultiFilterTags(items, nameKey, valueKey, category, primaryColor
|
|
|
4333
4566
|
return result;
|
|
4334
4567
|
}
|
|
4335
4568
|
|
|
4336
|
-
var styles$
|
|
4569
|
+
var styles$9 = {"cw-multifilter-tag":"cw-multi-filter-tag-module__cw-multifilter-tag__Epda-"};
|
|
4337
4570
|
|
|
4338
4571
|
const CwMultiFilterTag = props => {
|
|
4339
4572
|
const { ID, Name, Value, Category, Removable, PrimaryColor, Selectable, Selected } = props;
|
|
4340
4573
|
const backgroundColor = `rgb(${PrimaryColor.r},${PrimaryColor.g},${PrimaryColor.b})`;
|
|
4341
4574
|
const isOutlineMode = Selected && !Removable;
|
|
4342
|
-
return (jsx("li", { className: styles$
|
|
4575
|
+
return (jsx("li", { className: styles$9["cw-multifilter-tag"], id: ID, "data-value": Value, "data-category": Category, "data-selected": !!Selected, title: props.tooltip ?? Name, onClick: () => {
|
|
4343
4576
|
if (Selectable)
|
|
4344
4577
|
props.OnSelect?.(props);
|
|
4345
4578
|
}, children: jsx(CwChip, { label: Name, colorScheme: "custom", customColor: backgroundColor, variant: isOutlineMode ? "outline" : "soft", closable: Removable, onClose: () => props.OnRemove?.(props),
|
|
@@ -4349,7 +4582,7 @@ const CwMultiFilterTag = props => {
|
|
|
4349
4582
|
} : undefined }) }));
|
|
4350
4583
|
};
|
|
4351
4584
|
|
|
4352
|
-
var styles$
|
|
4585
|
+
var styles$8 = {"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"};
|
|
4353
4586
|
|
|
4354
4587
|
/**
|
|
4355
4588
|
* A multiple filter selector, a MULTI-SELECT even. Allows users to select and filter items based on tags.
|
|
@@ -4497,19 +4730,14 @@ const CwMultiFilter = ({ allTags, id, onChangeSelectedTags, selectedTags, placeh
|
|
|
4497
4730
|
const [selectedCategory, setSelectedCategory] = useState("All");
|
|
4498
4731
|
const componentRef = useRef(null);
|
|
4499
4732
|
const inputRef = useRef(null);
|
|
4733
|
+
const wrapperRef = useRef(null);
|
|
4734
|
+
const { panelRef, panelStyle, renderPanel } = useDropdownPortal({
|
|
4735
|
+
anchorRef: wrapperRef,
|
|
4736
|
+
isOpen: isPanelOpen,
|
|
4737
|
+
onClose: () => { setIsPanelOpen(false); setFilteredTags(getUnselectedAll()); setSelectedCategory("All"); setInputTextValue(""); },
|
|
4738
|
+
});
|
|
4500
4739
|
useEffect(() => {
|
|
4501
|
-
|
|
4502
|
-
if (componentRef.current && !componentRef.current.contains(event.target)) {
|
|
4503
|
-
setIsPanelOpen(false);
|
|
4504
|
-
setFilteredTags(new Set());
|
|
4505
|
-
setSelectedCategory("All");
|
|
4506
|
-
setInputTextValue("");
|
|
4507
|
-
}
|
|
4508
|
-
}
|
|
4509
|
-
document.addEventListener("mousedown", handleClickOutside);
|
|
4510
|
-
return () => {
|
|
4511
|
-
document.removeEventListener("mousedown", handleClickOutside);
|
|
4512
|
-
};
|
|
4740
|
+
// Outside-click is now handled by useDropdownPortal
|
|
4513
4741
|
}, []);
|
|
4514
4742
|
const categoriesMappedToTags = () => {
|
|
4515
4743
|
const categoriesMap = new Map();
|
|
@@ -4520,9 +4748,10 @@ const CwMultiFilter = ({ allTags, id, onChangeSelectedTags, selectedTags, placeh
|
|
|
4520
4748
|
});
|
|
4521
4749
|
return categoriesMap;
|
|
4522
4750
|
};
|
|
4751
|
+
const getUnselectedAll = (excluded = selectedTags) => new Set(Array.from(allTags).filter(tag => !Array.from(excluded).some(t => t.ID === tag.ID)));
|
|
4523
4752
|
const handleClickCategory = (category) => {
|
|
4524
4753
|
setSelectedCategory(category);
|
|
4525
|
-
setFilteredTags(category
|
|
4754
|
+
setFilteredTags(category !== "All" ? (categoriesMappedToTags().get(category) ?? new Set()) : getUnselectedAll());
|
|
4526
4755
|
};
|
|
4527
4756
|
const rgbAString = (r, g, b) => {
|
|
4528
4757
|
return `rgb(${r}, ${g}, ${b})`;
|
|
@@ -4554,7 +4783,7 @@ const CwMultiFilter = ({ allTags, id, onChangeSelectedTags, selectedTags, placeh
|
|
|
4554
4783
|
setFilteredTags(new Set(newFilterTags));
|
|
4555
4784
|
}
|
|
4556
4785
|
else {
|
|
4557
|
-
setFilteredTags(
|
|
4786
|
+
setFilteredTags(getUnselectedAll());
|
|
4558
4787
|
}
|
|
4559
4788
|
};
|
|
4560
4789
|
const addTag = (newTag) => {
|
|
@@ -4568,7 +4797,13 @@ const CwMultiFilter = ({ allTags, id, onChangeSelectedTags, selectedTags, placeh
|
|
|
4568
4797
|
const updatedTags = new Set(selectedTags).add(newTag);
|
|
4569
4798
|
onChangeSelectedTags(updatedTags);
|
|
4570
4799
|
setInputTextValue("");
|
|
4571
|
-
|
|
4800
|
+
setFilteredTags(selectedCategory === "All" ? getUnselectedAll(updatedTags) : prev => {
|
|
4801
|
+
const next = new Set(prev);
|
|
4802
|
+
const toRemove = Array.from(next).find(t => t.ID === newTag.ID);
|
|
4803
|
+
if (toRemove)
|
|
4804
|
+
next.delete(toRemove);
|
|
4805
|
+
return next;
|
|
4806
|
+
});
|
|
4572
4807
|
}
|
|
4573
4808
|
};
|
|
4574
4809
|
const removeTag = (id) => {
|
|
@@ -4592,9 +4827,9 @@ const CwMultiFilter = ({ allTags, id, onChangeSelectedTags, selectedTags, placeh
|
|
|
4592
4827
|
inputRef.current?.focus();
|
|
4593
4828
|
}
|
|
4594
4829
|
};
|
|
4595
|
-
return (jsxs("form", { ref: componentRef, id: id, className: `${styles$
|
|
4830
|
+
return (jsxs("form", { ref: componentRef, id: id, className: `${styles$8["cw-multi-filter"]}${className ? ` ${className}` : ""}`, style: style, onSubmit: (e) => {
|
|
4596
4831
|
e.preventDefault();
|
|
4597
|
-
}, children: [jsxs("div", { onClick: handleDivClick, className: styles$
|
|
4832
|
+
}, children: [jsxs("div", { onClick: handleDivClick, ref: wrapperRef, className: styles$8["cw-multi-filter-search"], style: isPanelOpen ? { outline: "1px solid var(--cw-color-outline-variant)" } : {}, children: [jsxs("ul", { id: id + "_selected_filters", children: [Array.from(selectedTags).map(tag => (createElement(CwMultiFilterTag, { ...tag, key: tag.ID, Selectable: false, Removable: true, OnRemove: () => removeTag(tag.ID) }))), jsx("input", { type: "text", id: id + "_input", ref: inputRef, value: inputTextValue, spellCheck: false, onFocus: () => setIsPanelOpen(true), onChange: e => handleInputText(e.currentTarget.value), autoComplete: "off", placeholder: selectedTags.size === 0 ? placeholder : "", onKeyDown: e => {
|
|
4598
4833
|
switch (e.key) {
|
|
4599
4834
|
case "Enter":
|
|
4600
4835
|
case "Tab": {
|
|
@@ -4621,21 +4856,10 @@ const CwMultiFilter = ({ allTags, id, onChangeSelectedTags, selectedTags, placeh
|
|
|
4621
4856
|
break;
|
|
4622
4857
|
}
|
|
4623
4858
|
}
|
|
4624
|
-
} })] }), selectedTags.size > 0
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
? {
|
|
4629
|
-
backgroundColor: getColor(category).primary,
|
|
4630
|
-
color: getColor(category).onPrimary,
|
|
4631
|
-
outline: "2px solid " + getColor(category).onPrimary,
|
|
4632
|
-
outlineOffset: "-2px",
|
|
4633
|
-
fontWeight: 400
|
|
4634
|
-
}
|
|
4635
|
-
: {
|
|
4636
|
-
backgroundColor: getColor(category).primary,
|
|
4637
|
-
color: getColor(category).onPrimary,
|
|
4638
|
-
}, onClick: () => handleClickCategory(category), children: category }, category)))] }), jsx("ul", { children: Array.from(filteredTags)
|
|
4859
|
+
} })] }), selectedTags.size > 0 && (jsx(CwButton, { type: "button", variant: "icon", color: "neutral", icon: "close", onMouseDown: e => e.preventDefault(), onClick: (e) => { e.stopPropagation(); onChangeSelectedTags(new Set()); setIsPanelOpen(false); }, tabIndex: -1, "aria-label": "Clear all" }))] }), renderPanel(jsxs("section", { ref: panelRef, className: styles$8["cw-multi-filter-catalog-container"], style: panelStyle, children: [jsxs("nav", { children: [jsx("button", { className: selectedCategory === "All" ? styles$8["category-selected"] : undefined, onClick: () => handleClickCategory("All"), children: allCategoriesLabel }), Array.from(categoriesMappedToTags().keys()).map(category => (jsx("button", { className: selectedCategory === category ? styles$8["category-selected"] : undefined, style: {
|
|
4860
|
+
backgroundColor: getColor(category).primary,
|
|
4861
|
+
color: getColor(category).onPrimary,
|
|
4862
|
+
}, onClick: () => handleClickCategory(category), children: category }, category)))] }), jsx("ul", { children: Array.from(filteredTags)
|
|
4639
4863
|
.sort((a, b) => {
|
|
4640
4864
|
const input = inputTextValue.trim().toLowerCase();
|
|
4641
4865
|
const aName = a.Name.trim().toLowerCase();
|
|
@@ -4655,9 +4879,148 @@ const CwMultiFilter = ({ allTags, id, onChangeSelectedTags, selectedTags, placeh
|
|
|
4655
4879
|
removeTag(props.ID);
|
|
4656
4880
|
else
|
|
4657
4881
|
addTag(props);
|
|
4658
|
-
} }, props.ID))) })] })] }));
|
|
4882
|
+
} }, props.ID))) })] }))] }));
|
|
4659
4883
|
};
|
|
4660
4884
|
|
|
4885
|
+
var styles$7 = {"container":"cw-tag-selector-module__container__-do8i","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"};
|
|
4886
|
+
|
|
4887
|
+
/**
|
|
4888
|
+
* Multi-select input with chip tags and searchable dropdown.
|
|
4889
|
+
* Works like a Kendo MultiSelect: selected items appear as removable chips
|
|
4890
|
+
* inside the input area, and a filtered dropdown lets the user pick more.
|
|
4891
|
+
*
|
|
4892
|
+
* @example
|
|
4893
|
+
* ```tsx
|
|
4894
|
+
* const options = [
|
|
4895
|
+
* { value: "1", label: "Alpha" },
|
|
4896
|
+
* { value: "2", label: "Bravo" },
|
|
4897
|
+
* { value: "3", label: "Charlie" },
|
|
4898
|
+
* ];
|
|
4899
|
+
* const [selected, setSelected] = useState<string[]>([]);
|
|
4900
|
+
*
|
|
4901
|
+
* <CwTagSelector
|
|
4902
|
+
* labelProps={{ text: "Tags" }}
|
|
4903
|
+
* options={options}
|
|
4904
|
+
* value={selected}
|
|
4905
|
+
* onChange={setSelected}
|
|
4906
|
+
* placeholder="Select items…"
|
|
4907
|
+
* />
|
|
4908
|
+
* ```
|
|
4909
|
+
*/
|
|
4910
|
+
function CwTagSelector({ id, options, value, onChange, labelProps, iconProps, alignProps, placeholder = "Select…", disabled = false, showSelectAll = false, feedback, className, style, }) {
|
|
4911
|
+
const [search, setSearch] = useState("");
|
|
4912
|
+
const [open, setOpen] = useState(false);
|
|
4913
|
+
const [focusedIndex, setFocusedIndex] = useState(-1);
|
|
4914
|
+
const containerRef = useRef(null);
|
|
4915
|
+
const inputBoxRef = useRef(null);
|
|
4916
|
+
const inputRef = useRef(null);
|
|
4917
|
+
const listRef = useRef(null);
|
|
4918
|
+
const { panelRef, panelStyle, renderPanel } = useDropdownPortal({
|
|
4919
|
+
anchorRef: inputBoxRef,
|
|
4920
|
+
isOpen: open,
|
|
4921
|
+
onClose: () => { setOpen(false); setFocusedIndex(-1); },
|
|
4922
|
+
});
|
|
4923
|
+
const selectedSet = useMemo(() => new Set(value), [value]);
|
|
4924
|
+
const filtered = useMemo(() => {
|
|
4925
|
+
if (!search)
|
|
4926
|
+
return options.filter(o => !selectedSet.has(o.value));
|
|
4927
|
+
const lower = search.toLowerCase();
|
|
4928
|
+
return options.filter(o => !selectedSet.has(o.value) && o.label.toLowerCase().includes(lower));
|
|
4929
|
+
}, [options, selectedSet, search]);
|
|
4930
|
+
const selectedOptions = useMemo(() => value.map(v => options.find(o => o.value === v)).filter(Boolean), [value, options]);
|
|
4931
|
+
const handleSelect = useCallback((optionValue) => {
|
|
4932
|
+
onChange([...value, optionValue]);
|
|
4933
|
+
setSearch("");
|
|
4934
|
+
setFocusedIndex(-1);
|
|
4935
|
+
inputRef.current?.focus();
|
|
4936
|
+
}, [value, onChange]);
|
|
4937
|
+
const handleRemove = useCallback((optionValue) => {
|
|
4938
|
+
onChange(value.filter(v => v !== optionValue));
|
|
4939
|
+
inputRef.current?.focus();
|
|
4940
|
+
}, [value, onChange]);
|
|
4941
|
+
const allSelected = options.length > 0 && options.every(o => selectedSet.has(o.value));
|
|
4942
|
+
const handleToggleAll = useCallback(() => {
|
|
4943
|
+
if (allSelected) {
|
|
4944
|
+
onChange([]);
|
|
4945
|
+
}
|
|
4946
|
+
else {
|
|
4947
|
+
onChange(options.map(o => o.value));
|
|
4948
|
+
}
|
|
4949
|
+
setSearch("");
|
|
4950
|
+
setFocusedIndex(-1);
|
|
4951
|
+
inputRef.current?.focus();
|
|
4952
|
+
}, [allSelected, options, onChange]);
|
|
4953
|
+
const handleInputChange = useCallback((e) => {
|
|
4954
|
+
setSearch(e.currentTarget.value);
|
|
4955
|
+
if (!open)
|
|
4956
|
+
setOpen(true);
|
|
4957
|
+
setFocusedIndex(-1);
|
|
4958
|
+
}, [open]);
|
|
4959
|
+
const handleInputFocus = useCallback(() => {
|
|
4960
|
+
if (!disabled)
|
|
4961
|
+
setOpen(true);
|
|
4962
|
+
}, [disabled]);
|
|
4963
|
+
const handleKeyDown = useCallback((e) => {
|
|
4964
|
+
if (e.key === "Backspace" && !search && value.length > 0) {
|
|
4965
|
+
onChange(value.slice(0, -1));
|
|
4966
|
+
return;
|
|
4967
|
+
}
|
|
4968
|
+
if (e.key === "Escape") {
|
|
4969
|
+
setOpen(false);
|
|
4970
|
+
setFocusedIndex(-1);
|
|
4971
|
+
return;
|
|
4972
|
+
}
|
|
4973
|
+
if (e.key === "Enter") {
|
|
4974
|
+
e.preventDefault();
|
|
4975
|
+
if (focusedIndex >= 0 && focusedIndex < filtered.length) {
|
|
4976
|
+
handleSelect(filtered[focusedIndex].value);
|
|
4977
|
+
}
|
|
4978
|
+
else if (search.trim().length > 0 && filtered.length > 0) {
|
|
4979
|
+
const lower = search.trim().toLowerCase();
|
|
4980
|
+
const match = filtered.find(o => o.label.trim().toLowerCase() === lower) ??
|
|
4981
|
+
filtered.find(o => o.label.trim().toLowerCase().startsWith(lower)) ??
|
|
4982
|
+
filtered[0];
|
|
4983
|
+
if (match)
|
|
4984
|
+
handleSelect(match.value);
|
|
4985
|
+
}
|
|
4986
|
+
else if (!open) {
|
|
4987
|
+
setOpen(true);
|
|
4988
|
+
setFocusedIndex(0);
|
|
4989
|
+
}
|
|
4990
|
+
return;
|
|
4991
|
+
}
|
|
4992
|
+
if (!open) {
|
|
4993
|
+
if (e.key === "ArrowDown") {
|
|
4994
|
+
setOpen(true);
|
|
4995
|
+
setFocusedIndex(0);
|
|
4996
|
+
e.preventDefault();
|
|
4997
|
+
}
|
|
4998
|
+
return;
|
|
4999
|
+
}
|
|
5000
|
+
if (e.key === "ArrowDown") {
|
|
5001
|
+
e.preventDefault();
|
|
5002
|
+
setFocusedIndex(prev => (prev < filtered.length - 1 ? prev + 1 : prev));
|
|
5003
|
+
}
|
|
5004
|
+
else if (e.key === "ArrowUp") {
|
|
5005
|
+
e.preventDefault();
|
|
5006
|
+
setFocusedIndex(prev => (prev > 0 ? prev - 1 : 0));
|
|
5007
|
+
}
|
|
5008
|
+
}, [open, search, value, filtered, focusedIndex, onChange, handleSelect]);
|
|
5009
|
+
// Scroll focused item into view
|
|
5010
|
+
useEffect(() => {
|
|
5011
|
+
if (focusedIndex >= 0 && listRef.current) {
|
|
5012
|
+
const item = listRef.current.children[focusedIndex];
|
|
5013
|
+
item?.scrollIntoView({ block: "nearest" });
|
|
5014
|
+
}
|
|
5015
|
+
}, [focusedIndex]);
|
|
5016
|
+
// Close on outside click — now handled by useDropdownPortal
|
|
5017
|
+
// Keeping containerRef for any other potential use
|
|
5018
|
+
const direction = alignProps?.flexDirection ?? "row";
|
|
5019
|
+
const feedbackMessages = feedback ? (Array.isArray(feedback) ? feedback : [feedback]) : [];
|
|
5020
|
+
return (jsxs("div", { ref: containerRef, className: `${styles$7.container}${className ? ` ${className}` : ""}`, style: style, id: id, "data-direction": direction, children: [jsxs(CwAlign, { ...alignProps, children: [labelProps && (jsxs(CwLabel, { ...labelProps, children: [iconProps && jsx(CwIcon, { ...iconProps }), labelProps.text] })), jsxs("div", { className: `${styles$7.inputBox}${disabled ? ` ${styles$7.disabled}` : ""}`, ref: inputBoxRef, onClick: () => { if (!disabled)
|
|
5021
|
+
inputRef.current?.focus(); }, children: [selectedOptions.map(opt => (jsx(CwChip, { label: opt.label, colorScheme: opt.color ? "custom" : "info", customColor: opt.color, closable: !disabled, onClose: () => handleRemove(opt.value) }, opt.value))), jsx("input", { ref: inputRef, type: "text", value: search, onChange: handleInputChange, onFocus: handleInputFocus, onKeyDown: handleKeyDown, placeholder: selectedOptions.length === 0 ? placeholder : "", disabled: disabled, autoComplete: "off" }), value.length > 0 && !disabled && (jsx(CwButton, { type: "button", variant: "icon", color: "neutral", icon: "close", onMouseDown: (e) => e.preventDefault(), onClick: (e) => { e.stopPropagation(); onChange([]); setSearch(""); setOpen(false); }, tabIndex: -1, "aria-label": "Clear all" }))] })] }), feedbackMessages.map((fb, i) => (jsx("p", { className: "cw-input-info", "data-color": fb.type, children: fb.message }, i))), renderPanel((showSelectAll || filtered.length > 0) ? (jsxs("div", { ref: panelRef, className: styles$7.dropdown, style: panelStyle, children: [showSelectAll && !search && (jsxs("div", { className: styles$7.selectAll, onMouseDown: (e) => e.preventDefault(), onClick: handleToggleAll, children: [jsx("input", { type: "checkbox", checked: allSelected, readOnly: true, tabIndex: -1 }), allSelected ? "Deselect all" : "Select all"] })), jsx("ul", { ref: listRef, children: filtered.map((opt, idx) => (jsxs("li", { className: `${styles$7.option}${idx === focusedIndex ? ` ${styles$7.optionFocused}` : ""}`, onMouseDown: (e) => e.preventDefault(), onClick: () => handleSelect(opt.value), onMouseEnter: () => setFocusedIndex(idx), title: opt.tooltip, children: [opt.color && jsx("span", { className: styles$7.optionDot, style: { backgroundColor: opt.color } }), opt.label] }, opt.value))) })] })) : null)] }));
|
|
5022
|
+
}
|
|
5023
|
+
|
|
4661
5024
|
var styles$6 = {"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"};
|
|
4662
5025
|
|
|
4663
5026
|
function CwTreeView({ data, onSelect, allowParentSelection = false, selectedId: initialSelectedId = null }) {
|
|
@@ -4764,12 +5127,30 @@ function CwTreeView({ data, onSelect, allowParentSelection = false, selectedId:
|
|
|
4764
5127
|
return (jsxs("div", { className: `cw-tree-view ${styles$6["tree-container"]}`, children: [jsxs("header", { children: [jsx("input", { type: "text", placeholder: "Search in tree...", value: search, onChange: (e) => setSearch(e.target.value) }), search && (jsx(CwButton, { onClick: () => setSearch(""), type: "button", icon: "close", variant: "icon", color: "neutral" }))] }), jsx("div", { className: styles$6["tree-wrapper"], children: filteredData.length > 0 ? (filteredData.map((node) => renderNode(node))) : (jsxs("div", { className: styles$6["empty-state"], children: [jsx(CwIcon, { iconId: "comment" }), search ? "No results found" : "There are no items"] })) })] }));
|
|
4765
5128
|
}
|
|
4766
5129
|
|
|
5130
|
+
/**
|
|
5131
|
+
* Text input with a live-filtered suggestion dropdown backed by a local `CwSelectList` array.
|
|
5132
|
+
* Use `CwFindAirport` or `CwFindCrewmember` for API-backed search inputs.
|
|
5133
|
+
*
|
|
5134
|
+
* @example
|
|
5135
|
+
* <CwSearchInput
|
|
5136
|
+
* selectList={myItems}
|
|
5137
|
+
* handleChange={(id) => console.log('selected', id)}
|
|
5138
|
+
* labelProps={{ text: 'Employee' }}
|
|
5139
|
+
* placeholder="Search by name…"
|
|
5140
|
+
* />
|
|
5141
|
+
*/
|
|
4767
5142
|
function CwSearchInput(optionsProps) {
|
|
4768
5143
|
const [searchText, setSearchText] = useState("");
|
|
4769
5144
|
const [filteredOptions, setFilteredOptions] = useState([]);
|
|
4770
5145
|
const [_selectedOption, setSelectedOption] = useState(null);
|
|
4771
5146
|
const [showDropdown, setShowDropdown] = useState(false);
|
|
4772
5147
|
const [isLoading, setIsLoading] = useState(false);
|
|
5148
|
+
const wrapperRef = useRef(null);
|
|
5149
|
+
const { panelRef, panelStyle, renderPanel } = useDropdownPortal({
|
|
5150
|
+
anchorRef: wrapperRef,
|
|
5151
|
+
isOpen: showDropdown && filteredOptions.length > 0,
|
|
5152
|
+
onClose: () => setShowDropdown(false),
|
|
5153
|
+
});
|
|
4773
5154
|
// Extract props
|
|
4774
5155
|
const { labelProps, iconProps, alignProps, selectList, handleChange, placeholder = "Search…", disabled, renderOption, style, id, className, defaultValue, ...otherProps } = optionsProps;
|
|
4775
5156
|
// Get direction for data attribute
|
|
@@ -4820,9 +5201,8 @@ function CwSearchInput(optionsProps) {
|
|
|
4820
5201
|
}
|
|
4821
5202
|
};
|
|
4822
5203
|
const handleInputBlur = () => {
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
}, 150);
|
|
5204
|
+
// Dropdown is closed by useDropdownPortal's outside-click handler.
|
|
5205
|
+
// Blur alone must not close it — clicking an option would lose focus before the click fires.
|
|
4826
5206
|
};
|
|
4827
5207
|
const handleOptionSelect = (option) => {
|
|
4828
5208
|
setSearchText(option.description);
|
|
@@ -4840,7 +5220,7 @@ function CwSearchInput(optionsProps) {
|
|
|
4840
5220
|
handleChange("");
|
|
4841
5221
|
}
|
|
4842
5222
|
};
|
|
4843
|
-
return (jsxs("div", { className: `cw-search-input ${className || ""}`, style: style, id: id, ...otherProps, "data-direction": direction, children: [jsxs(CwAlign, { ...alignProps, children: [labelProps && (jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxs("div", { className: "cw-search-input-wrapper", children: [jsx("input", { type: "text", value: searchText, onChange: handleInputChange, onFocus: handleInputFocus, onBlur: handleInputBlur, placeholder: placeholder, className: "cw-input-search", disabled: disabled }), isLoading && (jsx("div", { className: "cw-search-input-loading", children: jsx(CwIcon, { iconId: "spinner" }) })), jsx("div", { className: "cw-search-input-icons", children: searchText && !disabled && !isLoading ? (jsx(CwButton, { type: "button", onClick: handleClearClick, "aria-label": "Clear search", variant: "icon", icon: "close", color: "neutral" })) : (iconProps ? jsx(CwIcon, { ...iconProps }) : jsx(CwIcon, { iconId: "search" })) })] })] }),
|
|
5223
|
+
return (jsxs("div", { className: `cw-search-input ${className || ""}`, style: style, id: id, ...otherProps, "data-direction": direction, children: [jsxs(CwAlign, { ...alignProps, children: [labelProps && (jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxs("div", { className: "cw-search-input-wrapper", ref: wrapperRef, children: [jsx("input", { type: "text", value: searchText, onChange: handleInputChange, onFocus: handleInputFocus, onBlur: handleInputBlur, placeholder: placeholder, className: "cw-input-search", disabled: disabled }), isLoading && (jsx("div", { className: "cw-search-input-loading", children: jsx(CwIcon, { iconId: "spinner" }) })), jsx("div", { className: "cw-search-input-icons", children: searchText && !disabled && !isLoading ? (jsx(CwButton, { type: "button", onClick: handleClearClick, "aria-label": "Clear search", variant: "icon", icon: "close", color: "neutral" })) : (iconProps ? jsx(CwIcon, { ...iconProps }) : jsx(CwIcon, { iconId: "search" })) })] })] }), renderPanel(jsx("div", { ref: panelRef, className: dropdownStyles.dropdown, style: panelStyle, children: jsx("ul", { children: filteredOptions.map((option) => (jsx("li", { className: dropdownStyles.option, onClick: () => handleOptionSelect(option), onMouseDown: (e) => e.preventDefault(), children: renderOption ? renderOption(option) : option.description }, option.id))) }) }))] }));
|
|
4844
5224
|
}
|
|
4845
5225
|
|
|
4846
5226
|
var styles$5 = {"menu":"cw-context-menu-module__menu__tXmun","item":"cw-context-menu-module__item__-ko8L","arrow":"cw-context-menu-module__arrow__LHZmQ"};
|
|
@@ -5097,81 +5477,49 @@ var styles$4 = {"pickerWrapper":"cw-pickers-base-module__pickerWrapper__Fb9Zo","
|
|
|
5097
5477
|
/**
|
|
5098
5478
|
* Renders a picker popup in a portal so it escapes overflow containers.
|
|
5099
5479
|
* Positions itself relative to the anchor element.
|
|
5480
|
+
*
|
|
5481
|
+
* Thin wrapper around {@link useDropdownPortal} that adds directional
|
|
5482
|
+
* preference logic ("left-bottom", "right-top", etc.) for calendar popups.
|
|
5483
|
+
* The public API is unchanged — existing callers (`CwDatePicker`, etc.) need
|
|
5484
|
+
* no modifications.
|
|
5100
5485
|
*/
|
|
5101
5486
|
function usePickerPopup({ anchorRef, isOpen, onClose, position = "left-bottom" }) {
|
|
5102
|
-
const
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
const anchor = anchorRef.current;
|
|
5106
|
-
const popup = popupRef.current;
|
|
5107
|
-
if (!anchor || !popup)
|
|
5108
|
-
return;
|
|
5109
|
-
const rect = anchor.getBoundingClientRect();
|
|
5110
|
-
const popupRect = popup.getBoundingClientRect();
|
|
5111
|
-
// Determine vertical placement
|
|
5487
|
+
const pickerStrategy = useCallback((anchorRect, panelRect) => {
|
|
5488
|
+
const popupH = panelRect?.height ?? 300;
|
|
5489
|
+
const popupW = panelRect?.width ?? 300;
|
|
5112
5490
|
const preferTop = position.includes("top");
|
|
5113
|
-
const spaceBelow = window.innerHeight -
|
|
5114
|
-
const spaceAbove =
|
|
5115
|
-
const popupH = popupRect.height || 300; // fallback for first render
|
|
5491
|
+
const spaceBelow = window.innerHeight - anchorRect.bottom;
|
|
5492
|
+
const spaceAbove = anchorRect.top;
|
|
5116
5493
|
let top;
|
|
5117
5494
|
if (preferTop && spaceAbove >= popupH) {
|
|
5118
|
-
top =
|
|
5495
|
+
top = anchorRect.top - popupH;
|
|
5119
5496
|
}
|
|
5120
5497
|
else if (!preferTop && spaceBelow >= popupH + 4) {
|
|
5121
|
-
top =
|
|
5498
|
+
top = anchorRect.bottom + 4;
|
|
5122
5499
|
}
|
|
5123
5500
|
else {
|
|
5124
|
-
|
|
5125
|
-
top = spaceBelow >= popupH + 4 ? rect.bottom + 4 : rect.top - popupH;
|
|
5501
|
+
top = spaceBelow >= popupH + 4 ? anchorRect.bottom + 4 : anchorRect.top - popupH;
|
|
5126
5502
|
}
|
|
5127
|
-
// Determine horizontal placement
|
|
5128
5503
|
let left;
|
|
5129
5504
|
if (position.includes("right")) {
|
|
5130
|
-
left =
|
|
5505
|
+
left = anchorRect.right - popupW;
|
|
5131
5506
|
}
|
|
5132
5507
|
else {
|
|
5133
|
-
left =
|
|
5508
|
+
left = anchorRect.left;
|
|
5134
5509
|
}
|
|
5135
|
-
|
|
5136
|
-
left = Math.max(4, Math.min(left, window.innerWidth - (popupRect.width || 200) - 4));
|
|
5510
|
+
left = Math.max(4, Math.min(left, window.innerWidth - popupW - 4));
|
|
5137
5511
|
top = Math.max(4, top);
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
window.addEventListener("scroll", handleUpdate, true); // capture phase catches all scrollable ancestors
|
|
5150
|
-
return () => {
|
|
5151
|
-
window.removeEventListener("resize", handleUpdate);
|
|
5152
|
-
window.removeEventListener("scroll", handleUpdate, true);
|
|
5153
|
-
};
|
|
5154
|
-
}, [isOpen, reposition]);
|
|
5155
|
-
// Close on outside click — checks both anchor and popup
|
|
5156
|
-
useEffect(() => {
|
|
5157
|
-
if (!isOpen)
|
|
5158
|
-
return;
|
|
5159
|
-
const handleMouseDown = (e) => {
|
|
5160
|
-
const target = e.target;
|
|
5161
|
-
if (anchorRef.current?.contains(target) ||
|
|
5162
|
-
popupRef.current?.contains(target))
|
|
5163
|
-
return;
|
|
5164
|
-
onClose();
|
|
5165
|
-
};
|
|
5166
|
-
document.addEventListener("mousedown", handleMouseDown);
|
|
5167
|
-
return () => document.removeEventListener("mousedown", handleMouseDown);
|
|
5168
|
-
}, [isOpen, onClose, anchorRef]);
|
|
5169
|
-
const renderPopup = useCallback((children) => {
|
|
5170
|
-
if (!isOpen)
|
|
5171
|
-
return null;
|
|
5172
|
-
return createPortal(children, document.body);
|
|
5173
|
-
}, [isOpen]);
|
|
5174
|
-
return { popupRef, popupStyle: style, renderPopup };
|
|
5512
|
+
// No width override — the calendar has its own intrinsic size
|
|
5513
|
+
return { top, left };
|
|
5514
|
+
}, [position]);
|
|
5515
|
+
const { panelRef, panelStyle, renderPanel } = useDropdownPortal({
|
|
5516
|
+
anchorRef,
|
|
5517
|
+
isOpen,
|
|
5518
|
+
onClose,
|
|
5519
|
+
positionStrategy: pickerStrategy,
|
|
5520
|
+
});
|
|
5521
|
+
// Expose original API names so CwDatePicker needs zero changes
|
|
5522
|
+
return { popupRef: panelRef, popupStyle: panelStyle, renderPopup: renderPanel };
|
|
5175
5523
|
}
|
|
5176
5524
|
|
|
5177
5525
|
function CwDatePicker({ value, onChange, minDate, maxDate, disabledDates, disabledMatcher, defaultMonth, labelProps, alignProps, placeholder = "Select a date", displayFormat = "dd.MM.yyyy", disabled, required, className, showClear = true, popupPosition = "left-bottom", numberOfMonths = 1, showTodayButton = false, locale = enGB, todayLabel = "Today", feedback, }) {
|
|
@@ -9172,6 +9520,24 @@ const SuperScheduler = ({ id, state, header, rows, events, pinnedOrderCategory,
|
|
|
9172
9520
|
}, children: "Clear pinned" }) })] })), jsx(Scheduler, { id: `${id}-notPinned`, state: { ...state, isHeaderVisible: !isFirstVisible }, header: header, rows: notPinnedRows, events: events, backgroundEvents: backgroundEvents, indicatorRows: indicatorRows, contextMenuItems: contextMenuItems, orderCategories: unPinnedOrderCategory, onEvent: onEvent, EventComp: SchedulerEvent, RowTitleComp: PinRowHeader, rowHeightRem: rowHeightRem })] }));
|
|
9173
9521
|
};
|
|
9174
9522
|
|
|
9523
|
+
/**
|
|
9524
|
+
* API-backed airport search input for WinOps.
|
|
9525
|
+
*
|
|
9526
|
+
* Fetches matching airports from the WinOps backend as the user types (debounced).
|
|
9527
|
+
* Renders results in a shared `cw-choice-dropdown` styled panel with keyboard navigation.
|
|
9528
|
+
* Supports pre-selecting an airport by `value` (AptKey) and customising the displayed text
|
|
9529
|
+
* format via `displayMode`.
|
|
9530
|
+
*
|
|
9531
|
+
* Requires a running WinOps backend — set `cblConfig` to its base URL.
|
|
9532
|
+
*
|
|
9533
|
+
* @example
|
|
9534
|
+
* <CwFindAirport
|
|
9535
|
+
* cblConfig="https://localhost:44300"
|
|
9536
|
+
* handleChange={(key) => setAirportKey(key)}
|
|
9537
|
+
* labelProps={{ text: 'Departure Airport' }}
|
|
9538
|
+
* displayMode="iata-only"
|
|
9539
|
+
* />
|
|
9540
|
+
*/
|
|
9175
9541
|
const CwFindAirport = ({ handleChange, searchType = "OnlyDatabase", placeHolder = "Search airport…", required = false, cblConfig, className = "", value, disabled = false, displayMode, initialDisplayText, labelProps, alignProps, width }) => {
|
|
9176
9542
|
// State
|
|
9177
9543
|
const [inputValue, setInputValue] = useState("");
|
|
@@ -9184,8 +9550,13 @@ const CwFindAirport = ({ handleChange, searchType = "OnlyDatabase", placeHolder
|
|
|
9184
9550
|
const [tooltipText, setTooltipText] = useState("");
|
|
9185
9551
|
// Refs
|
|
9186
9552
|
const inputRef = useRef(null);
|
|
9187
|
-
const
|
|
9553
|
+
const wrapperRef = useRef(null);
|
|
9188
9554
|
const searchTimeoutRef = useRef();
|
|
9555
|
+
const { panelRef, panelStyle, renderPanel } = useDropdownPortal({
|
|
9556
|
+
anchorRef: wrapperRef,
|
|
9557
|
+
isOpen: showDropdown && options.length > 0,
|
|
9558
|
+
onClose: () => { setShowDropdown(false); setHighlightedIndex(-1); },
|
|
9559
|
+
});
|
|
9189
9560
|
// Utility function to extract ICAO from DisplayAirportText
|
|
9190
9561
|
const extractIcao = useCallback((displayText) => {
|
|
9191
9562
|
const match = displayText.match(/^([A-Z]{4})\(/);
|
|
@@ -9362,19 +9733,7 @@ const CwFindAirport = ({ handleChange, searchType = "OnlyDatabase", placeHolder
|
|
|
9362
9733
|
break;
|
|
9363
9734
|
}
|
|
9364
9735
|
};
|
|
9365
|
-
// Handle clicks outside dropdown
|
|
9366
|
-
useEffect(() => {
|
|
9367
|
-
const handleClickOutside = (event) => {
|
|
9368
|
-
if (dropdownRef.current &&
|
|
9369
|
-
!dropdownRef.current.contains(event.target) &&
|
|
9370
|
-
!inputRef.current?.contains(event.target)) {
|
|
9371
|
-
setShowDropdown(false);
|
|
9372
|
-
setHighlightedIndex(-1);
|
|
9373
|
-
}
|
|
9374
|
-
};
|
|
9375
|
-
document.addEventListener("mousedown", handleClickOutside);
|
|
9376
|
-
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
9377
|
-
}, []);
|
|
9736
|
+
// Handle clicks outside dropdown — now handled by useDropdownPortal
|
|
9378
9737
|
// Load initial value
|
|
9379
9738
|
useEffect(() => {
|
|
9380
9739
|
if (value && value !== 0 && value !== -1) {
|
|
@@ -9416,9 +9775,25 @@ const CwFindAirport = ({ handleChange, searchType = "OnlyDatabase", placeHolder
|
|
|
9416
9775
|
return (jsxs("div", { className: `cw-search-input ${className}`, style: {
|
|
9417
9776
|
...(width ? { width } : {}),
|
|
9418
9777
|
...(labelProps?.labelWidth ? { '--label-width': labelProps.labelWidth } : {})
|
|
9419
|
-
}, "data-direction": direction, children: [jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxs("div", { className: "cw-search-input-wrapper", children: [jsx("input", { ref: inputRef, type: "text", value: inputValue, onChange: handleInputChange, onKeyDown: handleKeyDown, onFocus: handleInputFocus, placeholder: isInitialLoading ? "Loading…" : placeHolder, disabled: disabled, required: required, autoComplete: "off", "aria-expanded": showDropdown, "aria-haspopup": "listbox", role: "combobox", title: tooltipText }), (isLoading || isInitialLoading) && (jsx("div", { className: "cw-search-input-loading", children: jsx(CwIcon, { iconId: "spinner" }) })), jsx("div", { className: "cw-search-input-icons", children: inputValue && !disabled && !isInitialLoading ? (jsx(CwButton, { type: "button", onClick: handleClear, "aria-label": "Clear selected airport", variant: "icon", icon: "close", color: "neutral" })) : (jsx(CwIcon, { iconId: "control-tower" })) })] })] }),
|
|
9778
|
+
}, "data-direction": direction, children: [jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxs("div", { className: "cw-search-input-wrapper", ref: wrapperRef, children: [jsx("input", { ref: inputRef, type: "text", value: inputValue, onChange: handleInputChange, onKeyDown: handleKeyDown, onFocus: handleInputFocus, placeholder: isInitialLoading ? "Loading…" : placeHolder, disabled: disabled, required: required, autoComplete: "off", "aria-expanded": showDropdown, "aria-haspopup": "listbox", role: "combobox", title: tooltipText }), (isLoading || isInitialLoading) && (jsx("div", { className: "cw-search-input-loading", children: jsx(CwIcon, { iconId: "spinner" }) })), jsx("div", { className: "cw-search-input-icons", children: inputValue && !disabled && !isInitialLoading ? (jsx(CwButton, { type: "button", onClick: handleClear, "aria-label": "Clear selected airport", variant: "icon", icon: "close", color: "neutral" })) : (jsx(CwIcon, { iconId: "control-tower" })) })] })] }), renderPanel(jsx("div", { ref: panelRef, className: dropdownStyles.dropdown, style: panelStyle, role: "listbox", children: jsx("ul", { children: options.map((option, index) => (jsx("li", { className: `${dropdownStyles.option}${index === highlightedIndex ? ` ${dropdownStyles.optionFocused}` : ""}`, onClick: () => handleOptionSelect(option.value), onMouseDown: (e) => e.preventDefault(), onMouseEnter: () => setHighlightedIndex(index), role: "option", "aria-selected": index === highlightedIndex, children: option.text }, option.value))) }) }))] }));
|
|
9420
9779
|
};
|
|
9421
9780
|
|
|
9781
|
+
/**
|
|
9782
|
+
* API-backed crewmember search input for WinOps.
|
|
9783
|
+
*
|
|
9784
|
+
* Fetches matching crewmembers from the WinOps backend as the user types (debounced).
|
|
9785
|
+
* Renders results in a shared `cw-choice-dropdown` styled panel with keyboard navigation.
|
|
9786
|
+
* Supports pre-selecting a crewmember by numeric `value` (ID).
|
|
9787
|
+
*
|
|
9788
|
+
* Requires a running WinOps backend — set `cblConfig` to its base URL.
|
|
9789
|
+
*
|
|
9790
|
+
* @example
|
|
9791
|
+
* <CwFindCrewmember
|
|
9792
|
+
* cblConfig="https://localhost:44300"
|
|
9793
|
+
* handleChange={(id) => setCrewId(id)}
|
|
9794
|
+
* labelProps={{ text: 'Crew Member' }}
|
|
9795
|
+
* />
|
|
9796
|
+
*/
|
|
9422
9797
|
const CwFindCrewmember = ({ handleChange, placeHolder = "Search crew…", required = false, cblConfig, className = "", value, disabled = false, initialDisplayText, labelProps, alignProps, width }) => {
|
|
9423
9798
|
const [inputValue, setInputValue] = useState("");
|
|
9424
9799
|
const [options, setOptions] = useState([]);
|
|
@@ -9429,8 +9804,13 @@ const CwFindCrewmember = ({ handleChange, placeHolder = "Search crew…", requir
|
|
|
9429
9804
|
const [highlightedIndex, setHighlightedIndex] = useState(-1);
|
|
9430
9805
|
const [tooltipText, setTooltipText] = useState("");
|
|
9431
9806
|
const inputRef = useRef(null);
|
|
9432
|
-
const
|
|
9807
|
+
const wrapperRef = useRef(null);
|
|
9433
9808
|
const searchTimeoutRef = useRef();
|
|
9809
|
+
const { panelRef, panelStyle, renderPanel } = useDropdownPortal({
|
|
9810
|
+
anchorRef: wrapperRef,
|
|
9811
|
+
isOpen: showDropdown && options.length > 0,
|
|
9812
|
+
onClose: () => { setShowDropdown(false); setHighlightedIndex(-1); },
|
|
9813
|
+
});
|
|
9434
9814
|
const getDisplayText = useCallback((crew) => {
|
|
9435
9815
|
return `${crew.threeLetterCode} - ${crew.lastName} ${crew.firstName}`;
|
|
9436
9816
|
}, []);
|
|
@@ -9532,16 +9912,7 @@ const CwFindCrewmember = ({ handleChange, placeHolder = "Search crew…", requir
|
|
|
9532
9912
|
}
|
|
9533
9913
|
};
|
|
9534
9914
|
useEffect(() => {
|
|
9535
|
-
|
|
9536
|
-
if (dropdownRef.current &&
|
|
9537
|
-
!dropdownRef.current.contains(event.target) &&
|
|
9538
|
-
!inputRef.current?.contains(event.target)) {
|
|
9539
|
-
setShowDropdown(false);
|
|
9540
|
-
setHighlightedIndex(-1);
|
|
9541
|
-
}
|
|
9542
|
-
};
|
|
9543
|
-
document.addEventListener("mousedown", handleClickOutside);
|
|
9544
|
-
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
9915
|
+
// Outside-click is now handled by useDropdownPortal
|
|
9545
9916
|
}, []);
|
|
9546
9917
|
useEffect(() => {
|
|
9547
9918
|
if (value && value !== 0 && value !== -1) {
|
|
@@ -9580,7 +9951,7 @@ const CwFindCrewmember = ({ handleChange, placeHolder = "Search crew…", requir
|
|
|
9580
9951
|
return (jsxs("div", { className: `cw-search-input ${className}`, style: {
|
|
9581
9952
|
...(width ? { width } : {}),
|
|
9582
9953
|
...(labelProps?.labelWidth ? { '--label-width': labelProps.labelWidth } : {})
|
|
9583
|
-
}, "data-direction": direction, children: [jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxs("div", { className: "cw-search-input-wrapper", children: [jsx("input", { ref: inputRef, type: "text", value: inputValue, onChange: handleInputChange, onKeyDown: handleKeyDown, onFocus: handleInputFocus, placeholder: isInitialLoading ? "Loading…" : placeHolder, disabled: disabled, required: required, autoComplete: "off", "aria-expanded": showDropdown, "aria-haspopup": "listbox", role: "combobox", title: tooltipText }), (isLoading || isInitialLoading) && (jsx("div", { className: "cw-search-input-loading", children: jsx(CwIcon, { iconId: "spinner" }) })), jsx("div", { className: "cw-search-input-icons", children: inputValue && !disabled && !isInitialLoading ? (jsx(CwButton, { type: "button", onClick: handleClear, "aria-label": "Clear selected crewmember", variant: "icon", icon: "close", color: "neutral" })) : (jsx(CwIcon, { iconId: "person" })) })] })] }),
|
|
9954
|
+
}, "data-direction": direction, children: [jsxs(CwAlign, { ...alignProps, itemProp: required ? "required" : "", children: [labelProps && (jsx(CwLabel, { ...labelProps, children: labelProps.text })), jsxs("div", { className: "cw-search-input-wrapper", ref: wrapperRef, children: [jsx("input", { ref: inputRef, type: "text", value: inputValue, onChange: handleInputChange, onKeyDown: handleKeyDown, onFocus: handleInputFocus, placeholder: isInitialLoading ? "Loading…" : placeHolder, disabled: disabled, required: required, autoComplete: "off", "aria-expanded": showDropdown, "aria-haspopup": "listbox", role: "combobox", title: tooltipText }), (isLoading || isInitialLoading) && (jsx("div", { className: "cw-search-input-loading", children: jsx(CwIcon, { iconId: "spinner" }) })), jsx("div", { className: "cw-search-input-icons", children: inputValue && !disabled && !isInitialLoading ? (jsx(CwButton, { type: "button", onClick: handleClear, "aria-label": "Clear selected crewmember", variant: "icon", icon: "close", color: "neutral" })) : (jsx(CwIcon, { iconId: "person" })) })] })] }), renderPanel(jsx("div", { ref: panelRef, className: dropdownStyles.dropdown, style: panelStyle, role: "listbox", children: jsx("ul", { children: options.map((option, index) => (jsx("li", { className: `${dropdownStyles.option}${index === highlightedIndex ? ` ${dropdownStyles.optionFocused}` : ""}`, onClick: () => handleOptionSelect(option.value), onMouseDown: (e) => e.preventDefault(), onMouseEnter: () => setHighlightedIndex(index), role: "option", "aria-selected": index === highlightedIndex, children: option.text }, option.value))) }) }))] }));
|
|
9584
9955
|
};
|
|
9585
9956
|
|
|
9586
|
-
export { CblDragAndDrop, CwAccordionContainer, CwAlign, CwAnchoredMenu, CwBtnDelete, CwBtnEdit, CwBtnSave, CwButton, CwCard, CwCardList, CwCheck, CwCheckbox, CwCheckboxGroup, CwChip, CwColorPicker, CwConfirmationPopup, CwContextMenu, CwDatePicker, CwDateRangePicker, CwDateTimePicker, CwDateTimePickerCompact, CwDialog, CwDialogManager, CwDigit, CwDisplayMessage, CwDropdownFilter, CwExpandable, CwFileUpload, CwFileUploadMultiple, CwFindAirport, CwFindCrewmember, CwGenericTooltip, CwHeadingMain, CwHeadingSecond, CwIcon, CwImageArea, CwImageGallery, CwImageZoom, CwInput, CwInputColor, CwInputDate, CwInputDatePicker, CwInputDateText, CwInputDatetime, CwInputImage, CwInputNumber, CwInputPhone, CwInputText, CwKeyValueList, CwLabel, CwLoading, CwLoadingSmall, CwMasterDetail, CwMessage, CwMessageManager, CwMessageType, CwModal, CwModalHover, CwModalReportFunctional, CwMultiFilter, CwMultiFilterTag, CwNote, CwOption, CwPopoverButton, CwReportModal, CwScheduler, CwSearchInput, CwSelect, CwSelectList, CwSelectListItems, CwSortableList, CwSortableTable, CwSuperScheduler, CwTable, CwTableGrouped, CwTableServerSide, CwTabs, CwTextArea, CwTime, CwTimePicker, CwToggle, CwTooltipManager, CwTooltipNew, CwTreeView, CwWeekdaySelector, DefaultRowHeader, OnClearPinned, OnClickContextMenu, OnClickEvent, OnClickRowEvent, OnClickRowHeader, OnClickUtc, OnDoubleClickEvent, OnDoubleClickRowEvent, OnDragEvent, OnDropCtrlEvent, OnDropEvent, OnEndClickHeaderEvent, OnLeftDragStart, OnMultiClickEvent, OnPinRow, OnRangeClickEvent, OnRightClickEvent, OnRightClickRow, OnRightDragStart, OnStartClickHeaderEvent, OnUnpinRow, PinRowHeader, Resource, Scheduler, SchedulerEvent, SuperScheduler, UiEvent, Weekdays, cblEvent, eventIsVisible, filterVisibleEvents, getDefaultDivisions, getEventSizes, itemsToMultiFilterTags, useCwMessage, useSortableList, useSortableTable };
|
|
9957
|
+
export { CblDragAndDrop, CwAccordionContainer, CwAlign, CwAnchoredMenu, CwBtnDelete, CwBtnEdit, CwBtnSave, CwButton, CwCard, CwCardList, CwCheck, CwCheckbox, CwCheckboxGroup, CwChip, CwColorPicker, CwConfirmationPopup, CwContextMenu, CwDatePicker, CwDateRangePicker, CwDateTimePicker, CwDateTimePickerCompact, CwDialog, CwDialogManager, CwDigit, CwDisplayMessage, CwDropdown, CwDropdownFilter, CwExpandable, CwFileUpload, CwFileUploadMultiple, CwFindAirport, CwFindCrewmember, CwGenericTooltip, CwHeadingMain, CwHeadingSecond, CwIcon, CwImageArea, CwImageGallery, CwImageZoom, CwInput, CwInputColor, CwInputDate, CwInputDatePicker, CwInputDateText, CwInputDatetime, CwInputImage, CwInputNumber, CwInputPhone, CwInputText, CwKeyValueList, CwLabel, CwLoading, CwLoadingSmall, CwMasterDetail, CwMessage, CwMessageManager, CwMessageType, CwModal, CwModalHover, CwModalReportFunctional, CwMultiFilter, CwMultiFilterTag, CwNote, CwOption, CwPopoverButton, CwReportModal, CwScheduler, CwSearchInput, CwSelect, CwSelectList, CwSelectListItems, CwSortableList, CwSortableTable, CwSuperScheduler, CwTable, CwTableGrouped, CwTableServerSide, CwTabs, CwTagSelector, CwTextArea, CwTime, CwTimePicker, CwToggle, CwTooltipManager, CwTooltipNew, CwTreeView, CwWeekdaySelector, DefaultRowHeader, OnClearPinned, OnClickContextMenu, OnClickEvent, OnClickRowEvent, OnClickRowHeader, OnClickUtc, OnDoubleClickEvent, OnDoubleClickRowEvent, OnDragEvent, OnDropCtrlEvent, OnDropEvent, OnEndClickHeaderEvent, OnLeftDragStart, OnMultiClickEvent, OnPinRow, OnRangeClickEvent, OnRightClickEvent, OnRightClickRow, OnRightDragStart, OnStartClickHeaderEvent, OnUnpinRow, PinRowHeader, Resource, Scheduler, SchedulerEvent, SuperScheduler, UiEvent, Weekdays, cblEvent, eventIsVisible, filterVisibleEvents, getDefaultDivisions, getEventSizes, itemsToMultiFilterTags, useCwMessage, useSortableList, useSortableTable };
|