Skip to content
Closed
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
157 changes: 132 additions & 25 deletions website/src/components/ProjectGridTemplate/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,50 @@ import projects_package_url from '@site/src/data/projects-package-url.json';
import projects_inspectors from '@site/src/data/projects-inspectors.json';
import projects_libraries from '@site/src/data/projects-libraries.json';

// Field descriptions for tooltips
const fieldDescriptions = {
repository_url:
'The URL of the source code repository (e.g., GitHub) where the project is hosted.',
package_download_url:
'Links to download the packaged software (e.g., PyPI, npm, or other package registries).',
documentation_url:
'Link to the official documentation for this project.',
service_url:
'URL to access a hosted/cloud version of this application or service.',
languages:
'The primary programming language(s) used to develop this project.',
platform:
'The operating system or platform this software runs on.',
software_license:
'The open source license under which the software code is released.',
data_license:
'The license under which any data produced or distributed by this project is released.',
lead_maintainer:
'The primary maintainer(s) responsible for this project.',
};

// InfoTooltip component for displaying field explanations
function InfoTooltip({ text }) {
const [isVisible, setIsVisible] = useState(false);

return (
<span
className={styles.infoIconWrapper}
onMouseEnter={() => setIsVisible(true)}
onMouseLeave={() => setIsVisible(false)}
onClick={(e) => {
e.stopPropagation();
setIsVisible(!isVisible);
}}
>
<span className={styles.infoIcon}>ⓘ</span>
{isVisible && (
<span className={styles.infoTooltip}>{text}</span>
)}
</span>
);
}

