UNPKG

@adaptabletools/adaptable-cjs

Version:

Powerful data-agnostic HTML5 AG Grid extension which provides advanced, cutting-edge functionality to meet all DataGrid requirements

133 lines (132 loc) 8.14 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.FloatingFilter = void 0; const tslib_1 = require("tslib"); const React = tslib_1.__importStar(require("react")); const OverlayTrigger_1 = tslib_1.__importDefault(require("../../../components/OverlayTrigger")); const SimpleButton_1 = tslib_1.__importDefault(require("../../../components/SimpleButton")); const AdaptableContext_1 = require("../../AdaptableContext"); const AdaptableIconComponent_1 = require("../AdaptableIconComponent"); const ColumnFilterMenu_1 = require("./components/ColumnFilterMenu"); const FloatingFilterInputList_1 = require("./components/FloatingFilterInputList"); const utils_1 = require("./utils"); const GeneralConstants_1 = require("../../../Utilities/Constants/GeneralConstants"); const Flex_1 = require("../../../components/Flex"); const FloatingFilter = (props) => { const adaptable = (0, AdaptableContext_1.useAdaptable)(); /** * We always have a syntetic predicate with AND or OR, so the logic is easyer. * The wrapper is in charge of constructing the correct column filter. */ const isMultiple = props.predicate?.args?.length > 1; const isManualApply = adaptable.api.filterApi.columnFilterApi.internalApi.shouldManuallyApplyColumnFilter(props.columnId); const isInlineEditable = !isMultiple && !isManualApply; // only used when there is only one filter selected let singleFilterPredicateDef = null; if (props.predicate.args.length === 1) { singleFilterPredicateDef = props.predicateDefs.find((predicateDef) => predicateDef.operator === props.predicate.args[0].operator); } const noInputs = !singleFilterPredicateDef?.inputs || singleFilterPredicateDef.inputs.length === 0; let showLabel = isMultiple || noInputs || isManualApply; if (props.columnId === GeneralConstants_1.AG_GRID_GROUPED_COLUMN) { showLabel = false; } let label = (0, utils_1.qlPredicateToString)(props.predicate, props.predicateDefs); if (isManualApply && singleFilterPredicateDef && (0, utils_1.isPredicateEmpty)(props.predicate.args[0], singleFilterPredicateDef)) { label = ''; } const showQuickFilterDropdown = adaptable.api.filterApi.columnFilterApi.internalApi.showQuickFilterDropdown(props.columnId); const handleClear = () => props.onClear?.(); const showEvent = 'click'; const hideEvent = 'blur'; const [overlayVisible, setOverlayVisible] = React.useState(false); let filterDropdown = null; const filterDropdownButton = (React.createElement(SimpleButton_1.default, { variant: "text", "data-name": "floating-filter-button", onBlur: () => { if (isInlineEditable) { setOverlayVisible(false); } }, "data-value": props.predicate?.args[0]?.operator, onClick: () => { if (!isInlineEditable) { adaptable.api.filterApi.columnFilterApi.internalApi.openColumnFilterPopup(props.columnId); } else { setOverlayVisible(true); } }, style: { textAlign: 'left', marginRight: 1, // just so that the focus outline is not cut off ...(!isInlineEditable ? { minWidth: 0, flex: 1, } : {}), } }, React.createElement(Flex_1.Box, null, !isManualApply && singleFilterPredicateDef?.icon ? (singleFilterPredicateDef?.icon) : (React.createElement(AdaptableIconComponent_1.AdaptableIconComponent, { icon: { name: 'filter' } }))), showLabel && (React.createElement(Flex_1.Box, { className: "ab-FloatingFilter-label twa:ml-2 twa:flex-1", title: label }, label)))); if (isInlineEditable) { filterDropdown = showQuickFilterDropdown && (React.createElement(OverlayTrigger_1.default, { className: "ab-FloatingFilter-overlay", showEvent: showEvent, hideEvent: hideEvent, visible: overlayVisible, onVisibleChange: setOverlayVisible, preventPortalEventPropagation: showEvent === 'click', targetOffset: 10, hideDelay: 50, "data-name": "floating-filter-overlay", render: () => { // we render this only for single filter return (React.createElement(ColumnFilterMenu_1.ColumnFilterMenu, { columnId: props.columnId, disabled: props.disabled, predicate: props.predicate.args[0], predicateDefs: props.predicateDefs, onPredicateChange: (predicate) => { setOverlayVisible(false); props.onPredicateChange({ operator: props.predicate.operator, args: [predicate], }); } })); } }, filterDropdownButton)); } else { filterDropdown = filterDropdownButton; } return (React.createElement(Flex_1.Flex, { className: "ab-FloatingFilter twa:w-full", onKeyDownCapture: (e) => { // AG Grid's header keyboard navigation intercepts Tab and calls preventDefault(), // which prevents focus from moving between elements inside the floating filter. // We handle Tab manually in the capture phase (before AG Grid's handlers). if (e.key === 'Tab') { const target = e.target; const wrapper = e.currentTarget; if (!e.shiftKey) { // Tab forward: from filter button → select input if (target.getAttribute('data-name') === 'floating-filter-button') { const selectInput = wrapper.querySelector('[data-name="Select Values"] input'); if (selectInput) { e.preventDefault(); e.nativeEvent.stopImmediatePropagation(); selectInput.focus(); // When the DummyInput gets focus, react-select sets isFocused=true, // triggering a React re-render. During this re-render, unstable component // references in selectComponents can cause the DummyInput to be removed // from DOM and recreated, losing focus. We restore focus after the re-render. requestAnimationFrame(() => { if (document.activeElement === document.body || document.activeElement === null) { const newInput = wrapper.querySelector('[data-name="Select Values"] input'); newInput?.focus(); } }); } } } } } }, filterDropdown, isInlineEditable && (React.createElement(Flex_1.Flex, { className: "twa:flex-1 twa:min-w-0" }, React.createElement(FloatingFilterInputList_1.FloatingFilterInputList, { onKeyDown: props.onKeydown, columnId: props.columnId, disabled: props.disabled, // It works only with a predicate predicate: props.predicate.args[0], predicateDefs: props.predicateDefs, onPredicateChange: (predicate) => { props.onPredicateChange({ operator: props.predicate.operator, args: [predicate], }); } }))), isInlineEditable && (React.createElement(SimpleButton_1.default, { className: "twa:px-0", onClick: () => { adaptable.api.filterApi.columnFilterApi.internalApi.openColumnFilterPopup(props.columnId, { onChange: props.onChange, }); }, "data-name": "expand-filter", icon: "expand", variant: "text" })), (isMultiple || !(0, utils_1.isPredicateEmpty)(props.predicate.args[0], singleFilterPredicateDef)) && (React.createElement(SimpleButton_1.default, { "data-name": "clear-filter", className: "twa:p-0", variant: "text", onClick: handleClear, icon: "close" })))); }; exports.FloatingFilter = FloatingFilter;