mirror of
https://github.com/modrinth/code.git
synced 2026-08-25 17:14:50 +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:
@@ -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,
|
||||
TwitterIcon,
|
||||
} from '@modrinth/assets'
|
||||
import { ButtonLink } from '@modrinth/ui'
|
||||
import { Button, ButtonLink } from '@modrinth/ui'
|
||||
import {
|
||||
AutoLink,
|
||||
defineMessage,
|
||||
@@ -266,11 +266,17 @@ function developerModeIncrement() {
|
||||
:aria-label="formatMessage(messages.modrinthInformation)"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<TextLogo
|
||||
aria-hidden="true"
|
||||
class="text-logo button-base h-6 w-auto text-contrast lg:h-8"
|
||||
<Button
|
||||
type="quiet"
|
||||
interaction="none"
|
||||
aria-label="Modrinth"
|
||||
class="!h-auto !p-0"
|
||||
@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
|
||||
v-if="flags.developerMode"
|
||||
v-tooltip="formatMessage(commonSettingsMessages.featureFlags)"
|
||||
|
||||
@@ -17,10 +17,12 @@
|
||||
{{ item.label }}
|
||||
</div>
|
||||
|
||||
<NuxtLink
|
||||
<ButtonLink
|
||||
v-else-if="item.link ?? item.to"
|
||||
type="quiet"
|
||||
interaction="surface"
|
||||
: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) }"
|
||||
>
|
||||
<component
|
||||
@@ -37,12 +39,14 @@
|
||||
{{ String(item.badge) }}
|
||||
</span>
|
||||
<span v-if="item.chevron" class="ml-auto"><ChevronRightIcon /></span>
|
||||
</NuxtLink>
|
||||
</ButtonLink>
|
||||
|
||||
<button
|
||||
<Button
|
||||
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]"
|
||||
:class="{ 'danger-button': item.danger }"
|
||||
type="quiet"
|
||||
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"
|
||||
>
|
||||
<component
|
||||
@@ -58,7 +62,7 @@
|
||||
>
|
||||
{{ String(item.badge) }}
|
||||
</span>
|
||||
</button>
|
||||
</Button>
|
||||
|
||||
<span v-else>You frog. 🐸</span>
|
||||
</li>
|
||||
@@ -69,6 +73,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ChevronRightIcon } from '@modrinth/assets'
|
||||
import { Button, ButtonLink } from '@modrinth/ui'
|
||||
import { type Component, computed, useSlots } from 'vue'
|
||||
|
||||
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 v-else>{{ getFormattedMessage(messages.invitationNoRole) }}</p>
|
||||
<div class="input-group">
|
||||
<Button type="colored" color="brand" class="brand-button" @click="acceptInvite()">
|
||||
<Button type="colored" color="brand" @click="acceptInvite()">
|
||||
<CheckIcon />
|
||||
{{ getFormattedMessage(commonMessages.acceptButton) }}
|
||||
</Button>
|
||||
|
||||
@@ -275,7 +275,6 @@
|
||||
{{ formatMessage(messages.actionApprove) }}
|
||||
</Button>
|
||||
<SplitButton
|
||||
class="!m-0 !inline-flex !flex-nowrap !justify-start !gap-0"
|
||||
type="colored"
|
||||
color="red"
|
||||
:menu-label="formatMessage(commonMessages.moreOptionsButton)"
|
||||
|
||||
Reference in New Issue
Block a user