mirror of
https://github.com/modrinth/code.git
synced 2026-08-25 17:14:50 +00:00
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:
@@ -3,9 +3,11 @@ import { PlusIcon, XIcon } from '@modrinth/assets'
|
||||
import type Stripe from 'stripe'
|
||||
import { nextTick, ref, useTemplateRef } from 'vue'
|
||||
|
||||
import { Button } from '#ui/components/base/buttons'
|
||||
|
||||
import { defineMessages, useVIntl } from '../../composables/i18n'
|
||||
import { commonMessages } from '../../utils'
|
||||
import { ButtonStyled, NewModal } from '../index'
|
||||
import { NewModal } from '../index'
|
||||
import type { AddPaymentMethodProps } from './AddPaymentMethod.vue'
|
||||
import AddPaymentMethod from './AddPaymentMethod.vue'
|
||||
|
||||
@@ -57,18 +59,14 @@ defineExpose({
|
||||
@stop-loading="loading = false"
|
||||
/>
|
||||
<div class="input-group mt-auto pt-4">
|
||||
<ButtonStyled color="brand">
|
||||
<button :disabled="loading" @click="addPaymentMethod.submit()">
|
||||
<PlusIcon />
|
||||
{{ formatMessage(messages.paymentMethodAdd) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<button @click="modal.hide()">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" :disabled="loading" @click="addPaymentMethod.submit()">
|
||||
<PlusIcon />
|
||||
{{ formatMessage(messages.paymentMethodAdd) }}
|
||||
</Button>
|
||||
<Button @click="modal.hide()">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</NewModal>
|
||||
|
||||
@@ -12,12 +12,12 @@ import { useQueryClient } from '@tanstack/vue-query'
|
||||
import type Stripe from 'stripe'
|
||||
import { computed, nextTick, onBeforeUnmount, ref, toRef, useTemplateRef, watch } from 'vue'
|
||||
|
||||
import { Button } from '#ui/components/base/buttons'
|
||||
import { injectNotificationManager } from '#ui/providers/web-notifications.ts'
|
||||
|
||||
import { defineMessage, type MessageDescriptor, useVIntl } from '../../composables/i18n'
|
||||
import { useStripe } from '../../composables/stripe'
|
||||
import { commonMessages } from '../../utils'
|
||||
import { ButtonStyled } from '../index'
|
||||
import ModalLoadingIndicator from '../modal/ModalLoadingIndicator.vue'
|
||||
import NewModal from '../modal/NewModal.vue'
|
||||
import PlanSelector from './ServersPurchase0Plan.vue'
|
||||
@@ -555,51 +555,50 @@ function goToBreadcrumbStep(id: string) {
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-2 justify-between mt-4">
|
||||
<ButtonStyled>
|
||||
<button v-if="previousStep" @click="previousStep && setStep(previousStep, true)">
|
||||
<LeftArrowIcon /> {{ formatMessage(commonMessages.backButton) }}
|
||||
</button>
|
||||
<button v-else-if="currentStep !== 'plan'" @click="modal?.hide()">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-if="currentStep !== 'plan'" color="brand">
|
||||
<button
|
||||
v-tooltip="
|
||||
currentStep === 'review' && !acceptedEula && !noPaymentRequired
|
||||
? 'You must accept the Minecraft EULA to proceed.'
|
||||
: undefined
|
||||
"
|
||||
:disabled="!canProceed"
|
||||
@click="
|
||||
noPaymentRequired && currentStep === 'review'
|
||||
? (async () => {
|
||||
if (props.onFinalizeNoPaymentChange) {
|
||||
try {
|
||||
await props.onFinalizeNoPaymentChange()
|
||||
} catch (e) {
|
||||
return
|
||||
}
|
||||
<Button v-if="previousStep" @click="previousStep && setStep(previousStep, true)">
|
||||
<LeftArrowIcon /> {{ formatMessage(commonMessages.backButton) }}
|
||||
</Button>
|
||||
<Button v-else-if="currentStep !== 'plan'" @click="modal?.hide()">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</Button>
|
||||
<Button
|
||||
v-if="currentStep !== 'plan'"
|
||||
v-tooltip="
|
||||
currentStep === 'review' && !acceptedEula && !noPaymentRequired
|
||||
? 'You must accept the Minecraft EULA to proceed.'
|
||||
: undefined
|
||||
"
|
||||
type="colored"
|
||||
color="brand"
|
||||
:disabled="!canProceed"
|
||||
@click="
|
||||
noPaymentRequired && currentStep === 'review'
|
||||
? (async () => {
|
||||
if (props.onFinalizeNoPaymentChange) {
|
||||
try {
|
||||
await props.onFinalizeNoPaymentChange()
|
||||
} catch (e) {
|
||||
return
|
||||
}
|
||||
modal?.hide()
|
||||
})()
|
||||
: setStep(nextStep)
|
||||
"
|
||||
>
|
||||
<template v-if="currentStep === 'review'">
|
||||
<template v-if="noPaymentRequired"><CheckCircleIcon /> Confirm Change</template>
|
||||
<template v-else>
|
||||
<SpinnerIcon v-if="completingPurchase" class="animate-spin" />
|
||||
<CheckCircleIcon v-else />
|
||||
Subscribe
|
||||
</template>
|
||||
</template>
|
||||
}
|
||||
modal?.hide()
|
||||
})()
|
||||
: setStep(nextStep)
|
||||
"
|
||||
>
|
||||
<template v-if="currentStep === 'review'">
|
||||
<template v-if="noPaymentRequired"><CheckCircleIcon /> Confirm Change</template>
|
||||
<template v-else>
|
||||
{{ formatMessage(commonMessages.nextButton) }} <RightArrowIcon />
|
||||
<SpinnerIcon v-if="completingPurchase" class="animate-spin" />
|
||||
<CheckCircleIcon v-else />
|
||||
Subscribe
|
||||
</template>
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ formatMessage(commonMessages.nextButton) }} <RightArrowIcon />
|
||||
</template>
|
||||
</Button>
|
||||
</div>
|
||||
</NewModal>
|
||||
</template>
|
||||
|
||||
@@ -78,18 +78,14 @@
|
||||
|
||||
<template #actions>
|
||||
<div class="flex justify-end gap-2">
|
||||
<ButtonStyled type="outlined">
|
||||
<button @click="handleCancel">
|
||||
<XIcon />
|
||||
{{ formatMessage(messages.cancelButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="brand">
|
||||
<button :disabled="!canResubscribe" @click="handleResubscribe">
|
||||
<RotateCounterClockwiseIcon />
|
||||
{{ formatMessage(messages.resubscribeButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="outlined" @click="handleCancel">
|
||||
<XIcon />
|
||||
{{ formatMessage(messages.cancelButton) }}
|
||||
</Button>
|
||||
<Button type="colored" color="brand" :disabled="!canResubscribe" @click="handleResubscribe">
|
||||
<RotateCounterClockwiseIcon />
|
||||
{{ formatMessage(messages.resubscribeButton) }}
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</NewModal>
|
||||
@@ -100,12 +96,13 @@ import type { Labrinth } from '@modrinth/api-client'
|
||||
import { RotateCounterClockwiseIcon, XIcon } from '@modrinth/assets'
|
||||
import { computed, ref, useTemplateRef } from 'vue'
|
||||
|
||||
import { Button } from '#ui/components/base/buttons'
|
||||
import { injectNotificationManager } from '#ui/providers/web-notifications.ts'
|
||||
|
||||
import { useFormatDateTime, useFormatPrice } from '../../composables'
|
||||
import { defineMessages, useVIntl } from '../../composables/i18n'
|
||||
import IntlFormatted from '../base/IntlFormatted.vue'
|
||||
import { ButtonStyled, NewModal } from '../index'
|
||||
import { NewModal } from '../index'
|
||||
|
||||
const { addNotification } = injectNotificationManager()
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
@@ -3,7 +3,8 @@ import type { Labrinth } from '@modrinth/api-client'
|
||||
import { ChevronRightIcon, ExternalIcon, XIcon } from '@modrinth/assets'
|
||||
import { computed, ref, useTemplateRef } from 'vue'
|
||||
|
||||
import ButtonStyled from '../base/ButtonStyled.vue'
|
||||
import { Button, IconButton } from '#ui/components/base/buttons'
|
||||
|
||||
import NewModal from '../modal/NewModal.vue'
|
||||
import type { ServerBillingInterval } from './ModrinthServersPurchaseModal.vue'
|
||||
import PlanSelector from './ServersPurchase0Plan.vue'
|
||||
@@ -143,11 +144,9 @@ defineExpose({
|
||||
class="absolute inset-x-0 bottom-0 -m-px z-30 rounded-2xl border border-solid border-surface-5 bg-bg-raised p-6 shadow-2xl"
|
||||
>
|
||||
<div class="absolute right-4 top-4">
|
||||
<ButtonStyled circular type="transparent">
|
||||
<button aria-label="Close sign in prompt" @click="closeSignInPrompt">
|
||||
<XIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<IconButton type="quiet" label="Close sign in prompt" @click="closeSignInPrompt">
|
||||
<XIcon />
|
||||
</IconButton>
|
||||
</div>
|
||||
|
||||
<div class="mx-auto flex max-w-xl flex-col items-center gap-4 text-center">
|
||||
@@ -155,12 +154,10 @@ defineExpose({
|
||||
<div class="font-semibold text-contrast">Sign in to continue your purchase</div>
|
||||
<div class="">You need a Modrinth account to add your billing details.</div>
|
||||
</div>
|
||||
<ButtonStyled color="brand">
|
||||
<button class="mt-2" @click="continueToAuth">
|
||||
Sign in or create an account
|
||||
<ExternalIcon class="size-4" />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" class="mt-2" @click="continueToAuth">
|
||||
Sign in or create an account
|
||||
<ExternalIcon class="size-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
@@ -3,10 +3,11 @@ import type { Labrinth } from '@modrinth/api-client'
|
||||
import { RightArrowIcon } from '@modrinth/assets'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import { Button } from '#ui/components/base/buttons'
|
||||
|
||||
import { useFormatPrice } from '../../composables'
|
||||
import { defineMessages, useVIntl } from '../../composables/i18n'
|
||||
import { getPriceForInterval, monthsInInterval } from '../../utils/product-utils'
|
||||
import ButtonStyled from '../base/ButtonStyled.vue'
|
||||
import OptionGroup from '../base/OptionGroup.vue'
|
||||
import type { ServerBillingInterval } from './ModrinthServersPurchaseModal.vue'
|
||||
import ServersSpecs from './ServersSpecs.vue'
|
||||
@@ -218,19 +219,19 @@ function selectCustom() {
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full">
|
||||
<ButtonStyled color="blue">
|
||||
<button
|
||||
class="w-full"
|
||||
:disabled="existingPlan?.id === plansByRam.small.id"
|
||||
@click="selectPlan(plansByRam.small!)"
|
||||
>
|
||||
{{
|
||||
existingPlan?.id === plansByRam.small.id
|
||||
? formatMessage(messages.yourCurrentPlan)
|
||||
: formatMessage(messages.selectPlan)
|
||||
}}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button
|
||||
type="colored"
|
||||
color="blue"
|
||||
class="w-full"
|
||||
:disabled="existingPlan?.id === plansByRam.small.id"
|
||||
@click="selectPlan(plansByRam.small!)"
|
||||
>
|
||||
{{
|
||||
existingPlan?.id === plansByRam.small.id
|
||||
? formatMessage(messages.yourCurrentPlan)
|
||||
: formatMessage(messages.selectPlan)
|
||||
}}
|
||||
</Button>
|
||||
</div>
|
||||
<ServersSpecs
|
||||
v-if="smallSpecs"
|
||||
@@ -281,19 +282,19 @@ function selectCustom() {
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full">
|
||||
<ButtonStyled color="brand" class="w-full">
|
||||
<button
|
||||
class="w-full"
|
||||
:disabled="existingPlan?.id === plansByRam.medium.id"
|
||||
@click="selectPlan(plansByRam.medium!)"
|
||||
>
|
||||
{{
|
||||
existingPlan?.id === plansByRam.medium.id
|
||||
? formatMessage(messages.yourCurrentPlan)
|
||||
: formatMessage(messages.selectPlan)
|
||||
}}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button
|
||||
type="colored"
|
||||
color="brand"
|
||||
class="w-full"
|
||||
:disabled="existingPlan?.id === plansByRam.medium.id"
|
||||
@click="selectPlan(plansByRam.medium!)"
|
||||
>
|
||||
{{
|
||||
existingPlan?.id === plansByRam.medium.id
|
||||
? formatMessage(messages.yourCurrentPlan)
|
||||
: formatMessage(messages.selectPlan)
|
||||
}}
|
||||
</Button>
|
||||
</div>
|
||||
<ServersSpecs
|
||||
v-if="mediumSpecs"
|
||||
@@ -329,19 +330,19 @@ function selectCustom() {
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full">
|
||||
<ButtonStyled color="purple" class="w-full">
|
||||
<button
|
||||
class="w-full"
|
||||
:disabled="existingPlan?.id === plansByRam.large.id"
|
||||
@click="selectPlan(plansByRam.large!)"
|
||||
>
|
||||
{{
|
||||
existingPlan?.id === plansByRam.large.id
|
||||
? formatMessage(messages.yourCurrentPlan)
|
||||
: formatMessage(messages.selectPlan)
|
||||
}}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button
|
||||
type="colored"
|
||||
color="purple"
|
||||
class="w-full"
|
||||
:disabled="existingPlan?.id === plansByRam.large.id"
|
||||
@click="selectPlan(plansByRam.large!)"
|
||||
>
|
||||
{{
|
||||
existingPlan?.id === plansByRam.large.id
|
||||
? formatMessage(messages.yourCurrentPlan)
|
||||
: formatMessage(messages.selectPlan)
|
||||
}}
|
||||
</Button>
|
||||
</div>
|
||||
<ServersSpecs
|
||||
v-if="largeSpecs"
|
||||
@@ -366,11 +367,9 @@ function selectCustom() {
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col items-end gap-2 shrink-0">
|
||||
<ButtonStyled>
|
||||
<button class="flex items-center gap-2" @click="selectCustom">
|
||||
{{ formatMessage(messages.getStarted) }} <RightArrowIcon class="h-4 w-4" />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button class="flex items-center gap-2" @click="selectCustom">
|
||||
{{ formatMessage(messages.getStarted) }} <RightArrowIcon class="h-4 w-4" />
|
||||
</Button>
|
||||
<div class="text-sm text-secondary whitespace-nowrap">
|
||||
Starting at {{ formatPrice(customStartingPrice, currency, true) }}/mo
|
||||
</div>
|
||||
|
||||
@@ -15,11 +15,12 @@ import dayjs from 'dayjs'
|
||||
import type Stripe from 'stripe'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import { Button } from '#ui/components/base/buttons'
|
||||
|
||||
import { useFormatPrice } from '../../composables'
|
||||
import { useVIntl } from '../../composables/i18n'
|
||||
import { getPriceForInterval, monthsInInterval } from '../../utils/product-utils'
|
||||
import { regionOverrides } from '../../utils/regions'
|
||||
import ButtonStyled from '../base/ButtonStyled.vue'
|
||||
import Checkbox from '../base/Checkbox.vue'
|
||||
import TagItem from '../base/TagItem.vue'
|
||||
import ModrinthServersIcon from '../servers/ModrinthServersIcon.vue'
|
||||
@@ -323,12 +324,10 @@ function setInterval(newInterval: ServerBillingInterval) {
|
||||
No payment method selected
|
||||
</div>
|
||||
</template>
|
||||
<ButtonStyled size="small" type="transparent">
|
||||
<button class="ml-auto" @click="emit('changePaymentMethod')">
|
||||
<template v-if="selectedPaymentMethod || hasPaymentMethod"> <EditIcon /> Change </template>
|
||||
<template v-else> Select payment method <RightArrowIcon /> </template>
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="quiet" size="xs" class="ml-auto !h-6" @click="emit('changePaymentMethod')">
|
||||
<template v-if="selectedPaymentMethod || hasPaymentMethod"> <EditIcon /> Change </template>
|
||||
<template v-else> Select payment method <RightArrowIcon /> </template>
|
||||
</Button>
|
||||
</div>
|
||||
<p v-if="!noPaymentRequired" class="m-0 mt-4 text-sm text-secondary">
|
||||
<template v-if="isUpgrade && (total ?? 0) > 0">
|
||||
|
||||
Reference in New Issue
Block a user