mirror of
				https://github.com/lingble/twenty.git
				synced 2025-10-31 04:37:56 +00:00 
			
		
		
		
	feat: default record group table (#8397)
This PR is preparing states to we'll be able to handle view groups correctly with table data. RowIds are now stores in 2 component states, one storing ids by view group and another storing all the rowIds. We're doing that because some other state like focus, or select must not be scoped by view group id.
This commit is contained in:
		| @@ -3,8 +3,10 @@ import { renderHook } from '@testing-library/react'; | |||||||
| import { ReactNode } from 'react'; | import { ReactNode } from 'react'; | ||||||
|  |  | ||||||
| import { mocks } from '@/auth/hooks/__mocks__/useAuth'; | import { mocks } from '@/auth/hooks/__mocks__/useAuth'; | ||||||
|  | import { RecordGroupContext } from '@/object-record/record-group/states/context/RecordGroupContext'; | ||||||
| import { useLoadRecordIndexTable } from '@/object-record/record-index/hooks/useLoadRecordIndexTable'; | import { useLoadRecordIndexTable } from '@/object-record/record-index/hooks/useLoadRecordIndexTable'; | ||||||
| import { RecordTableComponentInstance } from '@/object-record/record-table/components/RecordTableComponentInstance'; | import { RecordTableComponentInstance } from '@/object-record/record-table/components/RecordTableComponentInstance'; | ||||||
|  | import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; | ||||||
| import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper'; | import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper'; | ||||||
|  |  | ||||||
| const recordTableId = 'people'; | const recordTableId = 'people'; | ||||||
| @@ -23,12 +25,18 @@ const Wrapper = ({ children }: { children: ReactNode }) => { | |||||||
|   return ( |   return ( | ||||||
|     <HookMockWrapper> |     <HookMockWrapper> | ||||||
|       <ObjectNamePluralSetter> |       <ObjectNamePluralSetter> | ||||||
|  |         <ViewComponentInstanceContext.Provider | ||||||
|  |           value={{ instanceId: 'instanceId' }} | ||||||
|  |         > | ||||||
|           <RecordTableComponentInstance |           <RecordTableComponentInstance | ||||||
|             recordTableId={recordTableId} |             recordTableId={recordTableId} | ||||||
|             onColumnsChange={onColumnsChange} |             onColumnsChange={onColumnsChange} | ||||||
|           > |           > | ||||||
|  |             <RecordGroupContext.Provider value={{ recordGroupId: 'default' }}> | ||||||
|               {children} |               {children} | ||||||
|  |             </RecordGroupContext.Provider> | ||||||
|           </RecordTableComponentInstance> |           </RecordTableComponentInstance> | ||||||
|  |         </ViewComponentInstanceContext.Provider> | ||||||
|       </ObjectNamePluralSetter> |       </ObjectNamePluralSetter> | ||||||
|     </HookMockWrapper> |     </HookMockWrapper> | ||||||
|   ); |   ); | ||||||
|   | |||||||
| @@ -0,0 +1,37 @@ | |||||||
|  | import { RecordGroupContext } from '@/object-record/record-group/states/context/RecordGroupContext'; | ||||||
|  | import { hasRecordGroupDefinitionsComponentSelector } from '@/object-record/record-group/states/hasRecordGroupDefinitionsComponentSelector'; | ||||||
|  | import { recordGroupDefinitionsComponentState } from '@/object-record/record-group/states/recordGroupDefinitionsComponentState'; | ||||||
|  | import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; | ||||||
|  | import { useContext, useMemo } from 'react'; | ||||||
|  |  | ||||||
|  | export const useCurrentRecordGroupDefinition = (recordTableId?: string) => { | ||||||
|  |   const context = useContext(RecordGroupContext); | ||||||
|  |  | ||||||
|  |   const hasRecordGroups = useRecoilComponentValueV2( | ||||||
|  |     hasRecordGroupDefinitionsComponentSelector, | ||||||
|  |     recordTableId, | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   const recordGroupDefinitions = useRecoilComponentValueV2( | ||||||
|  |     recordGroupDefinitionsComponentState, | ||||||
|  |     recordTableId, | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   const recordGroupDefinition = useMemo(() => { | ||||||
|  |     if (!hasRecordGroups) { | ||||||
|  |       return undefined; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!context) { | ||||||
|  |       throw new Error( | ||||||
|  |         'useCurrentRecordGroupDefinition must be used within a RecordGroupContextProvider.', | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return recordGroupDefinitions.find( | ||||||
|  |       ({ id }) => id === context.recordGroupId, | ||||||
|  |     ); | ||||||
|  |   }, [context, hasRecordGroups, recordGroupDefinitions]); | ||||||
|  |  | ||||||
|  |   return recordGroupDefinition; | ||||||
|  | }; | ||||||
| @@ -0,0 +1,20 @@ | |||||||
|  | import { RecordGroupContext } from '@/object-record/record-group/states/context/RecordGroupContext'; | ||||||
|  | import { useContext } from 'react'; | ||||||
|  |  | ||||||
|  | export const useCurrentRecordGroupId = () => { | ||||||
|  |   const context = useContext(RecordGroupContext); | ||||||
|  |  | ||||||
|  |   if (!context) { | ||||||
|  |     throw new Error( | ||||||
|  |       'useCurrentRecordGroupId must be used within a RecordGroupContextProvider.', | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (!context.recordGroupId) { | ||||||
|  |     throw new Error( | ||||||
|  |       'RecordGroupContext is malformed. recordGroupId is missing.', | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return context.recordGroupId; | ||||||
|  | }; | ||||||
| @@ -23,7 +23,7 @@ export const useRecordGroupReorder = ({ | |||||||
|   ); |   ); | ||||||
|  |  | ||||||
|   const { visibleRecordGroups } = useRecordGroups({ |   const { visibleRecordGroups } = useRecordGroups({ | ||||||
|     objectNameSingular, |     objectNameSingular: objectNameSingular, | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   const { saveViewGroups } = useSaveCurrentViewGroups(viewBarId); |   const { saveViewGroups } = useSaveCurrentViewGroups(viewBarId); | ||||||
|   | |||||||
| @@ -0,0 +1,9 @@ | |||||||
|  | import { createContext } from 'react'; | ||||||
|  |  | ||||||
|  | export type RecordGroupContextProps = { | ||||||
|  |   recordGroupId: string; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export const RecordGroupContext = createContext<RecordGroupContextProps>( | ||||||
|  |   {} as RecordGroupContextProps, | ||||||
|  | ); | ||||||
| @@ -0,0 +1,21 @@ | |||||||
|  | import { recordGroupDefinitionsComponentState } from '@/object-record/record-group/states/recordGroupDefinitionsComponentState'; | ||||||
|  |  | ||||||
|  | import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2'; | ||||||
|  | import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; | ||||||
|  |  | ||||||
|  | export const hasRecordGroupDefinitionsComponentSelector = | ||||||
|  |   createComponentSelectorV2<boolean>({ | ||||||
|  |     key: 'hasRecordGroupDefinitionsComponentSelector', | ||||||
|  |     componentInstanceContext: ViewComponentInstanceContext, | ||||||
|  |     get: | ||||||
|  |       ({ instanceId }) => | ||||||
|  |       ({ get }) => { | ||||||
|  |         const recordGroupDefinitions = get( | ||||||
|  |           recordGroupDefinitionsComponentState.atomFamily({ | ||||||
|  |             instanceId, | ||||||
|  |           }), | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         return recordGroupDefinitions.length > 0; | ||||||
|  |       }, | ||||||
|  |   }); | ||||||
| @@ -6,6 +6,7 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata | |||||||
| import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; | import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; | ||||||
| import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy'; | import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy'; | ||||||
| import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter'; | import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter'; | ||||||
|  | import { useCurrentRecordGroupDefinition } from '@/object-record/record-group/hooks/useCurrentRecordGroupDefinition'; | ||||||
| import { useRecordTableRecordGqlFields } from '@/object-record/record-index/hooks/useRecordTableRecordGqlFields'; | import { useRecordTableRecordGqlFields } from '@/object-record/record-index/hooks/useRecordTableRecordGqlFields'; | ||||||
| import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable'; | import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable'; | ||||||
| import { tableFiltersComponentState } from '@/object-record/record-table/states/tableFiltersComponentState'; | import { tableFiltersComponentState } from '@/object-record/record-table/states/tableFiltersComponentState'; | ||||||
| @@ -14,6 +15,8 @@ import { tableViewFilterGroupsComponentState } from '@/object-record/record-tabl | |||||||
| import { SIGN_IN_BACKGROUND_MOCK_COMPANIES } from '@/sign-in-background-mock/constants/SignInBackgroundMockCompanies'; | import { SIGN_IN_BACKGROUND_MOCK_COMPANIES } from '@/sign-in-background-mock/constants/SignInBackgroundMockCompanies'; | ||||||
| import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; | import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; | ||||||
| import { isNull } from '@sniptt/guards'; | import { isNull } from '@sniptt/guards'; | ||||||
|  | import { useMemo } from 'react'; | ||||||
|  | import { isDefined } from 'twenty-ui'; | ||||||
| import { WorkspaceActivationStatus } from '~/generated/graphql'; | import { WorkspaceActivationStatus } from '~/generated/graphql'; | ||||||
|  |  | ||||||
| export const useFindManyParams = ( | export const useFindManyParams = ( | ||||||
| @@ -24,6 +27,9 @@ export const useFindManyParams = ( | |||||||
|     objectNameSingular, |     objectNameSingular, | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|  |   const currentRecordGroupDefinition = | ||||||
|  |     useCurrentRecordGroupDefinition(recordTableId); | ||||||
|  |  | ||||||
|   const tableViewFilterGroups = useRecoilComponentValueV2( |   const tableViewFilterGroups = useRecoilComponentValueV2( | ||||||
|     tableViewFilterGroupsComponentState, |     tableViewFilterGroupsComponentState, | ||||||
|     recordTableId, |     recordTableId, | ||||||
| @@ -37,15 +43,45 @@ export const useFindManyParams = ( | |||||||
|     recordTableId, |     recordTableId, | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|   const filter = computeViewRecordGqlOperationFilter( |   const stateFilter = computeViewRecordGqlOperationFilter( | ||||||
|     tableFilters, |     tableFilters, | ||||||
|     objectMetadataItem?.fields ?? [], |     objectMetadataItem?.fields ?? [], | ||||||
|     tableViewFilterGroups, |     tableViewFilterGroups, | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|  |   const recordGroupFilter = useMemo(() => { | ||||||
|  |     if (isDefined(currentRecordGroupDefinition)) { | ||||||
|  |       const fieldMetadataItem = objectMetadataItem?.fields.find( | ||||||
|  |         (fieldMetadataItem) => | ||||||
|  |           fieldMetadataItem.id === currentRecordGroupDefinition.fieldMetadataId, | ||||||
|  |       ); | ||||||
|  |  | ||||||
|  |       if (!fieldMetadataItem) { | ||||||
|  |         return {}; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       return { | ||||||
|  |         [fieldMetadataItem.name]: { | ||||||
|  |           eq: currentRecordGroupDefinition.value, | ||||||
|  |         }, | ||||||
|  |       }; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // TODO: Handle case when value is nullable | ||||||
|  |  | ||||||
|  |     return {}; | ||||||
|  |   }, [objectMetadataItem.fields, currentRecordGroupDefinition]); | ||||||
|  |  | ||||||
|   const orderBy = turnSortsIntoOrderBy(objectMetadataItem, tableSorts); |   const orderBy = turnSortsIntoOrderBy(objectMetadataItem, tableSorts); | ||||||
|  |  | ||||||
|   return { objectNameSingular, filter, orderBy }; |   return { | ||||||
|  |     objectNameSingular, | ||||||
|  |     filter: { | ||||||
|  |       ...stateFilter, | ||||||
|  |       ...recordGroupFilter, | ||||||
|  |     }, | ||||||
|  |     orderBy, | ||||||
|  |   }; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| export const useLoadRecordIndexTable = (objectNameSingular: string) => { | export const useLoadRecordIndexTable = (objectNameSingular: string) => { | ||||||
|   | |||||||
| @@ -1,18 +1,22 @@ | |||||||
| import styled from '@emotion/styled'; | import styled from '@emotion/styled'; | ||||||
| import { isNonEmptyString, isNull } from '@sniptt/guards'; | import { isNonEmptyString, isNull } from '@sniptt/guards'; | ||||||
|  |  | ||||||
|  | import { hasRecordGroupDefinitionsComponentSelector } from '@/object-record/record-group/states/hasRecordGroupDefinitionsComponentSelector'; | ||||||
| import { RecordTableComponentInstance } from '@/object-record/record-table/components/RecordTableComponentInstance'; | import { RecordTableComponentInstance } from '@/object-record/record-table/components/RecordTableComponentInstance'; | ||||||
| import { RecordTableContextProvider } from '@/object-record/record-table/components/RecordTableContextProvider'; | import { RecordTableContextProvider } from '@/object-record/record-table/components/RecordTableContextProvider'; | ||||||
|  | import { RecordTableStickyEffect } from '@/object-record/record-table/components/RecordTableStickyEffect'; | ||||||
| import { RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-table/constants/RecordTableClickOutsideListenerId'; | import { RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-table/constants/RecordTableClickOutsideListenerId'; | ||||||
| import { RecordTableEmptyState } from '@/object-record/record-table/empty-state/components/RecordTableEmptyState'; | import { RecordTableEmptyState } from '@/object-record/record-table/empty-state/components/RecordTableEmptyState'; | ||||||
| import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable'; | import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable'; | ||||||
| import { RecordTableBody } from '@/object-record/record-table/record-table-body/components/RecordTableBody'; |  | ||||||
| import { RecordTableBodyEffect } from '@/object-record/record-table/record-table-body/components/RecordTableBodyEffect'; |  | ||||||
| import { RecordTableBodyUnselectEffect } from '@/object-record/record-table/record-table-body/components/RecordTableBodyUnselectEffect'; | import { RecordTableBodyUnselectEffect } from '@/object-record/record-table/record-table-body/components/RecordTableBodyUnselectEffect'; | ||||||
|  | import { RecordTableNoRecordGroupBody } from '@/object-record/record-table/record-table-body/components/RecordTableNoRecordGroupBody'; | ||||||
|  | import { RecordTableNoRecordGroupBodyEffect } from '@/object-record/record-table/record-table-body/components/RecordTableNoRecordGroupBodyEffect'; | ||||||
|  | import { RecordTableRecordGroupBodyEffects } from '@/object-record/record-table/record-table-body/components/RecordTableRecordGroupBodyEffects'; | ||||||
|  | import { RecordTableRecordGroupsBody } from '@/object-record/record-table/record-table-body/components/RecordTableRecordGroupsBody'; | ||||||
| import { RecordTableHeader } from '@/object-record/record-table/record-table-header/components/RecordTableHeader'; | import { RecordTableHeader } from '@/object-record/record-table/record-table-header/components/RecordTableHeader'; | ||||||
| import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState'; | import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState'; | ||||||
| import { recordTablePendingRecordIdComponentState } from '@/object-record/record-table/states/recordTablePendingRecordIdComponentState'; | import { recordTablePendingRecordIdComponentState } from '@/object-record/record-table/states/recordTablePendingRecordIdComponentState'; | ||||||
| import { tableRowIdsComponentState } from '@/object-record/record-table/states/tableRowIdsComponentState'; | import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState'; | ||||||
| import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect'; | import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect'; | ||||||
| import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useClickOutsideListener'; | import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useClickOutsideListener'; | ||||||
| import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; | import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; | ||||||
| @@ -40,17 +44,17 @@ export const RecordTable = ({ | |||||||
| }: RecordTableProps) => { | }: RecordTableProps) => { | ||||||
|   const tableBodyRef = useRef<HTMLTableElement>(null); |   const tableBodyRef = useRef<HTMLTableElement>(null); | ||||||
|  |  | ||||||
|  |   const { toggleClickOutsideListener } = useClickOutsideListener( | ||||||
|  |     RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID, | ||||||
|  |   ); | ||||||
|  |  | ||||||
|   const isRecordTableInitialLoading = useRecoilComponentValueV2( |   const isRecordTableInitialLoading = useRecoilComponentValueV2( | ||||||
|     isRecordTableInitialLoadingComponentState, |     isRecordTableInitialLoadingComponentState, | ||||||
|     recordTableId, |     recordTableId, | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|   const { toggleClickOutsideListener } = useClickOutsideListener( |  | ||||||
|     RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID, |  | ||||||
|   ); |  | ||||||
|  |  | ||||||
|   const tableRowIds = useRecoilComponentValueV2( |   const tableRowIds = useRecoilComponentValueV2( | ||||||
|     tableRowIdsComponentState, |     tableAllRowIdsComponentState, | ||||||
|     recordTableId, |     recordTableId, | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
| @@ -59,15 +63,20 @@ export const RecordTable = ({ | |||||||
|     recordTableId, |     recordTableId, | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|   const { resetTableRowSelection, setRowSelected } = useRecordTable({ |   const hasRecordGroups = useRecoilComponentValueV2( | ||||||
|  |     hasRecordGroupDefinitionsComponentSelector, | ||||||
|     recordTableId, |     recordTableId, | ||||||
|   }); |   ); | ||||||
|  |  | ||||||
|   const recordTableIsEmpty = |   const recordTableIsEmpty = | ||||||
|     !isRecordTableInitialLoading && |     !isRecordTableInitialLoading && | ||||||
|     tableRowIds.length === 0 && |     tableRowIds.length === 0 && | ||||||
|     isNull(pendingRecordId); |     isNull(pendingRecordId); | ||||||
|  |  | ||||||
|  |   const { resetTableRowSelection, setRowSelected } = useRecordTable({ | ||||||
|  |     recordTableId, | ||||||
|  |   }); | ||||||
|  |  | ||||||
|   if (!isNonEmptyString(objectNameSingular)) { |   if (!isNonEmptyString(objectNameSingular)) { | ||||||
|     return <></>; |     return <></>; | ||||||
|   } |   } | ||||||
| @@ -82,7 +91,11 @@ export const RecordTable = ({ | |||||||
|         recordTableId={recordTableId} |         recordTableId={recordTableId} | ||||||
|         viewBarId={viewBarId} |         viewBarId={viewBarId} | ||||||
|       > |       > | ||||||
|         <RecordTableBodyEffect /> |         {!hasRecordGroups ? ( | ||||||
|  |           <RecordTableNoRecordGroupBodyEffect /> | ||||||
|  |         ) : ( | ||||||
|  |           <RecordTableRecordGroupBodyEffects /> | ||||||
|  |         )} | ||||||
|         <RecordTableBodyUnselectEffect |         <RecordTableBodyUnselectEffect | ||||||
|           tableBodyRef={tableBodyRef} |           tableBodyRef={tableBodyRef} | ||||||
|           recordTableId={recordTableId} |           recordTableId={recordTableId} | ||||||
| @@ -92,10 +105,15 @@ export const RecordTable = ({ | |||||||
|         ) : ( |         ) : ( | ||||||
|           <> |           <> | ||||||
|             <StyledTable className="entity-table-cell" ref={tableBodyRef}> |             <StyledTable className="entity-table-cell" ref={tableBodyRef}> | ||||||
|               <RecordTableHeader |               <RecordTableHeader objectNameSingular={objectNameSingular} /> | ||||||
|                 objectMetadataNameSingular={objectNameSingular} |               {!hasRecordGroups ? ( | ||||||
|  |                 <RecordTableNoRecordGroupBody /> | ||||||
|  |               ) : ( | ||||||
|  |                 <RecordTableRecordGroupsBody | ||||||
|  |                   objectNameSingular={objectNameSingular} | ||||||
|                 /> |                 /> | ||||||
|               <RecordTableBody /> |               )} | ||||||
|  |               <RecordTableStickyEffect /> | ||||||
|             </StyledTable> |             </StyledTable> | ||||||
|             <DragSelect |             <DragSelect | ||||||
|               dragSelectable={tableBodyRef} |               dragSelectable={tableBodyRef} | ||||||
|   | |||||||
| @@ -1,14 +1,14 @@ | |||||||
| import { RecordTableBodyFetchMoreLoader } from '@/object-record/record-table/record-table-body/components/RecordTableBodyFetchMoreLoader'; | import { RecordTableBodyFetchMoreLoader } from '@/object-record/record-table/record-table-body/components/RecordTableBodyFetchMoreLoader'; | ||||||
| import { RecordTableRow } from '@/object-record/record-table/record-table-row/components/RecordTableRow'; | import { RecordTableRow } from '@/object-record/record-table/record-table-row/components/RecordTableRow'; | ||||||
| import { tableRowIdsComponentState } from '@/object-record/record-table/states/tableRowIdsComponentState'; | import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState'; | ||||||
| import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; | import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; | ||||||
| 
 | 
 | ||||||
| export const RecordTableRows = () => { | export const RecordTableNoRecordGroupRows = () => { | ||||||
|   const tableRowIds = useRecoilComponentValueV2(tableRowIdsComponentState); |   const rowIds = useRecoilComponentValueV2(tableAllRowIdsComponentState); | ||||||
| 
 | 
 | ||||||
|   return ( |   return ( | ||||||
|     <> |     <> | ||||||
|       {tableRowIds.map((recordId, rowIndex) => { |       {rowIds.map((recordId, rowIndex) => { | ||||||
|         return ( |         return ( | ||||||
|           <RecordTableRow |           <RecordTableRow | ||||||
|             key={recordId} |             key={recordId} | ||||||
| @@ -0,0 +1,26 @@ | |||||||
|  | import { useCurrentRecordGroupId } from '@/object-record/record-group/hooks/useCurrentRecordGroupId'; | ||||||
|  | import { RecordTableRow } from '@/object-record/record-table/record-table-row/components/RecordTableRow'; | ||||||
|  | import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState'; | ||||||
|  | import { tableRowIdsByGroupComponentFamilyState } from '@/object-record/record-table/states/tableRowIdsByGroupComponentFamilyState'; | ||||||
|  | import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2'; | ||||||
|  | import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; | ||||||
|  |  | ||||||
|  | export const RecordTableRecordGroupRows = () => { | ||||||
|  |   const recordGroupId = useCurrentRecordGroupId(); | ||||||
|  |  | ||||||
|  |   const allRowIds = useRecoilComponentValueV2(tableAllRowIdsComponentState); | ||||||
|  |  | ||||||
|  |   const recordGroupRowIds = useRecoilComponentFamilyValueV2( | ||||||
|  |     tableRowIdsByGroupComponentFamilyState, | ||||||
|  |     recordGroupId, | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   return recordGroupRowIds.map((recordId) => { | ||||||
|  |     // Find the index of the recordId in allRowIds | ||||||
|  |     const rowIndex = allRowIds.indexOf(recordId); | ||||||
|  |  | ||||||
|  |     return ( | ||||||
|  |       <RecordTableRow key={recordId} recordId={recordId} rowIndex={rowIndex} /> | ||||||
|  |     ); | ||||||
|  |   }); | ||||||
|  | }; | ||||||
| @@ -0,0 +1,49 @@ | |||||||
|  | import { useEffect } from 'react'; | ||||||
|  |  | ||||||
|  | import { isRecordTableScrolledLeftComponentState } from '@/object-record/record-table/states/isRecordTableScrolledLeftComponentState'; | ||||||
|  | import { useScrollLeftValue } from '@/ui/utilities/scroll/hooks/useScrollLeftValue'; | ||||||
|  | import { useScrollTopValue } from '@/ui/utilities/scroll/hooks/useScrollTopValue'; | ||||||
|  | import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; | ||||||
|  |  | ||||||
|  | export const RecordTableStickyEffect = () => { | ||||||
|  |   const scrollTop = useScrollTopValue('recordTableWithWrappers'); | ||||||
|  |  | ||||||
|  |   useEffect(() => { | ||||||
|  |     if (scrollTop > 0) { | ||||||
|  |       document | ||||||
|  |         .getElementById('record-table-header') | ||||||
|  |         ?.classList.add('header-sticky'); | ||||||
|  |     } else { | ||||||
|  |       document | ||||||
|  |         .getElementById('record-table-header') | ||||||
|  |         ?.classList.remove('header-sticky'); | ||||||
|  |     } | ||||||
|  |   }, [scrollTop]); | ||||||
|  |  | ||||||
|  |   const scrollLeft = useScrollLeftValue('recordTableWithWrappers'); | ||||||
|  |  | ||||||
|  |   const setIsRecordTableScrolledLeft = useSetRecoilComponentStateV2( | ||||||
|  |     isRecordTableScrolledLeftComponentState, | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   useEffect(() => { | ||||||
|  |     setIsRecordTableScrolledLeft(scrollLeft === 0); | ||||||
|  |     if (scrollLeft > 0) { | ||||||
|  |       document | ||||||
|  |         .getElementById('record-table-body') | ||||||
|  |         ?.classList.add('first-columns-sticky'); | ||||||
|  |       document | ||||||
|  |         .getElementById('record-table-header') | ||||||
|  |         ?.classList.add('first-columns-sticky'); | ||||||
|  |     } else { | ||||||
|  |       document | ||||||
|  |         .getElementById('record-table-body') | ||||||
|  |         ?.classList.remove('first-columns-sticky'); | ||||||
|  |       document | ||||||
|  |         .getElementById('record-table-header') | ||||||
|  |         ?.classList.remove('first-columns-sticky'); | ||||||
|  |     } | ||||||
|  |   }, [scrollLeft, setIsRecordTableScrolledLeft]); | ||||||
|  |  | ||||||
|  |   return <></>; | ||||||
|  | }; | ||||||
| @@ -0,0 +1,43 @@ | |||||||
|  | import { isNull } from '@sniptt/guards'; | ||||||
|  |  | ||||||
|  | import { RecordTableEmptyState } from '@/object-record/record-table/empty-state/components/RecordTableEmptyState'; | ||||||
|  | import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState'; | ||||||
|  | import { recordTablePendingRecordIdComponentState } from '@/object-record/record-table/states/recordTablePendingRecordIdComponentState'; | ||||||
|  | import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState'; | ||||||
|  | import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; | ||||||
|  |  | ||||||
|  | type RecordTableEmptyHandlerProps = { | ||||||
|  |   recordTableId: string; | ||||||
|  |   children: React.ReactNode; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export const RecordTableEmptyHandler = ({ | ||||||
|  |   recordTableId, | ||||||
|  |   children, | ||||||
|  | }: RecordTableEmptyHandlerProps) => { | ||||||
|  |   const isRecordTableInitialLoading = useRecoilComponentValueV2( | ||||||
|  |     isRecordTableInitialLoadingComponentState, | ||||||
|  |     recordTableId, | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   const tableRowIds = useRecoilComponentValueV2( | ||||||
|  |     tableAllRowIdsComponentState, | ||||||
|  |     recordTableId, | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   const pendingRecordId = useRecoilComponentValueV2( | ||||||
|  |     recordTablePendingRecordIdComponentState, | ||||||
|  |     recordTableId, | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   const recordTableIsEmpty = | ||||||
|  |     !isRecordTableInitialLoading && | ||||||
|  |     tableRowIds.length === 0 && | ||||||
|  |     isNull(pendingRecordId); | ||||||
|  |  | ||||||
|  |   if (recordTableIsEmpty) { | ||||||
|  |     return <RecordTableEmptyState />; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return children; | ||||||
|  | }; | ||||||
| @@ -5,7 +5,7 @@ import { getActionMenuIdFromRecordIndexId } from '@/action-menu/utils/getActionM | |||||||
| import { hasUserSelectedAllRowsComponentState } from '@/object-record/record-table/record-table-row/states/hasUserSelectedAllRowsFamilyState'; | import { hasUserSelectedAllRowsComponentState } from '@/object-record/record-table/record-table-row/states/hasUserSelectedAllRowsFamilyState'; | ||||||
| import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; | import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; | ||||||
| import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; | import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; | ||||||
| import { tableRowIdsComponentState } from '@/object-record/record-table/states/tableRowIdsComponentState'; | import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState'; | ||||||
| import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState'; | import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState'; | ||||||
| import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; | import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; | ||||||
| import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; | import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; | ||||||
| @@ -18,8 +18,8 @@ export const useResetTableRowSelection = (recordTableId?: string) => { | |||||||
|     recordTableId, |     recordTableId, | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|   const tableRowIdsState = useRecoilComponentCallbackStateV2( |   const tableAllRowIdsState = useRecoilComponentCallbackStateV2( | ||||||
|     tableRowIdsComponentState, |     tableAllRowIdsComponentState, | ||||||
|     recordTableIdFromContext, |     recordTableIdFromContext, | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
| @@ -41,9 +41,9 @@ export const useResetTableRowSelection = (recordTableId?: string) => { | |||||||
|   ); |   ); | ||||||
|  |  | ||||||
|   return useRecoilCallback( |   return useRecoilCallback( | ||||||
|     ({ snapshot, set }) => |     ({ set, snapshot }) => | ||||||
|       () => { |       () => { | ||||||
|         const tableRowIds = getSnapshotValue(snapshot, tableRowIdsState); |         const tableRowIds = getSnapshotValue(snapshot, tableAllRowIdsState); | ||||||
|  |  | ||||||
|         for (const rowId of tableRowIds) { |         for (const rowId of tableRowIds) { | ||||||
|           set(isRowSelectedFamilyState(rowId), false); |           set(isRowSelectedFamilyState(rowId), false); | ||||||
| @@ -54,7 +54,7 @@ export const useResetTableRowSelection = (recordTableId?: string) => { | |||||||
|         set(isActionMenuDropdownOpenState, false); |         set(isActionMenuDropdownOpenState, false); | ||||||
|       }, |       }, | ||||||
|     [ |     [ | ||||||
|       tableRowIdsState, |       tableAllRowIdsState, | ||||||
|       hasUserSelectedAllRowsState, |       hasUserSelectedAllRowsState, | ||||||
|       isActionMenuDropdownOpenState, |       isActionMenuDropdownOpenState, | ||||||
|       isRowSelectedFamilyState, |       isRowSelectedFamilyState, | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ import { useRecoilCallback } from 'recoil'; | |||||||
|  |  | ||||||
| import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; | import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; | ||||||
| import { allRowsSelectedStatusComponentSelector } from '@/object-record/record-table/states/selectors/allRowsSelectedStatusComponentSelector'; | import { allRowsSelectedStatusComponentSelector } from '@/object-record/record-table/states/selectors/allRowsSelectedStatusComponentSelector'; | ||||||
| import { tableRowIdsComponentState } from '@/object-record/record-table/states/tableRowIdsComponentState'; | import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState'; | ||||||
| import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; | import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; | ||||||
| import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; | import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; | ||||||
|  |  | ||||||
| @@ -11,14 +11,14 @@ export const useSelectAllRows = (recordTableId?: string) => { | |||||||
|     allRowsSelectedStatusComponentSelector, |     allRowsSelectedStatusComponentSelector, | ||||||
|     recordTableId, |     recordTableId, | ||||||
|   ); |   ); | ||||||
|   const tableRowIdsState = useRecoilComponentCallbackStateV2( |  | ||||||
|     tableRowIdsComponentState, |  | ||||||
|     recordTableId, |  | ||||||
|   ); |  | ||||||
|   const isRowSelectedFamilyState = useRecoilComponentCallbackStateV2( |   const isRowSelectedFamilyState = useRecoilComponentCallbackStateV2( | ||||||
|     isRowSelectedComponentFamilyState, |     isRowSelectedComponentFamilyState, | ||||||
|     recordTableId, |     recordTableId, | ||||||
|   ); |   ); | ||||||
|  |   const tableAllRowIdsState = useRecoilComponentCallbackStateV2( | ||||||
|  |     tableAllRowIdsComponentState, | ||||||
|  |     recordTableId, | ||||||
|  |   ); | ||||||
|  |  | ||||||
|   const selectAllRows = useRecoilCallback( |   const selectAllRows = useRecoilCallback( | ||||||
|     ({ set, snapshot }) => |     ({ set, snapshot }) => | ||||||
| @@ -28,7 +28,7 @@ export const useSelectAllRows = (recordTableId?: string) => { | |||||||
|           allRowsSelectedStatusSelector, |           allRowsSelectedStatusSelector, | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
|         const tableRowIds = getSnapshotValue(snapshot, tableRowIdsState); |         const tableRowIds = getSnapshotValue(snapshot, tableAllRowIdsState); | ||||||
|  |  | ||||||
|         if ( |         if ( | ||||||
|           allRowsSelectedStatus === 'none' || |           allRowsSelectedStatus === 'none' || | ||||||
| @@ -43,7 +43,11 @@ export const useSelectAllRows = (recordTableId?: string) => { | |||||||
|           } |           } | ||||||
|         } |         } | ||||||
|       }, |       }, | ||||||
|     [allRowsSelectedStatusSelector, tableRowIdsState, isRowSelectedFamilyState], |     [ | ||||||
|  |       allRowsSelectedStatusSelector, | ||||||
|  |       tableAllRowIdsState, | ||||||
|  |       isRowSelectedFamilyState, | ||||||
|  |     ], | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|   return { |   return { | ||||||
|   | |||||||
| @@ -1,14 +1,16 @@ | |||||||
| import { useRecoilCallback } from 'recoil'; | import { useRecoilCallback } from 'recoil'; | ||||||
|  |  | ||||||
|  | import { recordGroupDefinitionsComponentState } from '@/object-record/record-group/states/recordGroupDefinitionsComponentState'; | ||||||
| import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; | import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; | ||||||
| import { hasUserSelectedAllRowsComponentState } from '@/object-record/record-table/record-table-row/states/hasUserSelectedAllRowsFamilyState'; | import { hasUserSelectedAllRowsComponentState } from '@/object-record/record-table/record-table-row/states/hasUserSelectedAllRowsFamilyState'; | ||||||
| import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; | import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; | ||||||
| import { numberOfTableRowsComponentState } from '@/object-record/record-table/states/numberOfTableRowsComponentState'; | import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState'; | ||||||
| import { tableRowIdsComponentState } from '@/object-record/record-table/states/tableRowIdsComponentState'; | import { tableRowIdsByGroupComponentFamilyState } from '@/object-record/record-table/states/tableRowIdsByGroupComponentFamilyState'; | ||||||
| import { ObjectRecord } from '@/object-record/types/ObjectRecord'; | import { ObjectRecord } from '@/object-record/types/ObjectRecord'; | ||||||
| import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; | import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; | ||||||
| import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; | import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; | ||||||
| import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; | import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; | ||||||
|  | import { isDefined } from '~/utils/isDefined'; | ||||||
|  |  | ||||||
| type useSetRecordTableDataProps = { | type useSetRecordTableDataProps = { | ||||||
|   recordTableId?: string; |   recordTableId?: string; | ||||||
| @@ -19,12 +21,12 @@ export const useSetRecordTableData = ({ | |||||||
|   recordTableId, |   recordTableId, | ||||||
|   onEntityCountChange, |   onEntityCountChange, | ||||||
| }: useSetRecordTableDataProps) => { | }: useSetRecordTableDataProps) => { | ||||||
|   const tableRowIdsState = useRecoilComponentCallbackStateV2( |   const tableRowIdsByGroupFamilyState = useRecoilComponentCallbackStateV2( | ||||||
|     tableRowIdsComponentState, |     tableRowIdsByGroupComponentFamilyState, | ||||||
|     recordTableId, |     recordTableId, | ||||||
|   ); |   ); | ||||||
|   const numberOfTableRowsState = useRecoilComponentCallbackStateV2( |   const tableAllRowIdsState = useRecoilComponentCallbackStateV2( | ||||||
|     numberOfTableRowsComponentState, |     tableAllRowIdsComponentState, | ||||||
|     recordTableId, |     recordTableId, | ||||||
|   ); |   ); | ||||||
|   const isRowSelectedFamilyState = useRecoilComponentCallbackStateV2( |   const isRowSelectedFamilyState = useRecoilComponentCallbackStateV2( | ||||||
| @@ -35,11 +37,23 @@ export const useSetRecordTableData = ({ | |||||||
|     hasUserSelectedAllRowsComponentState, |     hasUserSelectedAllRowsComponentState, | ||||||
|     recordTableId, |     recordTableId, | ||||||
|   ); |   ); | ||||||
|  |   const recordGroupDefinitionsState = useRecoilComponentCallbackStateV2( | ||||||
|  |     recordGroupDefinitionsComponentState, | ||||||
|  |     recordTableId, | ||||||
|  |   ); | ||||||
|  |  | ||||||
|   return useRecoilCallback( |   return useRecoilCallback( | ||||||
|     ({ set, snapshot }) => |     ({ set, snapshot }) => | ||||||
|       <T extends ObjectRecord>(newRecords: T[], totalCount?: number) => { |       <T extends ObjectRecord>({ | ||||||
|         for (const record of newRecords) { |         records, | ||||||
|  |         recordGroupId, | ||||||
|  |         totalCount, | ||||||
|  |       }: { | ||||||
|  |         records: T[]; | ||||||
|  |         recordGroupId?: string; | ||||||
|  |         totalCount?: number; | ||||||
|  |       }) => { | ||||||
|  |         for (const record of records) { | ||||||
|           // TODO: refactor with scoped state later |           // TODO: refactor with scoped state later | ||||||
|           const currentRecord = snapshot |           const currentRecord = snapshot | ||||||
|             .getLoadable(recordStoreFamilyState(record.id)) |             .getLoadable(recordStoreFamilyState(record.id)) | ||||||
| @@ -50,14 +64,24 @@ export const useSetRecordTableData = ({ | |||||||
|           } |           } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         const currentRowIds = getSnapshotValue(snapshot, tableRowIdsState); |         const currentRowIds = getSnapshotValue( | ||||||
|  |           snapshot, | ||||||
|  |           recordGroupId | ||||||
|  |             ? tableRowIdsByGroupFamilyState(recordGroupId) | ||||||
|  |             : tableAllRowIdsState, | ||||||
|  |         ); | ||||||
|  |  | ||||||
|         const hasUserSelectedAllRows = getSnapshotValue( |         const hasUserSelectedAllRows = getSnapshotValue( | ||||||
|           snapshot, |           snapshot, | ||||||
|           hasUserSelectedAllRowsState, |           hasUserSelectedAllRowsState, | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
|         const recordIds = newRecords.map((record) => record.id); |         const recordGroupDefinitions = getSnapshotValue( | ||||||
|  |           snapshot, | ||||||
|  |           recordGroupDefinitionsState, | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         const recordIds = records.map((record) => record.id); | ||||||
|  |  | ||||||
|         if (!isDeeplyEqual(currentRowIds, recordIds)) { |         if (!isDeeplyEqual(currentRowIds, recordIds)) { | ||||||
|           if (hasUserSelectedAllRows) { |           if (hasUserSelectedAllRows) { | ||||||
| @@ -66,14 +90,36 @@ export const useSetRecordTableData = ({ | |||||||
|             } |             } | ||||||
|           } |           } | ||||||
|  |  | ||||||
|           set(tableRowIdsState, recordIds); |           if (isDefined(recordGroupId)) { | ||||||
|           set(numberOfTableRowsState, totalCount ?? 0); |             // TODO: Hack to store all ids in the same order as the record group definitions | ||||||
|  |             // Should be replaced by something more efficient | ||||||
|  |             const allRowIds: string[] = []; | ||||||
|  |  | ||||||
|  |             set(tableRowIdsByGroupFamilyState(recordGroupId), recordIds); | ||||||
|  |  | ||||||
|  |             for (const recordGroupDefinition of recordGroupDefinitions) { | ||||||
|  |               const tableRowIdsByGroup = | ||||||
|  |                 recordGroupDefinition.id !== recordGroupId | ||||||
|  |                   ? getSnapshotValue( | ||||||
|  |                       snapshot, | ||||||
|  |                       tableRowIdsByGroupFamilyState(recordGroupDefinition.id), | ||||||
|  |                     ) | ||||||
|  |                   : recordIds; | ||||||
|  |  | ||||||
|  |               allRowIds.push(...tableRowIdsByGroup); | ||||||
|  |             } | ||||||
|  |             set(tableAllRowIdsState, allRowIds); | ||||||
|  |           } else { | ||||||
|  |             set(tableAllRowIdsState, recordIds); | ||||||
|  |           } | ||||||
|  |  | ||||||
|           onEntityCountChange(totalCount); |           onEntityCountChange(totalCount); | ||||||
|         } |         } | ||||||
|       }, |       }, | ||||||
|     [ |     [ | ||||||
|       numberOfTableRowsState, |       tableRowIdsByGroupFamilyState, | ||||||
|       tableRowIdsState, |       tableAllRowIdsState, | ||||||
|  |       recordGroupDefinitionsState, | ||||||
|       onEntityCountChange, |       onEntityCountChange, | ||||||
|       isRowSelectedFamilyState, |       isRowSelectedFamilyState, | ||||||
|       hasUserSelectedAllRowsState, |       hasUserSelectedAllRowsState, | ||||||
|   | |||||||
| @@ -3,9 +3,9 @@ import { useRecoilCallback } from 'recoil'; | |||||||
| import { MoveFocusDirection } from '@/object-record/record-table/types/MoveFocusDirection'; | import { MoveFocusDirection } from '@/object-record/record-table/types/MoveFocusDirection'; | ||||||
| import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; | import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; | ||||||
|  |  | ||||||
| import { numberOfTableRowsComponentState } from '@/object-record/record-table/states/numberOfTableRowsComponentState'; |  | ||||||
| import { numberOfTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/numberOfTableColumnsComponentSelector'; | import { numberOfTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/numberOfTableColumnsComponentSelector'; | ||||||
| import { softFocusPositionComponentState } from '@/object-record/record-table/states/softFocusPositionComponentState'; | import { softFocusPositionComponentState } from '@/object-record/record-table/states/softFocusPositionComponentState'; | ||||||
|  | import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState'; | ||||||
| import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; | import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; | ||||||
| import { useSetSoftFocusPosition } from './internal/useSetSoftFocusPosition'; | import { useSetSoftFocusPosition } from './internal/useSetSoftFocusPosition'; | ||||||
|  |  | ||||||
| @@ -17,6 +17,11 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => { | |||||||
|     recordTableId, |     recordTableId, | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|  |   const tableAllRowIdsState = useRecoilComponentCallbackStateV2( | ||||||
|  |     tableAllRowIdsComponentState, | ||||||
|  |     recordTableId, | ||||||
|  |   ); | ||||||
|  |  | ||||||
|   const moveUp = useRecoilCallback( |   const moveUp = useRecoilCallback( | ||||||
|     ({ snapshot }) => |     ({ snapshot }) => | ||||||
|       () => { |       () => { | ||||||
| @@ -25,50 +30,41 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => { | |||||||
|           softFocusPositionState, |           softFocusPositionState, | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
|         let newRowNumber = softFocusPosition.row - 1; |         let newRowIndex = softFocusPosition.row - 1; | ||||||
|  |  | ||||||
|         if (newRowNumber < 0) { |         if (newRowIndex < 0) { | ||||||
|           newRowNumber = 0; |           newRowIndex = 0; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         setSoftFocusPosition({ |         setSoftFocusPosition({ | ||||||
|           ...softFocusPosition, |           ...softFocusPosition, | ||||||
|           row: newRowNumber, |           row: newRowIndex, | ||||||
|         }); |         }); | ||||||
|       }, |       }, | ||||||
|     [softFocusPositionState, setSoftFocusPosition], |     [softFocusPositionState, setSoftFocusPosition], | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|   const numberOfTableRowsState = useRecoilComponentCallbackStateV2( |  | ||||||
|     numberOfTableRowsComponentState, |  | ||||||
|     recordTableId, |  | ||||||
|   ); |  | ||||||
|  |  | ||||||
|   const moveDown = useRecoilCallback( |   const moveDown = useRecoilCallback( | ||||||
|     ({ snapshot }) => |     ({ snapshot }) => | ||||||
|       () => { |       () => { | ||||||
|  |         const allRowIds = getSnapshotValue(snapshot, tableAllRowIdsState); | ||||||
|         const softFocusPosition = getSnapshotValue( |         const softFocusPosition = getSnapshotValue( | ||||||
|           snapshot, |           snapshot, | ||||||
|           softFocusPositionState, |           softFocusPositionState, | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
|         const numberOfTableRows = getSnapshotValue( |         let newRowIndex = softFocusPosition.row + 1; | ||||||
|           snapshot, |  | ||||||
|           numberOfTableRowsState, |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         let newRowNumber = softFocusPosition.row + 1; |         if (newRowIndex >= allRowIds.length) { | ||||||
|  |           newRowIndex = allRowIds.length - 1; | ||||||
|         if (newRowNumber >= numberOfTableRows) { |  | ||||||
|           newRowNumber = numberOfTableRows - 1; |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         setSoftFocusPosition({ |         setSoftFocusPosition({ | ||||||
|           ...softFocusPosition, |           ...softFocusPosition, | ||||||
|           row: newRowNumber, |           row: newRowIndex, | ||||||
|         }); |         }); | ||||||
|       }, |       }, | ||||||
|     [numberOfTableRowsState, setSoftFocusPosition, softFocusPositionState], |     [tableAllRowIdsState, setSoftFocusPosition, softFocusPositionState], | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|   const numberOfTableColumnsSelector = useRecoilComponentCallbackStateV2( |   const numberOfTableColumnsSelector = useRecoilComponentCallbackStateV2( | ||||||
| @@ -79,6 +75,7 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => { | |||||||
|   const moveRight = useRecoilCallback( |   const moveRight = useRecoilCallback( | ||||||
|     ({ snapshot }) => |     ({ snapshot }) => | ||||||
|       () => { |       () => { | ||||||
|  |         const allRowIds = getSnapshotValue(snapshot, tableAllRowIdsState); | ||||||
|         const softFocusPosition = getSnapshotValue( |         const softFocusPosition = getSnapshotValue( | ||||||
|           snapshot, |           snapshot, | ||||||
|           softFocusPositionState, |           softFocusPositionState, | ||||||
| @@ -89,24 +86,18 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => { | |||||||
|           numberOfTableColumnsSelector, |           numberOfTableColumnsSelector, | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
|         const numberOfTableRows = getSnapshotValue( |         const currentColumnIndex = softFocusPosition.column; | ||||||
|           snapshot, |         const currentRowIndex = softFocusPosition.row; | ||||||
|           numberOfTableRowsState, |  | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         const currentColumnNumber = softFocusPosition.column; |  | ||||||
|         const currentRowNumber = softFocusPosition.row; |  | ||||||
|  |  | ||||||
|         const isLastRowAndLastColumn = |         const isLastRowAndLastColumn = | ||||||
|           currentColumnNumber === numberOfTableColumns - 1 && |           currentColumnIndex === numberOfTableColumns - 1 && | ||||||
|           currentRowNumber === numberOfTableRows - 1; |           currentRowIndex === allRowIds.length - 1; | ||||||
|  |  | ||||||
|         const isLastColumnButNotLastRow = |         const isLastColumnButNotLastRow = | ||||||
|           currentColumnNumber === numberOfTableColumns - 1 && |           currentColumnIndex === numberOfTableColumns - 1 && | ||||||
|           currentRowNumber !== numberOfTableRows - 1; |           currentRowIndex !== allRowIds.length - 1; | ||||||
|  |  | ||||||
|         const isNotLastColumn = |         const isNotLastColumn = currentColumnIndex !== numberOfTableColumns - 1; | ||||||
|           currentColumnNumber !== numberOfTableColumns - 1; |  | ||||||
|  |  | ||||||
|         if (isLastRowAndLastColumn) { |         if (isLastRowAndLastColumn) { | ||||||
|           return; |           return; | ||||||
| @@ -114,20 +105,20 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => { | |||||||
|  |  | ||||||
|         if (isNotLastColumn) { |         if (isNotLastColumn) { | ||||||
|           setSoftFocusPosition({ |           setSoftFocusPosition({ | ||||||
|             row: currentRowNumber, |             row: currentRowIndex, | ||||||
|             column: currentColumnNumber + 1, |             column: currentColumnIndex + 1, | ||||||
|           }); |           }); | ||||||
|         } else if (isLastColumnButNotLastRow) { |         } else if (isLastColumnButNotLastRow) { | ||||||
|           setSoftFocusPosition({ |           setSoftFocusPosition({ | ||||||
|             row: currentRowNumber + 1, |             row: currentRowIndex + 1, | ||||||
|             column: 0, |             column: 0, | ||||||
|           }); |           }); | ||||||
|         } |         } | ||||||
|       }, |       }, | ||||||
|     [ |     [ | ||||||
|  |       tableAllRowIdsState, | ||||||
|       softFocusPositionState, |       softFocusPositionState, | ||||||
|       numberOfTableColumnsSelector, |       numberOfTableColumnsSelector, | ||||||
|       numberOfTableRowsState, |  | ||||||
|       setSoftFocusPosition, |       setSoftFocusPosition, | ||||||
|     ], |     ], | ||||||
|   ); |   ); | ||||||
| @@ -145,16 +136,16 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => { | |||||||
|           numberOfTableColumnsSelector, |           numberOfTableColumnsSelector, | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
|         const currentColumnNumber = softFocusPosition.column; |         const currentColumnIndex = softFocusPosition.column; | ||||||
|         const currentRowNumber = softFocusPosition.row; |         const currentRowIndex = softFocusPosition.row; | ||||||
|  |  | ||||||
|         const isFirstRowAndFirstColumn = |         const isFirstRowAndFirstColumn = | ||||||
|           currentColumnNumber === 0 && currentRowNumber === 0; |           currentColumnIndex === 0 && currentRowIndex === 0; | ||||||
|  |  | ||||||
|         const isFirstColumnButNotFirstRow = |         const isFirstColumnButNotFirstRow = | ||||||
|           currentColumnNumber === 0 && currentRowNumber > 0; |           currentColumnIndex === 0 && currentRowIndex > 0; | ||||||
|  |  | ||||||
|         const isNotFirstColumn = currentColumnNumber > 0; |         const isNotFirstColumn = currentColumnIndex > 0; | ||||||
|  |  | ||||||
|         if (isFirstRowAndFirstColumn) { |         if (isFirstRowAndFirstColumn) { | ||||||
|           return; |           return; | ||||||
| @@ -162,12 +153,12 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => { | |||||||
|  |  | ||||||
|         if (isNotFirstColumn) { |         if (isNotFirstColumn) { | ||||||
|           setSoftFocusPosition({ |           setSoftFocusPosition({ | ||||||
|             row: currentRowNumber, |             row: currentRowIndex, | ||||||
|             column: currentColumnNumber - 1, |             column: currentColumnIndex - 1, | ||||||
|           }); |           }); | ||||||
|         } else if (isFirstColumnButNotFirstRow) { |         } else if (isFirstColumnButNotFirstRow) { | ||||||
|           setSoftFocusPosition({ |           setSoftFocusPosition({ | ||||||
|             row: currentRowNumber - 1, |             row: currentRowIndex - 1, | ||||||
|             column: numberOfTableColumns - 1, |             column: numberOfTableColumns - 1, | ||||||
|           }); |           }); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord'; | |||||||
| import { RecordTableContext } from '@/object-record/record-table/contexts/RecordTableContext'; | import { RecordTableContext } from '@/object-record/record-table/contexts/RecordTableContext'; | ||||||
| import { useComputeNewRowPosition } from '@/object-record/record-table/hooks/useComputeNewRowPosition'; | import { useComputeNewRowPosition } from '@/object-record/record-table/hooks/useComputeNewRowPosition'; | ||||||
| import { isRemoveSortingModalOpenState } from '@/object-record/record-table/states/isRemoveSortingModalOpenState'; | import { isRemoveSortingModalOpenState } from '@/object-record/record-table/states/isRemoveSortingModalOpenState'; | ||||||
| import { tableRowIdsComponentState } from '@/object-record/record-table/states/tableRowIdsComponentState'; | import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState'; | ||||||
| import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; | import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; | ||||||
| import { useGetCurrentView } from '@/views/hooks/useGetCurrentView'; | import { useGetCurrentView } from '@/views/hooks/useGetCurrentView'; | ||||||
| import { isDefined } from '~/utils/isDefined'; | import { isDefined } from '~/utils/isDefined'; | ||||||
| @@ -22,7 +22,9 @@ export const RecordTableBodyDragDropContext = ({ | |||||||
|     objectNameSingular, |     objectNameSingular, | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   const tableRowIds = useRecoilComponentValueV2(tableRowIdsComponentState); |   const tableAllRowIds = useRecoilComponentValueV2( | ||||||
|  |     tableAllRowIdsComponentState, | ||||||
|  |   ); | ||||||
|  |  | ||||||
|   const { currentViewWithCombinedFiltersAndSorts } = |   const { currentViewWithCombinedFiltersAndSorts } = | ||||||
|     useGetCurrentView(recordTableId); |     useGetCurrentView(recordTableId); | ||||||
| @@ -41,7 +43,7 @@ export const RecordTableBodyDragDropContext = ({ | |||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const computeResult = computeNewRowPosition(result, tableRowIds); |     const computeResult = computeNewRowPosition(result, tableAllRowIds); | ||||||
|  |  | ||||||
|     if (!isDefined(computeResult)) { |     if (!isDefined(computeResult)) { | ||||||
|       return; |       return; | ||||||
|   | |||||||
| @@ -1,20 +1,22 @@ | |||||||
| import { RecordTableRows } from '@/object-record/record-table/components/RecordTableRows'; | import { RecordTableNoRecordGroupRows } from '@/object-record/record-table/components/RecordTableNoRecordGroupRows'; | ||||||
| import { RecordTableBodyDragDropContext } from '@/object-record/record-table/record-table-body/components/RecordTableBodyDragDropContext'; | import { RecordTableBodyDragDropContext } from '@/object-record/record-table/record-table-body/components/RecordTableBodyDragDropContext'; | ||||||
| import { RecordTableBodyDroppable } from '@/object-record/record-table/record-table-body/components/RecordTableBodyDroppable'; | import { RecordTableBodyDroppable } from '@/object-record/record-table/record-table-body/components/RecordTableBodyDroppable'; | ||||||
| import { RecordTableBodyLoading } from '@/object-record/record-table/record-table-body/components/RecordTableBodyLoading'; | import { RecordTableBodyLoading } from '@/object-record/record-table/record-table-body/components/RecordTableBodyLoading'; | ||||||
| import { RecordTablePendingRow } from '@/object-record/record-table/record-table-row/components/RecordTablePendingRow'; | import { RecordTablePendingRow } from '@/object-record/record-table/record-table-row/components/RecordTablePendingRow'; | ||||||
| import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState'; | import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState'; | ||||||
| import { tableRowIdsComponentState } from '@/object-record/record-table/states/tableRowIdsComponentState'; | import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState'; | ||||||
| import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; | import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; | ||||||
| 
 | 
 | ||||||
| export const RecordTableBody = () => { | export const RecordTableNoRecordGroupBody = () => { | ||||||
|   const tableRowIds = useRecoilComponentValueV2(tableRowIdsComponentState); |   const tableAllRowIds = useRecoilComponentValueV2( | ||||||
|  |     tableAllRowIdsComponentState, | ||||||
|  |   ); | ||||||
| 
 | 
 | ||||||
|   const isRecordTableInitialLoading = useRecoilComponentValueV2( |   const isRecordTableInitialLoading = useRecoilComponentValueV2( | ||||||
|     isRecordTableInitialLoadingComponentState, |     isRecordTableInitialLoadingComponentState, | ||||||
|   ); |   ); | ||||||
| 
 | 
 | ||||||
|   if (isRecordTableInitialLoading && tableRowIds.length === 0) { |   if (isRecordTableInitialLoading && tableAllRowIds.length === 0) { | ||||||
|     return <RecordTableBodyLoading />; |     return <RecordTableBodyLoading />; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @@ -22,7 +24,7 @@ export const RecordTableBody = () => { | |||||||
|     <RecordTableBodyDragDropContext> |     <RecordTableBodyDragDropContext> | ||||||
|       <RecordTableBodyDroppable> |       <RecordTableBodyDroppable> | ||||||
|         <RecordTablePendingRow /> |         <RecordTablePendingRow /> | ||||||
|         <RecordTableRows /> |         <RecordTableNoRecordGroupRows /> | ||||||
|       </RecordTableBodyDroppable> |       </RecordTableBodyDroppable> | ||||||
|     </RecordTableBodyDragDropContext> |     </RecordTableBodyDragDropContext> | ||||||
|   ); |   ); | ||||||
| @@ -7,20 +7,17 @@ import { useLoadRecordIndexTable } from '@/object-record/record-index/hooks/useL | |||||||
| import { ROW_HEIGHT } from '@/object-record/record-table/constants/RowHeight'; | import { ROW_HEIGHT } from '@/object-record/record-table/constants/RowHeight'; | ||||||
| import { RecordTableContext } from '@/object-record/record-table/contexts/RecordTableContext'; | import { RecordTableContext } from '@/object-record/record-table/contexts/RecordTableContext'; | ||||||
| import { hasRecordTableFetchedAllRecordsComponentStateV2 } from '@/object-record/record-table/states/hasRecordTableFetchedAllRecordsComponentStateV2'; | import { hasRecordTableFetchedAllRecordsComponentStateV2 } from '@/object-record/record-table/states/hasRecordTableFetchedAllRecordsComponentStateV2'; | ||||||
| import { isRecordTableScrolledLeftComponentState } from '@/object-record/record-table/states/isRecordTableScrolledLeftComponentState'; |  | ||||||
| import { tableLastRowVisibleComponentState } from '@/object-record/record-table/states/tableLastRowVisibleComponentState'; | import { tableLastRowVisibleComponentState } from '@/object-record/record-table/states/tableLastRowVisibleComponentState'; | ||||||
| import { isFetchingMoreRecordsFamilyState } from '@/object-record/states/isFetchingMoreRecordsFamilyState'; | import { isFetchingMoreRecordsFamilyState } from '@/object-record/states/isFetchingMoreRecordsFamilyState'; | ||||||
| import { useScrollLeftValue } from '@/ui/utilities/scroll/hooks/useScrollLeftValue'; |  | ||||||
| import { useScrollTopValue } from '@/ui/utilities/scroll/hooks/useScrollTopValue'; |  | ||||||
| import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; | import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; | ||||||
| import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; | import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; | ||||||
| import { isNonEmptyString } from '@sniptt/guards'; | import { isNonEmptyString } from '@sniptt/guards'; | ||||||
| import { useScrollToPosition } from '~/hooks/useScrollToPosition'; | import { useScrollToPosition } from '~/hooks/useScrollToPosition'; | ||||||
| 
 | 
 | ||||||
| export const RecordTableBodyEffect = () => { | export const RecordTableNoRecordGroupBodyEffect = () => { | ||||||
|   const { objectNameSingular } = useContext(RecordTableContext); |   const { objectNameSingular } = useContext(RecordTableContext); | ||||||
| 
 | 
 | ||||||
|   const [hasInitializedScroll, setHasInitiazedScroll] = useState(false); |   const [hasInitializedScroll, setHasInitializedScroll] = useState(false); | ||||||
| 
 | 
 | ||||||
|   const { |   const { | ||||||
|     fetchMoreRecords, |     fetchMoreRecords, | ||||||
| @@ -40,51 +37,11 @@ export const RecordTableBodyEffect = () => { | |||||||
|     tableLastRowVisibleComponentState, |     tableLastRowVisibleComponentState, | ||||||
|   ); |   ); | ||||||
| 
 | 
 | ||||||
|   const scrollTop = useScrollTopValue('recordTableWithWrappers'); |  | ||||||
| 
 |  | ||||||
|   const setHasRecordTableFetchedAllRecordsComponents = |   const setHasRecordTableFetchedAllRecordsComponents = | ||||||
|     useSetRecoilComponentStateV2( |     useSetRecoilComponentStateV2( | ||||||
|       hasRecordTableFetchedAllRecordsComponentStateV2, |       hasRecordTableFetchedAllRecordsComponentStateV2, | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|   // TODO: move this outside because it might cause way too many re-renders for other hooks
 |  | ||||||
|   useEffect(() => { |  | ||||||
|     if (scrollTop > 0) { |  | ||||||
|       document |  | ||||||
|         .getElementById('record-table-header') |  | ||||||
|         ?.classList.add('header-sticky'); |  | ||||||
|     } else { |  | ||||||
|       document |  | ||||||
|         .getElementById('record-table-header') |  | ||||||
|         ?.classList.remove('header-sticky'); |  | ||||||
|     } |  | ||||||
|   }, [scrollTop]); |  | ||||||
| 
 |  | ||||||
|   const scrollLeft = useScrollLeftValue('recordTableWithWrappers'); |  | ||||||
| 
 |  | ||||||
|   const setIsRecordTableScrolledLeft = useSetRecoilComponentStateV2( |  | ||||||
|     isRecordTableScrolledLeftComponentState, |  | ||||||
|   ); |  | ||||||
| 
 |  | ||||||
|   useEffect(() => { |  | ||||||
|     setIsRecordTableScrolledLeft(scrollLeft === 0); |  | ||||||
|     if (scrollLeft > 0) { |  | ||||||
|       document |  | ||||||
|         .getElementById('record-table-body') |  | ||||||
|         ?.classList.add('first-columns-sticky'); |  | ||||||
|       document |  | ||||||
|         .getElementById('record-table-header') |  | ||||||
|         ?.classList.add('first-columns-sticky'); |  | ||||||
|     } else { |  | ||||||
|       document |  | ||||||
|         .getElementById('record-table-body') |  | ||||||
|         ?.classList.remove('first-columns-sticky'); |  | ||||||
|       document |  | ||||||
|         .getElementById('record-table-header') |  | ||||||
|         ?.classList.remove('first-columns-sticky'); |  | ||||||
|     } |  | ||||||
|   }, [scrollLeft, setIsRecordTableScrolledLeft]); |  | ||||||
| 
 |  | ||||||
|   const [lastShowPageRecordId, setLastShowPageRecordId] = useRecoilState( |   const [lastShowPageRecordId, setLastShowPageRecordId] = useRecoilState( | ||||||
|     lastShowPageRecordIdState, |     lastShowPageRecordIdState, | ||||||
|   ); |   ); | ||||||
| @@ -106,7 +63,7 @@ export const RecordTableBodyEffect = () => { | |||||||
| 
 | 
 | ||||||
|         scrollToPosition(positionInPx); |         scrollToPosition(positionInPx); | ||||||
| 
 | 
 | ||||||
|         setHasInitiazedScroll(true); |         setHasInitializedScroll(true); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   }, [ |   }, [ | ||||||
| @@ -120,7 +77,10 @@ export const RecordTableBodyEffect = () => { | |||||||
| 
 | 
 | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     if (!loading) { |     if (!loading) { | ||||||
|       setRecordTableData(records, totalCount); |       setRecordTableData({ | ||||||
|  |         records, | ||||||
|  |         totalCount, | ||||||
|  |       }); | ||||||
|     } |     } | ||||||
|   }, [records, totalCount, setRecordTableData, loading]); |   }, [records, totalCount, setRecordTableData, loading]); | ||||||
| 
 | 
 | ||||||
| @@ -0,0 +1,79 @@ | |||||||
|  | import { useContext, useEffect, useState } from 'react'; | ||||||
|  | import { useRecoilState } from 'recoil'; | ||||||
|  |  | ||||||
|  | import { lastShowPageRecordIdState } from '@/object-record/record-field/states/lastShowPageRecordId'; | ||||||
|  | import { useCurrentRecordGroupId } from '@/object-record/record-group/hooks/useCurrentRecordGroupId'; | ||||||
|  | import { useLoadRecordIndexTable } from '@/object-record/record-index/hooks/useLoadRecordIndexTable'; | ||||||
|  | import { ROW_HEIGHT } from '@/object-record/record-table/constants/RowHeight'; | ||||||
|  | import { RecordTableContext } from '@/object-record/record-table/contexts/RecordTableContext'; | ||||||
|  | import { hasRecordTableFetchedAllRecordsComponentStateV2 } from '@/object-record/record-table/states/hasRecordTableFetchedAllRecordsComponentStateV2'; | ||||||
|  | import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; | ||||||
|  | import { isNonEmptyString } from '@sniptt/guards'; | ||||||
|  | import { useScrollToPosition } from '~/hooks/useScrollToPosition'; | ||||||
|  |  | ||||||
|  | export const RecordTableRecordGroupBodyEffect = () => { | ||||||
|  |   const { objectNameSingular } = useContext(RecordTableContext); | ||||||
|  |  | ||||||
|  |   const recordGroupId = useCurrentRecordGroupId(); | ||||||
|  |  | ||||||
|  |   const [hasInitializedScroll, setHasInitializedScroll] = useState(false); | ||||||
|  |  | ||||||
|  |   const { records, totalCount, setRecordTableData, loading, hasNextPage } = | ||||||
|  |     useLoadRecordIndexTable(objectNameSingular); | ||||||
|  |  | ||||||
|  |   const setHasRecordTableFetchedAllRecordsComponents = | ||||||
|  |     useSetRecoilComponentStateV2( | ||||||
|  |       hasRecordTableFetchedAllRecordsComponentStateV2, | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |   const [lastShowPageRecordId, setLastShowPageRecordId] = useRecoilState( | ||||||
|  |     lastShowPageRecordIdState, | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   const { scrollToPosition } = useScrollToPosition(); | ||||||
|  |  | ||||||
|  |   useEffect(() => { | ||||||
|  |     if (isNonEmptyString(lastShowPageRecordId) && !hasInitializedScroll) { | ||||||
|  |       const isRecordAlreadyFetched = records.some( | ||||||
|  |         (record) => record.id === lastShowPageRecordId, | ||||||
|  |       ); | ||||||
|  |  | ||||||
|  |       if (isRecordAlreadyFetched) { | ||||||
|  |         const recordPosition = records.findIndex( | ||||||
|  |           (record) => record.id === lastShowPageRecordId, | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         const positionInPx = recordPosition * ROW_HEIGHT; | ||||||
|  |  | ||||||
|  |         scrollToPosition(positionInPx); | ||||||
|  |  | ||||||
|  |         setHasInitializedScroll(true); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }, [ | ||||||
|  |     loading, | ||||||
|  |     lastShowPageRecordId, | ||||||
|  |     records, | ||||||
|  |     scrollToPosition, | ||||||
|  |     hasInitializedScroll, | ||||||
|  |     setLastShowPageRecordId, | ||||||
|  |   ]); | ||||||
|  |  | ||||||
|  |   useEffect(() => { | ||||||
|  |     if (!loading) { | ||||||
|  |       setRecordTableData({ | ||||||
|  |         records, | ||||||
|  |         recordGroupId, | ||||||
|  |         totalCount, | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |   }, [records, totalCount, setRecordTableData, loading, recordGroupId]); | ||||||
|  |  | ||||||
|  |   useEffect(() => { | ||||||
|  |     const allRecordsHaveBeenFetched = !hasNextPage; | ||||||
|  |  | ||||||
|  |     setHasRecordTableFetchedAllRecordsComponents(allRecordsHaveBeenFetched); | ||||||
|  |   }, [hasNextPage, setHasRecordTableFetchedAllRecordsComponents]); | ||||||
|  |  | ||||||
|  |   return <></>; | ||||||
|  | }; | ||||||
| @@ -0,0 +1,18 @@ | |||||||
|  | import { RecordGroupContext } from '@/object-record/record-group/states/context/RecordGroupContext'; | ||||||
|  | import { recordGroupDefinitionsComponentState } from '@/object-record/record-group/states/recordGroupDefinitionsComponentState'; | ||||||
|  | import { RecordTableRecordGroupBodyEffect } from '@/object-record/record-table/record-table-body/components/RecordTableRecordGroupBodyEffect'; | ||||||
|  | import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; | ||||||
|  |  | ||||||
|  | export const RecordTableRecordGroupBodyEffects = () => { | ||||||
|  |   const recordGroupDefinitions = useRecoilComponentValueV2( | ||||||
|  |     recordGroupDefinitionsComponentState, | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   return recordGroupDefinitions.map((recordGroupDefinition) => ( | ||||||
|  |     <RecordGroupContext.Provider | ||||||
|  |       value={{ recordGroupId: recordGroupDefinition.id }} | ||||||
|  |     > | ||||||
|  |       <RecordTableRecordGroupBodyEffect /> | ||||||
|  |     </RecordGroupContext.Provider> | ||||||
|  |   )); | ||||||
|  | }; | ||||||
| @@ -0,0 +1,47 @@ | |||||||
|  | import { useRecordGroups } from '@/object-record/record-group/hooks/useRecordGroups'; | ||||||
|  | import { RecordGroupContext } from '@/object-record/record-group/states/context/RecordGroupContext'; | ||||||
|  | import { RecordTableRecordGroupRows } from '@/object-record/record-table/components/RecordTableRecordGroupRows'; | ||||||
|  | import { RecordTableBodyDragDropContext } from '@/object-record/record-table/record-table-body/components/RecordTableBodyDragDropContext'; | ||||||
|  | import { RecordTableBodyDroppable } from '@/object-record/record-table/record-table-body/components/RecordTableBodyDroppable'; | ||||||
|  | import { RecordTableBodyLoading } from '@/object-record/record-table/record-table-body/components/RecordTableBodyLoading'; | ||||||
|  | import { RecordTablePendingRow } from '@/object-record/record-table/record-table-row/components/RecordTablePendingRow'; | ||||||
|  | import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState'; | ||||||
|  | import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState'; | ||||||
|  | import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; | ||||||
|  |  | ||||||
|  | type RecordTableRecordGroupsBodyProps = { | ||||||
|  |   objectNameSingular: string; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export const RecordTableRecordGroupsBody = ({ | ||||||
|  |   objectNameSingular, | ||||||
|  | }: RecordTableRecordGroupsBodyProps) => { | ||||||
|  |   const tableAllRowIds = useRecoilComponentValueV2( | ||||||
|  |     tableAllRowIdsComponentState, | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   const isRecordTableInitialLoading = useRecoilComponentValueV2( | ||||||
|  |     isRecordTableInitialLoadingComponentState, | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   const { visibleRecordGroups } = useRecordGroups({ objectNameSingular }); | ||||||
|  |  | ||||||
|  |   if (isRecordTableInitialLoading && tableAllRowIds.length === 0) { | ||||||
|  |     return <RecordTableBodyLoading />; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <RecordTableBodyDragDropContext> | ||||||
|  |       <RecordTableBodyDroppable> | ||||||
|  |         <RecordTablePendingRow /> | ||||||
|  |         {visibleRecordGroups.map((recordGroupDefinition) => ( | ||||||
|  |           <RecordGroupContext.Provider | ||||||
|  |             value={{ recordGroupId: recordGroupDefinition.id }} | ||||||
|  |           > | ||||||
|  |             <RecordTableRecordGroupRows /> | ||||||
|  |           </RecordGroupContext.Provider> | ||||||
|  |         ))} | ||||||
|  |       </RecordTableBodyDroppable> | ||||||
|  |     </RecordTableBodyDragDropContext> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
| @@ -78,9 +78,9 @@ const StyledTableHead = styled.thead` | |||||||
| `; | `; | ||||||
|  |  | ||||||
| export const RecordTableHeader = ({ | export const RecordTableHeader = ({ | ||||||
|   objectMetadataNameSingular, |   objectNameSingular, | ||||||
| }: { | }: { | ||||||
|   objectMetadataNameSingular: string; |   objectNameSingular: string; | ||||||
| }) => { | }) => { | ||||||
|   const visibleTableColumns = useRecoilComponentValueV2( |   const visibleTableColumns = useRecoilComponentValueV2( | ||||||
|     visibleTableColumnsComponentSelector, |     visibleTableColumnsComponentSelector, | ||||||
| @@ -95,7 +95,7 @@ export const RecordTableHeader = ({ | |||||||
|           <RecordTableHeaderCell |           <RecordTableHeaderCell | ||||||
|             key={column.fieldMetadataId} |             key={column.fieldMetadataId} | ||||||
|             column={column} |             column={column} | ||||||
|             objectMetadataNameSingular={objectMetadataNameSingular} |             objectNameSingular={objectNameSingular} | ||||||
|           /> |           /> | ||||||
|         ))} |         ))} | ||||||
|         <RecordTableHeaderLastColumn /> |         <RecordTableHeaderLastColumn /> | ||||||
|   | |||||||
| @@ -95,13 +95,13 @@ const StyledHeaderIcon = styled.div` | |||||||
|  |  | ||||||
| export const RecordTableHeaderCell = ({ | export const RecordTableHeaderCell = ({ | ||||||
|   column, |   column, | ||||||
|   objectMetadataNameSingular, |   objectNameSingular, | ||||||
| }: { | }: { | ||||||
|   column: ColumnDefinition<FieldMetadata>; |   column: ColumnDefinition<FieldMetadata>; | ||||||
|   objectMetadataNameSingular: string; |   objectNameSingular: string; | ||||||
| }) => { | }) => { | ||||||
|   const { objectMetadataItem } = useObjectMetadataItem({ |   const { objectMetadataItem } = useObjectMetadataItem({ | ||||||
|     objectNameSingular: objectMetadataNameSingular, |     objectNameSingular, | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   const resizeFieldOffsetState = useRecoilComponentCallbackStateV2( |   const resizeFieldOffsetState = useRecoilComponentCallbackStateV2( | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| import { selectedRowIdsComponentSelector } from '@/object-record/record-table/states/selectors/selectedRowIdsComponentSelector'; | import { selectedRowIdsComponentSelector } from '@/object-record/record-table/states/selectors/selectedRowIdsComponentSelector'; | ||||||
| import { tableRowIdsComponentState } from '@/object-record/record-table/states/tableRowIdsComponentState'; |  | ||||||
|  |  | ||||||
| import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; | import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; | ||||||
|  | import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState'; | ||||||
| import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2'; | import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2'; | ||||||
| import { AllRowsSelectedStatus } from '../../types/AllRowSelectedStatus'; | import { AllRowsSelectedStatus } from '../../types/AllRowSelectedStatus'; | ||||||
|  |  | ||||||
| @@ -13,11 +13,15 @@ export const allRowsSelectedStatusComponentSelector = | |||||||
|       ({ instanceId }) => |       ({ instanceId }) => | ||||||
|       ({ get }) => { |       ({ get }) => { | ||||||
|         const tableRowIds = get( |         const tableRowIds = get( | ||||||
|           tableRowIdsComponentState.atomFamily({ instanceId }), |           tableAllRowIdsComponentState.atomFamily({ | ||||||
|  |             instanceId, | ||||||
|  |           }), | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
|         const selectedRowIds = get( |         const selectedRowIds = get( | ||||||
|           selectedRowIdsComponentSelector.selectorFamily({ instanceId }), |           selectedRowIdsComponentSelector.selectorFamily({ | ||||||
|  |             instanceId, | ||||||
|  |           }), | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
|         const numberOfSelectedRows = selectedRowIds.length; |         const numberOfSelectedRows = selectedRowIds.length; | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; | import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; | ||||||
| import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; | import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; | ||||||
| import { tableRowIdsComponentState } from '@/object-record/record-table/states/tableRowIdsComponentState'; | import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState'; | ||||||
| import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2'; | import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2'; | ||||||
|  |  | ||||||
| export const selectedRowIdsComponentSelector = createComponentSelectorV2< | export const selectedRowIdsComponentSelector = createComponentSelectorV2< | ||||||
| @@ -11,7 +11,11 @@ export const selectedRowIdsComponentSelector = createComponentSelectorV2< | |||||||
|   get: |   get: | ||||||
|     ({ instanceId }) => |     ({ instanceId }) => | ||||||
|     ({ get }) => { |     ({ get }) => { | ||||||
|       const rowIds = get(tableRowIdsComponentState.atomFamily({ instanceId })); |       const rowIds = get( | ||||||
|  |         tableAllRowIdsComponentState.atomFamily({ | ||||||
|  |           instanceId, | ||||||
|  |         }), | ||||||
|  |       ); | ||||||
|  |  | ||||||
|       return rowIds.filter( |       return rowIds.filter( | ||||||
|         (rowId) => |         (rowId) => | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; | import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; | ||||||
| import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; | import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; | ||||||
| import { tableRowIdsComponentState } from '@/object-record/record-table/states/tableRowIdsComponentState'; | import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState'; | ||||||
| import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2'; | import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2'; | ||||||
|  |  | ||||||
| export const unselectedRowIdsComponentSelector = createComponentSelectorV2< | export const unselectedRowIdsComponentSelector = createComponentSelectorV2< | ||||||
| @@ -11,7 +11,11 @@ export const unselectedRowIdsComponentSelector = createComponentSelectorV2< | |||||||
|   get: |   get: | ||||||
|     ({ instanceId }) => |     ({ instanceId }) => | ||||||
|     ({ get }) => { |     ({ get }) => { | ||||||
|       const rowIds = get(tableRowIdsComponentState.atomFamily({ instanceId })); |       const rowIds = get( | ||||||
|  |         tableAllRowIdsComponentState.atomFamily({ | ||||||
|  |           instanceId, | ||||||
|  |         }), | ||||||
|  |       ); | ||||||
|  |  | ||||||
|       return rowIds.filter( |       return rowIds.filter( | ||||||
|         (rowId) => |         (rowId) => | ||||||
|   | |||||||
| @@ -1,8 +1,8 @@ | |||||||
| import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; | import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; | ||||||
| import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; | import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; | ||||||
| 
 | 
 | ||||||
| export const tableRowIdsComponentState = createComponentStateV2<string[]>({ | export const tableAllRowIdsComponentState = createComponentStateV2<string[]>({ | ||||||
|   key: 'tableRowIdsComponentState', |   key: 'tableAllRowIdsComponentState', | ||||||
|   defaultValue: [], |   defaultValue: [], | ||||||
|   componentInstanceContext: RecordTableComponentInstanceContext, |   componentInstanceContext: RecordTableComponentInstanceContext, | ||||||
| }); | }); | ||||||
| @@ -0,0 +1,11 @@ | |||||||
|  | import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition'; | ||||||
|  | import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; | ||||||
|  | import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; | ||||||
|  |  | ||||||
|  | export const tableRecordGroupIdsComponentState = createComponentStateV2< | ||||||
|  |   RecordGroupDefinition['id'][] | ||||||
|  | >({ | ||||||
|  |   key: 'tableRecordGroupIdsComponentState', | ||||||
|  |   defaultValue: [], | ||||||
|  |   componentInstanceContext: RecordTableComponentInstanceContext, | ||||||
|  | }); | ||||||
| @@ -0,0 +1,10 @@ | |||||||
|  | import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition'; | ||||||
|  | import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; | ||||||
|  | import { createComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentFamilyStateV2'; | ||||||
|  |  | ||||||
|  | export const tableRowIdsByGroupComponentFamilyState = | ||||||
|  |   createComponentFamilyStateV2<string[], RecordGroupDefinition['id']>({ | ||||||
|  |     key: 'tableRowIdsByGroupComponentFamilyState', | ||||||
|  |     defaultValue: [], | ||||||
|  |     componentInstanceContext: RecordTableComponentInstanceContext, | ||||||
|  |   }); | ||||||
| @@ -1,3 +1,5 @@ | |||||||
|  | /* eslint-disable no-redeclare */ | ||||||
|  | /* eslint-disable prefer-arrow/prefer-arrow-functions */ | ||||||
| import { selectorFamily, SerializableParam } from 'recoil'; | import { selectorFamily, SerializableParam } from 'recoil'; | ||||||
|  |  | ||||||
| import { ComponentFamilyReadOnlySelectorV2 } from '@/ui/utilities/state/component-state/types/ComponentFamilyReadOnlySelectorV2'; | import { ComponentFamilyReadOnlySelectorV2 } from '@/ui/utilities/state/component-state/types/ComponentFamilyReadOnlySelectorV2'; | ||||||
| @@ -9,7 +11,26 @@ import { SelectorGetter } from '@/ui/utilities/state/types/SelectorGetter'; | |||||||
| import { SelectorSetter } from '@/ui/utilities/state/types/SelectorSetter'; | import { SelectorSetter } from '@/ui/utilities/state/types/SelectorSetter'; | ||||||
| import { isDefined } from 'twenty-ui'; | import { isDefined } from 'twenty-ui'; | ||||||
|  |  | ||||||
| export const createComponentFamilySelectorV2 = < | export function createComponentFamilySelectorV2< | ||||||
|  |   ValueType, | ||||||
|  |   FamilyKey extends SerializableParam, | ||||||
|  | >(options: { | ||||||
|  |   key: string; | ||||||
|  |   get: SelectorGetter<ValueType, ComponentFamilyStateKeyV2<FamilyKey>>; | ||||||
|  |   componentInstanceContext: ComponentInstanceStateContext<any> | null; | ||||||
|  | }): ComponentFamilySelectorV2<ValueType, FamilyKey>; | ||||||
|  |  | ||||||
|  | export function createComponentFamilySelectorV2< | ||||||
|  |   ValueType, | ||||||
|  |   FamilyKey extends SerializableParam, | ||||||
|  | >(options: { | ||||||
|  |   key: string; | ||||||
|  |   get: SelectorGetter<ValueType, ComponentFamilyStateKeyV2<FamilyKey>>; | ||||||
|  |   set: SelectorSetter<ValueType, ComponentFamilyStateKeyV2<FamilyKey>>; | ||||||
|  |   componentInstanceContext: ComponentInstanceStateContext<any> | null; | ||||||
|  | }): ComponentFamilyReadOnlySelectorV2<ValueType, FamilyKey>; | ||||||
|  |  | ||||||
|  | export function createComponentFamilySelectorV2< | ||||||
|   ValueType, |   ValueType, | ||||||
|   FamilyKey extends SerializableParam, |   FamilyKey extends SerializableParam, | ||||||
| >({ | >({ | ||||||
| @@ -24,7 +45,7 @@ export const createComponentFamilySelectorV2 = < | |||||||
|   componentInstanceContext: ComponentInstanceStateContext<any> | null; |   componentInstanceContext: ComponentInstanceStateContext<any> | null; | ||||||
| }): | }): | ||||||
|   | ComponentFamilySelectorV2<ValueType, FamilyKey> |   | ComponentFamilySelectorV2<ValueType, FamilyKey> | ||||||
|   | ComponentFamilyReadOnlySelectorV2<ValueType, FamilyKey> => { |   | ComponentFamilyReadOnlySelectorV2<ValueType, FamilyKey> { | ||||||
|   if (isDefined(componentInstanceContext)) { |   if (isDefined(componentInstanceContext)) { | ||||||
|     globalComponentInstanceContextMap.set(key, componentInstanceContext); |     globalComponentInstanceContextMap.set(key, componentInstanceContext); | ||||||
|   } |   } | ||||||
| @@ -55,4 +76,4 @@ export const createComponentFamilySelectorV2 = < | |||||||
|       }), |       }), | ||||||
|     } satisfies ComponentFamilyReadOnlySelectorV2<ValueType, FamilyKey>; |     } satisfies ComponentFamilyReadOnlySelectorV2<ValueType, FamilyKey>; | ||||||
|   } |   } | ||||||
| }; | } | ||||||
|   | |||||||
| @@ -6,11 +6,18 @@ import { AtomEffect, atomFamily, SerializableParam } from 'recoil'; | |||||||
|  |  | ||||||
| import { isDefined } from 'twenty-ui'; | import { isDefined } from 'twenty-ui'; | ||||||
|  |  | ||||||
| type CreateComponentFamilyStateArgs<ValueType> = { | type CreateComponentFamilyStateArgs< | ||||||
|  |   ValueType, | ||||||
|  |   FamilyKey extends SerializableParam, | ||||||
|  | > = { | ||||||
|   key: string; |   key: string; | ||||||
|   defaultValue: ValueType; |   defaultValue: ValueType; | ||||||
|   componentInstanceContext: ComponentInstanceStateContext<any> | null; |   componentInstanceContext: ComponentInstanceStateContext<any> | null; | ||||||
|   effects?: AtomEffect<ValueType>[]; |   effects?: | ||||||
|  |     | AtomEffect<ValueType>[] | ||||||
|  |     | (( | ||||||
|  |         param: ComponentFamilyStateKeyV2<FamilyKey>, | ||||||
|  |       ) => ReadonlyArray<AtomEffect<ValueType>>); | ||||||
| }; | }; | ||||||
|  |  | ||||||
| export const createComponentFamilyStateV2 = < | export const createComponentFamilyStateV2 = < | ||||||
| @@ -21,10 +28,10 @@ export const createComponentFamilyStateV2 = < | |||||||
|   effects, |   effects, | ||||||
|   defaultValue, |   defaultValue, | ||||||
|   componentInstanceContext, |   componentInstanceContext, | ||||||
| }: CreateComponentFamilyStateArgs<ValueType>): ComponentFamilyStateV2< | }: CreateComponentFamilyStateArgs< | ||||||
|   ValueType, |   ValueType, | ||||||
|   FamilyKey |   FamilyKey | ||||||
| > => { | >): ComponentFamilyStateV2<ValueType, FamilyKey> => { | ||||||
|   if (isDefined(componentInstanceContext)) { |   if (isDefined(componentInstanceContext)) { | ||||||
|     globalComponentInstanceContextMap.set(key, componentInstanceContext); |     globalComponentInstanceContextMap.set(key, componentInstanceContext); | ||||||
|   } |   } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Jérémy M
					Jérémy M