Skip to content

Commit

Permalink
feat(react-components): Added support to list all classic 3D resource…
Browse files Browse the repository at this point in the history
…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
pramod-cog authored Feb 5, 2025
1 parent b51a2ed commit c1352ca
Show file tree
Hide file tree
Showing 15 changed files with 665 additions and 2 deletions.
2 changes: 1 addition & 1 deletion react-components/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cognite/reveal-react-components",
"version": "0.74.1",
"version": "0.75.0",
"exports": {
".": {
"import": "./dist/index.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"ADD_SLICE_Y": "Add vertical slice along X-axis. Select a point.",
"ADD_SLICE_Z": "Add horizontal slice. Select a point.",
"ALL": "All",
"ALL_3D_RESOURCES": "All 3D resources",
"AREA": "Area",
"ASSET": "Asset",
"BOX": "Box",
Expand Down Expand Up @@ -91,6 +92,8 @@
"MOUSE_TOUCH_NAVIGATION_SUBTITLE": "Navigate and select",
"MOUSE_ZOOM": "Zoom / scroll",
"NAME": "Name",
"NO_MODELS_FOUND": "No models found",
"NO_RESULTS": "No results",
"NONE": "None",
"PAN": "Pan",
"PARABOLOID": "Paraboloid",
Expand All @@ -111,7 +114,9 @@
"POLYLINE": "Polyline",
"RADIUS": "Radius",
"REDO": "Redo",
"RESOURCES_3D": "3D resources",
"RESET_CAMERA_TO_HOME": "Reset camera to home",
"REVISIONS": "Revisions",
"RGB": "RGB",
"RULESET_NO_SELECTION": "No Rule Set Selected",
"RULESET_SELECT_HEADER": "Select color overlay",
Expand All @@ -138,6 +143,7 @@
"TOUCH_NAVIGATION_TITLE": "Touch",
"TOUCH_SELECT": "Tap to select",
"TOUCH_ZOOM": "Zoom",
"TRY_AGAIN": "Try again",
"UNDO": "Undo",
"VERTICAL_AREA": "Vertical area",
"VERTICAL_LENGTH": "Vertical length",
Expand Down
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' });
}
};
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;
`;
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>
);
};
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
});
};
Loading

0 comments on commit c1352ca

Please sign in to comment.