Skip to content

Commit cd9420a

Browse files
authored
Merge pull request #140 from netbootxyz/updated_downloads
Update downloads page
2 parents 447acac + e2560ad commit cd9420a

File tree

8 files changed

+679
-65
lines changed

8 files changed

+679
-65
lines changed

docs/docker/dhcp.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,3 @@ The following bootfile names can be set as the boot file in the DHCP configurati
9797
| `netboot.xyz-arm64.efi` | DHCP EFI boot image file, uses built-in iPXE NIC drivers |
9898
| `netboot.xyz-arm64-snp.efi` | UEFI w/ Simple Network Protocol, attempts to boot all net devices |
9999
| `netboot.xyz-arm64-snponly.efi` | UEFI w/ Simple Network Protocol, only boots from device chained from |
100-
| `netboot.xyz-rpi4-snp.efi` | UEFI for Raspberry Pi 4, attempts to boot all net devices |

src/components/DownloadCard.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import clsx from 'clsx';
4+
import styles from './DownloadCard.module.css';
5+
6+
const DownloadCard = ({
7+
title,
8+
description,
9+
url,
10+
type,
11+
isRecommended = false,
12+
icon
13+
}) => {
14+
const handleDownload = () => {
15+
window.open(url, '_blank');
16+
};
17+
18+
return (
19+
<div className={clsx(
20+
styles.downloadCard,
21+
isRecommended && styles.recommended
22+
)}>
23+
{isRecommended && (
24+
<div className={styles.recommendedBadge}>
25+
Recommended
26+
</div>
27+
)}
28+
29+
<div className={styles.cardHeader}>
30+
{icon && <div className={styles.icon}>{icon}</div>}
31+
<h3 className={styles.title}>{title}</h3>
32+
<span className={styles.type}>{type}</span>
33+
</div>
34+
35+
<p className={styles.description}>{description}</p>
36+
37+
<button
38+
className={styles.downloadButton}
39+
onClick={handleDownload}
40+
aria-label={`Download ${title}`}
41+
>
42+
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
43+
<path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>
44+
</svg>
45+
Download
46+
</button>
47+
</div>
48+
);
49+
};
50+
DownloadCard.propTypes = {
51+
title: PropTypes.string.isRequired,
52+
description: PropTypes.string.isRequired,
53+
url: PropTypes.string.isRequired,
54+
type: PropTypes.string.isRequired,
55+
isRecommended: PropTypes.bool,
56+
icon: PropTypes.node
57+
};
58+
59+
export default DownloadCard;
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
.downloadCard {
2+
position: relative;
3+
background: var(--ifm-card-background-color, #fff);
4+
border: 1px solid var(--ifm-color-emphasis-200);
5+
border-radius: 12px;
6+
padding: 1.5rem;
7+
transition: all 0.2s ease;
8+
height: 100%;
9+
display: flex;
10+
flex-direction: column;
11+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
12+
}
13+
14+
.downloadCard:hover {
15+
border-color: var(--ifm-color-primary);
16+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
17+
transform: translateY(-2px);
18+
}
19+
20+
.recommended {
21+
border-color: var(--ifm-color-primary);
22+
box-shadow: 0 4px 12px rgba(37, 194, 160, 0.2);
23+
}
24+
25+
.recommendedBadge {
26+
position: absolute;
27+
top: -8px;
28+
right: 16px;
29+
background: white;
30+
border: 1px solid var(--ifm-color-primary);
31+
color: var(--ifm-color-primary);
32+
padding: 4px 12px;
33+
border-radius: 12px;
34+
font-size: 0.75rem;
35+
font-weight: 600;
36+
text-transform: uppercase;
37+
letter-spacing: 0.5px;
38+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
39+
}
40+
41+
[data-theme='dark'] .recommendedBadge {
42+
background: var(--ifm-card-background-color);
43+
border: 1px solid var(--ifm-color-primary);
44+
color: var(--ifm-color-primary);
45+
}
46+
47+
.cardHeader {
48+
display: flex;
49+
align-items: center;
50+
gap: 12px;
51+
margin-bottom: 1rem;
52+
}
53+
54+
.icon {
55+
font-size: 1.5rem;
56+
color: var(--ifm-color-primary);
57+
min-width: 24px;
58+
}
59+
60+
.title {
61+
margin: 0;
62+
font-size: 1.1rem;
63+
font-weight: 600;
64+
color: var(--ifm-color-emphasis-800);
65+
flex: 1;
66+
}
67+
68+
.type {
69+
background: var(--ifm-color-emphasis-100);
70+
color: var(--ifm-color-emphasis-700);
71+
padding: 4px 8px;
72+
border-radius: 6px;
73+
font-size: 0.75rem;
74+
font-weight: 500;
75+
text-transform: uppercase;
76+
}
77+
78+
.description {
79+
color: var(--ifm-color-emphasis-600);
80+
margin-bottom: 1.5rem;
81+
line-height: 1.5;
82+
flex: 1;
83+
}
84+
85+
.downloadButton {
86+
display: flex;
87+
align-items: center;
88+
justify-content: center;
89+
gap: 8px;
90+
background: white;
91+
border: 2px solid var(--ifm-color-primary);
92+
color: var(--ifm-color-primary);
93+
padding: 12px 20px;
94+
border-radius: 8px;
95+
font-weight: 600;
96+
cursor: pointer;
97+
transition: all 0.2s ease;
98+
text-decoration: none;
99+
font-size: 0.9rem;
100+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
101+
}
102+
103+
.downloadButton:hover {
104+
background: var(--ifm-color-primary);
105+
color: white;
106+
transform: translateY(-1px);
107+
box-shadow: 0 4px 12px rgba(37, 194, 160, 0.2);
108+
}
109+
110+
[data-theme='dark'] .downloadButton {
111+
background: var(--ifm-card-background-color);
112+
border: 2px solid var(--ifm-color-primary);
113+
color: var(--ifm-color-primary);
114+
}
115+
116+
[data-theme='dark'] .downloadButton:hover {
117+
background: var(--ifm-color-primary);
118+
color: var(--ifm-card-background-color);
119+
}
120+
121+
.downloadButton svg {
122+
transition: transform 0.2s ease;
123+
}
124+
125+
.downloadButton:hover svg {
126+
transform: translateY(2px);
127+
}
128+
129+
@media (max-width: 768px) {
130+
.downloadCard {
131+
padding: 1rem;
132+
}
133+
134+
.cardHeader {
135+
flex-direction: column;
136+
align-items: flex-start;
137+
gap: 8px;
138+
}
139+
140+
.title {
141+
font-size: 1rem;
142+
}
143+
}

src/components/DownloadSection.js

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import React, { useState } from 'react';
2+
import clsx from 'clsx';
3+
import DownloadCard from './DownloadCard';
4+
import styles from './DownloadSection.module.css';
5+
import PropTypes from 'prop-types';
6+
7+
const DownloadSection = ({
8+
title,
9+
description,
10+
downloads,
11+
isCollapsible = false,
12+
defaultExpanded = true
13+
}) => {
14+
const [isExpanded, setIsExpanded] = useState(defaultExpanded);
15+
16+
const toggleExpanded = () => {
17+
if (isCollapsible) {
18+
setIsExpanded(!isExpanded);
19+
}
20+
};
21+
22+
return (
23+
<div className={styles.downloadSection}>
24+
<div
25+
className={clsx(
26+
styles.sectionHeader,
27+
isCollapsible && styles.collapsible
28+
)}
29+
onClick={toggleExpanded}
30+
>
31+
<h3 className={styles.sectionTitle}>{title}</h3>
32+
{description && (
33+
<p className={styles.sectionDescription}>{description}</p>
34+
)}
35+
{isCollapsible && (
36+
<button className={styles.toggleButton} aria-label="Toggle section">
37+
<svg
38+
width="20"
39+
height="20"
40+
viewBox="0 0 24 24"
41+
fill="currentColor"
42+
className={clsx(styles.chevron, isExpanded && styles.expanded)}
43+
>
44+
<path d="M7.41 8.84L12 13.42l4.59-4.58L18 10.25l-6 6-6-6z"/>
45+
</svg>
46+
</button>
47+
)}
48+
</div>
49+
50+
{(!isCollapsible || isExpanded) && (
51+
<div className={styles.cardsGrid}>
52+
{downloads.map((download, index) => (
53+
<DownloadCard key={index} {...download} />
54+
))}
55+
</div>
56+
)}
57+
</div>
58+
);
59+
};
60+
DownloadSection.propTypes = {
61+
title: PropTypes.string,
62+
description: PropTypes.string,
63+
downloads: PropTypes.arrayOf(PropTypes.object).isRequired,
64+
isCollapsible: PropTypes.bool,
65+
defaultExpanded: PropTypes.bool,
66+
};
67+
68+
export default DownloadSection;
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
.downloadSection {
2+
margin-bottom: 3rem;
3+
}
4+
5+
.sectionHeader {
6+
margin-bottom: 2rem;
7+
padding-bottom: 1rem;
8+
border-bottom: 2px solid var(--ifm-color-emphasis-200);
9+
}
10+
11+
.collapsible {
12+
cursor: pointer;
13+
display: flex;
14+
align-items: center;
15+
justify-content: space-between;
16+
padding: 1rem;
17+
border: 1px solid var(--ifm-color-emphasis-200);
18+
border-radius: 8px;
19+
background: var(--ifm-background-surface-color);
20+
transition: all 0.2s ease;
21+
}
22+
23+
.collapsible:hover {
24+
border-color: var(--ifm-color-primary);
25+
background: var(--ifm-color-emphasis-100);
26+
}
27+
28+
.sectionTitle {
29+
margin: 0;
30+
color: var(--ifm-color-emphasis-800);
31+
font-size: 1.5rem;
32+
font-weight: 700;
33+
}
34+
35+
.sectionDescription {
36+
margin: 0.5rem 0 0 0;
37+
color: var(--ifm-color-emphasis-600);
38+
font-size: 1rem;
39+
line-height: 1.5;
40+
}
41+
42+
.toggleButton {
43+
background: none;
44+
border: none;
45+
cursor: pointer;
46+
padding: 4px;
47+
color: var(--ifm-color-emphasis-600);
48+
transition: color 0.2s ease;
49+
}
50+
51+
.toggleButton:hover {
52+
color: var(--ifm-color-primary);
53+
}
54+
55+
.chevron {
56+
transition: transform 0.2s ease;
57+
}
58+
59+
.chevron.expanded {
60+
transform: rotate(180deg);
61+
}
62+
63+
.cardsGrid {
64+
display: grid;
65+
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
66+
gap: 1.5rem;
67+
margin-top: 1.5rem;
68+
}
69+
70+
@media (max-width: 768px) {
71+
.cardsGrid {
72+
grid-template-columns: 1fr;
73+
gap: 1rem;
74+
}
75+
76+
.sectionTitle {
77+
font-size: 1.25rem;
78+
}
79+
80+
.collapsible {
81+
flex-direction: column;
82+
align-items: flex-start;
83+
gap: 1rem;
84+
}
85+
86+
.toggleButton {
87+
align-self: flex-end;
88+
}
89+
}

0 commit comments

Comments
 (0)