Project pages (#65)

* Initial impl of project pages

* Finalize project styling

* Revert browse page changes

* Fix lint

* Addressed some comments

* 😄

* Run lint

* Addressed issues

* fixed dist

* addressed changes

* Address changes

* I am speed

* Howd you get there
This commit is contained in:
Adrian O.V authored and GitHub committed 2023-04-06 18:06:37 -07:00
1 parent b9a3a6dc11
commit 34f4b762f9
26 files changed
+1706 -150

No files matched your search

+1
View File
@@ -10,6 +10,7 @@ pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
-4
View File
@@ -1,4 +0,0 @@
# Ignore everything in this directory
*
# except the gitignore
!.gitignore
+4 -3
View File
@@ -14,11 +14,12 @@
},
"dependencies": {
"@tauri-apps/api": "^1.2.0",
"ofetch": "^1.0.1",
"omorphia": "^0.4.2",
"omorphia": "^0.4.4",
"pinia": "^2.0.33",
"vite-svg-loader": "^4.0.0",
"vue": "^3.2.45",
"dayjs": "^1.11.7",
"ofetch": "^1.0.1",
"vue-multiselect": "^3.0.0-alpha.2",
"vue-router": "4"
},
@@ -34,4 +35,4 @@
"vite": "^4.0.0",
"vite-plugin-eslint": "^1.8.1"
}
}
}
+16
View File
@@ -0,0 +1,16 @@
<svg width="884" height="1279" viewBox="0 0 884 1279" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M791.109 297.518L790.231 297.002L788.201 296.383C789.018 297.072 790.04 297.472 791.109 297.518V297.518Z" fill="currentColor"/>
<path d="M803.896 388.891L802.916 389.166L803.896 388.891Z" fill="currentColor"/>
<path d="M791.484 297.377C791.359 297.361 791.237 297.332 791.118 297.29C791.111 297.371 791.111 297.453 791.118 297.534C791.252 297.516 791.379 297.462 791.484 297.377V297.377Z" fill="currentColor"/>
<path d="M791.113 297.529H791.244V297.447L791.113 297.529Z" fill="currentColor"/>
<path d="M803.111 388.726L804.591 387.883L805.142 387.573L805.641 387.04C804.702 387.444 803.846 388.016 803.111 388.726V388.726Z" fill="currentColor"/>
<path d="M793.669 299.515L792.223 298.138L791.243 297.605C791.77 298.535 792.641 299.221 793.669 299.515V299.515Z" fill="currentColor"/>
<path d="M430.019 1186.18C428.864 1186.68 427.852 1187.46 427.076 1188.45L427.988 1187.87C428.608 1187.3 429.485 1186.63 430.019 1186.18Z" fill="currentColor"/>
<path d="M641.187 1144.63C641.187 1143.33 640.551 1143.57 640.705 1148.21C640.705 1147.84 640.86 1147.46 640.929 1147.1C641.015 1146.27 641.084 1145.46 641.187 1144.63Z" fill="currentColor"/>
<path d="M619.284 1186.18C618.129 1186.68 617.118 1187.46 616.342 1188.45L617.254 1187.87C617.873 1187.3 618.751 1186.63 619.284 1186.18Z" fill="currentColor"/>
<path d="M281.304 1196.06C280.427 1195.3 279.354 1194.8 278.207 1194.61C279.136 1195.06 280.065 1195.51 280.684 1195.85L281.304 1196.06Z" fill="currentColor"/>
<path d="M247.841 1164.01C247.704 1162.66 247.288 1161.35 246.619 1160.16C247.093 1161.39 247.489 1162.66 247.806 1163.94L247.841 1164.01Z" fill="currentColor"/>
<path d="M472.623 590.836C426.682 610.503 374.546 632.802 306.976 632.802C278.71 632.746 250.58 628.868 223.353 621.274L270.086 1101.08C271.74 1121.13 280.876 1139.83 295.679 1153.46C310.482 1167.09 329.87 1174.65 349.992 1174.65C349.992 1174.65 416.254 1178.09 438.365 1178.09C462.161 1178.09 533.516 1174.65 533.516 1174.65C553.636 1174.65 573.019 1167.08 587.819 1153.45C602.619 1139.82 611.752 1121.13 613.406 1101.08L663.459 570.876C641.091 563.237 618.516 558.161 593.068 558.161C549.054 558.144 513.591 573.303 472.623 590.836Z" fill="currentColor"/>
<path d="M78.6885 386.132L79.4799 386.872L79.9962 387.182C79.5987 386.787 79.1603 386.435 78.6885 386.132V386.132Z" fill="currentColor"/>
<path d="M879.567 341.849L872.53 306.352C866.215 274.503 851.882 244.409 819.19 232.898C808.711 229.215 796.821 227.633 788.786 220.01C780.751 212.388 778.376 200.55 776.518 189.572C773.076 169.423 769.842 149.257 766.314 129.143C763.269 111.85 760.86 92.4243 752.928 76.56C742.604 55.2584 721.182 42.8009 699.88 34.559C688.965 30.4844 677.826 27.0375 666.517 24.2352C613.297 10.1947 557.342 5.03277 502.591 2.09047C436.875 -1.53577 370.983 -0.443234 305.422 5.35968C256.625 9.79894 205.229 15.1674 158.858 32.0469C141.91 38.224 124.445 45.6399 111.558 58.7341C95.7448 74.8221 90.5829 99.7026 102.128 119.765C110.336 134.012 124.239 144.078 138.985 150.737C158.192 159.317 178.251 165.846 198.829 170.215C256.126 182.879 315.471 187.851 374.007 189.968C438.887 192.586 503.87 190.464 568.44 183.618C584.408 181.863 600.347 179.758 616.257 177.304C634.995 174.43 647.022 149.928 641.499 132.859C634.891 112.453 617.134 104.538 597.055 107.618C594.095 108.082 591.153 108.512 588.193 108.942L586.06 109.252C579.257 110.113 572.455 110.915 565.653 111.661C551.601 113.175 537.515 114.414 523.394 115.378C491.768 117.58 460.057 118.595 428.363 118.647C397.219 118.647 366.058 117.769 334.983 115.722C320.805 114.793 306.661 113.611 292.552 112.177C286.134 111.506 279.733 110.801 273.333 110.009L267.241 109.235L265.917 109.046L259.602 108.134C246.697 106.189 233.792 103.953 221.025 101.251C219.737 100.965 218.584 100.249 217.758 99.2193C216.932 98.1901 216.482 96.9099 216.482 95.5903C216.482 94.2706 216.932 92.9904 217.758 91.9612C218.584 90.9319 219.737 90.2152 221.025 89.9293H221.266C232.33 87.5721 243.479 85.5589 254.663 83.8038C258.392 83.2188 262.131 82.6453 265.882 82.0832H265.985C272.988 81.6186 280.026 80.3625 286.994 79.5366C347.624 73.2302 408.614 71.0801 469.538 73.1014C499.115 73.9618 528.676 75.6996 558.116 78.6935C564.448 79.3474 570.746 80.0357 577.043 80.8099C579.452 81.1025 581.878 81.4465 584.305 81.7391L589.191 82.4445C603.438 84.5667 617.61 87.1419 631.708 90.1703C652.597 94.7128 679.422 96.1925 688.713 119.077C691.673 126.338 693.015 134.408 694.649 142.03L696.731 151.752C696.786 151.926 696.826 152.105 696.852 152.285C701.773 175.227 706.7 198.169 711.632 221.111C711.994 222.806 712.002 224.557 711.657 226.255C711.312 227.954 710.621 229.562 709.626 230.982C708.632 232.401 707.355 233.6 705.877 234.504C704.398 235.408 702.75 235.997 701.033 236.236H700.895L697.884 236.649L694.908 237.044C685.478 238.272 676.038 239.419 666.586 240.486C647.968 242.608 629.322 244.443 610.648 245.992C573.539 249.077 536.356 251.102 499.098 252.066C480.114 252.57 461.135 252.806 442.162 252.771C366.643 252.712 291.189 248.322 216.173 239.625C208.051 238.662 199.93 237.629 191.808 236.58C198.106 237.389 187.231 235.96 185.029 235.651C179.867 234.928 174.705 234.177 169.543 233.397C152.216 230.798 134.993 227.598 117.7 224.793C96.7944 221.352 76.8005 223.073 57.8906 233.397C42.3685 241.891 29.8055 254.916 21.8776 270.735C13.7217 287.597 11.2956 305.956 7.64786 324.075C4.00009 342.193 -1.67805 361.688 0.472751 380.288C5.10128 420.431 33.165 453.054 73.5313 460.35C111.506 467.232 149.687 472.807 187.971 477.556C338.361 495.975 490.294 498.178 641.155 484.129C653.44 482.982 665.708 481.732 677.959 480.378C681.786 479.958 685.658 480.398 689.292 481.668C692.926 482.938 696.23 485.005 698.962 487.717C701.694 490.429 703.784 493.718 705.08 497.342C706.377 500.967 706.846 504.836 706.453 508.665L702.633 545.797C694.936 620.828 687.239 695.854 679.542 770.874C671.513 849.657 663.431 928.434 655.298 1007.2C653.004 1029.39 650.71 1051.57 648.416 1073.74C646.213 1095.58 645.904 1118.1 641.757 1139.68C635.218 1173.61 612.248 1194.45 578.73 1202.07C548.022 1209.06 516.652 1212.73 485.161 1213.01C450.249 1213.2 415.355 1211.65 380.443 1211.84C343.173 1212.05 297.525 1208.61 268.756 1180.87C243.479 1156.51 239.986 1118.36 236.545 1085.37C231.957 1041.7 227.409 998.039 222.9 954.381L197.607 711.615L181.244 554.538C180.968 551.94 180.693 549.376 180.435 546.76C178.473 528.023 165.207 509.681 144.301 510.627C126.407 511.418 106.069 526.629 108.168 546.76L120.298 663.214L145.385 904.104C152.532 972.528 159.661 1040.96 166.773 1109.41C168.15 1122.52 169.44 1135.67 170.885 1148.78C178.749 1220.43 233.465 1259.04 301.224 1269.91C340.799 1276.28 381.337 1277.59 421.497 1278.24C472.979 1279.07 524.977 1281.05 575.615 1271.72C650.653 1257.95 706.952 1207.85 714.987 1130.13C717.282 1107.69 719.576 1085.25 721.87 1062.8C729.498 988.559 737.115 914.313 744.72 840.061L769.601 597.451L781.009 486.263C781.577 480.749 783.905 475.565 787.649 471.478C791.392 467.391 796.352 464.617 801.794 463.567C823.25 459.386 843.761 452.245 859.023 435.916C883.318 409.918 888.153 376.021 879.567 341.849ZM72.4301 365.835C72.757 365.68 72.1548 368.484 71.8967 369.792C71.8451 367.813 71.9483 366.058 72.4301 365.835ZM74.5121 381.94C74.6842 381.819 75.2003 382.508 75.7337 383.334C74.925 382.576 74.4089 382.009 74.4949 381.94H74.5121ZM76.5597 384.641C77.2996 385.897 77.6953 386.689Line truncated
</svg>

