From 0a8f489234222522ff25459cf3a580281d035b2b Mon Sep 17 00:00:00 2001 From: Prospector <6166773+Prospector@users.noreply.github.com> Date: Tue, 9 Dec 2025 14:44:10 -0800 Subject: [PATCH] NormalPage component w/ Collections refactor (#4873) * Refactor search page, migrate to /discover/ * Add NormalPage component for common layouts, refactor Collections page as an example, misc ui pkg cleanup * intl:extract * lint * lint * remove old components * Refactor search page, migrate to /discover/ * Add NormalPage component for common layouts, refactor Collections page as an example, misc ui pkg cleanup * intl:extract * lint * lint * remove old components --- apps/app-frontend/src/App.vue | 6 +- .../src/assets/stylesheets/global.scss | 2 + apps/frontend/src/app.vue | 11 +- apps/frontend/src/assets/styles/layout.scss | 54 - apps/frontend/src/assets/styles/tailwind.css | 2 + apps/frontend/src/components/ui/NavRow.vue | 160 --- .../ui/dashboard/CreatorTaxFormModal.vue | 10 +- .../withdraw-stages/CompletionStage.vue | 2 +- .../LegacyPaypalDetailsStage.vue | 9 +- .../withdraw-stages/MethodSelectionStage.vue | 2 +- .../withdraw-stages/MuralpayDetailsStage.vue | 2 +- .../withdraw-stages/TaxFormStage.vue | 3 +- .../TremendousDetailsStage.vue | 2 +- apps/frontend/src/error.vue | 11 +- apps/frontend/src/locales/en-US/index.json | 42 +- apps/frontend/src/pages/auth/authorize.vue | 9 +- apps/frontend/src/pages/auth/welcome.vue | 4 +- apps/frontend/src/pages/collection/[id].vue | 1115 ++++++++--------- .../src/pages/dashboard/collections.vue | 13 +- apps/frontend/src/pages/settings/index.vue | 3 +- apps/frontend/src/pages/user/[id].vue | 4 +- packages/api-client/src/modules/index.ts | 2 + .../src/modules/labrinth/collections.ts | 128 ++ .../api-client/src/modules/labrinth/index.ts | 1 + .../api-client/src/modules/labrinth/types.ts | 95 ++ packages/assets/generated-icons.ts | 2 + packages/assets/icons/heart-minus.svg | 1 + packages/assets/illustrations/empty.svg | 47 + packages/assets/index.ts | 3 + packages/ui/src/components/affiliate/index.ts | 2 + .../ui/src/components/base/HorizontalRule.vue | 3 + .../ui/src/components/base/RadioButtons.vue | 4 +- packages/ui/src/components/base/StatItem.vue | 27 - packages/ui/src/components/base/index.ts | 56 + packages/ui/src/components/billing/index.ts | 5 + packages/ui/src/components/brand/index.ts | 2 + packages/ui/src/components/changelog/index.ts | 1 + packages/ui/src/components/chart/index.ts | 2 + packages/ui/src/components/content/index.ts | 3 + packages/ui/src/components/index.ts | 157 +-- packages/ui/src/components/modal/index.ts | 6 + packages/ui/src/components/nav/NavItem.vue | 50 - packages/ui/src/components/nav/NavRow.vue | 166 --- packages/ui/src/components/nav/NavStack.vue | 22 - packages/ui/src/components/nav/index.ts | 3 + .../ui/src/components/page/NormalPage.vue | 78 ++ .../ui/src/components/page/SidebarCard.vue | 20 + packages/ui/src/components/page/index.ts | 2 + .../components/search/BrowseFiltersPanel.vue | 107 -- .../src/components/search/SearchDropdown.vue | 346 ----- .../ui/src/components/search/SearchFilter.vue | 75 -- packages/ui/src/components/search/index.ts | 4 + .../ui/src/components/servers/icons/index.ts | 2 + packages/ui/src/components/servers/index.ts | 7 + .../ui/src/components/servers/labels/index.ts | 1 + .../src/components/servers/marketing/index.ts | 2 + packages/ui/src/components/settings/index.ts | 1 + packages/ui/src/components/skin/index.ts | 5 + packages/ui/src/components/version/index.ts | 3 + packages/ui/src/locales/en-US/index.json | 6 + packages/ui/src/providers/index.ts | 1 + packages/ui/src/providers/page-context.ts | 14 + packages/ui/src/providers/user-page.ts | 13 + packages/ui/src/styles/tailwind-utilities.css | 14 + packages/ui/src/utils/common-messages.ts | 12 + packages/ui/src/utils/index.ts | 1 + .../ui}/src/utils/vue-children.ts | 4 +- 67 files changed, 1201 insertions(+), 1771 deletions(-) delete mode 100644 apps/frontend/src/components/ui/NavRow.vue create mode 100644 packages/api-client/src/modules/labrinth/collections.ts create mode 100644 packages/assets/icons/heart-minus.svg create mode 100644 packages/assets/illustrations/empty.svg create mode 100644 packages/ui/src/components/affiliate/index.ts create mode 100644 packages/ui/src/components/base/HorizontalRule.vue delete mode 100644 packages/ui/src/components/base/StatItem.vue create mode 100644 packages/ui/src/components/base/index.ts create mode 100644 packages/ui/src/components/billing/index.ts create mode 100644 packages/ui/src/components/brand/index.ts create mode 100644 packages/ui/src/components/changelog/index.ts create mode 100644 packages/ui/src/components/chart/index.ts create mode 100644 packages/ui/src/components/content/index.ts create mode 100644 packages/ui/src/components/modal/index.ts delete mode 100644 packages/ui/src/components/nav/NavItem.vue delete mode 100644 packages/ui/src/components/nav/NavRow.vue delete mode 100644 packages/ui/src/components/nav/NavStack.vue create mode 100644 packages/ui/src/components/nav/index.ts create mode 100644 packages/ui/src/components/page/NormalPage.vue create mode 100644 packages/ui/src/components/page/SidebarCard.vue create mode 100644 packages/ui/src/components/page/index.ts delete mode 100644 packages/ui/src/components/search/BrowseFiltersPanel.vue delete mode 100644 packages/ui/src/components/search/SearchDropdown.vue delete mode 100644 packages/ui/src/components/search/SearchFilter.vue create mode 100644 packages/ui/src/components/search/index.ts create mode 100644 packages/ui/src/components/servers/icons/index.ts create mode 100644 packages/ui/src/components/servers/index.ts create mode 100644 packages/ui/src/components/servers/labels/index.ts create mode 100644 packages/ui/src/components/servers/marketing/index.ts create mode 100644 packages/ui/src/components/settings/index.ts create mode 100644 packages/ui/src/components/skin/index.ts create mode 100644 packages/ui/src/components/version/index.ts create mode 100644 packages/ui/src/providers/page-context.ts create mode 100644 packages/ui/src/providers/user-page.ts create mode 100644 packages/ui/src/styles/tailwind-utilities.css rename {apps/frontend => packages/ui}/src/utils/vue-children.ts (86%) diff --git a/apps/app-frontend/src/App.vue b/apps/app-frontend/src/App.vue index a5c966623..26f041fd6 100644 --- a/apps/app-frontend/src/App.vue +++ b/apps/app-frontend/src/App.vue @@ -37,6 +37,7 @@ import { ProgressSpinner, provideModrinthClient, provideNotificationManager, + providePageContext, useDebugLogger, } from '@modrinth/ui' import { renderString } from '@modrinth/utils' @@ -119,7 +120,10 @@ const tauriApiClient = new TauriModrinthClient({ ], }) provideModrinthClient(tauriApiClient) - +providePageContext({ + hierarchicalSidebarAvailable: ref(true), + showAds: ref(false), +}) const news = ref([]) const availableSurvey = ref(false) diff --git a/apps/app-frontend/src/assets/stylesheets/global.scss b/apps/app-frontend/src/assets/stylesheets/global.scss index 2969d3cb4..493096396 100644 --- a/apps/app-frontend/src/assets/stylesheets/global.scss +++ b/apps/app-frontend/src/assets/stylesheets/global.scss @@ -2,6 +2,8 @@ @tailwind components; @tailwind utilities; +@import '@modrinth/ui/src/styles/tailwind-utilities.css'; + @font-face { font-family: 'bundled-minecraft-font-mrapp'; font-style: normal; diff --git a/apps/frontend/src/app.vue b/apps/frontend/src/app.vue index 5af1c1be9..a25d0e634 100644 --- a/apps/frontend/src/app.vue +++ b/apps/frontend/src/app.vue @@ -6,7 +6,12 @@ diff --git a/apps/frontend/src/assets/styles/layout.scss b/apps/frontend/src/assets/styles/layout.scss index 751974d9d..a7f038e3a 100644 --- a/apps/frontend/src/assets/styles/layout.scss +++ b/apps/frontend/src/assets/styles/layout.scss @@ -135,21 +135,6 @@ 'sidebar' / 100%; - .normal-page__ultimate-sidebar { - grid-area: ultimate-sidebar; - position: fixed; - bottom: 1rem; - right: 1rem; - z-index: 100; - max-width: calc(100% - 2rem); - max-height: calc(100vh - 2rem); - overflow-y: auto; - - > div { - box-shadow: 0 0 15px rgba(0, 0, 0, 0.3); - } - } - @media screen and (min-width: 1024px) { &.sidebar { grid-template: @@ -173,45 +158,6 @@ } } - @media screen and (min-width: 1400px) { - &.ultimate-sidebar { - max-width: calc(80rem + 0.75rem + 600px); - - grid-template: - 'header header ultimate-sidebar' auto - 'content sidebar ultimate-sidebar' auto - 'content dummy ultimate-sidebar' 1fr - / 1fr 18.75rem auto; - - .normal-page__header { - max-width: 80rem; - } - - .normal-page__ultimate-sidebar { - position: sticky; - top: 4.5rem; - bottom: unset; - right: unset; - z-index: unset; - align-self: start; - display: flex; - height: calc(100vh - 4.5rem * 2); - - > div { - box-shadow: none; - } - } - - &.alt-layout { - grid-template: - 'ultimate-sidebar header header' auto - 'ultimate-sidebar sidebar content' auto - 'ultimate-sidebar dummy content' 1fr - / auto 18.75rem 1fr; - } - } - } - .normal-page__sidebar { grid-area: sidebar; } diff --git a/apps/frontend/src/assets/styles/tailwind.css b/apps/frontend/src/assets/styles/tailwind.css index 2f3b0d6dd..ea20c6c69 100644 --- a/apps/frontend/src/assets/styles/tailwind.css +++ b/apps/frontend/src/assets/styles/tailwind.css @@ -1,3 +1,5 @@ +@import '@modrinth/ui/src/styles/tailwind-utilities.css'; + @tailwind base; @tailwind components; @tailwind utilities; diff --git a/apps/frontend/src/components/ui/NavRow.vue b/apps/frontend/src/components/ui/NavRow.vue deleted file mode 100644 index 5d2544fa8..000000000 --- a/apps/frontend/src/components/ui/NavRow.vue +++ /dev/null @@ -1,160 +0,0 @@ - - - - - diff --git a/apps/frontend/src/components/ui/dashboard/CreatorTaxFormModal.vue b/apps/frontend/src/components/ui/dashboard/CreatorTaxFormModal.vue index f1b36a3e7..cf3f4aa8c 100644 --- a/apps/frontend/src/components/ui/dashboard/CreatorTaxFormModal.vue +++ b/apps/frontend/src/components/ui/dashboard/CreatorTaxFormModal.vue @@ -158,12 +158,18 @@ import { SpinnerIcon, XIcon, } from '@modrinth/assets' -import { Admonition, ButtonStyled, Chips, injectNotificationManager, NewModal } from '@modrinth/ui' +import { + Admonition, + ButtonStyled, + Chips, + injectNotificationManager, + NewModal, + normalizeChildren, +} from '@modrinth/ui' import { defineMessages, useVIntl } from '@vintl/vintl' import { IntlFormatted } from '@vintl/vintl/components' import { type FormRequestResponse, useAvalara1099 } from '@/composables/avalara1099' -import { normalizeChildren } from '@/utils/vue-children.ts' const props = withDefaults( defineProps<{ diff --git a/apps/frontend/src/components/ui/dashboard/withdraw-stages/CompletionStage.vue b/apps/frontend/src/components/ui/dashboard/withdraw-stages/CompletionStage.vue index 32f0b6c1d..0987a9730 100644 --- a/apps/frontend/src/components/ui/dashboard/withdraw-stages/CompletionStage.vue +++ b/apps/frontend/src/components/ui/dashboard/withdraw-stages/CompletionStage.vue @@ -124,6 +124,7 @@ diff --git a/apps/frontend/src/pages/dashboard/collections.vue b/apps/frontend/src/pages/dashboard/collections.vue index 6c8b4ad7b..5ca86cd4e 100644 --- a/apps/frontend/src/pages/dashboard/collections.vue +++ b/apps/frontend/src/pages/dashboard/collections.vue @@ -27,7 +27,7 @@ :to="`/collection/following`" class="universal-card recessed collection" > - +
{{ formatMessage(commonMessages.followedProjectsLabel) }} @@ -57,7 +57,7 @@ :to="`/collection/${collection.id}`" class="universal-card recessed collection" > - +
{{ collection.name }} @@ -188,15 +188,6 @@ const orderedCollections = computed(() => { gap: var(--gap-md); margin-bottom: 0; - .icon { - width: 100% !important; - height: 6rem !important; - max-width: unset !important; - max-height: unset !important; - aspect-ratio: 1 / 1; - object-fit: cover; - } - .details { display: flex; flex-direction: column; diff --git a/apps/frontend/src/pages/settings/index.vue b/apps/frontend/src/pages/settings/index.vue index 698c3a68b..c39719de5 100644 --- a/apps/frontend/src/pages/settings/index.vue +++ b/apps/frontend/src/pages/settings/index.vue @@ -205,12 +205,11 @@ - diff --git a/packages/ui/src/components/base/index.ts b/packages/ui/src/components/base/index.ts new file mode 100644 index 000000000..cfaf81845 --- /dev/null +++ b/packages/ui/src/components/base/index.ts @@ -0,0 +1,56 @@ +export { default as Accordion } from './Accordion.vue' +export { default as Admonition } from './Admonition.vue' +export { default as AppearingProgressBar } from './AppearingProgressBar.vue' +export { default as AutoBrandIcon } from './AutoBrandIcon.vue' +export { default as AutoLink } from './AutoLink.vue' +export { default as Avatar } from './Avatar.vue' +export { default as Badge } from './Badge.vue' +export { default as BulletDivider } from './BulletDivider.vue' +export { default as Button } from './Button.vue' +export { default as ButtonStyled } from './ButtonStyled.vue' +export { default as Card } from './Card.vue' +export { default as Checkbox } from './Checkbox.vue' +export { default as Chips } from './Chips.vue' +export { default as Collapsible } from './Collapsible.vue' +export { default as CollapsibleRegion } from './CollapsibleRegion.vue' +export { default as Combobox } from './Combobox.vue' +export { default as ContentPageHeader } from './ContentPageHeader.vue' +export { default as CopyCode } from './CopyCode.vue' +export { default as DoubleIcon } from './DoubleIcon.vue' +export { default as DropArea } from './DropArea.vue' +export { default as DropdownSelect } from './DropdownSelect.vue' +export { default as EnvironmentIndicator } from './EnvironmentIndicator.vue' +export { default as ErrorInformationCard } from './ErrorInformationCard.vue' +export { default as FileInput } from './FileInput.vue' +export type { FilterBarOption } from './FilterBar.vue' +export { default as FilterBar } from './FilterBar.vue' +export { default as HeadingLink } from './HeadingLink.vue' +export { default as HorizontalRule } from './HorizontalRule.vue' +export { default as IconSelect } from './IconSelect.vue' +export type { JoinedButtonAction } from './JoinedButtons.vue' +export { default as JoinedButtons } from './JoinedButtons.vue' +export { default as LoadingIndicator } from './LoadingIndicator.vue' +export { default as ManySelect } from './ManySelect.vue' +export { default as MarkdownEditor } from './MarkdownEditor.vue' +export { default as OptionGroup } from './OptionGroup.vue' +export type { Option as OverflowMenuOption } from './OverflowMenu.vue' +export { default as OverflowMenu } from './OverflowMenu.vue' +export { default as Page } from './Page.vue' +export { default as Pagination } from './Pagination.vue' +export { default as PopoutMenu } from './PopoutMenu.vue' +export { default as PreviewSelectButton } from './PreviewSelectButton.vue' +export { default as ProgressBar } from './ProgressBar.vue' +export { default as ProgressSpinner } from './ProgressSpinner.vue' +export { default as ProjectCard } from './ProjectCard.vue' +export { default as RadialHeader } from './RadialHeader.vue' +export { default as RadioButtons } from './RadioButtons.vue' +export { default as ScrollablePanel } from './ScrollablePanel.vue' +export { default as ServerNotice } from './ServerNotice.vue' +export { default as SettingsLabel } from './SettingsLabel.vue' +export { default as SimpleBadge } from './SimpleBadge.vue' +export { default as Slider } from './Slider.vue' +export { default as SmartClickable } from './SmartClickable.vue' +export { default as TagItem } from './TagItem.vue' +export { default as Timeline } from './Timeline.vue' +export { default as Toggle } from './Toggle.vue' +export { default as UnsavedChangesPopup } from './UnsavedChangesPopup.vue' diff --git a/packages/ui/src/components/billing/index.ts b/packages/ui/src/components/billing/index.ts new file mode 100644 index 000000000..f2ef22353 --- /dev/null +++ b/packages/ui/src/components/billing/index.ts @@ -0,0 +1,5 @@ +export { default as AddPaymentMethodModal } from './AddPaymentMethodModal.vue' +export { default as ModrinthServersPurchaseModal } from './ModrinthServersPurchaseModal.vue' +export { default as PurchaseModal } from './PurchaseModal.vue' +export { default as ServersSpecs } from './ServersSpecs.vue' +export { default as ServersUpgradeModalWrapper } from './ServersUpgradeModalWrapper.vue' diff --git a/packages/ui/src/components/brand/index.ts b/packages/ui/src/components/brand/index.ts new file mode 100644 index 000000000..c3dc1ec40 --- /dev/null +++ b/packages/ui/src/components/brand/index.ts @@ -0,0 +1,2 @@ +export { default as AnimatedLogo } from './AnimatedLogo.vue' +export { default as TextLogo } from './TextLogo.vue' diff --git a/packages/ui/src/components/changelog/index.ts b/packages/ui/src/components/changelog/index.ts new file mode 100644 index 000000000..eb6ab0a19 --- /dev/null +++ b/packages/ui/src/components/changelog/index.ts @@ -0,0 +1 @@ +export { default as ChangelogEntry } from './ChangelogEntry.vue' diff --git a/packages/ui/src/components/chart/index.ts b/packages/ui/src/components/chart/index.ts new file mode 100644 index 000000000..7d215c98d --- /dev/null +++ b/packages/ui/src/components/chart/index.ts @@ -0,0 +1,2 @@ +export { default as Chart } from './Chart.vue' +export { default as CompactChart } from './CompactChart.vue' diff --git a/packages/ui/src/components/content/index.ts b/packages/ui/src/components/content/index.ts new file mode 100644 index 000000000..11714d04b --- /dev/null +++ b/packages/ui/src/components/content/index.ts @@ -0,0 +1,3 @@ +export { default as ContentListPanel } from './ContentListPanel.vue' +export type { Article as NewsArticle } from './NewsArticleCard.vue' +export { default as NewsArticleCard } from './NewsArticleCard.vue' diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts index 12ee057da..1f00fa63a 100644 --- a/packages/ui/src/components/index.ts +++ b/packages/ui/src/components/index.ts @@ -1,143 +1,16 @@ -// Base content -export { default as Accordion } from './base/Accordion.vue' -export { default as Admonition } from './base/Admonition.vue' -export { default as AppearingProgressBar } from './base/AppearingProgressBar.vue' -export { default as AutoBrandIcon } from './base/AutoBrandIcon.vue' -export { default as AutoLink } from './base/AutoLink.vue' -export { default as Avatar } from './base/Avatar.vue' -export { default as Badge } from './base/Badge.vue' -export { default as BulletDivider } from './base/BulletDivider.vue' -export { default as Button } from './base/Button.vue' -export { default as ButtonStyled } from './base/ButtonStyled.vue' -export { default as Card } from './base/Card.vue' -export { default as Checkbox } from './base/Checkbox.vue' -export { default as Chips } from './base/Chips.vue' -export { default as Collapsible } from './base/Collapsible.vue' -export { default as CollapsibleRegion } from './base/CollapsibleRegion.vue' -export { default as Combobox } from './base/Combobox.vue' -export { default as ContentPageHeader } from './base/ContentPageHeader.vue' -export { default as CopyCode } from './base/CopyCode.vue' -export { default as DoubleIcon } from './base/DoubleIcon.vue' -export { default as DropArea } from './base/DropArea.vue' -export { default as DropdownSelect } from './base/DropdownSelect.vue' -export { default as EnvironmentIndicator } from './base/EnvironmentIndicator.vue' -export { default as ErrorInformationCard } from './base/ErrorInformationCard.vue' -export { default as FileInput } from './base/FileInput.vue' -export type { FilterBarOption } from './base/FilterBar.vue' -export { default as FilterBar } from './base/FilterBar.vue' -export { default as HeadingLink } from './base/HeadingLink.vue' -export { default as IconSelect } from './base/IconSelect.vue' -export type { JoinedButtonAction } from './base/JoinedButtons.vue' -export { default as JoinedButtons } from './base/JoinedButtons.vue' -export { default as LoadingIndicator } from './base/LoadingIndicator.vue' -export { default as ManySelect } from './base/ManySelect.vue' -export { default as MarkdownEditor } from './base/MarkdownEditor.vue' -export { default as OptionGroup } from './base/OptionGroup.vue' -export type { Option as OverflowMenuOption } from './base/OverflowMenu.vue' -export { default as OverflowMenu } from './base/OverflowMenu.vue' -export { default as Page } from './base/Page.vue' -export { default as Pagination } from './base/Pagination.vue' -export { default as PopoutMenu } from './base/PopoutMenu.vue' -export { default as PreviewSelectButton } from './base/PreviewSelectButton.vue' -export { default as ProgressBar } from './base/ProgressBar.vue' -export { default as ProgressSpinner } from './base/ProgressSpinner.vue' -export { default as ProjectCard } from './base/ProjectCard.vue' -export { default as RadialHeader } from './base/RadialHeader.vue' -export { default as RadioButtons } from './base/RadioButtons.vue' -export { default as ScrollablePanel } from './base/ScrollablePanel.vue' -export { default as ServerNotice } from './base/ServerNotice.vue' -export { default as SettingsLabel } from './base/SettingsLabel.vue' -export { default as SimpleBadge } from './base/SimpleBadge.vue' -export { default as Slider } from './base/Slider.vue' -export { default as SmartClickable } from './base/SmartClickable.vue' -export { default as StatItem } from './base/StatItem.vue' -export { default as TagItem } from './base/TagItem.vue' -export { default as Timeline } from './base/Timeline.vue' -export { default as Toggle } from './base/Toggle.vue' -export { default as UnsavedChangesPopup } from './base/UnsavedChangesPopup.vue' - -// Branding -export { default as AnimatedLogo } from './brand/AnimatedLogo.vue' -export { default as TextLogo } from './brand/TextLogo.vue' - -// Changelog -export { default as ChangelogEntry } from './changelog/ChangelogEntry.vue' - -// Charts -export { default as Chart } from './chart/Chart.vue' -export { default as CompactChart } from './chart/CompactChart.vue' - -// Content -export { default as ContentListPanel } from './content/ContentListPanel.vue' -export type { Article as NewsArticle } from './content/NewsArticleCard.vue' -export { default as NewsArticleCard } from './content/NewsArticleCard.vue' - -// Modals -export { default as ConfirmModal } from './modal/ConfirmModal.vue' -export { default as Modal } from './modal/Modal.vue' -export { default as NewModal } from './modal/NewModal.vue' -export { default as ShareModal } from './modal/ShareModal.vue' -export type { Tab as TabbedModalTab } from './modal/TabbedModal.vue' -export { default as TabbedModal } from './modal/TabbedModal.vue' - -// Navigation -export { default as Breadcrumbs } from './nav/Breadcrumbs.vue' -export { default as NavItem } from './nav/NavItem.vue' -export { default as NavRow } from './nav/NavRow.vue' -export { default as NavStack } from './nav/NavStack.vue' -export { default as NotificationPanel } from './nav/NotificationPanel.vue' -export { default as PagewideBanner } from './nav/PagewideBanner.vue' - -// Project +export * from './affiliate' +export * from './base' +export * from './billing' +export * from './brand' +export * from './changelog' +export * from './chart' +export * from './content' +export * from './modal' +export * from './nav' +export * from './page' export * from './project' - -// Search -export { default as BrowseFiltersPanel } from './search/BrowseFiltersPanel.vue' -export { default as Categories } from './search/Categories.vue' -export { default as SearchDropdown } from './search/SearchDropdown.vue' -export { default as SearchFilter } from './search/SearchFilter.vue' -export { default as SearchFilterControl } from './search/SearchFilterControl.vue' -export { default as SearchFilterOption } from './search/SearchFilterOption.vue' -export { default as SearchSidebarFilter } from './search/SearchSidebarFilter.vue' - -// Affiliate -export { default as AffiliateLinkCard } from './affiliate/AffiliateLinkCard.vue' -export { default as AffiliateLinkCreateModal } from './affiliate/AffiliateLinkCreateModal.vue' - -// Billing -export { default as AddPaymentMethodModal } from './billing/AddPaymentMethodModal.vue' -export { default as ModrinthServersPurchaseModal } from './billing/ModrinthServersPurchaseModal.vue' -export { default as PurchaseModal } from './billing/PurchaseModal.vue' -export { default as ServersUpgradeModalWrapper } from './billing/ServersUpgradeModalWrapper.vue' - -// Skins -export { default as CapeButton } from './skin/CapeButton.vue' -export { default as CapeLikeTextButton } from './skin/CapeLikeTextButton.vue' -export { default as SkinButton } from './skin/SkinButton.vue' -export { default as SkinLikeTextButton } from './skin/SkinLikeTextButton.vue' -export { default as SkinPreviewRenderer } from './skin/SkinPreviewRenderer.vue' - -// Version -export { default as VersionChannelIndicator } from './version/VersionChannelIndicator.vue' -export { default as VersionFilterControl } from './version/VersionFilterControl.vue' -export { default as VersionSummary } from './version/VersionSummary.vue' - -// Settings -export { default as ThemeSelector } from './settings/ThemeSelector.vue' - -// Servers -export { default as ServersSpecs } from './billing/ServersSpecs.vue' -export { default as BackupCreateModal } from './servers/backups/BackupCreateModal.vue' -export { default as BackupDeleteModal } from './servers/backups/BackupDeleteModal.vue' -export { default as BackupItem } from './servers/backups/BackupItem.vue' -export { default as BackupRenameModal } from './servers/backups/BackupRenameModal.vue' -export { default as BackupRestoreModal } from './servers/backups/BackupRestoreModal.vue' -export { default as BackupWarning } from './servers/backups/BackupWarning.vue' -export { default as LoaderIcon } from './servers/icons/LoaderIcon.vue' -export { default as ServerIcon } from './servers/icons/ServerIcon.vue' -export { default as ServerInfoLabels } from './servers/labels/ServerInfoLabels.vue' -export { default as MedalBackgroundImage } from './servers/marketing/MedalBackgroundImage.vue' -export { default as MedalServerListing } from './servers/marketing/MedalServerListing.vue' -export type { PendingChange } from './servers/ServerListing.vue' -export { default as ServerListing } from './servers/ServerListing.vue' -export { default as ServersPromo } from './servers/ServersPromo.vue' +export * from './search' +export * from './servers' +export * from './settings' +export * from './skin' +export * from './version' diff --git a/packages/ui/src/components/modal/index.ts b/packages/ui/src/components/modal/index.ts new file mode 100644 index 000000000..89c4cf026 --- /dev/null +++ b/packages/ui/src/components/modal/index.ts @@ -0,0 +1,6 @@ +export { default as ConfirmModal } from './ConfirmModal.vue' +export { default as Modal } from './Modal.vue' +export { default as NewModal } from './NewModal.vue' +export { default as ShareModal } from './ShareModal.vue' +export type { Tab as TabbedModalTab } from './TabbedModal.vue' +export { default as TabbedModal } from './TabbedModal.vue' diff --git a/packages/ui/src/components/nav/NavItem.vue b/packages/ui/src/components/nav/NavItem.vue deleted file mode 100644 index 191767793..000000000 --- a/packages/ui/src/components/nav/NavItem.vue +++ /dev/null @@ -1,50 +0,0 @@ - - - - - diff --git a/packages/ui/src/components/nav/NavRow.vue b/packages/ui/src/components/nav/NavRow.vue deleted file mode 100644 index 45d64594d..000000000 --- a/packages/ui/src/components/nav/NavRow.vue +++ /dev/null @@ -1,166 +0,0 @@ - - - - - diff --git a/packages/ui/src/components/nav/NavStack.vue b/packages/ui/src/components/nav/NavStack.vue deleted file mode 100644 index 73cb4fdff..000000000 --- a/packages/ui/src/components/nav/NavStack.vue +++ /dev/null @@ -1,22 +0,0 @@ - - - diff --git a/packages/ui/src/components/nav/index.ts b/packages/ui/src/components/nav/index.ts new file mode 100644 index 000000000..19463e584 --- /dev/null +++ b/packages/ui/src/components/nav/index.ts @@ -0,0 +1,3 @@ +export { default as Breadcrumbs } from './Breadcrumbs.vue' +export { default as NotificationPanel } from './NotificationPanel.vue' +export { default as PagewideBanner } from './PagewideBanner.vue' diff --git a/packages/ui/src/components/page/NormalPage.vue b/packages/ui/src/components/page/NormalPage.vue new file mode 100644 index 000000000..ef3f89f84 --- /dev/null +++ b/packages/ui/src/components/page/NormalPage.vue @@ -0,0 +1,78 @@ + + + diff --git a/packages/ui/src/components/page/SidebarCard.vue b/packages/ui/src/components/page/SidebarCard.vue new file mode 100644 index 000000000..dc0eb38f1 --- /dev/null +++ b/packages/ui/src/components/page/SidebarCard.vue @@ -0,0 +1,20 @@ + + diff --git a/packages/ui/src/components/page/index.ts b/packages/ui/src/components/page/index.ts new file mode 100644 index 000000000..80eae38bd --- /dev/null +++ b/packages/ui/src/components/page/index.ts @@ -0,0 +1,2 @@ +export { default as NormalPage } from './NormalPage.vue' +export { default as SidebarCard } from './SidebarCard.vue' diff --git a/packages/ui/src/components/search/BrowseFiltersPanel.vue b/packages/ui/src/components/search/BrowseFiltersPanel.vue deleted file mode 100644 index 1c49530d0..000000000 --- a/packages/ui/src/components/search/BrowseFiltersPanel.vue +++ /dev/null @@ -1,107 +0,0 @@ - - - diff --git a/packages/ui/src/components/search/SearchDropdown.vue b/packages/ui/src/components/search/SearchDropdown.vue deleted file mode 100644 index 5cd0e381c..000000000 --- a/packages/ui/src/components/search/SearchDropdown.vue +++ /dev/null @@ -1,346 +0,0 @@ - - - - - diff --git a/packages/ui/src/components/search/SearchFilter.vue b/packages/ui/src/components/search/SearchFilter.vue deleted file mode 100644 index aaed9430a..000000000 --- a/packages/ui/src/components/search/SearchFilter.vue +++ /dev/null @@ -1,75 +0,0 @@ - - - - - diff --git a/packages/ui/src/components/search/index.ts b/packages/ui/src/components/search/index.ts new file mode 100644 index 000000000..a8a7bf07e --- /dev/null +++ b/packages/ui/src/components/search/index.ts @@ -0,0 +1,4 @@ +export { default as Categories } from './Categories.vue' +export { default as SearchFilterControl } from './SearchFilterControl.vue' +export { default as SearchFilterOption } from './SearchFilterOption.vue' +export { default as SearchSidebarFilter } from './SearchSidebarFilter.vue' diff --git a/packages/ui/src/components/servers/icons/index.ts b/packages/ui/src/components/servers/icons/index.ts new file mode 100644 index 000000000..71498d858 --- /dev/null +++ b/packages/ui/src/components/servers/icons/index.ts @@ -0,0 +1,2 @@ +export { default as LoaderIcon } from './LoaderIcon.vue' +export { default as ServerIcon } from './ServerIcon.vue' diff --git a/packages/ui/src/components/servers/index.ts b/packages/ui/src/components/servers/index.ts new file mode 100644 index 000000000..8a474342e --- /dev/null +++ b/packages/ui/src/components/servers/index.ts @@ -0,0 +1,7 @@ +export * from './backups' +export * from './icons' +export * from './labels' +export * from './marketing' +export type { PendingChange } from './ServerListing.vue' +export { default as ServerListing } from './ServerListing.vue' +export { default as ServersPromo } from './ServersPromo.vue' diff --git a/packages/ui/src/components/servers/labels/index.ts b/packages/ui/src/components/servers/labels/index.ts new file mode 100644 index 000000000..abd2e5cda --- /dev/null +++ b/packages/ui/src/components/servers/labels/index.ts @@ -0,0 +1 @@ +export { default as ServerInfoLabels } from './ServerInfoLabels.vue' diff --git a/packages/ui/src/components/servers/marketing/index.ts b/packages/ui/src/components/servers/marketing/index.ts new file mode 100644 index 000000000..58f353604 --- /dev/null +++ b/packages/ui/src/components/servers/marketing/index.ts @@ -0,0 +1,2 @@ +export { default as MedalBackgroundImage } from './MedalBackgroundImage.vue' +export { default as MedalServerListing } from './MedalServerListing.vue' diff --git a/packages/ui/src/components/settings/index.ts b/packages/ui/src/components/settings/index.ts new file mode 100644 index 000000000..9e6e827e0 --- /dev/null +++ b/packages/ui/src/components/settings/index.ts @@ -0,0 +1 @@ +export { default as ThemeSelector } from './ThemeSelector.vue' diff --git a/packages/ui/src/components/skin/index.ts b/packages/ui/src/components/skin/index.ts new file mode 100644 index 000000000..951224055 --- /dev/null +++ b/packages/ui/src/components/skin/index.ts @@ -0,0 +1,5 @@ +export { default as CapeButton } from './CapeButton.vue' +export { default as CapeLikeTextButton } from './CapeLikeTextButton.vue' +export { default as SkinButton } from './SkinButton.vue' +export { default as SkinLikeTextButton } from './SkinLikeTextButton.vue' +export { default as SkinPreviewRenderer } from './SkinPreviewRenderer.vue' diff --git a/packages/ui/src/components/version/index.ts b/packages/ui/src/components/version/index.ts new file mode 100644 index 000000000..67b33bde1 --- /dev/null +++ b/packages/ui/src/components/version/index.ts @@ -0,0 +1,3 @@ +export { default as VersionChannelIndicator } from './VersionChannelIndicator.vue' +export { default as VersionFilterControl } from './VersionFilterControl.vue' +export { default as VersionSummary } from './VersionSummary.vue' diff --git a/packages/ui/src/locales/en-US/index.json b/packages/ui/src/locales/en-US/index.json index 587d84883..dd4e335c3 100644 --- a/packages/ui/src/locales/en-US/index.json +++ b/packages/ui/src/locales/en-US/index.json @@ -551,6 +551,12 @@ "project-type.plugin.lowercase": { "defaultMessage": "{count, plural, one {plugin} other {plugins}}" }, + "project-type.project.category": { + "defaultMessage": "Projects" + }, + "project-type.project.lowercase": { + "defaultMessage": "{count, plural, one {project} other {projects}}" + }, "project-type.resourcepack.capital": { "defaultMessage": "{count, plural, one {Resource Pack} other {Resource Packs}}" }, diff --git a/packages/ui/src/providers/index.ts b/packages/ui/src/providers/index.ts index b47e27279..d4a06d627 100644 --- a/packages/ui/src/providers/index.ts +++ b/packages/ui/src/providers/index.ts @@ -79,6 +79,7 @@ export function createContext( } export * from './api-client' +export * from './page-context' export * from './project-page' export * from './server-context' export * from './web-notifications' diff --git a/packages/ui/src/providers/page-context.ts b/packages/ui/src/providers/page-context.ts new file mode 100644 index 000000000..9b77cd8ba --- /dev/null +++ b/packages/ui/src/providers/page-context.ts @@ -0,0 +1,14 @@ +import type { Ref } from 'vue' + +import { createContext } from '.' + +export interface PageContext { + // pages may render sidebar content in #sidebar-teleport-target instead of in the main layout when true + hierarchicalSidebarAvailable: Ref + showAds: Ref +} + +export const [injectPageContext, providePageContext] = createContext( + 'root', + 'pageContext', +) diff --git a/packages/ui/src/providers/user-page.ts b/packages/ui/src/providers/user-page.ts new file mode 100644 index 000000000..4c41d1e1f --- /dev/null +++ b/packages/ui/src/providers/user-page.ts @@ -0,0 +1,13 @@ +import type { Labrinth } from '@modrinth/api-client' +import type { Ref } from 'vue' + +import { createContext } from '.' + +export interface UserPageContext { + user: Ref +} + +export const [injectUserPageContext, provideUserPageContext] = createContext( + 'root', + 'userPageContext', +) diff --git a/packages/ui/src/styles/tailwind-utilities.css b/packages/ui/src/styles/tailwind-utilities.css new file mode 100644 index 000000000..adc17e4fd --- /dev/null +++ b/packages/ui/src/styles/tailwind-utilities.css @@ -0,0 +1,14 @@ +@layer utilities { + .heading-xl { + @apply m-0 text-xl font-semibold leading-none text-contrast; + } + .heading-2xl { + @apply m-0 text-2xl font-semibold leading-none text-contrast; + } + .heading-3xl { + @apply m-0 text-3xl font-semibold leading-none text-contrast; + } + .heading-4xl { + @apply m-0 text-4xl font-semibold leading-none text-contrast; + } +} diff --git a/packages/ui/src/utils/common-messages.ts b/packages/ui/src/utils/common-messages.ts index 36752e84d..b592ce4c6 100644 --- a/packages/ui/src/utils/common-messages.ts +++ b/packages/ui/src/utils/common-messages.ts @@ -437,6 +437,10 @@ export const commonProjectTypeCategoryMessages = defineMessages({ id: 'project-type.server.category', defaultMessage: 'Servers', }, + project: { + id: 'project-type.project.category', + defaultMessage: 'Projects', + }, }) export const commonProjectTypeTitleMessages = defineMessages({ @@ -468,6 +472,10 @@ export const commonProjectTypeTitleMessages = defineMessages({ id: 'project-type.server.capital', defaultMessage: '{count, plural, one {Server} other {Servers}}', }, + project: { + id: 'project-type.project.lowercase', + defaultMessage: '{count, plural, one {Project} other {Projects}}', + }, }) export const commonProjectTypeSentenceMessages = defineMessages({ @@ -499,6 +507,10 @@ export const commonProjectTypeSentenceMessages = defineMessages({ id: 'project-type.server.lowercase', defaultMessage: '{count, plural, one {server} other {servers}}', }, + project: { + id: 'project-type.project.lowercase', + defaultMessage: '{count, plural, one {project} other {projects}}', + }, }) export const commonSettingsMessages = defineMessages({ diff --git a/packages/ui/src/utils/index.ts b/packages/ui/src/utils/index.ts index 2a66710e4..85802d45c 100644 --- a/packages/ui/src/utils/index.ts +++ b/packages/ui/src/utils/index.ts @@ -3,3 +3,4 @@ export * from './game-modes' export * from './notices' export * from './savable' export * from './search' +export * from './vue-children' diff --git a/apps/frontend/src/utils/vue-children.ts b/packages/ui/src/utils/vue-children.ts similarity index 86% rename from apps/frontend/src/utils/vue-children.ts rename to packages/ui/src/utils/vue-children.ts index 419a77a56..ff12ee202 100644 --- a/apps/frontend/src/utils/vue-children.ts +++ b/packages/ui/src/utils/vue-children.ts @@ -8,7 +8,7 @@ import { createTextVNode, isVNode, toDisplayString, type VNode } from 'vue' * @returns Either the original VNode or a text VNode containing child converted * to a display string. */ -function normalizeChild(child: any): VNode { +function normalizeChild(child: unknown): VNode { return isVNode(child) ? child : createTextVNode(toDisplayString(child)) } @@ -20,6 +20,6 @@ function normalizeChild(child: any): VNode { * @param children Children to normalize. * @returns Children with all of non-VNodes converted to display strings. */ -export function normalizeChildren(children: any | any[]): VNode[] { +export function normalizeChildren(children: unknown | unknown[]): VNode[] { return Array.isArray(children) ? children.map(normalizeChild) : [normalizeChild(children)] }