Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 3D Resources layering react component #3495

Merged
merged 32 commits into from
Jul 24, 2023
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
792cd83
Initial layering component for Cad model
pramodcog Jul 13, 2023
0616cb8
Added sliders and adjusted UI for submenu
pramodcog Jul 18, 2023
c088b5c
Fixed lint error
pramodcog Jul 18, 2023
4678f7a
fixed 360 image visibility issue
pramodcog Jul 19, 2023
dfb89a4
Improved UI when one of the 3D resources is not available
pramodcog Jul 19, 2023
4131913
removed opacity from 3D resources layers option
pramodcog Jul 19, 2023
1265edd
removed additional elements which was not needed
pramodcog Jul 19, 2023
0b8d378
Merge branch 'master' into pramodcog/UX-958
pramodcog Jul 19, 2023
730f9c2
Merge refs/heads/master into pramodcog/UX-958
cognite-bulldozer[bot] Jul 20, 2023
9be5903
Merge refs/heads/master into pramodcog/UX-958
cognite-bulldozer[bot] Jul 20, 2023
3bc6a7a
Merge refs/heads/master into pramodcog/UX-958
cognite-bulldozer[bot] Jul 20, 2023
63740d5
Merge refs/heads/master into pramodcog/UX-958
cognite-bulldozer[bot] Jul 20, 2023
7ea401c
Merge refs/heads/master into pramodcog/UX-958
cognite-bulldozer[bot] Jul 20, 2023
b7e4e83
Merge refs/heads/master into pramodcog/UX-958
cognite-bulldozer[bot] Jul 20, 2023
31d2ec4
Merge refs/heads/master into pramodcog/UX-958
cognite-bulldozer[bot] Jul 20, 2023
162de70
added indeterminate option for checkbox and exposed LayerButton
pramodcog Jul 20, 2023
ed0e47b
Merge branch 'pramodcog/UX-958' of https://github.com/cognitedata/rev…
pramodcog Jul 20, 2023
8803ace
Merge refs/heads/master into pramodcog/UX-958
cognite-bulldozer[bot] Jul 20, 2023
84253c9
Update react-components/src/components/RevealToolbar/LayersContainer/…
pramodcog Jul 20, 2023
9ca0c2f
Update react-components/src/components/RevealToolbar/LayersContainer/…
pramodcog Jul 20, 2023
b4b0b6d
Update react-components/src/components/RevealToolbar/LayersContainer/…
pramodcog Jul 20, 2023
3e04bec
Added model name & number of models in each 3D resources UI
pramodcog Jul 21, 2023
8eec958
updated component key to lodash/uniqueId
pramodcog Jul 21, 2023
892d250
Merge branch 'master' into pramodcog/UX-958
pramodcog Jul 21, 2023
6f7e5ab
Merge refs/heads/master into pramodcog/UX-958
cognite-bulldozer[bot] Jul 24, 2023
c0c608c
Update react-components/src/components/RevealToolbar/LayersContainer/…
pramodcog Jul 24, 2023
3adb0b5
Update react-components/src/components/RevealToolbar/LayersContainer/…
pramodcog Jul 24, 2023
a79d324
Update react-components/src/components/RevealToolbar/LayersContainer/…
pramodcog Jul 24, 2023
a953a95
Update react-components/src/components/RevealToolbar/LayersContainer/…
pramodcog Jul 24, 2023
3a34e4c
Update react-components/src/hooks/use3DModelName.tsx
pramodcog Jul 24, 2023
fcc1c80
addresed review comments
pramodcog Jul 24, 2023
759ee6f
Updated pointCloud & 360 image count number styling
pramodcog Jul 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions react-components/src/components/RevealToolbar/LayersButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*!
* Copyright 2023 Cognite AS
*/

import { type ReactElement, useState } from 'react';
import { Button, Dropdown } from '@cognite/cogs.js';
import { LayersContainer } from './LayersContainer/LayersContainer';

