chore: clean up a bunch of legacy styles (#5973)

* remove unused experimental-styles-within

* remove unused styles

* more cleanup + prepr

* Refactor nearly all legacy buttons to use ButtonStyled

* prepr

* Update MC account selector to modern version

* prepr

---------

Co-authored-by: Calum H. <calum@modrinth.com>
This commit is contained in:
Prospector
2026-05-03 11:53:06 -07:00
committed by GitHub
parent 8a72ee9968
commit 7dbbbe590f
153 changed files with 2596 additions and 3817 deletions

View File

@@ -1,64 +1,8 @@
/*
Base components
*/
.known-error .multiselect__tags {
border-color: var(--color-red) !important;
background-color: var(--color-warning-bg) !important;
&::placeholder {
color: var(--color-warning-text);
}
}
.grid-display {
display: grid;
grid-gap: var(--spacing-card-md);
grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
.grid-display__item {
display: flex;
flex-grow: 1;
flex-direction: column;
justify-content: flex-start;
background-color: var(--color-bg);
border-radius: var(--size-rounded-card);
padding: var(--spacing-card-lg);
gap: var(--spacing-card-md);
outline: 1px solid transparent;
.label {
color: var(--color-heading);
font-weight: bold;
font-size: 1rem;
}
.value {
color: var(--color-text-primary);
font-weight: bold;
font-size: 2rem;
}
.goto-link {
margin-top: auto;
}
}
&.width-12 {
grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
}
&.width-16 {
grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
}
}
/*
Cards and body styling
*/
.base-card {
@extend .padding-lg;
padding: var(--spacing-card-lg);
position: relative;
background-color: var(--color-raised-bg);
@@ -129,73 +73,9 @@
}
}
.padding-lg {
padding: var(--spacing-card-lg);
}
.padding-bg {
padding: var(--spacing-card-bg);
}
.padding-md {
padding: var(--spacing-card-md);
}
.padding-sm {
padding: var(--spacing-card-sm);
}
.padding-0 {
padding: 0;
}
.padding-block-lg {
padding-block: var(--spacing-card-lg);
}
.padding-block-bg {
padding-block: var(--spacing-card-bg);
}
.padding-block-md {
padding-block: var(--spacing-card-md);
}
.padding-block-sm {
padding-block: var(--spacing-card-sm);
}
.padding-block-0 {
padding-block: 0;
}
.padding-inline-lg {
padding-inline: var(--spacing-card-lg);
}
.padding-inline-bg {
padding-inline: var(--spacing-card-bg);
}
.padding-inline-md {
padding-inline: var(--spacing-card-md);
}
.padding-inline-sm {
padding-inline: var(--spacing-card-sm);
}
.padding-inline-0 {
padding-inline: 0;
}
.universal-body {
@extend .universal-labels;
.multiselect {
width: 15rem;
}
> :where(input + *, .input-group + *, .chips + *, .input-div + *) {
&:not(:empty) {
margin-block-start: var(--spacing-card-md);
@@ -340,19 +220,6 @@
}
}
.navigation-card {
@extend .base-card;
@extend .padding-inline-lg;
@extend .padding-block-md;
align-items: center;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
row-gap: 0.5rem;
min-height: 3.75rem;
}
/*
Other
*/
@@ -372,19 +239,6 @@
}
}
.title-link {
text-decoration: underline;
&:focus-visible,
&:hover {
color: var(--color-heading);
}
&:active {
color: var(--color-text-primary);
}
}
.button-base {
@extend .button-animation;
font-weight: 500;
@@ -455,36 +309,6 @@ tr.button-transparent {
}
}
.button-within {
transition:
opacity 0.5s ease-in-out,
filter 0.2s ease-in-out,
transform 0.05s ease-in-out,
outline 0.2s ease-in-out;
&:focus-visible:not(&.disabled),
&:hover:not(&.disabled) {
filter: brightness(0.85);
}
&:active:not(&.disabled) {
filter: brightness(0.8);
}
&.disabled {
cursor: not-allowed;
filter: grayscale(50%);
opacity: 0.5;
box-shadow: none;
&disabled,
&[disabled='true'] {
cursor: not-allowed;
box-shadow: none;
}
}
}
.button-color-base {
box-sizing: border-box;
--text-color: var(--color-button-text);
@@ -536,10 +360,6 @@ tr.button-transparent {
background: none;
box-shadow: none;
}
&.bold-button {
font-weight: bold;
}
}
.square-button {
@@ -570,24 +390,6 @@ tr.button-transparent {
flex-shrink: 0;
}
.header-button {
@extend .iconified-button;
box-sizing: content-box;
min-height: unset;
border-radius: var(--size-rounded-max);
white-space: nowrap;
padding: 0.5rem 0.75rem;
max-height: 1.75rem;
svg {
vertical-align: middle;
margin-right: 0.5rem;
height: 1.25rem;
width: 1.25rem;
}
}
.raised-button {
--background-color: var(--color-raised-bg);
box-shadow: var(--shadow-inset-sm), var(--shadow-raised);
@@ -608,11 +410,6 @@ tr.button-transparent {
--text-color: var(--color-brand-inverted);
}
.alt-brand-button {
--background-color: var(--color-brand-highlight);
--text-color: var(--color-text);
}
.button-group {
display: flex;
grid-gap: var(--spacing-card-sm);
@@ -621,21 +418,6 @@ tr.button-transparent {
justify-content: right;
}
.multiselect--above .multiselect__content-wrapper {
border-top: none !important;
border-top-left-radius: var(--size-rounded-card) !important;
border-top-right-radius: var(--size-rounded-card) !important;
}
.known-error .multiselect__tags {
border-color: var(--color-red) !important;
background-color: var(--color-warning-bg) !important;
&::placeholder {
color: var(--color-warning-text);
}
}
.error {
display: flex;
flex-direction: column;
@@ -707,36 +489,6 @@ textarea.known-error {
color: var(--color-link-active);
}
h1 {
.beta-badge {
font-size: 0.4em;
}
}
.beta-badge {
font-size: 0.7em;
padding: 0.2rem 0.4rem;
background-color: transparent;
box-sizing: border-box;
border: 2px solid var(--color-text);
color: var(--color-text);
border-radius: var(--size-rounded-max);
margin-left: 0.5rem;
font-weight: bold;
}
.router-link-exact-active,
h1,
h2,
h3 {
.beta-badge {
background-color: var(--color-button-text-active);
box-sizing: border-box;
border-color: transparent;
color: var(--color-raised-bg);
}
}
@media (prefers-reduced-motion) {
.button-animation,
button {
@@ -751,7 +503,6 @@ h3 {
}
.full-width-inputs {
.multiselect,
input,
.iconified-input {
width: 100%;
@@ -774,10 +525,6 @@ button {
max-width: 100%;
align-items: center;
.multiselect {
width: 15rem;
}
input {
flex-shrink: 2;
}
@@ -799,20 +546,6 @@ button {
}
}
.input-stack {
display: flex;
flex-direction: column;
> *:not(:last-child) {
margin-bottom: var(--spacing-card-sm);
}
> .multiselect {
width: unset;
height: inherit;
}
}
.text-input-wrapper {
display: flex;
flex-direction: row;
@@ -844,22 +577,6 @@ button {
user-select: none;
}
.text-input-wrapper__after {
display: flex;
color: var(--color-text);
padding: 0.5rem 1rem 0.5rem 0;
font-weight: var(--font-weight-medium);
min-height: 36px;
box-sizing: border-box;
width: fit-content;
align-items: center;
filter: grayscale(50%);
opacity: 0.5;
box-shadow: none;
flex-shrink: 0;
user-select: none;
}
input,
textarea {
border-radius: 0;
@@ -880,30 +597,6 @@ button {
}
}
.primary-stat {
align-items: center;
display: flex;
margin-bottom: 0.6rem;
.primary-stat__icon {
height: 1rem;
width: 1rem;
}
.primary-stat__text {
margin-left: 0.4rem;
}
.primary-stat__counter {
font-size: var(--font-size-lg);
font-weight: bold;
}
&.no-margin {
margin: 0;
}
}
.project-list {
width: 100%;
gap: var(--spacing-card-md);
@@ -1033,181 +726,87 @@ a.iconified-link {
}
}
a.subtle-link {
&:focus-visible,
&:hover {
text-decoration: underline;
filter: var(--hover-filter);
}
&:active {
filter: var(--active-filter);
}
}
.inline-svg svg,
svg.inline-svg {
vertical-align: middle;
}
// START STUFF FOR OMORPHIA
.experimental-styles-within {
.tag-list {
display: flex;
flex-wrap: wrap;
gap: var(--gap-4);
.tag-list {
display: flex;
flex-wrap: wrap;
gap: var(--gap-4);
}
.flex-card {
display: flex;
flex-direction: column;
gap: var(--gap-12);
padding: var(--gap-16);
h2 {
font-size: var(--text-18);
font-weight: var(--weight-extrabold);
color: var(--color-contrast);
line-height: initial;
margin: 0;
}
.tag-list__item {
background-color: var(--_bg-color, var(--color-button-bg));
padding: var(--gap-4) var(--gap-8);
border-radius: var(--radius-max);
h3 {
font-size: var(--text-16);
font-weight: var(--weight-bold);
font-size: var(--text-14);
display: flex;
gap: var(--gap-4);
align-items: center;
vertical-align: middle;
color: var(--_color, var(--color-secondary));
svg {
width: var(--icon-14);
height: var(--icon-14);
display: flex;
}
}
.status-list {
display: flex;
flex-direction: column;
gap: var(--gap-8);
padding-left: var(--gap-6);
color: var(--color-base);
font-weight: var(--weight-bold);
margin: 0;
}
.status-list__item {
display: flex;
align-items: center;
gap: var(--gap-4);
svg {
width: var(--icon-16);
height: var(--icon-16);
margin-right: var(--gap-4);
}
span {
color: var(--color-secondary);
font-style: italic;
font-weight: var(--weight-normal);
}
}
.status-list__item--color-green svg {
color: var(--color-green);
}
.status-list__item--color-orange svg {
color: var(--color-orange);
}
.status-list__item--color-red svg {
color: var(--color-red);
}
.status-list__item--color-blue svg {
color: var(--color-blue);
}
.status-list__item--color-purple svg {
color: var(--color-purple);
}
&.flex-card,
.flex-card {
> section {
display: flex;
flex-direction: column;
gap: var(--gap-12);
padding: var(--gap-16);
h2 {
font-size: var(--text-18);
font-weight: var(--weight-extrabold);
color: var(--color-contrast);
line-height: initial;
margin: 0;
}
h3 {
font-size: var(--text-16);
font-weight: var(--weight-bold);
color: var(--color-base);
margin: 0;
}
> section {
display: flex;
flex-direction: column;
gap: var(--gap-8);
}
}
.list-style {
display: flex;
flex-direction: column;
gap: var(--gap-12);
font-weight: var(--weight-bold);
hr {
width: 100%;
border-color: var(--color-button-border);
margin-block: var(--gap-2);
}
}
.iconified-list-item {
display: flex;
gap: var(--gap-8);
vertical-align: middle;
align-items: center;
width: fit-content;
}
}
svg {
width: var(--icon-16);
height: var(--icon-16);
}
.list-style {
display: flex;
flex-direction: column;
gap: var(--gap-12);
font-weight: var(--weight-bold);
> svg:first-child {
flex-shrink: 0;
}
hr {
width: 100%;
border-color: var(--color-button-border);
margin-block: var(--gap-2);
}
}
.iconified-list-item {
display: flex;
gap: var(--gap-8);
vertical-align: middle;
align-items: center;
width: fit-content;
svg {
width: var(--icon-16);
height: var(--icon-16);
}
.links-list {
@extend .list-style;
> a {
@extend .iconified-list-item;
&:hover {
text-decoration: underline;
}
}
> svg:first-child {
flex-shrink: 0;
}
}
.details-list {
@extend .list-style;
}
.details-list {
@extend .list-style;
}
.details-list__item {
@extend .iconified-list-item;
.details-list__item {
@extend .iconified-list-item;
.details-list__item__text--style-secondary {
color: var(--color-secondary);
font-weight: var(--weight-normal);
font-size: var(--text-14);
}
.details-list__item__text--style-secondary {
color: var(--color-secondary);
font-weight: var(--weight-normal);
font-size: var(--text-14);
}
}

View File

@@ -12,9 +12,11 @@
<div class="modal-body">
<div v-if="header" class="header">
<strong>{{ header }}</strong>
<button class="iconified-button icon-only transparent" @click="hide">
<XIcon />
</button>
<ButtonStyled circular type="transparent">
<button @click="hide">
<XIcon />
</button>
</ButtonStyled>
</div>
<div class="content">
<slot />
@@ -27,9 +29,11 @@
<script>
import { XIcon } from '@modrinth/assets'
import { ButtonStyled } from '@modrinth/ui'
export default {
components: {
ButtonStyled,
XIcon,
},
props: {

View File

@@ -223,9 +223,7 @@ function developerModeIncrement() {
</script>
<template>
<footer
class="footer-brand-background experimental-styles-within border-0 border-t-[1px] border-solid"
>
<footer class="footer-brand-background border-0 border-t-[1px] border-solid">
<div class="mx-auto flex max-w-screen-xl flex-col gap-6 p-6 pb-20 sm:px-12 md:py-12">
<div
class="grid grid-cols-1 gap-4 text-primary md:grid-cols-[1fr_2fr] lg:grid-cols-[auto_auto_auto_auto_auto]"

View File

@@ -199,47 +199,9 @@
</span>
<div v-if="compact" class="notification__actions">
<template v-if="type === 'team_invite' || type === 'organization_invite'">
<button
v-tooltip="`Accept`"
class="iconified-button square-button brand-button button-transparent"
@click="
() => {
acceptTeamInvite(notification.body.team_id)
read()
}
"
>
<CheckIcon />
</button>
<button
v-tooltip="`Decline`"
class="iconified-button square-button danger-button button-transparent"
@click="
() => {
removeSelfFromTeam(notification.body.team_id)
read()
}
"
>
<XIcon />
</button>
</template>
<button
v-else-if="!notification.read"
v-tooltip="`Mark as read`"
class="iconified-button square-button button-transparent"
@click="read()"
>
<XIcon />
</button>
</div>
<div v-else class="notification__actions">
<div v-if="type !== null" class="input-group">
<template
v-if="(type === 'team_invite' || type === 'organization_invite') && !notification.read"
>
<ButtonStyled circular color="brand" type="transparent">
<button
class="iconified-button brand-button"
v-tooltip="`Accept`"
@click="
() => {
acceptTeamInvite(notification.body.team_id)
@@ -248,10 +210,11 @@
"
>
<CheckIcon />
Accept
</button>
</ButtonStyled>
<ButtonStyled circular color="red" type="transparent">
<button
class="iconified-button danger-button"
v-tooltip="`Decline`"
@click="
() => {
removeSelfFromTeam(notification.body.team_id)
@@ -260,51 +223,75 @@
"
>
<XIcon />
Decline
</button>
</template>
<button
v-else-if="!notification.read"
class="iconified-button"
:class="{ 'raised-button': raised }"
@click="read()"
>
<CheckIcon />
Mark as read
</ButtonStyled>
</template>
<ButtonStyled v-else-if="!notification.read" circular type="transparent">
<button v-tooltip="`Mark as read`" @click="read()">
<XIcon />
</button>
</ButtonStyled>
</div>
<div v-else class="notification__actions">
<div v-if="type !== null" class="input-group">
<template
v-if="(type === 'team_invite' || type === 'organization_invite') && !notification.read"
>
<ButtonStyled color="brand">
<button
@click="
() => {
acceptTeamInvite(notification.body.team_id)
read()
}
"
>
<CheckIcon />
Accept
</button>
</ButtonStyled>
<ButtonStyled color="red">
<button
@click="
() => {
removeSelfFromTeam(notification.body.team_id)
read()
}
"
>
<XIcon />
Decline
</button>
</ButtonStyled>
</template>
<ButtonStyled v-else-if="!notification.read">
<button @click="read()">
<CheckIcon />
Mark as read
</button>
</ButtonStyled>
<CopyCode v-if="flags.developerMode" :text="notification.id" />
</div>
<div v-else class="input-group">
<nuxt-link
v-if="notification.link && notification.link !== '#'"
class="iconified-button"
:class="{ 'raised-button': raised }"
:to="notification.link"
target="_blank"
>
<ExternalIcon />
Open link
</nuxt-link>
<button
v-for="(action, actionIndex) in notification.actions"
:key="actionIndex"
class="iconified-button"
:class="{ 'raised-button': raised }"
@click="performAction(notification, actionIndex)"
>
<CheckIcon v-if="action.title === 'Accept'" />
<XIcon v-else-if="action.title === 'Deny'" />
{{ action.title }}
</button>
<button
v-if="notification.actions.length === 0 && !notification.read"
class="iconified-button"
:class="{ 'raised-button': raised }"
@click="performAction(notification, null)"
>
<CheckIcon />
Mark as read
</button>
<ButtonStyled v-if="notification.link && notification.link !== '#'">
<nuxt-link :to="notification.link" target="_blank">
<ExternalIcon />
Open link
</nuxt-link>
</ButtonStyled>
<ButtonStyled v-for="(action, actionIndex) in notification.actions" :key="actionIndex">
<button @click="performAction(notification, actionIndex)">
<CheckIcon v-if="action.title === 'Accept'" />
<XIcon v-else-if="action.title === 'Deny'" />
{{ action.title }}
</button>
</ButtonStyled>
<ButtonStyled v-if="notification.actions.length === 0 && !notification.read">
<button @click="performAction(notification, null)">
<CheckIcon />
Mark as read
</button>
</ButtonStyled>
<CopyCode v-if="flags.developerMode" :text="notification.id" />
</div>
</div>
@@ -325,6 +312,7 @@ import {
} from '@modrinth/assets'
import {
Avatar,
ButtonStyled,
Categories,
CopyCode,
DoubleIcon,
@@ -594,10 +582,6 @@ function getLoaderCategories(ver) {
gap: var(--spacing-card-sm);
}
.notification__actions .iconified-button.square-button svg {
margin-right: 0;
}
.unknown-type {
color: var(--color-red);
}
@@ -618,4 +602,8 @@ function getLoaderCategories(ver) {
color: var(--color-blue);
}
}
.title-link {
@apply underline hover:brightness-[--hover-brightness];
}
</style>

View File

@@ -1,7 +1,7 @@
<template>
<nav
ref="scrollContainer"
class="card-shadow experimental-styles-within relative flex w-fit overflow-x-auto rounded-full bg-bg-raised p-1 text-sm font-bold"
class="card-shadow relative flex w-fit overflow-x-auto rounded-full bg-bg-raised p-1 text-sm font-bold"
>
<button
v-for="(option, index) in options"

View File

@@ -67,50 +67,55 @@
</div>
<div class="table-cell">
<nuxt-link
class="btn icon-only"
:to="`/project/${project.slug ? project.slug : project.id}/settings`"
>
<SettingsIcon />
</nuxt-link>
<ButtonStyled circular>
<nuxt-link :to="`/project/${project.slug ? project.slug : project.id}/settings`">
<SettingsIcon />
</nuxt-link>
</ButtonStyled>
</div>
</div>
</div>
<div class="push-right input-group">
<Button @click="$refs.modalOpen?.hide()">
<XIcon />
Cancel
</Button>
<Button :disabled="!selectedProjects?.length" color="primary" @click="onSubmitHandler()">
<TransferIcon />
<span>
Transfer
<ButtonStyled type="outlined">
<button @click="$refs.modalOpen?.hide()">
<XIcon />
Cancel
</button>
</ButtonStyled>
<ButtonStyled color="brand">
<button :disabled="!selectedProjects?.length" @click="onSubmitHandler()">
<TransferIcon />
<span>
{{
selectedProjects.length === props.projects.length
? 'All'
: selectedProjects.length
}}
Transfer
<span>
{{
selectedProjects.length === props.projects.length
? 'All'
: selectedProjects.length
}}
</span>
<span>
{{ ' ' }}
{{ selectedProjects.length === 1 ? 'project' : 'projects' }}
</span>
</span>
<span>
{{ ' ' }}
{{ selectedProjects.length === 1 ? 'project' : 'projects' }}
</span>
</span>
</Button>
</button>
</ButtonStyled>
</div>
</div>
</Modal>
<Button @click="$refs.modalOpen?.show()">
<TransferIcon />
<span>Transfer projects</span>
</Button>
<ButtonStyled>
<button @click="$refs.modalOpen?.show()">
<TransferIcon />
<span>Transfer projects</span>
</button>
</ButtonStyled>
</div>
</template>
<script setup>
import { BoxIcon, SettingsIcon, TransferIcon, XIcon } from '@modrinth/assets'
import { Avatar, Button, Checkbox, CopyCode, Modal } from '@modrinth/ui'
import { Avatar, ButtonStyled, Checkbox, CopyCode, Modal } from '@modrinth/ui'
import { formatProjectType } from '@modrinth/utils'
const EDIT_DETAILS = 1 << 2

View File

@@ -1,6 +1,6 @@
<script setup lang="ts">
import { SettingsIcon } from '@modrinth/assets'
import { defineMessages, PagewideBanner, useVIntl } from '@modrinth/ui'
import { ButtonStyled, defineMessages, PagewideBanner, useVIntl } from '@modrinth/ui'
const { formatMessage } = useVIntl()
@@ -30,10 +30,12 @@ const messages = defineMessages({
<span>{{ formatMessage(messages.description) }}</span>
</template>
<template #actions>
<nuxt-link class="btn" to="/settings/billing">
<SettingsIcon aria-hidden="true" />
{{ formatMessage(messages.action) }}
</nuxt-link>
<ButtonStyled>
<nuxt-link to="/settings/billing">
<SettingsIcon aria-hidden="true" />
{{ formatMessage(messages.action) }}
</nuxt-link>
</ButtonStyled>
</template>
</PagewideBanner>
</template>

View File

@@ -1,6 +1,12 @@
<script setup lang="ts">
import { SettingsIcon } from '@modrinth/assets'
import { defineMessages, injectNotificationManager, PagewideBanner, useVIntl } from '@modrinth/ui'
import {
ButtonStyled,
defineMessages,
injectNotificationManager,
PagewideBanner,
useVIntl,
} from '@modrinth/ui'
import { FetchError } from 'ofetch'
const { addNotification } = injectNotificationManager()
@@ -91,13 +97,17 @@ async function handleResendEmailVerification() {
</span>
</template>
<template #actions>
<button v-if="hasEmail" class="btn" @click="handleResendEmailVerification">
{{ formatMessage(verifyEmailBannerMessages.action) }}
</button>
<nuxt-link v-else class="btn" to="/settings/account">
<SettingsIcon aria-hidden="true" />
{{ formatMessage(addEmailBannerMessages.action) }}
</nuxt-link>
<ButtonStyled v-if="hasEmail">
<button @click="handleResendEmailVerification">
{{ formatMessage(verifyEmailBannerMessages.action) }}
</button>
</ButtonStyled>
<ButtonStyled v-else>
<nuxt-link to="/settings/account">
<SettingsIcon aria-hidden="true" />
{{ formatMessage(addEmailBannerMessages.action) }}
</nuxt-link>
</ButtonStyled>
</template>
</PagewideBanner>
</template>

View File

@@ -81,15 +81,21 @@
</span>
</h2>
<div class="chart-controls__buttons">
<Button v-tooltip="'Toggle project colors'" icon-only @click="onToggleColors">
<PaletteIcon />
</Button>
<Button v-tooltip="'Download this data as CSV'" icon-only @click="onDownloadSetAsCSV">
<DownloadIcon />
</Button>
<Button v-tooltip="'Refresh the chart'" icon-only @click="resetCharts">
<UpdatedIcon />
</Button>
<ButtonStyled circular type="outlined">
<button v-tooltip="'Toggle project colors'" @click="onToggleColors">
<PaletteIcon />
</button>
</ButtonStyled>
<ButtonStyled circular type="outlined">
<button v-tooltip="'Download this data as CSV'" @click="onDownloadSetAsCSV">
<DownloadIcon />
</button>
</ButtonStyled>
<ButtonStyled circular type="outlined">
<button v-tooltip="'Refresh the chart'" @click="resetCharts">
<UpdatedIcon />
</button>
</ButtonStyled>
<DropdownSelect
v-model="selectedRange"
class="range-dropdown"
@@ -156,8 +162,8 @@
<template v-for="project in selectedDataSetProjects" :key="project">
<button
v-tooltip="project.title"
:class="`legend__item button-base btn-transparent ${
!projectIsOnDisplay(project.id) ? 'btn-dimmed' : ''
:class="`legend__item button-base legend__item-transparent ${
!projectIsOnDisplay(project.id) ? 'legend__item-dimmed' : ''
}`"
@click="
() =>
@@ -311,7 +317,7 @@
<script setup lang="ts">
import { DownloadIcon, PaletteIcon, UpdatedIcon } from '@modrinth/assets'
import {
Button,
ButtonStyled,
Card,
DropdownSelect,
useCompactNumber,
@@ -842,7 +848,7 @@ const defaultRanges: RangeObject[] = [
}
}
.btn-transparent {
.legend__item-transparent {
background-color: transparent;
border: none;
cursor: pointer;
@@ -851,7 +857,7 @@ const defaultRanges: RangeObject[] = [
font-weight: var(--font-weight-regular);
}
.btn-dimmed {
.legend__item-dimmed {
opacity: 0.5;
}

View File

@@ -38,13 +38,13 @@
{{ formatMessage(messages.collectionInfo, { count: projectIds.length }) }}
</p>
<div class="flex justify-end gap-2">
<ButtonStyled class="w-24">
<ButtonStyled type="outlined">
<button @click="modal.hide()">
<XIcon aria-hidden="true" />
{{ formatMessage(commonMessages.cancelButton) }}
</button>
</ButtonStyled>
<ButtonStyled color="brand" class="w-36">
<ButtonStyled color="brand">
<button :disabled="hasHitLimit" @click="create">
<PlusIcon aria-hidden="true" />
{{ formatMessage(messages.createCollection) }}

View File

@@ -59,13 +59,13 @@
{{ formatMessage(messages.ownershipInfo) }}
</p>
<div class="flex justify-end gap-2">
<ButtonStyled class="w-24">
<ButtonStyled type="outlined">
<button @click="hide">
<XIcon aria-hidden="true" />
{{ formatMessage(commonMessages.cancelButton) }}
</button>
</ButtonStyled>
<ButtonStyled color="brand" class="w-40">
<ButtonStyled color="brand">
<button :disabled="hasHitLimit" @click="createOrganization">
<PlusIcon aria-hidden="true" />
{{ formatMessage(messages.createOrganization) }}

View File

@@ -110,13 +110,13 @@
<span>{{ formatMessage(messages.summaryDescription) }}</span>
</div>
<div class="flex justify-end gap-2.5">
<ButtonStyled class="w-24">
<ButtonStyled type="outlined">
<button @click="cancel">
<XIcon aria-hidden="true" />
{{ formatMessage(commonMessages.cancelButton) }}
</button>
</ButtonStyled>
<ButtonStyled color="brand" class="w-32">
<ButtonStyled color="brand">
<button v-tooltip="missingFieldsTooltip" :disabled="disableCreate" @click="createProject">
<PlusIcon aria-hidden="true" />
{{ formatMessage(messages.createProject) }}

View File

@@ -58,24 +58,20 @@
</div>
<template #actions>
<div v-if="currentStage === 'completion'" class="mt-4 flex w-full gap-3">
<ButtonStyled class="flex-1">
<button class="w-full text-contrast" @click="handleClose">
<ButtonStyled>
<button class="w-full flex-1 text-contrast" @click="handleClose">
{{ formatMessage(commonMessages.closeButton) }}
</button>
</ButtonStyled>
<ButtonStyled class="flex-1">
<button class="w-full text-contrast" @click="handleViewTransactions">
<ButtonStyled>
<button class="w-full flex-1 text-contrast" @click="handleViewTransactions">
{{ formatMessage(messages.transactionsButton) }}
</button>
</ButtonStyled>
</div>
<div v-else class="mt-4 flex flex-col justify-end gap-2 sm:flex-row">
<ButtonStyled type="outlined">
<button
class="!border-surface-5"
:disabled="leftButtonConfig.disabled"
@click="leftButtonConfig.handler"
>
<button :disabled="leftButtonConfig.disabled" @click="leftButtonConfig.handler">
<component :is="leftButtonConfig.icon" />
{{ leftButtonConfig.label }}
</button>

View File

@@ -30,9 +30,13 @@
</ButtonStyled>
</div>
</div>
<div v-if="!collapsed" class="grid-display width-16 mt-4">
<div v-for="nag in visibleNags" :key="nag.id" class="grid-display__item">
<span class="flex items-center gap-2 font-semibold">
<div v-if="!collapsed" class="mt-4 grid grid-cols-[repeat(auto-fit,minmax(15rem,1fr))] gap-2">
<div
v-for="nag in visibleNags"
:key="nag.id"
class="flex flex-col gap-3 rounded-2xl border border-solid border-surface-5 bg-surface-2 p-4"
>
<span class="flex items-center gap-2 font-medium text-contrast">
<component
:is="nag.icon || getDefaultIcon(nag.status)"
v-tooltip="getStatusTooltip(nag.status)"
@@ -52,7 +56,7 @@
:to="`/${project.project_type}/${project.slug ? project.slug : project.id}/${
nag.link.path
}`"
class="goto-link"
class="goto-link mt-auto"
>
{{ getFormattedMessage(nag.link.title) }}
<ChevronRightIcon aria-hidden="true" class="featured-header-chevron" />

View File

@@ -154,8 +154,8 @@
@update-thread="updateThread"
>
<template #closedActions>
<ButtonStyled v-if="isStaff(auth.user)" color="green" class="mt-2">
<button class="w-full gap-2 sm:w-auto" @click="reopenReport()">
<ButtonStyled v-if="isStaff(auth.user)" color="green">
<button class="mt-2 w-full gap-2 sm:w-auto" @click="reopenReport()">
<CheckCircleIcon class="size-4" />
Reopen Thread
</button>

View File

@@ -1201,7 +1201,6 @@ async function handleSubmitReview(verdict: 'safe' | 'unsafe') {
:href="file.download_url"
:title="`Download ${file.file_name}`"
:download="file.file_name"
class="!border-px !border-surface-4"
tabindex="0"
>
<DownloadIcon /> Download
@@ -1362,7 +1361,6 @@ async function handleSubmitReview(verdict: 'safe' | 'unsafe') {
"
>
<button
class="!border-[1px]"
:disabled="updatingDetails.has(flag.detail.id)"
@click="updateDetailStatus(flag.detail.id, 'safe')"
>
@@ -1379,7 +1377,6 @@ async function handleSubmitReview(verdict: 'safe' | 'unsafe') {
"
>
<button
class="!border-[1px]"
:disabled="updatingDetails.has(flag.detail.id)"
@click="updateDetailStatus(flag.detail.id, 'unsafe')"
>

View File

@@ -40,11 +40,7 @@
</div>
</div>
<ButtonStyled v-if="content" type="outlined">
<button
class="!border-[1px]"
:disabled="!hasPermission"
@click="handleSwitchCompatibility"
>
<button :disabled="!hasPermission" @click="handleSwitchCompatibility">
<ArrowLeftRightIcon />
Switch type
</button>

View File

@@ -21,10 +21,10 @@
</span>
</div>
</div>
<ButtonStyled color="medal-promo" type="outlined" size="large" class="z-10 my-auto mt-2">
<ButtonStyled color="medal-promo" type="outlined" size="large">
<nuxt-link
to="https://medal.tv/modrinth"
class="z-10 flex w-full items-center justify-center gap-1 md:mt-0 md:w-auto"
class="z-10 my-auto mt-2 flex w-full items-center justify-center gap-1 md:mt-0 md:w-auto"
>{{ formatMessage(messages.learnMoreButton) }} <ExternalIcon
/></nuxt-link>
</ButtonStyled>

View File

@@ -23,14 +23,12 @@
I confirm that I have properly addressed the moderators' comments.
</Checkbox>
<div class="input-group push-right">
<button
class="iconified-button moderation-button"
:disabled="!submissionConfirmation"
@click="resubmit()"
>
<ScaleIcon aria-hidden="true" />
Resubmit for review
</button>
<ButtonStyled color="orange">
<button :disabled="!submissionConfirmation" @click="resubmit()">
<ScaleIcon aria-hidden="true" />
Resubmit for review
</button>
</ButtonStyled>
</div>
</div>
</Modal>
@@ -55,14 +53,12 @@
I acknowledge that the moderators do not actively monitor the thread.
</Checkbox>
<div class="input-group push-right">
<button
class="btn btn-primary"
:disabled="!replyConfirmation"
@click="sendReplyFromModal()"
>
<ReplyIcon aria-hidden="true" />
Reply to thread
</button>
<ButtonStyled color="brand">
<button :disabled="!replyConfirmation" @click="sendReplyFromModal()">
<ReplyIcon aria-hidden="true" />
Reply to thread
</button>
</ButtonStyled>
</div>
</div>
</Modal>
@@ -85,10 +81,12 @@
</div>
<template v-if="report && report.closed">
<p>This thread is closed and new messages cannot be sent to it.</p>
<button v-if="isStaff(auth.user)" class="iconified-button" @click="reopenReport()">
<CheckCircleIcon aria-hidden="true" />
Reopen thread
</button>
<ButtonStyled v-if="isStaff(auth.user)">
<button @click="reopenReport()">
<CheckCircleIcon aria-hidden="true" />
Reopen thread
</button>
</ButtonStyled>
</template>
<template v-else-if="!report || !report.closed">
<div class="markdown-editor-spacing">
@@ -99,196 +97,175 @@
/>
</div>
<div class="input-group">
<button
v-if="sortedMessages.length > 0"
class="btn btn-primary"
:disabled="!replyBody"
@click="isApproved(project) && !isStaff(auth.user) ? openReplyModal() : sendReply()"
>
<ReplyIcon aria-hidden="true" />
Reply
</button>
<button
v-else
class="btn btn-primary"
:disabled="!replyBody"
@click="isApproved(project) && !isStaff(auth.user) ? openReplyModal() : sendReply()"
>
<SendIcon aria-hidden="true" />
Send
</button>
<button
v-if="isStaff(auth.user)"
class="btn"
:disabled="!replyBody"
@click="sendReply(null, true)"
>
<ScaleIcon aria-hidden="true" />
Add private note
</button>
<ButtonStyled color="brand">
<button
v-if="sortedMessages.length > 0"
:disabled="!replyBody"
@click="isApproved(project) && !isStaff(auth.user) ? openReplyModal() : sendReply()"
>
<ReplyIcon aria-hidden="true" />
Reply
</button>
<button
v-else
:disabled="!replyBody"
@click="isApproved(project) && !isStaff(auth.user) ? openReplyModal() : sendReply()"
>
<SendIcon aria-hidden="true" />
Send
</button>
</ButtonStyled>
<ButtonStyled v-if="isStaff(auth.user)">
<button :disabled="!replyBody" @click="sendReply(null, true)">
<ScaleIcon aria-hidden="true" />
Add private note
</button>
</ButtonStyled>
<template v-if="currentMember && !isStaff(auth.user)">
<template v-if="isRejected(project)">
<button
v-if="replyBody"
class="iconified-button moderation-button"
@click="openResubmitModal(true)"
>
<ScaleIcon aria-hidden="true" />
Resubmit for review with reply
</button>
<button
v-else
class="iconified-button moderation-button"
@click="openResubmitModal(false)"
>
<ScaleIcon aria-hidden="true" />
Resubmit for review
</button>
<ButtonStyled color="orange">
<button v-if="replyBody" @click="openResubmitModal(true)">
<ScaleIcon aria-hidden="true" />
Resubmit for review with reply
</button>
<button v-else @click="openResubmitModal(false)">
<ScaleIcon aria-hidden="true" />
Resubmit for review
</button>
</ButtonStyled>
</template>
</template>
<div class="spacer"></div>
<div class="input-group extra-options">
<template v-if="report">
<template v-if="isStaff(auth.user)">
<button
v-if="replyBody"
class="iconified-button danger-button"
@click="closeReport(true)"
>
<CheckCircleIcon aria-hidden="true" />
Close with reply
</button>
<button v-else class="iconified-button danger-button" @click="closeReport()">
<CheckCircleIcon aria-hidden="true" />
Close thread
</button>
<ButtonStyled color="red">
<button v-if="replyBody" @click="closeReport(true)">
<CheckCircleIcon aria-hidden="true" />
Close with reply
</button>
<button v-else @click="closeReport()">
<CheckCircleIcon aria-hidden="true" />
Close thread
</button>
</ButtonStyled>
</template>
</template>
<template v-if="project">
<template v-if="isStaff(auth.user)">
<button
v-if="replyBody"
class="btn btn-green"
:disabled="isApproved(project)"
@click="sendReply(requestedStatus)"
>
<CheckIcon aria-hidden="true" />
Approve with reply
</button>
<button
v-else
class="btn btn-green"
:disabled="isApproved(project)"
@click="setStatus(requestedStatus)"
>
<CheckIcon aria-hidden="true" />
Approve
</button>
<ButtonStyled v-if="replyBody" color="green">
<button :disabled="isApproved(project)" @click="sendReply(requestedStatus)">
<CheckIcon aria-hidden="true" />
Approve with reply
</button>
</ButtonStyled>
<ButtonStyled v-else color="green">
<button :disabled="isApproved(project)" @click="setStatus(requestedStatus)">
<CheckIcon aria-hidden="true" />
Approve
</button>
</ButtonStyled>
<div class="joined-buttons">
<button
v-if="replyBody"
class="btn btn-danger"
:disabled="project.status === 'rejected'"
@click="sendReply('rejected')"
>
<XIcon aria-hidden="true" />
Reject with reply
</button>
<button
v-else
class="btn btn-danger"
:disabled="project.status === 'rejected'"
@click="setStatus('rejected')"
>
<XIcon aria-hidden="true" />
Reject
</button>
<OverflowMenu
class="btn btn-danger btn-dropdown-animation icon-only"
:options="
replyBody
? [
{
id: 'withhold-reply',
color: 'danger',
action: () => {
sendReply('withheld')
<ButtonStyled v-if="replyBody" color="red">
<button :disabled="project.status === 'rejected'" @click="sendReply('rejected')">
<XIcon aria-hidden="true" />
Reject with reply
</button>
</ButtonStyled>
<ButtonStyled v-else color="red">
<button :disabled="project.status === 'rejected'" @click="setStatus('rejected')">
<XIcon aria-hidden="true" />
Reject
</button>
</ButtonStyled>
<ButtonStyled color="red">
<OverflowMenu
class="btn-dropdown-animation"
:options="
replyBody
? [
{
id: 'withhold-reply',
color: 'danger',
action: () => {
sendReply('withheld')
},
hoverFilled: true,
disabled: project.status === 'withheld',
},
hoverFilled: true,
disabled: project.status === 'withheld',
},
{
id: 'set-to-draft-reply',
action: () => {
sendReply('draft')
{
id: 'set-to-draft-reply',
action: () => {
sendReply('draft')
},
hoverFilled: true,
disabled: project.status === 'draft',
},
hoverFilled: true,
disabled: project.status === 'draft',
},
{
id: 'send-to-review-reply',
action: () => {
sendReply('processing', true)
{
id: 'send-to-review-reply',
action: () => {
sendReply('processing', true)
},
hoverFilled: true,
disabled: project.status === 'processing',
},
hoverFilled: true,
disabled: project.status === 'processing',
},
]
: [
{
id: 'withhold',
color: 'danger',
action: () => {
setStatus('withheld')
]
: [
{
id: 'withhold',
color: 'danger',
action: () => {
setStatus('withheld')
},
hoverFilled: true,
disabled: project.status === 'withheld',
},
hoverFilled: true,
disabled: project.status === 'withheld',
},
{
id: 'set-to-draft',
action: () => {
setStatus('draft')
{
id: 'set-to-draft',
action: () => {
setStatus('draft')
},
hoverFilled: true,
disabled: project.status === 'draft',
},
hoverFilled: true,
disabled: project.status === 'draft',
},
{
id: 'send-to-review',
action: () => {
setStatus('processing')
{
id: 'send-to-review',
action: () => {
setStatus('processing')
},
hoverFilled: true,
disabled: project.status === 'processing',
},
hoverFilled: true,
disabled: project.status === 'processing',
},
]
"
>
<DropdownIcon style="rotate: 180deg" aria-hidden="true" />
<template #withhold-reply>
<EyeOffIcon aria-hidden="true" />
Withhold with reply
</template>
<template #withhold>
<EyeOffIcon aria-hidden="true" />
Withhold
</template>
<template #set-to-draft-reply>
<FileTextIcon aria-hidden="true" />
Set to draft with reply
</template>
<template #set-to-draft>
<FileTextIcon aria-hidden="true" />
Set to draft
</template>
<template #send-to-review-reply>
<ScaleIcon aria-hidden="true" />
Send to review with reply
</template>
<template #send-to-review>
<ScaleIcon aria-hidden="true" />
Send to review
</template>
</OverflowMenu>
]
"
>
<DropdownIcon aria-hidden="true" />
<template #withhold-reply>
<EyeOffIcon aria-hidden="true" />
Withhold with reply
</template>
<template #withhold>
<EyeOffIcon aria-hidden="true" />
Withhold
</template>
<template #set-to-draft-reply>
<FileTextIcon aria-hidden="true" />
Set to draft with reply
</template>
<template #set-to-draft>
<FileTextIcon aria-hidden="true" />
Set to draft
</template>
<template #send-to-review-reply>
<ScaleIcon aria-hidden="true" />
Send to review with reply
</template>
<template #send-to-review>
<ScaleIcon aria-hidden="true" />
Send to review
</template>
</OverflowMenu>
</ButtonStyled>
</div>
</template>
</template>
@@ -311,6 +288,7 @@ import {
XIcon,
} from '@modrinth/assets'
import {
ButtonStyled,
Checkbox,
CopyCode,
injectNotificationManager,

View File

@@ -104,20 +104,22 @@
</span>
</span>
<div v-if="isStaff(auth.user) && message.author_id === auth.user.id" class="message__actions">
<OverflowMenu
class="btn btn-transparent icon-only"
:options="[
{
id: 'delete',
action: () => deleteMessage(),
color: 'red',
hoverFilled: true,
},
]"
>
<MoreHorizontalIcon />
<template #delete> <TrashIcon /> Delete </template>
</OverflowMenu>
<ButtonStyled circular type="transparent">
<OverflowMenu
class="btn-dropdown-animation"
:options="[
{
id: 'delete',
action: () => deleteMessage(),
color: 'red',
hoverFilled: true,
},
]"
>
<MoreHorizontalIcon />
<template #delete> <TrashIcon /> Delete </template>
</OverflowMenu>
</ButtonStyled>
</div>
</div>
</template>
@@ -135,6 +137,7 @@ import {
AutoLink,
Avatar,
Badge,
ButtonStyled,
OverflowMenu,
useFormatDateTime,
useRelativeTime,

View File

@@ -2,7 +2,7 @@
<NuxtLayout>
<LoadingBar />
<NotificationPanel />
<div class="main experimental-styles-within">
<div class="main">
<div v-if="is404" class="error-graphic">
<Logo404 />
</div>

View File

@@ -54,7 +54,7 @@
:api-url="config.public.apiBaseUrl"
/>
<header
class="experimental-styles-within desktop-only relative z-[5] mx-auto grid max-w-[1280px] grid-cols-[1fr_auto] items-center gap-2 px-6 py-4 lg:grid-cols-[auto_1fr_auto]"
class="desktop-only relative z-[5] mx-auto grid max-w-[1280px] grid-cols-[1fr_auto] items-center gap-2 px-6 py-4 lg:grid-cols-[auto_1fr_auto]"
>
<div>
<NuxtLink
@@ -1298,7 +1298,8 @@ const { cycle: changeTheme } = useTheme()
justify-content: center;
padding: 1rem;
.iconified-button {
> button,
> a {
width: 100%;
max-width: 500px;
padding: 0.75rem;

View File

@@ -45,15 +45,7 @@
</div>
</div>
<div v-else class="experimental-styles-within">
<NewModal ref="settingsModal">
<template #title>
<Avatar :src="project.icon_url" :alt="project.title" class="icon" size="32px" />
<span class="text-lg font-extrabold text-contrast">
{{ formatMessage(messages.settingsTitle) }}
</span>
</template>
</NewModal>
<div v-else>
<NewModal
ref="modalLicense"
:header="project.license.name ? project.license.name : formatMessage(messages.licenseTitle)"
@@ -464,30 +456,19 @@
:member="!!currentMember"
>
<template #actions>
<ButtonStyled
v-if="auth.user && currentMember"
size="large"
color="brand"
class="lg:!hidden"
circular
>
<ButtonStyled v-if="auth.user && currentMember" size="large" color="brand" circular>
<nuxt-link
v-tooltip="'Edit project'"
:to="`/${project.project_type}/${project.slug ? project.slug : project.id}/settings`"
class="!font-bold"
class="!font-bold lg:!hidden"
>
<SettingsIcon aria-hidden="true" />
</nuxt-link>
</ButtonStyled>
<ButtonStyled
v-if="auth.user && currentMember"
size="large"
color="brand"
class="max-lg:!hidden"
>
<ButtonStyled v-if="auth.user && currentMember" size="large" color="brand">
<nuxt-link
:to="`/${project.project_type}/${project.slug ? project.slug : project.id}/settings`"
class="!font-bold"
class="!font-bold max-lg:!hidden"
>
<SettingsIcon aria-hidden="true" />
Edit project
@@ -594,7 +575,7 @@
</nuxt-link>
</ButtonStyled>
<template #popper>
<div class="experimental-styles-within grid grid-cols-[min-content] gap-1">
<div class="grid grid-cols-[min-content] gap-1">
<div class="flex min-w-60 items-center justify-between gap-4">
<h3
class="m-0 flex items-center gap-2 whitespace-nowrap text-base font-bold text-contrast"
@@ -724,13 +705,15 @@
<div v-else class="menu-text">
<p class="popout-text">{{ formatMessage(messages.noCollectionsFound) }}</p>
</div>
<button
class="btn collection-button"
@click="(event) => $refs.modal_collection.show(event)"
>
<PlusIcon aria-hidden="true" />
{{ formatMessage(messages.createNewCollection) }}
</button>
<ButtonStyled>
<button
class="mx-3 mb-3"
@click="(event) => $refs.modal_collection.show(event)"
>
<PlusIcon aria-hidden="true" />
{{ formatMessage(messages.createNewCollection) }}
</button>
</ButtonStyled>
</template>
</PopoutMenu>
<nuxt-link v-else v-tooltip="'Save'" :to="signInRouteObj" aria-label="Save">
@@ -890,32 +873,29 @@
:supported-versions="serverSupportedVersions"
:loaders="serverModpackLoaders"
:status-online="projectV3?.minecraft_java_server?.ping?.data != null"
class="card flex-card experimental-styles-within"
class="card flex-card"
/>
<ProjectSidebarCompatibility
v-if="projectV3Loaded && !isServerProject"
:project="project"
:tags="tags"
:project-v3="projectV3"
class="card flex-card experimental-styles-within"
class="card flex-card"
/>
<AdPlaceholder v-if="!auth.user && tags.approvedStatuses.includes(project.status)" />
<ProjectSidebarLinks
:project="project"
:project-v3="projectV3"
:link-target="$external()"
class="card flex-card experimental-styles-within"
/>
<ProjectSidebarTags
:project="project"
class="card flex-card experimental-styles-within"
class="card flex-card"
/>
<ProjectSidebarTags :project="project" class="card flex-card" />
<ProjectSidebarCreators
:organization="organization"
:members="members"
:org-link="(slug) => `/organization/${slug}`"
:user-link="(username) => `/user/${username}`"
class="card flex-card experimental-styles-within"
class="card flex-card"
/>
<!-- TODO: Finish license modal and enable -->
<ProjectSidebarDetails
@@ -924,9 +904,9 @@
:has-versions="versions.length > 0"
:link-target="$external()"
:show-followers="isServerProject"
class="card flex-card experimental-styles-within"
class="card flex-card"
/>
<div class="card flex-card experimental-styles-within">
<div class="card flex-card">
<h2>{{ formatMessage(detailsMessages.title) }}</h2>
<div class="details-list">
@@ -1177,7 +1157,6 @@ const formatDateTime = useFormatDateTime({
const debug = useDebugLogger('DownloadModal')
const settingsModal = ref()
const downloadModal = ref()
const openInAppModal = ref()
const overTheTopDownloadAnimation = ref()
@@ -2748,11 +2727,6 @@ provideProjectPageContext({
color: var(--color-secondary);
}
.collection-button {
margin: var(--gap-sm) var(--gap-md);
white-space: nowrap;
}
.menu-text {
padding: 0 var(--gap-md);
font-size: var(--font-size-nm);

View File

@@ -53,14 +53,16 @@
{{ formatDate(version.date_published) }}</span
>
</div>
<a
:href="version.primaryFile?.url"
class="iconified-button download"
:title="`Download ${version.name}`"
>
<DownloadIcon aria-hidden="true" />
Download
</a>
<ButtonStyled color="brand" type="transparent">
<a
class="ml-auto"
:href="version.primaryFile?.url"
:title="`Download ${version.name}`"
>
<DownloadIcon aria-hidden="true" />
Download
</a>
</ButtonStyled>
</div>
<div
v-if="version.changelog && !version.duplicate"
@@ -87,6 +89,7 @@
<script setup>
import { DownloadIcon, SpinnerIcon } from '@modrinth/assets'
import {
ButtonStyled,
injectModrinthClient,
injectProjectPageContext,
Pagination,

View File

@@ -10,23 +10,24 @@
<div class="file-header">
<ImageIcon aria-hidden="true" />
<strong>{{ editFile ? editFile.name : 'Current image' }}</strong>
<FileInput
v-if="editIndex === -1"
class="iconified-button raised-button"
prompt="Replace"
:accept="acceptFileTypes"
:max-size="5242880"
should-always-reset
aria-label="Replace image"
@change="
(x) => {
editFile = x[0]
showPreviewImage()
}
"
>
<TransferIcon aria-hidden="true" />
</FileInput>
<ButtonStyled v-if="editIndex === -1" type="outlined">
<FileInput
class="button-like"
prompt="Replace"
:accept="acceptFileTypes"
:max-size="5242880"
should-always-reset
aria-label="Replace image"
@change="
(x) => {
editFile = x[0]
showPreviewImage()
}
"
>
<TransferIcon aria-hidden="true" />
</FileInput>
</ButtonStyled>
</div>
<img
:src="
@@ -68,53 +69,42 @@
placeholder="Enter order index..."
/>
<label for="gallery-image-featured">
<span class="label__title">Featured</span>
<span class="label__title">Banner image</span>
<span class="label__description">
A featured gallery image shows up in search and your project card. Only one gallery
image can be featured.
You can feature one image on your project to be used as a banner image.
</span>
</label>
<button
v-if="!editFeatured"
id="gallery-image-featured"
class="iconified-button"
@click="editFeatured = true"
>
<StarIcon aria-hidden="true" />
Feature image
</button>
<button
v-else
id="gallery-image-featured"
class="iconified-button"
@click="editFeatured = false"
>
<StarIcon fill="currentColor" aria-hidden="true" />
Unfeature image
</button>
<ButtonStyled v-if="!editFeatured">
<button id="gallery-image-featured" class="w-fit" @click="editFeatured = true">
<StarIcon aria-hidden="true" />
Set as banner
</button>
</ButtonStyled>
<ButtonStyled v-else>
<button id="gallery-image-featured" class="w-fit" @click="editFeatured = false">
<StarIcon fill="currentColor" aria-hidden="true" />
Unset as banner
</button>
</ButtonStyled>
<div class="button-group">
<button class="iconified-button" @click="modalEditItem?.hide()">
<XIcon aria-hidden="true" />
Cancel
</button>
<button
v-if="editIndex === -1"
class="iconified-button brand-button"
:disabled="shouldPreventActions"
@click="createGalleryItem"
>
<PlusIcon aria-hidden="true" />
Add gallery image
</button>
<button
v-else
class="iconified-button brand-button"
:disabled="shouldPreventActions"
@click="editGalleryItem"
>
<SaveIcon aria-hidden="true" />
Save changes
</button>
<ButtonStyled type="outlined">
<button @click="modalEditItem?.hide()">
<XIcon aria-hidden="true" />
Cancel
</button>
</ButtonStyled>
<ButtonStyled v-if="editIndex === -1" color="brand">
<button :disabled="shouldPreventActions" @click="createGalleryItem">
<PlusIcon aria-hidden="true" />
Add gallery image
</button>
</ButtonStyled>
<ButtonStyled v-else color="brand">
<button :disabled="shouldPreventActions" @click="editGalleryItem">
<SaveIcon aria-hidden="true" />
Save changes
</button>
</ButtonStyled>
</div>
</div>
</Modal>
@@ -155,39 +145,41 @@
</p>
</div>
<div class="controls">
<div class="buttons">
<button class="close circle-button" @click="expandedGalleryItem = null">
<XIcon aria-hidden="true" />
</button>
<a
class="open circle-button"
target="_blank"
:href="
expandedGalleryItem?.raw_url
? expandedGalleryItem?.raw_url
: 'https://cdn.modrinth.com/placeholder-banner.svg'
"
>
<ExternalIcon aria-hidden="true" />
</a>
<button class="circle-button" @click="zoomedIn = !zoomedIn">
<ExpandIcon v-if="!zoomedIn" aria-hidden="true" />
<ContractIcon v-else aria-hidden="true" />
</button>
<button
v-if="filteredGallery.length > 1"
class="previous circle-button"
@click="previousImage()"
>
<LeftArrowIcon aria-hidden="true" />
</button>
<button
v-if="filteredGallery.length > 1"
class="next circle-button"
@click="nextImage()"
>
<RightArrowIcon aria-hidden="true" />
</button>
<div class="flex gap-2">
<ButtonStyled circular>
<button class="close" @click="expandedGalleryItem = null">
<XIcon aria-hidden="true" />
</button>
</ButtonStyled>
<ButtonStyled circular>
<a
class="open"
target="_blank"
:href="
expandedGalleryItem?.raw_url
? expandedGalleryItem?.raw_url
: 'https://cdn.modrinth.com/placeholder-banner.svg'
"
>
<ExternalIcon aria-hidden="true" />
</a>
</ButtonStyled>
<ButtonStyled circular>
<button @click="zoomedIn = !zoomedIn">
<ExpandIcon v-if="!zoomedIn" aria-hidden="true" />
<ContractIcon v-else aria-hidden="true" />
</button>
</ButtonStyled>
<ButtonStyled v-if="filteredGallery.length > 1" circular>
<button class="previous" @click="previousImage()">
<LeftArrowIcon aria-hidden="true" />
</button>
</ButtonStyled>
<ButtonStyled v-if="filteredGallery.length > 1" circular>
<button class="next" @click="nextImage()">
<RightArrowIcon aria-hidden="true" />
</button>
</ButtonStyled>
</div>
</div>
</div>
@@ -195,17 +187,19 @@
</div>
<div v-if="currentMember && filteredGallery.length" class="card header-buttons">
<FileInput
:max-size="5242880"
:accept="acceptFileTypes"
prompt="Upload an image"
aria-label="Upload an image"
class="iconified-button brand-button"
:disabled="!isPermission(currentMember?.permissions, 1 << 2)"
@change="handleFiles"
>
<UploadIcon aria-hidden="true" />
</FileInput>
<ButtonStyled color="brand">
<FileInput
:max-size="5242880"
:accept="acceptFileTypes"
prompt="Upload an image"
aria-label="Upload an image"
class="button-like"
:disabled="!isPermission(currentMember?.permissions, 1 << 2)"
@change="handleFiles"
>
<UploadIcon aria-hidden="true" />
</FileInput>
</ButtonStyled>
<span class="indicator">
<InfoIcon aria-hidden="true" /> Click to choose an image or drag one onto this page
</span>
@@ -239,35 +233,37 @@
{{ formatDate(item.created) }}
</div>
<div v-if="currentMember" class="gallery-buttons input-group">
<button
class="iconified-button"
@click="
() => {
resetEdit()
editIndex = index
editTitle = item.title ?? ''
editDescription = item.description ?? ''
editFeatured = item.featured
editOrder = item.ordering
modalEditItem?.show()
}
"
>
<EditIcon aria-hidden="true" />
Edit
</button>
<button
class="iconified-button"
@click="
() => {
deleteIndex = index
modalConfirm?.show()
}
"
>
<TrashIcon aria-hidden="true" />
Remove
</button>
<ButtonStyled>
<button
@click="
() => {
resetEdit()
editIndex = index
editTitle = item.title ?? ''
editDescription = item.description ?? ''
editFeatured = item.featured
editOrder = item.ordering
modalEditItem?.show()
}
"
>
<EditIcon aria-hidden="true" />
Edit
</button>
</ButtonStyled>
<ButtonStyled>
<button
@click="
() => {
deleteIndex = index
modalConfirm?.show()
}
"
>
<TrashIcon aria-hidden="true" />
Remove
</button>
</ButtonStyled>
</div>
</div>
</div>
@@ -304,6 +300,7 @@ import {
XIcon,
} from '@modrinth/assets'
import {
ButtonStyled,
ConfirmModal,
DropArea,
FileInput,
@@ -541,43 +538,6 @@ async function deleteGalleryImage() {
width: calc(100vw - 2 * var(--spacing-card-lg));
height: calc(100vh - 2 * var(--spacing-card-lg));
.circle-button {
padding: 0.5rem;
line-height: 1;
display: flex;
max-width: 2rem;
color: var(--color-button-text);
background-color: var(--color-button-bg);
border-radius: var(--size-rounded-max);
margin: 0;
box-shadow: inset 0px -1px 1px rgb(17 24 39 / 10%);
&:not(:last-child) {
margin-right: 0.5rem;
}
&:hover {
background-color: var(--color-button-bg-hover) !important;
svg {
color: var(--color-button-text-hover) !important;
}
}
&:active {
background-color: var(--color-button-bg-active) !important;
svg {
color: var(--color-button-text-active) !important;
}
}
svg {
height: 1rem;
width: 1rem;
}
}
.image {
position: absolute;
left: 50%;
@@ -653,14 +613,6 @@ async function deleteGalleryImage() {
}
}
.buttons {
display: flex;
button {
margin-right: 0.5rem;
}
}
.items {
display: grid;
grid-template-rows: 1fr;
@@ -762,10 +714,6 @@ async function deleteGalleryImage() {
strong {
word-wrap: anywhere;
}
.iconified-button {
margin-left: auto;
}
}
img {
@@ -778,8 +726,4 @@ async function deleteGalleryImage() {
}
}
}
.brand-button {
color: var(--color-accent-contrast);
}
</style>

View File

@@ -152,7 +152,7 @@ watch(route, () => {
@toggle-collapsed="() => (collapsedChecklist = !collapsedChecklist)"
@set-processing="setProcessing"
/>
<div class="experimental-styles-within grid gap-4 lg:grid-cols-[1fr_3fr]">
<div class="grid gap-4 lg:grid-cols-[1fr_3fr]">
<div>
<NavStack :items="navItems" />
</div>

View File

@@ -1,6 +1,6 @@
<template>
<LoadingIndicator v-if="!projectV3" class="py-6" />
<div v-else-if="showEnvironmentMigration" class="card experimental-styles-within">
<div v-else-if="showEnvironmentMigration" class="card">
<h2 class="m-0 mb-2 block text-lg font-extrabold text-contrast">Project environment</h2>
<EnvironmentMigration />
</div>

View File

@@ -10,23 +10,24 @@
<div class="file-header">
<ImageIcon aria-hidden="true" />
<strong>{{ editFile ? editFile.name : 'Current image' }}</strong>
<FileInput
v-if="editIndex === -1"
class="iconified-button raised-button"
prompt="Replace"
:accept="acceptFileTypes"
:max-size="5242880"
should-always-reset
aria-label="Replace image"
@change="
(x) => {
editFile = x[0]
showPreviewImage()
}
"
>
<TransferIcon aria-hidden="true" />
</FileInput>
<ButtonStyled v-if="editIndex === -1" type="outlined">
<FileInput
class="button-like"
prompt="Replace"
:accept="acceptFileTypes"
:max-size="5242880"
should-always-reset
aria-label="Replace image"
@change="
(x) => {
editFile = x[0]
showPreviewImage()
}
"
>
<TransferIcon aria-hidden="true" />
</FileInput>
</ButtonStyled>
</div>
<img
:src="
@@ -68,53 +69,42 @@
placeholder="Enter order index..."
/>
<label for="gallery-image-featured">
<span class="label__title">Featured</span>
<span class="label__title">Banner image</span>
<span class="label__description">
A featured gallery image shows up in search and your project card. Only one gallery
image can be featured.
You can feature one image on your project to be used as a banner image.
</span>
</label>
<button
v-if="!editFeatured"
id="gallery-image-featured"
class="iconified-button"
@click="editFeatured = true"
>
<StarIcon aria-hidden="true" />
Feature image
</button>
<button
v-else
id="gallery-image-featured"
class="iconified-button"
@click="editFeatured = false"
>
<StarIcon fill="currentColor" aria-hidden="true" />
Unfeature image
</button>
<ButtonStyled v-if="!editFeatured">
<button id="gallery-image-featured" class="w-fit" @click="editFeatured = true">
<StarIcon aria-hidden="true" />
Set as banner
</button>
</ButtonStyled>
<ButtonStyled v-else>
<button id="gallery-image-featured" class="w-fit" @click="editFeatured = false">
<StarIcon fill="currentColor" aria-hidden="true" />
Unset as banner
</button>
</ButtonStyled>
<div class="button-group">
<button class="iconified-button" @click="modal_edit_item.hide()">
<XIcon aria-hidden="true" />
Cancel
</button>
<button
v-if="editIndex === -1"
class="iconified-button brand-button"
:disabled="shouldPreventActions"
@click="createGalleryItem"
>
<PlusIcon aria-hidden="true" />
Add gallery image
</button>
<button
v-else
class="iconified-button brand-button"
:disabled="shouldPreventActions"
@click="editGalleryItem"
>
<SaveIcon aria-hidden="true" />
Save changes
</button>
<ButtonStyled type="outlined">
<button @click="modal_edit_item.hide()">
<XIcon aria-hidden="true" />
Cancel
</button>
</ButtonStyled>
<ButtonStyled v-if="editIndex === -1" color="brand">
<button :disabled="shouldPreventActions" @click="createGalleryItem">
<PlusIcon aria-hidden="true" />
Add gallery image
</button>
</ButtonStyled>
<ButtonStyled v-else color="brand">
<button :disabled="shouldPreventActions" @click="editGalleryItem">
<SaveIcon aria-hidden="true" />
Save changes
</button>
</ButtonStyled>
</div>
</div>
</Modal>
@@ -155,56 +145,60 @@
</p>
</div>
<div class="controls">
<div class="buttons">
<button class="close circle-button" @click="expandedGalleryItem = null">
<XIcon aria-hidden="true" />
</button>
<a
class="open circle-button"
target="_blank"
:href="
expandedGalleryItem.raw_url
? expandedGalleryItem.raw_url
: 'https://cdn.modrinth.com/placeholder-banner.svg'
"
>
<ExternalIcon aria-hidden="true" />
</a>
<button class="circle-button" @click="zoomedIn = !zoomedIn">
<ExpandIcon v-if="!zoomedIn" aria-hidden="true" />
<ContractIcon v-else aria-hidden="true" />
</button>
<button
v-if="filteredGallery.length > 1"
class="previous circle-button"
@click="previousImage()"
>
<LeftArrowIcon aria-hidden="true" />
</button>
<button
v-if="filteredGallery.length > 1"
class="next circle-button"
@click="nextImage()"
>
<RightArrowIcon aria-hidden="true" />
</button>
<div class="flex gap-2">
<ButtonStyled circular>
<button class="close" @click="expandedGalleryItem = null">
<XIcon aria-hidden="true" />
</button>
</ButtonStyled>
<ButtonStyled circular>
<a
class="open"
target="_blank"
:href="
expandedGalleryItem.raw_url
? expandedGalleryItem.raw_url
: 'https://cdn.modrinth.com/placeholder-banner.svg'
"
>
<ExternalIcon aria-hidden="true" />
</a>
</ButtonStyled>
<ButtonStyled circular>
<button @click="zoomedIn = !zoomedIn">
<ExpandIcon v-if="!zoomedIn" aria-hidden="true" />
<ContractIcon v-else aria-hidden="true" />
</button>
</ButtonStyled>
<ButtonStyled v-if="filteredGallery.length > 1" circular>
<button class="previous" @click="previousImage()">
<LeftArrowIcon aria-hidden="true" />
</button>
</ButtonStyled>
<ButtonStyled v-if="filteredGallery.length > 1" circular>
<button class="next" @click="nextImage()">
<RightArrowIcon aria-hidden="true" />
</button>
</ButtonStyled>
</div>
</div>
</div>
</div>
</div>
<div v-if="currentMember" class="card header-buttons">
<FileInput
:max-size="5242880"
:accept="acceptFileTypes"
prompt="Upload an image"
aria-label="Upload an image"
class="iconified-button brand-button"
:disabled="!isPermission(currentMember?.permissions, 1 << 2)"
@change="handleFiles"
>
<UploadIcon aria-hidden="true" />
</FileInput>
<ButtonStyled color="brand">
<FileInput
:max-size="5242880"
:accept="acceptFileTypes"
prompt="Upload an image"
aria-label="Upload an image"
class="button-like"
:disabled="!isPermission(currentMember?.permissions, 1 << 2)"
@change="handleFiles"
>
<UploadIcon aria-hidden="true" />
</FileInput>
</ButtonStyled>
<span class="indicator">
<InfoIcon aria-hidden="true" /> Click to choose an image or drag one onto this page
</span>
@@ -238,35 +232,37 @@
{{ formatDate(item.created) }}
</div>
<div v-if="currentMember" class="gallery-buttons input-group">
<button
class="iconified-button"
@click="
() => {
resetEdit()
editIndex = index
editTitle = item.title
editDescription = item.description
editFeatured = item.featured
editOrder = item.ordering
modal_edit_item.show()
}
"
>
<EditIcon aria-hidden="true" />
Edit
</button>
<button
class="iconified-button"
@click="
() => {
deleteIndex = index
modal_confirm.show()
}
"
>
<TrashIcon aria-hidden="true" />
Remove
</button>
<ButtonStyled>
<button
@click="
() => {
resetEdit()
editIndex = index
editTitle = item.title
editDescription = item.description
editFeatured = item.featured
editOrder = item.ordering
modal_edit_item.show()
}
"
>
<EditIcon aria-hidden="true" />
Edit
</button>
</ButtonStyled>
<ButtonStyled>
<button
@click="
() => {
deleteIndex = index
modal_confirm.show()
}
"
>
<TrashIcon aria-hidden="true" />
Remove
</button>
</ButtonStyled>
</div>
</div>
</div>
@@ -294,6 +290,7 @@ import {
XIcon,
} from '@modrinth/assets'
import {
ButtonStyled,
ConfirmModal,
DropArea,
FileInput,
@@ -501,43 +498,6 @@ onUnmounted(() => {
width: calc(100vw - 2 * var(--spacing-card-lg));
height: calc(100vh - 2 * var(--spacing-card-lg));
.circle-button {
padding: 0.5rem;
line-height: 1;
display: flex;
max-width: 2rem;
color: var(--color-button-text);
background-color: var(--color-button-bg);
border-radius: var(--size-rounded-max);
margin: 0;
box-shadow: inset 0px -1px 1px rgb(17 24 39 / 10%);
&:not(:last-child) {
margin-right: 0.5rem;
}
&:hover {
background-color: var(--color-button-bg-hover) !important;
svg {
color: var(--color-button-text-hover) !important;
}
}
&:active {
background-color: var(--color-button-bg-active) !important;
svg {
color: var(--color-button-text-active) !important;
}
}
svg {
height: 1rem;
width: 1rem;
}
}
.image {
position: absolute;
left: 50%;
@@ -613,14 +573,6 @@ onUnmounted(() => {
}
}
.buttons {
display: flex;
button {
margin-right: 0.5rem;
}
}
.items {
display: grid;
grid-template-rows: 1fr;
@@ -723,7 +675,7 @@ onUnmounted(() => {
word-wrap: anywhere;
}
.iconified-button {
label.button-like {
margin-left: auto;
}
}
@@ -738,8 +690,4 @@ onUnmounted(() => {
}
}
}
.brand-button {
color: var(--color-accent-contrast);
}
</style>

View File

@@ -81,29 +81,28 @@
size="md"
class="project__icon"
/>
<div class="input-stack">
<FileInput
id="project-icon"
:max-size="262144000"
:show-icon="true"
accept="image/png,image/jpeg,image/gif,image/webp"
class="choose-image iconified-button"
prompt="Upload icon"
aria-label="Upload icon"
:disabled="!hasPermission"
@change="showPreviewImage"
>
<UploadIcon aria-hidden="true" />
</FileInput>
<button
v-if="!deletedIcon && (previewImage || project.icon_url)"
class="iconified-button"
:disabled="!hasPermission"
@click="markIconForDeletion"
>
<TrashIcon aria-hidden="true" />
Remove icon
</button>
<div class="flex flex-col gap-2">
<ButtonStyled>
<FileInput
id="project-icon"
:max-size="262144000"
:show-icon="true"
accept="image/png,image/jpeg,image/gif,image/webp"
class="button-like choose-image"
prompt="Upload icon"
aria-label="Upload icon"
:disabled="!hasPermission"
@change="showPreviewImage"
>
<UploadIcon aria-hidden="true" />
</FileInput>
</ButtonStyled>
<ButtonStyled v-if="!deletedIcon && (previewImage || project.icon_url)">
<button :disabled="!hasPermission" @click="markIconForDeletion">
<TrashIcon aria-hidden="true" />
Remove icon
</button>
</ButtonStyled>
</div>
</div>
</div>
@@ -157,26 +156,25 @@
</label>
</div>
<div class="mt-2 flex items-center gap-2">
<FileInput
:max-size="524288"
:show-icon="true"
accept="image/png,image/jpeg,image/gif,image/webp"
class="iconified-button"
prompt="Upload banner"
:disabled="!hasPermission"
@change="showBannerPreview"
>
<UploadIcon aria-hidden="true" />
</FileInput>
<button
v-if="!deletedBanner && (bannerPreview || bannerGalleryImage?.url)"
class="iconified-button"
:disabled="!hasPermission"
@click="markBannerForDeletion"
>
<TrashIcon aria-hidden="true" />
Remove banner
</button>
<ButtonStyled>
<FileInput
:max-size="524288"
:show-icon="true"
accept="image/png,image/jpeg,image/gif,image/webp"
class="button-like"
prompt="Upload banner"
:disabled="!hasPermission"
@change="showBannerPreview"
>
<UploadIcon aria-hidden="true" />
</FileInput>
</ButtonStyled>
<ButtonStyled v-if="!deletedBanner && (bannerPreview || bannerGalleryImage?.url)">
<button :disabled="!hasPermission" @click="markBannerForDeletion">
<TrashIcon aria-hidden="true" />
Remove banner
</button>
</ButtonStyled>
</div>
<div class="mt-2 text-secondary">Gif, 468×60px recommended.</div>
</div>
@@ -285,15 +283,12 @@
Removes your project from Modrinth's servers and search. Clicking on this will delete your
project, so be extra careful!
</p>
<button
type="button"
class="iconified-button danger-button"
:disabled="!hasDeletePermission"
@click="$refs.modal_confirm.show()"
>
<TrashIcon aria-hidden="true" />
Delete project
</button>
<ButtonStyled color="red">
<button :disabled="!hasDeletePermission" @click="$refs.modal_confirm.show()">
<TrashIcon aria-hidden="true" />
Delete project
</button>
</ButtonStyled>
</section>
<UnsavedChangesPopup
:original="original"
@@ -319,6 +314,7 @@ import {
import { MIN_SUMMARY_CHARS } from '@modrinth/moderation'
import {
Avatar,
ButtonStyled,
Combobox,
ConfirmLeaveModal,
ConfirmModal,

View File

@@ -104,7 +104,7 @@
>
</label>
<div class="input-stack w-1/2">
<div class="flex w-1/2 flex-col gap-2">
<StyledInput
v-if="!current.nonSpdxLicense"
id="license-spdx"

View File

@@ -105,15 +105,12 @@
/>
</div>
<div class="button-group">
<button
type="button"
class="iconified-button brand-button"
:disabled="!hasServerChanges"
@click="saveServerChanges()"
>
<SaveIcon />
Save changes
</button>
<ButtonStyled color="brand">
<button :disabled="!hasServerChanges" @click="saveServerChanges()">
<SaveIcon />
Save changes
</button>
</ButtonStyled>
</div>
</section>
@@ -275,15 +272,12 @@
/>
</div>
<div class="button-group">
<button
type="button"
class="iconified-button brand-button"
:disabled="!hasChanges"
@click="saveChanges()"
>
<SaveIcon />
Save changes
</button>
<ButtonStyled color="brand">
<button :disabled="!hasChanges" @click="saveChanges()">
<SaveIcon />
Save changes
</button>
</ButtonStyled>
</div>
</section>
</div>
@@ -293,6 +287,7 @@
import { SaveIcon, TriangleAlertIcon } from '@modrinth/assets'
import { commonLinkDomains, isCommonUrl, isDiscordUrl, isLinkShortener } from '@modrinth/moderation'
import {
ButtonStyled,
DropdownSelect,
injectModrinthClient,
injectNotificationManager,

View File

@@ -41,31 +41,33 @@
@keypress.enter="inviteTeamMember()"
/>
<label for="username" class="hidden">Username</label>
<button
class="iconified-button brand-button"
:disabled="(currentMember?.permissions & MANAGE_INVITES) !== MANAGE_INVITES"
@click="inviteTeamMember()"
>
<UserPlusIcon />
Invite
</button>
<ButtonStyled color="brand">
<button
:disabled="(currentMember?.permissions & MANAGE_INVITES) !== MANAGE_INVITES"
@click="inviteTeamMember()"
>
<UserPlusIcon />
Invite
</button>
</ButtonStyled>
</div>
<div class="adjacent-input">
<span class="label">
<span class="label__title">Leave project</span>
<span class="label__description"> Remove yourself as a member of this project. </span>
</span>
<button
class="iconified-button danger-button"
:disabled="currentMember?.is_owner"
:title="
currentMember?.is_owner ? 'You cannot leave the project if you are the owner!' : ''
"
@click="leaveProject()"
>
<UserXIcon />
Leave project
</button>
<ButtonStyled color="red">
<button
:disabled="currentMember?.is_owner"
:title="
currentMember?.is_owner ? 'You cannot leave the project if you are the owner!' : ''
"
@click="leaveProject()"
>
<UserXIcon />
Leave project
</button>
</ButtonStyled>
</div>
</Card>
<div
@@ -88,16 +90,18 @@
<div class="side-buttons">
<Badge v-if="member.accepted" type="accepted" />
<Badge v-else type="pending" />
<button
class="square-button dropdown-icon"
@click="
openTeamMembers.indexOf(member.user.id) === -1
? openTeamMembers.push(member.user.id)
: (openTeamMembers = openTeamMembers.filter((it) => it !== member.user.id))
"
>
<DropdownIcon />
</button>
<ButtonStyled circular>
<button
class="dropdown-icon"
@click="
openTeamMembers.indexOf(member.user.id) === -1
? openTeamMembers.push(member.user.id)
: (openTeamMembers = openTeamMembers.filter((it) => it !== member.user.id))
"
>
<DropdownIcon />
</button>
</ButtonStyled>
</div>
</div>
<div class="content">
@@ -225,31 +229,30 @@
</div>
</template>
<div class="input-group">
<button
class="iconified-button brand-button"
:disabled="(currentMember?.permissions & EDIT_MEMBER) !== EDIT_MEMBER"
@click="updateTeamMember(index)"
>
<SaveIcon />
Save changes
</button>
<button
v-if="!member.is_owner"
class="iconified-button danger-button"
:disabled="(currentMember?.permissions & EDIT_MEMBER) !== EDIT_MEMBER"
@click="removeTeamMember(index)"
>
<UserXIcon />
Remove member
</button>
<button
v-if="!member.is_owner && currentMember?.is_owner && member.accepted"
class="iconified-button"
@click="openTransferModal(index, $event)"
>
<TransferIcon />
Transfer ownership
</button>
<ButtonStyled color="brand">
<button
:disabled="(currentMember?.permissions & EDIT_MEMBER) !== EDIT_MEMBER"
@click="updateTeamMember(index)"
>
<SaveIcon />
Save changes
</button>
</ButtonStyled>
<ButtonStyled v-if="!member.is_owner" color="red">
<button
:disabled="(currentMember?.permissions & EDIT_MEMBER) !== EDIT_MEMBER"
@click="removeTeamMember(index)"
>
<UserXIcon />
Remove member
</button>
</ButtonStyled>
<ButtonStyled v-if="!member.is_owner && currentMember?.is_owner && member.accepted">
<button @click="openTransferModal(index, $event)">
<TransferIcon />
Transfer ownership
</button>
</ButtonStyled>
</div>
</div>
</div>
@@ -305,19 +308,19 @@
force-direction="up"
:disabled="!currentMember?.is_owner || organizationOptions.length === 0"
/>
<button
class="btn btn-primary"
:disabled="!selectedOrganization"
@click="openTransferToOrgModal($event)"
>
<CheckIcon />
<span class="w-max"> Transfer management </span>
</button>
<ButtonStyled color="brand">
<button :disabled="!selectedOrganization" @click="openTransferToOrgModal($event)">
<CheckIcon />
<span class="w-max"> Transfer management </span>
</button>
</ButtonStyled>
</div>
<button v-if="organization" class="btn" @click="$refs.modal_remove.show()">
<OrganizationIcon />
Remove from organization
</button>
<ButtonStyled v-if="organization">
<button @click="$refs.modal_remove.show()">
<OrganizationIcon />
Remove from organization
</button>
</ButtonStyled>
</section>
<div
v-for="(member, index) in allOrgMembers"
@@ -339,16 +342,18 @@
<div class="side-buttons">
<Badge v-if="member.accepted" type="accepted" />
<Badge v-else type="pending" />
<button
class="square-button dropdown-icon"
@click="
openTeamMembers.indexOf(member.user.id) === -1
? openTeamMembers.push(member.user.id)
: (openTeamMembers = openTeamMembers.filter((it) => it !== member.user.id))
"
>
<DropdownIcon />
</button>
<ButtonStyled circular>
<button
class="dropdown-icon"
@click="
openTeamMembers.indexOf(member.user.id) === -1
? openTeamMembers.push(member.user.id)
: (openTeamMembers = openTeamMembers.filter((it) => it !== member.user.id))
"
>
<DropdownIcon />
</button>
</ButtonStyled>
</div>
</div>
<div class="content">
@@ -522,17 +527,18 @@
we don't allow clicking the button in that last case.
-->
<button
class="iconified-button brand-button"
:disabled="
(currentMember?.permissions & EDIT_MEMBER) !== EDIT_MEMBER ||
(!allOrgMembers[index].oldOverride && !allOrgMembers[index].override)
"
@click="updateOrgMember(index)"
>
<SaveIcon />
Save changes
</button>
<ButtonStyled color="brand">
<button
:disabled="
(currentMember?.permissions & EDIT_MEMBER) !== EDIT_MEMBER ||
(!allOrgMembers[index].oldOverride && !allOrgMembers[index].override)
"
@click="updateOrgMember(index)"
>
<SaveIcon />
Save changes
</button>
</ButtonStyled>
</div>
</div>
</div>
@@ -554,6 +560,7 @@ import {
import {
Avatar,
Badge,
ButtonStyled,
Card,
Checkbox,
Combobox,

View File

@@ -1,5 +1,5 @@
<template>
<section class="experimental-styles-within overflow-visible">
<section class="overflow-visible">
<!-- Loading state -->
<div
v-if="versionsLoading && !versions?.length"

View File

@@ -132,7 +132,7 @@
</div>
</div>
</NewModal>
<div class="page experimental-styles-within">
<div class="page">
<div
class="mb-4 flex items-center justify-between border-0 border-b border-solid border-divider pb-4"
>

View File

@@ -96,8 +96,8 @@ onMounted(() => {
</div>
<div class="mt-auto flex gap-2">
<ButtonStyled color="brand" class="flex-1">
<button class="w-full justify-center" @click="openPreview(id)">
<ButtonStyled color="brand">
<button class="w-full flex-1 justify-center" @click="openPreview(id)">
<PlayIcon class="h-4 w-4" aria-hidden="true" />
Preview
</button>

View File

@@ -1,6 +1,6 @@
<script setup lang="ts">
import { CopyIcon, LibraryIcon, PlayIcon, SearchIcon } from '@modrinth/assets'
import { ButtonStyled, Card, NewModal, StyledInput } from '@modrinth/ui'
import { ButtonStyled, NewModal, StyledInput } from '@modrinth/ui'
import { computed, onMounted, ref } from 'vue'
import emails from '~/templates/emails'
@@ -178,84 +178,82 @@ onMounted(() => {
</div>
<div class="input-group mt-4">
<button class="iconified-button transparent" type="button" @click="closePreview">
Close
</button>
<ButtonStyled type="transparent">
<button @click="closePreview">Close</button>
</ButtonStyled>
</div>
</div>
</div>
</NewModal>
<div class="normal-page__content">
<Card class="mb-6 flex flex-col gap-4">
<div class="flex flex-wrap items-center gap-3">
<StyledInput
id="email-search"
v-model="query"
type="search"
:icon="SearchIcon"
placeholder="Search templates..."
wrapper-class="w-72"
/>
<div class="flex flex-wrap items-center gap-3">
<StyledInput
id="email-search"
v-model="query"
type="search"
:icon="SearchIcon"
placeholder="Search templates..."
wrapper-class="w-72"
/>
<ButtonStyled color="brand">
<button :disabled="filtered.length === 0" @click="openAll">
<LibraryIcon class="h-4 w-4" aria-hidden="true" />
Open all ({{ counts.shown }})
</button>
</ButtonStyled>
<ButtonStyled color="brand">
<button :disabled="filtered.length === 0" @click="openAll">
<LibraryIcon class="h-4 w-4" aria-hidden="true" />
Open all ({{ counts.shown }})
</button>
</ButtonStyled>
<span class="text-sm text-secondary">
Showing <span class="font-medium text-contrast">{{ counts.shown }}</span> of
<span class="font-medium text-contrast">{{ counts.total }}</span>
</span>
</div>
<span class="text-sm text-secondary">
Showing <span class="font-medium text-contrast">{{ counts.shown }}</span> of
<span class="font-medium text-contrast">{{ counts.total }}</span>
</span>
</div>
<div
v-if="filtered.length === 0"
class="rounded-lg border border-dashed border-divider px-6 py-10 text-center text-sm text-secondary"
<div
v-if="filtered.length === 0"
class="mt-4 border-0 border-b border-solid border-surface-4 pb-4"
>
No templates match your search.
</div>
<ul v-else class="m-0 mt-4 grid gap-3 p-0 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
<li
v-for="id in filtered"
:key="id"
class="group flex flex-col justify-between rounded-2xl border border-solid border-surface-4 bg-surface-3 p-4 shadow-sm transition hover:shadow"
>
No templates match your search.
</div>
<ul v-else class="grid gap-4 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
<li
v-for="id in filtered"
:key="id"
class="hover:border-green/70 group flex flex-col justify-between rounded-lg border border-divider bg-button-bg p-4 shadow-sm transition hover:shadow"
>
<div class="mb-3">
<div class="font-mono text-sm font-semibold tracking-tight text-contrast">
{{ id }}
</div>
<div class="mt-1 truncate text-xs text-secondary">
/_internal/templates/email/{{ id }}
</div>
<div class="mb-3">
<div class="font-mono text-sm font-semibold tracking-tight text-contrast">
{{ id }}
</div>
<div class="mt-auto flex gap-2">
<ButtonStyled color="brand" class="flex-1">
<button class="w-full justify-center" @click="openPreview(id, $event)">
<PlayIcon class="h-4 w-4" aria-hidden="true" />
Preview
</button>
</ButtonStyled>
<ButtonStyled>
<button class="justify-center" title="Copy preview URL" @click="copy(id)">
<CopyIcon class="h-4 w-4" aria-hidden="true" />
</button>
</ButtonStyled>
<div class="mt-1 truncate text-xs text-secondary">
/_internal/templates/email/{{ id }}
</div>
</li>
</ul>
</Card>
</div>
<p class="mt-2 text-xs text-secondary">
<div class="mt-auto flex gap-2">
<ButtonStyled color="brand">
<button @click="openPreview(id, $event)">
<PlayIcon aria-hidden="true" />
Preview
</button>
</ButtonStyled>
<ButtonStyled circular type="outlined">
<button title="Copy preview URL" @click="copy(id)">
<CopyIcon aria-hidden="true" />
</button>
</ButtonStyled>
</div>
</li>
</ul>
<p class="mt-4">
All templates come from
<code class="rounded bg-code-bg px-1 py-0.5 text-[11px] text-code-text"
<code class="rounded bg-code-bg px-1 py-0.5 text-sm text-code-text"
>src/emails/index.ts</code
>. Popouts render via
<code class="rounded bg-code-bg px-1 py-0.5 text-[11px] text-code-text"
<code class="rounded bg-code-bg px-1 py-0.5 text-sm text-code-text"
>/_internal/templates/email/[template]</code
>.
</p>

View File

@@ -120,7 +120,7 @@
</div>
</NewModal>
<AssignNoticeModal ref="assignNoticeModal" @close="refreshNotices" />
<div class="page experimental-styles-within">
<div class="page">
<div
class="mb-6 flex items-end justify-between border-0 border-b border-solid border-divider pb-4"
>

View File

@@ -8,7 +8,7 @@
proceed-label="Cancel transfer"
@proceed="confirmCancel"
/>
<div class="experimental-styles-within mx-auto max-w-[78.5rem] p-4">
<div class="mx-auto max-w-[78.5rem] p-4">
<div
class="mb-6 flex items-end justify-between border-0 border-b border-solid border-divider pb-4"
>

File diff suppressed because one or more lines are too long

View File

@@ -31,17 +31,19 @@ useSeoMeta({
margin: 0;
}
.auth-container .btn {
.auth-container .btn-wrapper :is(a, button) {
font-weight: 700;
min-height: 2.5rem;
text-decoration: none;
}
.centered-btn {
.centered-btn :is(a, button),
.centered-btn:is(a, button) {
margin-inline: auto;
}
.btn.continue-btn svg {
.continue-btn :is(a, button) svg,
.continue-btn:is(a, button) svg {
margin: 0 0 0 0.5rem;
}
@@ -52,19 +54,19 @@ useSeoMeta({
width: 100%;
}
.third-party .btn {
.third-party a {
width: 100%;
vertical-align: middle;
}
.third-party .btn svg {
.third-party a svg {
margin-right: var(--gap-sm);
width: 1.25rem;
height: 1.25rem;
}
@media screen and (max-width: 25.5rem) {
.third-party .btn {
.third-party a {
grid-column: 1 / 3;
}
}

View File

@@ -55,14 +55,18 @@
</div>
</div>
<div class="button-row">
<Button class="wide-button" large :action="onReject" :disabled="pending">
<XIcon />
{{ formatMessage(messages.decline) }}
</Button>
<Button class="wide-button" color="primary" large :action="onAuthorize" :disabled="pending">
<CheckIcon />
{{ formatMessage(messages.authorize) }}
</Button>
<ButtonStyled size="large">
<button class="wide-button" :disabled="pending" @click="onReject">
<XIcon />
{{ formatMessage(messages.decline) }}
</button>
</ButtonStyled>
<ButtonStyled color="brand" size="large">
<button class="wide-button" :disabled="pending" @click="onAuthorize">
<CheckIcon />
{{ formatMessage(messages.authorize) }}
</button>
</ButtonStyled>
</div>
<div class="redirection-notice">
<p class="redirect-instructions">
@@ -83,7 +87,7 @@
import { CheckIcon, XIcon } from '@modrinth/assets'
import {
Avatar,
Button,
ButtonStyled,
commonMessages,
defineMessages,
injectModrinthClient,

View File

@@ -22,13 +22,15 @@
<HCaptcha v-if="globals?.captcha_enabled" ref="captcha" v-model="token" />
<button
class="btn btn-primary centered-btn"
:disabled="globals?.captcha_enabled ? !token : false"
@click="recovery"
>
<SendIcon /> {{ formatMessage(methodChoiceMessages.action) }}
</button>
<ButtonStyled color="brand">
<button
class="mx-auto"
:disabled="globals?.captcha_enabled ? !token : false"
@click="recovery"
>
<SendIcon /> {{ formatMessage(methodChoiceMessages.action) }}
</button>
</ButtonStyled>
</template>
<template v-else-if="step === 'passed_challenge'">
<p>{{ formatMessage(postChallengeMessages.description) }}</p>
@@ -57,9 +59,11 @@
wrapper-class="w-full"
/>
<button class="auth-form__input btn btn-primary continue-btn" @click="changePassword">
{{ formatMessage(postChallengeMessages.action) }}
</button>
<ButtonStyled color="brand">
<button class="auth-form__input continue-btn" @click="changePassword">
{{ formatMessage(postChallengeMessages.action) }}
</button>
</ButtonStyled>
</template>
</section>
</div>
@@ -67,6 +71,7 @@
<script setup>
import { KeyIcon, MailIcon, SendIcon } from '@modrinth/assets'
import {
ButtonStyled,
commonMessages,
defineMessages,
injectModrinthClient,

View File

@@ -23,38 +23,52 @@
@keyup.enter="begin2FASignIn"
/>
<button class="btn btn-primary continue-btn" @click="begin2FASignIn">
{{ formatMessage(commonMessages.signInButton) }} <RightArrowIcon />
</button>
<ButtonStyled color="brand">
<button class="continue-btn" @click="begin2FASignIn">
{{ formatMessage(commonMessages.signInButton) }} <RightArrowIcon />
</button>
</ButtonStyled>
</template>
<template v-else>
<h1>{{ formatMessage(messages.signInWithLabel) }}</h1>
<section class="third-party">
<a class="btn" :href="getAuthUrl('discord', redirectTarget)">
<DiscordColorIcon />
<span>Discord</span>
</a>
<a class="btn" :href="getAuthUrl('github', redirectTarget)">
<GitHubColorIcon />
<span>GitHub</span>
</a>
<a class="btn" :href="getAuthUrl('microsoft', redirectTarget)">
<MicrosoftColorIcon />
<span>Microsoft</span>
</a>
<a class="btn" :href="getAuthUrl('google', redirectTarget)">
<GoogleColorIcon />
<span>Google</span>
</a>
<a class="btn" :href="getAuthUrl('steam', redirectTarget)">
<SteamColorIcon />
<span>Steam</span>
</a>
<a class="btn" :href="getAuthUrl('gitlab', redirectTarget)">
<GitLabColorIcon />
<span>GitLab</span>
</a>
<ButtonStyled>
<a :href="getAuthUrl('discord', redirectTarget)">
<DiscordColorIcon />
<span>Discord</span>
</a>
</ButtonStyled>
<ButtonStyled>
<a :href="getAuthUrl('github', redirectTarget)">
<GitHubColorIcon />
<span>GitHub</span>
</a>
</ButtonStyled>
<ButtonStyled>
<a :href="getAuthUrl('microsoft', redirectTarget)">
<MicrosoftColorIcon />
<span>Microsoft</span>
</a>
</ButtonStyled>
<ButtonStyled>
<a :href="getAuthUrl('google', redirectTarget)">
<GoogleColorIcon />
<span>Google</span>
</a>
</ButtonStyled>
<ButtonStyled>
<a :href="getAuthUrl('steam', redirectTarget)">
<SteamColorIcon />
<span>Steam</span>
</a>
</ButtonStyled>
<ButtonStyled>
<a :href="getAuthUrl('gitlab', redirectTarget)">
<GitLabColorIcon />
<span>GitLab</span>
</a>
</ButtonStyled>
</section>
<h1>{{ formatMessage(messages.usePasswordLabel) }}</h1>
@@ -85,13 +99,15 @@
<HCaptcha v-if="globals?.captcha_enabled" ref="captcha" v-model="token" />
<button
class="btn btn-primary continue-btn centered-btn"
:disabled="globals?.captcha_enabled ? !token : false"
@click="beginPasswordSignIn()"
>
{{ formatMessage(commonMessages.signInButton) }} <RightArrowIcon />
</button>
<ButtonStyled color="brand">
<button
class="continue-btn centered-btn"
:disabled="globals?.captcha_enabled ? !token : false"
@click="beginPasswordSignIn()"
>
{{ formatMessage(commonMessages.signInButton) }} <RightArrowIcon />
</button>
</ButtonStyled>
<div class="auth-form__additional-options">
<IntlFormatted :message-id="messages.additionalOptionsLabel">
@@ -137,6 +153,7 @@ import {
SteamColorIcon,
} from '@modrinth/assets'
import {
ButtonStyled,
commonMessages,
defineMessages,
injectModrinthClient,

View File

@@ -3,30 +3,42 @@
<h1>{{ formatMessage(messages.signUpWithTitle) }}</h1>
<section class="third-party">
<a class="btn discord-btn" :href="getAuthUrl('discord', redirectTarget)">
<DiscordColorIcon />
<span>Discord</span>
</a>
<a class="btn" :href="getAuthUrl('github', redirectTarget)">
<GitHubColorIcon />
<span>GitHub</span>
</a>
<a class="btn" :href="getAuthUrl('microsoft', redirectTarget)">
<MicrosoftColorIcon />
<span>Microsoft</span>
</a>
<a class="btn" :href="getAuthUrl('google', redirectTarget)">
<GoogleColorIcon />
<span>Google</span>
</a>
<a class="btn" :href="getAuthUrl('steam', redirectTarget)">
<SteamColorIcon />
<span>Steam</span>
</a>
<a class="btn" :href="getAuthUrl('gitlab', redirectTarget)">
<GitLabColorIcon />
<span>GitLab</span>
</a>
<ButtonStyled>
<a class="discord-btn" :href="getAuthUrl('discord', redirectTarget)">
<DiscordColorIcon />
<span>Discord</span>
</a>
</ButtonStyled>
<ButtonStyled>
<a :href="getAuthUrl('github', redirectTarget)">
<GitHubColorIcon />
<span>GitHub</span>
</a>
</ButtonStyled>
<ButtonStyled>
<a :href="getAuthUrl('microsoft', redirectTarget)">
<MicrosoftColorIcon />
<span>Microsoft</span>
</a>
</ButtonStyled>
<ButtonStyled>
<a :href="getAuthUrl('google', redirectTarget)">
<GoogleColorIcon />
<span>Google</span>
</a>
</ButtonStyled>
<ButtonStyled>
<a :href="getAuthUrl('steam', redirectTarget)">
<SteamColorIcon />
<span>Steam</span>
</a>
</ButtonStyled>
<ButtonStyled>
<a :href="getAuthUrl('gitlab', redirectTarget)">
<GitLabColorIcon />
<span>GitLab</span>
</a>
</ButtonStyled>
</section>
<h1>{{ formatMessage(messages.createAccountTitle) }}</h1>
@@ -100,13 +112,15 @@
<HCaptcha v-if="globals?.captcha_enabled" ref="captcha" v-model="token" />
<button
class="btn btn-primary continue-btn centered-btn"
:disabled="globals?.captcha_enabled ? !token : false"
@click="createAccount"
>
{{ formatMessage(messages.createAccountButton) }} <RightArrowIcon />
</button>
<ButtonStyled color="brand">
<button
class="continue-btn centered-btn"
:disabled="globals?.captcha_enabled ? !token : false"
@click="createAccount"
>
{{ formatMessage(messages.createAccountButton) }} <RightArrowIcon />
</button>
</ButtonStyled>
<div class="auth-form__additional-options">
{{ formatMessage(messages.alreadyHaveAccountLabel) }}
@@ -138,6 +152,7 @@ import {
UserIcon,
} from '@modrinth/assets'
import {
ButtonStyled,
Checkbox,
commonMessages,
defineMessages,

View File

@@ -6,9 +6,11 @@
<section class="auth-form">
<p>{{ formatMessage(alreadyVerifiedMessages.description) }}</p>
<NuxtLink class="btn" to="/settings/account">
<SettingsIcon /> {{ formatMessage(messages.accountSettings) }}
</NuxtLink>
<ButtonStyled>
<NuxtLink to="/settings/account">
<SettingsIcon /> {{ formatMessage(messages.accountSettings) }}
</NuxtLink>
</ButtonStyled>
</section>
</template>

View File

@@ -24,10 +24,12 @@
:description="formatMessage(messages.subscribeCheckbox)"
/>
<button class="btn btn-primary centered-btn" @click="continueSignUp">
{{ formatMessage(commonMessages.continueButton) }}
<RightArrowIcon />
</button>
<ButtonStyled color="brand">
<button class="centered-btn" @click="continueSignUp">
{{ formatMessage(commonMessages.continueButton) }}
<RightArrowIcon />
</button>
</ButtonStyled>
<p class="tos-text">
<IntlFormatted :message-id="messages.tosLabel">
@@ -50,6 +52,7 @@
<script setup>
import { RightArrowIcon, WavingRinthbot } from '@modrinth/assets'
import {
ButtonStyled,
Checkbox,
commonMessages,
defineMessages,

View File

@@ -116,14 +116,14 @@
</RadioButtons>
</div>
<div class="flex justify-end gap-2">
<ButtonStyled class="w-24">
<button @click="() => editModal?.hide()">
<ButtonStyled>
<button class="w-24" @click="() => editModal?.hide()">
<XIcon aria-hidden="true" />
{{ formatMessage(commonMessages.cancelButton) }}
</button>
</ButtonStyled>
<ButtonStyled color="brand" class="w-36">
<button :disabled="saving" @click="save()">
<ButtonStyled color="brand">
<button class="w-36" :disabled="saving" @click="save()">
<SpinnerIcon v-if="saving" class="animate-spin" aria-hidden="true" />
<SaveIcon v-else aria-hidden="true" />
{{
@@ -193,7 +193,7 @@
}"
>
<template #stat="{ children }">
<span class="primary-stat__counter">
<span>
<component :is="() => normalizeChildren(children)" />
</span>
</template>
@@ -333,24 +333,19 @@
"
>
<template v-if="canEdit || collection.id === 'following'" #actions>
<button
v-if="canEdit"
class="iconified-button remove-btn"
:disabled="removing"
@click="() => removeProject(project)"
>
<SpinnerIcon v-if="removing" class="animate-spin" aria-hidden="true" />
<XIcon v-else aria-hidden="true" />
{{ formatMessage(messages.removeProjectButton) }}
</button>
<button
v-if="collection.id === 'following'"
class="iconified-button"
@click="unfollowProject(project)"
>
<HeartMinusIcon aria-hidden="true" />
{{ formatMessage(messages.unfollowProjectButton) }}
</button>
<ButtonStyled v-if="canEdit">
<button class="remove-btn" :disabled="removing" @click="() => removeProject(project)">
<SpinnerIcon v-if="removing" class="animate-spin" aria-hidden="true" />
<XIcon v-else aria-hidden="true" />
{{ formatMessage(messages.removeProjectButton) }}
</button>
</ButtonStyled>
<ButtonStyled v-if="collection.id === 'following'">
<button @click="unfollowProject(project)">
<HeartMinusIcon aria-hidden="true" />
{{ formatMessage(messages.unfollowProjectButton) }}
</button>
</ButtonStyled>
</template>
</ProjectCard>
</ProjectCardList>

View File

@@ -30,14 +30,12 @@
<span class="font-semibold text-secondary">{{ selected }}</span>
</DropdownSelect>
<Button
color="primary"
class="ml-auto"
@click="(event) => $refs.modal_creation.show(event)"
>
<PlusIcon aria-hidden="true" />
{{ formatMessage(messages.createNewButton) }}
</Button>
<ButtonStyled color="brand">
<button class="ml-auto" @click="(event) => $refs.modal_creation.show(event)">
<PlusIcon aria-hidden="true" />
{{ formatMessage(messages.createNewButton) }}
</button>
</ButtonStyled>
</div>
</div>
<div class="collections-grid">
@@ -146,7 +144,7 @@ import {
} from '@modrinth/assets'
import {
Avatar,
Button,
ButtonStyled,
commonMessages,
defineMessages,
DropdownSelect,

View File

@@ -1,5 +1,5 @@
<template>
<div class="dashboard-overview">
<div>
<section class="universal-card dashboard-header">
<Avatar :src="auth.user.avatar_url" size="md" circle :alt="auth.user.username" />
<div class="username">
@@ -12,7 +12,7 @@
</NuxtLink>
</div>
</section>
<div class="dashboard-notifications">
<div>
<section class="universal-card">
<div class="header__row">
<h2 class="header__title text-2xl">
@@ -50,44 +50,12 @@
</template>
<div v-else class="universal-body">
<p>{{ formatMessage(messages.noUnreadNotifications) }}</p>
<nuxt-link class="iconified-button !mt-4" to="/dashboard/notifications/history">
<HistoryIcon />
{{ formatMessage(messages.viewNotificationHistory) }}
</nuxt-link>
</div>
</section>
</div>
<div class="dashboard-analytics">
<section class="universal-card">
<h2>{{ formatMessage(commonMessages.analyticsButton) }}</h2>
<div class="grid-display">
<div class="grid-display__item">
<div class="label">{{ formatMessage(messages.totalDownloads) }}</div>
<div class="value">
{{ $formatNumber(projects.reduce((agg, x) => agg + x.downloads, 0)) }}
</div>
<span>{{
formatMessage(messages.fromProjects, { count: downloadsProjectCount })
}}</span>
<!-- <NuxtLink class="goto-link" to="/dashboard/analytics"-->
<!-- >View breakdown-->
<!-- <ChevronRightIcon-->
<!-- class="featured-header-chevron"-->
<!-- aria-hidden="true"-->
<!-- /></NuxtLink>-->
</div>
<div class="grid-display__item">
<div class="label">{{ formatMessage(messages.totalFollowers) }}</div>
<div class="value">
{{ $formatNumber(projects.reduce((agg, x) => agg + x.followers, 0)) }}
</div>
<span>
<span>{{
formatMessage(messages.fromProjects, { count: followersProjectCount })
}}</span>
</span>
</div>
<ButtonStyled>
<nuxt-link to="/dashboard/notifications/history" class="!mt-4 w-fit">
<HistoryIcon />
{{ formatMessage(messages.viewNotificationHistory) }}
</nuxt-link>
</ButtonStyled>
</div>
</section>
</div>
@@ -97,6 +65,7 @@
import { ChevronRightIcon, HistoryIcon } from '@modrinth/assets'
import {
Avatar,
ButtonStyled,
commonMessages,
defineMessages,
injectModrinthClient,
@@ -152,19 +121,6 @@ useHead({
const auth = await useAuth()
const client = injectModrinthClient()
const { data: projects } = useQuery({
queryKey: computed(() => ['user', auth.value?.user?.id, 'projects']),
queryFn: () => client.labrinth.users_v2.getProjects(auth.value?.user?.id),
placeholderData: [],
})
const downloadsProjectCount = computed(
() => projects.value.filter((project) => project.downloads > 0).length,
)
const followersProjectCount = computed(
() => projects.value.filter((project) => project.followers > 0).length,
)
const { data, refetch } = useQuery({
queryKey: computed(() => ['user', auth.value?.user?.id, 'notifications']),
queryFn: async () => {
@@ -190,40 +146,6 @@ const notifications = computed(() => {
const extraNotifs = computed(() => (data.value ? data.value.extraNotifs : 0))
</script>
<style lang="scss">
.dashboard-overview {
display: grid;
grid-template:
'header header'
'notifications analytics' / 1fr auto;
gap: var(--spacing-card-md);
> .universal-card {
margin: 0;
}
@media screen and (max-width: 750px) {
display: flex;
flex-direction: column;
}
}
.dashboard-notifications {
grid-area: notifications;
//display: flex;
//flex-direction: column;
//gap: var(--spacing-card-md);
a.view-more-notifs {
display: flex;
width: fit-content;
margin-left: auto;
}
}
.dashboard-analytics {
grid-area: analytics;
}
.dashboard-header {
display: flex;
gap: var(--spacing-card-bg);

View File

@@ -21,14 +21,18 @@
</h2>
</div>
<template v-if="!history">
<Button v-if="data.hasRead" @click="updateRoute()">
<HistoryIcon />
{{ formatMessage(messages.viewHistory) }}
</Button>
<Button v-if="notifications.length > 0" color="danger" @click="readAll()">
<CheckCheckIcon />
{{ formatMessage(messages.markAllAsRead) }}
</Button>
<ButtonStyled v-if="data.hasRead">
<button @click="updateRoute()">
<HistoryIcon />
{{ formatMessage(messages.viewHistory) }}
</button>
</ButtonStyled>
<ButtonStyled v-if="notifications.length > 0" color="red">
<button @click="readAll()">
<CheckCheckIcon />
{{ formatMessage(messages.markAllAsRead) }}
</button>
</ButtonStyled>
</template>
</div>
<Chips
@@ -67,7 +71,7 @@
<script setup>
import { CheckCheckIcon, HistoryIcon } from '@modrinth/assets'
import {
Button,
ButtonStyled,
Chips,
commonMessages,
defineMessages,

View File

@@ -5,10 +5,12 @@
<div class="header__row">
<h2 class="header__title text-2xl">{{ formatMessage(messages.organizationsTitle) }}</h2>
<div class="input-group">
<button class="iconified-button brand-button" @click="openCreateOrgModal">
<PlusIcon aria-hidden="true" />
{{ formatMessage(messages.createOrganization) }}
</button>
<ButtonStyled color="brand">
<button @click="openCreateOrgModal">
<PlusIcon aria-hidden="true" />
{{ formatMessage(messages.createOrganization) }}
</button>
</ButtonStyled>
</div>
</div>
<template v-if="orgs?.length > 0">
@@ -51,7 +53,7 @@
<script setup>
import { PlusIcon, UsersIcon } from '@modrinth/assets'
import { Avatar, defineMessages, injectModrinthClient, useVIntl } from '@modrinth/ui'
import { Avatar, ButtonStyled, defineMessages, injectModrinthClient, useVIntl } from '@modrinth/ui'
import { useQuery } from '@tanstack/vue-query'
import OrganizationCreateModal from '~/components/ui/create/OrganizationCreateModal.vue'

View File

@@ -7,68 +7,77 @@
<label for="issue-tracker-input" :title="formatMessage(messages.issueTrackerDescription)">
<span class="label__title">{{ formatMessage(messages.issueTrackerLabel) }}</span>
</label>
<div class="input-group shrink-first">
<div class="flex gap-2">
<StyledInput
id="issue-tracker-input"
v-model="editLinks.issues.val"
:disabled="editLinks.issues.clear"
type="url"
class="w-full"
:placeholder="getLinkInputPlaceholder(editLinks.issues.clear)"
:maxlength="2048"
/>
<button
v-tooltip="formatMessage(messages.clearLinkLabel)"
:aria-label="formatMessage(messages.clearLinkLabel)"
class="square-button label-button"
:data-active="editLinks.issues.clear"
@click="editLinks.issues.clear = !editLinks.issues.clear"
>
<TrashIcon />
</button>
<ButtonStyled circular>
<button
v-tooltip="formatMessage(messages.clearLinkLabel)"
class="label-button"
:aria-label="formatMessage(messages.clearLinkLabel)"
:data-active="editLinks.issues.clear"
@click="editLinks.issues.clear = !editLinks.issues.clear"
>
<TrashIcon />
</button>
</ButtonStyled>
</div>
<label for="source-code-input" :title="formatMessage(messages.sourceCodeDescription)">
<span class="label__title">{{ formatMessage(messages.sourceCodeLabel) }}</span>
</label>
<div class="input-group shrink-first">
<div class="flex gap-2">
<StyledInput
id="source-code-input"
v-model="editLinks.source.val"
:disabled="editLinks.source.clear"
type="url"
class="w-full"
:maxlength="2048"
:placeholder="getLinkInputPlaceholder(editLinks.source.clear)"
/>
<button
v-tooltip="formatMessage(messages.clearLinkLabel)"
:aria-label="formatMessage(messages.clearLinkLabel)"
class="square-button label-button"
:data-active="editLinks.source.clear"
@click="editLinks.source.clear = !editLinks.source.clear"
>
<TrashIcon />
</button>
<ButtonStyled circular>
<button
v-tooltip="formatMessage(messages.clearLinkLabel)"
class="label-button"
:aria-label="formatMessage(messages.clearLinkLabel)"
:data-active="editLinks.source.clear"
@click="editLinks.source.clear = !editLinks.source.clear"
>
<TrashIcon />
</button>
</ButtonStyled>
</div>
<label for="wiki-page-input" :title="formatMessage(messages.wikiPageDescription)">
<span class="label__title">{{ formatMessage(messages.wikiPageLabel) }}</span>
</label>
<div class="input-group shrink-first">
<div class="flex gap-2">
<StyledInput
id="wiki-page-input"
v-model="editLinks.wiki.val"
:disabled="editLinks.wiki.clear"
type="url"
class="w-full"
:maxlength="2048"
:placeholder="getLinkInputPlaceholder(editLinks.wiki.clear)"
/>
<button
v-tooltip="formatMessage(messages.clearLinkLabel)"
:aria-label="formatMessage(messages.clearLinkLabel)"
class="square-button label-button"
:data-active="editLinks.wiki.clear"
@click="editLinks.wiki.clear = !editLinks.wiki.clear"
>
<TrashIcon />
</button>
<ButtonStyled circular>
<button
v-tooltip="formatMessage(messages.clearLinkLabel)"
class="label-button"
:aria-label="formatMessage(messages.clearLinkLabel)"
:data-active="editLinks.wiki.clear"
@click="editLinks.wiki.clear = !editLinks.wiki.clear"
>
<TrashIcon />
</button>
</ButtonStyled>
</div>
<label
for="discord-invite-input"
@@ -76,24 +85,27 @@
>
<span class="label__title">{{ formatMessage(messages.discordInviteLabel) }}</span>
</label>
<div class="input-group shrink-first">
<div class="flex gap-2">
<StyledInput
id="discord-invite-input"
v-model="editLinks.discord.val"
:disabled="editLinks.discord.clear"
class="w-full"
type="url"
:maxlength="2048"
:placeholder="getLinkInputPlaceholder(editLinks.discord.clear, true)"
/>
<button
v-tooltip="formatMessage(messages.clearLinkLabel)"
:aria-label="formatMessage(messages.clearLinkLabel)"
class="square-button label-button"
:data-active="editLinks.discord.clear"
@click="editLinks.discord.clear = !editLinks.discord.clear"
>
<TrashIcon />
</button>
<ButtonStyled circular>
<button
v-tooltip="formatMessage(messages.clearLinkLabel)"
class="label-button"
:aria-label="formatMessage(messages.clearLinkLabel)"
:data-active="editLinks.discord.clear"
@click="editLinks.discord.clear = !editLinks.discord.clear"
>
<TrashIcon />
</button>
</ButtonStyled>
</div>
</section>
<p>
@@ -128,15 +140,19 @@
:label="formatMessage(messages.showAllProjects)"
:description="formatMessage(messages.showAllProjects)"
/>
<div class="push-right input-group">
<button class="iconified-button" @click="$refs.editLinksModal.hide()">
<XIcon />
{{ formatMessage(commonMessages.cancelButton) }}
</button>
<button class="iconified-button brand-button" @click="bulkEditLinks()">
<SaveIcon />
{{ formatMessage(commonMessages.saveChangesButton) }}
</button>
<div class="input-group ml-auto mt-4">
<ButtonStyled type="outlined">
<button @click="$refs.editLinksModal.hide()">
<XIcon />
{{ formatMessage(commonMessages.cancelButton) }}
</button>
</ButtonStyled>
<ButtonStyled color="brand">
<button @click="bulkEditLinks()">
<SaveIcon />
{{ formatMessage(commonMessages.saveChangesButton) }}
</button>
</ButtonStyled>
</div>
</div>
</NewModal>
@@ -145,10 +161,12 @@
<div class="header__row">
<h2 class="header__title text-2xl">{{ formatMessage(messages.headTitle) }}</h2>
<div class="input-group">
<button class="iconified-button brand-button" @click="$refs.modal_creation.show($event)">
<PlusIcon />
{{ formatMessage(commonMessages.createAProjectButton) }}
</button>
<ButtonStyled color="brand">
<button @click="$refs.modal_creation.show($event)">
<PlusIcon />
{{ formatMessage(commonMessages.createAProjectButton) }}
</button>
</ButtonStyled>
</div>
</div>
<p v-if="projects.length < 1">
@@ -157,14 +175,12 @@
<template v-else>
<p>{{ formatMessage(messages.bulkEditHint) }}</p>
<div class="input-group">
<button
class="iconified-button"
:disabled="selectedProjects.length === 0"
@click="$refs.editLinksModal.show()"
>
<EditIcon />
{{ formatMessage(messages.editLinksButton) }}
</button>
<ButtonStyled>
<button :disabled="selectedProjects.length === 0" @click="$refs.editLinksModal.show()">
<EditIcon />
{{ formatMessage(messages.editLinksButton) }}
</button>
</ButtonStyled>
<div class="push-right">
<div class="labeled-control-row">
{{ formatMessage(commonMessages.sortByLabel) }}
@@ -175,14 +191,15 @@
:options="sortOptions"
@update:model-value="projects = updateSort(projects, sortBy, descending)"
/>
<button
v-tooltip="formatMessage(descending ? messages.descending : messages.ascending)"
class="square-button"
@click="updateDescending()"
>
<SortDescIcon v-if="descending" />
<SortAscIcon v-else />
</button>
<ButtonStyled circular>
<button
v-tooltip="formatMessage(descending ? messages.descending : messages.ascending)"
@click="updateDescending()"
>
<SortDescIcon v-if="descending" />
<SortAscIcon v-else />
</button>
</ButtonStyled>
</div>
</div>
</div>

View File

@@ -603,9 +603,7 @@
</h2>
</div>
<div
class="experimental-styles-within flex w-full flex-col-reverse gap-2 md:w-auto md:flex-col md:items-center"
>
<div class="flex w-full flex-col-reverse gap-2 md:w-auto md:flex-col md:items-center">
<ButtonStyled color="standard" size="large">
<button class="w-full md:w-fit" @click="selectProduct('custom')">
{{ formatMessage(messages.getStartedButton) }}

View File

@@ -208,7 +208,7 @@
<p>
<IntlFormatted :message-id="messages.playWithLauncherDescription">
<template #link="{ children }">
<nuxt-link class="title-link" to="/app">
<nuxt-link class="underline hover:brightness-[--hover-brightness]" to="/app">
<component :is="() => children" />
</nuxt-link>
</template>

View File

@@ -1,7 +1,5 @@
<template>
<div
class="experimental-styles-within relative mx-auto mb-6 flex min-h-screen w-full max-w-[1280px] flex-col px-6"
>
<div class="relative mx-auto mb-6 flex min-h-screen w-full max-w-[1280px] flex-col px-6">
<h1>Moderation</h1>
<NavTabs :links="moderationLinks" class="mb-4 hidden sm:flex" />
<div class="mb-4 sm:hidden">

View File

@@ -57,7 +57,7 @@
</Combobox>
</div>
<ButtonStyled color="orange" class="w-full sm:w-auto">
<ButtonStyled color="orange">
<button
class="flex !h-[40px] w-full items-center justify-center gap-2 sm:w-auto"
:disabled="paginatedProjects?.length === 0"

View File

@@ -108,7 +108,7 @@ onMounted(() => {
</script>
<template>
<div class="page experimental-styles-within py-6">
<div class="page py-6">
<div
class="flex flex-wrap items-center justify-between gap-4 border-0 border-b-[1px] border-solid border-divider px-6 pb-6"
>
@@ -122,7 +122,7 @@ onMounted(() => {
<RssIcon />
</a>
</ButtonStyled>
<ButtonStyled circular icon-only>
<ButtonStyled circular>
<a v-tooltip="`Changelog`" href="/news/changelog" aria-label="Changelog">
<GitGraphIcon />
</a>

View File

@@ -1,5 +1,5 @@
<template>
<div class="page experimental-styles-within">
<div class="page">
<h1 class="m-0 text-3xl font-extrabold">Changelog</h1>
<p class="my-3">Keep up-to-date on what's new with Modrinth.</p>
<NuxtPage />

View File

@@ -41,7 +41,7 @@ useSeoMeta({
</script>
<template>
<div class="page experimental-styles-within py-6">
<div class="page py-6">
<div class="flex flex-wrap items-center justify-between gap-4 px-6">
<div>
<h1 class="m-0 text-3xl font-extrabold">News</h1>
@@ -53,7 +53,7 @@ useSeoMeta({
<RssIcon />
</a>
</ButtonStyled>
<ButtonStyled circular icon-only>
<ButtonStyled circular>
<a v-tooltip="`Changelog`" href="/news/changelog" aria-label="Changelog">
<GitGraphIcon />
</a>

View File

@@ -4,7 +4,7 @@
</div>
<div
v-else-if="organization"
class="experimental-styles-within new-page sidebar"
class="new-page sidebar"
:class="{ 'alt-layout': cosmetics.leftContentLayout || routeHasSettings }"
>
<ModalCreation ref="modal_creation" :organization-id="organization.id" />
@@ -176,14 +176,18 @@
<h2>Invitation to join {{ organization.name }}</h2>
<p>You have been invited to join {{ organization.name }}.</p>
<div class="input-group">
<button class="iconified-button brand-button" @click="onAcceptInvite">
<CheckIcon />
Accept
</button>
<button class="iconified-button danger-button" @click="onDeclineInvite">
<XIcon />
Decline
</button>
<ButtonStyled color="brand">
<button @click="onAcceptInvite">
<CheckIcon />
Accept
</button>
</ButtonStyled>
<ButtonStyled color="red">
<button @click="onDeclineInvite">
<XIcon />
Decline
</button>
</ButtonStyled>
</div>
</div>
<div v-if="navLinks.length > 2" class="mb-4 max-w-full overflow-x-auto">

View File

@@ -2,7 +2,7 @@
import { TrashIcon, UploadIcon } from '@modrinth/assets'
import {
Avatar,
Button,
ButtonStyled,
ConfirmModal,
FileInput,
injectNotificationManager,
@@ -161,27 +161,27 @@ const onDeleteOrganization = useClientTry(async () => {
size="md"
class="project__icon"
/>
<div class="input-stack">
<FileInput
id="project-icon"
:max-size="262144"
:show-icon="true"
accept="image/png,image/jpeg,image/gif,image/webp"
class="btn"
prompt="Upload icon"
:disabled="!hasPermission"
@change="showPreviewImage"
>
<UploadIcon />
</FileInput>
<Button
v-if="!deletedIcon && (previewImage || organization.icon_url)"
:disabled="!hasPermission"
@click="markIconForDeletion"
>
<TrashIcon />
Remove icon
</Button>
<div class="flex flex-col gap-2">
<ButtonStyled>
<FileInput
id="project-icon"
:max-size="262144"
:show-icon="true"
accept="image/png,image/jpeg,image/gif,image/webp"
class="button-like"
prompt="Upload icon"
:disabled="!hasPermission"
@change="showPreviewImage"
>
<UploadIcon />
</FileInput>
</ButtonStyled>
<ButtonStyled v-if="!deletedIcon && (previewImage || organization.icon_url)">
<button :disabled="!hasPermission" @click="markIconForDeletion">
<TrashIcon />
Remove icon
</button>
</ButtonStyled>
</div>
</div>
@@ -231,10 +231,12 @@ const onDeleteOrganization = useClientTry(async () => {
Deleting your organization will transfer all of its projects to the organization owner. This
action cannot be undone.
</p>
<Button color="danger" @click="() => $refs.modal_deletion.show()">
<TrashIcon />
Delete organization
</Button>
<ButtonStyled color="red">
<button @click="() => $refs.modal_deletion.show()">
<TrashIcon />
Delete organization
</button>
</ButtonStyled>
</div>
<UnsavedChangesPopup
:original="originalState"

View File

@@ -43,19 +43,20 @@
@keypress.enter="() => onInviteTeamMember(organization.team, currentUsername)"
/>
<label for="username" class="hidden">Username</label>
<Button
color="primary"
:disabled="
!isPermission(
currentMember.organization_permissions,
organizationPermissions.MANAGE_INVITES,
)
"
@click="() => onInviteTeamMember(organization.team_id, currentUsername)"
>
<UserPlusIcon />
Invite
</Button>
<ButtonStyled color="brand">
<button
:disabled="
!isPermission(
currentMember.organization_permissions,
organizationPermissions.MANAGE_INVITES,
)
"
@click="() => onInviteTeamMember(organization.team_id, currentUsername)"
>
<UserPlusIcon />
Invite
</button>
</ButtonStyled>
</div>
<div class="adjacent-input">
<span class="label">
@@ -64,14 +65,15 @@
Remove yourself as a member of this organization.
</span>
</span>
<Button
color="danger"
:disabled="currentMember.is_owner"
@click="() => onLeaveProject(organization.team_id, auth.user.id)"
>
<UserRemoveIcon />
Leave organization
</Button>
<ButtonStyled color="red">
<button
:disabled="currentMember.is_owner"
@click="() => onLeaveProject(organization.team_id, auth.user.id)"
>
<UserRemoveIcon />
Leave organization
</button>
</ButtonStyled>
</div>
</div>
<div
@@ -94,18 +96,18 @@
<div class="side-buttons">
<Badge v-if="member.accepted" type="accepted" />
<Badge v-else type="pending" />
<Button
icon-only
transparent
class="dropdown-icon"
@click="
openTeamMembers.indexOf(member.user.id) === -1
? openTeamMembers.push(member.user.id)
: (openTeamMembers = openTeamMembers.filter((it) => it !== member.user.id))
"
>
<DropdownIcon />
</Button>
<ButtonStyled circular type="transparent">
<button
class="dropdown-icon"
@click="
openTeamMembers.indexOf(member.user.id) === -1
? openTeamMembers.push(member.user.id)
: (openTeamMembers = openTeamMembers.filter((it) => it !== member.user.id))
"
>
<DropdownIcon />
</button>
</ButtonStyled>
</div>
</div>
<div class="content">
@@ -188,44 +190,44 @@
</div>
</template>
<div class="input-group">
<Button
color="primary"
:disabled="
!isPermission(
currentMember.organization_permissions,
organizationPermissions.EDIT_MEMBER,
)
"
@click="onUpdateTeamMember(organization.team_id, member)"
>
<SaveIcon />
Save changes
</Button>
<Button
v-if="!member.is_owner"
color="danger"
:disabled="
!isPermission(
currentMember.organization_permissions,
organizationPermissions.EDIT_MEMBER,
) &&
!isPermission(
currentMember.organization_permissions,
organizationPermissions.REMOVE_MEMBER,
)
"
@click="onRemoveMember(organization.team_id, member)"
>
<UserRemoveIcon />
Remove member
</Button>
<Button
v-if="!member.is_owner && currentMember.is_owner && member.accepted"
@click="(e) => openTransferModal(member, e)"
>
<TransferIcon />
Transfer ownership
</Button>
<ButtonStyled color="brand">
<button
:disabled="
!isPermission(
currentMember.organization_permissions,
organizationPermissions.EDIT_MEMBER,
)
"
@click="onUpdateTeamMember(organization.team_id, member)"
>
<SaveIcon />
Save changes
</button>
</ButtonStyled>
<ButtonStyled v-if="!member.is_owner" color="red">
<button
:disabled="
!isPermission(
currentMember.organization_permissions,
organizationPermissions.EDIT_MEMBER,
) &&
!isPermission(
currentMember.organization_permissions,
organizationPermissions.REMOVE_MEMBER,
)
"
@click="onRemoveMember(organization.team_id, member)"
>
<UserRemoveIcon />
Remove member
</button>
</ButtonStyled>
<ButtonStyled v-if="!member.is_owner && currentMember.is_owner && member.accepted">
<button @click="(e) => openTransferModal(member, e)">
<TransferIcon />
Transfer ownership
</button>
</ButtonStyled>
</div>
</div>
</div>
@@ -244,7 +246,7 @@ import {
import {
Avatar,
Badge,
Button,
ButtonStyled,
Checkbox,
injectNotificationManager,
StyledInput,

View File

@@ -25,15 +25,17 @@
"
:maxlength="2048"
/>
<button
v-tooltip="'Clear link'"
aria-label="Clear link"
class="square-button label-button"
:data-active="editLinks.issues.clear"
@click="editLinks.issues.clear = !editLinks.issues.clear"
>
<TrashIcon />
</button>
<ButtonStyled circular>
<button
v-tooltip="'Clear link'"
class="label-button"
aria-label="Clear link"
:data-active="editLinks.issues.clear"
@click="editLinks.issues.clear = !editLinks.issues.clear"
>
<TrashIcon />
</button>
</ButtonStyled>
</div>
<label
for="source-code-input"
@@ -52,15 +54,17 @@
editLinks.source.clear ? 'Existing link will be cleared' : 'Enter a valid URL'
"
/>
<button
v-tooltip="'Clear link'"
aria-label="Clear link"
class="square-button label-button"
:data-active="editLinks.source.clear"
@click="editLinks.source.clear = !editLinks.source.clear"
>
<TrashIcon />
</button>
<ButtonStyled circular>
<button
v-tooltip="'Clear link'"
class="label-button"
aria-label="Clear link"
:data-active="editLinks.source.clear"
@click="editLinks.source.clear = !editLinks.source.clear"
>
<TrashIcon />
</button>
</ButtonStyled>
</div>
<label
for="wiki-page-input"
@@ -79,15 +83,17 @@
editLinks.wiki.clear ? 'Existing link will be cleared' : 'Enter a valid URL'
"
/>
<button
v-tooltip="'Clear link'"
aria-label="Clear link"
class="square-button label-button"
:data-active="editLinks.wiki.clear"
@click="editLinks.wiki.clear = !editLinks.wiki.clear"
>
<TrashIcon />
</button>
<ButtonStyled circular>
<button
v-tooltip="'Clear link'"
class="label-button"
aria-label="Clear link"
:data-active="editLinks.wiki.clear"
@click="editLinks.wiki.clear = !editLinks.wiki.clear"
>
<TrashIcon />
</button>
</ButtonStyled>
</div>
<label for="discord-invite-input" title="An invitation link to your Discord server.">
<span class="label__title">Discord invite</span>
@@ -105,15 +111,17 @@
: 'Enter a valid Discord invite URL'
"
/>
<button
v-tooltip="'Clear link'"
aria-label="Clear link"
class="square-button label-button"
:data-active="editLinks.discord.clear"
@click="editLinks.discord.clear = !editLinks.discord.clear"
>
<TrashIcon />
</button>
<ButtonStyled circular>
<button
v-tooltip="'Clear link'"
class="label-button"
aria-label="Clear link"
:data-active="editLinks.discord.clear"
@click="editLinks.discord.clear = !editLinks.discord.clear"
>
<TrashIcon />
</button>
</ButtonStyled>
</div>
</section>
<p>
@@ -143,14 +151,18 @@
description="Show all projects"
/>
<div class="push-right input-group">
<button class="iconified-button" @click="$refs.editLinksModal.hide()">
<XIcon />
Cancel
</button>
<button class="iconified-button brand-button" @click="onBulkEditLinks">
<SaveIcon />
Save changes
</button>
<ButtonStyled>
<button @click="$refs.editLinksModal.hide()">
<XIcon />
Cancel
</button>
</ButtonStyled>
<ButtonStyled color="brand">
<button @click="onBulkEditLinks">
<SaveIcon />
Save changes
</button>
</ButtonStyled>
</div>
</div>
</NewModal>
@@ -159,10 +171,12 @@
<div class="header__row">
<h2 class="header__title text-2xl">Projects</h2>
<div class="input-group">
<button class="iconified-button brand-button" @click="$refs.modal_creation.show($event)">
<PlusIcon />
{{ formatMessage(commonMessages.createAProjectButton) }}
</button>
<ButtonStyled color="brand">
<button @click="$refs.modal_creation.show($event)">
<PlusIcon />
{{ formatMessage(commonMessages.createAProjectButton) }}
</button>
</ButtonStyled>
<OrganizationProjectTransferModal
:projects="usersOwnedProjects || []"
@submit="onProjectTransferSubmit"
@@ -175,14 +189,12 @@
<template v-else>
<p>You can edit multiple projects at once by selecting them below.</p>
<div class="input-group">
<button
class="iconified-button"
:disabled="selectedProjects.length === 0"
@click="$refs.editLinksModal.show()"
>
<EditIcon />
Edit links
</button>
<ButtonStyled>
<button :disabled="selectedProjects.length === 0" @click="$refs.editLinksModal.show()">
<EditIcon />
Edit links
</button>
</ButtonStyled>
<div class="push-right">
<div class="labeled-control-row">
Sort by
@@ -193,14 +205,15 @@
:options="sortOptions"
@change="sortedProjects = updateSort(sortedProjects, sortBy, descending)"
/>
<button
v-tooltip="descending ? 'Descending' : 'Ascending'"
class="square-button"
@click="updateDescending()"
>
<SortDescIcon v-if="descending" />
<SortAscIcon v-else />
</button>
<ButtonStyled circular>
<button
v-tooltip="descending ? 'Descending' : 'Ascending'"
@click="updateDescending()"
>
<SortDescIcon v-if="descending" />
<SortAscIcon v-else />
</button>
</ButtonStyled>
</div>
</div>
</div>

View File

@@ -38,24 +38,24 @@
{{ calculateSavings(price.prices.intervals.monthly, price.prices.intervals.yearly) }}% with
annual billing!
</p>
<nuxt-link
<ButtonStyled
v-if="auth.user && isPermission(auth.user.badges, 1 << 0)"
to="/settings/billing"
class="btn btn-purple btn-large"
color="purple"
size="large"
>
<SettingsIcon aria-hidden="true" />
Manage subscription
</nuxt-link>
<button v-else-if="auth.user" class="btn btn-purple btn-large" @click="purchaseModal.show()">
Subscribe
</button>
<nuxt-link
v-else
:to="`/auth/sign-in?redirect=${encodeURIComponent('/plus?showModal=true')}`"
class="btn btn-purple btn-large"
>
Subscribe
</nuxt-link>
<nuxt-link to="/settings/billing">
<SettingsIcon aria-hidden="true" />
Manage subscription
</nuxt-link>
</ButtonStyled>
<ButtonStyled v-else-if="auth.user" color="purple" size="large">
<button @click="purchaseModal.show()">Subscribe</button>
</ButtonStyled>
<ButtonStyled v-else color="purple" size="large">
<nuxt-link :to="`/auth/sign-in?redirect=${encodeURIComponent('/plus?showModal=true')}`">
Subscribe
</nuxt-link>
</ButtonStyled>
</div>
</div>
<div class="perks-hero">
@@ -86,7 +86,12 @@
</template>
<script setup>
import { HeartIcon, ModrinthPlusIcon, SettingsIcon, SparklesIcon, StarIcon } from '@modrinth/assets'
import { injectNotificationManager, PurchaseModal, useFormatPrice } from '@modrinth/ui'
import {
ButtonStyled,
injectNotificationManager,
PurchaseModal,
useFormatPrice,
} from '@modrinth/ui'
import { calculateSavings, getCurrency } from '@modrinth/utils'
import { useBaseFetch } from '@/composables/fetch.js'

View File

@@ -1,6 +1,6 @@
<template>
<div class="page">
<div class="experimental-styles-within flex flex-col gap-2">
<div class="flex flex-col gap-2">
<RadialHeader class="top-box mb-2 flex flex-col items-center justify-center" color="orange">
<ScaleIcon class="h-12 w-12 text-brand-orange" />
<h1 class="m-3 gap-2 text-3xl font-extrabold">

View File

@@ -27,19 +27,18 @@
@keyup.enter="saveEmail()"
/>
<div class="input-group push-right">
<button class="iconified-button" @click="$refs.changeEmailModal.hide()">
<XIcon />
{{ formatMessage(commonMessages.cancelButton) }}
</button>
<button
type="button"
class="iconified-button brand-button"
:disabled="!email"
@click="saveEmail()"
>
<SaveIcon />
{{ formatMessage(messages.saveEmailButton) }}
</button>
<ButtonStyled>
<button @click="$refs.changeEmailModal.hide()">
<XIcon />
{{ formatMessage(commonMessages.cancelButton) }}
</button>
</ButtonStyled>
<ButtonStyled color="brand">
<button :disabled="!email" @click="saveEmail()">
<SaveIcon />
{{ formatMessage(messages.saveEmailButton) }}
</button>
</ButtonStyled>
</div>
</div>
</Modal>
@@ -108,44 +107,43 @@
</template>
<p></p>
<div class="input-group push-right">
<button class="iconified-button" @click="$refs.managePasswordModal.hide()">
<XIcon />
{{ formatMessage(commonMessages.cancelButton) }}
</button>
<template v-if="removePasswordMode">
<button
type="button"
class="iconified-button danger-button"
:disabled="!oldPassword"
@click="savePassword"
>
<TrashIcon />
{{ formatMessage(messages.removePasswordButton) }}
<ButtonStyled>
<button @click="$refs.managePasswordModal.hide()">
<XIcon />
{{ formatMessage(commonMessages.cancelButton) }}
</button>
</ButtonStyled>
<template v-if="removePasswordMode">
<ButtonStyled color="red">
<button :disabled="!oldPassword" @click="savePassword">
<TrashIcon />
{{ formatMessage(messages.removePasswordButton) }}
</button>
</ButtonStyled>
</template>
<template v-else>
<button
<ButtonStyled
v-if="auth.user.has_password && auth.user.auth_providers.length > 0"
type="button"
class="iconified-button danger-button"
@click="removePasswordMode = true"
color="red"
>
<TrashIcon />
{{ formatMessage(messages.removePasswordButton) }}
</button>
<button
type="button"
class="iconified-button brand-button"
:disabled="
newPassword.length == 0 ||
(auth.user.has_password && oldPassword.length == 0) ||
newPassword !== confirmNewPassword
"
@click="savePassword"
>
<SaveIcon />
{{ formatMessage(messages.savePasswordButton) }}
</button>
<button @click="removePasswordMode = true">
<TrashIcon />
{{ formatMessage(messages.removePasswordButton) }}
</button>
</ButtonStyled>
<ButtonStyled color="brand">
<button
:disabled="
newPassword.length == 0 ||
(auth.user.has_password && oldPassword.length == 0) ||
newPassword !== confirmNewPassword
"
@click="savePassword"
>
<SaveIcon />
{{ formatMessage(messages.savePasswordButton) }}
</button>
</ButtonStyled>
</template>
</div>
</div>
@@ -173,14 +171,18 @@
{{ formatMessage(messages.twoFactorIncorrectError) }}
</p>
<div class="input-group push-right">
<button class="iconified-button" @click="$refs.manageTwoFactorModal.hide()">
<XIcon />
{{ formatMessage(commonMessages.cancelButton) }}
</button>
<button class="iconified-button danger-button" @click="removeTwoFactor">
<TrashIcon />
{{ formatMessage(messages.twoFactorRemoveButton) }}
</button>
<ButtonStyled>
<button @click="$refs.manageTwoFactorModal.hide()">
<XIcon />
{{ formatMessage(commonMessages.cancelButton) }}
</button>
</ButtonStyled>
<ButtonStyled color="red">
<button @click="removeTwoFactor">
<TrashIcon />
{{ formatMessage(messages.twoFactorRemoveButton) }}
</button>
</ButtonStyled>
</div>
</template>
<template v-else>
@@ -247,34 +249,30 @@
</ul>
</template>
<div class="input-group push-right">
<button v-if="twoFactorStep === 1" class="iconified-button" @click="twoFactorStep = 0">
<LeftArrowIcon />
{{ formatMessage(commonMessages.backButton) }}
</button>
<button
v-if="twoFactorStep !== 2"
class="iconified-button"
@click="$refs.manageTwoFactorModal.hide()"
>
<XIcon />
{{ formatMessage(commonMessages.cancelButton) }}
</button>
<button
v-if="twoFactorStep <= 1"
class="iconified-button brand-button"
@click="twoFactorStep === 1 ? verifyTwoFactorCode() : (twoFactorStep = 1)"
>
<RightArrowIcon />
{{ formatMessage(commonMessages.continueButton) }}
</button>
<button
v-if="twoFactorStep === 2"
class="iconified-button brand-button"
@click="$refs.manageTwoFactorModal.hide()"
>
<CheckIcon />
{{ formatMessage(messages.completeSetupButton) }}
</button>
<ButtonStyled v-if="twoFactorStep === 1">
<button @click="twoFactorStep = 0">
<LeftArrowIcon />
{{ formatMessage(commonMessages.backButton) }}
</button>
</ButtonStyled>
<ButtonStyled v-if="twoFactorStep !== 2">
<button @click="$refs.manageTwoFactorModal.hide()">
<XIcon />
{{ formatMessage(commonMessages.cancelButton) }}
</button>
</ButtonStyled>
<ButtonStyled v-if="twoFactorStep <= 1" color="brand">
<button @click="twoFactorStep === 1 ? verifyTwoFactorCode() : (twoFactorStep = 1)">
<RightArrowIcon />
{{ formatMessage(commonMessages.continueButton) }}
</button>
</ButtonStyled>
<ButtonStyled v-if="twoFactorStep === 2" color="brand">
<button @click="$refs.manageTwoFactorModal.hide()">
<CheckIcon />
{{ formatMessage(messages.completeSetupButton) }}
</button>
</ButtonStyled>
</div>
</template>
</div>
@@ -295,29 +293,27 @@
<span><component :is="provider.icon" /> {{ provider.display }}</span>
</div>
<div class="table-text manage table-cell">
<button
v-if="auth.user.auth_providers.includes(provider.id)"
class="btn"
@click="handleRemoveAuthProvider(provider.id)"
>
<TrashIcon /> {{ formatMessage(commonMessages.removeButton) }}
</button>
<a
v-else
class="btn"
:href="`${getAuthUrl(provider.id, '/settings/account')}&token=${auth.token}`"
>
<ExternalIcon /> {{ formatMessage(messages.providerAddButton) }}
</a>
<ButtonStyled v-if="auth.user.auth_providers.includes(provider.id)">
<button @click="handleRemoveAuthProvider(provider.id)">
<TrashIcon /> {{ formatMessage(commonMessages.removeButton) }}
</button>
</ButtonStyled>
<ButtonStyled v-else>
<a :href="`${getAuthUrl(provider.id, '/settings/account')}&token=${auth.token}`">
<ExternalIcon /> {{ formatMessage(messages.providerAddButton) }}
</a>
</ButtonStyled>
</div>
</div>
</div>
<p></p>
<div class="input-group push-right">
<button class="iconified-button" @click="$refs.manageProvidersModal.hide()">
<XIcon />
{{ formatMessage(commonMessages.closeButton) }}
</button>
<ButtonStyled>
<button @click="$refs.manageProvidersModal.hide()">
<XIcon />
{{ formatMessage(commonMessages.closeButton) }}
</button>
</ButtonStyled>
</div>
</div>
</Modal>
@@ -332,16 +328,18 @@
}}</span>
</label>
<div>
<button class="iconified-button" @click="$refs.changeEmailModal.show()">
<template v-if="auth.user.email">
<EditIcon />
{{ formatMessage(messages.changeEmailButton) }}
</template>
<template v-else>
<PlusIcon />
{{ formatMessage(messages.addEmailButton) }}
</template>
</button>
<ButtonStyled>
<button @click="$refs.changeEmailModal.show()">
<template v-if="auth.user.email">
<EditIcon />
{{ formatMessage(messages.changeEmailButton) }}
</template>
<template v-else>
<PlusIcon />
{{ formatMessage(messages.addEmailButton) }}
</template>
</button>
</ButtonStyled>
</div>
</div>
<div class="adjacent-input">
@@ -359,24 +357,25 @@
</span>
</label>
<div>
<button
class="iconified-button"
@click="
() => {
oldPassword = ''
newPassword = ''
confirmNewPassword = ''
removePasswordMode = false
$refs.managePasswordModal.show()
}
"
>
<KeyIcon />
<template v-if="auth.user.has_password">{{
formatMessage(messages.changePasswordButton)
}}</template>
<template v-else> {{ formatMessage(messages.addPasswordButton) }} </template>
</button>
<ButtonStyled>
<button
@click="
() => {
oldPassword = ''
newPassword = ''
confirmNewPassword = ''
removePasswordMode = false
$refs.managePasswordModal.show()
}
"
>
<KeyIcon />
<template v-if="auth.user.has_password">{{
formatMessage(messages.changePasswordButton)
}}</template>
<template v-else> {{ formatMessage(messages.addPasswordButton) }} </template>
</button>
</ButtonStyled>
</div>
</div>
<div class="adjacent-input">
@@ -387,14 +386,16 @@
}}</span>
</label>
<div>
<button class="iconified-button" @click="showTwoFactorModal">
<template v-if="auth.user.has_totp">
<TrashIcon /> {{ formatMessage(messages.twoFactorRemoveButton) }}
</template>
<template v-else>
<PlusIcon /> {{ formatMessage(messages.twoFactorSetupButton) }}
</template>
</button>
<ButtonStyled>
<button @click="showTwoFactorModal">
<template v-if="auth.user.has_totp">
<TrashIcon /> {{ formatMessage(messages.twoFactorRemoveButton) }}
</template>
<template v-else>
<PlusIcon /> {{ formatMessage(messages.twoFactorSetupButton) }}
</template>
</button>
</ButtonStyled>
</div>
</div>
<div class="adjacent-input">
@@ -405,9 +406,11 @@
}}</span>
</label>
<div>
<button class="iconified-button" @click="$refs.manageProvidersModal.show()">
<SettingsIcon /> {{ formatMessage(messages.manageProvidersButton) }}
</button>
<ButtonStyled>
<button @click="$refs.manageProvidersModal.show()">
<SettingsIcon /> {{ formatMessage(messages.manageProvidersButton) }}
</button>
</ButtonStyled>
</div>
</div>
</section>
@@ -415,31 +418,33 @@
<section id="data-export" class="universal-card">
<h2>{{ formatMessage(messages.dataExportTitle) }}</h2>
<p>{{ formatMessage(messages.dataExportDescription) }}</p>
<a v-if="generated" class="iconified-button" :href="generated" download="export.json">
<DownloadIcon />
{{ formatMessage(messages.downloadExportButton) }}
</a>
<button v-else class="iconified-button" :disabled="generatingExport" @click="exportData">
<template v-if="generatingExport">
<UpdatedIcon /> {{ formatMessage(messages.generatingExportButton) }}
</template>
<template v-else>
<UpdatedIcon /> {{ formatMessage(messages.generateExportButton) }}
</template>
</button>
<ButtonStyled v-if="generated">
<a :href="generated" download="export.json">
<DownloadIcon />
{{ formatMessage(messages.downloadExportButton) }}
</a>
</ButtonStyled>
<ButtonStyled v-else>
<button :disabled="generatingExport" @click="exportData">
<template v-if="generatingExport">
<UpdatedIcon /> {{ formatMessage(messages.generatingExportButton) }}
</template>
<template v-else>
<UpdatedIcon /> {{ formatMessage(messages.generateExportButton) }}
</template>
</button>
</ButtonStyled>
</section>
<section id="delete-account" class="universal-card">
<h2>{{ formatMessage(messages.deleteAccountSectionTitle) }}</h2>
<p>{{ formatMessage(messages.deleteAccountSectionDescription) }}</p>
<button
type="button"
class="iconified-button danger-button"
@click="$refs.modal_confirm.show()"
>
<TrashIcon />
{{ formatMessage(messages.deleteAccountButton) }}
</button>
<ButtonStyled color="red">
<button type="button" @click="$refs.modal_confirm.show()">
<TrashIcon />
{{ formatMessage(messages.deleteAccountButton) }}
</button>
</ButtonStyled>
</section>
</div>
</template>
@@ -460,6 +465,7 @@ import {
XIcon,
} from '@modrinth/assets'
import {
ButtonStyled,
commonMessages,
ConfirmModal,
defineMessages,

View File

@@ -24,15 +24,17 @@
</label>
<div v-if="editingId" class="icon-submission">
<Avatar size="md" :src="icon" />
<FileInput
:max-size="262144"
class="btn"
:prompt="formatMessage(messages.uploadIcon)"
accept="image/png,image/jpeg,image/gif,image/webp"
@change="onImageSelection"
>
<UploadIcon />
</FileInput>
<ButtonStyled>
<FileInput
:max-size="262144"
class="button-like"
:prompt="formatMessage(messages.uploadIcon)"
accept="image/png,image/jpeg,image/gif,image/webp"
@change="onImageSelection"
>
<UploadIcon />
</FileInput>
</ButtonStyled>
</div>
<label v-if="editingId" for="app-url">
<span class="label__title">{{ formatMessage(messages.urlLabel) }}</span>
@@ -94,51 +96,46 @@
autocomplete="off"
:placeholder="formatMessage(messages.redirectUriPlaceholder)"
/>
<Button v-if="index !== 0" icon-only @click="() => redirectUris.splice(index, 1)">
<TrashIcon />
</Button>
<Button
v-if="index === 0"
color="primary"
icon-only
@click="() => redirectUris.push('')"
>
<PlusIcon /> {{ formatMessage(messages.addMore) }}
</Button>
<ButtonStyled v-if="index !== 0" circular>
<button @click="() => redirectUris.splice(index, 1)">
<TrashIcon />
</button>
</ButtonStyled>
<ButtonStyled v-if="index === 0" color="brand">
<button @click="() => redirectUris.push('')">
<PlusIcon /> {{ formatMessage(messages.addMore) }}
</button>
</ButtonStyled>
</div>
</div>
<div v-if="redirectUris.length <= 0">
<Button color="primary" icon-only @click="() => redirectUris.push('')">
<PlusIcon /> {{ formatMessage(messages.addRedirectUri) }}
</Button>
<ButtonStyled color="brand">
<button @click="() => redirectUris.push('')">
<PlusIcon /> {{ formatMessage(messages.addRedirectUri) }}
</button>
</ButtonStyled>
</div>
</div>
<div class="submit-row input-group push-right">
<button class="iconified-button" @click="$refs.appModal.hide()">
<XIcon />
{{ formatMessage(messages.cancel) }}
</button>
<button
v-if="editingId"
:disabled="!canSubmit"
type="button"
class="iconified-button brand-button"
@click="editApp"
>
<SaveIcon />
{{ formatMessage(messages.saveChanges) }}
</button>
<button
v-else
:disabled="!canSubmit"
type="button"
class="iconified-button brand-button"
@click="createApp"
>
<PlusIcon />
{{ formatMessage(messages.createApp) }}
</button>
<ButtonStyled>
<button @click="$refs.appModal.hide()">
<XIcon />
{{ formatMessage(messages.cancel) }}
</button>
</ButtonStyled>
<ButtonStyled v-if="editingId" color="brand">
<button :disabled="!canSubmit" @click="editApp">
<SaveIcon />
{{ formatMessage(messages.saveChanges) }}
</button>
</ButtonStyled>
<ButtonStyled v-else color="brand">
<button :disabled="!canSubmit" @click="createApp">
<PlusIcon />
{{ formatMessage(messages.createApp) }}
</button>
</ButtonStyled>
</div>
</div>
</Modal>
@@ -147,22 +144,22 @@
<div class="header__title">
<h2 class="text-2xl">{{ formatMessage(commonSettingsMessages.applications) }}</h2>
</div>
<button
class="btn btn-primary"
@click="
() => {
name = null
icon = null
scopesVal = 0
redirectUris = ['']
editingId = null
expires = null
$refs.appModal.show()
}
"
>
<PlusIcon /> {{ formatMessage(messages.newApplication) }}
</button>
<ButtonStyled color="brand">
<button
@click="
() => {
name = null
icon = null
scopesVal = 0
redirectUris = ['']
editingId = null
$refs.appModal.show()
}
"
>
<PlusIcon /> {{ formatMessage(messages.newApplication) }}
</button>
</ButtonStyled>
</div>
<p>
<IntlFormatted :message-id="messages.descriptionIntro">
@@ -210,34 +207,35 @@
</div>
</div>
<div class="input-group">
<Button
icon-only
@click="
() => {
setForm({
...app,
redirect_uris: app.redirect_uris.map((u) => u.uri) || [],
})
$refs.appModal.show()
}
"
>
<EditIcon />
{{ formatMessage(messages.edit) }}
</Button>
<Button
color="danger"
icon-only
@click="
() => {
editingId = app.id
$refs.modal_confirm.show()
}
"
>
<TrashIcon />
{{ formatMessage(messages.delete) }}
</Button>
<ButtonStyled>
<button
@click="
() => {
setForm({
...app,
redirect_uris: app.redirect_uris.map((u) => u.uri) || [],
})
$refs.appModal.show()
}
"
>
<EditIcon />
{{ formatMessage(messages.edit) }}
</button>
</ButtonStyled>
<ButtonStyled color="red">
<button
@click="
() => {
editingId = app.id
$refs.modal_confirm.show()
}
"
>
<TrashIcon />
{{ formatMessage(messages.delete) }}
</button>
</ButtonStyled>
</div>
</div>
</div>
@@ -246,7 +244,7 @@
import { EditIcon, PlusIcon, SaveIcon, TrashIcon, UploadIcon, XIcon } from '@modrinth/assets'
import {
Avatar,
Button,
ButtonStyled,
Checkbox,
commonMessages,
commonSettingsMessages,

View File

@@ -69,19 +69,19 @@
</div>
<div class="input-group">
<Button
color="danger"
icon-only
@click="
() => {
revokingId = authorization.app_id
$refs.modal_confirm.show()
}
"
>
<TrashIcon />
{{ formatMessage(messages.revokeAction) }}
</Button>
<ButtonStyled color="red">
<button
@click="
() => {
revokingId = authorization.app_id
$refs.modal_confirm.show()
}
"
>
<TrashIcon />
{{ formatMessage(messages.revokeAction) }}
</button>
</ButtonStyled>
</div>
</div>
</div>
@@ -90,7 +90,7 @@
import { CheckIcon, TrashIcon } from '@modrinth/assets'
import {
Avatar,
Button,
ButtonStyled,
commonMessages,
commonSettingsMessages,
ConfirmModal,

View File

@@ -1,7 +1,7 @@
<template>
<ServersUpgradeModalWrapper ref="upgradeModal" />
<ResubscribeModal ref="pyroResubscribeModal" @resubscribe="handlePyroResubscribeConfirm" />
<section class="universal-card experimental-styles-within">
<section class="universal-card">
<h2>{{ formatMessage(messages.subscriptionTitle) }}</h2>
<p>{{ formatMessage(messages.subscriptionDescription) }}</p>
<div class="universal-card recessed">
@@ -533,7 +533,7 @@
</div>
</section>
<section class="universal-card experimental-styles-within">
<section class="universal-card">
<ConfirmModal
ref="modal_confirm"
:title="formatMessage(deleteModalMessages.title)"
@@ -573,12 +573,16 @@
<div class="header__title">
<h2 class="text-2xl">{{ formatMessage(messages.paymentMethodTitle) }}</h2>
</div>
<nuxt-link class="btn" to="/settings/billing/charges">
<HistoryIcon /> {{ formatMessage(messages.paymentMethodHistory) }}
</nuxt-link>
<button class="btn" @click="addPaymentMethod">
<PlusIcon /> {{ formatMessage(messages.paymentMethodAdd) }}
</button>
<ButtonStyled>
<nuxt-link to="/settings/billing/charges">
<HistoryIcon /> {{ formatMessage(messages.paymentMethodHistory) }}
</nuxt-link>
</ButtonStyled>
<ButtonStyled>
<button @click="addPaymentMethod">
<PlusIcon /> {{ formatMessage(messages.paymentMethodAdd) }}
</button>
</ButtonStyled>
</div>
<div
v-if="!paymentMethods || paymentMethods.length === 0"
@@ -637,41 +641,43 @@
</div>
</div>
</div>
<OverflowMenu
:dropdown-id="`${baseId}-payment-method-overflow-${index}`"
class="btn icon-only transparent"
:options="
[
{
id: 'primary',
action: () => editPaymentMethod(index, true),
},
{
id: 'remove',
action: () => {
removePaymentMethodIndex = index
$refs.modal_confirm.show()
<ButtonStyled circular type="transparent">
<OverflowMenu
:dropdown-id="`${baseId}-payment-method-overflow-${index}`"
class="btn-dropdown-animation !w-10"
:options="
[
{
id: 'primary',
action: () => editPaymentMethod(index, true),
},
color: 'red',
hoverOnly: true,
},
].slice(primaryPaymentMethodId === method.id ? 1 : 0, 2)
"
>
<MoreVerticalIcon />
<template #primary>
<StarIcon />
{{ formatMessage(messages.paymentMethodMakePrimary) }}
</template>
<template #edit>
<EditIcon />
{{ formatMessage(commonMessages.editButton) }}
</template>
<template #remove>
<TrashIcon />
{{ formatMessage(commonMessages.deleteLabel) }}
</template>
</OverflowMenu>
{
id: 'remove',
action: () => {
removePaymentMethodIndex = index
$refs.modal_confirm.show()
},
color: 'red',
hoverOnly: true,
},
].slice(primaryPaymentMethodId === method.id ? 1 : 0, 2)
"
>
<MoreVerticalIcon />
<template #primary>
<StarIcon />
{{ formatMessage(messages.paymentMethodMakePrimary) }}
</template>
<template #edit>
<EditIcon />
{{ formatMessage(commonMessages.editButton) }}
</template>
<template #remove>
<TrashIcon />
{{ formatMessage(commonMessages.deleteLabel) }}
</template>
</OverflowMenu>
</ButtonStyled>
</div>
</div>
</section>

View File

@@ -9,9 +9,11 @@
</strong>
</template>
</IntlFormatted>
<Button :action="() => disableDeveloperMode()">
{{ formatMessage(developerModeBanner.deactivate) }}
</Button>
<ButtonStyled color="red" type="highlight">
<button class="mt-3" @click="disableDeveloperMode()">
{{ formatMessage(developerModeBanner.deactivate) }}
</button>
</ButtonStyled>
</MessageBanner>
<section class="universal-card">
<h2 class="text-2xl">{{ formatMessage(colorTheme.title) }}</h2>
@@ -178,7 +180,7 @@
<script setup lang="ts">
import { CodeIcon, RadioButtonCheckedIcon, RadioButtonIcon } from '@modrinth/assets'
import {
Button,
ButtonStyled,
defineMessages,
injectNotificationManager,
IntlFormatted,
@@ -506,9 +508,5 @@ const listTypes = computed(() => {
margin-bottom: 2px;
margin-right: 0.5rem;
}
.btn {
margin-top: var(--gap-sm);
}
}
</style>

View File

@@ -66,31 +66,25 @@
<p></p>
</div>
<div class="input-group push-right">
<button class="iconified-button" @click="$refs.patModal.hide()">
<XIcon />
{{ formatMessage(commonMessages.cancelButton) }}
</button>
<button
v-if="editPatId !== null"
:disabled="loading || !name || !expires"
type="button"
class="iconified-button brand-button"
@click="editPat"
>
<SaveIcon />
{{ formatMessage(commonMessages.saveChangesButton) }}
</button>
<button
v-else
:disabled="loading || !name || !expires"
type="button"
class="iconified-button brand-button"
@click="createPat"
>
<PlusIcon />
{{ formatMessage(createModalMessages.action) }}
</button>
<div class="ml-auto flex gap-2">
<ButtonStyled type="outlined">
<button @click="$refs.patModal.hide()">
<XIcon />
{{ formatMessage(commonMessages.cancelButton) }}
</button>
</ButtonStyled>
<ButtonStyled v-if="editPatId !== null" color="brand">
<button :disabled="loading || !name || !expires" @click="editPat">
<SaveIcon />
{{ formatMessage(commonMessages.saveChangesButton) }}
</button>
</ButtonStyled>
<ButtonStyled v-else color="brand">
<button :disabled="loading || !name || !expires" @click="createPat">
<PlusIcon />
{{ formatMessage(createModalMessages.action) }}
</button>
</ButtonStyled>
</div>
</div>
</NewModal>
@@ -99,20 +93,21 @@
<div class="header__title">
<h2 class="text-2xl">{{ formatMessage(commonSettingsMessages.pats) }}</h2>
</div>
<button
class="btn btn-primary"
@click="
() => {
name = null
scopesVal = 0
expires = null
editPatId = null
$refs.patModal.show()
}
"
>
<PlusIcon /> {{ formatMessage(messages.create) }}
</button>
<ButtonStyled color="brand">
<button
@click="
() => {
name = null
scopesVal = 0
expires = null
editPatId = null
$refs.patModal.show()
}
"
>
<PlusIcon /> {{ formatMessage(messages.create) }}
</button>
</ButtonStyled>
</div>
<p>
<IntlFormatted :message-id="messages.description">
@@ -172,31 +167,33 @@
</div>
</div>
<div class="token-actions ml-auto flex flex-col gap-2">
<button
class="iconified-button raised-button"
@click="
() => {
editPatId = pat.id
name = pat.name
scopesVal = pat.scopes
expires = $dayjs(pat.expires).format('YYYY-MM-DD')
$refs.patModal.show()
}
"
>
<EditIcon /> {{ formatMessage(tokenMessages.edit) }}
</button>
<button
class="iconified-button raised-button"
@click="
() => {
deletePatIndex = pat.id
$refs.modal_confirm.show()
}
"
>
<TrashIcon /> {{ formatMessage(tokenMessages.revoke) }}
</button>
<ButtonStyled>
<button
@click="
() => {
editPatId = pat.id
name = pat.name
scopesVal = pat.scopes
expires = $dayjs(pat.expires).format('YYYY-MM-DD')
$refs.patModal.show()
}
"
>
<EditIcon /> {{ formatMessage(tokenMessages.edit) }}
</button>
</ButtonStyled>
<ButtonStyled>
<button
@click="
() => {
deletePatIndex = pat.id
$refs.modal_confirm.show()
}
"
>
<TrashIcon /> {{ formatMessage(tokenMessages.revoke) }}
</button>
</ButtonStyled>
</div>
</div>
</div>
@@ -204,6 +201,7 @@
<script setup>
import { EditIcon, PlusIcon, SaveIcon, TrashIcon, XIcon } from '@modrinth/assets'
import {
ButtonStyled,
Checkbox,
commonMessages,
commonSettingsMessages,

View File

@@ -21,33 +21,38 @@
circle
:alt="auth.user.username"
/>
<div class="input-stack">
<FileInput
:max-size="262144"
:show-icon="true"
class="btn"
:prompt="formatMessage(commonMessages.uploadImageButton)"
accept="image/png,image/jpeg,image/gif,image/webp"
@change="showPreviewImage"
>
<UploadIcon />
</FileInput>
<Button v-if="avatarUrl !== null" :action="removePreviewImage">
<TrashIcon />
{{ formatMessage(commonMessages.removeImageButton) }}
</Button>
<Button
v-if="previewImage"
:action="
() => {
icon = null
previewImage = null
}
"
>
<UndoIcon />
{{ formatMessage(commonMessages.resetButton) }}
</Button>
<div class="flex flex-col gap-2">
<ButtonStyled>
<FileInput
:max-size="262144"
:show-icon="true"
class="button-like"
:prompt="formatMessage(commonMessages.uploadImageButton)"
accept="image/png,image/jpeg,image/gif,image/webp"
@change="showPreviewImage"
>
<UploadIcon />
</FileInput>
</ButtonStyled>
<ButtonStyled v-if="avatarUrl !== null">
<button @click="removePreviewImage">
<TrashIcon />
{{ formatMessage(commonMessages.removeImageButton) }}
</button>
</ButtonStyled>
<ButtonStyled v-if="previewImage">
<button
@click="
() => {
icon = null
previewImage = null
}
"
>
<UndoIcon />
{{ formatMessage(commonMessages.resetButton) }}
</button>
</ButtonStyled>
</div>
</div>
<label for="username-field">
@@ -65,9 +70,11 @@
</label>
<StyledInput id="bio-field" v-model="current.bio" multiline />
<div class="input-group mt-4">
<Button :link="`/user/${auth.user.username}`">
<UserIcon /> {{ formatMessage(commonMessages.visitYourProfile) }}
</Button>
<ButtonStyled>
<NuxtLink :to="`/user/${auth.user.username}`">
<UserIcon /> {{ formatMessage(commonMessages.visitYourProfile) }}
</NuxtLink>
</ButtonStyled>
</div>
</section>
<UnsavedChangesPopup
@@ -84,7 +91,7 @@
import { TrashIcon, UndoIcon, UploadIcon, UserIcon } from '@modrinth/assets'
import {
Avatar,
Button,
ButtonStyled,
commonMessages,
defineMessages,
FileInput,

View File

@@ -34,9 +34,11 @@
</div>
<div class="input-group">
<i v-if="session.current">{{ formatMessage(messages.currentSessionLabel) }}</i>
<button v-else class="iconified-button raised-button" @click="revokeSession(session.id)">
<XIcon /> {{ formatMessage(messages.revokeSessionButton) }}
</button>
<ButtonStyled v-else>
<button @click="revokeSession(session.id)">
<XIcon /> {{ formatMessage(messages.revokeSessionButton) }}
</button>
</ButtonStyled>
</div>
</div>
</div>
@@ -44,6 +46,7 @@
<script setup>
import { XIcon } from '@modrinth/assets'
import {
ButtonStyled,
commonMessages,
commonSettingsMessages,
defineMessages,

View File

@@ -1,5 +1,5 @@
<template>
<div v-if="user" class="experimental-styles-within">
<div v-if="user">
<ModalCreation ref="modal_creation" />
<CollectionCreateModal ref="modal_collection_creation" />
<NewModal ref="editRoleModal" header="Edit role">