After

Width:  |  Height:  |  Size: 8.1 KiB

+10
View File
@@ -0,0 +1,10 @@
<svg width="71" height="55" viewBox="0 0 71 55" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0)">
<path d="M60.1045 4.8978C55.5792 2.8214 50.7265 1.2916 45.6527 0.41542C45.5603 0.39851 45.468 0.440769 45.4204 0.525289C44.7963 1.6353 44.105 3.0834 43.6209 4.2216C38.1637 3.4046 32.7345 3.4046 27.3892 4.2216C26.905 3.0581 26.1886 1.6353 25.5617 0.525289C25.5141 0.443589 25.4218 0.40133 25.3294 0.41542C20.2584 1.2888 15.4057 2.8186 10.8776 4.8978C10.8384 4.9147 10.8048 4.9429 10.7825 4.9795C1.57795 18.7309 -0.943561 32.1443 0.293408 45.3914C0.299005 45.4562 0.335386 45.5182 0.385761 45.5576C6.45866 50.0174 12.3413 52.7249 18.1147 54.5195C18.2071 54.5477 18.305 54.5139 18.3638 54.4378C19.7295 52.5728 20.9469 50.6063 21.9907 48.5383C22.0523 48.4172 21.9935 48.2735 21.8676 48.2256C19.9366 47.4931 18.0979 46.6 16.3292 45.5858C16.1893 45.5041 16.1781 45.304 16.3068 45.2082C16.679 44.9293 17.0513 44.6391 17.4067 44.3461C17.471 44.2926 17.5606 44.2813 17.6362 44.3151C29.2558 49.6202 41.8354 49.6202 53.3179 44.3151C53.3935 44.2785 53.4831 44.2898 53.5502 44.3433C53.9057 44.6363 54.2779 44.9293 54.6529 45.2082C54.7816 45.304 54.7732 45.5041 54.6333 45.5858C52.8646 46.6197 51.0259 47.4931 49.0921 48.2228C48.9662 48.2707 48.9102 48.4172 48.9718 48.5383C50.038 50.6034 51.2554 52.5699 52.5959 54.435C52.6519 54.5139 52.7526 54.5477 52.845 54.5195C58.6464 52.7249 64.529 50.0174 70.6019 45.5576C70.6551 45.5182 70.6887 45.459 70.6943 45.3942C72.1747 30.0791 68.2147 16.7757 60.1968 4.9823C60.1772 4.9429 60.1437 4.9147 60.1045 4.8978ZM23.7259 37.3253C20.2276 37.3253 17.3451 34.1136 17.3451 30.1693C17.3451 26.225 20.1717 23.0133 23.7259 23.0133C27.308 23.0133 30.1626 26.2532 30.1066 30.1693C30.1066 34.1136 27.28 37.3253 23.7259 37.3253ZM47.3178 37.3253C43.8196 37.3253 40.9371 34.1136 40.9371 30.1693C40.9371 26.225 43.7636 23.0133 47.3178 23.0133C50.9 23.0133 53.7545 26.2532 53.6986 30.1693C53.6986 34.1136 50.9 37.3253 47.3178 37.3253Z" fill="currentColor"/>
</g>
<defs>
<clipPath id="clip0">
<rect width="71" height="55" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

