Skip to content

Commit

Permalink
feat: Support multiple infocards
Browse files Browse the repository at this point in the history
  • Loading branch information
haakonflatval-cognite committed Jul 31, 2023
1 parent 5c7c129 commit b8a82a0
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 15 deletions.
30 changes: 18 additions & 12 deletions react-components/src/components/InfoCard/InfoCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
* Copyright 2023 Cognite AS
*/

import React, { JSX, useState, useEffect, useRef } from 'react';
import { Vector3 } from 'three';
import React, { type JSX, useState, useEffect, useRef } from 'react';
import { type Vector3 } from 'three';

import { useReveal } from '../RevealContainer/RevealContext';

Expand All @@ -18,35 +18,41 @@ export type InfoCardElementMapping = {
export type InfoCardProps = {
position: Vector3;
children: React.ReactNode;
uniqueKey: string;
};

export const InfoCard = ({ position, children }: InfoCardProps): JSX.Element => {
export const InfoCard = ({ position, children, uniqueKey }: InfoCardProps): JSX.Element => {
const viewer = useReveal();

const mods = useAuxillaryDivContext();
const [htmlTool, _setHtmlTool] = useState<HtmlOverlayTool>(new HtmlOverlayTool(viewer));

const auxContext = useAuxillaryDivContext();

const htmlRef = useRef<HTMLDivElement>(null);
const element = (
<div id={'infoCardContent'} key={'info'} ref={htmlRef} style={{ position: 'absolute' }}>
<div className="infocard" key={uniqueKey} ref={htmlRef} style={{ position: 'absolute' }}>
{children}
</div>
);

const [htmlTool, _setHtmlTool] = useState<HtmlOverlayTool>(new HtmlOverlayTool(viewer));

useEffect(() => {
mods.addElement(element);
return () => mods.removeElement(element);
auxContext.addElement(element);
return () => {
auxContext.removeElement(element);
};
}, []);

useEffect(() => {
if (htmlRef.current === null) {
return;
}

htmlTool.clear();
htmlTool.add(htmlRef.current, position);
}, [htmlTool, children, htmlRef.current]);
const elementRef = htmlRef.current;

htmlTool.add(elementRef, position);

return () => htmlTool.remove(elementRef);
}, [auxContext, children, htmlRef.current]);

return <></>;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*!
* Copyright 2023 Cognite AS
*/

import { HtmlOverlayTool } from '@cognite/reveal/tools';
import React, {
createContext,
useContext,
useState,
useCallback,
useEffect,
type JSX
} from 'react';

export const AuxillaryDivProvider = ({ children }: { children: React.ReactNode }): JSX.Element => {
const [elements, setElements] = useState<JSX.Element[]>([]);

// Maintain a local copy of the elements, needed for properly supporting multiple
// `addElement` calls between rerenders
let cachedElements = elements;

const addElement = useCallback(
(element: JSX.Element) => {
const newElementList = [...cachedElements, element];

setElements(newElementList);
cachedElements = newElementList;
},
[elements, setElements]
);

const removeElement = useCallback(
(element: JSX.Element) => {
const newElementList = cachedElements.filter((e) => e !== element);

setElements(newElementList);
cachedElements = newElementList;
},
[elements, setElements]
);

return (
<>
<AuxillaryDivContext.Provider value={{ addElement, removeElement }}>
<div>{elements}</div>
{children}
</AuxillaryDivContext.Provider>
</>
);
};

type AuxillaryContextData = {
addElement: (element: JSX.Element) => void;
removeElement: (element: JSX.Element) => void;
};

const AuxillaryDivContext = createContext<AuxillaryContextData | null>(null);

export const useAuxillaryDivContext = (): AuxillaryContextData => {
const auxContext = useContext(AuxillaryDivContext);
if (auxContext === null) {
throw new Error('useAuxillaryDivContext must be used inside AuxillaryDivContext');
}

return auxContext;
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { type Color } from 'three';
import { ModelsLoadingStateContext } from '../Reveal3DResources/ModelsLoadingContext';
import { SDKProvider } from './SDKProvider';
import { QueryClientProvider, QueryClient } from '@tanstack/react-query';
import { AuxillaryDivContext, AuxillaryDivProvider } from './AuxillaryDivProvider';
import { AuxillaryDivProvider } from './AuxillaryDivProvider';

type RevealContainerProps = {
color?: Color;
Expand Down
18 changes: 16 additions & 2 deletions react-components/stories/Reveal3DResources.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ export const Main: Story = {
},
render: ({ resources, styling, fdmAssetMappingConfig }) => {
const position = new Vector3(50, 30, 50);
const position2 = new Vector3(0, 0, 0);

return (
<RevealContainer
Expand All @@ -143,19 +144,32 @@ export const Main: Story = {
styling={styling}
fdmAssetMappingConfig={fdmAssetMappingConfig}
/>
<InfoCard position={position}>
<InfoCard position={position} uniqueKey="key2">
<p
style={{
backgroundColor: 'turquoise',
borderColor: 'black',
borderWidth: '10px',
borderStyle: 'solid',
maxWidth: '300px',
transform: 'translate(0px, calc(-100% - 50px))'
transform: 'translate(-50%, calc(-100% - 50px))'
}}>
This label is stuck at position {position.toArray().join(',')}
</p>
</InfoCard>
<InfoCard position={position2} uniqueKey="key1">
<p
style={{
backgroundColor: 'red',
borderColor: 'black',
borderWidth: '10px',
borderStyle: 'solid',
maxWidth: '300px',
transform: 'translate(0px, 0px)'
}}>
This label is stuck at position {position2.toArray().join(',')}
</p>
</InfoCard>
<CameraController
initialFitCamera={{
to: 'allModels'
Expand Down

0 comments on commit b8a82a0

Please sign in to comment.