-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(react-components): Added support to list all classic 3D resource…
…s in project (#4984) * added model list components, currently only supports classic data * refactor some code as utill functions * exposed callback to pass selected modelId and revisionId * exposed useRemoveNonReferencedModels * exposed selectedModel to maintain the previous selected vlaue * added new storybook example for Reveal3DResourcesList * add translation string and updated the components * refracted code * bump version to 0.75.0 * lint fix try * addressed code review
- Loading branch information
1 parent
b51a2ed
commit c1352ca
Showing
15 changed files
with
665 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
84 changes: 84 additions & 0 deletions
84
react-components/src/components/Reveal3DResourcesList/ModelList.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
/*! | ||
* Copyright 2025 Cognite AS | ||
*/ | ||
import { SelectPanel } from '@cognite/cogs-lab'; | ||
import { EmptyState, FindIllustration, Flex, Tooltip } from '@cognite/cogs.js'; | ||
import { type ReactElement } from 'react'; | ||
import { type ModelWithRevisionInfo } from '../../hooks/network/types'; | ||
import { useTranslation } from '../i18n/I18n'; | ||
import { type TranslationInput } from '../../architecture'; | ||
|
||
const MAX_LABEL_LENGTH = 25; | ||
|
||
type ModelListProps = { | ||
modelType: string | undefined; | ||
models: ModelWithRevisionInfo[] | undefined; | ||
selectedRevisions: Record<number, number | undefined>; | ||
handleModelClick: (modelData: ModelWithRevisionInfo) => void; | ||
}; | ||
|
||
export const ModelList = ({ | ||
modelType, | ||
models, | ||
selectedRevisions, | ||
handleModelClick | ||
}: ModelListProps): ReactElement => { | ||
const { t } = useTranslation(); | ||
|
||
const title = getTitle(modelType, t); | ||
|
||
if (models === undefined || models.length === 0) { | ||
return ( | ||
<SelectPanel.Section title={t({ key: 'NO_MODELS_FOUND' })}> | ||
<Flex | ||
justifyContent="center" | ||
style={{ | ||
maxWidth: '320px', | ||
padding: '20px 8px' | ||
}}> | ||
<EmptyState title={t({ key: 'NO_MODELS_FOUND' })} illustration={<FindIllustration />} /> | ||
</Flex> | ||
</SelectPanel.Section> | ||
); | ||
} | ||
|
||
return ( | ||
<SelectPanel.Section title={title}> | ||
{models.map((modelData) => { | ||
const isLargeLabel = modelData.displayName.length > MAX_LABEL_LENGTH; | ||
const modelLabel = isLargeLabel | ||
? `${modelData.displayName.slice(0, MAX_LABEL_LENGTH)}...` | ||
: modelData.displayName; | ||
|
||
return ( | ||
<Tooltip key={modelData.id} disabled={!isLargeLabel} content={modelData.displayName}> | ||
<SelectPanel.Item | ||
variant="toggle" | ||
label={modelLabel} | ||
checked={selectedRevisions[modelData.id] !== undefined} | ||
onClick={() => { | ||
handleModelClick(modelData); | ||
}} | ||
/> | ||
</Tooltip> | ||
); | ||
})} | ||
</SelectPanel.Section> | ||
); | ||
}; | ||
|
||
const getTitle = ( | ||
modelType: string | undefined, | ||
t: (translationInput: TranslationInput) => string | ||
): string => { | ||
switch (modelType) { | ||
case 'CAD': | ||
return t({ key: 'CAD_MODELS' }); | ||
case 'Point cloud': | ||
return t({ key: 'POINT_CLOUDS' }); | ||
case '360 image': | ||
return t({ key: 'IMAGES_360' }); | ||
default: | ||
return t({ key: 'ALL_3D_RESOURCES' }); | ||
} | ||
}; |
156 changes: 156 additions & 0 deletions
156
react-components/src/components/Reveal3DResourcesList/Reveal3DResourcesList.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
/*! | ||
* Copyright 2025 Cognite AS | ||
*/ | ||
import { SelectPanel } from '@cognite/cogs-lab'; | ||
import { LoaderIcon, EmptyState, FindIllustration, Flex } from '@cognite/cogs.js'; | ||
import { type ReactElement, useState } from 'react'; | ||
import { useAllResourcesList } from './hooks/useAllResourcesList'; | ||
import { type CogniteClient } from '@cognite/sdk'; | ||
import styled from 'styled-components'; | ||
import { type ModelWithRevisionInfo } from '../../hooks/network/types'; | ||
import { ModelList } from './ModelList'; | ||
import { RevisionList } from './RevisionList'; | ||
import { handleModelClick, handleRevisionSelect, onSearchInputChange } from './utils'; | ||
import { useTranslation } from '../i18n/I18n'; | ||
|
||
type Reveal3DResourcesListProps = { | ||
sdk: CogniteClient; | ||
modelType?: string; | ||
onRevisionSelect?: (modelId: number, revisionId: number) => void; | ||
selectedModel: ModelWithRevisionInfo | undefined; | ||
setSelectedModel: (model: ModelWithRevisionInfo | undefined) => void; | ||
selectedRevisions: Record<number, number | undefined>; | ||
setSelectedRevisions: (revisions: Record<number, number | undefined>) => void; | ||
}; | ||
|
||
// Currently supports only Cad, point cloud from classic source type | ||
export function Reveal3DResourcesList({ | ||
sdk, | ||
modelType, | ||
onRevisionSelect, | ||
selectedModel, | ||
setSelectedModel, | ||
selectedRevisions, | ||
setSelectedRevisions | ||
}: Reveal3DResourcesListProps): ReactElement { | ||
const { t } = useTranslation(); | ||
const { data: modelsWithRevision, isLoading: isItemsLoading } = useAllResourcesList(sdk); | ||
const [revisions, setRevisions] = useState<Array<{ id: number; createdTime: Date }>>([]); | ||
const [currentPage, setCurrentPage] = useState(1); | ||
const [isRevisionsLoading, setIsRevisionsLoading] = useState(false); | ||
const [searchQuery, setSearchQuery] = useState(''); | ||
|
||
const filteredModels = | ||
modelType !== undefined | ||
? modelsWithRevision?.filter((model) => model.resourceType === modelType) | ||
: modelsWithRevision; | ||
|
||
const filteredAndSearchedModels = filteredModels?.filter((model) => | ||
model.displayName.toLowerCase().includes(searchQuery.toLowerCase()) | ||
); | ||
|
||
const handleRevisionSelectWithCallback = (revisionId: number): void => { | ||
if (selectedModel !== undefined) { | ||
handleRevisionSelect({ | ||
revisionId, | ||
selectedModel, | ||
setSelectedRevisions | ||
}); | ||
if (onRevisionSelect !== undefined) { | ||
onRevisionSelect(selectedModel.id, revisionId); | ||
} | ||
} | ||
}; | ||
|
||
return ( | ||
<Reveal3DResourcesListContainer> | ||
<SelectPanel visible> | ||
<SelectPanel.Header title={t({ key: 'RESOURCES_3D' })}> | ||
{currentPage !== 1 && ( | ||
<SelectPanel.BackButton | ||
onClick={(_) => { | ||
setCurrentPage((page) => (page - 1 > 0 ? page - 1 : page)); | ||
}} | ||
/> | ||
)} | ||
{currentPage !== 2 && ( | ||
<SelectPanel.SearchBar | ||
onChange={(event) => { | ||
onSearchInputChange(event, setSearchQuery); | ||
}} | ||
/> | ||
)} | ||
</SelectPanel.Header> | ||
|
||
<SelectPanel.Body length="small"> | ||
{isItemsLoading ? ( | ||
<LoaderIcon /> | ||
) : ( | ||
<> | ||
{currentPage === 1 && | ||
(filteredAndSearchedModels !== undefined && filteredAndSearchedModels.length > 0 ? ( | ||
<ModelList | ||
modelType={modelType} | ||
models={filteredAndSearchedModels} | ||
selectedRevisions={selectedRevisions} | ||
handleModelClick={async (modelData) => { | ||
await handleModelClick({ | ||
modelData, | ||
setSelectedModel, | ||
setCurrentPage, | ||
setIsRevisionsLoading, | ||
setRevisions, | ||
sdk | ||
}); | ||
}} | ||
/> | ||
) : ( | ||
<EmptyStateResources /> | ||
))} | ||
{currentPage === 2 && selectedModel !== undefined && ( | ||
<RevisionList | ||
revisions={revisions} | ||
isRevisionsLoading={isRevisionsLoading} | ||
selectedModel={selectedModel} | ||
selectedRevisions={selectedRevisions} | ||
handleRevisionSelect={handleRevisionSelectWithCallback} | ||
/> | ||
)} | ||
</> | ||
)} | ||
</SelectPanel.Body> | ||
<SelectPanel.Footer /> | ||
</SelectPanel> | ||
</Reveal3DResourcesListContainer> | ||
); | ||
} | ||
|
||
function EmptyStateResources(): ReactElement { | ||
const { t } = useTranslation(); | ||
return ( | ||
<Flex | ||
justifyContent="center" | ||
style={{ | ||
maxWidth: '320px', | ||
padding: '20px 8px' | ||
}}> | ||
<EmptyState | ||
illustration={<FindIllustration />} | ||
size="small" | ||
title={t({ key: 'TRY_AGAIN' })} | ||
description={t({ key: 'NO_RESULTS' })} | ||
/> | ||
</Flex> | ||
); | ||
} | ||
|
||
const Reveal3DResourcesListContainer = styled.div` | ||
position: absolute; | ||
top: 50px; | ||
left: 10px; | ||
display: flex; | ||
flex-direction: column; | ||
height: 400px; | ||
width: 300px; | ||
z-index: 10000; | ||
`; |
45 changes: 45 additions & 0 deletions
45
react-components/src/components/Reveal3DResourcesList/RevisionList.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
/*! | ||
* Copyright 2025 Cognite AS | ||
*/ | ||
import { SelectPanel } from '@cognite/cogs-lab'; | ||
import { LoaderIcon } from '@cognite/cogs.js'; | ||
import { type ReactElement } from 'react'; | ||
import { type ModelWithRevisionInfo } from '../../hooks/network/types'; | ||
import { useTranslation } from '../i18n/I18n'; | ||
|
||
export const RevisionList = ({ | ||
revisions, | ||
isRevisionsLoading, | ||
selectedModel, | ||
selectedRevisions, | ||
handleRevisionSelect | ||
}: { | ||
revisions: Array<{ id: number; createdTime: Date }>; | ||
isRevisionsLoading: boolean; | ||
selectedModel: ModelWithRevisionInfo; | ||
selectedRevisions: Record<number, number | undefined>; | ||
handleRevisionSelect: (revisionId: number) => void; | ||
}): ReactElement => { | ||
const { t } = useTranslation(); | ||
return ( | ||
<SelectPanel.Section title={t({ key: 'REVISIONS' })}> | ||
{isRevisionsLoading ? ( | ||
<LoaderIcon /> | ||
) : ( | ||
revisions | ||
.sort((a, b) => a.createdTime.getTime() - b.createdTime.getTime()) | ||
.map((revision, index) => ( | ||
<SelectPanel.Item | ||
key={revision.id} | ||
label={`Revision ${index + 1}`} | ||
variant="toggle" | ||
checked={selectedRevisions[selectedModel.id] === revision.id} | ||
onClick={() => { | ||
handleRevisionSelect(revision.id); | ||
}} | ||
/> | ||
)) | ||
)} | ||
</SelectPanel.Section> | ||
); | ||
}; |
21 changes: 21 additions & 0 deletions
21
react-components/src/components/Reveal3DResourcesList/hooks/useAllResourcesList.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/*! | ||
* Copyright 2025 Cognite AS | ||
*/ | ||
|
||
import { useQuery, type UseQueryResult } from '@tanstack/react-query'; | ||
import { getModels } from '../../../hooks/network/getModels'; | ||
import { type CogniteClient } from '@cognite/sdk'; | ||
import { type ModelWithRevisionInfo } from '../../../hooks/network/types'; | ||
import { queryKeys } from '../../../utilities/queryKeys'; | ||
|
||
export const useAllResourcesList = ( | ||
sdk?: CogniteClient | ||
): UseQueryResult<ModelWithRevisionInfo[]> => { | ||
return useQuery({ | ||
queryKey: queryKeys.all3DResources(), | ||
queryFn: async () => { | ||
return await getModels(sdk); | ||
}, | ||
staleTime: Infinity | ||
}); | ||
}; |
Oops, something went wrong.