diff --git a/package-lock.json b/package-lock.json index 6f4b06a53..cd7f77454 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,18 @@ { "name": "@gisce/react-ooui", - "version": "2.52.1", + "version": "2.52.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@gisce/react-ooui", - "version": "2.52.1", + "version": "2.52.2", "dependencies": { "@ant-design/plots": "^1.0.9", "@gisce/fiber-diagram": "2.1.1", "@gisce/ooui": "2.27.0", "@gisce/react-formiga-components": "1.8.0", - "@gisce/react-formiga-table": "1.9.0", + "@gisce/react-formiga-table": "1.9.1", "@monaco-editor/react": "^4.4.5", "@tabler/icons-react": "^2.11.0", "@types/deep-equal": "^1.0.4", @@ -3409,9 +3409,9 @@ } }, "node_modules/@gisce/react-formiga-table": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@gisce/react-formiga-table/-/react-formiga-table-1.9.0.tgz", - "integrity": "sha512-jKMW2i3rbYThoHQpSIAyaQORsRI6opcLDHMMQAPr33fm/6WoYHTsCkccH7RTZQONC/hGfQQoS7hlSUCKu2Oweg==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@gisce/react-formiga-table/-/react-formiga-table-1.9.1.tgz", + "integrity": "sha512-5f3Z+OoGV7lh7ed3TVhatO4Dwrr0nf7jgMnLT69ZZLEg9QUzePyGn4seUfVt8ZsLlt4Hn2jJRtbGm+P32gRnzg==", "dependencies": { "ag-grid-community": "^31.2.1", "ag-grid-react": "^31.2.1", diff --git a/package.json b/package.json index 614000bea..5bbe63a52 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@gisce/react-ooui", - "version": "2.52.1", + "version": "2.52.2", "engines": { "node": "20.5.0" }, @@ -37,7 +37,7 @@ "@gisce/fiber-diagram": "2.1.1", "@gisce/ooui": "2.27.0", "@gisce/react-formiga-components": "1.8.0", - "@gisce/react-formiga-table": "1.9.0", + "@gisce/react-formiga-table": "1.9.1", "@monaco-editor/react": "^4.4.5", "@tabler/icons-react": "^2.11.0", "@types/deep-equal": "^1.0.4", diff --git a/src/context/ActionViewContext.tsx b/src/context/ActionViewContext.tsx index 134ec5beb..0eabae1fb 100644 --- a/src/context/ActionViewContext.tsx +++ b/src/context/ActionViewContext.tsx @@ -1,5 +1,6 @@ import { DEFAULT_SEARCH_LIMIT } from "@/models/constants"; import { View } from "@/types"; +import { ColumnState } from "@gisce/react-formiga-table"; import { createContext, useContext, useEffect, useState } from "react"; type ActionViewProviderProps = { @@ -68,6 +69,8 @@ export type ActionViewContextType = Omit< setSearchQuery?: (value: SearchQueryParams) => void; isInfiniteTree?: boolean; setIsInfiniteTree?: (value: boolean) => void; + sortState?: ColumnState[]; + setSortState?: (value: ColumnState[] | undefined) => void; }; export const ActionViewContext = createContext( @@ -127,6 +130,7 @@ const ActionViewProvider = (props: ActionViewProviderProps): any => { const [treeFirstVisibleRow, setTreeFirstVisibleRow] = useState(0); const [searchQuery, setSearchQuery] = useState(); const [isInfiniteTree, setIsInfiniteTree] = useState(false); + const [sortState, setSortState] = useState(); const [limit, setLimit] = useState( limitProps !== undefined ? limitProps : DEFAULT_SEARCH_LIMIT, @@ -234,6 +238,8 @@ const ActionViewProvider = (props: ActionViewProviderProps): any => { setSearchQuery, isInfiniteTree, setIsInfiniteTree, + sortState, + setSortState, }} > {children} @@ -310,6 +316,8 @@ export const useActionViewContext = () => { setSearchQuery: () => {}, isInfiniteTree: false, setIsInfiniteTree: () => {}, + sortState: undefined, + setSortState: () => {}, }; } diff --git a/src/helpers/treeHelper.tsx b/src/helpers/treeHelper.tsx index 9b71a56b2..d48faad53 100644 --- a/src/helpers/treeHelper.tsx +++ b/src/helpers/treeHelper.tsx @@ -6,7 +6,7 @@ import { Reference, } from "@gisce/ooui"; import { TreeView, Column } from "@/types"; -import { SortDirection } from "@gisce/react-formiga-table"; +import { SortDirection, ColumnState } from "@gisce/react-formiga-table"; const getTree = (treeView: TreeView): TreeOoui => { const xml = treeView.arch; @@ -219,6 +219,33 @@ function hasActualValues(obj: Record): boolean { return false; } +const getSortedFieldsFromState = ({ + state, +}: { + state?: ColumnState[]; +}): Record | undefined => { + if (!state) { + return undefined; + } + + const columnsWithSort = state + .filter((col) => col.sort) + .sort((a, b) => (a.sortIndex || 0) - (b.sortIndex || 0)); + + if (columnsWithSort.length === 0) { + return undefined; + } + const sortFields = columnsWithSort.reduce( + (acc, col) => ({ + ...acc, + [col.colId]: col.sort, + }), + {}, + ); + + return sortFields; +}; + const getOrderFromSortFields = (sortFields?: Record) => { if (!sortFields) { return undefined; @@ -260,4 +287,5 @@ export { hasActualValues, getOrderFromSortFields, extractTreeXmlAttribute, + getSortedFieldsFromState, }; diff --git a/src/hooks/useSearchTreeState.ts b/src/hooks/useSearchTreeState.ts index afbd69278..f02e4243a 100644 --- a/src/hooks/useSearchTreeState.ts +++ b/src/hooks/useSearchTreeState.ts @@ -4,6 +4,7 @@ import { SearchQueryParams, useIsUnderActionViewContext, } from "@/context/ActionViewContext"; +import { ColumnState } from "@gisce/react-formiga-table"; export type SearchTreeState = { treeIsLoading: boolean; @@ -27,6 +28,8 @@ export type SearchTreeState = { totalItems: number; setTotalItems: (value: number) => void; isActive?: boolean; + sortState?: ColumnState[]; + setSortState: (value: ColumnState[] | undefined) => void; }; export function useSearchTreeState({ @@ -50,6 +53,9 @@ export function useSearchTreeState({ const [localResults, setLocalResults] = useState([]); const [localSearchQuery, setLocalSearchQuery] = useState(); const [localTotalItems, setLocalTotalItems] = useState(0); + const [localSortState, setLocalSortState] = useState< + ColumnState[] | undefined + >(); // Return either context values or local state values based on isUnderActionViewContext return isUnderActionViewContext @@ -78,6 +84,8 @@ export function useSearchTreeState({ totalItems: actionViewContext.totalItems ?? 0, setTotalItems: actionViewContext.setTotalItems ?? (() => {}), isActive: actionViewContext.isActive, + sortState: actionViewContext.sortState, + setSortState: actionViewContext.setSortState ?? (() => {}), } : { treeIsLoading: localTreeIsLoading, @@ -101,5 +109,7 @@ export function useSearchTreeState({ totalItems: localTotalItems, setTotalItems: setLocalTotalItems, isActive: undefined, + sortState: localSortState, + setSortState: setLocalSortState, }; } diff --git a/src/views/actionViews/TreeActionView.tsx b/src/views/actionViews/TreeActionView.tsx index 7cd439bac..a9bde73ee 100644 --- a/src/views/actionViews/TreeActionView.tsx +++ b/src/views/actionViews/TreeActionView.tsx @@ -1,7 +1,14 @@ import TreeActionBar from "@/actionbar/TreeActionBar"; import { FormView, TreeView, View } from "@/types"; import TitleHeader from "@/ui/TitleHeader"; -import { Fragment, useCallback, useContext, useEffect, useMemo } from "react"; +import { + Fragment, + useCallback, + useContext, + useEffect, + useMemo, + useRef, +} from "react"; import { ActionViewContext, ActionViewContextType, @@ -43,6 +50,7 @@ export const TreeActionView = (props: TreeActionViewProps) => { availableViews, searchTreeNameSearch, } = props; + const previousVisibleRef = useRef(visible); const isInfiniteTree = useMemo(() => { if (!treeView?.arch || treeView.isExpandable) { @@ -57,9 +65,12 @@ export const TreeActionView = (props: TreeActionViewProps) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [isInfiniteTree]); - const { currentView, setPreviousView, setIsInfiniteTree } = useContext( - ActionViewContext, - ) as ActionViewContextType; + const { + currentView, + setPreviousView, + setIsInfiniteTree, + setSelectedRowItems, + } = useContext(ActionViewContext) as ActionViewContextType; const onRowClicked = useCallback( (event: any) => { @@ -86,6 +97,14 @@ export const TreeActionView = (props: TreeActionViewProps) => { ], ); + useEffect(() => { + if (previousVisibleRef.current && !visible && isInfiniteTree) { + setSelectedRowItems?.([]); + } + previousVisibleRef.current = visible; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [visible, isInfiniteTree]); + if (!visible) { return null; } diff --git a/src/widgets/base/one2many/One2manyTree.tsx b/src/widgets/base/one2many/One2manyTree.tsx index 6f1a6a2f4..6eda2a9f8 100644 --- a/src/widgets/base/one2many/One2manyTree.tsx +++ b/src/widgets/base/one2many/One2manyTree.tsx @@ -1,12 +1,16 @@ import { + ColumnState, InfiniteTable, InfiniteTableRef, SortDirection, } from "@gisce/react-formiga-table"; import { One2manyItem } from "./One2manyInput"; import { Tree as TreeOoui } from "@gisce/ooui"; -import { RefObject, useCallback, useMemo, useRef } from "react"; -import { getTableColumns, getTableItems } from "@/helpers/treeHelper"; +import { RefObject, useCallback, useRef } from "react"; +import { + getSortedFieldsFromState, + getTableColumns, +} from "@/helpers/treeHelper"; import { COLUMN_COMPONENTS } from "@/widgets/views/Tree/treeComponents"; import useDeepCompareEffect from "use-deep-compare-effect"; import { useDeepCompareMemo } from "use-deep-compare"; @@ -113,12 +117,15 @@ export const One2manyTree = ({ async ({ startRow, endRow, - sortFields, + state, }: { startRow: number; endRow: number; - sortFields?: Record; + state?: ColumnState[]; }) => { + const sortFields = getSortedFieldsFromState({ + state, + }); const { results, colors, status } = await onFetchRecords({ allItems: itemsRef.current, startRow, diff --git a/src/widgets/views/SearchTreeInfinite.tsx b/src/widgets/views/SearchTreeInfinite.tsx index 4cf798bb2..edda42708 100644 --- a/src/widgets/views/SearchTreeInfinite.tsx +++ b/src/widgets/views/SearchTreeInfinite.tsx @@ -17,17 +17,22 @@ import { Badge, Spin } from "antd"; import { getColorMap, getOrderFromSortFields, + getSortedFieldsFromState, getStatusMap, getTableColumns, getTableItems, getTree, } from "@/helpers/treeHelper"; import { COLUMN_COMPONENTS } from "./Tree/treeComponents"; -import { useDeepCompareEffect, useDeepCompareMemo } from "use-deep-compare"; import { + useDeepCompareCallback, + useDeepCompareEffect, + useDeepCompareMemo, +} from "use-deep-compare"; +import { + ColumnState, InfiniteTable, InfiniteTableRef, - SortDirection, } from "@gisce/react-formiga-table"; import ConnectionProvider from "@/ConnectionProvider"; import { useAvailableHeight } from "@/hooks/useAvailableHeight"; @@ -91,6 +96,7 @@ function SearchTreeInfiniteComp(props: SearchTreeInfiniteProps, ref: any) { const statusForResults = useRef<{ [key: number]: string }>(); const tableRef: RefObject = useRef(null); const lastAssignedResults = useRef([]); + const hasRestoredSortStateForFirstTime = useRef(false); const showErrorDialog = useShowErrorDialog(); const [totalRowsLoading, setTotalRowsLoading] = useState(true); @@ -130,6 +136,8 @@ function SearchTreeInfiniteComp(props: SearchTreeInfiniteProps, ref: any) { setSearchQuery, setTotalItems: setTotalItemsActionView, isActive, + sortState: actionViewSortState, + setSortState: setActionViewSortState, } = useSearchTreeState({ useLocalState: !rootTree }); const nameSearch = nameSearchProps || searchTreeNameSearch; @@ -188,11 +196,14 @@ function SearchTreeInfiniteComp(props: SearchTreeInfiniteProps, ref: any) { }, [treeOoui, parentContext]); const columnStateKey = useMemo(() => { + if (loading) { + return undefined; + } return getKey({ treeViewId: treeView?.view_id, model, }); - }, [model, treeView?.view_id]); + }, [model, treeView?.view_id, loading]); const { loading: getColumnStateInProgress, @@ -245,15 +256,15 @@ function SearchTreeInfiniteComp(props: SearchTreeInfiniteProps, ref: any) { showErrorDialog, ]); - const fetchResults = useCallback( + const fetchResults = useDeepCompareCallback( async ({ startRow, endRow, - sortFields, + state, }: { startRow: number; endRow: number; - sortFields?: Record; + state?: ColumnState[]; }) => { if (!treeOoui) { return []; @@ -267,8 +278,33 @@ function SearchTreeInfiniteComp(props: SearchTreeInfiniteProps, ref: any) { attrs.status = treeOoui.status; } + let order; + if (!hasRestoredSortStateForFirstTime.current && actionViewSortState) { + hasRestoredSortStateForFirstTime.current = true; + const sortFields = getSortedFieldsFromState({ + state: actionViewSortState, + }); + order = getOrderFromSortFields(sortFields); + } else { + const stateWithSortData = state + ?.filter((column) => column.sort || column.sortIndex) + .map((column) => ({ + sort: column.sort || undefined, + sortIndex: column.sortIndex || undefined, + colId: column.colId, + })); + const finalStateWithSortData = + stateWithSortData && stateWithSortData?.length > 0 + ? stateWithSortData + : undefined; + const sortFields = getSortedFieldsFromState({ + state: finalStateWithSortData, + }); + setActionViewSortState?.(finalStateWithSortData); + order = getOrderFromSortFields(sortFields); + } + const params = nameSearch ? domain : mergedParams; - const order = getOrderFromSortFields(sortFields); const { results, attrsEvaluated } = await ConnectionProvider.getHandler().searchForTree({ @@ -337,6 +373,7 @@ function SearchTreeInfiniteComp(props: SearchTreeInfiniteProps, ref: any) { }, [ actionViewResults, + actionViewSortState, domain, mergedParams, model, @@ -344,6 +381,7 @@ function SearchTreeInfiniteComp(props: SearchTreeInfiniteProps, ref: any) { nameSearch, parentContext, setActionViewResults, + setActionViewSortState, setSearchQuery, setTotalItemsActionView, treeOoui, @@ -363,18 +401,18 @@ function SearchTreeInfiniteComp(props: SearchTreeInfiniteProps, ref: any) { async ({ startRow, endRow, - sortFields, + state, }: { startRow: number; endRow: number; - sortFields?: Record; + state?: ColumnState[]; }) => { try { setTreeIsLoading?.(true); const results = await fetchResults({ startRow, endRow, - sortFields, + state, }); return results; } catch (error) { @@ -525,9 +563,11 @@ function SearchTreeInfiniteComp(props: SearchTreeInfiniteProps, ref: any) { statusComponent={statusComp} onRowStatus={onRowStatus} strings={strings} + initialSortState={actionViewSortState} /> ); }, [ + actionViewSortState, availableHeight, changeSelectedRowKeys, columns,