portosaurus 1.16.1 → 1.16.3
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/bin/portosaurus.js +79 -29
- package/package.json +10 -9
- package/src/{internal/src/config → config}/iconMappings.js +1 -1
- package/src/config/metaTags.js +239 -0
- package/src/internal/src/components/ContactSection/index.js +2 -1
- package/src/internal/src/components/NoteIndex/index.js +3 -1
- package/src/internal/src/components/SocialLinks/index.js +56 -60
- package/src/internal/src/utils/cssUtils.js +52 -25
- package/src/internal/src/utils/imageDownloader.js +44 -40
- package/src/template/config.js +19 -24
- package/src/utils/appVersion.js +28 -0
- package/src/utils/{createConfig.js → createDocuConf.js} +137 -25
- package/src/internal/src/config/metaTags.js +0 -240
- package/src/internal/src/utils/appVersion.js +0 -27
- package/src/internal/src/utils/compileConfig.js +0 -82
- package/src/utils/ss.js +0 -294
- /package/src/{internal/src/config → config}/prism.js +0 -0
- /package/src/{internal/src/config → config}/sidebars.js +0 -0
package/bin/portosaurus.js
CHANGED
|
@@ -19,7 +19,7 @@ import { spawn, execSync } from "child_process";
|
|
|
19
19
|
import { createRequire } from "module";
|
|
20
20
|
|
|
21
21
|
import { logger } from "../src/utils/logger.js";
|
|
22
|
-
import {
|
|
22
|
+
import { createDocuConf, writeDocuConf } from "../src/utils/createDocuConf.js";
|
|
23
23
|
|
|
24
24
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
25
25
|
|
|
@@ -67,29 +67,41 @@ async function prepareDocusaurusRun(projectRoot) {
|
|
|
67
67
|
fs.ensureDirSync(path.join(projectRoot, "blog"));
|
|
68
68
|
fs.ensureDirSync(path.join(projectRoot, "static"));
|
|
69
69
|
|
|
70
|
-
//
|
|
71
|
-
|
|
70
|
+
// Ensure notes/index.md exists
|
|
71
|
+
const userIndexPage = path.join(projectRoot, "notes/index.md");
|
|
72
|
+
if (!existsSync(userIndexPage)) {
|
|
73
|
+
const internalIndexPage = path.join(internalDir, "notes/index.md");
|
|
74
|
+
if (existsSync(internalIndexPage)) {
|
|
75
|
+
fs.copySync(internalIndexPage, userIndexPage);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// 1. Copy everything from internal to .portosaurus (skipping content folders except static)
|
|
80
|
+
fs.copySync(internalDir, runtimeDir, {
|
|
81
|
+
filter: (src) => {
|
|
82
|
+
const relative = path.relative(internalDir, src);
|
|
83
|
+
return !["notes", "blog"].includes(relative);
|
|
84
|
+
},
|
|
85
|
+
});
|
|
72
86
|
|
|
73
87
|
// 2. Overwrite with user files if they exist in project root
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
];
|
|
88
|
+
// We only sync "static" and other internal-shadowing files to runtime
|
|
89
|
+
// Notes and Blog are served directly from projectRoot via createConfig.js
|
|
90
|
+
const syncItems = [...new Set([...readdirSync(internalDir), "static"])];
|
|
91
|
+
|
|
77
92
|
for (const file of syncItems) {
|
|
78
93
|
const userFile = path.resolve(projectRoot, file);
|
|
79
94
|
const destFile = path.resolve(runtimeDir, file);
|
|
80
95
|
|
|
96
|
+
// Skip notes and blog folders in the sync loop - they are served from root!
|
|
97
|
+
if (file === "notes" || file === "blog") continue;
|
|
98
|
+
|
|
81
99
|
if (existsSync(userFile)) {
|
|
82
100
|
if (fs.statSync(userFile).isDirectory()) {
|
|
83
101
|
fs.ensureDirSync(destFile);
|
|
84
102
|
}
|
|
85
103
|
fs.copySync(userFile, destFile, {
|
|
86
104
|
overwrite: true,
|
|
87
|
-
filter: (src) => {
|
|
88
|
-
if (src.endsWith(path.join("notes", "index.md"))) {
|
|
89
|
-
return false;
|
|
90
|
-
}
|
|
91
|
-
return true;
|
|
92
|
-
},
|
|
93
105
|
});
|
|
94
106
|
}
|
|
95
107
|
}
|
|
@@ -106,26 +118,10 @@ async function prepareDocusaurusRun(projectRoot) {
|
|
|
106
118
|
}
|
|
107
119
|
|
|
108
120
|
// Ensure defaults if missing
|
|
109
|
-
if (!userConfig.usrConf) {
|
|
110
|
-
userConfig.usrConf = {};
|
|
111
|
-
}
|
|
112
|
-
if (!userConfig.usrConf.site_url) {
|
|
113
|
-
userConfig.usrConf.site_url = "auto";
|
|
114
|
-
}
|
|
115
|
-
if (!userConfig.usrConf.site_path) {
|
|
116
|
-
userConfig.usrConf.site_path = "auto";
|
|
117
|
-
}
|
|
118
|
-
|
|
119
121
|
// Generate Docusaurus Config
|
|
120
|
-
const docusaurusConfig = createConfig(userConfig, projectRoot);
|
|
121
|
-
|
|
122
122
|
// Write temp config file INSIDE .portosaurus
|
|
123
123
|
const tempConfigPath = path.join(runtimeDir, "docusaurus.config.js");
|
|
124
|
-
|
|
125
|
-
const configContent = `// Auto-generated by Portosaurus
|
|
126
|
-
module.exports = ${JSON.stringify(docusaurusConfig, null, 2)};`;
|
|
127
|
-
|
|
128
|
-
writeFileSync(tempConfigPath, configContent);
|
|
124
|
+
writeDocuConf(userConfig, projectRoot, tempConfigPath);
|
|
129
125
|
logger.success("Generated Docusaurus config in .portosaurus.");
|
|
130
126
|
|
|
131
127
|
return { runtimeDir };
|
|
@@ -302,6 +298,60 @@ program
|
|
|
302
298
|
}
|
|
303
299
|
});
|
|
304
300
|
|
|
301
|
+
// --- CONFIG COMMAND ---
|
|
302
|
+
|
|
303
|
+
program
|
|
304
|
+
.command("config")
|
|
305
|
+
.description("Show the generated Docusaurus config")
|
|
306
|
+
.option(
|
|
307
|
+
"-f, --file [filePath]",
|
|
308
|
+
"Output to a file (default: porto.docusaurus.config.js)",
|
|
309
|
+
)
|
|
310
|
+
.option("-c, --config <path>", "Path to config file")
|
|
311
|
+
.action(async (options) => {
|
|
312
|
+
const projectRoot = process.cwd();
|
|
313
|
+
let configPath;
|
|
314
|
+
|
|
315
|
+
if (options.config) {
|
|
316
|
+
configPath = path.resolve(projectRoot, options.config);
|
|
317
|
+
} else {
|
|
318
|
+
configPath = path.join(projectRoot, "config.js");
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (!existsSync(configPath)) {
|
|
322
|
+
logger.error(`Config file not found at ${configPath}`);
|
|
323
|
+
process.exit(1);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const require = createRequire(import.meta.url);
|
|
327
|
+
|
|
328
|
+
let userConfig;
|
|
329
|
+
try {
|
|
330
|
+
userConfig = require(configPath);
|
|
331
|
+
} catch (e) {
|
|
332
|
+
logger.error(`Failed to load config.js: ${e.message}`);
|
|
333
|
+
process.exit(1);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
try {
|
|
337
|
+
if (options.file) {
|
|
338
|
+
let filename = "porto.docusaurus.config.js";
|
|
339
|
+
if (typeof options.file === "string") {
|
|
340
|
+
filename = options.file;
|
|
341
|
+
}
|
|
342
|
+
const outputPath = path.resolve(projectRoot, filename);
|
|
343
|
+
writeDocuConf(userConfig, projectRoot, outputPath);
|
|
344
|
+
logger.success(`Config written to ${filename}`);
|
|
345
|
+
} else {
|
|
346
|
+
const docusaurusConfig = createDocuConf(userConfig, projectRoot);
|
|
347
|
+
console.log(JSON.stringify(docusaurusConfig, null, 2));
|
|
348
|
+
}
|
|
349
|
+
} catch (error) {
|
|
350
|
+
logger.error(`Failed to generate config: ${error.message}`);
|
|
351
|
+
process.exit(1);
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
|
|
305
355
|
// --- START COMMAND ---
|
|
306
356
|
|
|
307
357
|
program
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "portosaurus",
|
|
3
|
-
"version": "1.16.
|
|
3
|
+
"version": "1.16.3",
|
|
4
4
|
"author": "soymadip",
|
|
5
5
|
"license": "GPL-3.0-only",
|
|
6
6
|
"description": "Complete portfolio cum personal website solution for your digital personality.",
|
|
@@ -28,24 +28,25 @@
|
|
|
28
28
|
"bin/",
|
|
29
29
|
"src/internal/",
|
|
30
30
|
"src/template/",
|
|
31
|
-
"src/utils/"
|
|
31
|
+
"src/utils/",
|
|
32
|
+
"src/config/"
|
|
32
33
|
],
|
|
33
34
|
"dependencies": {
|
|
34
35
|
"@docusaurus/core": "^3.9.2",
|
|
35
36
|
"@docusaurus/preset-classic": "^3.9.2",
|
|
37
|
+
"@easyops-cn/docusaurus-search-local": "^0.52.1",
|
|
36
38
|
"chalk": "^5.6.2",
|
|
39
|
+
"clsx": "^2.1.1",
|
|
37
40
|
"commander": "^12.1.0",
|
|
41
|
+
"favicons": "^7.2.0",
|
|
38
42
|
"fs-extra": "^11.3.3",
|
|
43
|
+
"plugin-image-zoom": "github:flexanalytics/plugin-image-zoom",
|
|
44
|
+
"prism-react-renderer": "^2.4.1",
|
|
39
45
|
"prompts": "^2.4.2",
|
|
40
46
|
"react": "^18.3.1",
|
|
41
47
|
"react-dom": "^18.3.1",
|
|
42
|
-
"react-icons": "^5.
|
|
48
|
+
"react-icons": "^5.5.0",
|
|
43
49
|
"react-slick": "^0.30.2",
|
|
44
|
-
"slick-carousel": "^1.8.1"
|
|
45
|
-
"clsx": "^2.1.1",
|
|
46
|
-
"prism-react-renderer": "^2.4.1",
|
|
47
|
-
"favicons": "^7.2.0",
|
|
48
|
-
"plugin-image-zoom": "github:flexanalytics/plugin-image-zoom",
|
|
49
|
-
"@easyops-cn/docusaurus-search-local": "^0.52.1"
|
|
50
|
+
"slick-carousel": "^1.8.1"
|
|
50
51
|
}
|
|
51
52
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TbBrandCSharp,
|
|
1
|
+
import { TbBrandCSharp, TbBrandVscode, TbBrandOnedrive, TbBrandAzure, TbBrandBing, TbBrandGithubCopilot } from "react-icons/tb";
|
|
2
2
|
import { GrVirtualMachine } from "react-icons/gr";
|
|
3
3
|
import { DiRasberryPi } from "react-icons/di";
|
|
4
4
|
import { PiGithubLogoFill, PiMicrosoftExcelLogoDuotone, PiMicrosoftOutlookLogo, PiMicrosoftPowerpointLogo, PiMicrosoftWordLogo } from "react-icons/pi";
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { getCssVar } from "../internal/src/utils/cssUtils.js";
|
|
2
|
+
|
|
3
|
+
const backgroundColor = getCssVar("--ifm-background-color");
|
|
4
|
+
|
|
5
|
+
export const metaTags = [
|
|
6
|
+
// Theme color meta tags
|
|
7
|
+
{
|
|
8
|
+
tagName: "meta",
|
|
9
|
+
attributes: {
|
|
10
|
+
name: "msapplication-TileColor",
|
|
11
|
+
content: backgroundColor,
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
tagName: "meta",
|
|
16
|
+
attributes: {
|
|
17
|
+
name: "theme-color",
|
|
18
|
+
content: backgroundColor,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
// Android Chrome icons
|
|
23
|
+
{
|
|
24
|
+
tagName: "link",
|
|
25
|
+
attributes: {
|
|
26
|
+
rel: "icon",
|
|
27
|
+
type: "image/png",
|
|
28
|
+
sizes: "36x36",
|
|
29
|
+
href: "/favicon/android-chrome-36x36.png",
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
tagName: "link",
|
|
34
|
+
attributes: {
|
|
35
|
+
rel: "icon",
|
|
36
|
+
type: "image/png",
|
|
37
|
+
sizes: "48x48",
|
|
38
|
+
href: "/favicon/android-chrome-48x48.png",
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
tagName: "link",
|
|
43
|
+
attributes: {
|
|
44
|
+
rel: "icon",
|
|
45
|
+
type: "image/png",
|
|
46
|
+
sizes: "72x72",
|
|
47
|
+
href: "/favicon/android-chrome-72x72.png",
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
tagName: "link",
|
|
52
|
+
attributes: {
|
|
53
|
+
rel: "icon",
|
|
54
|
+
type: "image/png",
|
|
55
|
+
sizes: "96x96",
|
|
56
|
+
href: "/favicon/android-chrome-96x96.png",
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
tagName: "link",
|
|
61
|
+
attributes: {
|
|
62
|
+
rel: "icon",
|
|
63
|
+
type: "image/png",
|
|
64
|
+
sizes: "144x144",
|
|
65
|
+
href: "/favicon/android-chrome-144x144.png",
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
tagName: "link",
|
|
70
|
+
attributes: {
|
|
71
|
+
rel: "icon",
|
|
72
|
+
type: "image/png",
|
|
73
|
+
sizes: "192x192",
|
|
74
|
+
href: "/favicon/android-chrome-192x192.png",
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
tagName: "link",
|
|
79
|
+
attributes: {
|
|
80
|
+
rel: "icon",
|
|
81
|
+
type: "image/png",
|
|
82
|
+
sizes: "256x256",
|
|
83
|
+
href: "/favicon/android-chrome-256x256.png",
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
tagName: "link",
|
|
88
|
+
attributes: {
|
|
89
|
+
rel: "icon",
|
|
90
|
+
type: "image/png",
|
|
91
|
+
sizes: "384x384",
|
|
92
|
+
href: "/favicon/android-chrome-384x384.png",
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
tagName: "link",
|
|
97
|
+
attributes: {
|
|
98
|
+
rel: "icon",
|
|
99
|
+
type: "image/png",
|
|
100
|
+
sizes: "512x512",
|
|
101
|
+
href: "/favicon/android-chrome-512x512.png",
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
// Apple touch icons
|
|
106
|
+
{
|
|
107
|
+
tagName: "link",
|
|
108
|
+
attributes: {
|
|
109
|
+
rel: "apple-touch-icon",
|
|
110
|
+
sizes: "57x57",
|
|
111
|
+
href: "/favicon/apple-touch-icon-57x57.png",
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
tagName: "link",
|
|
116
|
+
attributes: {
|
|
117
|
+
rel: "apple-touch-icon",
|
|
118
|
+
sizes: "60x60",
|
|
119
|
+
href: "/favicon/apple-touch-icon-60x60.png",
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
tagName: "link",
|
|
124
|
+
attributes: {
|
|
125
|
+
rel: "apple-touch-icon",
|
|
126
|
+
sizes: "72x72",
|
|
127
|
+
href: "/favicon/apple-touch-icon-72x72.png",
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
tagName: "link",
|
|
132
|
+
attributes: {
|
|
133
|
+
rel: "apple-touch-icon",
|
|
134
|
+
sizes: "76x76",
|
|
135
|
+
href: "/favicon/apple-touch-icon-76x76.png",
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
tagName: "link",
|
|
140
|
+
attributes: {
|
|
141
|
+
rel: "apple-touch-icon",
|
|
142
|
+
sizes: "114x114",
|
|
143
|
+
href: "/favicon/apple-touch-icon-114x114.png",
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
tagName: "link",
|
|
148
|
+
attributes: {
|
|
149
|
+
rel: "apple-touch-icon",
|
|
150
|
+
sizes: "120x120",
|
|
151
|
+
href: "/favicon/apple-touch-icon-120x120.png",
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
tagName: "link",
|
|
156
|
+
attributes: {
|
|
157
|
+
rel: "apple-touch-icon",
|
|
158
|
+
sizes: "144x144",
|
|
159
|
+
href: "/favicon/apple-touch-icon-144x144.png",
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
tagName: "link",
|
|
164
|
+
attributes: {
|
|
165
|
+
rel: "apple-touch-icon",
|
|
166
|
+
sizes: "152x152",
|
|
167
|
+
href: "/favicon/apple-touch-icon-152x152.png",
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
tagName: "link",
|
|
172
|
+
attributes: {
|
|
173
|
+
rel: "apple-touch-icon",
|
|
174
|
+
sizes: "167x167",
|
|
175
|
+
href: "/favicon/apple-touch-icon-167x167.png",
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
tagName: "link",
|
|
180
|
+
attributes: {
|
|
181
|
+
rel: "apple-touch-icon",
|
|
182
|
+
sizes: "180x180",
|
|
183
|
+
href: "/favicon/apple-touch-icon-180x180.png",
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
tagName: "link",
|
|
188
|
+
attributes: {
|
|
189
|
+
rel: "apple-touch-icon",
|
|
190
|
+
sizes: "1024x1024",
|
|
191
|
+
href: "/favicon/apple-touch-icon-1024x1024.png",
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
tagName: "link",
|
|
196
|
+
attributes: {
|
|
197
|
+
rel: "apple-touch-icon-precomposed",
|
|
198
|
+
href: "/favicon/apple-touch-icon-precomposed.png",
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
// Standard favicons
|
|
203
|
+
{
|
|
204
|
+
tagName: "link",
|
|
205
|
+
attributes: {
|
|
206
|
+
rel: "icon",
|
|
207
|
+
type: "image/png",
|
|
208
|
+
sizes: "16x16",
|
|
209
|
+
href: "/favicon/favicon-16x16.png",
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
tagName: "link",
|
|
214
|
+
attributes: {
|
|
215
|
+
rel: "icon",
|
|
216
|
+
type: "image/png",
|
|
217
|
+
sizes: "32x32",
|
|
218
|
+
href: "/favicon/favicon-32x32.png",
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
tagName: "link",
|
|
223
|
+
attributes: {
|
|
224
|
+
rel: "icon",
|
|
225
|
+
type: "image/png",
|
|
226
|
+
sizes: "48x48",
|
|
227
|
+
href: "/favicon/favicon-48x48.png",
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
|
|
231
|
+
// Web manifest
|
|
232
|
+
{
|
|
233
|
+
tagName: "link",
|
|
234
|
+
attributes: {
|
|
235
|
+
rel: "manifest",
|
|
236
|
+
href: "/favicon/manifest.webmanifest",
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
];
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
|
|
2
|
-
import { iconMap } from "@site/src/config/iconMappings";
|
|
3
2
|
import { FaQuestionCircle } from "react-icons/fa";
|
|
4
3
|
import styles from "./styles.module.css";
|
|
5
4
|
|
|
@@ -25,6 +24,8 @@ const sortEmail = (links) => {
|
|
|
25
24
|
export default function ContactSection({ id, className, title, subtitle }) {
|
|
26
25
|
const { siteConfig } = useDocusaurusContext();
|
|
27
26
|
const { customFields } = siteConfig;
|
|
27
|
+
const { iconMap } = customFields.iconMap;
|
|
28
|
+
|
|
28
29
|
let socialLinks = customFields.socialLinks.links || [];
|
|
29
30
|
|
|
30
31
|
socialLinks = sortEmail(socialLinks);
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import Link from "@docusaurus/Link";
|
|
2
2
|
import useBaseUrl from "@docusaurus/useBaseUrl";
|
|
3
3
|
import { usePluginData } from "@docusaurus/useGlobalData";
|
|
4
|
-
import
|
|
4
|
+
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
|
|
5
5
|
import DocCardList from "@theme/DocCardList";
|
|
6
6
|
|
|
7
|
+
const { iconMap } = useDocusaurusContext().siteConfig.customFields.iconMap;
|
|
8
|
+
|
|
7
9
|
import { FaBook } from "react-icons/fa";
|
|
8
10
|
import styles from "./styles.module.css";
|
|
9
11
|
|
|
@@ -1,75 +1,70 @@
|
|
|
1
|
-
import { useState, useEffect, useMemo, useCallback } from
|
|
2
|
-
import styles from
|
|
3
|
-
import { FaQuestionCircle } from
|
|
1
|
+
import { useState, useEffect, useMemo, useCallback } from "react";
|
|
2
|
+
import styles from "./styles.module.css";
|
|
3
|
+
import { FaQuestionCircle } from "react-icons/fa";
|
|
4
4
|
|
|
5
|
-
import useDocusaurusContext from
|
|
6
|
-
import useIsBrowser from
|
|
7
|
-
|
|
8
|
-
import Tooltip from '@site/src/components/Tooltip';
|
|
9
|
-
import { iconMap } from '@site/src/config/iconMappings';
|
|
5
|
+
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
|
|
6
|
+
import useIsBrowser from "@docusaurus/useIsBrowser";
|
|
10
7
|
|
|
8
|
+
import Tooltip from "@site/src/components/Tooltip";
|
|
11
9
|
|
|
12
10
|
// Default icon & icon
|
|
13
11
|
const DEFAULT_ICON = FaQuestionCircle;
|
|
14
|
-
const DEFAULT_COLOR =
|
|
15
|
-
|
|
12
|
+
const DEFAULT_COLOR = "var(--ifm-color-primary)";
|
|
16
13
|
|
|
17
14
|
export default function SocialIcons({ showAll = false }) {
|
|
18
15
|
const { siteConfig } = useDocusaurusContext();
|
|
19
16
|
const { customFields } = siteConfig;
|
|
17
|
+
const { iconMap } = customFields.iconMap;
|
|
18
|
+
|
|
20
19
|
const isBrowser = useIsBrowser();
|
|
21
20
|
|
|
22
21
|
const [animationDelays, setAnimationDelays] = useState({});
|
|
23
22
|
|
|
24
23
|
const allSocialLinks = customFields.socialLinks.links || [];
|
|
25
|
-
|
|
24
|
+
|
|
26
25
|
// FIX: `to prevent unnecessary recalculations`
|
|
27
26
|
const socialLinks = useMemo(() => {
|
|
28
|
-
return showAll
|
|
29
|
-
? allSocialLinks
|
|
30
|
-
: allSocialLinks.filter(link => link.pin);
|
|
27
|
+
return showAll ? allSocialLinks : allSocialLinks.filter((link) => link.pin);
|
|
31
28
|
}, [allSocialLinks, showAll]);
|
|
32
|
-
|
|
29
|
+
|
|
33
30
|
// Calculate delays based on screen size
|
|
34
31
|
const calculateDelays = useCallback(() => {
|
|
35
32
|
if (!isBrowser) return {};
|
|
36
|
-
|
|
33
|
+
|
|
37
34
|
const isTablet = window.innerWidth <= 768;
|
|
38
35
|
const isMobile = window.innerWidth <= 480;
|
|
39
36
|
const delays = {};
|
|
40
|
-
|
|
41
|
-
const baseDelay = isMobile ? 0.7 :
|
|
37
|
+
|
|
38
|
+
const baseDelay = isMobile ? 0.7 : isTablet ? 0.9 : 1.3;
|
|
42
39
|
const incrementDelay = 0.1;
|
|
43
|
-
|
|
40
|
+
|
|
44
41
|
socialLinks.forEach((_, index) => {
|
|
45
|
-
delays[index] = `${baseDelay +
|
|
42
|
+
delays[index] = `${baseDelay + index * incrementDelay}s`;
|
|
46
43
|
});
|
|
47
|
-
|
|
44
|
+
|
|
48
45
|
return delays;
|
|
49
46
|
}, [isBrowser, socialLinks]);
|
|
50
47
|
|
|
51
48
|
useEffect(() => {
|
|
52
49
|
if (!isBrowser) return;
|
|
53
|
-
|
|
50
|
+
|
|
54
51
|
// Set initial delays
|
|
55
52
|
setAnimationDelays(calculateDelays());
|
|
56
|
-
|
|
53
|
+
|
|
57
54
|
const handleResize = () => {
|
|
58
55
|
setAnimationDelays(calculateDelays());
|
|
59
56
|
};
|
|
60
|
-
|
|
61
|
-
window.addEventListener('resize', handleResize);
|
|
62
|
-
return () => window.removeEventListener('resize', handleResize);
|
|
63
|
-
}, [isBrowser, calculateDelays]);
|
|
64
57
|
|
|
58
|
+
window.addEventListener("resize", handleResize);
|
|
59
|
+
return () => window.removeEventListener("resize", handleResize);
|
|
60
|
+
}, [isBrowser, calculateDelays]);
|
|
65
61
|
|
|
66
62
|
// Get icon component and color
|
|
67
63
|
const getIconDetails = (iconName) => {
|
|
68
|
-
|
|
69
64
|
if (!iconName) {
|
|
70
65
|
return {
|
|
71
66
|
icon: DEFAULT_ICON,
|
|
72
|
-
color: DEFAULT_COLOR
|
|
67
|
+
color: DEFAULT_COLOR,
|
|
73
68
|
};
|
|
74
69
|
}
|
|
75
70
|
|
|
@@ -79,50 +74,51 @@ export default function SocialIcons({ showAll = false }) {
|
|
|
79
74
|
if (!iconDetails) {
|
|
80
75
|
return {
|
|
81
76
|
icon: DEFAULT_ICON,
|
|
82
|
-
color: DEFAULT_COLOR
|
|
77
|
+
color: DEFAULT_COLOR,
|
|
83
78
|
};
|
|
84
79
|
}
|
|
85
80
|
|
|
86
81
|
return {
|
|
87
82
|
icon: iconDetails.icon,
|
|
88
|
-
color: iconDetails.color || DEFAULT_COLOR
|
|
83
|
+
color: iconDetails.color || DEFAULT_COLOR,
|
|
89
84
|
};
|
|
90
85
|
};
|
|
91
|
-
|
|
86
|
+
|
|
92
87
|
if (socialLinks.length === 0) {
|
|
93
88
|
return null;
|
|
94
89
|
}
|
|
95
|
-
|
|
90
|
+
|
|
96
91
|
return (
|
|
97
92
|
<div className={styles.socialIcons}>
|
|
98
|
-
{
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
93
|
+
{socialLinks.map((social, index) => {
|
|
94
|
+
const { icon: IconComponent, color: iconColor } = getIconDetails(
|
|
95
|
+
social.icon,
|
|
96
|
+
);
|
|
97
|
+
const href = social.url || "#";
|
|
98
|
+
const displayColor = social.color || iconColor;
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
<Tooltip
|
|
102
|
+
key={index}
|
|
103
|
+
content={social.desc || social.icon || "Link"}
|
|
104
|
+
position="top"
|
|
105
|
+
color={displayColor}
|
|
106
|
+
>
|
|
107
|
+
<a
|
|
108
|
+
href={href}
|
|
109
|
+
target="_blank"
|
|
110
|
+
rel="noopener noreferrer"
|
|
111
|
+
className={styles.socialLink}
|
|
112
|
+
style={{
|
|
113
|
+
"--hover-color": displayColor,
|
|
114
|
+
animationDelay: animationDelays[index] || "0s",
|
|
115
|
+
}}
|
|
116
|
+
aria-label={social.icon || "social link"}
|
|
110
117
|
>
|
|
111
|
-
<
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
className={styles.socialLink}
|
|
116
|
-
style={{
|
|
117
|
-
'--hover-color': displayColor,
|
|
118
|
-
animationDelay: animationDelays[index] || '0s'
|
|
119
|
-
}}
|
|
120
|
-
aria-label={social.icon || 'social link'}
|
|
121
|
-
>
|
|
122
|
-
<IconComponent size={24} />
|
|
123
|
-
</a>
|
|
124
|
-
</Tooltip>
|
|
125
|
-
);
|
|
118
|
+
<IconComponent size={24} />
|
|
119
|
+
</a>
|
|
120
|
+
</Tooltip>
|
|
121
|
+
);
|
|
126
122
|
})}
|
|
127
123
|
</div>
|
|
128
124
|
);
|