export default function ProjectGrids() {
const [selectedProject, setSelectedProject] = useState(null);
const [isModalOpen, setIsModalOpen] = useState(false);
Expand Down Expand Up @@ -216,9 +260,16 @@ export default function ProjectGrids() {

<div className={styles.column}>
<div className={styles.modalLinks01}>
<span style={{ fontWeight: 'bold' }}>
Repository URL:{' '}
</span>
<div className={styles.fieldLabel}>
<span style={{ fontWeight: 'bold' }}>
Repository URL:
</span>
<InfoTooltip
text={
fieldDescriptions.repository_url
}
/>
</div>
{selectedProject.repository_url &&
selectedProject.repository_url !==
'n/a' &&
Expand All @@ -241,7 +292,14 @@ export default function ProjectGrids() {

{/* Catch empty and n/a and just display n/a as text. */}
<div className={styles.modalLinks01}>
<strong>Package Download URL:</strong>
<div className={styles.fieldLabel}>
<strong>Package Download URL:</strong>
<InfoTooltip
text={
fieldDescriptions.package_download_url
}
/>
</div>
{selectedProject.package_download_url?.filter(
(url) =>
url &&
Expand Down Expand Up @@ -278,9 +336,16 @@ export default function ProjectGrids() {
</div>

<div className={styles.modalLinks01}>
<span style={{ fontWeight: 'bold' }}>
Documentation URL:{' '}
</span>
<div className={styles.fieldLabel}>
<span style={{ fontWeight: 'bold' }}>
Documentation URL:
</span>
<InfoTooltip
text={
fieldDescriptions.documentation_url
}
/>
</div>
{selectedProject.documentation_url &&
selectedProject.documentation_url !==
'n/a' &&
Expand All @@ -304,9 +369,16 @@ export default function ProjectGrids() {
</div>

<div className={styles.modalLinks01}>
<span style={{ fontWeight: 'bold' }}>
Service URL:{' '}
</span>
<div className={styles.fieldLabel}>
<span style={{ fontWeight: 'bold' }}>
Service URL:
</span>
<InfoTooltip
text={
fieldDescriptions.service_url
}
/>
</div>
{selectedProject.service_url &&
selectedProject.service_url !== 'n/a' &&
selectedProject.service_url !== '#' ? (
Expand All @@ -326,45 +398,80 @@ export default function ProjectGrids() {
</div>

<div className={styles.note_field}>
<span style={{ fontWeight: 'bold' }}>
Language(s):{' '}
</span>
<div className={styles.fieldLabel}>
<span style={{ fontWeight: 'bold' }}>
Language(s):
</span>
<InfoTooltip
text={
fieldDescriptions.languages
}
/>
</div>
<div className={styles.modalText}>
{selectedProject.languages}
</div>
</div>

<div className={styles.note_field}>
<span style={{ fontWeight: 'bold' }}>
Platform:{' '}
</span>
<div className={styles.fieldLabel}>
<span style={{ fontWeight: 'bold' }}>
Platform:
</span>
<InfoTooltip
text={
fieldDescriptions.platform
}
/>
</div>
<div className={styles.modalText}>
{selectedProject.platform}
</div>
</div>

<div className={styles.note_field}>
<span style={{ fontWeight: 'bold' }}>
Software License:{' '}
</span>
<div className={styles.fieldLabel}>
<span style={{ fontWeight: 'bold' }}>
Software License:
</span>
<InfoTooltip
text={
fieldDescriptions.software_license
}
/>
</div>
<div className={styles.modalText}>
{selectedProject.software_license}
</div>
</div>

<div className={styles.note_field}>
<span style={{ fontWeight: 'bold' }}>
Data License:{' '}
</span>
<div className={styles.fieldLabel}>
<span style={{ fontWeight: 'bold' }}>
Data License:
</span>
<InfoTooltip
text={
fieldDescriptions.data_license
}
/>
</div>
<div className={styles.modalText}>
{selectedProject.data_license}
</div>
</div>

<div className={styles.modalLinks01}>
<span style={{ fontWeight: 'bold' }}>
Lead Maintainer(s):
</span>
<div className={styles.fieldLabel}>
<span style={{ fontWeight: 'bold' }}>
Lead Maintainer(s):
</span>
<InfoTooltip
text={
fieldDescriptions.lead_maintainer
}
/>
</div>

{leadMaintainers.length > 0 ? (
<ul
Expand Down
101 changes: 99 additions & 2 deletions website/src/components/ProjectGridTemplate/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -618,9 +618,7 @@
margin-bottom: 20px;
}
[data-theme='dark'] .sectionTitle h2 {
color: #666699;
color: #a0a0a0;
color: #gridSection;
}

.sectionIntro {
Expand All @@ -635,3 +633,102 @@
[data-theme='dark'] .gridSection {
border-bottom: solid 1px #505050;
}

/* Info icon tooltip styles */
.infoIconWrapper {
position: relative;
display: inline-block;
cursor: help;
margin-left: 2px;
vertical-align: middle;
}

.infoIcon {
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 0.9rem;
color: #0066ff;
transition: color 0.2s ease;
width: 16px;
height: 16px;
border-radius: 50%;
background-color: transparent;
}

.infoIcon:hover {
color: #0044cc;
}

[data-theme='dark'] .infoIcon {
color: #6699ff;
}

[data-theme='dark'] .infoIcon:hover {
color: #88aaff;
}

.infoTooltip {
position: absolute;
bottom: calc(100% + 8px);
left: 50%;
transform: translateX(-50%);
background: #ffffff;
color: #333333;
border: 1px solid #cccccc;
border-radius: 6px;
padding: 8px 12px;
font-size: 0.85rem;
font-weight: normal;
line-height: 1.4;
white-space: normal;
width: max-content;
max-width: 280px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
z-index: 1000;
text-align: left;
}

.infoTooltip::after {
content: '';
position: absolute;
top: 100%;
left: 50%;
margin-left: -6px;
border-width: 6px;
border-style: solid;
border-color: #ffffff transparent transparent transparent;
}

.infoTooltip::before {
content: '';
position: absolute;
top: 100%;
left: 50%;
margin-left: -7px;
border-width: 7px;
border-style: solid;
border-color: #cccccc transparent transparent transparent;
}

[data-theme='dark'] .infoTooltip {
background: #2a2a2a;
color: #e0e0e0;
border-color: #505050;
}

[data-theme='dark'] .infoTooltip::after {
border-color: #2a2a2a transparent transparent transparent;
}

[data-theme='dark'] .infoTooltip::before {
border-color: #505050 transparent transparent transparent;
}

/* Field label with info icon */
.fieldLabel {
display: flex;
align-items: center;
gap: 4px;
margin-bottom: 4px;
}