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
@@ -8,18 +8,14 @@
<template #actions>
<div class="flex justify-end gap-2">
<ButtonStyled type="outlined">
<button @click="cancel">
<XIcon />
{{ localizeIfPossible(stayLabel) }}
</button>
</ButtonStyled>
<ButtonStyled color="red">
<button @click="leave">
<RightArrowIcon />
{{ localizeIfPossible(leaveLabel) }}
</button>
</ButtonStyled>
<Button type="outlined" @click="cancel">
<XIcon />
{{ localizeIfPossible(stayLabel) }}
</Button>
<Button type="colored" color="red" @click="leave">
<RightArrowIcon />
{{ localizeIfPossible(leaveLabel) }}
</Button>
</div>
</template>
</NewModal>
@@ -30,7 +26,7 @@ import { RightArrowIcon, XIcon } from '@modrinth/assets'
import { ref } from 'vue'
import Admonition from '#ui/components/base/Admonition.vue'
import ButtonStyled from '#ui/components/base/ButtonStyled.vue'
import { Button } from '#ui/components/base/buttons'
import { defineMessage, type MessageDescriptor, useVIntl } from '#ui/composables/i18n'
import NewModal from './NewModal.vue'
@@ -31,18 +31,19 @@
wrapper-class="max-w-[20rem]"
/>
<div class="flex gap-2 justify-end">
<ButtonStyled>
<button class="!shadow-none" @click="hide()">
<XIcon />
Cancel
</button>
</ButtonStyled>
<ButtonStyled :color="danger ? 'red' : 'brand'">
<button :disabled="action_disabled" @click="proceed">
<component :is="proceedIcon" />
{{ proceedLabel }}
</button>
</ButtonStyled>
<Button @click="hide()">
<XIcon />
Cancel
</Button>
<Button
type="colored"
:color="danger ? 'red' : 'brand'"
:disabled="action_disabled"
@click="proceed"
>
<component :is="proceedIcon" />
{{ proceedLabel }}
</Button>
</div>
</div>
</NewModal>
@@ -53,7 +54,8 @@ import { TrashIcon, XIcon } from '@modrinth/assets'
import { renderString } from '@modrinth/utils'
import { computed, ref } from 'vue'
import ButtonStyled from '../base/ButtonStyled.vue'
import { Button } from '#ui/components/base/buttons'
import StyledInput from '../base/StyledInput.vue'
import NewModal from './NewModal.vue'
+18 -22
View File
@@ -43,33 +43,28 @@
</div>
<div class="flex items-center gap-2">
<slot name="header-actions" />
<ButtonStyled v-if="closable" circular>
<button
v-tooltip="closeLabel"
:aria-label="closeLabel"
:disabled="disableClose"
@click="hide"
>
<XIcon aria-hidden="true" />
</button>
</ButtonStyled>
<IconButton
v-if="closable"
v-tooltip="closeLabel"
:label="closeLabel"
:disabled="disableClose"
@click="hide"
>
<XIcon aria-hidden="true" />
</IconButton>
</div>
</div>
<ButtonStyled
<IconButton
v-if="props.mergeHeader && closable"
v-tooltip="closeLabel"
:label="closeLabel"
class="absolute top-4 right-4 z-10"
circular
:disabled="disableClose"
@click="hide"
>
<button
v-tooltip="closeLabel"
:aria-label="closeLabel"
:disabled="disableClose"
@click="hide"
>
<XIcon aria-hidden="true" />
</button>
</ButtonStyled>
<XIcon aria-hidden="true" />
</IconButton>
<div v-if="scrollable" class="relative flex-1 min-h-0 flex flex-col">
<Transition
@@ -144,12 +139,13 @@
import { XIcon } from '@modrinth/assets'
import { computed, nextTick, onUnmounted, ref } from 'vue'
import { IconButton } from '#ui/components/base/buttons'
import { useVIntl } from '../../composables/i18n'
import { useModalStack } from '../../composables/modal-stack'
import { useScrollIndicator } from '../../composables/scroll-indicator'
import { injectModalBehavior } from '../../providers'
import { commonMessages } from '../../utils/common-messages'
import ButtonStyled from '../base/ButtonStyled.vue'
const { formatMessage } = useVIntl()
@@ -88,23 +88,21 @@
formatMessage(messages.openingAutomatically)
}}</span>
<div v-else class="grid grid-cols-2 gap-2 w-full">
<ButtonStyled>
<button class="flex-1" @click="hide">
<XIcon />
{{ formatMessage(commonMessages.closeButton) }}
</button>
</ButtonStyled>
<ButtonStyled color="brand">
<a
class="flex-1"
href="https://modrinth.com/app"
target="_blank"
rel="noopener noreferrer"
>
<DownloadIcon />
{{ formatMessage(messages.getApp) }}
</a>
</ButtonStyled>
<Button class="flex-1" @click="hide">
<XIcon />
{{ formatMessage(commonMessages.closeButton) }}
</Button>
<ButtonLink
type="colored"
color="brand"
class="flex-1"
href="https://modrinth.com/app"
target="_blank"
rel="noopener noreferrer"
>
<DownloadIcon />
{{ formatMessage(messages.getApp) }}
</ButtonLink>
</div>
</div>
</div>
@@ -116,8 +114,10 @@ import { CheckIcon, DownloadIcon, XIcon } from '@modrinth/assets'
import { commonMessages } from '@modrinth/ui'
import { computed, nextTick, onUnmounted, ref } from 'vue'
import { Button, ButtonLink } from '#ui/components/base/buttons'
import { defineMessages, useVIntl } from '../../composables/i18n'
import { Avatar, ButtonStyled } from '../base'
import { Avatar } from '../base'
import ServerOnlinePlayers from '../project/server/ServerOnlinePlayers.vue'
import ServerRegion from '../project/server/ServerRegion.vue'
+81 -90
View File
@@ -12,9 +12,10 @@ import {
import QrcodeVue from 'qrcode.vue'
import { computed, nextTick, ref } from 'vue'
import { ButtonLink, IconButton } from '#ui/components/base/buttons'
import { injectNotificationManager } from '#ui/providers'
import { ButtonStyled, NewModal, StyledInput } from '../index'
import { NewModal, StyledInput } from '../index'
const props = defineProps({
header: {
@@ -156,16 +157,15 @@ defineExpose({
<div ref="qrCode">
<QrcodeVue :value="url" class="!bg-white rounded-[var(--radius-md)]" margin="3" />
</div>
<ButtonStyled circular type="transparent">
<button
v-tooltip="'Copy QR code'"
class="absolute top-0 right-0 m-2"
aria-label="Copy QR code"
@click="copyImage"
>
<ClipboardCopyIcon class="h-5 w-5" aria-hidden="true" />
</button>
</ButtonStyled>
<IconButton
v-tooltip="'Copy QR code'"
type="quiet"
label="Copy QR code"
class="absolute top-0 right-0 m-2"
@click="copyImage"
>
<ClipboardCopyIcon class="h-5 w-5" aria-hidden="true" />
</IconButton>
</div>
<StyledInput
v-else
@@ -175,17 +175,16 @@ defineExpose({
wrapper-class="h-full w-[30rem]"
>
<template #right>
<ButtonStyled circular type="transparent">
<button
v-tooltip="'Copy Text'"
type="button"
aria-label="Copy Text"
class="absolute top-0 right-0 m-2"
@click="copyText"
>
<ClipboardCopyIcon class="h-5 w-5" aria-hidden="true" />
</button>
</ButtonStyled>
<IconButton
v-tooltip="'Copy Text'"
type="quiet"
label="Copy Text"
native-type="button"
class="absolute top-0 right-0 m-2"
@click="copyText"
>
<ClipboardCopyIcon class="h-5 w-5" aria-hidden="true" />
</IconButton>
</template>
</StyledInput>
<div
@@ -207,75 +206,67 @@ defineExpose({
<ClipboardCopyIcon class="h-5 w-5" aria-hidden="true" />
</div>
</button>
<ButtonStyled v-if="link">
<a
:href="url"
target="_blank"
rel="noopener noreferrer"
aria-label="Open in new tab"
class="w-full"
>
Open in new tab
<ExternalIcon aria-hidden="true" />
</a>
</ButtonStyled>
<ButtonLink
v-if="link"
:href="url"
target="_blank"
rel="noopener noreferrer"
aria-label="Open in new tab"
class="w-full"
>
Open in new tab
<ExternalIcon aria-hidden="true" />
</ButtonLink>
<div v-if="socialButtons" class="flex flex-row gap-1">
<ButtonStyled v-if="canShare" circular>
<button v-tooltip="'Share'" aria-label="Share" @click="share">
<ShareIcon aria-hidden="true" />
</button>
</ButtonStyled>
<ButtonStyled circular>
<a
v-tooltip="'Send as an email'"
:href="sendEmail"
:target="targetParameter"
aria-label="Send as an email"
>
<MailIcon aria-hidden="true" />
</a>
</ButtonStyled>
<ButtonStyled circular>
<a
v-if="link"
v-tooltip="'Open link in browser'"
:target="targetParameter"
:href="url"
aria-label="Open link in browser"
>
<GlobeIcon aria-hidden="true" />
</a>
</ButtonStyled>
<ButtonStyled circular>
<a
v-tooltip="'Toot about it'"
:target="targetParameter"
:href="sendToot"
aria-label="Toot about it"
>
<MastodonIcon aria-hidden="true" />
</a>
</ButtonStyled>
<ButtonStyled circular>
<a
v-tooltip="'Tweet about it'"
:target="targetParameter"
:href="sendTweet"
aria-label="Tweet about it"
>
<TwitterIcon aria-hidden="true" />
</a>
</ButtonStyled>
<ButtonStyled circular>
<a
v-tooltip="'Share on Reddit'"
:target="targetParameter"
:href="postOnReddit"
aria-label="Share on Reddit"
>
<RedditIcon aria-hidden="true" />
</a>
</ButtonStyled>
<IconButton v-if="canShare" v-tooltip="'Share'" label="Share" @click="share">
<ShareIcon aria-hidden="true" />
</IconButton>
<ButtonLink
v-tooltip="'Send as an email'"
:href="sendEmail"
:target="targetParameter"
aria-label="Send as an email"
class="!w-9 !px-0 !rounded-full"
>
<MailIcon aria-hidden="true" />
</ButtonLink>
<ButtonLink
v-if="link"
v-tooltip="'Open link in browser'"
:target="targetParameter"
:href="url"
aria-label="Open link in browser"
class="!w-9 !px-0 !rounded-full"
>
<GlobeIcon aria-hidden="true" />
</ButtonLink>
<ButtonLink
v-tooltip="'Toot about it'"
:target="targetParameter"
:href="sendToot"
aria-label="Toot about it"
class="!w-9 !px-0 !rounded-full"
>
<MastodonIcon aria-hidden="true" />
</ButtonLink>
<ButtonLink
v-tooltip="'Tweet about it'"
:target="targetParameter"
:href="sendTweet"
aria-label="Tweet about it"
class="!w-9 !px-0 !rounded-full"
>
<TwitterIcon aria-hidden="true" />
</ButtonLink>
<ButtonLink
v-tooltip="'Share on Reddit'"
:target="targetParameter"
:href="postOnReddit"
aria-label="Share on Reddit"
class="!w-9 !px-0 !rounded-full"
>
<RedditIcon aria-hidden="true" />
</ButtonLink>
</div>
</div>
</div>
@@ -86,17 +86,13 @@
/>
<div class="flex items-center gap-2">
<ButtonStyled type="transparent" color="orange">
<button type="button" @click="continueInstallation">
{{ formatMessage(messages.installAnyway) }}
</button>
</ButtonStyled>
<ButtonStyled color="brand">
<button type="button" @click="cancelInstallation">
<BanIcon aria-hidden="true" />
{{ formatMessage(messages.dontInstall) }}
</button>
</ButtonStyled>
<Button type="quiet" color="orange" native-type="button" @click="continueInstallation">
{{ formatMessage(messages.installAnyway) }}
</Button>
<Button type="colored" color="brand" native-type="button" @click="cancelInstallation">
<BanIcon aria-hidden="true" />
{{ formatMessage(messages.dontInstall) }}
</Button>
</div>
</div>
</NewModal>
@@ -106,10 +102,11 @@
import { BanIcon } from '@modrinth/assets'
import { computed, nextTick, ref, useTemplateRef } from 'vue'
import { Button } from '#ui/components/base/buttons'
import { defineMessages, useVIntl } from '../../composables/i18n'
import { useScrollIndicator } from '../../composables/scroll-indicator'
import Admonition from '../base/Admonition.vue'
import ButtonStyled from '../base/ButtonStyled.vue'
import Checkbox from '../base/Checkbox.vue'
import Table, { type TableColumn } from '../base/Table.vue'
import NewModal from './NewModal.vue'