export const LayersButton = (): ReactElement => {
const [layersEnabled, setLayersEnabled] = useState(false);
const showLayers = (): void => {
setLayersEnabled(!layersEnabled);
};

return (
<Dropdown
appendTo={document.body}
content={<LayersContainer />}
visible={layersEnabled}
placement="auto">
<Button type="ghost" icon="Layers" aria-label="3D Resource layers" onClick={showLayers} />
</Dropdown>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*!
* Copyright 2023 Cognite AS
*/

import React, { type ReactElement, useState } from 'react';
import { useReveal } from '../../RevealContainer/RevealContext';
import { type CogniteCadModel } from '@cognite/reveal';
import { Checkbox, Flex, Menu } from '@cognite/cogs.js';
import { StyledChipCount, StyledLabel, StyledSubMenu } from './elements';
import { use3DModelName } from '../../../hooks/use3DModelName';
import uniqueId from 'lodash/uniqueId';

export const CadModelLayersContainer = (): ReactElement => {
const viewer = useReveal();
const cadModels = viewer.models.filter((model) => model.type === 'cad');
const cadModelIds = cadModels.map((model) => model.modelId);

const modelName = use3DModelName(cadModelIds);

const [selectedCadModels, setSelectedCadModels] = useState<
Array<{ model: CogniteCadModel; isToggled: boolean; name: string }>
>(
cadModels.map((model, index) => ({
model: model as CogniteCadModel,
isToggled: (model as CogniteCadModel).visible,
name: modelName?.data?.[index] ?? 'No model name'
}))
);

const [allCadModelVisible, setAllCadModelVisible] = useState(true);
const [indeterminate, setIndeterminate] = useState<boolean>(false);

const count = selectedCadModels.length.toString();

const handleCadModelVisibility = (model: CogniteCadModel): void => {
selectedCadModels.map((data) => {
if (data.model === model) {
return {
..data,
isToggled: !data.isToggled
}
return data;
});
model.visible = !model.visible;
viewer.requestRedraw();
setSelectedCadModels([...selectedCadModels]);
setIndeterminate(selectedCadModels.some((data) => !data.isToggled));
setAllCadModelVisible(!selectedCadModels.every((data) => !data.isToggled));
};

const handleAllCadModelsVisibility = (visible: boolean): void => {
selectedCadModels.forEach((data) => {
data.isToggled = visible;
data.model.visible = visible;
});
viewer.requestRedraw();
setAllCadModelVisible(visible);
setIndeterminate(false);
setSelectedCadModels([...selectedCadModels]);
};

const cadModelContent = (): React.JSX.Element => {
return (
<StyledSubMenu>
{selectedCadModels.map((data) => (
<Menu.Item
key={uniqueId()}
hasCheckbox
checkboxProps={{
checked: data.isToggled,
onChange: (e: { stopPropagation: () => void }) => {
e.stopPropagation();
handleCadModelVisibility(data.model);
}
}}>
{data.name}
</Menu.Item>
))}
</StyledSubMenu>
);
};

return (
<Menu.Submenu content={cadModelContent()} title="CAD models">
<Flex direction="row" justifyContent="space-between" gap={4}>
<Checkbox
checked={allCadModelVisible}
indeterminate={indeterminate}
onChange={(e, c) => {
e.stopPropagation();
handleAllCadModelsVisibility(c as boolean);
}}
/>
<StyledLabel> CAD models </StyledLabel>
pramodcog marked this conversation as resolved.
Show resolved Hide resolved
<StyledChipCount label={count} hideTooltip type='neutral' />
</Flex>
</Menu.Submenu>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*!
* Copyright 2023 Cognite AS
*/

import React, { type ReactElement, useState } from 'react';
import { useReveal } from '../../RevealContainer/RevealContext';
import { Checkbox, Flex, Menu } from '@cognite/cogs.js';
import { StyledChipCount, StyledLabel, StyledSubMenu } from './elements';
import { type Image360Collection } from '@cognite/reveal';
import uniqueId from 'lodash/uniqueId';

export const Image360CollectionLayerContainer = (): ReactElement => {
const viewer = useReveal();
const image360Collection = viewer.get360ImageCollections();

const [selectedImage360Collection, setSelectedImage360Collection] = useState<
Array<{ image360: Image360Collection; isToggled: boolean }>
>(
image360Collection.map((image360Collection) => ({
image360: image360Collection,
isToggled: true
}))
);

const [all360ImagesVisible, setAll360ImagesVisible] = useState(true);
const [indeterminate, setIndeterminate] = useState<boolean>(false);

const count = image360Collection.length.toString();

const handle360ImagesVisibility = (image360: Image360Collection): void => {
selectedImage360Collection.map((data) => {
if (data.image360 === image360) {
data.isToggled = !data.isToggled;
image360.setIconsVisibility(data.isToggled);
}
pramodcog marked this conversation as resolved.
Show resolved Hide resolved
return data;
});
viewer.requestRedraw();
setSelectedImage360Collection([...selectedImage360Collection]);
setIndeterminate(selectedImage360Collection.some((data) => !data.isToggled));
setAll360ImagesVisible(!selectedImage360Collection.every((data) => !data.isToggled));
};

const handleAll360ImagesVisibility = (visible: boolean): void => {
[...selectedImage360Collection].forEach((data) => {
data.isToggled = visible;
data.image360.setIconsVisibility(data.isToggled);
});
viewer.requestRedraw();
setAll360ImagesVisible(visible);
setIndeterminate(false);
setSelectedImage360Collection([...selectedImage360Collection]);
};

const image360Content = (): React.JSX.Element => {
return (
<StyledSubMenu>
{selectedImage360Collection.map((data) => (
<Menu.Item
key={uniqueId()}
hasCheckbox
checkboxProps={{
checked: data.isToggled,
onChange: (e: { stopPropagation: () => void }) => {
e.stopPropagation();
handle360ImagesVisibility(data.image360);
}
}}>
{data.image360.label}
</Menu.Item>
))}
</StyledSubMenu>
);
};

return (
<Menu.Submenu content={image360Content()} title="360 images">
<Flex direction="row" justifyContent="space-between">
<Checkbox
checked={all360ImagesVisible}
indeterminate={indeterminate}
onChange={(e, c) => {
e.stopPropagation();
handleAll360ImagesVisibility(c as boolean);
}}
/>
<StyledLabel> 360 images </StyledLabel>
<StyledChipCount label={count} hideTooltip />
</Flex>
</Menu.Submenu>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*!
* Copyright 2023 Cognite AS
*/

import { type ReactElement } from 'react';

import { Menu } from '@cognite/cogs.js';
import styled from 'styled-components';

import { CadModelLayersContainer } from './CadModelLayersContainer';
import { PointCloudLayersContainer } from './PointCloudLayersContainer';
import { Image360CollectionLayerContainer } from './Image360LayersContainer';

export const LayersContainer = (): ReactElement => {
return (
<Container>
<StyledMenu>
<CadModelLayersContainer />
<PointCloudLayersContainer />
<Image360CollectionLayerContainer />
pramodcog marked this conversation as resolved.
Show resolved Hide resolved
</StyledMenu>
</Container>
);
};

const Container = styled.div`
position: absolute;
left: calc(0%);
top: calc(-70%);
pramodcog marked this conversation as resolved.
Show resolved Hide resolved
`;

const StyledMenu = styled(Menu)`
padding: 6px;
width: 214px;
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*!
* Copyright 2023 Cognite AS
*/

import React, { type ReactElement, useState } from 'react';

import { useReveal } from '../../RevealContainer/RevealContext';
import { Checkbox, Flex, Menu } from '@cognite/cogs.js';
import { StyledChipCount, StyledLabel, StyledSubMenu } from './elements';
import { type CognitePointCloudModel } from '@cognite/reveal';
import { use3DModelName } from '../../../hooks/use3DModelName';
import uniqueId from 'lodash/uniqueId';

export const PointCloudLayersContainer = (): ReactElement => {
const viewer = useReveal();
const pointCloudModels = viewer.models.filter((model) => model.type === 'pointcloud');
const pointCloudModelIds = pointCloudModels.map((model) => model.modelId);

const modelName = use3DModelName(pointCloudModelIds);

const [selectedPointCloudModels, setSelectedPointCloudModels] = useState<
Array<{ model: CognitePointCloudModel; isToggled: boolean; name: string }>
>(
pointCloudModels.map((model, index) => ({
model: model as CognitePointCloudModel,
isToggled: (model as CognitePointCloudModel).getDefaultPointCloudAppearance().visible ?? true,
name: modelName?.data?.[index] ?? 'No model name'
}))
);

const [allPointCloudModelVisible, setAllPointCloudModelVisible] = useState(true);
const [indeterminate, setIndeterminate] = useState<boolean>(false);

const count = pointCloudModels.length.toString();

const handlePointCloudVisibility = (model: CognitePointCloudModel): void => {
selectedPointCloudModels.map((data) => {
if (data.model === model) {
data.isToggled = !data.isToggled;
model.setDefaultPointCloudAppearance({ visible: data.isToggled });
}
return data;
});
viewer.requestRedraw();
setSelectedPointCloudModels([...selectedPointCloudModels]);
setIndeterminate(selectedPointCloudModels.some((data) => !data.isToggled));
setAllPointCloudModelVisible(!selectedPointCloudModels.every((data) => !data.isToggled));
};

const handleAllPointCloudModelsVisibility = (visible: boolean): void => {
selectedPointCloudModels.forEach((data) => {
data.isToggled = visible;
data.model.setDefaultPointCloudAppearance({ visible });
});
viewer.requestRedraw();
setAllPointCloudModelVisible(visible);
setSelectedPointCloudModels([...selectedPointCloudModels]);
};

const pointCloudModelContent = (): React.JSX.Element => {
return (
<StyledSubMenu>
{selectedPointCloudModels.map((data) => (
<Menu.Item
key={uniqueId()}
hasCheckbox
checkboxProps={{
checked: data.isToggled,
onChange: (e: { stopPropagation: () => void }) => {
e.stopPropagation();
handlePointCloudVisibility(data.model);
}
}}>
{data.name}
</Menu.Item>
))}
</StyledSubMenu>
);
};

return (
<Menu.Submenu content={pointCloudModelContent()} title="Point clouds">
<Flex direction="row" justifyContent="space-between">
<Checkbox
checked={allPointCloudModelVisible}
indeterminate={indeterminate}
onChange={(e, c) => {
e.stopPropagation();
handleAllPointCloudModelsVisibility(c as boolean);
}}
/>
<StyledLabel> Point clouds </StyledLabel>
<StyledChipCount label={count} hideTooltip />
</Flex>
</Menu.Submenu>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*!
* Copyright 2023 Cognite AS
*/

import { Checkbox, Chip, Menu } from '@cognite/cogs.js';
import styled from 'styled-components';

export const StyledCheckbox = styled(Checkbox)`
margin-left: 10px;
`;

export const StyledMenu = styled(Menu)`
padding: 6px;
`;

export const StyledSubMenu = styled(Menu)`
box-shadow: none;
padding: 8px;
`;
pramodcog marked this conversation as resolved.
Show resolved Hide resolved

export const StyledChipCount = styled(Chip)`
&& {
border-radius: 2px;
width: fit-content;
height: 20px;
max-height: 20px;
min-height: 20px;
min-width: 20px;
padding: 4px;
}
`;

export const StyledLabel = styled.div`
/* Font */
font-family: 'Inter';
font-style: normal;
font-weight: 500;
font-size: 16px;
line-height: 20px;
color: #000000;
`;
Loading
Loading