portosaurus 1.18.13 → 1.18.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/components/ContactSection/index.js +1 -1
- package/src/components/HeroSection/index.js +1 -1
- package/src/components/NoteIndex/index.js +1 -1
- package/src/components/ProjectsSection/index.js +281 -225
- package/src/components/SocialLinks/index.js +2 -2
- package/src/pages/Home.js +1 -1
- package/src/pages/Notes.js +1 -1
- package/src/pages/Tasks.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FaQuestionCircle } from "react-icons/fa";
|
|
2
2
|
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
|
|
3
|
-
import { iconMap as defaultIconMap } from "
|
|
3
|
+
import { iconMap as defaultIconMap } from "../../config/iconMappings.js";
|
|
4
4
|
import styles from "./styles.module.css";
|
|
5
5
|
|
|
6
6
|
const sortEmail = (links) => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
|
|
2
|
-
import SocialLinks from "
|
|
2
|
+
import SocialLinks from "../SocialLinks/index.js";
|
|
3
3
|
import styles from "./styles.module.css";
|
|
4
4
|
|
|
5
5
|
export default function HeroSection({ id, className }) {
|
|
@@ -4,7 +4,7 @@ import { usePluginData } from "@docusaurus/useGlobalData";
|
|
|
4
4
|
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
|
|
5
5
|
import DocCardList from "@theme/DocCardList";
|
|
6
6
|
|
|
7
|
-
import { iconMap } from "
|
|
7
|
+
import { iconMap } from "../../config/iconMappings.js";
|
|
8
8
|
|
|
9
9
|
import { FaBook } from "react-icons/fa";
|
|
10
10
|
import styles from "./styles.module.css";
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
-
import { useRef, useState, useEffect, useCallback, useMemo } from
|
|
2
|
-
import
|
|
3
|
-
|
|
1
|
+
import { useRef, useState, useEffect, useCallback, useMemo } from "react";
|
|
2
|
+
import SliderImport from "react-slick";
|
|
3
|
+
const Slider = SliderImport.default || SliderImport;
|
|
4
|
+
import {
|
|
5
|
+
FaGithub,
|
|
6
|
+
FaGlobe,
|
|
7
|
+
FaPlay,
|
|
8
|
+
FaChevronLeft,
|
|
9
|
+
FaChevronRight,
|
|
10
|
+
FaStar,
|
|
11
|
+
} from "react-icons/fa";
|
|
4
12
|
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
|
|
5
|
-
import styles from
|
|
13
|
+
import styles from "./styles.module.css";
|
|
6
14
|
|
|
7
15
|
// Import slick carousel css
|
|
8
16
|
import "slick-carousel/slick/slick.css";
|
|
@@ -23,102 +31,119 @@ export default function ProjectsSection({ id, className, title, subtitle }) {
|
|
|
23
31
|
const activeDotRef = useRef(null);
|
|
24
32
|
const dotsContainerRef = useRef(null);
|
|
25
33
|
|
|
26
|
-
// Default Settings
|
|
34
|
+
// Default Settings
|
|
27
35
|
const projectDefaults = {
|
|
28
36
|
title: "Future Project",
|
|
29
|
-
desc:
|
|
37
|
+
desc: "Coming soon...",
|
|
30
38
|
image: "img/project-blank.png",
|
|
31
39
|
state: "active",
|
|
32
|
-
tags:
|
|
40
|
+
tags: ["planned"],
|
|
33
41
|
};
|
|
34
42
|
|
|
43
|
+
const createPlaceholders = useCallback(
|
|
44
|
+
(count, existingProjects) => {
|
|
45
|
+
if (existingProjects.length === 0) return [];
|
|
35
46
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}, [projectDefaults]);
|
|
47
|
+
return [
|
|
48
|
+
...existingProjects,
|
|
49
|
+
...Array.from({ length: count }, (_, i) => ({
|
|
50
|
+
...projectDefaults,
|
|
51
|
+
|
|
52
|
+
// Dummy card config
|
|
53
|
+
state: "n/a",
|
|
54
|
+
title: `Project ${existingProjects.length + i + 1}`,
|
|
55
|
+
description: projectDefaults.desc,
|
|
56
|
+
image: projectDefaults.image,
|
|
57
|
+
id: `placeholder-${i}`,
|
|
58
|
+
tags: null,
|
|
59
|
+
})),
|
|
60
|
+
];
|
|
61
|
+
},
|
|
62
|
+
[projectDefaults],
|
|
63
|
+
);
|
|
54
64
|
|
|
55
65
|
// Get current slidesToShow based on screen width
|
|
56
66
|
const getVisibleSlidesPerView = useCallback(() => {
|
|
57
|
-
if (typeof window ===
|
|
58
|
-
|
|
67
|
+
if (typeof window === "undefined") return 3;
|
|
68
|
+
|
|
59
69
|
const width = window.innerWidth;
|
|
60
70
|
if (width <= 600) return 1;
|
|
61
71
|
if (width <= 1024) return 2;
|
|
62
72
|
return 3;
|
|
63
73
|
}, []);
|
|
64
|
-
|
|
65
|
-
const prepareProjects = useCallback(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
74
|
+
|
|
75
|
+
const prepareProjects = useCallback(
|
|
76
|
+
(projectList, slides) => {
|
|
77
|
+
if (!projectList?.length) return { projects: [], totalPages: 0 };
|
|
78
|
+
|
|
79
|
+
// Sort featured first
|
|
80
|
+
const sortedProjects = [...projectList]
|
|
81
|
+
.sort((a, b) => (a.featured ? -1 : 0) - (b.featured ? -1 : 0))
|
|
82
|
+
.map((project) => {
|
|
83
|
+
// Apply defaults if value not null
|
|
84
|
+
const processedProject = {
|
|
85
|
+
...project,
|
|
86
|
+
description:
|
|
87
|
+
project.desc === undefined ? projectDefaults.desc : project.desc,
|
|
88
|
+
image:
|
|
89
|
+
project.image === undefined
|
|
90
|
+
? projectDefaults.image
|
|
91
|
+
: project.image,
|
|
92
|
+
tags:
|
|
93
|
+
project.tags === undefined
|
|
94
|
+
? [...projectDefaults.tags]
|
|
95
|
+
: project.tags,
|
|
96
|
+
state:
|
|
97
|
+
project.state === undefined
|
|
98
|
+
? projectDefaults.state
|
|
99
|
+
: project.state,
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// Add ID
|
|
103
|
+
if (!processedProject.id) {
|
|
104
|
+
processedProject.id = processedProject.title
|
|
105
|
+
.toLowerCase()
|
|
106
|
+
.replace(/[^\w\s-]/g, "")
|
|
107
|
+
.replace(/\s+/g, "-")
|
|
108
|
+
.replace(/-+/g, "-");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return processedProject;
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Calculate pagination and placeholder needs
|
|
115
|
+
const totalPages = Math.ceil(sortedProjects.length / slides);
|
|
116
|
+
const slotsPerPage = slides;
|
|
117
|
+
const totalSlots = totalPages * slotsPerPage;
|
|
118
|
+
const placeholderCount = totalSlots - sortedProjects.length;
|
|
119
|
+
|
|
120
|
+
// Return prepared data
|
|
121
|
+
return {
|
|
122
|
+
projects:
|
|
123
|
+
placeholderCount > 0
|
|
124
|
+
? createPlaceholders(placeholderCount, sortedProjects)
|
|
125
|
+
: sortedProjects,
|
|
126
|
+
totalPages,
|
|
80
127
|
};
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
processedProject.id = processedProject.title
|
|
85
|
-
.toLowerCase()
|
|
86
|
-
.replace(/[^\w\s-]/g, '')
|
|
87
|
-
.replace(/\s+/g, '-')
|
|
88
|
-
.replace(/-+/g, '-');
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return processedProject;
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
// Calculate pagination and placeholder needs
|
|
95
|
-
const totalPages = Math.ceil(sortedProjects.length / slides);
|
|
96
|
-
const slotsPerPage = slides;
|
|
97
|
-
const totalSlots = totalPages * slotsPerPage;
|
|
98
|
-
const placeholderCount = totalSlots - sortedProjects.length;
|
|
99
|
-
|
|
100
|
-
// Return prepared data
|
|
101
|
-
return {
|
|
102
|
-
projects: placeholderCount > 0
|
|
103
|
-
? createPlaceholders(placeholderCount, sortedProjects)
|
|
104
|
-
: sortedProjects,
|
|
105
|
-
totalPages
|
|
106
|
-
};
|
|
107
|
-
}, [createPlaceholders, projectDefaults]);
|
|
128
|
+
},
|
|
129
|
+
[createPlaceholders, projectDefaults],
|
|
130
|
+
);
|
|
108
131
|
|
|
109
132
|
// Load and set up projects on initial load and on resize
|
|
110
133
|
useEffect(() => {
|
|
111
134
|
const projectShelf = siteConfig.customFields?.projects;
|
|
112
|
-
const configuredProjects = projectShelf?.enable
|
|
113
|
-
|
|
135
|
+
const configuredProjects = projectShelf?.enable
|
|
136
|
+
? projectShelf?.projects || []
|
|
137
|
+
: [];
|
|
138
|
+
|
|
114
139
|
const handleLayout = () => {
|
|
115
140
|
const newSlidesToShow = getVisibleSlidesPerView();
|
|
116
|
-
|
|
141
|
+
|
|
117
142
|
if (newSlidesToShow !== slidesToShow || !projects.length) {
|
|
118
143
|
setSlidesToShow(newSlidesToShow);
|
|
119
|
-
const { projects: newProjects, totalPages: newTotalPages } =
|
|
144
|
+
const { projects: newProjects, totalPages: newTotalPages } =
|
|
120
145
|
prepareProjects(configuredProjects, newSlidesToShow);
|
|
121
|
-
|
|
146
|
+
|
|
122
147
|
setProjects(newProjects);
|
|
123
148
|
setTotalPages(newTotalPages);
|
|
124
149
|
setAtEnd(newProjects.length <= newSlidesToShow);
|
|
@@ -127,124 +152,135 @@ export default function ProjectsSection({ id, className, title, subtitle }) {
|
|
|
127
152
|
|
|
128
153
|
// Initial setup
|
|
129
154
|
handleLayout();
|
|
130
|
-
|
|
155
|
+
|
|
131
156
|
// Resize handler
|
|
132
|
-
window.addEventListener(
|
|
133
|
-
return () => window.removeEventListener(
|
|
134
|
-
}, [
|
|
157
|
+
window.addEventListener("resize", handleLayout);
|
|
158
|
+
return () => window.removeEventListener("resize", handleLayout);
|
|
159
|
+
}, [
|
|
160
|
+
siteConfig,
|
|
161
|
+
getVisibleSlidesPerView,
|
|
162
|
+
prepareProjects,
|
|
163
|
+
slidesToShow,
|
|
164
|
+
projects.length,
|
|
165
|
+
]);
|
|
135
166
|
|
|
136
167
|
// Method to go to a specific slide
|
|
137
|
-
const goToSlide = useCallback(
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
|
|
168
|
+
const goToSlide = useCallback(
|
|
169
|
+
(index) => {
|
|
170
|
+
if (sliderRef.current) {
|
|
171
|
+
sliderRef.current.slickGoTo(index * slidesToShow);
|
|
172
|
+
setCurrentSlide(index);
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
[slidesToShow],
|
|
176
|
+
);
|
|
145
177
|
|
|
146
178
|
useEffect(() => {
|
|
147
179
|
const scrollTimeout = setTimeout(() => {
|
|
148
|
-
|
|
149
180
|
if (activeDotRef.current && dotsContainerRef.current) {
|
|
150
181
|
const container = dotsContainerRef.current;
|
|
151
182
|
const activeDot = activeDotRef.current;
|
|
152
|
-
|
|
153
|
-
try {
|
|
154
183
|
|
|
184
|
+
try {
|
|
155
185
|
// For first few
|
|
156
186
|
const adaptiveThreshold = Math.max(1, Math.floor(totalPages * 0.1));
|
|
157
187
|
if (currentSlide <= adaptiveThreshold) {
|
|
158
188
|
container.scrollTo({
|
|
159
189
|
left: 0,
|
|
160
|
-
behavior:
|
|
190
|
+
behavior: "smooth",
|
|
161
191
|
});
|
|
162
192
|
return;
|
|
163
193
|
}
|
|
164
|
-
|
|
194
|
+
|
|
165
195
|
// For last few
|
|
166
196
|
if (currentSlide >= totalPages - 2) {
|
|
167
197
|
const scrollMax = container.scrollWidth - container.clientWidth;
|
|
168
198
|
container.scrollTo({
|
|
169
199
|
left: scrollMax,
|
|
170
|
-
behavior:
|
|
200
|
+
behavior: "smooth",
|
|
171
201
|
});
|
|
172
202
|
return;
|
|
173
203
|
}
|
|
174
|
-
|
|
204
|
+
|
|
175
205
|
// Center the active dot at mobile
|
|
176
206
|
const dotRect = activeDot.getBoundingClientRect();
|
|
177
207
|
const containerRect = container.getBoundingClientRect();
|
|
178
|
-
|
|
208
|
+
|
|
179
209
|
// Check if dot is within visible area with margins
|
|
180
210
|
const isOutsideLeft = dotRect.left < containerRect.left + 20;
|
|
181
211
|
const isOutsideRight = dotRect.right > containerRect.right - 20;
|
|
182
|
-
|
|
212
|
+
|
|
183
213
|
if (isOutsideLeft || isOutsideRight) {
|
|
184
214
|
const dotPosition = activeDot.offsetLeft;
|
|
185
215
|
const dotWidth = activeDot.clientWidth;
|
|
186
216
|
const containerWidth = container.clientWidth;
|
|
187
|
-
const scrollPosition =
|
|
188
|
-
|
|
217
|
+
const scrollPosition =
|
|
218
|
+
dotPosition - containerWidth / 2 + dotWidth / 2;
|
|
219
|
+
|
|
189
220
|
// Disable smooth scroll if not supported
|
|
190
|
-
if (
|
|
221
|
+
if ("scrollBehavior" in document.documentElement.style) {
|
|
191
222
|
container.scrollTo({
|
|
192
223
|
left: Math.max(0, scrollPosition),
|
|
193
|
-
behavior:
|
|
224
|
+
behavior: "smooth",
|
|
194
225
|
});
|
|
195
226
|
} else {
|
|
196
227
|
container.scrollLeft = Math.max(0, scrollPosition);
|
|
197
228
|
}
|
|
198
229
|
}
|
|
199
230
|
} catch (error) {
|
|
200
|
-
console.warn(
|
|
231
|
+
console.warn("Dot scrolling error:", error);
|
|
201
232
|
}
|
|
202
233
|
}
|
|
203
234
|
}, 50);
|
|
204
|
-
|
|
235
|
+
|
|
205
236
|
return () => clearTimeout(scrollTimeout);
|
|
206
237
|
}, [currentSlide, totalPages]);
|
|
207
238
|
|
|
208
239
|
// Carousel settings
|
|
209
|
-
const settings = useMemo(
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
240
|
+
const settings = useMemo(
|
|
241
|
+
() => ({
|
|
242
|
+
dots: false,
|
|
243
|
+
infinite: false,
|
|
244
|
+
speed: 600,
|
|
245
|
+
slidesToShow: Math.min(projects.length, slidesToShow),
|
|
246
|
+
slidesToScroll: slidesToShow,
|
|
247
|
+
autoplay: false,
|
|
248
|
+
adaptiveHeight: false,
|
|
249
|
+
centerPadding: "20px",
|
|
250
|
+
centerMode: false,
|
|
251
|
+
variableWidth: false,
|
|
252
|
+
swipeToSlide: false,
|
|
253
|
+
focusOnSelect: false,
|
|
254
|
+
responsive: [
|
|
255
|
+
{
|
|
256
|
+
breakpoint: 1024,
|
|
257
|
+
settings: {
|
|
258
|
+
slidesToShow: Math.min(projects.length, 2),
|
|
259
|
+
slidesToScroll: 2,
|
|
260
|
+
dots: false,
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
breakpoint: 600,
|
|
265
|
+
settings: {
|
|
266
|
+
slidesToShow: 1,
|
|
267
|
+
slidesToScroll: 1,
|
|
268
|
+
dots: false,
|
|
269
|
+
arrows: false,
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
],
|
|
273
|
+
className: styles.projectsCarousel,
|
|
274
|
+
beforeChange: (current, next) => {
|
|
275
|
+
setAtBeginning(next === 0);
|
|
276
|
+
setCurrentSlide(Math.floor(next / slidesToShow));
|
|
277
|
+
setAtEnd(
|
|
278
|
+
next + Math.min(projects.length, slidesToShow) >= projects.length,
|
|
279
|
+
);
|
|
230
280
|
},
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
slidesToShow: 1,
|
|
235
|
-
slidesToScroll: 1,
|
|
236
|
-
dots: false,
|
|
237
|
-
arrows: false,
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
],
|
|
241
|
-
className: styles.projectsCarousel,
|
|
242
|
-
beforeChange: (current, next) => {
|
|
243
|
-
setAtBeginning(next === 0);
|
|
244
|
-
setCurrentSlide(Math.floor(next / slidesToShow));
|
|
245
|
-
setAtEnd(next + Math.min(projects.length, slidesToShow) >= projects.length);
|
|
246
|
-
},
|
|
247
|
-
}), [projects, slidesToShow]);
|
|
281
|
+
}),
|
|
282
|
+
[projects, slidesToShow],
|
|
283
|
+
);
|
|
248
284
|
|
|
249
285
|
// Navigation handlers
|
|
250
286
|
const goToNext = useCallback(() => {
|
|
@@ -264,10 +300,10 @@ export default function ProjectsSection({ id, className, title, subtitle }) {
|
|
|
264
300
|
if (!url) return null;
|
|
265
301
|
|
|
266
302
|
return (
|
|
267
|
-
<a
|
|
268
|
-
href={url}
|
|
269
|
-
target="_blank"
|
|
270
|
-
rel="noopener noreferrer"
|
|
303
|
+
<a
|
|
304
|
+
href={url}
|
|
305
|
+
target="_blank"
|
|
306
|
+
rel="noopener noreferrer"
|
|
271
307
|
className={styles.projectLink}
|
|
272
308
|
aria-label={ariaLabel}
|
|
273
309
|
>
|
|
@@ -279,48 +315,48 @@ export default function ProjectsSection({ id, className, title, subtitle }) {
|
|
|
279
315
|
|
|
280
316
|
// Get state label and class
|
|
281
317
|
const getProjectStateInfo = useCallback((state) => {
|
|
282
|
-
switch(state?.toLowerCase()) {
|
|
318
|
+
switch (state?.toLowerCase()) {
|
|
283
319
|
// For projects currently in active development
|
|
284
|
-
case
|
|
285
|
-
return {
|
|
286
|
-
label:
|
|
320
|
+
case "active":
|
|
321
|
+
return {
|
|
322
|
+
label: "Active",
|
|
287
323
|
className: styles.stateActive,
|
|
288
324
|
};
|
|
289
325
|
// For finished projects
|
|
290
|
-
case
|
|
291
|
-
return {
|
|
292
|
-
label:
|
|
326
|
+
case "completed":
|
|
327
|
+
return {
|
|
328
|
+
label: "Completed",
|
|
293
329
|
className: styles.stateCompleted,
|
|
294
330
|
};
|
|
295
331
|
// For projects receiving updates/maintenance
|
|
296
|
-
case
|
|
297
|
-
return {
|
|
298
|
-
label:
|
|
332
|
+
case "maintenance":
|
|
333
|
+
return {
|
|
334
|
+
label: "Maintenance",
|
|
299
335
|
className: styles.stateMaintenance,
|
|
300
336
|
};
|
|
301
337
|
// For temporarily paused development
|
|
302
|
-
case
|
|
303
|
-
return {
|
|
304
|
-
label:
|
|
338
|
+
case "paused":
|
|
339
|
+
return {
|
|
340
|
+
label: "Paused",
|
|
305
341
|
className: styles.statePaused,
|
|
306
342
|
};
|
|
307
343
|
// For projects no longer maintained
|
|
308
|
-
case
|
|
309
|
-
return {
|
|
310
|
-
label:
|
|
344
|
+
case "archived":
|
|
345
|
+
return {
|
|
346
|
+
label: "Archived",
|
|
311
347
|
className: styles.stateArchived,
|
|
312
348
|
};
|
|
313
349
|
// For future projects in planning stage
|
|
314
|
-
case
|
|
315
|
-
return {
|
|
316
|
-
label:
|
|
350
|
+
case "planned":
|
|
351
|
+
return {
|
|
352
|
+
label: "Planned",
|
|
317
353
|
className: styles.statePlanned,
|
|
318
354
|
};
|
|
319
355
|
// Default state when not specified
|
|
320
|
-
case
|
|
356
|
+
case "n/a":
|
|
321
357
|
default:
|
|
322
|
-
return {
|
|
323
|
-
label:
|
|
358
|
+
return {
|
|
359
|
+
label: "N/A",
|
|
324
360
|
className: styles.stateNA,
|
|
325
361
|
};
|
|
326
362
|
}
|
|
@@ -332,9 +368,9 @@ export default function ProjectsSection({ id, className, title, subtitle }) {
|
|
|
332
368
|
|
|
333
369
|
// Determine if we should use scrollable or centered layout
|
|
334
370
|
const fewDots = totalPages <= 5;
|
|
335
|
-
|
|
371
|
+
|
|
336
372
|
return (
|
|
337
|
-
<div
|
|
373
|
+
<div
|
|
338
374
|
className={`${styles.navDotsContainer} ${fewDots ? styles.centerDots : styles.scrollDots}`}
|
|
339
375
|
role="tablist"
|
|
340
376
|
aria-label="Project carousel navigation"
|
|
@@ -342,7 +378,7 @@ export default function ProjectsSection({ id, className, title, subtitle }) {
|
|
|
342
378
|
{Array.from({ length: totalPages }, (_, i) => (
|
|
343
379
|
<button
|
|
344
380
|
key={i}
|
|
345
|
-
className={`${styles.navDot} ${currentSlide === i ? styles.activeDot :
|
|
381
|
+
className={`${styles.navDot} ${currentSlide === i ? styles.activeDot : ""}`}
|
|
346
382
|
onClick={() => goToSlide(i)}
|
|
347
383
|
aria-label={`Go to slide ${i + 1} of ${totalPages}`}
|
|
348
384
|
aria-selected={currentSlide === i}
|
|
@@ -356,17 +392,21 @@ export default function ProjectsSection({ id, className, title, subtitle }) {
|
|
|
356
392
|
}, [currentSlide, totalPages, goToSlide]);
|
|
357
393
|
|
|
358
394
|
return (
|
|
359
|
-
<div
|
|
395
|
+
<div
|
|
396
|
+
id={id}
|
|
397
|
+
className={`${styles.projectsSection} ${className || ""}`}
|
|
398
|
+
role="region"
|
|
399
|
+
aria-label="Projects section"
|
|
400
|
+
>
|
|
360
401
|
<div className={styles.projectsContainer}>
|
|
361
402
|
<div className={styles.projectsHeader}>
|
|
362
|
-
<h2 className={styles.projectsTitle}>
|
|
363
|
-
{title || "My Projects"}
|
|
364
|
-
</h2>
|
|
403
|
+
<h2 className={styles.projectsTitle}>{title || "My Projects"}</h2>
|
|
365
404
|
<p className={styles.projectsSubtitle}>
|
|
366
|
-
{subtitle ||
|
|
405
|
+
{subtitle ||
|
|
406
|
+
"A collection of all my works, with featured projects highlighted"}
|
|
367
407
|
</p>
|
|
368
408
|
</div>
|
|
369
|
-
|
|
409
|
+
|
|
370
410
|
{projects.length === 0 ? (
|
|
371
411
|
<div className={styles.noProjects}>
|
|
372
412
|
<p>No projects to display.</p>
|
|
@@ -374,8 +414,8 @@ export default function ProjectsSection({ id, className, title, subtitle }) {
|
|
|
374
414
|
) : (
|
|
375
415
|
<div className={styles.carouselContainer}>
|
|
376
416
|
{/* Desktop navigation buttons (sides) */}
|
|
377
|
-
<button
|
|
378
|
-
className={`${styles.carouselControl} ${styles.prevButton} ${styles.desktopOnly} ${atBeginning ? styles.disabledButton :
|
|
417
|
+
<button
|
|
418
|
+
className={`${styles.carouselControl} ${styles.prevButton} ${styles.desktopOnly} ${atBeginning ? styles.disabledButton : ""}`}
|
|
379
419
|
onClick={goToPrev}
|
|
380
420
|
aria-label="View previous projects"
|
|
381
421
|
aria-disabled={atBeginning}
|
|
@@ -384,25 +424,33 @@ export default function ProjectsSection({ id, className, title, subtitle }) {
|
|
|
384
424
|
>
|
|
385
425
|
<FaChevronLeft aria-hidden="true" />
|
|
386
426
|
</button>
|
|
387
|
-
|
|
388
|
-
<div
|
|
427
|
+
|
|
428
|
+
<div
|
|
429
|
+
className={styles.carouselWrapper}
|
|
430
|
+
aria-roledescription="carousel"
|
|
431
|
+
aria-label="Projects carousel"
|
|
432
|
+
>
|
|
389
433
|
<Slider ref={sliderRef} {...settings}>
|
|
390
434
|
{projects.map((project, index) => (
|
|
391
|
-
<div
|
|
392
|
-
key={project.id || project.title + index}
|
|
393
|
-
className={styles.carouselSlide}
|
|
435
|
+
<div
|
|
436
|
+
key={project.id || project.title + index}
|
|
437
|
+
className={styles.carouselSlide}
|
|
394
438
|
data-project-id={project.id}
|
|
395
|
-
aria-roledescription="slide"
|
|
439
|
+
aria-roledescription="slide"
|
|
396
440
|
aria-label={`Project ${index + 1} of ${projects.length}: ${project.title}`}
|
|
397
441
|
>
|
|
398
|
-
<div
|
|
442
|
+
<div
|
|
443
|
+
className={`${styles.carouselCard} ${project.featured ? styles.featuredCard : ""}`}
|
|
444
|
+
>
|
|
399
445
|
{/* Project state badge */}
|
|
400
446
|
{project.state && (
|
|
401
|
-
<div
|
|
447
|
+
<div
|
|
402
448
|
className={styles.projectStateBadge}
|
|
403
449
|
title={`Project status: ${getProjectStateInfo(project.state).label}`}
|
|
404
450
|
>
|
|
405
|
-
<span
|
|
451
|
+
<span
|
|
452
|
+
className={`${styles.projectStateLabel} ${getProjectStateInfo(project.state).className}`}
|
|
453
|
+
>
|
|
406
454
|
{getProjectStateInfo(project.state).label}
|
|
407
455
|
</span>
|
|
408
456
|
</div>
|
|
@@ -410,74 +458,82 @@ export default function ProjectsSection({ id, className, title, subtitle }) {
|
|
|
410
458
|
|
|
411
459
|
<div className={styles.projectImageContainer}>
|
|
412
460
|
{project.image && (
|
|
413
|
-
<img
|
|
414
|
-
src={project.image}
|
|
415
|
-
alt={project.title}
|
|
416
|
-
className={styles.projectImage}
|
|
461
|
+
<img
|
|
462
|
+
src={project.image}
|
|
463
|
+
alt={project.title}
|
|
464
|
+
className={styles.projectImage}
|
|
417
465
|
loading="lazy"
|
|
418
466
|
/>
|
|
419
467
|
)}
|
|
420
|
-
|
|
468
|
+
|
|
421
469
|
{/* Featured badge */}
|
|
422
470
|
{project.featured && (
|
|
423
|
-
<div
|
|
471
|
+
<div
|
|
472
|
+
className={styles.featuredBadge}
|
|
473
|
+
title="Featured Project"
|
|
474
|
+
aria-label="Featured project"
|
|
475
|
+
>
|
|
424
476
|
<FaStar aria-hidden="true" />
|
|
425
477
|
</div>
|
|
426
478
|
)}
|
|
427
479
|
</div>
|
|
428
|
-
|
|
480
|
+
|
|
429
481
|
<div className={styles.projectContent}>
|
|
430
482
|
<h3 className={styles.projectTitle}>{project.title}</h3>
|
|
431
|
-
|
|
483
|
+
|
|
432
484
|
{project.tags?.length > 0 && (
|
|
433
485
|
<div className={styles.projectTags}>
|
|
434
|
-
{project.tags.map(tag => (
|
|
435
|
-
<span key={tag} className={styles.projectTag}>
|
|
486
|
+
{project.tags.map((tag) => (
|
|
487
|
+
<span key={tag} className={styles.projectTag}>
|
|
488
|
+
{tag}
|
|
489
|
+
</span>
|
|
436
490
|
))}
|
|
437
491
|
</div>
|
|
438
492
|
)}
|
|
439
|
-
|
|
440
|
-
<p className={styles.projectDescription}>
|
|
493
|
+
|
|
494
|
+
<p className={styles.projectDescription}>
|
|
495
|
+
{project.description}
|
|
496
|
+
</p>
|
|
441
497
|
</div>
|
|
442
|
-
|
|
498
|
+
|
|
443
499
|
<div className={styles.projectLinks}>
|
|
444
500
|
{renderProjectLink(
|
|
445
|
-
project.website,
|
|
446
|
-
FaGlobe,
|
|
447
|
-
"Website",
|
|
448
|
-
`Visit ${project.title} website
|
|
501
|
+
project.website,
|
|
502
|
+
FaGlobe,
|
|
503
|
+
"Website",
|
|
504
|
+
`Visit ${project.title} website`,
|
|
449
505
|
)}
|
|
450
|
-
|
|
506
|
+
|
|
451
507
|
{renderProjectLink(
|
|
452
|
-
project.github,
|
|
453
|
-
FaGithub,
|
|
454
|
-
"Source",
|
|
455
|
-
`Repository with source code
|
|
508
|
+
project.github,
|
|
509
|
+
FaGithub,
|
|
510
|
+
"Source",
|
|
511
|
+
`Repository with source code`,
|
|
456
512
|
)}
|
|
457
|
-
|
|
513
|
+
|
|
458
514
|
{renderProjectLink(
|
|
459
|
-
project.Demo,
|
|
460
|
-
FaPlay,
|
|
461
|
-
"Demo",
|
|
462
|
-
`Live demo for ${project.title}
|
|
515
|
+
project.Demo,
|
|
516
|
+
FaPlay,
|
|
517
|
+
"Demo",
|
|
518
|
+
`Live demo for ${project.title}`,
|
|
463
519
|
)}
|
|
464
520
|
</div>
|
|
465
521
|
</div>
|
|
466
522
|
</div>
|
|
467
523
|
))}
|
|
468
524
|
</Slider>
|
|
469
|
-
|
|
525
|
+
|
|
470
526
|
{/* Desktop navigation dots */}
|
|
471
527
|
<div className={styles.desktopDotsContainer}>
|
|
472
528
|
{renderNavigationDots()}
|
|
473
529
|
</div>
|
|
474
|
-
|
|
530
|
+
|
|
475
531
|
{/* Mobile navigation controls (bottom) */}
|
|
476
532
|
<div className={styles.mobileNavigationControls}>
|
|
477
533
|
{totalPages > 1 && (
|
|
478
534
|
<>
|
|
479
|
-
<button
|
|
480
|
-
className={`${styles.carouselControl} ${styles.prevButton} ${atBeginning ? styles.disabledButton :
|
|
535
|
+
<button
|
|
536
|
+
className={`${styles.carouselControl} ${styles.prevButton} ${atBeginning ? styles.disabledButton : ""}`}
|
|
481
537
|
onClick={goToPrev}
|
|
482
538
|
aria-label="View previous projects"
|
|
483
539
|
aria-disabled={atBeginning}
|
|
@@ -486,17 +542,17 @@ export default function ProjectsSection({ id, className, title, subtitle }) {
|
|
|
486
542
|
>
|
|
487
543
|
<FaChevronLeft aria-hidden="true" />
|
|
488
544
|
</button>
|
|
489
|
-
|
|
545
|
+
|
|
490
546
|
{/* Mobile navigation dots */}
|
|
491
|
-
<div
|
|
547
|
+
<div
|
|
492
548
|
className={styles.dotsScrollContainer}
|
|
493
549
|
ref={dotsContainerRef}
|
|
494
550
|
>
|
|
495
551
|
{renderNavigationDots()}
|
|
496
552
|
</div>
|
|
497
|
-
|
|
498
|
-
<button
|
|
499
|
-
className={`${styles.carouselControl} ${styles.nextButton} ${atEnd ? styles.disabledButton :
|
|
553
|
+
|
|
554
|
+
<button
|
|
555
|
+
className={`${styles.carouselControl} ${styles.nextButton} ${atEnd ? styles.disabledButton : ""}`}
|
|
500
556
|
onClick={goToNext}
|
|
501
557
|
aria-label="View next projects"
|
|
502
558
|
aria-disabled={atEnd}
|
|
@@ -509,10 +565,10 @@ export default function ProjectsSection({ id, className, title, subtitle }) {
|
|
|
509
565
|
)}
|
|
510
566
|
</div>
|
|
511
567
|
</div>
|
|
512
|
-
|
|
568
|
+
|
|
513
569
|
{/* Desktop navigation button (right side) */}
|
|
514
|
-
<button
|
|
515
|
-
className={`${styles.carouselControl} ${styles.nextButton} ${styles.desktopOnly} ${atEnd ? styles.disabledButton :
|
|
570
|
+
<button
|
|
571
|
+
className={`${styles.carouselControl} ${styles.nextButton} ${styles.desktopOnly} ${atEnd ? styles.disabledButton : ""}`}
|
|
516
572
|
onClick={goToNext}
|
|
517
573
|
aria-label="View next projects"
|
|
518
574
|
aria-disabled={atEnd}
|
|
@@ -5,13 +5,13 @@ import { FaQuestionCircle } from "react-icons/fa";
|
|
|
5
5
|
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
|
|
6
6
|
import useIsBrowser from "@docusaurus/useIsBrowser";
|
|
7
7
|
|
|
8
|
-
import Tooltip from "
|
|
8
|
+
import Tooltip from "../Tooltip/index.js";
|
|
9
9
|
|
|
10
10
|
// Default icon & icon
|
|
11
11
|
const DEFAULT_ICON = FaQuestionCircle;
|
|
12
12
|
const DEFAULT_COLOR = "var(--ifm-color-primary)";
|
|
13
13
|
|
|
14
|
-
import { iconMap } from "
|
|
14
|
+
import { iconMap } from "../../config/iconMappings.js";
|
|
15
15
|
|
|
16
16
|
export default function SocialIcons({ showAll = false }) {
|
|
17
17
|
const { siteConfig } = useDocusaurusContext();
|
package/src/pages/Home.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Docusaurus Utils
|
|
2
2
|
import Layout from "@theme/Layout";
|
|
3
3
|
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
|
|
4
|
-
import UpdateTitle from "
|
|
4
|
+
import UpdateTitle from "../utils/updateTitle.js";
|
|
5
5
|
|
|
6
6
|
// Import components
|
|
7
7
|
import HeroSection from "#components/HeroSection/index.js";
|
package/src/pages/Notes.js
CHANGED
|
@@ -3,7 +3,7 @@ import Layout from "@theme/Layout";
|
|
|
3
3
|
import NoteCards from "#components/NoteIndex/index.js";
|
|
4
4
|
import { usePluginData } from "@docusaurus/useGlobalData";
|
|
5
5
|
import ScrollToTop from "#components/ScrollToTop/index.js";
|
|
6
|
-
import HashNavigation from "
|
|
6
|
+
import HashNavigation from "../utils/HashNavigation.js";
|
|
7
7
|
|
|
8
8
|
const style = {
|
|
9
9
|
notesContainer: {
|
package/src/pages/Tasks.js
CHANGED
|
@@ -12,7 +12,7 @@ import { useState } from "react";
|
|
|
12
12
|
import Layout from "@theme/Layout";
|
|
13
13
|
import Head from "@docusaurus/Head";
|
|
14
14
|
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
|
|
15
|
-
import "
|
|
15
|
+
import "../css/tasks.css";
|
|
16
16
|
import {
|
|
17
17
|
FaClipboardList,
|
|
18
18
|
FaSyncAlt,
|