diff --git a/desktop/package.json b/desktop/package.json index f63286bb..144d59c1 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -46,6 +46,7 @@ "sass": "^1.77.6", "unocss": "^0.61.0", "vite-plugin-solid": "^2.10.2", + "vite-plugin-solid-svg": "^0.8.1", "vite-tsconfig-paths": "^4.3.2" } } diff --git a/desktop/src/assets/logos/discord.svg b/desktop/src/assets/logos/discord.svg new file mode 100644 index 00000000..e5e50cad --- /dev/null +++ b/desktop/src/assets/logos/discord.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/desktop/src/ui/components/base/Dropdown.tsx b/desktop/src/ui/components/base/Dropdown.tsx index c7626071..ab709fb2 100644 --- a/desktop/src/ui/components/base/Dropdown.tsx +++ b/desktop/src/ui/components/base/Dropdown.tsx @@ -1,5 +1,5 @@ -import { Index, type JSX, type ParentProps, children, createSignal, splitProps } from 'solid-js'; -import { ChevronDownIcon } from '@untitled-theme/icons-solid'; +import { type Accessor, Index, type JSX, Match, type ParentProps, type ResolvedJSXElement, type Setter, Switch, children, createSignal, splitProps } from 'solid-js'; +import { ChevronDownIcon, ChevronUpIcon } from '@untitled-theme/icons-solid'; import Popup from '../overlay/Popup'; import Button from './Button'; import styles from './Dropdown.module.scss'; @@ -8,10 +8,18 @@ export type DropdownProps = Omit, 'onChange'> onChange?: (selected: number) => any; text?: string; selected?: number; + dropdownClass?: string; + component?: ( + props: { + visible: Accessor; + setVisible: Setter; + children: () => ResolvedJSXElement[]; + } + ) => JSX.Element; }; function Dropdown(props: DropdownProps) { - const [split, rest] = splitProps(props, ['children', 'class', 'text', 'onChange', 'selected']); + const [split, rest] = splitProps(props, ['children', 'class', 'text', 'onChange', 'selected', 'component', 'dropdownClass']); const [visible, setVisible] = createSignal(false); // eslint-disable-next-line solid/reactivity -- todo @@ -29,27 +37,39 @@ function Dropdown(props: DropdownProps) { split.onChange(selected()); } - return ( -
-
- +
+ {split.text} + {items()[selected()]} +
+ + ) + ); + + return ( +
+ ref.classList.add('mt-1', 'w-full')} + ref={(ref) => { + ref.classList.add('mt-1', 'w-full'); + if (split.dropdownClass) + ref.classList.add(...split.dropdownClass.split(' ')); + }} >
@@ -78,4 +98,38 @@ Dropdown.Row = function (props: ParentProps) { ); }; +type MinimalDropdownProps = Omit & { + icon?: JSX.Element; +}; + +Dropdown.Minimal = function (props: MinimalDropdownProps) { + const [split, rest] = splitProps(props, ['icon']); + return ( + ( +
+
+ +
+ {/* eslint-disable-next-line ts/no-use-before-define -- blh */} + + {row => ( + <> + + + + + )} + +
+ +
+ ); +} + +const _modRowData: ModsRowProps[] = [ + { + header: 'Highly Endorsed', + category: 'endorsed', + mods: [ + { + name: 'EvergreenHUD', + description: 'Improves your heads up display.', + author: 'Polyfrost', + icon_url: 'https://cdn.modrinth.com/data/1yIQcc2b/icon.png', + page_url: 'https://modrinth.com/mod/evergreenhud', + provider: Provider.Modrinth, + downloads: 281700, + ratings: 220, + }, + ], + }, +]; + +interface ModsRowProps { + header: string; + category: string; + mods: Mod[]; +}; + +interface Mod { + name: string; + description: string; + author: string; + icon_url: string; + provider: Provider; + page_url: string; + downloads: number; + ratings: number; +} + +function ModsRow(props: ModsRowProps) { + return ( +
+
+

{props.header}

+ See all +
+ +
+ + {mod => } + +
+ +
+ ); +} + +function ModCard(props: Mod) { + return ( +
+
+ {`Icon + {`Icon +
+
+
+
{props.name}
+

+ By + {' '} + {props.author} + {' '} + on + {' '} + {Provider[props.provider]} +

+
+ +

{props.description}

+ +
+
+ + {abbreviateNumber(props.downloads)} +
+ +
+ + {abbreviateNumber(props.ratings)} +
+
); diff --git a/desktop/src/ui/pages/browser/BrowserRoot.tsx b/desktop/src/ui/pages/browser/BrowserRoot.tsx index c8cdde62..bf12b62b 100644 --- a/desktop/src/ui/pages/browser/BrowserRoot.tsx +++ b/desktop/src/ui/pages/browser/BrowserRoot.tsx @@ -1,11 +1,13 @@ import { Route } from '@solidjs/router'; import type { ParentProps } from 'solid-js'; import BrowserMain from './BrowserMain'; +import BrowserCategory from './BrowserCategory'; function BrowserRoutes() { return ( <> + ); } diff --git a/desktop/src/ui/pages/cluster/cluster/ClusterOverview.tsx b/desktop/src/ui/pages/cluster/cluster/ClusterOverview.tsx index 117fd65d..16d285ab 100644 --- a/desktop/src/ui/pages/cluster/cluster/ClusterOverview.tsx +++ b/desktop/src/ui/pages/cluster/cluster/ClusterOverview.tsx @@ -5,7 +5,7 @@ import Button from '../../../components/base/Button'; import ScrollableContainer from '~ui/components/ScrollableContainer'; import Sidebar from '~ui/components/Sidebar'; import type { Cluster } from '~bindings'; -import { upperFirst } from '~utils/string'; +import { upperFirst } from '~utils/primitives'; import useClusterContext from '~ui/hooks/useCluster'; function ClusterOverview() { diff --git a/desktop/src/ui/pages/settings/launcher/SettingsGeneral.tsx b/desktop/src/ui/pages/settings/launcher/SettingsGeneral.tsx index 1e8395bc..a4f1944b 100644 --- a/desktop/src/ui/pages/settings/launcher/SettingsGeneral.tsx +++ b/desktop/src/ui/pages/settings/launcher/SettingsGeneral.tsx @@ -6,6 +6,7 @@ import Toggle from '~ui/components/base/Toggle'; import Sidebar from '~ui/components/Sidebar'; import useSettingsContext from '~ui/hooks/useSettings'; import Button from '~ui/components/base/Button'; +import DiscordIcon from '~assets/logos/discord.svg?component-solid'; function SettingsGeneral() { const settings = useSettingsContext(); @@ -18,7 +19,7 @@ function SettingsGeneral() { } + icon={} > = 1e3 && n < 1e6) + return `${+(n / 1e3).toFixed(1)}K`; + if (n >= 1e6 && n < 1e9) + return `${+(n / 1e6).toFixed(1)}M`; + if (n >= 1e9 && n < 1e12) + return `${+(n / 1e9).toFixed(1)}B`; + if (n >= 1e12) + return `${+(n / 1e12).toFixed(1)}T`; +}; + +export default { + upperFirst, + abbreviateNumber, +}; diff --git a/desktop/src/utils/string.ts b/desktop/src/utils/string.ts deleted file mode 100644 index 7eba83a5..00000000 --- a/desktop/src/utils/string.ts +++ /dev/null @@ -1,8 +0,0 @@ -export function upperFirst(object: any): string { - const str = object.toString(); - return str.charAt(0).toUpperCase() + str.slice(1); -} - -export default { - upperFirst, -}; diff --git a/desktop/tsconfig.json b/desktop/tsconfig.json index 68a81e39..a6820e81 100644 --- a/desktop/tsconfig.json +++ b/desktop/tsconfig.json @@ -14,7 +14,11 @@ }, "resolveJsonModule": true, - "types": ["vitest/globals", "@testing-library/jest-dom"], + "types": [ + "vitest/globals", + "@testing-library/jest-dom", + "vite-plugin-solid-svg/types" + ], "allowImportingTsExtensions": true, "strict": true, "noFallthroughCasesInSwitch": true, diff --git a/desktop/vite.config.ts b/desktop/vite.config.ts index b3411d90..84d48ac6 100644 --- a/desktop/vite.config.ts +++ b/desktop/vite.config.ts @@ -5,6 +5,7 @@ import type { Plugin, UserConfig } from 'vite'; import { defineConfig, loadEnv } from 'vite'; import solid from 'vite-plugin-solid'; +import solidSvg from 'vite-plugin-solid-svg'; import paths from 'vite-tsconfig-paths'; import unocss from 'unocss/vite'; @@ -15,6 +16,9 @@ export default defineConfig(async ({ mode }) => { plugins: [ unocss(), solid(), + solidSvg({ + defaultAsComponent: false, + }), paths(), devtools(), ], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 48b62fa4..cf38e9f9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -162,6 +162,9 @@ importers: vite-plugin-solid: specifier: ^2.10.2 version: 2.10.2(@testing-library/jest-dom@6.4.6(vitest@1.6.0(@types/node@20.14.3)(@vitest/ui@1.6.0(vitest@1.6.0))(jsdom@24.1.0)(sass@1.77.6)))(solid-js@1.8.17)(vite@5.3.1(@types/node@20.14.3)(sass@1.77.6)) + vite-plugin-solid-svg: + specifier: ^0.8.1 + version: 0.8.1(solid-js@1.8.17)(vite@5.3.1(@types/node@20.14.3)(sass@1.77.6)) vite-tsconfig-paths: specifier: ^4.3.2 version: 4.3.2(typescript@5.4.5)(vite@5.3.1(@types/node@20.14.3)(sass@1.77.6)) @@ -1116,6 +1119,10 @@ packages: peerDependencies: solid-js: ^1.5 + '@trysound/sax@0.2.0': + resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} + engines: {node: '>=10.13.0'} + '@tsconfig/node10@1.0.11': resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} @@ -1588,6 +1595,10 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + comment-parser@1.4.1: resolution: {integrity: sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==} engines: {node: '>= 12.0.0'} @@ -1615,10 +1626,21 @@ packages: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} + css-select@5.1.0: + resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + + css-tree@2.2.1: + resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + css-tree@2.3.1: resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + css-what@6.1.0: + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + engines: {node: '>= 6'} + css.escape@1.5.1: resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} @@ -1627,6 +1649,10 @@ packages: engines: {node: '>=4'} hasBin: true + csso@5.0.5: + resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + cssstyle@4.0.1: resolution: {integrity: sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==} engines: {node: '>=18'} @@ -1710,6 +1736,19 @@ packages: dom-accessibility-api@0.6.3: resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@3.1.0: + resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} + dotenv@16.4.5: resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} engines: {node: '>=12'} @@ -2409,6 +2448,9 @@ packages: mdast-util-to-string@2.0.0: resolution: {integrity: sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==} + mdn-data@2.0.28: + resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} + mdn-data@2.0.30: resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} @@ -2962,6 +3004,11 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + svgo@3.3.2: + resolution: {integrity: sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==} + engines: {node: '>=14.0.0'} + hasBin: true + symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} @@ -3159,6 +3206,12 @@ packages: engines: {node: ^18.0.0 || >=20.0.0} hasBin: true + vite-plugin-solid-svg@0.8.1: + resolution: {integrity: sha512-ROGC2ae1eYUCMd+zfJtsbUtuZwsb6DZS0+Sy5/ZXDokOunGi0Ez/cL7OPdsixN3I0/rNYd/3hilo3kpRMAS+IA==} + peerDependencies: + solid-js: ^1 + vite: '>=4' + vite-plugin-solid@2.10.2: resolution: {integrity: sha512-AOEtwMe2baBSXMXdo+BUwECC8IFHcKS6WQV/1NEd+Q7vHPap5fmIhLcAzr+DUJ04/KHx/1UBU0l1/GWP+rMAPQ==} peerDependencies: @@ -4276,6 +4329,8 @@ snapshots: dependencies: solid-js: 1.8.17 + '@trysound/sax@0.2.0': {} + '@tsconfig/node10@1.0.11': {} '@tsconfig/node12@1.0.11': {} @@ -4752,8 +4807,7 @@ snapshots: binary-extensions@2.3.0: {} - boolbase@1.0.0: - optional: true + boolbase@1.0.0: {} brace-expansion@1.1.11: dependencies: @@ -4902,6 +4956,8 @@ snapshots: dependencies: delayed-stream: 1.0.0 + commander@7.2.0: {} + comment-parser@1.4.1: {} concat-map@0.0.1: {} @@ -4924,16 +4980,35 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + css-select@5.1.0: + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 5.0.3 + domutils: 3.1.0 + nth-check: 2.1.1 + + css-tree@2.2.1: + dependencies: + mdn-data: 2.0.28 + source-map-js: 1.2.0 + css-tree@2.3.1: dependencies: mdn-data: 2.0.30 source-map-js: 1.2.0 + css-what@6.1.0: {} + css.escape@1.5.1: {} cssesc@3.0.0: optional: true + csso@5.0.5: + dependencies: + css-tree: 2.2.1 + cssstyle@4.0.1: dependencies: rrweb-cssom: 0.6.0 @@ -4989,6 +5064,24 @@ snapshots: dom-accessibility-api@0.6.3: {} + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@2.3.0: {} + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@3.1.0: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + dotenv@16.4.5: {} duplexer@0.1.2: {} @@ -5803,6 +5896,8 @@ snapshots: mdast-util-to-string@2.0.0: {} + mdn-data@2.0.28: {} + mdn-data@2.0.30: {} merge-anything@5.1.7: @@ -5915,7 +6010,6 @@ snapshots: nth-check@2.1.1: dependencies: boolbase: 1.0.0 - optional: true nwsapi@2.2.10: {} @@ -6340,6 +6434,16 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + svgo@3.3.2: + dependencies: + '@trysound/sax': 0.2.0 + commander: 7.2.0 + css-select: 5.1.0 + css-tree: 2.3.1 + css-what: 6.1.0 + csso: 5.0.5 + picocolors: 1.0.1 + symbol-tree@3.2.4: {} synckit@0.6.2: @@ -6548,6 +6652,12 @@ snapshots: - supports-color - terser + vite-plugin-solid-svg@0.8.1(solid-js@1.8.17)(vite@5.3.1(@types/node@20.14.3)(sass@1.77.6)): + dependencies: + solid-js: 1.8.17 + svgo: 3.3.2 + vite: 5.3.1(@types/node@20.14.3)(sass@1.77.6) + vite-plugin-solid@2.10.2(@testing-library/jest-dom@6.4.6(vitest@1.6.0(@types/node@20.14.3)(@vitest/ui@1.6.0(vitest@1.6.0))(jsdom@24.1.0)(sass@1.77.6)))(solid-js@1.8.17)(vite@5.3.1(@types/node@20.14.3)(sass@1.77.6)): dependencies: '@babel/core': 7.24.4