Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { IntlProvider } from 'react-intl';
import { getMessagesForLocale } from './localization/localization';
import { LayoutContainer } from './components/LayoutContainer';
import { SettingsProvider } from './components/SettingsContext';
import { TemplatesProvider } from './components/TemplatesContext';

const App = () => {
const [locale, setLocale] = useState<Locale>('en');
Expand All @@ -25,7 +26,9 @@ const App = () => {
return (
<IntlProvider locale={locale} messages={messages}>
<SettingsProvider>
<LayoutContainer id='homeContainer' />
<TemplatesProvider>
<LayoutContainer id='homeContainer' />
</TemplatesProvider>
</SettingsProvider>
</IntlProvider>
);
Expand Down
2 changes: 2 additions & 0 deletions src/assets/home.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,5 @@ export const graphs = [
Thumbnail: img
}
];

export const templates = graphs;
71 changes: 53 additions & 18 deletions src/components/Common/Tooltip.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,81 @@
import { useState, useRef, useEffect, CSSProperties } from 'react';
import Portal from './Portal'; // Import your Portal component

export const Tooltip = ({ children, content, verticalOffset = 12 }: Tooltip) => {
export const Tooltip = ({ children, content, verticalOffset = 12, position: positionProp = 'below' }: Tooltip) => {
const [show, setShow] = useState<boolean>(false);
const [position, setPosition] = useState<CSSProperties>({});
const tooltipRef = useRef(null);
const contentRef = useRef(null); // Ref for the tooltip content
const tooltipRef = useRef<HTMLSpanElement | null>(null);
const contentRef = useRef<HTMLDivElement | null>(null);

useEffect(() => {
if (tooltipRef.current && contentRef.current && show) {
if (!tooltipRef.current || !contentRef.current || !show) {
return;
}

const updatePosition = () => {
if (!tooltipRef.current || !contentRef.current) {
return;
}

const targetRect = tooltipRef.current.getBoundingClientRect();
const tooltipRect = contentRef.current.getBoundingClientRect();

let left = targetRect.left + window.scrollX + (targetRect.width / 2); // Center align
const top = targetRect.bottom + window.scrollY + verticalOffset;

// Check if the tooltip is going off the right side of the screen
if (left + tooltipRect.width > window.innerWidth) {
left = window.innerWidth - tooltipRect.width / 2 - 10; // Adjust to keep it on screen
let left: number;
let top: number;

if (positionProp === 'right') {
left = targetRect.right + window.scrollX + verticalOffset;
top = targetRect.top + window.scrollY + (targetRect.height / 2) - (tooltipRect.height / 2);

if (left + tooltipRect.width > window.innerWidth + window.scrollX) {
left = targetRect.left + window.scrollX - tooltipRect.width - verticalOffset;
}
} else {
left = targetRect.left + window.scrollX + (targetRect.width / 2);
top = targetRect.bottom + window.scrollY + verticalOffset;

if (left + tooltipRect.width / 2 > window.innerWidth + window.scrollX) {
left = window.innerWidth + window.scrollX - tooltipRect.width / 2 - 10;
}

if (left - tooltipRect.width / 2 < window.scrollX) {
left = window.scrollX + tooltipRect.width / 2 + 10;
}
}

if (top < window.scrollY) {
top = window.scrollY + 10;
}
// Check if the tooltip is going off the left side of the screen
if (left - tooltipRect.width / 2 < 0) {
left += 10; // Adjust to keep it on screen

if (top + tooltipRect.height > window.innerHeight + window.scrollY) {
top = window.innerHeight + window.scrollY - tooltipRect.height - 10;
}

setPosition({
top: top,
left: left,
position: 'absolute'
position: 'absolute',
transform: positionProp === 'right' ? 'none' : 'translateX(-50%)'
});
};

if (window.requestAnimationFrame) {
const animationFrame = window.requestAnimationFrame(updatePosition);
return () => window.cancelAnimationFrame?.(animationFrame);
}
}, [show, content, verticalOffset]); // Added 'content' to dependencies array

updatePosition();
}, [show, content, verticalOffset, positionProp]);

return (
<span className="tooltip-wrapper"
onMouseEnter={() => setShow(true)}
<span className="tooltip-wrapper"
onMouseEnter={() => setShow(true)}
onMouseLeave={() => setShow(false)}
ref={tooltipRef}>
{children}
{show && (
<Portal>
<div className={`tooltip-box ${show ? 'show' : ''}`} ref={contentRef} style={position}>
<div className={`tooltip-box ${show ? 'show' : ''} ${positionProp === 'right' ? 'arrow-left' : ''}`} ref={contentRef} style={position}>
<div className="tooltip-arrow" />
<div style={{ whiteSpace: 'pre-line' }}>{content}</div>
</div>
Expand Down
13 changes: 6 additions & 7 deletions src/components/Recent/GraphTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ export const GraphTable = ({ columns, data, onRowClick }: GraphTable) => {
}),
[]
);
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
resetResizing
} = useTable(
Expand All @@ -27,7 +27,7 @@ export const GraphTable = ({ columns, data, onRowClick }: GraphTable) => {
defaultColumn
},
useFlexLayout,
useResizeColumns
useResizeColumns
);

const handleRowClick = (row:Row) => {
Expand All @@ -42,7 +42,6 @@ export const GraphTable = ({ columns, data, onRowClick }: GraphTable) => {
<div className={styles['table-container']}>
<table {...getTableProps()}>
<thead>
{console.log(headerGroups)}
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column: any, columnIndex: number) => (
Expand Down
122 changes: 105 additions & 17 deletions src/components/Recent/PageRecent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,31 @@ import { CustomNameCellRenderer } from './CustomNameCellRenderer';
import { CustomLocationCellRenderer } from './CustomLocationCellRenderer';
import { CustomAuthorCellRenderer } from './CustomAuthorCellRenderer';
import { GraphTable } from './GraphTable';
import { GridViewIcon, ListViewIcon } from '../Common/CustomIcons';
import { GridViewIcon, ListViewIcon, QuestionMarkIcon } from '../Common/CustomIcons';
import { openFile, saveHomePageSettings } from '../../functions/utility';
import { FormattedMessage } from 'react-intl';
import { Tooltip } from '../Common/Tooltip';
import { useSettings } from '../SettingsContext';
import { useTemplates } from '../TemplatesContext';

export const RecentPage = ({ setIsDisabled, recentPageViewMode }: RecentPage) => {
export const RecentPage = ({ setIsDisabled, recentPageViewMode }: RecentPage) => {
const { settings, updateSettings } = useSettings();
const [viewMode, setViewMode] = useState(recentPageViewMode);
const templates = useTemplates();
const [viewMode, setViewMode] = useState(recentPageViewMode);
const [templatesViewMode, setTemplatesViewMode] = useState(settings?.templatesPageViewMode || 'grid');
const [initialized, setInitialized] = useState<boolean>(false);
const [templatesInitialized, setTemplatesInitialized] = useState<boolean>(false);

// Set a placeholder for the graphs which will be used differently during dev and prod
// Set a placeholder for the graphs which will be used differently during dev and prod
let initialGraphs = [];

// If we are under development, we will load the graphs from the local asset folder
if (process.env.NODE_ENV === 'development') {
// eslint-disable-next-line @typescript-eslint/no-require-imports
initialGraphs = require('../../assets/home').graphs;
}

const [graphs, setGraphs] = useState(initialGraphs);
const [graphs, setGraphs] = useState(initialGraphs);

// A method exposed to the backend used to set the graph data coming from Dynamo
const receiveGraphDataFromDotNet = (jsonData) => {
Expand All @@ -50,31 +54,49 @@ export const RecentPage = ({ setIsDisabled, recentPageViewMode }: RecentPage) =>
delete window.receiveGraphDataFromDotNet;
}
};
}, []);
}, []);

useEffect(() => {
// Set the viewMode based on the HomePage preferences
setViewMode(recentPageViewMode);
}, [recentPageViewMode]);
}, [recentPageViewMode]);

useEffect(() => {
// Set the templatesViewMode based on the HomePage preferences
if (settings?.templatesPageViewMode) {
setTemplatesViewMode(settings.templatesPageViewMode);
}
}, [settings?.templatesPageViewMode]);

useEffect(() => {
if (initialized || recentPageViewMode !== viewMode) {
setInitialized(true);
updateSettings({ recentPageViewMode: viewMode });

// Send settings to Dynamo to save
saveHomePageSettings({ ...settings, recentPageViewMode: viewMode });
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [viewMode]);

useEffect(() => {
if (templatesInitialized || (settings?.templatesPageViewMode && settings.templatesPageViewMode !== templatesViewMode)) {
setTemplatesInitialized(true);
updateSettings({ templatesPageViewMode: templatesViewMode });

// Send settings to Dynamo to save
saveHomePageSettings({ ...settings, templatesPageViewMode: templatesViewMode });
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [templatesViewMode]);

// This variable defins the table structure displaying the graphs
const columns: Column[] = React.useMemo(() => [
{
Header: 'Title',
accessor: 'Caption',
width: 300,
resizable: true,
resizable: true,
Cell: CustomNameCellRenderer,
},
{
Expand All @@ -98,15 +120,33 @@ export const RecentPage = ({ setIsDisabled, recentPageViewMode }: RecentPage) =>

// Handles mouse click over each row
const handleRowClick = (row: Row) => {
// freezes the UI
setIsDisabled(true);

const contextData = row.original.ContextData;
// freezes the UI
setIsDisabled(true);

const contextData = row.original.ContextData;
openFile(contextData);
};

// Handles mouse click over each template row
const handleTemplateRowClick = (row: Row) => {
// freezes the UI
setIsDisabled(true);

const contextData = row.original.ContextData;
openFile(contextData);
};

// Map templates to match Graph structure for table view (templates use 'date' instead of 'DateModified')
const templatesForTable = templates.map(template => ({
...template,
DateModified: template.date || template.DateModified || '',
Author: template.Author || '',
Description: template.Description || ''
}));

return(
<div data-testid="page-recent">
{/* Recent Section */}
<div className='drop-shadow-2xl'>
<p className='title-paragraph'><FormattedMessage id="title.text.recent"/></p>
</div>
Expand All @@ -133,7 +173,7 @@ export const RecentPage = ({ setIsDisabled, recentPageViewMode }: RecentPage) =>
<div style={{ marginRight: '20px', paddingBottom: '35px' }}>
{viewMode === 'list' && (
<GraphTable columns={columns} data={graphs} onRowClick={handleRowClick}/>
)}
)}
{viewMode === 'grid' && (
<div className="main-graph-grid" id="graphContainer" data-testid="graph-grid">
{graphs.map(graph => (
Expand All @@ -142,6 +182,54 @@ export const RecentPage = ({ setIsDisabled, recentPageViewMode }: RecentPage) =>
</div>
)}
</div>

{/* Templates Section */}
<div className='drop-shadow-2xl' style={{ display: 'flex', alignItems: 'center' }}>
<p className='title-paragraph' style={{ display: 'inline-block', width: 'fit-content' }}>
<FormattedMessage id="title.text.templates"/>
</p>
<Tooltip content={<FormattedMessage id="tooltip.text.templates" />} position="right">
<QuestionMarkIcon />
</Tooltip>
</div>
<div style={{ display: 'flex', alignItems: 'center', marginBottom:'10px' }}>
<button
className={`viewmode-button ${templatesViewMode === 'grid' ? 'active' : ''}`}
onClick={() => setTemplatesViewMode('grid')}
disabled={templatesViewMode === 'grid'}
data-testid="templates-view-toggle-grid">
<Tooltip content={<FormattedMessage id="tooltip.text.grid.view.button" />}>
<GridViewIcon/>
</Tooltip>
</button>
<button
className={`viewmode-button ${templatesViewMode === 'list' ? 'active' : ''}`}
onClick={() => setTemplatesViewMode('list')}
disabled={templatesViewMode === 'list'}
data-testid="templates-view-toggle-list">
<Tooltip content={<FormattedMessage id="tooltip.text.list.view.button" />}>
<ListViewIcon/>
</Tooltip>
</button>
</div>
<div style={{ marginRight: '20px', paddingBottom: '35px' }}>
{templatesViewMode === 'list' && (
<GraphTable columns={columns} data={templatesForTable} onRowClick={handleTemplateRowClick}/>
)}
{templatesViewMode === 'grid' && (
<div className="main-graph-grid" id="templatesContainer">
{templates.map(template => (
<GraphGridItem
key={template.id}
{...template}
DateModified={template.date || template.DateModified || ''}
Description={template.Description || ''}
setIsDisabled={setIsDisabled}
/>
))}
</div>
)}
</div>
</div>
);
};
};
Loading
Loading