+6
View File
@@ -0,0 +1,6 @@
export { default as BuyMeACoffeeIcon } from './bmac.svg'
export { default as DiscordIcon } from './discord.svg'
export { default as KoFiIcon } from './kofi.svg'
export { default as PatreonIcon } from './patreon.svg'
export { default as PaypalIcon } from './paypal.svg'
export { default as OpenCollectiveIcon } from './opencollective.svg'
+1
View File
@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Ko-fi</title><path d="M23.881 8.948c-.773-4.085-4.859-4.593-4.859-4.593H.723c-.604 0-.679.798-.679.798s-.082 7.324-.022 11.822c.164 2.424 2.586 2.672 2.586 2.672s8.267-.023 11.966-.049c2.438-.426 2.683-2.566 2.658-3.734 4.352.24 7.422-2.831 6.649-6.916zm-11.062 3.511c-1.246 1.453-4.011 3.976-4.011 3.976s-.121.119-.31.023c-.076-.057-.108-.09-.108-.09-.443-.441-3.368-3.049-4.034-3.954-.709-.965-1.041-2.7-.091-3.71.951-1.01 3.005-1.086 4.363.407 0 0 1.565-1.782 3.468-.963 1.904.82 1.832 3.011.723 4.311zm6.173.478c-.928.116-1.682.028-1.682.028V7.284h1.77s1.971.551 1.971 2.638c0 1.913-.985 2.667-2.059 3.015z" fill="currentColor" /></svg>

After

Width:  |  Height:  |  Size: 718 B

+1
View File
@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Open Collective</title><path d="M12 0C5.373 0 0 5.373 0 12s5.373 12 12 12c2.54 0 4.894-.79 6.834-2.135l-3.107-3.109a7.715 7.715 0 1 1 0-13.512l3.107-3.109A11.943 11.943 0 0 0 12 0zm9.865 5.166l-3.109 3.107A7.67 7.67 0 0 1 19.715 12a7.682 7.682 0 0 1-.959 3.727l3.109 3.107A11.943 11.943 0 0 0 24 12c0-2.54-.79-4.894-2.135-6.834z"/></svg>

After

Width:  |  Height:  |  Size: 415 B

+1
View File
@@ -0,0 +1 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Patreon</title><path d="M0 .48v23.04h4.22V.48zm15.385 0c-4.764 0-8.641 3.88-8.641 8.65 0 4.755 3.877 8.623 8.641 8.623 4.75 0 8.615-3.868 8.615-8.623C24 4.36 20.136.48 15.385.48z" fill="currentColor"/></svg>

After

Width:  |  Height:  |  Size: 274 B

+1
View File
@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>PayPal</title><path d="M7.076 21.337H2.47a.641.641 0 0 1-.633-.74L4.944.901C5.026.382 5.474 0 5.998 0h7.46c2.57 0 4.578.543 5.69 1.81 1.01 1.15 1.304 2.42 1.012 4.287-.023.143-.047.288-.077.437-.983 5.05-4.349 6.797-8.647 6.797h-2.19c-.524 0-.968.382-1.05.9l-1.12 7.106zm14.146-14.42a3.35 3.35 0 0 0-.607-.541c-.013.076-.026.175-.041.254-.93 4.778-4.005 7.201-9.138 7.201h-2.19a.563.563 0 0 0-.556.479l-1.187 7.527h-.506l-.24 1.516a.56.56 0 0 0 .554.647h3.882c.46 0 .85-.334.922-.788.06-.26.76-4.852.816-5.09a.932.932 0 0 1 .923-.788h.58c3.76 0 6.705-1.528 7.565-5.946.36-1.847.174-3.388-.777-4.471z" fill="currentColor"/></svg>

After

Width:  |  Height:  |  Size: 706 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"></circle>
<line x1="15" y1="9" x2="9" y2="15"></line>
<line x1="9" y1="9" x2="15" y2="15"></line>
</svg>

After

Width:  |  Height:  |  Size: 324 B

