From 6200c561694d74788e723f569b0dd8026afe2a02 Mon Sep 17 00:00:00 2001 From: Oksamies Date: Wed, 10 Sep 2025 23:18:26 +0300 Subject: [PATCH 1/2] Uh, more skeletons? And some improvements to perceived performance --- apps/cyberstorm-remix/app/c/community.tsx | 10 +- .../c/tabs/PackageSearch/PackageSearch.tsx | 13 +- .../CheckboxList/CheckboxList.css | 48 ++- .../CheckboxList/CheckboxList.tsx | 64 ++-- .../Collapsible/Collapsible.css | 3 +- .../PackageSearch/PackageSearch.css | 20 + .../PackageSearch/PackageSearch.tsx | 160 ++++---- .../app/p/dependants/Dependants.tsx | 347 +++++++----------- apps/cyberstorm-remix/app/p/packageEdit.tsx | 41 --- apps/cyberstorm-remix/app/p/team/Team.tsx | 244 +++++------- apps/cyberstorm-remix/app/root.tsx | 293 +++++++++------ apps/cyberstorm-remix/app/routes.ts | 19 +- .../manifest-validator/manifestValidator.tsx | 6 - .../markdown-preview/markdownPreview.tsx | 7 +- .../package-format-docs/packageFormatDocs.tsx | 12 +- apps/cyberstorm-remix/app/upload/upload.tsx | 6 - 16 files changed, 635 insertions(+), 658 deletions(-) diff --git a/apps/cyberstorm-remix/app/c/community.tsx b/apps/cyberstorm-remix/app/c/community.tsx index ade0f3bcf..2a8041fce 100644 --- a/apps/cyberstorm-remix/app/c/community.tsx +++ b/apps/cyberstorm-remix/app/c/community.tsx @@ -39,8 +39,7 @@ export async function loader({ params }: LoaderFunctionArgs) { sessionId: undefined, }; }); - const community = dapper.getCommunity(params.communityId); - + const community = await dapper.getCommunity(params.communityId); return { community: community, }; @@ -236,7 +235,12 @@ export default function Community() { - + diff --git a/apps/cyberstorm-remix/app/c/tabs/PackageSearch/PackageSearch.tsx b/apps/cyberstorm-remix/app/c/tabs/PackageSearch/PackageSearch.tsx index 2cf8be887..71b1b0a0c 100644 --- a/apps/cyberstorm-remix/app/c/tabs/PackageSearch/PackageSearch.tsx +++ b/apps/cyberstorm-remix/app/c/tabs/PackageSearch/PackageSearch.tsx @@ -1,4 +1,3 @@ -import type { LoaderFunctionArgs } from "react-router"; import { useLoaderData, useOutletContext } from "react-router"; import { PackageSearch } from "~/commonComponents/PackageSearch/PackageSearch"; import { PackageOrderOptions } from "~/commonComponents/PackageSearch/components/PackageOrder"; @@ -8,8 +7,9 @@ import { getSessionTools, } from "cyberstorm/security/publicEnvVariables"; import { OutletContextShape } from "~/root"; +import { Route } from "./+types/PackageSearch"; -export async function loader({ request, params }: LoaderFunctionArgs) { +export async function loader({ params, request }: Route.LoaderArgs) { if (params.communityId) { const publicEnvVariables = getPublicEnvVariables(["VITE_API_URL"]); const dapper = new DapperTs(() => { @@ -28,11 +28,11 @@ export async function loader({ request, params }: LoaderFunctionArgs) { const section = searchParams.get("section"); const nsfw = searchParams.get("nsfw"); const deprecated = searchParams.get("deprecated"); - const filters = dapper.getCommunityFilters(params.communityId); + const filters = await dapper.getCommunityFilters(params.communityId); return { filters: filters, - listings: dapper.getPackageListings( + listings: await dapper.getPackageListings( { kind: "community", communityId: params.communityId, @@ -51,7 +51,10 @@ export async function loader({ request, params }: LoaderFunctionArgs) { throw new Response("Community not found", { status: 404 }); } -export async function clientLoader({ request, params }: LoaderFunctionArgs) { +export async function clientLoader({ + request, + params, +}: Route.ClientLoaderArgs) { if (params.communityId) { const tools = getSessionTools(); const dapper = new DapperTs(() => { diff --git a/apps/cyberstorm-remix/app/commonComponents/CheckboxList/CheckboxList.css b/apps/cyberstorm-remix/app/commonComponents/CheckboxList/CheckboxList.css index 25af9390e..f5f80bba8 100644 --- a/apps/cyberstorm-remix/app/commonComponents/CheckboxList/CheckboxList.css +++ b/apps/cyberstorm-remix/app/commonComponents/CheckboxList/CheckboxList.css @@ -16,30 +16,49 @@ transition: var(--animation-duration-xs); user-select: none; + > span { + display: inline-flex; + flex-wrap: nowrap; + gap: 0.75rem; + align-items: center; + } + &.checkbox-list__label--off { color: var(--color-text-secondary); svg { --icon-color: var(--color-surface-a8); } + + .checkbox-list__exclude-button svg { + --icon-color: transparent; + } } &.checkbox-list__label--include { color: var(--color-cyber-green-7); font-weight: var(--font-weight-bold); - svg { + .checkbox-list__checkbox-button svg { --icon-color: var(--color-cyber-green-7); } + + .checkbox-list__exclude-button svg { + --icon-color: transparent; + } } &.checkbox-list__label--exclude { color: var(--color-accent-red-7); font-weight: var(--font-weight-bold); - svg { + .checkbox-list__exclude-button svg { --icon-color: var(--color-accent-red-7); } + + .checkbox-list__checkbox-button svg { + --icon-color: var(--color-surface-a8); + } } &:hover { @@ -57,22 +76,30 @@ &.checkbox-list__label--include { color: var(--color-cyber-green-8); - svg { + .checkbox-list__checkbox-button svg { --icon-color: var(--color-cyber-green-8); } + + .checkbox-list__exclude-button svg { + --icon-color: var(--color-accent-red-8); + } } &.checkbox-list__label--exclude { color: var(--color-accent-red-8); - svg { + .checkbox-list__checkbox-button svg { + --icon-color: var(--color-surface-a10); + } + + .checkbox-list__exclude-button svg { --icon-color: var(--color-accent-red-8); } } } } - .checkbox-list__checkbox { + .checkbox-list__icon { display: flex; align-items: center; justify-content: center; @@ -83,6 +110,17 @@ background-color: transparent; transition: ease-out var(--animation-duration-xs); + + &.checkbox-list__exclude-button { + --icon-inline-size: var(--space-16); + + width: var(--space-20); + height: var(--space-20); + + &:hover { + --icon-color: var(--color-accent-red-8); + } + } } } } diff --git a/apps/cyberstorm-remix/app/commonComponents/CheckboxList/CheckboxList.tsx b/apps/cyberstorm-remix/app/commonComponents/CheckboxList/CheckboxList.tsx index 1e7ef018d..468b8e062 100644 --- a/apps/cyberstorm-remix/app/commonComponents/CheckboxList/CheckboxList.tsx +++ b/apps/cyberstorm-remix/app/commonComponents/CheckboxList/CheckboxList.tsx @@ -1,12 +1,12 @@ import { + faBan, faSquare, faSquareCheck, - faSquareXmark, } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import "./CheckboxList.css"; -import { CycleButton, NewIcon } from "@thunderstore/cyberstorm"; +import { Actionable, NewIcon } from "@thunderstore/cyberstorm"; import { classnames } from "@thunderstore/cyberstorm/src/utils/utils"; import { resolveTriState } from "~/commonComponents/utils"; import { TRISTATE } from "~/commonComponents/types"; @@ -45,28 +45,48 @@ export const CheckboxList = memo(function CheckboxList(props: Props) { "checkbox-list__label", `checkbox-list__label--${cs}` )} + htmlFor={`checkbox-list__checkbox__${item.label}`} > - {item.label} - { - item.setStateFunc(nextStateResolve(item.state)); - }} - rootClasses="checkbox-list__checkbox" - value={`checkbox-list__label--${cs}`} - noState - > - - + { + if (typeof item.state === "string") { + item.setStateFunc( + item.state !== "include" ? "include" : "off" + ); + } else { + item.setStateFunc(!item.state); } - /> - - + }} + rootClasses="checkbox-list__icon checkbox-list__checkbox-button" + id={`checkbox-list__checkbox__${item.label}`} + > + + + + + {item.label} + + {typeof item.state === "string" && ( + { + if (item.state === "exclude") { + item.setStateFunc("off"); + } else { + item.setStateFunc("exclude"); + } + }} + rootClasses="checkbox-list__icon checkbox-list__exclude-button" + > + + + + + )} ); diff --git a/apps/cyberstorm-remix/app/commonComponents/Collapsible/Collapsible.css b/apps/cyberstorm-remix/app/commonComponents/Collapsible/Collapsible.css index 126fd96de..1099eb8d4 100644 --- a/apps/cyberstorm-remix/app/commonComponents/Collapsible/Collapsible.css +++ b/apps/cyberstorm-remix/app/commonComponents/Collapsible/Collapsible.css @@ -3,8 +3,7 @@ display: flex; flex: 1; flex-direction: column; - align-items: flex-start; - align-self: stretch; + align-items: stretch; padding: 0.5rem; border-radius: var(--Radius-md, 0.5rem); font-size: var(--font-size-body-md); diff --git a/apps/cyberstorm-remix/app/commonComponents/PackageSearch/PackageSearch.css b/apps/cyberstorm-remix/app/commonComponents/PackageSearch/PackageSearch.css index d16f40b92..55c096871 100644 --- a/apps/cyberstorm-remix/app/commonComponents/PackageSearch/PackageSearch.css +++ b/apps/cyberstorm-remix/app/commonComponents/PackageSearch/PackageSearch.css @@ -51,6 +51,12 @@ align-self: stretch; justify-content: center; padding-top: var(--space-40); + + > .skeleton { + width: 100%; + max-width: 20rem; + height: 2.5rem; + } } .package-search__search-params { @@ -104,6 +110,12 @@ flex: 1; gap: var(--gap-xxs); align-items: center; + justify-content: flex-end; + + .skeleton { + width: 18ch; + height: 1rem; + } } .package-search__packages { @@ -120,6 +132,10 @@ grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr)); gap: var(--gap-sm); width: 100%; + + > .skeleton { + height: 30rem; + } } @media (width <= 48rem) { @@ -165,6 +181,10 @@ .package-search__grid { grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr)); + + > .skeleton { + height: 28rem; + } } .package-search__content { diff --git a/apps/cyberstorm-remix/app/commonComponents/PackageSearch/PackageSearch.tsx b/apps/cyberstorm-remix/app/commonComponents/PackageSearch/PackageSearch.tsx index 6ebe0e050..c063cd317 100644 --- a/apps/cyberstorm-remix/app/commonComponents/PackageSearch/PackageSearch.tsx +++ b/apps/cyberstorm-remix/app/commonComponents/PackageSearch/PackageSearch.tsx @@ -5,7 +5,7 @@ import { PackageListings, Section, } from "@thunderstore/dapper/types"; -import { Suspense, useEffect, useRef, useState } from "react"; +import { memo, Suspense, useEffect, useRef, useState } from "react"; import { useDebounce } from "use-debounce"; import "./PackageSearch.css"; @@ -16,6 +16,7 @@ import { NewButton, NewPagination, NewTextInput, + SkeletonBox, } from "@thunderstore/cyberstorm"; import { Await, useNavigationType, useSearchParams } from "react-router"; import { PackageCount } from "./components/PackageCount/PackageCount"; @@ -35,12 +36,13 @@ import { RequestConfig, } from "@thunderstore/thunderstore-api"; import { DapperTs } from "@thunderstore/dapper-ts"; +import { isPromise } from "cyberstorm/utils/typeChecks"; const PER_PAGE = 20; interface Props { - listings: Promise; - filters: Promise; + listings: Promise | PackageListings; + filters: Promise | CommunityFilters; config: () => RequestConfig; currentUser?: CurrentUser; dapper: DapperTs; @@ -148,34 +150,26 @@ const compareSearchParamBlobs = ( export function PackageSearch(props: Props) { const { listings, filters, config, currentUser, dapper } = props; - // const navigation = useNavigation(); - const navigationType = useNavigationType(); - // const listingsAndFiltersMemo = useMemo( - // () => Promise.all([listings, filters]), - // [listings, filters] - // ); + // This exists to resolve insert the initial sections and categories on server-side + // so that we don't have to await the clientLoader to get the options, to then be able to + // do the initial fetch + const possibleFilters = isPromise(filters) ? undefined : filters; - const [sortedSections, setSortedSections] = - useState(); - const [categories, setCategories] = useState(); + const [sortedSections, setSortedSections] = useState< + CommunityFilters["sections"] | undefined + >(possibleFilters?.sections); - const [searchParams, setSearchParams] = useSearchParams(); + const [categories, setCategories] = useState( + possibleFilters?.package_categories + .sort((a, b) => a.slug.localeCompare(b.slug)) + .map((c) => ({ ...c, selection: "off" })) + ); - // filters.then((resolvedFilters) => { - // // Set sorted sections - // setSortedSections( - // resolvedFilters.sections.sort((a, b) => b.priority - a.priority) - // ); - // // Set current "initial" categories - // const categories: CategorySelection[] = resolvedFilters.package_categories - // .sort((a, b) => a.slug.localeCompare(b.slug)) - // .map((c) => ({ ...c, selection: "off" })); - // setCategories(categories); - // }); + const [searchParams, setSearchParams] = useSearchParams(); - const initialParams = searchParamsToBlob(searchParams); + const initialParams = searchParamsToBlob(searchParams, sortedSections); const [searchParamsBlob, setSearchParamsBlob] = useState(initialParams); @@ -184,6 +178,37 @@ export function PackageSearch(props: Props) { searchParams.get("page") ? Number(searchParams.get("page")) : 1 ); + const categoriesRef = useRef< + undefined | Awaited>["package_categories"] + >(undefined); + + useEffect(() => { + if (isPromise(filters)) { + // On mount, resolve filters promise and set sections and categories states + filters.then((resolvedFilters) => { + // Set sorted sections + setSortedSections( + resolvedFilters.sections.sort((a, b) => b.priority - a.priority) + ); + if (sortedSections && sortedSections.length !== 0) { + setSearchParamsBlob((prev) => ({ + ...prev, + section: sortedSections[0].uuid, + })); + } + if (resolvedFilters.package_categories !== categoriesRef.current) { + // Set current "initial" categories + const categories: CategorySelection[] = + resolvedFilters.package_categories + .sort((a, b) => a.slug.localeCompare(b.slug)) + .map((c) => ({ ...c, selection: "off" })); + setCategories(categories); + categoriesRef.current = resolvedFilters.package_categories; + } + }); + } + }, []); + // Categories start const parsedCategories = parseCategories( @@ -307,38 +332,30 @@ export function PackageSearch(props: Props) { resetPage = true; } // Section - // Because of the first section being a empty value, the logic check is a bit funky - - // If no section in search params, delete - if (sortedSections && sortedSections.length === 0) - searchParams.delete("section"); - - // If new section is empty, delete (defaults to first) - if (debouncedSearchParamsBlob.section === "") - searchParams.delete("section"); - - // If new section is the first one, delete. And reset page number if section is different from last render. if ( - sortedSections && - debouncedSearchParamsBlob.section === sortedSections[0]?.uuid + debouncedSearchParamsBlob.section === "" || + (sortedSections && sortedSections.length === 0) ) { - if ( - searchParamsBlobRef.current.section !== - debouncedSearchParamsBlob.section - ) { - resetPage = true; - } searchParams.delete("section"); + } else { + if (sortedSections && sortedSections.length !== 0) { + if (debouncedSearchParamsBlob.section === sortedSections[0]?.uuid) { + // If the first one, ensure the search param isn't set as it's defaulted to the first one in SearchParmsToBlob function. + searchParams.delete("section"); + } else { + searchParams.set("section", debouncedSearchParamsBlob.section); + } + } else { + // This else is for completeness + searchParams.delete("section"); + } } - // If new section is different and not the first one, set it. + // Reset page if section has changed if ( - sortedSections && searchParamsBlobRef.current.section !== - debouncedSearchParamsBlob.section && - debouncedSearchParamsBlob.section !== sortedSections[0]?.uuid + debouncedSearchParamsBlob.section ) { - searchParams.set("section", debouncedSearchParamsBlob.section); resetPage = true; } @@ -470,7 +487,11 @@ export function PackageSearch(props: Props) { priority: -999999999, }, ]} - selected={searchParamsBlob.section ?? sortedSections[0]?.uuid} + selected={ + searchParamsBlob.section === "" + ? sortedSections[0]?.uuid + : searchParamsBlob.section + } setSelected={setParamsBlobValue( setSearchParamsBlob, searchParamsBlob, @@ -581,13 +602,7 @@ export function PackageSearch(props: Props) {
- - Loading... - - } - > + }> {(resolvedValue) => (
- - Loading... - - } - > + }> {(resolvedValue) => ( <> @@ -686,13 +695,7 @@ export function PackageSearch(props: Props) {
- - Loading... - - } - > + }> {(resolvedValue) => ( + {Array.from({ length: 12 }).map((_, index) => ( + + ))} +
+ ); + } +); diff --git a/apps/cyberstorm-remix/app/p/dependants/Dependants.tsx b/apps/cyberstorm-remix/app/p/dependants/Dependants.tsx index 76e11222b..8db343bcb 100644 --- a/apps/cyberstorm-remix/app/p/dependants/Dependants.tsx +++ b/apps/cyberstorm-remix/app/p/dependants/Dependants.tsx @@ -1,14 +1,11 @@ -import type { LoaderFunctionArgs, MetaFunction } from "react-router"; -import { useLoaderData, useOutletContext } from "react-router"; +import { Await, useLoaderData, useOutletContext } from "react-router"; import { formatToDisplayName, - NewBreadCrumbs, - NewBreadCrumbsLink, NewLink, + SkeletonBox, } from "@thunderstore/cyberstorm"; import "./Dependants.css"; import { PackageSearch } from "~/commonComponents/PackageSearch/PackageSearch"; -import { ApiError } from "@thunderstore/thunderstore-api"; import { DapperTs } from "@thunderstore/dapper-ts"; import { PackageOrderOptions } from "../../commonComponents/PackageSearch/components/PackageOrder"; import { OutletContextShape } from "../../root"; @@ -17,229 +14,159 @@ import { getPublicEnvVariables, getSessionTools, } from "cyberstorm/security/publicEnvVariables"; +import { Route } from "./+types/Dependants"; +import { Suspense } from "react"; -export const meta: MetaFunction = ({ data }) => { - return [ - { title: data?.community.name }, - { name: "description", content: `Mods for ${data?.community.name}` }, - ]; -}; - -export async function loader({ request, params }: LoaderFunctionArgs) { - if (params.communityId && params.namespaceId && params.packageId) { - try { - const publicEnvVariables = getPublicEnvVariables(["VITE_API_URL"]); - const dapper = new DapperTs(() => { - return { - apiHost: publicEnvVariables.VITE_API_URL, - sessionId: undefined, - }; - }); - const searchParams = new URL(request.url).searchParams; - const ordering = - searchParams.get("ordering") ?? PackageOrderOptions.Updated; - const page = searchParams.get("page"); - const search = searchParams.get("search"); - const includedCategories = searchParams.get("includedCategories"); - const excludedCategories = searchParams.get("excludedCategories"); - const section = searchParams.get("section"); - const nsfw = searchParams.get("nsfw"); - const deprecated = searchParams.get("deprecated"); - const community = await dapper.getCommunity(params.communityId); - const filters = await dapper.getCommunityFilters(params.communityId); - const sortedSections = filters.sections.sort( - (a, b) => b.priority - a.priority - ); +export async function loader({ params, request }: Route.LoaderArgs) { + if (params.communityId && params.packageId && params.namespaceId) { + const publicEnvVariables = getPublicEnvVariables(["VITE_API_URL"]); + const dapper = new DapperTs(() => { return { - community: community, - filters: filters, - listing: await dapper.getPackageListingDetails( - params.communityId, - params.namespaceId, - params.packageId - ), - listings: await dapper.getPackageListings( - { - kind: "package-dependants", - communityId: params.communityId, - namespaceId: params.namespaceId, - packageName: params.packageId, - }, - ordering ?? "", - page === null ? undefined : Number(page), - search ?? "", - includedCategories?.split(",") ?? undefined, - excludedCategories?.split(",") ?? undefined, - section - ? section === "all" - ? "" - : section - : sortedSections && sortedSections[0] - ? sortedSections[0].uuid - : "", - nsfw === "true" ? true : false, - deprecated === "true" ? true : false - ), - sortedSections: sortedSections, + apiHost: publicEnvVariables.VITE_API_URL, + sessionId: undefined, }; - } catch (error) { - if (error instanceof ApiError) { - throw new Response("Package not found", { status: 404 }); - } else { - throw error; - } - } + }); + const searchParams = new URL(request.url).searchParams; + const ordering = + searchParams.get("ordering") ?? PackageOrderOptions.Updated; + const page = searchParams.get("page"); + const search = searchParams.get("search"); + const includedCategories = searchParams.get("includedCategories"); + const excludedCategories = searchParams.get("excludedCategories"); + const section = searchParams.get("section"); + const nsfw = searchParams.get("nsfw"); + const deprecated = searchParams.get("deprecated"); + const filters = await dapper.getCommunityFilters(params.communityId); + + return { + community: dapper.getCommunity(params.communityId), + listing: dapper.getPackageListingDetails( + params.communityId, + params.namespaceId, + params.packageId + ), + filters: filters, + listings: await dapper.getPackageListings( + { + kind: "package-dependants", + communityId: params.communityId, + namespaceId: params.namespaceId, + packageName: params.packageId, + }, + ordering ?? "", + page === null ? undefined : Number(page), + search ?? "", + includedCategories?.split(",") ?? undefined, + excludedCategories?.split(",") ?? undefined, + section ? (section === "all" ? "" : section) : "", + nsfw === "true" ? true : false, + deprecated === "true" ? true : false + ), + }; } - throw new Response("Package not found", { status: 404 }); + throw new Response("Community not found", { status: 404 }); } -export async function clientLoader({ request, params }: LoaderFunctionArgs) { - if (params.communityId && params.namespaceId && params.packageId) { - try { - const tools = getSessionTools(); - const dapper = new DapperTs(() => { - return { - apiHost: tools?.getConfig().apiHost, - sessionId: tools?.getConfig().sessionId, - }; - }); - const searchParams = new URL(request.url).searchParams; - const ordering = - searchParams.get("ordering") ?? PackageOrderOptions.Updated; - const page = searchParams.get("page"); - const search = searchParams.get("search"); - const includedCategories = searchParams.get("includedCategories"); - const excludedCategories = searchParams.get("excludedCategories"); - const section = searchParams.get("section"); - const nsfw = searchParams.get("nsfw"); - const deprecated = searchParams.get("deprecated"); - const community = await dapper.getCommunity(params.communityId); - const filters = await dapper.getCommunityFilters(params.communityId); - const sortedSections = filters.sections.sort( - (a, b) => b.priority - a.priority - ); +export async function clientLoader({ + request, + params, +}: Route.ClientLoaderArgs) { + if (params.communityId && params.packageId && params.namespaceId) { + const tools = getSessionTools(); + const dapper = new DapperTs(() => { return { - community: community, - filters: filters, - listing: await dapper.getPackageListingDetails( - params.communityId, - params.namespaceId, - params.packageId - ), - listings: await dapper.getPackageListings( - { - kind: "package-dependants", - communityId: params.communityId, - namespaceId: params.namespaceId, - packageName: params.packageId, - }, - ordering ?? "", - page === null ? undefined : Number(page), - search ?? "", - includedCategories?.split(",") ?? undefined, - excludedCategories?.split(",") ?? undefined, - section - ? section === "all" - ? "" - : section - : sortedSections && sortedSections[0] - ? sortedSections[0].uuid - : "", - nsfw === "true" ? true : false, - deprecated === "true" ? true : false - ), - sortedSections: sortedSections, + apiHost: tools?.getConfig().apiHost, + sessionId: tools?.getConfig().sessionId, }; - } catch (error) { - if (error instanceof ApiError) { - throw new Response("Package not found", { status: 404 }); - } else { - throw error; - } - } + }); + const searchParams = new URL(request.url).searchParams; + const ordering = + searchParams.get("ordering") ?? PackageOrderOptions.Updated; + const page = searchParams.get("page"); + const search = searchParams.get("search"); + const includedCategories = searchParams.get("includedCategories"); + const excludedCategories = searchParams.get("excludedCategories"); + const section = searchParams.get("section"); + const nsfw = searchParams.get("nsfw"); + const deprecated = searchParams.get("deprecated"); + const filters = dapper.getCommunityFilters(params.communityId); + return { + community: dapper.getCommunity(params.communityId), + listing: dapper.getPackageListingDetails( + params.communityId, + params.namespaceId, + params.packageId + ), + filters: filters, + listings: dapper.getPackageListings( + { + kind: "package-dependants", + communityId: params.communityId, + namespaceId: params.namespaceId, + packageName: params.packageId, + }, + ordering ?? "", + page === null ? undefined : Number(page), + search ?? "", + includedCategories?.split(",") ?? undefined, + excludedCategories?.split(",") ?? undefined, + section ? (section === "all" ? "" : section) : "", + nsfw === "true" ? true : false, + deprecated === "true" ? true : false + ), + }; } - throw new Response("Package not found", { status: 404 }); + throw new Response("Community not found", { status: 404 }); } export default function Dependants() { - const { community, filters, listing, listings, sortedSections } = - useLoaderData(); + const { filters, listing, listings } = useLoaderData< + typeof loader | typeof clientLoader + >(); const outletContext = useOutletContext() as OutletContextShape; return ( <> - - - Communities - - - {community.name} - - - {listing.namespace} - - - {formatToDisplayName(listing.name)} - - - Dependants - -
- - Mods that depend on{" "} - - {formatToDisplayName(listing.name)} - - {" by "} - - {listing.namespace} - - - + }> + + {(resolvedValue) => ( + + Mods that depend on{" "} + + {formatToDisplayName(resolvedValue.name)} + + {" by "} + + {resolvedValue.namespace} + + + )} + + + <> + +
); diff --git a/apps/cyberstorm-remix/app/p/packageEdit.tsx b/apps/cyberstorm-remix/app/p/packageEdit.tsx index 1ccf2704e..a146434af 100644 --- a/apps/cyberstorm-remix/app/p/packageEdit.tsx +++ b/apps/cyberstorm-remix/app/p/packageEdit.tsx @@ -2,8 +2,6 @@ import type { LoaderFunctionArgs, MetaFunction } from "react-router"; import { useLoaderData, useOutletContext, useRevalidator } from "react-router"; import { NewAlert, - NewBreadCrumbs, - NewBreadCrumbsLink, NewButton, NewIcon, NewSelectSearch, @@ -237,45 +235,6 @@ export default function PackageListing() { return ( <> - - - Communities - - - {community.name} - - - {listing.namespace} - - - {listing.name} - - - Edit package - - Edit package diff --git a/apps/cyberstorm-remix/app/p/team/Team.tsx b/apps/cyberstorm-remix/app/p/team/Team.tsx index 0f66077de..aa80d6847 100644 --- a/apps/cyberstorm-remix/app/p/team/Team.tsx +++ b/apps/cyberstorm-remix/app/p/team/Team.tsx @@ -1,9 +1,6 @@ -import type { LoaderFunctionArgs, MetaFunction } from "react-router"; import { useLoaderData, useOutletContext } from "react-router"; -import { NewBreadCrumbs, NewBreadCrumbsLink } from "@thunderstore/cyberstorm"; import "./Team.css"; import { PackageSearch } from "~/commonComponents/PackageSearch/PackageSearch"; -import { ApiError } from "@thunderstore/thunderstore-api"; import { DapperTs } from "@thunderstore/dapper-ts"; import { PackageOrderOptions } from "../../commonComponents/PackageSearch/components/PackageOrder"; import { OutletContextShape } from "../../root"; @@ -12,142 +9,100 @@ import { getPublicEnvVariables, getSessionTools, } from "cyberstorm/security/publicEnvVariables"; +import { Route } from "./+types/Team"; -export const meta: MetaFunction = ({ data }) => { - return [ - { title: data?.community.name }, - { name: "description", content: `Mods for ${data?.community.name}` }, - ]; -}; - -export async function loader({ request, params }: LoaderFunctionArgs) { +export async function loader({ params, request }: Route.LoaderArgs) { if (params.communityId && params.namespaceId) { - try { - const publicEnvVariables = getPublicEnvVariables(["VITE_API_URL"]); - const dapper = new DapperTs(() => { - return { - apiHost: publicEnvVariables.VITE_API_URL, - sessionId: undefined, - }; - }); - const searchParams = new URL(request.url).searchParams; - const ordering = - searchParams.get("ordering") ?? PackageOrderOptions.Updated; - const page = searchParams.get("page"); - const search = searchParams.get("search"); - const includedCategories = searchParams.get("includedCategories"); - const excludedCategories = searchParams.get("excludedCategories"); - const section = searchParams.get("section"); - const nsfw = searchParams.get("nsfw"); - const deprecated = searchParams.get("deprecated"); - const community = await dapper.getCommunity(params.communityId); - const filters = await dapper.getCommunityFilters(params.communityId); - const sortedSections = filters.sections.sort( - (a, b) => b.priority - a.priority - ); + const publicEnvVariables = getPublicEnvVariables(["VITE_API_URL"]); + const dapper = new DapperTs(() => { return { - community: community, - team: params.namespaceId, - filters: filters, - listings: await dapper.getPackageListings( - { - kind: "namespace", - communityId: params.communityId, - namespaceId: params.namespaceId, - }, - ordering ?? "", - page === null ? undefined : Number(page), - search ?? "", - includedCategories?.split(",") ?? undefined, - excludedCategories?.split(",") ?? undefined, - section - ? section === "all" - ? "" - : section - : sortedSections && sortedSections[0] - ? sortedSections[0].uuid - : "", - nsfw === "true" ? true : false, - deprecated === "true" ? true : false - ), - sortedSections: sortedSections, + apiHost: publicEnvVariables.VITE_API_URL, + sessionId: undefined, }; - } catch (error) { - if (error instanceof ApiError) { - throw new Response("Package not found", { status: 404 }); - } else { - throw error; - } - } + }); + const searchParams = new URL(request.url).searchParams; + const ordering = + searchParams.get("ordering") ?? PackageOrderOptions.Updated; + const page = searchParams.get("page"); + const search = searchParams.get("search"); + const includedCategories = searchParams.get("includedCategories"); + const excludedCategories = searchParams.get("excludedCategories"); + const section = searchParams.get("section"); + const nsfw = searchParams.get("nsfw"); + const deprecated = searchParams.get("deprecated"); + const filters = await dapper.getCommunityFilters(params.communityId); + + return { + teamId: params.namespaceId, + filters: filters, + listings: await dapper.getPackageListings( + { + kind: "namespace", + communityId: params.communityId, + namespaceId: params.namespaceId, + }, + ordering ?? "", + page === null ? undefined : Number(page), + search ?? "", + includedCategories?.split(",") ?? undefined, + excludedCategories?.split(",") ?? undefined, + section ? (section === "all" ? "" : section) : "", + nsfw === "true" ? true : false, + deprecated === "true" ? true : false + ), + }; } - throw new Response("Package not found", { status: 404 }); + throw new Response("Community not found", { status: 404 }); } -export async function clientLoader({ request, params }: LoaderFunctionArgs) { +export async function clientLoader({ + request, + params, +}: Route.ClientLoaderArgs) { if (params.communityId && params.namespaceId) { - try { - const tools = getSessionTools(); - const dapper = new DapperTs(() => { - return { - apiHost: tools?.getConfig().apiHost, - sessionId: tools?.getConfig().sessionId, - }; - }); - const searchParams = new URL(request.url).searchParams; - const ordering = - searchParams.get("ordering") ?? PackageOrderOptions.Updated; - const page = searchParams.get("page"); - const search = searchParams.get("search"); - const includedCategories = searchParams.get("includedCategories"); - const excludedCategories = searchParams.get("excludedCategories"); - const section = searchParams.get("section"); - const nsfw = searchParams.get("nsfw"); - const deprecated = searchParams.get("deprecated"); - const community = await dapper.getCommunity(params.communityId); - const filters = await dapper.getCommunityFilters(params.communityId); - const sortedSections = filters.sections.sort( - (a, b) => b.priority - a.priority - ); + const tools = getSessionTools(); + const dapper = new DapperTs(() => { return { - community: community, - team: params.namespaceId, - filters: filters, - listings: await dapper.getPackageListings( - { - kind: "namespace", - communityId: params.communityId, - namespaceId: params.namespaceId, - }, - ordering ?? "", - page === null ? undefined : Number(page), - search ?? "", - includedCategories?.split(",") ?? undefined, - excludedCategories?.split(",") ?? undefined, - section - ? section === "all" - ? "" - : section - : sortedSections && sortedSections[0] - ? sortedSections[0].uuid - : "", - nsfw === "true" ? true : false, - deprecated === "true" ? true : false - ), - sortedSections: sortedSections, + apiHost: tools?.getConfig().apiHost, + sessionId: tools?.getConfig().sessionId, }; - } catch (error) { - if (error instanceof ApiError) { - throw new Response("Package not found", { status: 404 }); - } else { - throw error; - } - } + }); + const searchParams = new URL(request.url).searchParams; + const ordering = + searchParams.get("ordering") ?? PackageOrderOptions.Updated; + const page = searchParams.get("page"); + const search = searchParams.get("search"); + const includedCategories = searchParams.get("includedCategories"); + const excludedCategories = searchParams.get("excludedCategories"); + const section = searchParams.get("section"); + const nsfw = searchParams.get("nsfw"); + const deprecated = searchParams.get("deprecated"); + const filters = dapper.getCommunityFilters(params.communityId); + return { + teamId: params.namespaceId, + filters: filters, + listings: dapper.getPackageListings( + { + kind: "namespace", + communityId: params.communityId, + namespaceId: params.namespaceId, + }, + ordering ?? "", + page === null ? undefined : Number(page), + search ?? "", + includedCategories?.split(",") ?? undefined, + excludedCategories?.split(",") ?? undefined, + section ? (section === "all" ? "" : section) : "", + nsfw === "true" ? true : false, + deprecated === "true" ? true : false + ), + }; } - throw new Response("Package not found", { status: 404 }); + throw new Response("Community not found", { status: 404 }); } export default function Team() { - const { community, team, filters, listings, sortedSections } = useLoaderData< + const { filters, listings, teamId } = useLoaderData< typeof loader | typeof clientLoader >(); @@ -155,38 +110,19 @@ export default function Team() { return ( <> - - - Communities - - - {community.name} - - - {team} - -
- Mods uploaded by {team} + Mods uploaded by {teamId} - + <> + +
); diff --git a/apps/cyberstorm-remix/app/root.tsx b/apps/cyberstorm-remix/app/root.tsx index eb091d430..1b5d5840b 100644 --- a/apps/cyberstorm-remix/app/root.tsx +++ b/apps/cyberstorm-remix/app/root.tsx @@ -9,6 +9,7 @@ import { Scripts, ScrollRestoration, ShouldRevalidateFunctionArgs, + UIMatch, isRouteErrorResponse, useLoaderData, useLocation, @@ -49,7 +50,6 @@ import { publicEnvVariablesType, } from "cyberstorm/security/publicEnvVariables"; import { StorageManager } from "@thunderstore/ts-api-react/src/storage"; -import { isPromise } from "cyberstorm/utils/typeChecks"; // REMIX TODO: https://remix.run/docs/en/main/route/links // export const links: LinksFunction = () => [{ rel: "stylesheet", href: styles }]; @@ -220,8 +220,24 @@ export function Layout({ children }: { children: React.ReactNode }) { const communitiesPage = matches.find( (m) => m.id === "communities/communities" ); + const uploadPage = matches.find((m) => m.id === "upload/upload"); const communityPage = matches.find((m) => m.id === "c/community"); const packageListingPage = matches.find((m) => m.id === "p/packageListing"); + const packageEditPage = matches.find((m) => m.id === "p/packageEdit"); + const packageDependantsPage = matches.find( + (m) => m.id === "p/dependants/Dependants" + ); + const packageTeamPage = matches.find((m) => m.id === "p/team/Team"); + const packageFormatDocsPage = matches.find( + (m) => m.id === "tools/package-format-docs/packageFormatDocs" + ); + const manifestValidatorPage = matches.find( + (m) => m.id === "tools/manifest-validator/manifestValidator" + ); + const markdownPreviewPage = matches.find( + (m) => m.id === "tools/markdown-preview/markdownPreview" + ); + const shouldShowAds = location.pathname.startsWith("/teams") ? false : location.pathname.startsWith("/settings") @@ -284,114 +300,78 @@ export function Layout({ children }: { children: React.ReactNode }) {
{/* Breadcrumbs are build progressively */} + {/* Upload */} + {uploadPage ? ( + + Upload + + ) : null} {/* Communities page */} - {communitiesPage || communityPage ? ( - - Communities - + {communitiesPage || + communityPage || + packageDependantsPage || + packageTeamPage ? ( + communityPage || + packageDependantsPage || + packageTeamPage ? ( + + Communities + + ) : ( + + Communities + + ) ) : null} {/* Community page */} - {communityPage && - isRecord(communityPage.data) && - Object.prototype.hasOwnProperty.call( - communityPage.data, - "community" - ) && - isPromise(communityPage.data.community) ? ( - - Loading... - - } - > - - {(resolvedValue) => { - let label = undefined; - let icon = undefined; - if (isRecord(resolvedValue)) { - label = - Object.prototype.hasOwnProperty.call( - resolvedValue, - "name" - ) && typeof resolvedValue.name === "string" - ? resolvedValue.name - : communityPage.params.communityId; - icon = - Object.prototype.hasOwnProperty.call( - resolvedValue, - "community_icon_url" - ) && - typeof resolvedValue.community_icon_url === - "string" ? ( - - ) : undefined; - } - return matches[matches.length - 1] === - communityPage ? ( - - - {icon} - {label} - - - ) : ( - - {icon} - {label} - - ); - }} - - - ) : null} + {getCommunityBreadcrumb( + communityPage || + packageListingPage || + packageDependantsPage || + packageTeamPage, + Boolean(packageListingPage) || + Boolean(packageDependantsPage) || + Boolean(packageTeamPage) + )} {/* Package listing page */} - {packageListingPage && - isRecord(packageListingPage.data) && - Object.prototype.hasOwnProperty.call( - packageListingPage.data, - "listing" - ) && - isPromise(packageListingPage.data.listing) ? ( - - Loading... - - } - > - - {(resolvedValue) => { - let label = undefined; - if (isRecord(resolvedValue)) { - label = - Object.prototype.hasOwnProperty.call( - resolvedValue, - "name" - ) && typeof resolvedValue.name === "string" - ? resolvedValue.name - : packageListingPage.params.packageId; - } - return ( - - {label} - - ); - }} - - + {getPackageListingBreadcrumb( + packageListingPage, + packageEditPage, + packageDependantsPage + )} + {packageEditPage ? ( + + Edit package + + ) : null} + {packageDependantsPage ? ( + + Dependants + + ) : null} + {packageTeamPage ? ( + + {packageTeamPage.params.namespaceId} + + ) : null} + {packageFormatDocsPage ? ( + + Package Format Docs + + ) : null} + {manifestValidatorPage ? ( + + Manifest Validator + + ) : null} + {markdownPreviewPage ? ( + + Markdown Preview + ) : null} {children} @@ -568,3 +548,106 @@ function BetaButtonInit() { return <>; } + +function getCommunityBreadcrumb( + communityPage: UIMatch | undefined, + isNotLast: boolean +) { + if (!communityPage) return null; + return ( + <> + {communityPage && + isRecord(communityPage.data) && + Object.prototype.hasOwnProperty.call(communityPage.data, "community") ? ( + + Loading... + + } + > + + {(resolvedValue) => { + let label = undefined; + let icon = undefined; + if (isRecord(resolvedValue)) { + label = + Object.prototype.hasOwnProperty.call(resolvedValue, "name") && + typeof resolvedValue.name === "string" + ? resolvedValue.name + : communityPage.params.communityId; + icon = + Object.prototype.hasOwnProperty.call( + resolvedValue, + "community_icon_url" + ) && typeof resolvedValue.community_icon_url === "string" ? ( + + ) : undefined; + } + return isNotLast ? ( + + {icon} + {label} + + ) : ( + + + {icon} + {label} + + + ); + }} + + + ) : null} + + ); +} + +function getPackageListingBreadcrumb( + packageListingPage: UIMatch | undefined, + packageEditPage: UIMatch | undefined, + packageDependantsPage: UIMatch | undefined +) { + if (!packageListingPage && !packageEditPage && !packageDependantsPage) + return null; + return ( + <> + {packageListingPage ? ( + + {packageListingPage.params.packageId} + + ) : null} + {packageEditPage ? ( + + {packageEditPage.params.packageId} + + ) : null} + {packageDependantsPage ? ( + + {packageDependantsPage.params.packageId} + + ) : null} + + ); +} diff --git a/apps/cyberstorm-remix/app/routes.ts b/apps/cyberstorm-remix/app/routes.ts index 68b4cea2e..cda5def83 100644 --- a/apps/cyberstorm-remix/app/routes.ts +++ b/apps/cyberstorm-remix/app/routes.ts @@ -13,10 +13,13 @@ export default [ route("/communities", "./communities/communities.tsx"), route("/c/:communityId", "c/community.tsx", [ - route("", "c/tabs/PackageSearch/PackageSearch.tsx", { index: true }), + route("/c/:communityId/", "c/tabs/PackageSearch/PackageSearch.tsx"), ...prefix("p", [ route(":namespaceId/:packageId", "p/packageListing.tsx", [ - route("", "p/tabs/Readme/Readme.tsx", { index: true }), + route( + "/c/:communityId/p/:namespaceId/:packageId/", + "p/tabs/Readme/Readme.tsx" + ), route("required", "p/tabs/Required/Required.tsx"), route("changelog", "p/tabs/Changelog/Changelog.tsx"), route("versions", "p/tabs/Versions/Versions.tsx"), @@ -28,15 +31,15 @@ export default [ route("/:slug/edit", "p/tabs/Wiki/WikiPageEdit.tsx"), ]), ]), - route("edit", "p/packageEdit.tsx"), ]), - route( - ":namespaceId/:packageId/dependants", - "p/dependants/Dependants.tsx" - ), - route(":namespaceId", "p/team/Team.tsx"), + route(":namespaceId/:packageId/edit", "p/packageEdit.tsx"), ]), ]), + route( + "/c/:communityId/p/:namespaceId/:packageId/dependants", + "p/dependants/Dependants.tsx" + ), + route("/c/:communityId/p/:namespaceId", "p/team/Team.tsx"), route( "/package/create/docs", "tools/package-format-docs/packageFormatDocs.tsx" diff --git a/apps/cyberstorm-remix/app/tools/manifest-validator/manifestValidator.tsx b/apps/cyberstorm-remix/app/tools/manifest-validator/manifestValidator.tsx index edbc715dd..f0ceef115 100644 --- a/apps/cyberstorm-remix/app/tools/manifest-validator/manifestValidator.tsx +++ b/apps/cyberstorm-remix/app/tools/manifest-validator/manifestValidator.tsx @@ -1,7 +1,6 @@ import { CodeInput, NewAlert, - NewBreadCrumbs, NewSelect, isRecord, isStringArray, @@ -75,11 +74,6 @@ export default function ManifestValidator() { return ( <> - - - Manifest Validator - - Manifest Validator diff --git a/apps/cyberstorm-remix/app/tools/markdown-preview/markdownPreview.tsx b/apps/cyberstorm-remix/app/tools/markdown-preview/markdownPreview.tsx index 109a9d3be..27eb3e7cd 100644 --- a/apps/cyberstorm-remix/app/tools/markdown-preview/markdownPreview.tsx +++ b/apps/cyberstorm-remix/app/tools/markdown-preview/markdownPreview.tsx @@ -1,6 +1,6 @@ import { useOutletContext } from "react-router"; import "./MarkdownPreview.css"; -import { CodeInput, NewBreadCrumbs, isRecord } from "@thunderstore/cyberstorm"; +import { CodeInput, isRecord } from "@thunderstore/cyberstorm"; import { isApiError, RequestConfig, @@ -54,11 +54,6 @@ export default function MarkdownPreview() { return ( <> - - - Markdown Preview - - Markdown Preview diff --git a/apps/cyberstorm-remix/app/tools/package-format-docs/packageFormatDocs.tsx b/apps/cyberstorm-remix/app/tools/package-format-docs/packageFormatDocs.tsx index 0aef7e819..da356a5aa 100644 --- a/apps/cyberstorm-remix/app/tools/package-format-docs/packageFormatDocs.tsx +++ b/apps/cyberstorm-remix/app/tools/package-format-docs/packageFormatDocs.tsx @@ -1,22 +1,12 @@ import "./PackageFormatDocs.css"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faCheck, faXmark } from "@fortawesome/free-solid-svg-icons"; -import { - CodeBox, - NewBreadCrumbs, - NewIcon, - NewTable, -} from "@thunderstore/cyberstorm"; +import { CodeBox, NewIcon, NewTable } from "@thunderstore/cyberstorm"; import { PageHeader } from "~/commonComponents/PageHeader/PageHeader"; export default function PackageFormatDocs() { return ( <> - - - Package Format Docs - - Package Format Docs diff --git a/apps/cyberstorm-remix/app/upload/upload.tsx b/apps/cyberstorm-remix/app/upload/upload.tsx index 1fa5277b5..c63566c0c 100644 --- a/apps/cyberstorm-remix/app/upload/upload.tsx +++ b/apps/cyberstorm-remix/app/upload/upload.tsx @@ -1,6 +1,5 @@ import "./Upload.css"; import { - NewBreadCrumbs, NewButton, NewIcon, NewSelectSearch, @@ -366,11 +365,6 @@ export default function Upload() { return ( <> - - - Upload - - Upload package From 4cf6b95b76defe723e7a861ca7b150c8ed6710d6 Mon Sep 17 00:00:00 2001 From: Oksamies Date: Thu, 11 Sep 2025 23:47:38 +0300 Subject: [PATCH 2/2] Update styles and mobile responsivity --- apps/cyberstorm-remix/app/c/Community.css | 74 ++++++- apps/cyberstorm-remix/app/c/community.tsx | 157 +++++++------- .../Connection/Connection.css | 6 +- .../app/commonComponents/Footer/Footer.css | 10 +- .../ListingDependency/ListingDependency.css | 2 +- .../PackageSearch/PackageSearch.css | 11 +- .../app/communities/Communities.css | 10 +- .../app/communities/communities.tsx | 1 + apps/cyberstorm-remix/app/p/packageEdit.css | 2 +- .../cyberstorm-remix/app/p/packageListing.css | 23 +- .../app/p/tabs/Required/Required.css | 2 +- .../app/settings/teams/Teams.css | 2 +- .../teams/team/tabs/Members/Members.css | 4 +- .../teams/team/tabs/Settings/Settings.css | 4 +- .../app/settings/user/Account/Account.css | 2 +- .../manifest-validator/manifestValidator.css | 2 +- .../markdown-preview/MarkdownPreview.css | 4 +- .../package-format-docs/PackageFormatDocs.css | 2 +- apps/cyberstorm-remix/app/upload/Upload.css | 4 +- .../src/components/TextInput/TextInput.css | 6 +- .../src/components/componentsColors.css | 196 ++++++++++-------- .../src/components/componentsSizes.css | 34 +-- .../cyberstorm-theme/src/styles/colors.css | 6 +- .../cyberstorm-theme/src/styles/globals.css | 17 +- .../cyberstorm-theme/src/styles/layout.css | 2 +- .../components/CodeInput/CodeInput.module.css | 2 +- .../ValidationBar/ValidationBar.module.css | 2 +- .../src/newComponents/Drawer/Drawer.css | 4 +- .../newComponents/EmptyState/EmptyState.css | 2 +- .../SelectSearch/SelectSearch.css | 2 +- .../src/newComponents/TextInput/TextInput.tsx | 14 +- 31 files changed, 356 insertions(+), 253 deletions(-) diff --git a/apps/cyberstorm-remix/app/c/Community.css b/apps/cyberstorm-remix/app/c/Community.css index 1548c945b..1d5d40e0d 100644 --- a/apps/cyberstorm-remix/app/c/Community.css +++ b/apps/cyberstorm-remix/app/c/Community.css @@ -26,7 +26,7 @@ align-items: center; align-self: stretch; justify-content: center; - height: 12.5rem; + max-height: 12.5rem; border-radius: 0.5rem; overflow-y: hidden; transition: height 2s; @@ -36,6 +36,10 @@ background: var(--color-ui-surface-1); opacity: 0.8; } + + .skeleton { + height: 12.5rem; + } } .community__background--packagePage { @@ -47,12 +51,14 @@ } } - .community__content-header { + .community__content-header-wrapper { z-index: 1; display: flex; + flex-wrap: wrap; gap: 1.5rem; - align-items: flex-end; + place-items: flex-end stretch; align-self: stretch; + justify-content: space-between; height: max-content; margin-top: -1rem; padding-left: 1rem; @@ -62,6 +68,16 @@ visibility 1s 0s; } + .community__content-header { + display: flex; + flex-grow: 1; + gap: 1.5rem; + align-items: flex-end; + align-self: stretch; + min-width: 75%; + height: max-content; + } + .community__content-header--hide { height: 0; visibility: collapse; @@ -101,11 +117,10 @@ .community__content-header-content { display: flex; flex-direction: column; + flex-grow: 1; gap: 0.75rem; align-items: flex-start; justify-content: center; - width: 100%; - height: 5rem; } .community__header-info { @@ -115,16 +130,26 @@ gap: 0.25rem; align-items: flex-start; align-self: stretch; - width: 40%; + min-width: 60%; + max-width: 100%; + + > h1 { + line-height: 80%; + overflow-wrap: anywhere; + } } .community__header-meta { display: flex; - flex: 0 1 0; + flex: 0 1 60%; gap: 1.5rem; align-items: center; - width: 60%; - min-height: 16px; + min-width: 60%; + height: 16px; + + > .skeleton { + height: 1rem; + } } .community__small-image { @@ -151,7 +176,7 @@ .community__meta { display: flex; flex-wrap: wrap; - gap: var(--gap-xxxl); + gap: var(--gap-3xl); align-items: center; } @@ -169,9 +194,36 @@ } } + @media (width >= 41rem) { + .community__background { + height: 12.5rem; + } + } + + @media (width < 41rem) { + .community__background { + .skeleton { + height: 8rem; + } + } + + .community__header { + gap: 1rem; + } + + .community__content-header-wrapper { + margin-top: 0; + padding-left: 0; + } + + .community__game-icon { + display: none; + } + } + @media (width <= 48rem) { .community__meta { - gap: var(--gap-xxs); + gap: var(--gap-2xs); } .community__item { diff --git a/apps/cyberstorm-remix/app/c/community.tsx b/apps/cyberstorm-remix/app/c/community.tsx index 2a8041fce..c89b611a4 100644 --- a/apps/cyberstorm-remix/app/c/community.tsx +++ b/apps/cyberstorm-remix/app/c/community.tsx @@ -151,88 +151,90 @@ export default function Community() {
-
-
- }> - - {(resolvedValue) => - resolvedValue.community_icon_url ? ( - {resolvedValue.name} - ) : null - } - - +
+
+
+ }> + + {(resolvedValue) => + resolvedValue.community_icon_url ? ( + {resolvedValue.name} + ) : null + } + + +
-
-
-
- }> - - {(resolvedValue) => ( - - {resolvedValue.name} - - )} - - -
-
- }> - - {(resolvedValue) => - resolvedValue.wiki_url ? ( - - - - - Modding Wiki - - - - - ) : null - } - - - }> - - {(resolvedValue) => - resolvedValue.discord_url ? ( - +
+ }> + + {(resolvedValue) => ( + - - - - Modding Discord - - - - - ) : null - } - - + {resolvedValue.name} + + )} + + +
+
+ }> + + {(resolvedValue) => + resolvedValue.wiki_url ? ( + + + + + Modding Wiki + + + + + ) : null + } + + + }> + + {(resolvedValue) => + resolvedValue.discord_url ? ( + + + + + Modding Discord + + + + + ) : null + } + + +
diff --git a/apps/cyberstorm-remix/app/commonComponents/Connection/Connection.css b/apps/cyberstorm-remix/app/commonComponents/Connection/Connection.css index 982599319..2496be7ba 100644 --- a/apps/cyberstorm-remix/app/commonComponents/Connection/Connection.css +++ b/apps/cyberstorm-remix/app/commonComponents/Connection/Connection.css @@ -1,7 +1,7 @@ @layer nimbus-components { .connection { display: flex; - gap: var(--gap-xxxl); + gap: var(--gap-3xl); align-items: center; align-self: stretch; padding: var(--space-16) var(--space-32); @@ -48,7 +48,7 @@ .connection__actions { display: flex; - gap: var(--gap-xxxl); + gap: var(--gap-3xl); align-items: center; justify-content: flex-end; } @@ -56,7 +56,7 @@ .connection__description { display: flex; flex-direction: column; - gap: var(--gap-xxxs); + gap: var(--gap-3xs); align-items: flex-end; justify-content: center; } diff --git a/apps/cyberstorm-remix/app/commonComponents/Footer/Footer.css b/apps/cyberstorm-remix/app/commonComponents/Footer/Footer.css index dc80f1e21..718fd0109 100644 --- a/apps/cyberstorm-remix/app/commonComponents/Footer/Footer.css +++ b/apps/cyberstorm-remix/app/commonComponents/Footer/Footer.css @@ -21,7 +21,7 @@ .footer__links-wrapper { display: flex; flex-wrap: wrap; - gap: var(--gap-xxxl); + gap: var(--gap-3xl); justify-content: space-between; margin-left: 3.5rem; } @@ -47,7 +47,7 @@ .footer__icon-links { display: flex; flex-shrink: 0; - gap: var(--gap-xxxl); + gap: var(--gap-3xl); justify-content: center; } @@ -181,7 +181,7 @@ .footnote__inner { display: flex; flex-grow: 1; - gap: var(--gap-xxl); + gap: var(--gap-2xl); max-width: calc(var(--footer-half-width) * 2); margin-right: 3.5rem; margin-left: 3.5rem; @@ -191,7 +191,7 @@ display: flex; flex: 1 0 0; flex-wrap: wrap; - gap: var(--gap-xxxl); + gap: var(--gap-3xl); align-items: flex-start; > a { @@ -240,7 +240,7 @@ display: flex; flex-flow: column wrap; flex-shrink: 0; - gap: var(--gap-xxxl); + gap: var(--gap-3xl); justify-content: space-between; max-width: unset; margin-left: unset; diff --git a/apps/cyberstorm-remix/app/commonComponents/ListingDependency/ListingDependency.css b/apps/cyberstorm-remix/app/commonComponents/ListingDependency/ListingDependency.css index b8e2e5b3f..787069f08 100644 --- a/apps/cyberstorm-remix/app/commonComponents/ListingDependency/ListingDependency.css +++ b/apps/cyberstorm-remix/app/commonComponents/ListingDependency/ListingDependency.css @@ -29,7 +29,7 @@ .listing-dependency__title { display: flex; - gap: var(--gap-xxs); + gap: var(--gap-2xs); align-items: center; align-self: stretch; font-weight: var(--font-weight-regular); diff --git a/apps/cyberstorm-remix/app/commonComponents/PackageSearch/PackageSearch.css b/apps/cyberstorm-remix/app/commonComponents/PackageSearch/PackageSearch.css index 55c096871..31218c261 100644 --- a/apps/cyberstorm-remix/app/commonComponents/PackageSearch/PackageSearch.css +++ b/apps/cyberstorm-remix/app/commonComponents/PackageSearch/PackageSearch.css @@ -46,7 +46,7 @@ .package-search__pagination { display: flex; - gap: var(--gap-xxs); + gap: var(--gap-2xs); align-items: center; align-self: stretch; justify-content: center; @@ -108,7 +108,7 @@ .package-search__results { display: flex; flex: 1; - gap: var(--gap-xxs); + gap: var(--gap-2xs); align-items: center; justify-content: flex-end; @@ -161,6 +161,7 @@ @media (width <= 475px) { .package-search { flex-direction: column; + gap: 0.25rem; } .package-search__sidebar { @@ -169,8 +170,13 @@ width: 100%; } + .package-search__filters { + width: unset; + } + .package-search__tools { flex-direction: column; + gap: 0.5rem; align-items: stretch; } @@ -188,6 +194,7 @@ } .package-search__content { + gap: 0.5rem; align-self: stretch; } } diff --git a/apps/cyberstorm-remix/app/communities/Communities.css b/apps/cyberstorm-remix/app/communities/Communities.css index 13224fec1..ad7868656 100644 --- a/apps/cyberstorm-remix/app/communities/Communities.css +++ b/apps/cyberstorm-remix/app/communities/Communities.css @@ -12,7 +12,7 @@ .communities__content { display: flex; flex-direction: column; - gap: var(--gap-xxxl); + gap: var(--gap-3xl); align-items: flex-start; align-self: stretch; } @@ -38,7 +38,7 @@ display: grid; flex-flow: row wrap; grid-template-columns: repeat(auto-fill, minmax(10.5rem, 1fr)); - gap: var(--gap-xxxxxxxl) var(--gap-xl); + gap: var(--gap-7xl) var(--gap-xl); width: 100%; } @@ -77,6 +77,12 @@ } } + @media (width <= 41rem) { + .communities__communities-list { + gap: var(--gap-3xl) var(--gap-xl); + } + } + @media (width <= 39rem) { .communities__tools { flex-direction: column; diff --git a/apps/cyberstorm-remix/app/communities/communities.tsx b/apps/cyberstorm-remix/app/communities/communities.tsx index 4707d2b05..709e5f561 100644 --- a/apps/cyberstorm-remix/app/communities/communities.tsx +++ b/apps/cyberstorm-remix/app/communities/communities.tsx @@ -175,6 +175,7 @@ export default function CommunitiesPage() { leftIcon={} type="search" rootClasses="communities__search" + csSize="small" /> ) : null} - {csSize === "default" ? ( + {csSize === "textarea" ? ( {children} ) : ( {children}