UNPKG

@adaptabletools/adaptable-cjs

Version:

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

273 lines (272 loc) 13 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TreeDropdown = exports.toDisplayValueDefault = void 0; const tslib_1 = require("tslib"); const React = tslib_1.__importStar(require("react")); const FieldWrap_1 = tslib_1.__importDefault(require("../../FieldWrap")); const TreeList_1 = require("../TreeList"); const react_1 = require("react"); const OverlayTrigger_1 = tslib_1.__importDefault(require("../../OverlayTrigger")); const rebass_1 = require("rebass"); const NotifyResize_1 = tslib_1.__importDefault(require("../../NotifyResize")); const Input_1 = tslib_1.__importDefault(require("../../Input")); const InfiniteTable_1 = require("../../InfiniteTable"); const SimpleButton_1 = tslib_1.__importDefault(require("../../SimpleButton")); const CheckBox_1 = require("../../CheckBox"); const useLatest_1 = require("../../utils/useLatest"); const re_resizable_1 = require("re-resizable"); const AdaptableComputedCSSVarsContext_1 = require("../../../View/AdaptableComputedCSSVarsContext"); const resizableDirections = { right: true, bottom: true, bottomRight: true, }; function toDisplayValueDefault(value) { if (!Array.isArray(value)) { return `${value}`; } return value.map((v) => (Array.isArray(v) ? v.join('-') : v)).join(', '); } exports.toDisplayValueDefault = toDisplayValueDefault; const getLabelColumn = (field, { includeExpandCollapseButton }) => { return { field, defaultFlex: 1, renderTreeIcon: true, defaultSortable: false, resizable: false, renderSelectionCheckBox: ({ rowInfo, dataSourceApi, api }) => { if (!rowInfo.isTreeNode) { return null; } return (React.createElement(CheckBox_1.CheckBox, { mx: 1, checked: rowInfo?.rowSelected, onChange: (checked) => { dataSourceApi.treeApi.setNodeSelection(rowInfo.nodePath, checked); api.focus(); } })); }, renderHeader: ({ dataSourceApi, api, allRowsSelected, someRowsSelected }) => { const { treeApi } = dataSourceApi; const allFirstLevelCollapsed = dataSourceApi .getOriginalDataArray() .every((item) => !treeApi.isNodeExpanded([item.id])); return (React.createElement(rebass_1.Flex, { flexDirection: 'row', alignItems: 'center', width: '100%', onMouseDown: (e) => { // so we can keep the focus on the Grid e.preventDefault(); } }, React.createElement(CheckBox_1.CheckBox, { checked: someRowsSelected && !allRowsSelected ? null : allRowsSelected, mr: 2, onChange: () => { if (allRowsSelected) { dataSourceApi.treeApi.deselectAll(); } else { dataSourceApi.treeApi.selectAll(); } api.focus(); } }, allRowsSelected ? '(Deselect All)' : '(Select All)'), React.createElement(rebass_1.Flex, { flex: 1 }), includeExpandCollapseButton ? (React.createElement(SimpleButton_1.default, { label: "toggle-expand-collapse", icon: allFirstLevelCollapsed ? 'expand-all' : 'collapse-all', onMouseDown: () => { if (allFirstLevelCollapsed) { dataSourceApi.treeApi.expandAll(); } else { dataSourceApi.treeApi.collapseAll(); } }, iconPosition: "end" })) : null)); }, }; }; const sizeFull = { width: '100%', height: '100%', }; function getRowCount(options) { return options.reduce((acc, option) => { if (Array.isArray(option.children)) { return acc + getRowCount(option.children) + 1; } return acc + 1; }, 0); } function TreeDropdown(props) { const [visible, doSetVisible] = (0, react_1.useState)(false); const overlayDOMRef = (0, react_1.useRef)(null); const getProps = (0, useLatest_1.useLatest)(props); const computedCSSVars = (0, AdaptableComputedCSSVarsContext_1.useAdaptableComputedCSSVars)(); const [treeExpandState, setTreeExpandState] = (0, react_1.useState)(undefined); const [searchValue, setSearchValue] = (0, react_1.useState)(''); const labelField = props.labelField ?? 'label'; const [stateValue, setStateValue] = (0, react_1.useState)(props.value !== undefined ? props.value : props.defaultValue || []); const onChange = (0, react_1.useCallback)((value) => { const paths = value instanceof InfiniteTable_1.TreeSelectionState ? value.getState().selectedPaths : value.selectedPaths || []; if (props.value === undefined) { setStateValue(paths); } props.onChange?.(paths); }, [props.onChange, props.value]); const value = props.value !== undefined ? props.value : stateValue; const treeSelection = (0, react_1.useMemo)(() => { const selection = { defaultSelection: false, selectedPaths: value, }; return selection; }, [value]); const rowCount = (0, react_1.useMemo)(() => { return getRowCount(props.options); }, [props.options]); const hasChildren = rowCount > props.options.length; const columns = (0, react_1.useMemo)(() => { return { label: getLabelColumn(labelField, { includeExpandCollapseButton: hasChildren, }), }; }, [labelField, hasChildren]); const [size, setSize] = (0, react_1.useState)({ width: 0, height: rowCount * 35, }); const setHeight = (0, react_1.useCallback)((height) => { setSize((s) => { return { ...s, height, }; }); }, []); const setWidth = (0, react_1.useCallback)((width) => { setSize((s) => { return { ...s, width, }; }); }, []); const getSize = (0, useLatest_1.useLatest)(size); (0, react_1.useEffect)(() => { if (!getSize().height) { setHeight(rowCount * 35); } }, [rowCount]); const setVisible = (visible) => { if (visible) { const { onMenuOpen } = getProps(); if (onMenuOpen) { onMenuOpen(); } requestAnimationFrame(() => { doSetVisible(visible); }); } else { const { onMenuClose } = getProps(); if (onMenuClose) { onMenuClose(); } doSetVisible(visible); } }; const [treeListApi, setTreeListApi] = (0, react_1.useState)(null); const { listSizeConstraints } = props; const nodeMatches = (0, react_1.useCallback)(({ data }) => { return !searchValue ? data : `${data[labelField]}`.toLowerCase().includes(searchValue.toLowerCase()); }, [searchValue]); const filterFunction = (0, react_1.useCallback)(({ data, filterTreeNode }) => { if (!Array.isArray(data.children)) { return nodeMatches({ data }); } // allow non-leaf nodes to match if (nodeMatches({ data })) { return data; } return filterTreeNode(data); }, [nodeMatches]); return (React.createElement(rebass_1.Flex, { flexDirection: 'row', className: "ab-TreeDropdown", style: { width: '100%', ...props.style, }, onMouseDown: props.onMouseDown, onBlur: (e) => { const { relatedTarget } = e; const overlayDOMNode = overlayDOMRef.current; if ((overlayDOMNode && relatedTarget == overlayDOMNode) || overlayDOMNode?.contains(relatedTarget)) { return; } setVisible(false); } }, React.createElement(NotifyResize_1.default, { onResize: (newSize) => { setWidth(newSize.width); } }), React.createElement(OverlayTrigger_1.default, { visible: visible, targetOffset: 20, alignPosition: [ // overlay - target ['TopLeft', 'BottomLeft'], ['TopRight', 'BottomRight'], ['BottomLeft', 'TopLeft'], ['BottomRight', 'TopRight'], ], render: () => { const minWidth = listSizeConstraints?.minWidth || computedCSSVars['--ab-cmp-select-menu__min-width'] || 240; const maxWidth = listSizeConstraints?.maxWidth || computedCSSVars['--ab-cmp-select-menu__max-width'] || '60vw'; const minHeight = listSizeConstraints?.minHeight || 200; const maxHeight = listSizeConstraints?.maxHeight || computedCSSVars['--ab-cmp-select-menu__max-height'] || '50vh'; const resizable = getProps().resizable; const treeListStyle = resizable ? { ...sizeFull } : { width: size.width, height: size.height, maxWidth, minHeight, maxHeight, minWidth, }; if (!hasChildren) { // @ts-ignore - don't leave any space for the > expand icon, as there are no children treeListStyle['--infinite-group-row-column-nesting'] = 'var(--ab-space-2)'; } const treeList = (React.createElement(TreeList_1.TreeList, { primaryKey: props.primaryKey ?? 'id', treeFilterFunction: filterFunction, columnHeaderHeight: 30, onReady: ({ api }) => { setTreeListApi(api); api.focus(); }, defaultTreeExpandState: treeExpandState, onTreeExpandStateChange: setTreeExpandState, columns: columns, options: props.options, treeSelection: treeSelection, onTreeSelectionChange: (0, InfiniteTable_1.withSelectedLeafNodesOnly)(onChange), style: treeListStyle })); let children = (React.createElement(rebass_1.Flex, { flexDirection: 'column', height: '100%' }, React.createElement(rebass_1.Flex, { backgroundColor: 'defaultbackground', p: 1, alignItems: 'center', justifyContent: 'stretch', justifyItems: 'stretch' }, React.createElement(Input_1.default, { "data-name": "menulist-search-input", placeholder: "Search...", style: { width: '100%' }, value: searchValue, onChange: (e) => setSearchValue(e.target.value) })), treeList)); if (resizable) { const onResizeStop = (_e, _direction, ref) => { const newSize = { width: ref.style.width, height: ref.style.height, }; setSize(newSize); }; children = (React.createElement(re_resizable_1.Resizable, { enable: resizableDirections, minWidth: minWidth, maxWidth: maxWidth, minHeight: minHeight, maxHeight: maxHeight, defaultSize: size, onResizeStop: onResizeStop, onResizeStart: (e) => { // in order to prevent focus from being lost e.preventDefault(); } }, children)); } return (React.createElement(rebass_1.Box, { ref: overlayDOMRef, className: "ab-TreeDropdownOverlay", "data-name": "menu-container", style: { fontSize: 'var(--ab-cmp-select__font-size)', } }, children)); } }, React.createElement(FieldWrap_1.default, { style: { width: '100%', ...props.fieldStyle } }, React.createElement(Input_1.default, { type: "text", readOnly: true, "data-name": "Select Values", placeholder: props.placeholder ?? 'Select a value', style: { width: '100%', }, pr: props.clearable ? 0 : undefined, value: props.toDisplayValue ? props.toDisplayValue(value) : toDisplayValueDefault(value), onFocus: () => { if (!visible) { setVisible(true); } treeListApi?.focus(); } }), props.clearable && (React.createElement(SimpleButton_1.default, { style: { visibility: Array.isArray(value) && value.length > 0 ? 'visible' : 'hidden', }, variant: "text", icon: "close", onClick: () => onChange({ selectedPaths: [] }) })))))); } exports.TreeDropdown = TreeDropdown;