refactor: Button components (#6929)

* feat: refactor button components

* fix: qa

* fix: storybook

* a11y: pass

* fix: build

* fix: rename default ->md

* fix: lint

* docs: buttons.md

* refactor part 1

* refactor part 2

* fix: undo refactor for fresh restart

* refactor

* Revert "refactor"

This reverts commit 96d65902d7.

* refactor: part 1

* fix: qa

* fix: qa

* fix: qa

* fix: qa

* fix: qa

* fix: qa

* fix: qa

* fix: qa

* fix: remove text-contrast

* fix: qa

* fix: v-tooltip

* fix: lint

* fix: split broken

* fix: passkey qa

* fix: qa

* fix: qa

* fix: prepr

* fix: splitbutton

* improve button group seam

---------

Signed-off-by: Calum H. <calum@modrinth.com>
Co-authored-by: Prospector <6166773+Prospector@users.noreply.github.com>
This commit is contained in:
Calum H.
2026-08-04 13:54:04 -07:00
committed by GitHub
co-authored by Prospector
parent 6cfe999cb3
commit bb2193b6f5
398 changed files with 13688 additions and 13133 deletions
@@ -74,29 +74,28 @@
<template #cell-actions="{ row: member }">
<div v-if="!member.isOwner" class="flex items-center justify-end gap-1">
<ButtonStyled v-if="member.pending" circular type="transparent">
<button
v-tooltip="resendInviteTooltip(member)"
:aria-label="resendInviteLabel(member)"
:disabled="resendInviteDisabled(member)"
class="text-secondary hover:!filter-none hover:text-contrast focus-visible:!filter-none active:!scale-100 active:!filter-none disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:text-secondary"
@click="handleResendInvite(member)"
>
<SendIcon aria-hidden="true" />
</button>
</ButtonStyled>
<ButtonStyled circular type="transparent">
<button
v-tooltip="memberAccessActionTooltip(member)"
:aria-label="memberAccessActionLabel(member)"
:disabled="!canManageUsers"
class="text-secondary hover:!filter-none hover:text-red focus-visible:!filter-none active:!scale-100 active:!filter-none disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:text-secondary"
@click="member.pending ? handleCancelInvite(member) : handleRemoveMember(member)"
>
<XIcon v-if="member.pending" aria-hidden="true" />
<UserXIcon v-else aria-hidden="true" />
</button>
</ButtonStyled>
<IconButton
v-if="member.pending"
v-tooltip="resendInviteTooltip(member)"
type="quiet"
:label="resendInviteLabel(member)"
:disabled="resendInviteDisabled(member)"
class="text-secondary hover:!filter-none hover:text-contrast focus-visible:!filter-none active:!scale-100 active:!filter-none disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:text-secondary"
@click="handleResendInvite(member)"
>
<SendIcon aria-hidden="true" />
</IconButton>
<IconButton
v-tooltip="memberAccessActionTooltip(member)"
type="quiet"
:label="memberAccessActionLabel(member)"
:disabled="!canManageUsers"
class="text-secondary hover:!filter-none hover:text-red focus-visible:!filter-none active:!scale-100 active:!filter-none disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:text-secondary"
@click="member.pending ? handleCancelInvite(member) : handleRemoveMember(member)"
>
<XIcon v-if="member.pending" aria-hidden="true" />
<UserXIcon v-else aria-hidden="true" />
</IconButton>
</div>
</template>
</Table>
@@ -223,29 +222,30 @@
<span v-else>{{ formatMessage(messages.unknownJoinedDate) }}</span>
</div>
<div class="flex min-w-0 items-center justify-end pr-4">
<ButtonStyled v-if="!member.isOwner" circular type="transparent">
<TeleportOverflowMenu
:options="memberActionOptions(member)"
btn-class="hover:!filter-none focus-visible:!filter-none active:!scale-100 active:!filter-none"
>
<MoreVerticalIcon aria-hidden="true" class="size-5" />
<span class="sr-only">
{{ formatMessage(messages.memberActionsLabel, { username: member.user.username }) }}
</span>
<template #resend-invite>
<SendIcon aria-hidden="true" />
{{ resendInviteLabel(member) }}
</template>
<template #cancel-invite>
<XIcon aria-hidden="true" />
{{ formatMessage(messages.cancelInvite) }}
</template>
<template #remove-user>
<UserXIcon aria-hidden="true" />
{{ formatMessage(messages.removeUser) }}
</template>
</TeleportOverflowMenu>
</ButtonStyled>
<TeleportOverflowMenu
v-if="!member.isOwner"
type="quiet"
:label="formatMessage(messages.memberActionsLabel, { username: member.user.username })"
:options="memberActionOptions(member)"
class="hover:!filter-none focus-visible:!filter-none active:!scale-100 active:!filter-none"
>
<MoreVerticalIcon aria-hidden="true" class="size-5" />
<span class="sr-only">
{{ formatMessage(messages.memberActionsLabel, { username: member.user.username }) }}
</span>
<template #resend-invite>
<SendIcon aria-hidden="true" />
{{ resendInviteLabel(member) }}
</template>
<template #cancel-invite>
<XIcon aria-hidden="true" />
{{ formatMessage(messages.cancelInvite) }}
</template>
<template #remove-user>
<UserXIcon aria-hidden="true" />
{{ formatMessage(messages.removeUser) }}
</template>
</TeleportOverflowMenu>
</div>
</div>
</div>
@@ -284,17 +284,21 @@ import {
UserXIcon,
XIcon,
} from '@modrinth/assets'
import { type Component, computed, onMounted, onUnmounted, ref } from 'vue'
import { computed, onMounted, onUnmounted, ref } from 'vue'
import {
IconButton,
type OverflowMenuOption,
TeleportOverflowMenu,
} from '#ui/components/base/buttons'
import { useFormatDateTime, useRelativeTime } from '../../../composables'
import { defineMessages, useVIntl } from '../../../composables/i18n'
import { commonMessages } from '../../../utils/common-messages'
import AutoLink from '../../base/AutoLink.vue'
import Avatar from '../../base/Avatar.vue'
import ButtonStyled from '../../base/ButtonStyled.vue'
import Combobox, { type ComboboxOption } from '../../base/Combobox.vue'
import Table, { type SortDirection, type TableColumn } from '../../base/Table.vue'
import TeleportOverflowMenu from '../../base/TeleportOverflowMenu.vue'
import type {
ServerAccessMember,
ServerAccessRole,
@@ -396,16 +400,6 @@ const messages = defineMessages({
type AccessTableColumn = 'user' | 'role' | 'joined' | 'actions'
type AccessTableSortableColumn = Exclude<AccessTableColumn, 'actions'>
type AccessTableRow = ServerAccessMember & Record<string, unknown>
type OverflowMenuOption = {
id: string
icon?: Component
action: () => void
shown?: boolean
color?: 'standard' | 'brand' | 'red' | 'orange' | 'green' | 'blue' | 'purple'
disabled?: boolean
tooltip?: string
}
const columns = computed<TableColumn<AccessTableColumn>[]>(() => [
{ key: 'user', label: formatMessage(messages.userColumn), width: '32%', enableSorting: true },
{ key: 'role', label: formatMessage(messages.roleColumn), width: '28%', enableSorting: true },
@@ -609,6 +603,7 @@ function memberActionOptions(member: ServerAccessMember): OverflowMenuOption[] {
return [
{
id: 'resend-invite',
label: resendInviteLabel(member),
icon: SendIcon,
action: () => handleResendInvite(member),
shown: member.pending,
@@ -617,18 +612,20 @@ function memberActionOptions(member: ServerAccessMember): OverflowMenuOption[] {
},
{
id: 'cancel-invite',
label: formatMessage(messages.cancelInvite),
icon: XIcon,
action: () => handleCancelInvite(member),
color: 'red',
tone: 'red',
shown: member.pending,
disabled: !canManageUsers.value,
tooltip: memberAccessActionTooltip(member),
},
{
id: 'remove-user',
label: formatMessage(messages.removeUser),
icon: UserXIcon,
action: () => handleRemoveMember(member),
color: 'red',
tone: 'red',
shown: !member.pending,
disabled: !canManageUsers.value,
tooltip: memberAccessActionTooltip(member),