@@ -1,11 +0,0 @@
@import url('https://rsms.me/inter/inter.css');
:root {
font-family: var(--font-standard);
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
@@ -0,0 +1,153 @@
@import url('https://rsms.me/inter/inter.css');
:root {
font-family: var(--font-standard);
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.card-divider {
background-color: var(--color-button-bg);
border: none;
color: var(--color-button-bg);
height: 1px;
margin: var(--gap-sm) 0;
}
.no-wrap {
white-space: nowrap;
}
a {
color: var(--color-link);
text-decoration: none;
&:hover {
text-decoration: none;
}
}
.multiselect {
color: var(--color-base) !important;
outline: 2px solid transparent;
.multiselect__input:focus-visible {
outline: none !important;
box-shadow: none !important;
padding: 0 !important;
min-height: 0 !important;
font-weight: normal !important;
margin-left: 0.5rem;
margin-bottom: 10px;
}
input {
background: transparent;
box-shadow: none;
border: none !important;
&:focus {
box-shadow: none;
}
}
input::placeholder {
color: var(--color-base);
}
.multiselect__tags {
border-radius: var(--radius-md);
background: var(--color-button-bg);
box-shadow: var(--shadow-inset-sm);
border: none;
cursor: pointer;
padding-left: 0.5rem;
font-size: 1rem;
transition: background-color 0.1s ease-in-out;
&:active {
filter: brightness(1.25);
.multiselect__spinner {
filter: brightness(1.25);
}
}
.multiselect__single {
background: transparent;
}
.multiselect__tag {
border-radius: var(--radius-md);
color: var(--color-base);
background: transparent;
border: 2px solid var(--color-brand);
}
.multiselect__tag-icon {
background: transparent;
&:after {
color: var(--color-contrast);
}
}
.multiselect__placeholder {
color: var(--color-base);
margin-left: 0.5rem;
opacity: 0.6;
font-size: 1rem;
line-height: 1.25rem;
}
}
.multiselect__content-wrapper {
background: var(--color-button-bg);
border: none;
overflow-x: hidden;
box-shadow: var(--shadow-inset-sm), var(--shadow-floating);
width: 100%;
.multiselect__element {
.multiselect__option--highlight {
background: var(--color-button-bg);
filter: brightness(1.25);
color: var(--color-contrast);
}
.multiselect__option--selected {
background: var(--color-brand);
font-weight: bold;
color: var(--color-accent-contrast);
}
}
}
.multiselect__spinner {
background: var(--color-button-bg);
&:active {
filter: brightness(1.25);
}
}
&.multiselect--disabled {
background: none;
.multiselect__current,
.multiselect__select {
background: none;
}
}
}
.multiselect--above .multiselect__content-wrapper {
border-top: none !important;
border-top-left-radius: var(--radius-md) !important;
border-top-right-radius: var(--radius-md) !important;
}
+12
View File
@@ -0,0 +1,12 @@
export const releaseColor = (releaseType) => {
switch (releaseType) {
case 'release':
return 'green'
case 'beta':
return 'orange'
case 'alpha':
return 'red'
default:
return ''
}
}
+1 -1
View File
@@ -3,7 +3,7 @@ import router from '@/routes'
import App from '@/App.vue'
import { createPinia } from 'pinia'
import '../node_modules/omorphia/dist/style.css'
import '@/assets/stylesheets/global.css'
import '@/assets/stylesheets/global.scss'
const pinia = createPinia()
+3 -125
View File
@@ -269,7 +269,7 @@ const handleReset = async () => {
<section class="project-list display-mode--list instance-results" role="list">
<ProjectCard
v-for="result in searchStore.searchResults"
:id="result?.project_id"
:id="`${result?.project_id}/`"
:key="result?.project_id"
class="result-project-item"
:type="result?.project_type"
@@ -292,13 +292,12 @@ const handleReset = async () => {
),
]"
:project-type-display="result?.project_type"
project-type-url="instance"
project-type-url="project"
:server-side="result?.server_side"
:client-side="result?.client_side"
:show-updated-date="false"
:color="result?.color"
>
</ProjectCard>
/>
</section>
</div>
</div>
@@ -412,125 +411,4 @@ const handleReset = async () => {
}
}
}
.multiselect {
color: var(--color-base) !important;
outline: 2px solid transparent;
.multiselect__input:focus-visible {
outline: none !important;
box-shadow: none !important;
padding: 0 !important;
min-height: 0 !important;
font-weight: normal !important;
margin-left: 0.5rem;
margin-bottom: 10px;
}
input {
background: transparent;
box-shadow: none;
border: none !important;
&:focus {
box-shadow: none;
}
}
input::placeholder {
color: var(--color-base);
}
.multiselect__tags {
border-radius: var(--radius-md);
background: var(--color-button-bg);
box-shadow: var(--shadow-inset-sm);
border: none;
cursor: pointer;
padding-left: 0.5rem;
font-size: 1rem;
transition: background-color 0.1s ease-in-out;
&:active {
filter: brightness(1.25);
.multiselect__spinner {
filter: brightness(1.25);
}
}
.multiselect__single {
background: transparent;
}
.multiselect__tag {
border-radius: var(--radius-md);
color: var(--color-base);
background: transparent;
border: 2px solid var(--color-brand);
}
.multiselect__tag-icon {
background: transparent;
&:after {
color: var(--color-contrast);
}
}
.multiselect__placeholder {
color: var(--color-base);
margin-left: 0.5rem;
opacity: 0.6;
font-size: 1rem;
line-height: 1.25rem;
}
}
.multiselect__content-wrapper {
background: var(--color-button-bg);
border: none;
overflow-x: hidden;
box-shadow: var(--shadow-inset-sm), var(--shadow-floating);
width: 100%;
.multiselect__element {
.multiselect__option--highlight {
background: var(--color-button-bg);
filter: brightness(1.25);
color: var(--color-contrast);
}
.multiselect__option--selected {
background: var(--color-brand);
font-weight: bold;
color: var(--color-accent-contrast);
}
}
}
.multiselect__spinner {
background: var(--color-button-bg);
&:active {
filter: brightness(1.25);
}
}
&.multiselect--disabled {
background: none;
.multiselect__current,
.multiselect__select {
background: none;
}
}
}
.multiselect--above .multiselect__content-wrapper {
border-top: none !important;
border-top-left-radius: var(--radius-md) !important;
border-top-right-radius: var(--radius-md) !important;
}
</style>
+7 -2
View File
@@ -43,10 +43,10 @@
</Button>
</div>
<div class="table-cell table-text name-cell">
<span class="mod-text">
<router-link :to="`/project/${mod.slug}/`" class="mod-text">
<Avatar :src="mod.icon" />
{{ mod.name }}
</span>
</router-link>
</div>
<div class="table-cell table-text">{{ mod.version }}</div>
<div class="table-cell table-text">{{ mod.author }}</div>
@@ -71,6 +71,7 @@ export default {
mods: [
{
name: 'Fabric API',
slug: 'fabric-api',
icon: 'https://cdn.modrinth.com/data/P7dR8mSH/icon.png',
version: '0.76.0+1.19.4',
author: 'modmuss50',
@@ -80,6 +81,7 @@ export default {
},
{
name: 'Spirit',
slug: 'spirit',
icon: 'https://cdn.modrinth.com/data/b1LdOZlE/465598dc5d89f67fb8f8de6def21240fa35e3a54.png',
version: '2.2.4',
author: 'CodexAdrian',
@@ -88,6 +90,7 @@ export default {
},
{
name: 'Botarium',
slug: 'botarium',
icon: 'https://cdn.modrinth.com/data/2u6LRnMa/98b286b0d541ad4f9409e0af3df82ad09403f179.gif',
version: '2.0.5',
author: 'CodexAdrian',
@@ -97,6 +100,7 @@ export default {
},
{
name: 'Tempad',
slug: 'tempad',
icon: 'https://cdn.modrinth.com/data/gKNwt7xu/icon.gif',
version: '2.2.4',
author: 'CodexAdrian',
@@ -105,6 +109,7 @@ export default {
},
{
name: 'Sodium',
slug: 'sodium',
icon: 'https://cdn.modrinth.com/data/AANobbMI/icon.png',
version: '0.4.10',
author: 'jellysquid3',
@@ -0,0 +1,11 @@
<template>
<div></div>
</template>
<script>
export default {
name: 'Changelog',
}
</script>
<style scoped></style>
@@ -0,0 +1,37 @@
<template>
<Card>
<div class="markdown-body" v-html="renderHighlightedString(project.body)" />
</Card>
</template>
<script setup>
import { Card, renderHighlightedString } from 'omorphia'
defineProps({
project: {
type: Object,
default: () => {},
},
})
</script>
<script>
export default {
name: 'Description',
}
</script>
<style scoped lang="scss">
.markdown-body {
:deep(hr),
:deep(h1),
:deep(h2) {
max-width: max(60rem, 90%);
}
:deep(ul),
:deep(ol) {
margin-left: 2rem;
}
}
</style>
+296
View File
@@ -0,0 +1,296 @@
<template>
<div class="gallery">
<Card v-for="(image, index) in project.gallery" :key="image.url" class="gallery-item">
<a @click="expandImage(image, index)">
<img :src="image.url" :alt="image.title" class="gallery-image" />
</a>
<div class="gallery-body">
<h3>{{ image.title }}</h3>
{{ image.description }}
</div>
<span class="gallery-time">
<CalendarIcon />
{{
new Date(image.created).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
})
}}
</span>
</Card>
</div>
<div v-if="expandedGalleryItem" class="expanded-image-modal" @click="expandedGalleryItem = null">
<div class="content">
<img
class="image"
:class="{ 'zoomed-in': zoomedIn }"
:src="
expandedGalleryItem.url
? expandedGalleryItem.url
: 'https://cdn.modrinth.com/placeholder-banner.svg'
"
:alt="expandedGalleryItem.title ? expandedGalleryItem.title : 'gallery-image'"
@click.stop=""
/>
<div class="floating" @click.stop="">
<div class="text">
<h2 v-if="expandedGalleryItem.title">
{{ expandedGalleryItem.title }}
</h2>
<p v-if="expandedGalleryItem.description">
{{ expandedGalleryItem.description }}
</p>
</div>
<div class="controls">
<div class="buttons">
<Button class="close" icon-only @click="expandedGalleryItem = null">
<XIcon aria-hidden="true" />
</Button>
<a
class="open btn icon-only"
target="_blank"
:href="
expandedGalleryItem.url
? expandedGalleryItem.url
: 'https://cdn.modrinth.com/placeholder-banner.svg'
"
>
<ExternalIcon aria-hidden="true" />
</a>
<Button icon-only @click="zoomedIn = !zoomedIn">
<ExpandIcon v-if="!zoomedIn" aria-hidden="true" />
<ContractIcon v-else aria-hidden="true" />
</Button>
<Button
v-if="project.gallery.length > 1"
class="previous"
icon-only
@click="previousImage()"
>
<LeftArrowIcon aria-hidden="true" />
</Button>
<Button v-if="project.gallery.length > 1" class="next" icon-only @click="nextImage()">
<RightArrowIcon aria-hidden="true" />
</Button>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import {
Card,
ExpandIcon,
RightArrowIcon,
LeftArrowIcon,
ExternalIcon,
ContractIcon,
XIcon,
CalendarIcon,
Button,
} from 'omorphia'
import { ref } from 'vue'
const props = defineProps({
project: {
type: Object,
default: () => {},
},
})
let expandedGalleryItem = ref(null)
let expandedGalleryIndex = ref(0)
let zoomedIn = ref(false)
const nextImage = () => {
expandedGalleryIndex.value++
if (expandedGalleryIndex.value >= props.project.gallery.length) {
expandedGalleryIndex.value = 0
}
expandedGalleryItem.value = props.project.gallery[expandedGalleryIndex.value]
}
const previousImage = () => {
expandedGalleryIndex.value--
if (expandedGalleryIndex.value < 0) {
expandedGalleryIndex.value = props.project.gallery.length - 1
}
expandedGalleryItem.value = props.project.gallery[expandedGalleryIndex.value]
}
const expandImage = (item, index) => {
expandedGalleryItem.value = item
expandedGalleryIndex.value = index
zoomedIn.value = false
}
</script>
<style scoped lang="scss">
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
width: 100%;
gap: 1rem;
}
.gallery-item {
padding: 0;
overflow: hidden;
margin: 0;
display: flex;
flex-direction: column;
.gallery-image {
width: 100%;
aspect-ratio: 2/1;
object-fit: cover;
object-position: center;
}
.gallery-body {
flex-grow: 1;
padding: 1rem;
}
.gallery-time {
padding: 0 1rem 1rem;
vertical-align: center;
}
}
.expanded-image-modal {
position: fixed;
z-index: 20;
overflow: auto;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: #000000;
background-color: rgba(0, 0, 0, 0.7);
display: flex;
justify-content: center;
align-items: center;
.content {
position: relative;
width: calc(100vw - 2 * var(--gap-lg));
height: calc(100vh - 2 * var(--gap-lg));
.circle-button {
padding: 0.5rem;
line-height: 1;
display: flex;
max-width: 2rem;
color: var(--color-button-text);
background-color: var(--color-button-bg);
border-radius: var(--size-rounded-max);
margin: 0;
box-shadow: inset 0px -1px 1px rgb(17 24 39 / 10%);
&:not(:last-child) {
margin-right: 0.5rem;
}
&:hover {
background-color: var(--color-button-bg-hover) !important;
svg {
color: var(--color-button-text-hover) !important;
}
}
&:active {
background-color: var(--color-button-bg-active) !important;
svg {
color: var(--color-button-text-active) !important;
}
}
svg {
height: 1rem;
width: 1rem;
}
}
.image {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
max-width: calc(100vw - 2 * var(--gap-lg));
max-height: calc(100vh - 2 * var(--gap-lg));
border-radius: var(--radius-lg);
&.zoomed-in {
object-fit: cover;
width: auto;
height: calc(100vh - 2 * var(--gap-lg));
max-width: calc(100vw - 2 * var(--gap-lg));
}
}
.floating {
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: var(--gap-md);
display: flex;
flex-direction: column;
align-items: center;
gap: var(--gap-md);
transition: opacity 0.25s ease-in-out;
opacity: 1;
padding: 2rem 2rem 0 2rem;
&:not(&:hover) {
opacity: 0.4;
.text {
transform: translateY(2.5rem) scale(0.8);
opacity: 0;
}
.controls {
transform: translateY(0.25rem) scale(0.9);
}
}
.text {
display: flex;
flex-direction: column;
max-width: 40rem;
transition: opacity 0.25s ease-in-out, transform 0.25s ease-in-out;
text-shadow: 1px 1px 10px #000000d4;
margin-bottom: 0.25rem;
gap: 0.5rem;
h2 {
color: var(--dark-color-base);
font-size: 1.25rem;
text-align: center;
margin: 0;
}
p {
color: var(--dark-color-base);
margin: 0;
}
}
.controls {
background-color: var(--color-raised-bg);
padding: var(--gap-md);
border-radius: var(--radius-md);
transition: opacity 0.25s ease-in-out, transform 0.25s ease-in-out;
}
}
}
}
.buttons {
display: flex;
gap: 0.5rem;
}
</style>
+375
View File
@@ -0,0 +1,375 @@
<template>
<div class="root-container">
<div v-if="data" class="project-sidebar">
<Card class="sidebar-card">
<Avatar size="lg" :src="data.icon_url" />
<div class="instance-info">
<h2 class="name">{{ data.title }}</h2>
{{ data.description }}
</div>
<Categories
class="tags"
type=""
:categories="[
...categories.filter(
(cat) => data.categories.includes(cat.name) && cat.project_type === 'mod'
),
...loaders.filter(
(loader) =>
data.categories.includes(loader.name) &&
loader.supported_project_types?.includes('modpack')
),
]"
>
<EnvironmentIndicator
:type-only="moderation"
:client-side="data.client_side"
:server-side="data.server_side"
:type="data.project_type"
/>
</Categories>
<hr class="card-divider" />
<div class="button-group">
<Button color="primary" class="instance-button">
<DownloadIcon />
Install
</Button>
<a
:href="`https://modrinth.com/${data.project_type}/${data.slug}`"
rel="external"
target="_blank"
class="btn"
>
<ExternalIcon />
Website
</a>
</div>
<hr class="card-divider" />
<div class="stats">
<div class="stat">
<DownloadIcon aria-hidden="true" />
<p>
<strong>{{ formatNumber(data.downloads) }}</strong>
<span class="stat-label"> download<span v-if="data.downloads !== '1'">s</span></span>
</p>
</div>
<div class="stat">
<HeartIcon aria-hidden="true" />
<p>
<strong>{{ formatNumber(data.followers) }}</strong>
<span class="stat-label"> follower<span v-if="data.followers !== '1'">s</span></span>
</p>
</div>
<div class="stat date">
<CalendarIcon aria-hidden="true" />
<span
><span class="date-label">Created </span> {{ dayjs(data.published).fromNow() }}</span
>
</div>
<div class="stat date">
<UpdatedIcon aria-hidden="true" />
<span
><span class="date-label">Updated </span> {{ dayjs(data.updated).fromNow() }}</span
>
</div>
</div>
<hr class="card-divider" />
<div class="button-group">
<Button class="instance-button">
<ReportIcon />
Report
</Button>
<Button class="instance-button">
<HeartIcon />
Follow
</Button>
</div>
<hr class="card-divider" />
<div class="links">
<a
v-if="data.issues_url"
:href="data.issues_url"
class="title"
rel="noopener nofollow ugc"
>
<IssuesIcon aria-hidden="true" />
<span>Issues</span>
</a>
<a
v-if="data.source_url"
:href="data.source_url"
class="title"
rel="noopener nofollow ugc"
>
<CodeIcon aria-hidden="true" />
<span>Source</span>
</a>
<a v-if="data.wiki_url" :href="data.wiki_url" class="title" rel="noopener nofollow ugc">
<WikiIcon aria-hidden="true" />
<span>Wiki</span>
</a>
<a v-if="data.wiki_url" :href="data.wiki_url" class="title" rel="noopener nofollow ugc">
<DiscordIcon aria-hidden="true" />
<span>Discord</span>
</a>
<a
v-for="(donation, index) in data.donation_urls"
:key="index"
:href="donation.url"
rel="noopener nofollow ugc"
>
<BuyMeACoffeeIcon v-if="donation.id === 'bmac'" aria-hidden="true" />
<PatreonIcon v-else-if="donation.id === 'patreon'" aria-hidden="true" />
<KoFiIcon v-else-if="donation.id === 'ko-fi'" aria-hidden="true" />
<PaypalIcon v-else-if="donation.id === 'paypal'" aria-hidden="true" />
<OpenCollectiveIcon v-else-if="donation.id === 'open-collective'" aria-hidden="true" />
<HeartIcon v-else-if="donation.id === 'github'" />
<CoinsIcon v-else />
<span v-if="donation.id === 'bmac'">Buy Me a Coffee</span>
<span v-else-if="donation.id === 'patreon'">Patreon</span>
<span v-else-if="donation.id === 'paypal'">PayPal</span>
<span v-else-if="donation.id === 'ko-fi'">Ko-fi</span>
<span v-else-if="donation.id === 'github'">GitHub Sponsors</span>
<span v-else>Donate</span>
</a>
</div>
</Card>
</div>
<div v-if="data" class="content-container">
<Promotion />
<Card class="tabs">
<NavRow
v-if="data.gallery.length > 0"
:links="[
{
label: 'Description',
href: `/project/${$route.params.id}/`,
},
{
label: 'Versions',
href: `/project/${$route.params.id}/versions`,
},
{
label: 'Gallery',
href: `/project/${$route.params.id}/gallery`,
},
]"
/>
<NavRow
v-else
:links="[
{
label: 'Description',
href: `/project/${$route.params.id}/`,
},
{
label: 'Versions',
href: `/project/${$route.params.id}/versions`,
},
]"
/>
</Card>
<RouterView
:project="data"
:versions="versions"
:members="members"
:dependencies="dependencies"
/>
</div>
</div>
</template>
<script setup>
import {
Card,
Avatar,
Button,
DownloadIcon,
ReportIcon,
HeartIcon,
Categories,
EnvironmentIndicator,
UpdatedIcon,
CalendarIcon,
IssuesIcon,
WikiIcon,
Promotion,
NavRow,
CoinsIcon,
CodeIcon,
formatNumber,
ExternalIcon,
} from 'omorphia'
import {
BuyMeACoffeeIcon,
DiscordIcon,
PatreonIcon,
PaypalIcon,
KoFiIcon,
OpenCollectiveIcon,
} from '@/assets/external'
import { get_categories, get_loaders } from '@/helpers/tags'
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
import { ofetch } from 'ofetch'
import { useRoute, useRouter } from 'vue-router'
import { ref, shallowRef, watch } from 'vue'
const route = useRoute()
const router = useRouter()
const loaders = ref(await get_loaders())
const categories = ref(await get_categories())
const [data, versions, members, dependencies] = await Promise.all([
ofetch(`https://api.modrinth.com/v2/project/${route.params.id}`).then(shallowRef),
ofetch(`https://api.modrinth.com/v2/project/${route.params.id}/version`).then(shallowRef),
ofetch(`https://api.modrinth.com/v2/project/${route.params.id}/members`).then(shallowRef),
ofetch(`https://api.modrinth.com/v2/project/${route.params.id}/dependencies`).then(shallowRef),
])
watch(
() => route.params.id,
() => {
router.go()
}
)
dayjs.extend(relativeTime)
</script>
<style scoped lang="scss">
.root-container {
display: flex;
flex-direction: row;
min-height: 100%;
}
.project-sidebar {
width: 20rem;
min-width: 20rem;
background: var(--color-raised-bg);
padding: 1rem;
}
.sidebar-card {
display: flex;
flex-direction: column;
gap: 1rem;
background-color: var(--color-bg);
}
.content-container {
display: flex;
flex-direction: column;
width: 100%;
padding: 1rem;
}
.button-group {
display: flex;
flex-wrap: wrap;
flex-direction: row;
gap: 0.5rem;
}
.stats {
display: flex;
flex-direction: column;
flex-wrap: wrap;
gap: var(--gap-md);
.stat {
display: flex;
flex-direction: row;
align-items: center;
width: fit-content;
gap: var(--gap-xs);
--stat-strong-size: 1.25rem;
strong {
font-size: var(--stat-strong-size);
}
p {
margin: 0;
}
svg {
min-height: var(--stat-strong-size);
min-width: var(--stat-strong-size);
}
}
.date {
margin-top: auto;
}
}
.tabs {
display: flex;
flex-direction: row;
gap: 1rem;
margin-bottom: var(--gap-md);
justify-content: space-between;
.tab {
display: flex;
flex-direction: row;
align-items: center;
border-radius: var(--border-radius);
cursor: pointer;
transition: background-color 0.2s ease-in-out;
&:hover {
background-color: var(--color-raised-bg);
}
&.router-view-active {
background-color: var(--color-raised-bg);
}
}
}
.links {
a {
display: inline-flex;
align-items: center;
border-radius: 1rem;
color: var(--color-text);
svg,
img {
height: 1rem;
width: 1rem;
}
span {
margin-left: 0.25rem;
text-decoration: underline;
line-height: 2rem;
}
&:focus-visible,
&:hover {
svg,
img,
span {
color: var(--color-heading);
}
}
&:active {
svg,
img,
span {
color: var(--color-text-dark);
}
}
&:not(:last-child)::after {
content: '•';
margin: 0 0.25rem;
}
}
}
</style>
+385
View File
@@ -0,0 +1,385 @@
<template>
<div>
<Card>
<div class="version-title">
<h2>{{ version.name }}</h2>
<span v-if="version.featured">Auto-Featured</span>
</div>
<div class="button-group">
<Button color="primary">
<DownloadIcon />
Install
</Button>
<Button :link="`/project/${route.params.id}/versions`">
<LeftArrowIcon />
Back to list
</Button>
<Button>
<ReportIcon />
Report
</Button>
<a
:href="`https://modrinth.com/mod/${route.params.id}/version/${route.params.version}`"
rel="external"
target="_blank"
class="btn"
>
<ExternalIcon />
Modrinth Website
</a>
</div>
</Card>
<div class="version-container">
<div class="description-cards">
<Card>
<h3 class="card-title">Changelog</h3>
<div class="markdown-body" v-html="renderString(version.changelog ?? '')" />
</Card>
<Card>
<h3 class="card-title">Files</h3>
<Card
v-for="file in version.files"
:key="file.id"
:class="{ primary: file.primary }"
class="file"
>
<span class="label">
<FileIcon />
<span>
<span class="title">
{{ file.filename }}
</span>
({{ formatBytes(file.size) }})
<span v-if="file.primary" class="primary-label"> Primary </span>
</span>
</span>
<Button class="download">
<DownloadIcon />
Install
</Button>
</Card>
</Card>
<Card v-if="displayDependencies[0]">
<h2>Dependencies</h2>
<div v-for="dependency in displayDependencies" :key="dependency.title">
<router-link v-if="dependency.link" class="btn dependency" :to="dependency.link">
<Avatar size="sm" :src="dependency.icon" />
<div>
<span class="title"> {{ dependency.title }} </span> <br />
<span> {{ dependency.subtitle }} </span>
</div>
</router-link>
<div v-else class="dependency disabled" disabled="">
<Avatar size="sm" :src="dependency.icon" />
<div class="text">
<div class="title">{{ dependency.title }}</div>
<div>{{ dependency.subtitle }}</div>
</div>
</div>
</div>
</Card>
</div>
<Card class="metadata-card">
<h3 class="card-title">Metadata</h3>
<div class="metadata">
<div class="metadata-item">
<span class="metadata-label">Release Channel</span>
<span class="metadata-value"
><Badge
:color="releaseColor(version.version_type)"
:type="
version.version_type.charAt(0).toUpperCase() + version.version_type.slice(1)
"
/></span>
</div>
<div class="metadata-item">
<span class="metadata-label">Version Number</span>
<span class="metadata-value">{{ version.version_number }}</span>
</div>
<div class="metadata-item">
<span class="metadata-label">Loaders</span>
<span class="metadata-value">{{
version.loaders
.map((loader) => loader.charAt(0).toUpperCase() + loader.slice(1))
.join(', ')
}}</span>
</div>
<div class="metadata-item">
<span class="metadata-label">Game Versions</span>
<span class="metadata-value"> {{ version.game_versions.join(', ') }} </span>
</div>
<div class="metadata-item">
<span class="metadata-label">Downloads</span>
<span class="metadata-value">{{ version.downloads }}</span>
</div>
<div class="metadata-item">
<span class="metadata-label">Publication Date</span>
<span class="metadata-value">
{{
new Date(version.date_published).toLocaleString('en-US', {
month: 'long',
day: 'numeric',
year: 'numeric',
})
}}
at
{{
new Date(version.date_published).toLocaleString('en-US', {
hour: 'numeric',
minute: 'numeric',
second: 'numeric',
hour12: true,
})
}}
</span>
</div>
<div v-if="author" class="metadata-item">
<span class="metadata-label">Author</span>
<a
:href="`https://modrinth.com/user/${author.user.username}`"
rel="external"
target="_blank"
class="metadata-value btn author"
>
<Avatar size="sm" :src="author.user.avatar_url" circle />
<span>
<strong>
{{ author.user.username }}
</strong>
<br />
{{ author.role }}
</span>
</a>
</div>
<div class="metadata-item">
<span class="metadata-label">Version ID</span>
<span class="metadata-value"><CopyCode class="copycode" :text="version.id" /></span>
</div>
</div>
</Card>
</div>
</div>
</template>
<script setup>
import {
Card,
Button,
DownloadIcon,
FileIcon,
Avatar,
LeftArrowIcon,
ReportIcon,
Badge,
ExternalIcon,
CopyCode,
formatBytes,
renderString,
} from 'omorphia'
import { releaseColor } from '@/helpers/utils'
import { ref, defineProps } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
const props = defineProps({
versions: {
type: Array,
required: true,
},
dependencies: {
type: Object,
required: true,
},
members: {
type: Array,
required: true,
},
})
const version = ref(props.versions.find((version) => version.id === route.params.version))
const author = ref(props.members.find((member) => member.user.id === version.value.author_id))
const displayDependencies = ref(
version.value.dependencies.map((dependency) => {
const version = props.dependencies.versions.find((obj) => obj.id === dependency.version_id)
if (version) {
const project = props.dependencies.projects.find(
(obj) => obj.id === version.project_id || obj.id === dependency.project_id
)
return {
icon: project?.icon_url,
title: project?.title || project?.name,
subtitle: `Version ${version.version_number} is ${dependency.dependency_type}`,
link: `/project/${project.slug}/version/${version.id}`,
}
} else
return {
icon: null,
title: dependency.file_name,
subtitle: `Added via overrides`,
link: null,
}
})
)
</script>
<style scoped lang="scss">
.version-container {
display: flex;
flex-direction: row;
gap: 1rem;
}
.version-title {
margin-bottom: 1rem;
h2 {
font-size: var(--font-size-2xl);
font-weight: 700;
color: var(--color-contrast);
margin: 0;
}
}
.dependency {
display: flex;
padding: 0.5rem 1rem 0.5rem 0.5rem;
gap: 0.5rem;
background: var(--color-raised-bg);
color: var(--color-base);
width: 100%;
.title {
font-weight: bolder;
}
:deep(svg) {
margin-right: 0 !important;
}
}
.file {
display: flex;
flex-direction: row;
gap: 0.5rem;
background: var(--color-button-bg);
color: var(--color-base);
padding: 0.5rem 1rem;
.download {
margin-left: auto;
background-color: var(--color-raised-bg);
}
.label {
display: flex;
margin: auto 0 auto;
gap: 0.5rem;
.title {
font-weight: bolder;
word-break: break-all;
}
svg {
min-width: 1.1rem;
min-height: 1.1rem;
width: 1.1rem;
height: 1.1rem;
margin: auto 0;
}
.primary-label {
font-style: italic;
}
}
}
.primary {
background: var(--color-brand-highlight);
color: var(--color-contrast);
}
.button-group {
display: flex;
flex-wrap: wrap;
flex-direction: row;
gap: 0.5rem;
}
.card-title {
font-size: var(--font-size-lg);
color: var(--color-contrast);
margin: 0 0 0.5rem;
}
.description-cards {
width: 100%;
}
.metadata-card {
width: 20rem;
height: min-content;
}
.metadata {
display: flex;
flex-direction: column;
flex-wrap: wrap;
gap: 1rem;
.metadata-item {
display: flex;
flex-direction: column;
gap: 0.5rem;
.metadata-label {
font-weight: bold;
}
}
}
.author {
display: flex;
flex-direction: row;
gap: 0.5rem;
align-items: center;
text-decoration: none;
color: var(--color-base);
background: var(--color-raised-bg);
padding: 0.5rem;
width: 100%;
box-shadow: none;
}
.markdown-body {
:deep(hr),
:deep(h1),
:deep(h2),
img {
max-width: max(60rem, 90%) !important;
}
:deep(ul),
:deep(ol) {
margin-left: 2rem;
}
}
.copycode {
border: 0;
color: var(--color-contrast);
}
.disabled {
display: flex;
flex-direction: row;
vertical-align: center;
align-items: center;
cursor: not-allowed;
border-radius: var(--radius-lg);
.text {
filter: brightness(0.5);
}
}
</style>
+281
View File
@@ -0,0 +1,281 @@
<template>
<Card>
<div class="filter-header">
<div class="manage">
<multiselect
v-model="filterVersions"
:options="
versions
.flatMap((value) => value.loaders)
.filter((value, index, self) => self.indexOf(value) === index)
"
:multiple="true"
:searchable="true"
:show-no-results="false"
:close-on-select="false"
:clear-search-on-select="false"
:show-labels="false"
:selectable="() => versions.length <= 6"
placeholder="Filter loader..."
/>
<multiselect
v-model="filterLoader"
:options="
versions
.flatMap((value) => value.game_versions)
.filter((value, index, self) => self.indexOf(value) === index)
"
:multiple="true"
:searchable="true"
:show-no-results="false"
:close-on-select="false"
:clear-search-on-select="false"
:show-labels="false"
:selectable="() => versions.length <= 6"
placeholder="Filter versions..."
/>
</div>
<Checkbox
v-model="filterCompatible"
label="Only show compatible versions"
class="filter-checkbox"
/>
<Button
class="no-wrap clear-filters"
:disabled="!filterLoader && !filterVersions && !filterCompatible"
:action="clearFilters"
>
<CheckCircleIcon />
Clear Filters
</Button>
</div>
</Card>
<Card class="mod-card">
<div class="table-container">
<div class="table-row table-head">
<div class="table-cell table-text download-cell" />
<div class="name-cell table-cell table-text">Name</div>
<div class="table-cell table-text">Supports</div>
<div class="table-cell table-text">Stats</div>
</div>
<router-link
v-for="version in versions"
:key="version.id"
class="button-base table-row"
:to="`/project/${$route.params.id}/version/${version.id}`"
>
<div class="table-cell table-text">
<Button color="primary" icon-only>
<DownloadIcon />
</Button>
</div>
<div class="name-cell table-cell table-text">
<div class="version-link">
{{ version.name.charAt(0).toUpperCase() + version.name.slice(1) }}
<div class="version-badge">
<div class="channel-indicator">
<Badge
:color="releaseColor(version.version_type)"
:type="
version.version_type.charAt(0).toUpperCase() + version.version_type.slice(1)
"
/>
</div>
<div>
{{ version.version_number }}
</div>
</div>
</div>
</div>
<div class="table-cell table-text stacked-text">
<span>
{{
version.loaders.map((str) => str.charAt(0).toUpperCase() + str.slice(1)).join(', ')
}}
</span>
<span>
{{ version.game_versions.join(', ') }}
</span>
</div>
<div class="table-cell table-text stacked-text">
<div>
<span> Published on </span>
<strong>
{{
new Date(version.date_published).toLocaleDateString('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric',
})
}}
</strong>
</div>
<div>
<strong>
{{ formatNumber(version.downloads) }}
</strong>
<span> Downloads </span>
</div>
</div>
</router-link>
</div>
</Card>
</template>
<script setup>
import {
Card,
Button,
CheckCircleIcon,
Badge,
DownloadIcon,
Checkbox,
formatNumber,
} from 'omorphia'
import Multiselect from 'vue-multiselect'
import { releaseColor } from '@/helpers/utils'
import { ref } from 'vue'
let filterVersions = ref(null)
let filterLoader = ref(null)
let filterCompatible = ref(false)
const clearFilters = () => {
filterVersions.value = null
filterLoader.value = null
filterCompatible.value = false
}
defineProps({
versions: {
type: Array,
required: true,
},
})
</script>
<style scoped lang="scss">
.filter-header {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
gap: 0.5rem;
}
.table-container {
display: grid;
grid-template-rows: repeat(auto-fill, auto);
width: 100%;
border-radius: var(--radius-md);
overflow: hidden;
}
.table-row {
display: grid;
grid-template-columns: min-content 1fr 1fr 1.5fr;
}
.table-head {
.table-cell {
background-color: var(--color-accent-contrast);
}
}
.table-cell {
padding: 1rem;
height: 100%;
align-items: center;
display: flex;
background-color: var(--color-raised-bg);
}
.name-cell {
padding-left: 0;
}
.table-text {
overflow: hidden;
white-space: nowrap;
text-overflow: fade;
}
.manage {
display: flex;
gap: 0.5rem;
flex-grow: 1;
.multiselect {
flex-grow: 1;
}
}
.mod-text {
display: flex;
align-items: center;
gap: 1rem;
color: var(--color-contrast);
}
.table-row:nth-child(even) .table-cell {
background-color: var(--color-bg);
}
.card-row {
display: flex;
align-items: center;
justify-content: space-between;
background-color: var(--color-raised-bg);
}
.mod-card {
display: flex;
flex-direction: column;
gap: 1rem;
overflow: hidden;
}
.text-combo {
display: flex;
align-items: center;
gap: 0.5rem;
}
.select {
width: 100% !important;
max-width: 20rem;
}
.version-link {
display: flex;
flex-direction: column;
gap: 0.25rem;
.version-badge {
display: flex;
flex-wrap: wrap;
.channel-indicator {
margin-right: 0.5rem;
}
}
}
.stacked-text {
display: flex;
flex-direction: column;
gap: 0.25rem;
align-items: flex-start;
}
.download-cell {
width: 4rem;
padding: 1rem;
}
.filter-checkbox {
:deep(.checkbox) {
border: none;
}
}
</style>
+7
View File
@@ -0,0 +1,7 @@
import Index from './Index.vue'
import Description from './Description.vue'
import Versions from './Versions.vue'
import Gallery from './Gallery.vue'
import Version from './Version.vue'
export { Index, Description, Versions, Gallery, Version }
+30
View File
@@ -1,5 +1,6 @@
import { createRouter, createWebHistory } from 'vue-router'
import * as Pages from '@/pages'
import * as Project from '@/pages/project'
import * as Instance from '@/pages/instance'
/**
@@ -38,6 +39,35 @@ export default new createRouter({
name: 'Settings',
component: Pages.Settings,
},
{
path: '/project/:id',
name: 'Project',
component: Project.Index,
props: true,
children: [
{
path: '',
name: 'Description',
component: Project.Description,
},
{
path: 'versions',
name: 'Versions',
component: Project.Versions,
},
{
path: 'version/:version',
name: 'Version',
component: Project.Version,
props: true,
},
{
path: 'gallery',
name: 'Gallery',
component: Project.Gallery,
},
],
},
{
path: '/instance/:id',
name: 'Instance',
+61 -4
View File
@@ -449,6 +449,20 @@ argparse@^2.0.1:
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
axios@^1.3.4:
version "1.3.4"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.4.tgz#f5760cefd9cfb51fd2481acf88c05f67c4523024"
integrity sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==
dependencies:
follow-redirects "^1.15.0"
form-data "^4.0.0"
proxy-from-env "^1.1.0"
balanced-match@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
@@ -519,6 +533,13 @@ color-name@~1.1.4:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
combined-stream@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
dependencies:
delayed-stream "~1.0.0"
commander@^2.20.3:
version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
@@ -614,6 +635,11 @@ deep-is@^0.1.3:
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
destr@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/destr/-/destr-1.2.2.tgz#7ba9befcafb645a50e76b260449c63927b51e22f"
@@ -879,6 +905,20 @@ floating-vue@^2.0.0-beta.20:
"@floating-ui/dom" "^0.1.10"
vue-resize "^2.0.0-alpha.1"
follow-redirects@^1.15.0:
version "1.15.2"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
form-data@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
mime-types "^2.1.12"
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
@@ -1120,6 +1160,18 @@ mdurl@^1.0.1:
resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==
mime-db@1.52.0:
version "1.52.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
mime-types@^2.1.12:
version "2.1.35"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
dependencies:
mime-db "1.52.0"
minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
@@ -1168,10 +1220,10 @@ ofetch@^1.0.1:
node-fetch-native "^1.0.2"
ufo "^1.1.0"
omorphia@^0.4.2:
version "0.4.3"
resolved "https://registry.yarnpkg.com/omorphia/-/omorphia-0.4.3.tgz#a247e2e15b77b4b5f55fe766f1c64419bb6fbc72"
integrity sha512-aGjtYhUrWNgjKM3HiL+MF5DdXfrllfDxql2mzu7JRzpjTB20xnksHKvU71x17FS9P1QXxumzADVF5v2rn9lb6A==
omorphia@^0.4.4:
version "0.4.4"
resolved "https://registry.yarnpkg.com/omorphia/-/omorphia-0.4.4.tgz#788e5953eb1ae81c8a65ce277a5086668acf50a4"
integrity sha512-HnIZahQVQmH6kHPzeb5iQTzgPhWu8VUwgNnCIUzWhudFqgB32NNUHNQW3seHgM8pmLSeIA3/2fjqkTrzK9EQvw==
dependencies:
dayjs "^1.11.7"
floating-vue "^2.0.0-beta.20"
@@ -1287,6 +1339,11 @@ prettier@^2.8.7:
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.7.tgz#bb79fc8729308549d28fe3a98fce73d2c0656450"
integrity sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==
proxy-from-env@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
punycode@^2.1.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f"