mirror of
https://github.com/modrinth/code.git
synced 2026-08-24 16:44:51 +00:00
fix: remove legacy button classes (#7011)
* fix: remove legacy button-group class for tailwind (conflicts) * fix: delete legacy button classes from scss * fix: fmt
This commit is contained in:
@@ -93,7 +93,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:where(button:not([data-button]), .button, .iconified-button) {
|
:where(button:not([data-button])) {
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +118,6 @@
|
|||||||
gap: var(--spacing-card-sm);
|
gap: var(--spacing-card-sm);
|
||||||
margin-bottom: calc(var(--spacing-card-sm) + var(--spacing-card-md));
|
margin-bottom: calc(var(--spacing-card-sm) + var(--spacing-card-md));
|
||||||
|
|
||||||
.iconified-button,
|
|
||||||
.input-group {
|
.input-group {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
@@ -250,185 +249,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-base {
|
|
||||||
@extend .button-animation;
|
|
||||||
font-weight: 500;
|
|
||||||
outline: 2px solid transparent;
|
|
||||||
|
|
||||||
&:focus-visible:not(&:disabled),
|
|
||||||
&:hover:not(&:disabled) {
|
|
||||||
cursor: pointer;
|
|
||||||
filter: brightness(0.85);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active:not(&:disabled) {
|
|
||||||
filter: brightness(0.8);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:disabled,
|
|
||||||
&[disabled='true'] {
|
|
||||||
cursor: not-allowed;
|
|
||||||
filter: grayscale(50%);
|
|
||||||
opacity: 0.5;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:not(tr).button-transparent {
|
|
||||||
@extend .button-base;
|
|
||||||
background-color: transparent;
|
|
||||||
border-radius: var(--size-rounded-sm);
|
|
||||||
|
|
||||||
&:focus-visible:not(&:disabled),
|
|
||||||
&:hover:not(&:disabled),
|
|
||||||
&:active:not(&:disabled) {
|
|
||||||
background-color: var(--color-raised-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.brand-button {
|
|
||||||
color: var(--color-brand);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.danger-button {
|
|
||||||
color: var(--color-red);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tr.button-transparent {
|
|
||||||
@extend .button-animation;
|
|
||||||
background-color: transparent;
|
|
||||||
border-radius: var(--size-rounded-sm);
|
|
||||||
|
|
||||||
&:focus-visible:not(&:disabled) > *,
|
|
||||||
&:hover:not(&:disabled) > * {
|
|
||||||
cursor: pointer;
|
|
||||||
filter: brightness(0.85);
|
|
||||||
background-color: var(--color-raised-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active:not(&:disabled) > * {
|
|
||||||
filter: brightness(0.8);
|
|
||||||
background-color: var(--color-raised-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:disabled > *,
|
|
||||||
&[disabled='true'] > * {
|
|
||||||
cursor: not-allowed;
|
|
||||||
filter: grayscale(50%);
|
|
||||||
opacity: 0.5;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-color-base {
|
|
||||||
box-sizing: border-box;
|
|
||||||
--text-color: var(--color-button-text);
|
|
||||||
--background-color: var(--color-button-bg);
|
|
||||||
|
|
||||||
color: var(--text-color);
|
|
||||||
background-color: var(--background-color);
|
|
||||||
box-shadow:
|
|
||||||
var(--shadow-inset-sm),
|
|
||||||
0 0 0 0 transparent;
|
|
||||||
border-radius: var(--size-rounded-sm);
|
|
||||||
}
|
|
||||||
|
|
||||||
.iconified-button {
|
|
||||||
@extend .button-base;
|
|
||||||
@extend .button-color-base;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
padding: var(--spacing-card-sm) var(--spacing-card-bg);
|
|
||||||
margin: 0;
|
|
||||||
font-size: var(--font-size-nm);
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
width: fit-content;
|
|
||||||
height: fit-content;
|
|
||||||
transition:
|
|
||||||
opacity 0.5s ease-in-out,
|
|
||||||
filter 0.2s ease-in-out,
|
|
||||||
scale 0.05s ease-in-out,
|
|
||||||
outline 0.2s ease-in-out;
|
|
||||||
|
|
||||||
text-decoration: none;
|
|
||||||
|
|
||||||
svg {
|
|
||||||
width: 1.1rem;
|
|
||||||
height: 1.1rem;
|
|
||||||
margin-right: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.icon-only {
|
|
||||||
padding: 0 0.5rem;
|
|
||||||
|
|
||||||
svg {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.transparent {
|
|
||||||
background: none;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.square-button {
|
|
||||||
@extend .button-base;
|
|
||||||
|
|
||||||
--text-color: var(--color-button-text);
|
|
||||||
--background-color: var(--color-button-bg);
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
height: 2.25rem;
|
|
||||||
width: 2.25rem;
|
|
||||||
border-radius: var(--size-rounded-sm);
|
|
||||||
color: var(--text-color);
|
|
||||||
background-color: var(--background-color);
|
|
||||||
box-shadow:
|
|
||||||
var(--shadow-inset-sm),
|
|
||||||
0 0 0 0 transparent;
|
|
||||||
|
|
||||||
svg {
|
|
||||||
min-width: 1.25rem;
|
|
||||||
max-width: 1.25rem;
|
|
||||||
min-height: 1.25rem;
|
|
||||||
max-height: 1.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.raised-button {
|
|
||||||
--background-color: var(--color-raised-bg);
|
|
||||||
box-shadow: var(--shadow-inset-sm), var(--shadow-raised);
|
|
||||||
}
|
|
||||||
|
|
||||||
.danger-button {
|
|
||||||
--background-color: var(--color-red);
|
|
||||||
--text-color: var(--color-brand-inverted);
|
|
||||||
}
|
|
||||||
|
|
||||||
.moderation-button {
|
|
||||||
--background-color: var(--color-orange);
|
|
||||||
--text-color: var(--color-brand-inverted);
|
|
||||||
}
|
|
||||||
|
|
||||||
.brand-button {
|
|
||||||
--background-color: var(--color-brand);
|
|
||||||
--text-color: var(--color-brand-inverted);
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-group {
|
|
||||||
display: flex;
|
|
||||||
grid-gap: var(--spacing-card-sm);
|
|
||||||
flex-wrap: wrap;
|
|
||||||
margin-top: var(--spacing-card-md);
|
|
||||||
justify-content: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error {
|
.error {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -501,7 +321,6 @@ textarea.known-error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-reduced-motion) {
|
@media (prefers-reduced-motion) {
|
||||||
.button-animation,
|
|
||||||
button {
|
button {
|
||||||
transform: none !important;
|
transform: none !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -475,18 +475,6 @@ input {
|
|||||||
outline: 2px solid transparent !important;
|
outline: 2px solid transparent !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-animation {
|
|
||||||
transition:
|
|
||||||
opacity 0.5s ease-in-out,
|
|
||||||
filter 0.2s ease-in-out,
|
|
||||||
transform 0.05s ease-in-out,
|
|
||||||
outline-width 0.2s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-transparent {
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.qc-cmp2-close-tooltip {
|
.qc-cmp2-close-tooltip {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
color: hsl(145, 78%, 28%);
|
color: hsl(145, 78%, 28%);
|
||||||
|
|||||||
@@ -1,103 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="chips">
|
|
||||||
<button
|
|
||||||
v-for="item in items"
|
|
||||||
:key="item"
|
|
||||||
class="iconified-button"
|
|
||||||
:class="{ selected: selected === item, capitalize: capitalize }"
|
|
||||||
@click="toggleItem(item)"
|
|
||||||
>
|
|
||||||
<CheckIcon v-if="selected === item" />
|
|
||||||
<span>{{ formatLabel(item) }}</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { CheckIcon } from '@modrinth/assets'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
CheckIcon,
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
modelValue: {
|
|
||||||
required: true,
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
items: {
|
|
||||||
required: true,
|
|
||||||
type: Array,
|
|
||||||
},
|
|
||||||
neverEmpty: {
|
|
||||||
default: true,
|
|
||||||
type: Boolean,
|
|
||||||
},
|
|
||||||
formatLabel: {
|
|
||||||
default: (x) => x,
|
|
||||||
type: Function,
|
|
||||||
},
|
|
||||||
capitalize: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
emits: ['update:modelValue'],
|
|
||||||
computed: {
|
|
||||||
selected: {
|
|
||||||
get() {
|
|
||||||
return this.modelValue
|
|
||||||
},
|
|
||||||
set(value) {
|
|
||||||
this.$emit('update:modelValue', value)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
if (this.items.length > 0 && this.neverEmpty) {
|
|
||||||
this.selected = this.items[0]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
toggleItem(item) {
|
|
||||||
if (this.selected === item && !this.neverEmpty) {
|
|
||||||
this.selected = null
|
|
||||||
} else {
|
|
||||||
this.selected = item
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.chips {
|
|
||||||
display: flex;
|
|
||||||
grid-gap: 0.5rem;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
|
|
||||||
.iconified-button {
|
|
||||||
&.capitalize {
|
|
||||||
text-transform: capitalize;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg {
|
|
||||||
width: 1em;
|
|
||||||
height: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:focus-visible {
|
|
||||||
outline: 0.25rem solid var(--color-focus-ring);
|
|
||||||
border-radius: 0.25rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.selected {
|
|
||||||
color: var(--color-button-text-active);
|
|
||||||
background-color: var(--color-brand-highlight);
|
|
||||||
box-shadow:
|
|
||||||
inset 0 0 0 transparent,
|
|
||||||
0 0 0 2px var(--color-brand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
ToggleRightIcon,
|
ToggleRightIcon,
|
||||||
TwitterIcon,
|
TwitterIcon,
|
||||||
} from '@modrinth/assets'
|
} from '@modrinth/assets'
|
||||||
import { ButtonLink } from '@modrinth/ui'
|
import { Button, ButtonLink } from '@modrinth/ui'
|
||||||
import {
|
import {
|
||||||
AutoLink,
|
AutoLink,
|
||||||
defineMessage,
|
defineMessage,
|
||||||
@@ -266,11 +266,17 @@ function developerModeIncrement() {
|
|||||||
:aria-label="formatMessage(messages.modrinthInformation)"
|
:aria-label="formatMessage(messages.modrinthInformation)"
|
||||||
>
|
>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<TextLogo
|
<Button
|
||||||
aria-hidden="true"
|
type="quiet"
|
||||||
class="text-logo button-base h-6 w-auto text-contrast lg:h-8"
|
interaction="none"
|
||||||
|
aria-label="Modrinth"
|
||||||
|
class="!h-auto !p-0"
|
||||||
@click="developerModeIncrement()"
|
@click="developerModeIncrement()"
|
||||||
/>
|
>
|
||||||
|
<span class="inline-flex">
|
||||||
|
<TextLogo aria-hidden="true" class="text-logo h-6 w-auto text-contrast lg:h-8" />
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
<ButtonLink
|
<ButtonLink
|
||||||
v-if="flags.developerMode"
|
v-if="flags.developerMode"
|
||||||
v-tooltip="formatMessage(commonSettingsMessages.featureFlags)"
|
v-tooltip="formatMessage(commonSettingsMessages.featureFlags)"
|
||||||
|
|||||||
@@ -17,10 +17,12 @@
|
|||||||
{{ item.label }}
|
{{ item.label }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<NuxtLink
|
<ButtonLink
|
||||||
v-else-if="item.link ?? item.to"
|
v-else-if="item.link ?? item.to"
|
||||||
|
type="quiet"
|
||||||
|
interaction="surface"
|
||||||
:to="(item.link ?? item.to) as string"
|
:to="(item.link ?? item.to) as string"
|
||||||
class="nav-item inline-flex w-full cursor-pointer items-center gap-2 rounded-xl border-none bg-transparent px-4 py-2.5 text-left text-base font-semibold leading-tight text-button-text transition-all hover:bg-button-bg hover:text-contrast active:scale-[0.97]"
|
class="nav-item !h-auto !w-full !justify-start !rounded-xl !px-4 !py-2.5 text-left leading-tight"
|
||||||
:class="{ 'is-active': isActive(item as NavStackLinkItem) }"
|
:class="{ 'is-active': isActive(item as NavStackLinkItem) }"
|
||||||
>
|
>
|
||||||
<component
|
<component
|
||||||
@@ -37,12 +39,14 @@
|
|||||||
{{ String(item.badge) }}
|
{{ String(item.badge) }}
|
||||||
</span>
|
</span>
|
||||||
<span v-if="item.chevron" class="ml-auto"><ChevronRightIcon /></span>
|
<span v-if="item.chevron" class="ml-auto"><ChevronRightIcon /></span>
|
||||||
</NuxtLink>
|
</ButtonLink>
|
||||||
|
|
||||||
<button
|
<Button
|
||||||
v-else-if="item.action"
|
v-else-if="item.action"
|
||||||
class="nav-item inline-flex w-full cursor-pointer items-center gap-2 text-nowrap rounded-xl border-none bg-transparent px-4 py-2.5 text-left text-base font-semibold leading-tight text-button-text transition-all hover:bg-button-bg hover:text-contrast active:scale-[0.97]"
|
type="quiet"
|
||||||
:class="{ 'danger-button': item.danger }"
|
interaction="surface"
|
||||||
|
:color="item.danger ? 'red' : undefined"
|
||||||
|
class="nav-item !h-auto !w-full !justify-start !rounded-xl !px-4 !py-2.5 text-left leading-tight"
|
||||||
@click="item.action"
|
@click="item.action"
|
||||||
>
|
>
|
||||||
<component
|
<component
|
||||||
@@ -58,7 +62,7 @@
|
|||||||
>
|
>
|
||||||
{{ String(item.badge) }}
|
{{ String(item.badge) }}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</Button>
|
||||||
|
|
||||||
<span v-else>You frog. 🐸</span>
|
<span v-else>You frog. 🐸</span>
|
||||||
</li>
|
</li>
|
||||||
@@ -69,6 +73,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ChevronRightIcon } from '@modrinth/assets'
|
import { ChevronRightIcon } from '@modrinth/assets'
|
||||||
|
import { Button, ButtonLink } from '@modrinth/ui'
|
||||||
import { type Component, computed, useSlots } from 'vue'
|
import { type Component, computed, useSlots } from 'vue'
|
||||||
|
|
||||||
type NavStackBaseItem = {
|
type NavStackBaseItem = {
|
||||||
|
|||||||
@@ -1,128 +0,0 @@
|
|||||||
<template>
|
|
||||||
<nav
|
|
||||||
ref="scrollContainer"
|
|
||||||
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"
|
|
||||||
:key="`option-group-${index}`"
|
|
||||||
ref="optionButtons"
|
|
||||||
class="button-animation z-[1] flex flex-row items-center gap-2 rounded-full bg-transparent px-4 py-2 font-semibold"
|
|
||||||
:class="{
|
|
||||||
'text-button-textSelected': modelValue === option,
|
|
||||||
'text-primary': modelValue !== option,
|
|
||||||
}"
|
|
||||||
@click="setOption(option)"
|
|
||||||
>
|
|
||||||
<slot :option="option" :selected="modelValue === option" />
|
|
||||||
</button>
|
|
||||||
<div
|
|
||||||
class="navtabs-transition pointer-events-none absolute h-[calc(100%-0.5rem)] overflow-hidden rounded-full bg-button-bgSelected p-1"
|
|
||||||
:style="{
|
|
||||||
left: sliderLeftPx,
|
|
||||||
top: sliderTopPx,
|
|
||||||
right: sliderRightPx,
|
|
||||||
bottom: sliderBottomPx,
|
|
||||||
opacity: initialized ? 1 : 0,
|
|
||||||
}"
|
|
||||||
aria-hidden="true"
|
|
||||||
></div>
|
|
||||||
</nav>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts" generic="T">
|
|
||||||
import { computed, onMounted, ref } from 'vue'
|
|
||||||
|
|
||||||
const modelValue = defineModel<T>({ required: true })
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
options: T[]
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const scrollContainer = ref<HTMLElement | null>(null)
|
|
||||||
|
|
||||||
const sliderLeft = ref(4)
|
|
||||||
const sliderTop = ref(4)
|
|
||||||
const sliderRight = ref(4)
|
|
||||||
const sliderBottom = ref(4)
|
|
||||||
|
|
||||||
const sliderLeftPx = computed(() => `${sliderLeft.value}px`)
|
|
||||||
const sliderTopPx = computed(() => `${sliderTop.value}px`)
|
|
||||||
const sliderRightPx = computed(() => `${sliderRight.value}px`)
|
|
||||||
const sliderBottomPx = computed(() => `${sliderBottom.value}px`)
|
|
||||||
|
|
||||||
const optionButtons = ref()
|
|
||||||
|
|
||||||
const initialized = ref(false)
|
|
||||||
|
|
||||||
function setOption(option: T) {
|
|
||||||
modelValue.value = option
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(modelValue, () => {
|
|
||||||
startAnimation(props.options.indexOf(modelValue.value))
|
|
||||||
})
|
|
||||||
|
|
||||||
function startAnimation(index: number) {
|
|
||||||
const el = optionButtons.value[index]
|
|
||||||
|
|
||||||
if (!el || !el.offsetParent) return
|
|
||||||
|
|
||||||
const newValues = {
|
|
||||||
left: el.offsetLeft,
|
|
||||||
top: el.offsetTop,
|
|
||||||
right: el.offsetParent.offsetWidth - el.offsetLeft - el.offsetWidth,
|
|
||||||
bottom: el.offsetParent.offsetHeight - el.offsetTop - el.offsetHeight,
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sliderLeft.value === 4 && sliderRight.value === 4) {
|
|
||||||
sliderLeft.value = newValues.left
|
|
||||||
sliderRight.value = newValues.right
|
|
||||||
sliderTop.value = newValues.top
|
|
||||||
sliderBottom.value = newValues.bottom
|
|
||||||
} else {
|
|
||||||
const delay = 200
|
|
||||||
|
|
||||||
if (newValues.left < sliderLeft.value) {
|
|
||||||
sliderLeft.value = newValues.left
|
|
||||||
setTimeout(() => {
|
|
||||||
sliderRight.value = newValues.right
|
|
||||||
}, delay)
|
|
||||||
} else {
|
|
||||||
sliderRight.value = newValues.right
|
|
||||||
setTimeout(() => {
|
|
||||||
sliderLeft.value = newValues.left
|
|
||||||
}, delay)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newValues.top < sliderTop.value) {
|
|
||||||
sliderTop.value = newValues.top
|
|
||||||
setTimeout(() => {
|
|
||||||
sliderBottom.value = newValues.bottom
|
|
||||||
}, delay)
|
|
||||||
} else {
|
|
||||||
sliderBottom.value = newValues.bottom
|
|
||||||
setTimeout(() => {
|
|
||||||
sliderTop.value = newValues.top
|
|
||||||
}, delay)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
initialized.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
startAnimation(props.options.indexOf(modelValue.value))
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.navtabs-transition {
|
|
||||||
transition:
|
|
||||||
all 150ms cubic-bezier(0.4, 0, 0.2, 1),
|
|
||||||
opacity 250ms cubic-bezier(0.5, 0, 0.2, 1) 50ms;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-shadow {
|
|
||||||
box-shadow: var(--shadow-card);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<p v-else>{{ getFormattedMessage(messages.invitationNoRole) }}</p>
|
<p v-else>{{ getFormattedMessage(messages.invitationNoRole) }}</p>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<Button type="colored" color="brand" class="brand-button" @click="acceptInvite()">
|
<Button type="colored" color="brand" @click="acceptInvite()">
|
||||||
<CheckIcon />
|
<CheckIcon />
|
||||||
{{ getFormattedMessage(commonMessages.acceptButton) }}
|
{{ getFormattedMessage(commonMessages.acceptButton) }}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -275,7 +275,6 @@
|
|||||||
{{ formatMessage(messages.actionApprove) }}
|
{{ formatMessage(messages.actionApprove) }}
|
||||||
</Button>
|
</Button>
|
||||||
<SplitButton
|
<SplitButton
|
||||||
class="!m-0 !inline-flex !flex-nowrap !justify-start !gap-0"
|
|
||||||
type="colored"
|
type="colored"
|
||||||
color="red"
|
color="red"
|
||||||
:menu-label="formatMessage(commonMessages.moreOptionsButton)"
|
:menu-label="formatMessage(commonMessages.moreOptionsButton)"
|
||||||
|
|||||||
@@ -629,14 +629,14 @@
|
|||||||
@focusout="isBrowseMenuOpen = false"
|
@focusout="isBrowseMenuOpen = false"
|
||||||
>
|
>
|
||||||
<div class="links cascade-links">
|
<div class="links cascade-links">
|
||||||
<NuxtLink
|
<ButtonLink
|
||||||
v-for="navRoute in navRoutes"
|
v-for="navRoute in navRoutes"
|
||||||
:key="navRoute.href"
|
:key="navRoute.href"
|
||||||
:to="navRoute.href"
|
:to="navRoute.href"
|
||||||
class="iconified-button"
|
class="!h-auto !whitespace-normal"
|
||||||
>
|
>
|
||||||
{{ navRoute.label }}
|
{{ navRoute.label }}
|
||||||
</NuxtLink>
|
</ButtonLink>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@@ -646,10 +646,10 @@
|
|||||||
@focusout="isMobileMenuOpen = false"
|
@focusout="isMobileMenuOpen = false"
|
||||||
>
|
>
|
||||||
<div class="account-container">
|
<div class="account-container">
|
||||||
<NuxtLink
|
<ButtonLink
|
||||||
v-if="auth.user"
|
v-if="auth.user"
|
||||||
:to="`/user/${auth.user.username}`"
|
:to="`/user/${auth.user.username}`"
|
||||||
class="iconified-button account-button"
|
class="account-button !h-auto !whitespace-normal"
|
||||||
>
|
>
|
||||||
<Avatar
|
<Avatar
|
||||||
:src="auth.user.avatar_url"
|
:src="auth.user.avatar_url"
|
||||||
@@ -662,66 +662,85 @@
|
|||||||
<div>@{{ auth.user.username }}</div>
|
<div>@{{ auth.user.username }}</div>
|
||||||
<div>{{ formatMessage(commonMessages.visitYourProfile) }}</div>
|
<div>{{ formatMessage(commonMessages.visitYourProfile) }}</div>
|
||||||
</div>
|
</div>
|
||||||
</NuxtLink>
|
</ButtonLink>
|
||||||
<nuxt-link v-else class="iconified-button brand-button" :to="signInRouteObj">
|
<ButtonLink
|
||||||
|
v-else
|
||||||
|
type="colored"
|
||||||
|
color="brand"
|
||||||
|
:to="signInRouteObj"
|
||||||
|
class="!h-auto !whitespace-normal"
|
||||||
|
>
|
||||||
<LogInIcon aria-hidden="true" /> {{ formatMessage(commonMessages.signInButton) }}
|
<LogInIcon aria-hidden="true" /> {{ formatMessage(commonMessages.signInButton) }}
|
||||||
</nuxt-link>
|
</ButtonLink>
|
||||||
</div>
|
</div>
|
||||||
<div class="links">
|
<div class="links">
|
||||||
<template v-if="auth.user">
|
<template v-if="auth.user">
|
||||||
<button class="iconified-button danger-button" @click="logoutUser()">
|
<Button
|
||||||
|
type="colored"
|
||||||
|
color="red"
|
||||||
|
class="!h-auto !whitespace-normal"
|
||||||
|
@click="logoutUser()"
|
||||||
|
>
|
||||||
<LogOutIcon aria-hidden="true" />
|
<LogOutIcon aria-hidden="true" />
|
||||||
{{ formatMessage(commonMessages.signOutButton) }}
|
{{ formatMessage(commonMessages.signOutButton) }}
|
||||||
</button>
|
</Button>
|
||||||
<button class="iconified-button" @click="$refs.modal_creation.show()">
|
<Button class="!h-auto !whitespace-normal" @click="$refs.modal_creation.show()">
|
||||||
<PlusIcon aria-hidden="true" />
|
<PlusIcon aria-hidden="true" />
|
||||||
{{ formatMessage(commonMessages.createAProjectButton) }}
|
{{ formatMessage(commonMessages.createAProjectButton) }}
|
||||||
</button>
|
</Button>
|
||||||
<NuxtLink class="iconified-button" to="/dashboard/collections">
|
<ButtonLink class="!h-auto !whitespace-normal" to="/dashboard/collections">
|
||||||
<LibraryIcon class="icon" />
|
<LibraryIcon class="icon" />
|
||||||
{{ formatMessage(commonMessages.collectionsLabel) }}
|
{{ formatMessage(commonMessages.collectionsLabel) }}
|
||||||
</NuxtLink>
|
</ButtonLink>
|
||||||
<NuxtLink class="iconified-button" to="/hosting/manage">
|
<ButtonLink class="!h-auto !whitespace-normal" to="/hosting/manage">
|
||||||
<ServerIcon class="icon" />
|
<ServerIcon class="icon" />
|
||||||
{{ formatMessage(commonMessages.serversLabel) }}
|
{{ formatMessage(commonMessages.serversLabel) }}
|
||||||
</NuxtLink>
|
</ButtonLink>
|
||||||
<NuxtLink
|
<ButtonLink
|
||||||
v-if="auth.user.role === 'moderator' || auth.user.role === 'admin'"
|
v-if="auth.user.role === 'moderator' || auth.user.role === 'admin'"
|
||||||
class="iconified-button"
|
class="!h-auto !whitespace-normal"
|
||||||
to="/moderation"
|
to="/moderation"
|
||||||
>
|
>
|
||||||
<ScaleIcon aria-hidden="true" />
|
<ScaleIcon aria-hidden="true" />
|
||||||
{{ formatMessage(commonMessages.moderationLabel) }}
|
{{ formatMessage(commonMessages.moderationLabel) }}
|
||||||
</NuxtLink>
|
</ButtonLink>
|
||||||
<NuxtLink v-if="flags.developerMode" class="iconified-button" to="/settings/flags">
|
<ButtonLink
|
||||||
|
v-if="flags.developerMode"
|
||||||
|
class="!h-auto !whitespace-normal"
|
||||||
|
to="/settings/flags"
|
||||||
|
>
|
||||||
<ToggleRightIcon aria-hidden="true" />
|
<ToggleRightIcon aria-hidden="true" />
|
||||||
{{ formatMessage(commonSettingsMessages.featureFlags) }}
|
{{ formatMessage(commonSettingsMessages.featureFlags) }}
|
||||||
</NuxtLink>
|
</ButtonLink>
|
||||||
</template>
|
</template>
|
||||||
<NuxtLink class="iconified-button" to="/settings">
|
<ButtonLink class="!h-auto !whitespace-normal" to="/settings">
|
||||||
<SettingsIcon aria-hidden="true" />
|
<SettingsIcon aria-hidden="true" />
|
||||||
{{ formatMessage(commonMessages.settingsLabel) }}
|
{{ formatMessage(commonMessages.settingsLabel) }}
|
||||||
</NuxtLink>
|
</ButtonLink>
|
||||||
<button class="iconified-button" @click="changeTheme">
|
<Button class="!h-auto !whitespace-normal" @click="changeTheme">
|
||||||
<MoonIcon v-if="$theme.active === 'light'" class="icon" />
|
<MoonIcon v-if="$theme.active === 'light'" class="icon" />
|
||||||
<SunIcon v-else class="icon" />
|
<SunIcon v-else class="icon" />
|
||||||
<span class="dropdown-item__text">
|
<span class="dropdown-item__text">
|
||||||
{{ formatMessage(messages.changeTheme) }}
|
{{ formatMessage(messages.changeTheme) }}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mobile-navbar" :class="{ expanded: isBrowseMenuOpen || isMobileMenuOpen }">
|
<div class="mobile-navbar" :class="{ expanded: isBrowseMenuOpen || isMobileMenuOpen }">
|
||||||
<NuxtLink
|
<ButtonLink
|
||||||
to="/"
|
to="/"
|
||||||
class="tab button-animation"
|
type="quiet"
|
||||||
|
interaction="none"
|
||||||
|
class="tab !h-auto !rounded-none !px-0"
|
||||||
:title="formatMessage(navMenuMessages.home)"
|
:title="formatMessage(navMenuMessages.home)"
|
||||||
:aria-label="formatMessage(navMenuMessages.home)"
|
:aria-label="formatMessage(navMenuMessages.home)"
|
||||||
>
|
>
|
||||||
<HomeIcon aria-hidden="true" />
|
<HomeIcon aria-hidden="true" />
|
||||||
</NuxtLink>
|
</ButtonLink>
|
||||||
<button
|
<Button
|
||||||
class="tab button-animation"
|
type="quiet"
|
||||||
|
interaction="none"
|
||||||
|
class="tab !h-auto !rounded-none !px-0"
|
||||||
:class="{ 'router-link-exact-active': isBrowseMenuOpen }"
|
:class="{ 'router-link-exact-active': isBrowseMenuOpen }"
|
||||||
:title="formatMessage(navMenuMessages.search)"
|
:title="formatMessage(navMenuMessages.search)"
|
||||||
:aria-label="formatMessage(navMenuMessages.search)"
|
:aria-label="formatMessage(navMenuMessages.search)"
|
||||||
@@ -734,11 +753,13 @@
|
|||||||
<SearchIcon aria-hidden="true" class="smaller" />
|
<SearchIcon aria-hidden="true" class="smaller" />
|
||||||
{{ formatMessage(navMenuMessages.search) }}
|
{{ formatMessage(navMenuMessages.search) }}
|
||||||
</template>
|
</template>
|
||||||
</button>
|
</Button>
|
||||||
<template v-if="auth.user">
|
<template v-if="auth.user">
|
||||||
<NuxtLink
|
<ButtonLink
|
||||||
to="/dashboard/notifications"
|
to="/dashboard/notifications"
|
||||||
class="tab button-animation"
|
type="quiet"
|
||||||
|
interaction="none"
|
||||||
|
class="tab !h-auto !rounded-none !px-0"
|
||||||
:aria-label="formatMessage(commonMessages.notificationsLabel)"
|
:aria-label="formatMessage(commonMessages.notificationsLabel)"
|
||||||
:class="{
|
:class="{
|
||||||
'no-active': isMobileMenuOpen || isBrowseMenuOpen,
|
'no-active': isMobileMenuOpen || isBrowseMenuOpen,
|
||||||
@@ -752,18 +773,22 @@
|
|||||||
"
|
"
|
||||||
>
|
>
|
||||||
<BellIcon aria-hidden="true" />
|
<BellIcon aria-hidden="true" />
|
||||||
</NuxtLink>
|
</ButtonLink>
|
||||||
<NuxtLink
|
<ButtonLink
|
||||||
to="/dashboard"
|
to="/dashboard"
|
||||||
class="tab button-animation"
|
type="quiet"
|
||||||
|
interaction="none"
|
||||||
|
class="tab !h-auto !rounded-none !px-0"
|
||||||
:aria-label="formatMessage(commonMessages.dashboardLabel)"
|
:aria-label="formatMessage(commonMessages.dashboardLabel)"
|
||||||
:title="formatMessage(commonMessages.dashboardLabel)"
|
:title="formatMessage(commonMessages.dashboardLabel)"
|
||||||
>
|
>
|
||||||
<ChartIcon aria-hidden="true" />
|
<ChartIcon aria-hidden="true" />
|
||||||
</NuxtLink>
|
</ButtonLink>
|
||||||
</template>
|
</template>
|
||||||
<button
|
<Button
|
||||||
class="tab button-animation"
|
type="quiet"
|
||||||
|
interaction="none"
|
||||||
|
class="tab !h-auto !rounded-none !px-0"
|
||||||
:title="formatMessage(messages.toggleMenu)"
|
:title="formatMessage(messages.toggleMenu)"
|
||||||
:aria-label="
|
:aria-label="
|
||||||
isMobileMenuOpen ? formatMessage(messages.closeMenu) : formatMessage(messages.openMenu)
|
isMobileMenuOpen ? formatMessage(messages.closeMenu) : formatMessage(messages.openMenu)
|
||||||
@@ -784,7 +809,7 @@
|
|||||||
circle
|
circle
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<main class="min-h-[calc(100vh-4.5rem-310.59px)]">
|
<main class="min-h-[calc(100vh-4.5rem-310.59px)]">
|
||||||
@@ -842,6 +867,7 @@ import {
|
|||||||
} from '@modrinth/assets'
|
} from '@modrinth/assets'
|
||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
|
Button,
|
||||||
ButtonLink,
|
ButtonLink,
|
||||||
commonMessages,
|
commonMessages,
|
||||||
commonProjectTypeCategoryMessages,
|
commonProjectTypeCategoryMessages,
|
||||||
|
|||||||
@@ -368,8 +368,4 @@ function getVersionLoaders(version) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.brand-button {
|
|
||||||
color: var(--color-accent-contrast);
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -86,7 +86,7 @@
|
|||||||
<StarIcon fill="currentColor" aria-hidden="true" />
|
<StarIcon fill="currentColor" aria-hidden="true" />
|
||||||
Unset as banner
|
Unset as banner
|
||||||
</Button>
|
</Button>
|
||||||
<div class="button-group">
|
<div class="mt-3 flex flex-wrap justify-end gap-2">
|
||||||
<Button type="outlined" @click="modalEditItem?.hide()">
|
<Button type="outlined" @click="modalEditItem?.hide()">
|
||||||
<XIcon aria-hidden="true" />
|
<XIcon aria-hidden="true" />
|
||||||
Cancel
|
Cancel
|
||||||
|
|||||||
@@ -86,7 +86,7 @@
|
|||||||
<StarIcon fill="currentColor" aria-hidden="true" />
|
<StarIcon fill="currentColor" aria-hidden="true" />
|
||||||
Unset as banner
|
Unset as banner
|
||||||
</Button>
|
</Button>
|
||||||
<div class="button-group">
|
<div class="mt-3 flex flex-wrap justify-end gap-2">
|
||||||
<Button type="outlined" @click="modal_edit_item.hide()">
|
<Button type="outlined" @click="modal_edit_item.hide()">
|
||||||
<XIcon aria-hidden="true" />
|
<XIcon aria-hidden="true" />
|
||||||
Cancel
|
Cancel
|
||||||
|
|||||||
@@ -740,8 +740,4 @@ svg {
|
|||||||
color: var(--color-orange);
|
color: var(--color-orange);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-group {
|
|
||||||
justify-content: flex-start;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -104,7 +104,7 @@
|
|||||||
:disabled="!hasPermission"
|
:disabled="!hasPermission"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="button-group">
|
<div class="mt-3 flex flex-wrap justify-start gap-2">
|
||||||
<Button
|
<Button
|
||||||
type="colored"
|
type="colored"
|
||||||
color="brand"
|
color="brand"
|
||||||
@@ -272,7 +272,7 @@
|
|||||||
@update:model-value="updateDonationLinks"
|
@update:model-value="updateDonationLinks"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="button-group">
|
<div class="mt-3 flex flex-wrap justify-start gap-2">
|
||||||
<Button type="colored" color="brand" :disabled="!hasChanges" @click="saveChanges()">
|
<Button type="colored" color="brand" :disabled="!hasChanges" @click="saveChanges()">
|
||||||
<SaveIcon />
|
<SaveIcon />
|
||||||
Save changes
|
Save changes
|
||||||
@@ -560,8 +560,4 @@ function checkDifference(newLink, existingLink) {
|
|||||||
max-width: 26rem;
|
max-width: 26rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-group {
|
|
||||||
justify-content: flex-start;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -270,9 +270,11 @@
|
|||||||
</nuxt-link>
|
</nuxt-link>
|
||||||
. You may override them below.
|
. You may override them below.
|
||||||
</p>
|
</p>
|
||||||
<nuxt-link
|
<ButtonLink
|
||||||
|
type="quiet"
|
||||||
|
interaction="none"
|
||||||
:to="`/organization/${organization.slug}`"
|
:to="`/organization/${organization.slug}`"
|
||||||
class="universal-card button-base recessed org"
|
class="universal-card recessed org !h-auto !w-full !shrink !items-stretch !justify-start !whitespace-normal !p-[var(--spacing-card-bg)]"
|
||||||
>
|
>
|
||||||
<Avatar :src="organization.icon_url" :alt="organization.name" size="md" />
|
<Avatar :src="organization.icon_url" :alt="organization.name" size="md" />
|
||||||
<div class="details">
|
<div class="details">
|
||||||
@@ -294,7 +296,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</nuxt-link>
|
</ButtonLink>
|
||||||
</div>
|
</div>
|
||||||
<p v-else>
|
<p v-else>
|
||||||
This project is not managed by an organization. If you are the member of any organizations,
|
This project is not managed by an organization. If you are the member of any organizations,
|
||||||
@@ -562,6 +564,7 @@ import {
|
|||||||
Avatar,
|
Avatar,
|
||||||
Badge,
|
Badge,
|
||||||
Button,
|
Button,
|
||||||
|
ButtonLink,
|
||||||
Card,
|
Card,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
Combobox,
|
Combobox,
|
||||||
|
|||||||
@@ -434,11 +434,6 @@ const toggleFeaturedCategory = (category: Category) => {
|
|||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-group {
|
|
||||||
justify-content: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-list {
|
.category-list {
|
||||||
column-count: 4;
|
column-count: 4;
|
||||||
column-gap: var(--spacing-card-lg);
|
column-gap: var(--spacing-card-lg);
|
||||||
|
|||||||
@@ -166,7 +166,7 @@
|
|||||||
v-if="
|
v-if="
|
||||||
subscription.metadata?.type === 'pyro' || subscription.metadata?.type === 'medal'
|
subscription.metadata?.type === 'pyro' || subscription.metadata?.type === 'medal'
|
||||||
"
|
"
|
||||||
:to="`/hosting/manage/${subscription.metadata.id}`"
|
:href="`/hosting/manage/${subscription.metadata.id}`"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
class="w-fit"
|
class="w-fit"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
Avatar,
|
Avatar,
|
||||||
Badge,
|
Badge,
|
||||||
Button,
|
Button,
|
||||||
|
ButtonLink,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
commonMessages,
|
commonMessages,
|
||||||
defineMessages,
|
defineMessages,
|
||||||
@@ -546,7 +547,7 @@ useSeoMeta({
|
|||||||
<h2 class="main-subheader">
|
<h2 class="main-subheader">
|
||||||
{{ formatMessage(messages.description) }}
|
{{ formatMessage(messages.description) }}
|
||||||
</h2>
|
</h2>
|
||||||
<div class="button-group">
|
<div class="mx-auto flex w-fit flex-wrap justify-center gap-2 [mask-image:none]">
|
||||||
<Button v-if="os" type="colored" color="brand" size="xl" @click="handleDownload">
|
<Button v-if="os" type="colored" color="brand" size="xl" @click="handleDownload">
|
||||||
<LinuxIcon v-if="os === 'Linux'" />
|
<LinuxIcon v-if="os === 'Linux'" />
|
||||||
<WindowsIcon v-else-if="os === 'Windows'" />
|
<WindowsIcon v-else-if="os === 'Windows'" />
|
||||||
@@ -745,10 +746,12 @@ useSeoMeta({
|
|||||||
<div class="projects-showcase">
|
<div class="projects-showcase">
|
||||||
<div v-for="(row, index) in rows" :key="index" class="row">
|
<div v-for="(row, index) in rows" :key="index" class="row">
|
||||||
<div v-for="n in 2" :key="n" class="row__content" :class="{ offset: index % 2 }">
|
<div v-for="n in 2" :key="n" class="row__content" :class="{ offset: index % 2 }">
|
||||||
<nuxt-link
|
<ButtonLink
|
||||||
v-for="project in row"
|
v-for="project in row"
|
||||||
:key="project.id"
|
:key="project.id"
|
||||||
class="project button-animation gradient-border"
|
type="quiet"
|
||||||
|
interaction="none"
|
||||||
|
class="project gradient-border !h-auto !shrink !items-stretch !justify-start !whitespace-normal !p-4 !font-normal !leading-normal"
|
||||||
:to="`/${project.project_type}/${project.slug ? project.slug : project.id}`"
|
:to="`/${project.project_type}/${project.slug ? project.slug : project.id}`"
|
||||||
>
|
>
|
||||||
<Avatar :src="project.icon_url!" alt="" size="sm" />
|
<Avatar :src="project.icon_url!" alt="" size="sm" />
|
||||||
@@ -760,7 +763,7 @@ useSeoMeta({
|
|||||||
{{ project.description }}
|
{{ project.description }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</nuxt-link>
|
</ButtonLink>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1228,13 +1231,6 @@ useSeoMeta({
|
|||||||
mask-image: none;
|
mask-image: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-group {
|
|
||||||
width: fit-content;
|
|
||||||
margin: 0 auto;
|
|
||||||
justify-content: center;
|
|
||||||
mask-image: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 65rem;
|
max-width: 65rem;
|
||||||
|
|||||||
@@ -13,11 +13,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<template v-if="orgs?.length > 0">
|
<template v-if="orgs?.length > 0">
|
||||||
<div class="orgs-grid">
|
<div class="orgs-grid">
|
||||||
<nuxt-link
|
<ButtonLink
|
||||||
v-for="org in sortedOrgs"
|
v-for="org in sortedOrgs"
|
||||||
:key="org.id"
|
:key="org.id"
|
||||||
|
type="quiet"
|
||||||
|
interaction="none"
|
||||||
:to="`/organization/${org.slug}`"
|
:to="`/organization/${org.slug}`"
|
||||||
class="universal-card button-base recessed org"
|
class="universal-card recessed org !h-auto !w-full !shrink !items-stretch !justify-start !whitespace-normal !p-[var(--spacing-card-bg)]"
|
||||||
:class="{ 'is-disabled': onlyAcceptedMembers(org.members).length === 0 }"
|
:class="{ 'is-disabled': onlyAcceptedMembers(org.members).length === 0 }"
|
||||||
>
|
>
|
||||||
<Avatar :src="org.icon_url" :alt="org.name" class="icon" />
|
<Avatar :src="org.icon_url" :alt="org.name" class="icon" />
|
||||||
@@ -41,7 +43,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</nuxt-link>
|
</ButtonLink>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-else> {{ formatMessage(messages.makeOrganization) }} </template>
|
<template v-else> {{ formatMessage(messages.makeOrganization) }} </template>
|
||||||
@@ -51,7 +53,14 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { PlusIcon, UsersIcon } from '@modrinth/assets'
|
import { PlusIcon, UsersIcon } from '@modrinth/assets'
|
||||||
import { Avatar, Button, defineMessages, injectModrinthClient, useVIntl } from '@modrinth/ui'
|
import {
|
||||||
|
Avatar,
|
||||||
|
Button,
|
||||||
|
ButtonLink,
|
||||||
|
defineMessages,
|
||||||
|
injectModrinthClient,
|
||||||
|
useVIntl,
|
||||||
|
} from '@modrinth/ui'
|
||||||
import { useQuery } from '@tanstack/vue-query'
|
import { useQuery } from '@tanstack/vue-query'
|
||||||
|
|
||||||
import OrganizationCreateModal from '~/components/ui/create/OrganizationCreateModal.vue'
|
import OrganizationCreateModal from '~/components/ui/create/OrganizationCreateModal.vue'
|
||||||
|
|||||||
@@ -643,6 +643,7 @@ import {
|
|||||||
IntlFormatted,
|
IntlFormatted,
|
||||||
LoaderIcon,
|
LoaderIcon,
|
||||||
ModrinthServersPurchaseModal,
|
ModrinthServersPurchaseModal,
|
||||||
|
OptionGroup,
|
||||||
useFormatPrice,
|
useFormatPrice,
|
||||||
useVIntl,
|
useVIntl,
|
||||||
} from '@modrinth/ui'
|
} from '@modrinth/ui'
|
||||||
@@ -650,7 +651,6 @@ import { monthsInInterval } from '@modrinth/ui/src/utils/billing.ts'
|
|||||||
import { useQuery } from '@tanstack/vue-query'
|
import { useQuery } from '@tanstack/vue-query'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
|
||||||
import OptionGroup from '~/components/ui/OptionGroup.vue'
|
|
||||||
import MedalPlanPromotion from '~/components/ui/servers/marketing/MedalPlanPromotion.vue'
|
import MedalPlanPromotion from '~/components/ui/servers/marketing/MedalPlanPromotion.vue'
|
||||||
import ServerPlanSelector from '~/components/ui/servers/marketing/ServerPlanSelector.vue'
|
import ServerPlanSelector from '~/components/ui/servers/marketing/ServerPlanSelector.vue'
|
||||||
import { products } from '~/generated/state.json'
|
import { products } from '~/generated/state.json'
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
<h2>
|
<h2>
|
||||||
{{ formatMessage(messages.discoverHeading) }}
|
{{ formatMessage(messages.discoverHeading) }}
|
||||||
</h2>
|
</h2>
|
||||||
<div class="button-group">
|
<div class="mx-auto mb-20 flex w-fit flex-wrap justify-center gap-5">
|
||||||
<ButtonLink type="colored" color="brand" size="xl" to="/discover/mods">
|
<ButtonLink type="colored" color="brand" size="xl" to="/discover/mods">
|
||||||
<CompassIcon aria-hidden="true" />
|
<CompassIcon aria-hidden="true" />
|
||||||
{{ formatMessage(messages.discoverMods) }}
|
{{ formatMessage(messages.discoverMods) }}
|
||||||
@@ -50,10 +50,12 @@
|
|||||||
<div v-if="rows" class="projects-showcase">
|
<div v-if="rows" class="projects-showcase">
|
||||||
<div v-for="(row, index) in rows" :key="index" class="row">
|
<div v-for="(row, index) in rows" :key="index" class="row">
|
||||||
<div v-for="n in 2" :key="n" class="row__content" :class="{ offset: index % 2 }">
|
<div v-for="n in 2" :key="n" class="row__content" :class="{ offset: index % 2 }">
|
||||||
<nuxt-link
|
<ButtonLink
|
||||||
v-for="project in row"
|
v-for="project in row"
|
||||||
:key="project.id"
|
:key="project.id"
|
||||||
class="project button-animation"
|
type="quiet"
|
||||||
|
interaction="none"
|
||||||
|
class="project !h-auto !shrink !items-stretch !justify-start !whitespace-normal !p-4 !font-normal !leading-normal"
|
||||||
:to="`/${project.project_type}/${project.slug ? project.slug : project.id}`"
|
:to="`/${project.project_type}/${project.slug ? project.slug : project.id}`"
|
||||||
>
|
>
|
||||||
<Avatar :src="project.icon_url" :alt="project.title" size="sm" loading="lazy" />
|
<Avatar :src="project.icon_url" :alt="project.title" size="sm" loading="lazy" />
|
||||||
@@ -65,7 +67,7 @@
|
|||||||
{{ project.description }}
|
{{ project.description }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</nuxt-link>
|
</ButtonLink>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -738,13 +740,6 @@ const creatorFeatureMessages = defineMessages({
|
|||||||
color: var(--landing-color-subheading);
|
color: var(--landing-color-subheading);
|
||||||
max-width: 50rem;
|
max-width: 50rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-group {
|
|
||||||
width: fit-content;
|
|
||||||
gap: 1.25rem;
|
|
||||||
margin: 0 auto 5rem;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.users-section-outer {
|
.users-section-outer {
|
||||||
|
|||||||
@@ -43,13 +43,17 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="project-list-layouts">
|
<div class="project-list-layouts">
|
||||||
<button
|
<button
|
||||||
class="preview-radio button-base"
|
type="button"
|
||||||
|
class="flex !w-full cursor-pointer flex-col overflow-hidden rounded-[var(--radius-md)] border border-solid border-divider bg-button-bg p-0 text-left text-primary outline-2 outline-transparent transition-[filter,transform] hover:brightness-[0.85] focus-visible:ring-4 focus-visible:ring-brand-shadow active:scale-[0.97] active:brightness-[0.8] [&_.example-card]:m-0 [&_.example-card]:min-h-0 [&_.example-card]:border-2 [&_.example-card]:border-solid [&_.example-card]:border-transparent [&_.example-card]:p-4 [&_.example-card]:outline-2 [&_.example-card]:outline-transparent"
|
||||||
:class="{
|
:class="{
|
||||||
selected: cosmetics.searchDisplayMode[projectType.id] === 'list',
|
'!text-contrast [&_.example-card]:!border-brand [&_.example-card]:!bg-brand-highlight [&_.radio]:text-brand':
|
||||||
|
cosmetics.searchDisplayMode[projectType.id] === 'list',
|
||||||
}"
|
}"
|
||||||
@click="() => (cosmetics.searchDisplayMode[projectType.id] = 'list')"
|
@click="() => (cosmetics.searchDisplayMode[projectType.id] = 'list')"
|
||||||
>
|
>
|
||||||
<div class="preview">
|
<div
|
||||||
|
class="preview flex w-full items-center justify-center bg-bg p-6 outline-2 outline-transparent"
|
||||||
|
>
|
||||||
<div class="layout-list-mode">
|
<div class="layout-list-mode">
|
||||||
<div class="example-card card"></div>
|
<div class="example-card card"></div>
|
||||||
<div class="example-card card"></div>
|
<div class="example-card card"></div>
|
||||||
@@ -57,25 +61,30 @@
|
|||||||
<div class="example-card card"></div>
|
<div class="example-card card"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="label">
|
<div
|
||||||
|
class="label flex grow items-center px-[var(--gap-lg)] py-[var(--gap-md)] text-left"
|
||||||
|
>
|
||||||
<RadioButtonCheckedIcon
|
<RadioButtonCheckedIcon
|
||||||
v-if="cosmetics.searchDisplayMode[projectType.id] === 'list'"
|
v-if="cosmetics.searchDisplayMode[projectType.id] === 'list'"
|
||||||
class="radio shrink-0"
|
class="radio mr-2 shrink-0"
|
||||||
/>
|
/>
|
||||||
<RadioButtonIcon v-else class="radio shrink-0" />
|
<RadioButtonIcon v-else class="radio mr-2 shrink-0" />
|
||||||
{{ formatMessage(layoutMode.rows) }}
|
{{ formatMessage(layoutMode.rows) }}
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="preview-radio button-base"
|
type="button"
|
||||||
|
class="flex !w-full cursor-pointer flex-col overflow-hidden rounded-[var(--radius-md)] border border-solid border-divider bg-button-bg p-0 text-left text-primary outline-2 outline-transparent transition-[filter,transform] hover:brightness-[0.85] focus-visible:ring-4 focus-visible:ring-brand-shadow active:scale-[0.97] active:brightness-[0.8] [&_.example-card]:m-0 [&_.example-card]:min-h-0 [&_.example-card]:border-2 [&_.example-card]:border-solid [&_.example-card]:border-transparent [&_.example-card]:p-4 [&_.example-card]:outline-2 [&_.example-card]:outline-transparent"
|
||||||
:class="{
|
:class="{
|
||||||
selected:
|
'!text-contrast [&_.example-card]:!border-brand [&_.example-card]:!bg-brand-highlight [&_.radio]:text-brand':
|
||||||
cosmetics.searchDisplayMode[projectType.id] === 'gallery' ||
|
cosmetics.searchDisplayMode[projectType.id] === 'gallery' ||
|
||||||
cosmetics.searchDisplayMode[projectType.id] === 'grid',
|
cosmetics.searchDisplayMode[projectType.id] === 'grid',
|
||||||
}"
|
}"
|
||||||
@click="() => (cosmetics.searchDisplayMode[projectType.id] = 'grid')"
|
@click="() => (cosmetics.searchDisplayMode[projectType.id] = 'grid')"
|
||||||
>
|
>
|
||||||
<div class="preview">
|
<div
|
||||||
|
class="preview flex w-full items-center justify-center bg-bg p-6 outline-2 outline-transparent"
|
||||||
|
>
|
||||||
<div class="layout-gallery-mode">
|
<div class="layout-gallery-mode">
|
||||||
<div class="example-card card"></div>
|
<div class="example-card card"></div>
|
||||||
<div class="example-card card"></div>
|
<div class="example-card card"></div>
|
||||||
@@ -83,15 +92,17 @@
|
|||||||
<div class="example-card card"></div>
|
<div class="example-card card"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="label">
|
<div
|
||||||
|
class="label flex grow items-center px-[var(--gap-lg)] py-[var(--gap-md)] text-left"
|
||||||
|
>
|
||||||
<RadioButtonCheckedIcon
|
<RadioButtonCheckedIcon
|
||||||
v-if="
|
v-if="
|
||||||
cosmetics.searchDisplayMode[projectType.id] === 'gallery' ||
|
cosmetics.searchDisplayMode[projectType.id] === 'gallery' ||
|
||||||
cosmetics.searchDisplayMode[projectType.id] === 'grid'
|
cosmetics.searchDisplayMode[projectType.id] === 'grid'
|
||||||
"
|
"
|
||||||
class="radio shrink-0"
|
class="radio mr-2 shrink-0"
|
||||||
/>
|
/>
|
||||||
<RadioButtonIcon v-else class="radio shrink-0" />
|
<RadioButtonIcon v-else class="radio mr-2 shrink-0" />
|
||||||
{{ formatMessage(layoutMode.grid) }}
|
{{ formatMessage(layoutMode.grid) }}
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
@@ -487,20 +498,5 @@ const listTypes = computed(() => {
|
|||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
|
||||||
gap: var(--gap-lg);
|
gap: var(--gap-lg);
|
||||||
|
|
||||||
.preview-radio .example-card {
|
|
||||||
border: 2px solid transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-radio.selected .example-card {
|
|
||||||
border-color: var(--color-brand);
|
|
||||||
background-color: var(--color-brand-highlight);
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -3,19 +3,21 @@
|
|||||||
ref="scrollContainer"
|
ref="scrollContainer"
|
||||||
class="card-shadow 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
|
<Button
|
||||||
v-for="(option, index) in options"
|
v-for="(option, index) in options"
|
||||||
:key="`option-group-${index}`"
|
:key="`option-group-${index}`"
|
||||||
ref="optionButtons"
|
ref="optionButtons"
|
||||||
class="button-animation z-[1] flex flex-row items-center gap-2 rounded-full bg-transparent px-4 py-2 font-semibold"
|
type="quiet"
|
||||||
|
interaction="none"
|
||||||
|
class="z-[1] !h-auto !gap-2 !rounded-full !px-4 !py-2"
|
||||||
:class="{
|
:class="{
|
||||||
'text-button-textSelected': modelValue === option,
|
'!text-brand': modelValue === option,
|
||||||
'text-primary': modelValue !== option,
|
'!text-primary': modelValue !== option,
|
||||||
}"
|
}"
|
||||||
@click="setOption(option)"
|
@click="setOption(option)"
|
||||||
>
|
>
|
||||||
<slot :option="option" :selected="modelValue === option" />
|
<slot :option="option" :selected="modelValue === option" />
|
||||||
</button>
|
</Button>
|
||||||
<div
|
<div
|
||||||
class="navtabs-transition pointer-events-none absolute h-[calc(100%-0.5rem)] overflow-hidden rounded-full bg-button-bgSelected p-1"
|
class="navtabs-transition pointer-events-none absolute h-[calc(100%-0.5rem)] overflow-hidden rounded-full bg-button-bgSelected p-1"
|
||||||
:style="{
|
:style="{
|
||||||
@@ -33,6 +35,8 @@
|
|||||||
<script setup lang="ts" generic="T">
|
<script setup lang="ts" generic="T">
|
||||||
import { computed, onMounted, ref, watch } from 'vue'
|
import { computed, onMounted, ref, watch } from 'vue'
|
||||||
|
|
||||||
|
import Button from './buttons/Button.vue'
|
||||||
|
|
||||||
const modelValue = defineModel<T>({ required: true })
|
const modelValue = defineModel<T>({ required: true })
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
@@ -64,7 +68,7 @@ watch(modelValue, () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
function startAnimation(index: number) {
|
function startAnimation(index: number) {
|
||||||
const el = optionButtons.value[index]
|
const el = optionButtons.value[index]?.element
|
||||||
|
|
||||||
if (!el || !el.offsetParent) return
|
if (!el || !el.offsetParent) return
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user