refactor: ♻️ Port all buttons to new UI library

This commit is contained in:
Jesse Wierzbinski 2024-06-27 14:09:05 -10:00
parent 13faf840dd
commit 091615b04e
No known key found for this signature in database
20 changed files with 120 additions and 155 deletions

View file

@ -1,23 +0,0 @@
<template>
<button v-bind="$props" type="button" :disabled="loading"
:class="['relative isolate text-base/6 font-semibold px-[calc(theme(spacing[3.5])-1px)] py-[calc(theme(spacing[2.5])-1px)] sm:px-[calc(theme(spacing.3)-1px)] sm:py-[calc(theme(spacing[1.5])-1px)] sm:text-sm/6 focus:outline-none focus:outline focus:outline-2 focus:outline-offset-2 focus:outline-[--btn-bg] before:absolute before:inset-0 before:-z-10 before:rounded-[calc(theme(borderRadius.lg)-1px)] before:shadow before:hidden after:absolute after:-z-10 after:-inset-px after:rounded-md before:disabled:shadow-none after:disabled:shadow-none text-white cursor-default rounded-md duration-200 hover:shadow disabled:opacity-70 content-none disabled:cursor-not-allowed shadow-sm bg-[--btn-bg] before:bg-[--btn-bg] after:active:bg-[--btn-hover-overlay] after:hover:bg-[--btn-hover-overlay]', loading && '[&>*]:invisible']">
<div v-if="loading" class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 !visible size-5">
<iconify-icon icon="tabler:loader-2" height="none" class="animate-spin size-5" />
</div>
<slot />
</button>
</template>
<script lang="ts" setup>
import type { ButtonHTMLAttributes } from "vue";
interface Props extends /* @vue-ignore */ ButtonHTMLAttributes {}
defineProps<
Props & {
loading?: boolean;
}
>();
</script>
<style></style>

View file

@ -1,12 +1,12 @@
<template> <template>
<ButtonBase <ButtonBase class="hover:bg-white/5 text-xs max-w-full w-full h-full !p-0">
class="hover:bg-white/5 text-xs max-w-full w-full h-full !p-0"> <Icon :icon="icon" class="!size-6" />
<iconify-icon :icon="icon" class="size-6" width="none" />
</ButtonBase> </ButtonBase>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import ButtonBase from "~/packages/ui/components/buttons/button.vue"; import ButtonBase from "~/packages/ui/components/buttons/button.vue";
import Icon from "~/packages/ui/components/icons/icon.vue";
defineProps<{ defineProps<{
icon: string; icon: string;

View file

@ -1,23 +0,0 @@
<template>
<ButtonBase
class="[--btn-border:theme(colors.primary.950/90%)] [--btn-bg:theme(colors.primary.600)] [--btn-hover-overlay:theme(colors.white/5%)] [--btn-icon:theme(colors.primary.400)] active:[--btn-icon:theme(colors.primary.300)] hover:[--btn-icon:theme(colors.primary.300)] after:shadow-[shadow:inset_0_1px_theme(colors.white/15%)] border border-white/5"
v-bind="$props">
<slot />
</ButtonBase>
</template>
<script lang="ts" setup>
import type { ButtonHTMLAttributes } from "vue";
import ButtonBase from "~/packages/ui/components/buttons/button.vue";
// import ButtonBase from "./button-base.vue";
interface Props extends /* @vue-ignore */ ButtonHTMLAttributes {}
defineProps<
Props & {
loading?: boolean;
}
>();
</script>
<style></style>

View file

@ -1,22 +0,0 @@
<template>
<ButtonBase
class="[--btn-border:theme(colors.zinc.950/90%)] [--btn-bg:theme(colors.zinc.800)] [--btn-hover-overlay:theme(colors.white/5%)] [--btn-icon:theme(colors.zinc.400)] active:[--btn-icon:theme(colors.zinc.300)] hover:[--btn-icon:theme(colors.zinc.300)] after:shadow-[shadow:inset_0_1px_theme(colors.white/15%)] border border-white/5"
v-bind="$props" :loading="loading">
<slot />
</ButtonBase>
</template>
<script lang="ts" setup>
import type { ButtonHTMLAttributes } from "vue";
import ButtonBase from "./button-base.vue";
interface Props extends /* @vue-ignore */ ButtonHTMLAttributes {}
defineProps<
Props & {
loading?: boolean;
}
>();
</script>
<style></style>

View file

@ -34,12 +34,12 @@
<Button title="Add content warning" @click="cw = !cw" :toggled="cw"> <Button title="Add content warning" @click="cw = !cw" :toggled="cw">
<iconify-icon width="1.25rem" height="1.25rem" icon="tabler:rating-18-plus" aria-hidden="true" /> <iconify-icon width="1.25rem" height="1.25rem" icon="tabler:rating-18-plus" aria-hidden="true" />
</Button> </Button>
<ButtonPrimary :loading="loading" @click="send" class="ml-auto rounded-full" <ButtonBase theme="primary" :loading="loading" @click="send" class="ml-auto rounded-full"
:disabled="!canSubmit || loading"> :disabled="!canSubmit || loading">
<span>{{ {{
respondingType === "edit" ? "Edit!" : "Send!" respondingType === "edit" ? "Edit!" : "Send!"
}}</span> }}
</ButtonPrimary> </ButtonBase>
</div> </div>
</div> </div>
</template> </template>
@ -47,8 +47,8 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { Instance, Status } from "@lysand-org/client/types"; import type { Instance, Status } from "@lysand-org/client/types";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
import ButtonBase from "~/packages/ui/components/buttons/button.vue";
import { OverlayScrollbarsComponent } from "#imports"; import { OverlayScrollbarsComponent } from "#imports";
import ButtonPrimary from "../buttons/button-primary.vue";
import RichTextboxInput from "../inputs/rich-textbox-input.vue"; import RichTextboxInput from "../inputs/rich-textbox-input.vue";
import Note from "../social-elements/notes/note.vue"; import Note from "../social-elements/notes/note.vue";
import Button from "./button.vue"; import Button from "./button.vue";

View file

@ -42,10 +42,10 @@
<textarea :disabled="data.progress < 1.0" @keydown.enter.stop v-model="data.alt_text" <textarea :disabled="data.progress < 1.0" @keydown.enter.stop v-model="data.alt_text"
placeholder="Add alt text" placeholder="Add alt text"
class="w-full p-2 text-sm prose prose-invert bg-dark-900 rounded focus:!ring-0 !ring-none !border-none !outline-none placeholder:text-zinc-500 appearance-none focus:!border-none focus:!outline-none" /> class="w-full p-2 text-sm prose prose-invert bg-dark-900 rounded focus:!ring-0 !ring-none !border-none !outline-none placeholder:text-zinc-500 appearance-none focus:!border-none focus:!outline-none" />
<ButtonSecondary @click="updateAltText(data.id, data.alt_text)" class="w-full" <Button theme="secondary" @click="updateAltText(data.id, data.alt_text)" class="w-full"
:loading="data.progress < 1.0"> :loading="data.progress < 1.0">
<span>Edit</span> <span>Edit</span>
</ButtonSecondary> </Button>
</Popover.Content> </Popover.Content>
</Popover.Positioner> </Popover.Positioner>
</Popover.Root> </Popover.Root>
@ -57,7 +57,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { Popover } from "@ark-ui/vue"; import { Popover } from "@ark-ui/vue";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
import ButtonSecondary from "../buttons/button-secondary.vue"; import Button from "~/packages/ui/components/buttons/button.vue";
const files = defineModel< const files = defineModel<
{ {

View file

@ -1,15 +1,14 @@
<template> <template>
<slot name="error" v-if="error" v-bind="{ error }"> <slot name="error" v-if="error" v-bind="{ error }">
<div id="error" class="grid min-h-screen place-items-center px-6 py-24 sm:py-32 lg:px-8"> <div id="error" class="grid min-h-screen place-items-center px-6 py-24 sm:py-32 lg:px-8">
<div class="text-center prose prose-invert"> <div class="text-center prose prose-invert max-w-md w-full">
<p class="text-base font-semibold text-primary-400">404</p>
<h1 class="mt-4 text-3xl font-bold tracking-tight text-gray-100 sm:text-5xl">{{ error.title }} <h1 class="mt-4 text-3xl font-bold tracking-tight text-gray-100 sm:text-5xl">{{ error.title }}
</h1> </h1>
<p class="mt-6 text-base leading-7 text-gray-400" v-html="error.message"></p> <p class="mt-6 text-base leading-7 text-gray-400" v-html="error.message"></p>
<div class="mt-10 grid grid-cols-2 gap-x-6 mx-auto max-w-md"> <div class="mt-10 grid grid-cols-2 gap-x-6 mx-auto max-w-md">
<ButtonPrimary class="w-full" @click="back">Go back</ButtonPrimary> <Button theme="primary" class="w-full" @click="back">Go back</Button>
<a href="https://github.com/lysand-org/lysand-fe/issues" target="_blank"> <a href="https://github.com/lysand-org/lysand-fe/issues" target="_blank">
<ButtonSecondary class="w-full">Report an issue</ButtonSecondary> <Button theme="secondary" class="w-full">Report an issue</Button>
</a> </a>
</div> </div>
</div> </div>
@ -19,8 +18,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import ButtonPrimary from "../buttons/button-primary.vue"; import Button from "~/packages/ui/components/buttons/button.vue";
import ButtonSecondary from "../buttons/button-secondary.vue";
const error = ref<{ const error = ref<{
title: string; title: string;

View file

@ -13,13 +13,13 @@
<span class="font-semibold text-gray-300">{{ provider.name }}</span> <span class="font-semibold text-gray-300">{{ provider.name }}</span>
</div> </div>
<div> <div>
<ButtonPrimary :loading="loading" v-if="!linkedProviders?.find(p => p.id === provider.id)" <Button theme="primary" :loading="loading"
@click="link(provider.id)"> v-if="!linkedProviders?.find(p => p.id === provider.id)" @click="link(provider.id)">
<span>Link</span> Link
</ButtonPrimary> </Button>
<ButtonSecondary :loading="loading" v-else @click="unlink(provider.id)"> <Button theme="secondary" :loading="loading" v-else @click="unlink(provider.id)">
<span>Unlink</span> Unlink
</ButtonSecondary> </Button>
</div> </div>
</div> </div>
</div> </div>
@ -29,9 +29,8 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { ResponseError } from "@lysand-org/client"; import type { ResponseError } from "@lysand-org/client";
import Button from "~/packages/ui/components/buttons/button.vue";
import Avatar from "../avatars/avatar.vue"; import Avatar from "../avatars/avatar.vue";
import ButtonPrimary from "../buttons/button-primary.vue";
import ButtonSecondary from "../buttons/button-secondary.vue";
const client = useClient(); const client = useClient();
const ssoConfig = useSSOConfig(); const ssoConfig = useSSOConfig();

View file

@ -30,12 +30,12 @@
</div> </div>
<div class="px-4 mt-4 grid grid-cols-2 gap-2"> <div class="px-4 mt-4 grid grid-cols-2 gap-2">
<ButtonPrimary class="w-full" type="submit" :loading="loading"> <Button theme="primary" class="w-full" type="submit" :loading="loading">
<span>Save</span> <span>Save</span>
</ButtonPrimary> </Button>
<ButtonSecondary class="w-full" @click="revert" type="button" :loading="loading"> <Button theme="secondary" class="w-full" @click="revert" type="button" :loading="loading">
<span>Revert</span> <span>Revert</span>
</ButtonSecondary> </Button>
</div> </div>
</form> </form>
<div> <div>
@ -46,9 +46,8 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { ResponseError } from "@lysand-org/client"; import type { ResponseError } from "@lysand-org/client";
import Button from "~/packages/ui/components/buttons/button.vue";
import Avatar from "../avatars/avatar.vue"; import Avatar from "../avatars/avatar.vue";
import ButtonPrimary from "../buttons/button-primary.vue";
import ButtonSecondary from "../buttons/button-secondary.vue";
import RichTextboxInput from "../inputs/rich-textbox-input.vue"; import RichTextboxInput from "../inputs/rich-textbox-input.vue";
import TextInput from "../inputs/text-input.vue"; import TextInput from "../inputs/text-input.vue";
import Oidc from "./oidc.vue"; import Oidc from "./oidc.vue";

View file

@ -20,7 +20,7 @@
</span> </span>
</div> </div>
</div> </div>
<ButtonBase theme="secondary" v-else class="w-full !justify-start"> <ButtonBase theme="secondary" v-else class="w-full !justify-start overflow-hidden">
<Icon icon="tabler:login" class="!size-6" /> <Icon icon="tabler:login" class="!size-6" />
<span class="shrink-0 line-clamp-1">Sign In</span> <span class="shrink-0 line-clamp-1">Sign In</span>
</ButtonBase> </ButtonBase>
@ -61,21 +61,21 @@
</Menu.Item> </Menu.Item>
<Menu.Item value="" v-if="currentIdentity"> <Menu.Item value="" v-if="currentIdentity">
<NuxtLink href="/settings" class="w-full"> <NuxtLink href="/settings" class="w-full">
<ButtonBase theme="secondary" class="w-full !justify-start"> <ButtonBase theme="outline" class="w-full !justify-start">
<Icon icon="tabler:adjustments" class="!size-6" /> <Icon icon="tabler:adjustments" class="!size-6" />
<span class="shrink-0 line-clamp-1">Settings</span> <span class="shrink-0 line-clamp-1">Settings</span>
</ButtonBase> </ButtonBase>
</NuxtLink> </NuxtLink>
</Menu.Item> </Menu.Item>
<Menu.Item value=""> <Menu.Item value="">
<ButtonBase @click="$emit('signIn')" theme="secondary" class="w-full !justify-start"> <ButtonBase @click="$emit('signIn')" theme="outline" class="w-full !justify-start">
<Icon icon="tabler:user-plus" class="!size-6" /> <Icon icon="tabler:user-plus" class="!size-6" />
<span class="shrink-0 line-clamp-1">Add new account</span> <span class="shrink-0 line-clamp-1">Add new account</span>
</ButtonBase> </ButtonBase>
</Menu.Item> </Menu.Item>
<Menu.Item value="" v-if="!currentIdentity"> <Menu.Item value="" v-if="!currentIdentity">
<NuxtLink href="/register" class="w-full"> <NuxtLink href="/register" class="w-full">
<ButtonBase theme="secondary" class="w-full !justify-start"> <ButtonBase theme="outline" class="w-full !justify-start">
<Icon icon="tabler:certificate" class="!size-6" /> <Icon icon="tabler:certificate" class="!size-6" />
<span class="shrink-0 line-clamp-1">Create new account</span> <span class="shrink-0 line-clamp-1">Create new account</span>
</ButtonBase> </ButtonBase>

View file

@ -12,7 +12,7 @@
Timelines</h3> Timelines</h3>
<NuxtLink v-for="timeline in visibleTimelines" :key="timeline.href" :to="timeline.href"> <NuxtLink v-for="timeline in visibleTimelines" :key="timeline.href" :to="timeline.href">
<ButtonBase theme="secondary" class="w-full !justify-start"> <ButtonBase theme="outline" class="w-full !justify-start overflow-hidden rounded-sm">
<Icon :icon="timeline.icon" class="!size-6" /> <Icon :icon="timeline.icon" class="!size-6" />
<span class="shrink-0 line-clamp-1">{{ timeline.name }}</span> <span class="shrink-0 line-clamp-1">{{ timeline.name }}</span>
</ButtonBase> </ButtonBase>
@ -27,13 +27,13 @@
<AccountPicker @sign-in="signIn().finally(() => loadingAuth = false)" <AccountPicker @sign-in="signIn().finally(() => loadingAuth = false)"
@sign-out="id => signOut(id).finally(() => loadingAuth = false)" /> @sign-out="id => signOut(id).finally(() => loadingAuth = false)" />
<NuxtLink href="/register" v-if="!identity"> <NuxtLink href="/register" v-if="!identity">
<ButtonBase theme="secondary" class="w-full !justify-start"> <ButtonBase theme="outline" class="w-full !justify-start overflow-hidden rounded-sm">
<Icon icon="tabler:certificate" class="!size-6" /> <Icon icon="tabler:certificate" class="!size-6" />
<span class="shrink-0 line-clamp-1">Register</span> <span class="shrink-0 line-clamp-1">Register</span>
</ButtonBase> </ButtonBase>
</NuxtLink> </NuxtLink>
<NuxtLink href="/settings" v-if="identity"> <NuxtLink href="/settings" v-if="identity">
<ButtonBase @click="$emit('signIn')" theme="secondary" class="w-full !justify-start"> <ButtonBase @click="$emit('signIn')" theme="secondary" class="w-full !justify-start overflow-hidden">
<Icon icon="tabler:adjustments" class="!size-6" /> <Icon icon="tabler:adjustments" class="!size-6" />
<span class="shrink-0 line-clamp-1">Settings</span> <span class="shrink-0 line-clamp-1">Settings</span>
</ButtonBase> </ButtonBase>
@ -41,7 +41,8 @@
<h3 v-if="identity" <h3 v-if="identity"
class="font-semibold text-gray-300 text-xs uppercase opacity-0 group-hover:opacity-100 duration-200"> class="font-semibold text-gray-300 text-xs uppercase opacity-0 group-hover:opacity-100 duration-200">
Posts</h3> Posts</h3>
<ButtonBase v-if="identity" @click="compose" title="Open composer (shortcut: n)" theme="gradient" class="!justify-start"> <ButtonBase v-if="identity" @click="compose" title="Open composer (shortcut: n)" theme="gradient"
class="!justify-start overflow-hidden">
<Icon icon="tabler:writing" class="!size-6" /> <Icon icon="tabler:writing" class="!size-6" />
<span class="shrink-0 line-clamp-1">Compose</span> <span class="shrink-0 line-clamp-1">Compose</span>
<kbd class="text-xs font-semibold rounded bg-dark-500 font-mono px-1 flex flex-row ml-auto"> <kbd class="text-xs font-semibold rounded bg-dark-500 font-mono px-1 flex flex-row ml-auto">
@ -50,7 +51,8 @@
aria-hidden="true" /> aria-hidden="true" />
</kbd> </kbd>
</ButtonBase> </ButtonBase>
<ButtonBase v-if="$pwa?.needRefresh" @click="$pwa?.updateServiceWorker()" title="Update service worker" theme="primary" class="w-full !justify-start"> <ButtonBase v-if="$pwa?.needRefresh" @click="$pwa?.updateServiceWorker()" title="Update service worker"
theme="primary" class="w-full !justify-start overflow-hidden">
<Icon icon="tabler:refresh" class="!size-6" /> <Icon icon="tabler:refresh" class="!size-6" />
<span class="shrink-0 line-clamp-1">Update</span> <span class="shrink-0 line-clamp-1">Update</span>
</ButtonBase> </ButtonBase>

View file

@ -20,14 +20,14 @@
<!-- Spoiler text is it's specified --> <!-- Spoiler text is it's specified -->
<span v-if="note?.spoiler_text" class="mt-2 break-all">{{ note.spoiler_text <span v-if="note?.spoiler_text" class="mt-2 break-all">{{ note.spoiler_text
}}</span> }}</span>
<ButtonSecondary @click="collapsed = false" class="mt-4">Show content</ButtonSecondary> <Button theme="secondary" @click="collapsed = false" class="mt-4">Show content</Button>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import type { Status } from "@lysand-org/client/types"; import type { Status } from "@lysand-org/client/types";
import ButtonSecondary from "~/components/buttons/button-secondary.vue";
import Skeleton from "~/components/skeleton/Skeleton.vue"; import Skeleton from "~/components/skeleton/Skeleton.vue";
import Button from "~/packages/ui/components/buttons/button.vue";
import Attachment from "./attachment.vue"; import Attachment from "./attachment.vue";
import Note from "./note.vue"; import Note from "./note.vue";

View file

@ -18,10 +18,12 @@
</div> </div>
<div v-if="notification?.type === 'follow_request' && relationship?.requested_by" <div v-if="notification?.type === 'follow_request' && relationship?.requested_by"
class="w-full grid grid-cols-2 gap-4 p-2 "> class="w-full grid grid-cols-2 gap-4 p-2 ">
<ButtonPrimary :loading="isWorkingOnFollowRequest" @click="acceptFollowRequest"><span>Accept</span> <Button theme="primary" :loading="isWorkingOnFollowRequest"
</ButtonPrimary> @click="acceptFollowRequest"><span>Accept</span>
<ButtonSecondary :loading="isWorkingOnFollowRequest" @click="rejectFollowRequest"><span>Reject</span> </Button>
</ButtonSecondary> <Button theme="secondary" :loading="isWorkingOnFollowRequest"
@click="rejectFollowRequest"><span>Reject</span>
</Button>
</div> </div>
</div> </div>
</div> </div>
@ -30,9 +32,8 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { Notification } from "@lysand-org/client/types"; import type { Notification } from "@lysand-org/client/types";
import Avatar from "~/components/avatars/avatar.vue"; import Avatar from "~/components/avatars/avatar.vue";
import ButtonPrimary from "~/components/buttons/button-primary.vue";
import ButtonSecondary from "~/components/buttons/button-secondary.vue";
import Skeleton from "~/components/skeleton/Skeleton.vue"; import Skeleton from "~/components/skeleton/Skeleton.vue";
import Button from "~/packages/ui/components/buttons/button.vue";
import Note from "../notes/note.vue"; import Note from "../notes/note.vue";
import SmallCard from "../users/SmallCard.vue"; import SmallCard from "../users/SmallCard.vue";

View file

@ -7,20 +7,20 @@
<Avatar :src="account?.avatar" :alt="`${account?.acct}'s avatar'`" <Avatar :src="account?.avatar" :alt="`${account?.acct}'s avatar'`"
class="h-32 w-32 -mt-[4.5rem] z-10 shrink-0 rounded ring-2 ring-dark-800" /> class="h-32 w-32 -mt-[4.5rem] z-10 shrink-0 rounded ring-2 ring-dark-800" />
<ButtonSecondary v-if="account && account?.id === identity?.account?.id">Edit Profile <Button theme="secondary" v-if="account && account?.id === identity?.account?.id">Edit Profile
</ButtonSecondary> </Button>
<ButtonSecondary :loading="isLoading" @click="follow()" <Button theme="secondary" :loading="isLoading" @click="follow()"
v-if="account && account?.id !== identity?.account?.id && relationship && !relationship.following && !relationship.requested"> v-if="account && account?.id !== identity?.account?.id && relationship && !relationship.following && !relationship.requested">
<span>Follow</span> <span>Follow</span>
</ButtonSecondary> </Button>
<ButtonSecondary :loading="isLoading" @click="unfollow()" <Button theme="secondary" :loading="isLoading" @click="unfollow()"
v-if="account && account?.id !== identity?.account?.id && relationship && relationship.following"> v-if="account && account?.id !== identity?.account?.id && relationship && relationship.following">
<span>Unfollow</span> <span>Unfollow</span>
</ButtonSecondary> </Button>
<ButtonSecondary :loading="isLoading" :disabled="true" <Button theme="secondary" :loading="isLoading" :disabled="true"
v-if="account && account?.id !== identity?.account?.id && relationship && !relationship.following && relationship.requested"> v-if="account && account?.id !== identity?.account?.id && relationship && !relationship.following && relationship.requested">
<span>Requested</span> <span>Requested</span>
</ButtonSecondary> </Button>
</div> </div>
<div class="mt-2 px-4"> <div class="mt-2 px-4">
@ -100,8 +100,8 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { Account } from "@lysand-org/client/types"; import type { Account } from "@lysand-org/client/types";
import Avatar from "~/components/avatars/avatar.vue"; import Avatar from "~/components/avatars/avatar.vue";
import ButtonSecondary from "~/components/buttons/button-secondary.vue";
import Skeleton from "~/components/skeleton/Skeleton.vue"; import Skeleton from "~/components/skeleton/Skeleton.vue";
import Button from "~/packages/ui/components/buttons/button.vue";
import Badge from "./Badge.vue"; import Badge from "./Badge.vue";
const props = defineProps<{ const props = defineProps<{

View file

@ -1,6 +1,6 @@
<template> <template>
<button v-bind="$attrs" type="button" :disabled="loading" <button v-bind="$attrs" type="button" :disabled="loading"
:class="['relative isolate text-base/6 font-semibold px-[calc(theme(spacing[3.5])-1px)] py-[calc(theme(spacing[2.5])-1px)] sm:px-[calc(theme(spacing.3)-1px)] sm:py-[calc(theme(spacing[1.5])-1px)] sm:text-sm/6 focus:outline-none focus:outline focus:outline-2 focus:outline-offset-2 focus:outline-[--btn-bg] before:absolute before:inset-0 before:-z-10 before:rounded-[calc(theme(borderRadius.lg)-1px)] overflow-hidden before:shadow before:hidden after:absolute after:-z-10 after:-inset-px after:rounded-md before:disabled:shadow-none after:disabled:shadow-none text-white cursor-default rounded-md duration-200 hover:shadow disabled:opacity-70 content-none disabled:cursor-not-allowed shadow-sm bg-[--btn-bg] before:bg-[--btn-bg] after:active:bg-[--btn-hover-overlay] after:hover:bg-[--btn-hover-overlay] [&>[data-slot=icon]]:my-0.5 [&>[data-slot=icon]]:size-5 [&>[data-slot=icon]]:shrink-0 [&>[data-slot=icon]]:text-[--btn-icon] [&>[data-slot=icon]]:sm:my-1 [&>[data-slot=icon]]:sm:size-4 inline-flex items-center justify-center gap-x-3', theme && themes[theme]]"> :class="['relative isolate text-base/6 font-semibold px-[calc(theme(spacing[3.5])-1px)] py-[calc(theme(spacing[2.5])-1px)] sm:px-[calc(theme(spacing.3)-1px)] sm:py-[calc(theme(spacing[1.5])-1px)] sm:text-sm/6 focus:outline focus:outline-2 focus:outline-offset-2 focus:outline-[--btn-bg] before:absolute before:inset-0 before:-z-10 before:rounded-[calc(theme(borderRadius.lg)-1px)] before:shadow before:hidden after:absolute after:-z-10 after:-inset-px after:rounded-md before:disabled:shadow-none after:disabled:shadow-none text-white cursor-default rounded-md duration-200 hover:shadow disabled:opacity-70 content-none disabled:cursor-not-allowed shadow-sm bg-[--btn-bg] before:bg-[--btn-bg] after:active:bg-[--btn-hover-overlay] after:duration-200 after:hover:bg-[--btn-hover-overlay] [&>[data-slot=icon]]:my-0.5 [&>[data-slot=icon]]:size-5 [&>[data-slot=icon]]:shrink-0 [&>[data-slot=icon]]:text-[--btn-icon] [&>[data-slot=icon]]:sm:my-1 [&>[data-slot=icon]]:sm:size-4 inline-flex items-center justify-center gap-x-3', theme && themes[theme]]">
<div data-spinner v-if="loading" class="absolute inset-0 bg-[--btn-bg] flex items-center justify-center"> <div data-spinner v-if="loading" class="absolute inset-0 bg-[--btn-bg] flex items-center justify-center">
<iconify-icon icon="tabler:loader-2" height="none" class="animate-spin size-5" /> <iconify-icon icon="tabler:loader-2" height="none" class="animate-spin size-5" />
</div> </div>
@ -20,6 +20,8 @@ const themes = {
// Gradient: bg-gradient-to-tr from-primary-300 via-purple-300 to-indigo-400 // Gradient: bg-gradient-to-tr from-primary-300 via-purple-300 to-indigo-400
gradient: gradient:
"bg-[image:--btn-bg] before:bg-[image:--btn-bg] [--btn-border:theme(colors.primary.950/90%)] [--btn-bg:linear-gradient(to_right,theme(colors.primary.300),theme(colors.purple.300),theme(colors.indigo.400))] [--btn-hover-overlay:theme(colors.white/10%)] [--btn-icon:theme(colors.gray.100)] active:[--btn-icon:theme(colors.gray.50)] hover:[--btn-icon:theme(colors.gray.50)] after:shadow-[shadow:inset_0_1px_theme(colors.white/15%)] [&>[data-spinner]]:bg-[image:--btn-bg]", "bg-[image:--btn-bg] before:bg-[image:--btn-bg] [--btn-border:theme(colors.primary.950/90%)] [--btn-bg:linear-gradient(to_right,theme(colors.primary.300),theme(colors.purple.300),theme(colors.indigo.400))] [--btn-hover-overlay:theme(colors.white/10%)] [--btn-icon:theme(colors.gray.100)] active:[--btn-icon:theme(colors.gray.50)] hover:[--btn-icon:theme(colors.gray.50)] after:shadow-[shadow:inset_0_1px_theme(colors.white/15%)] [&>[data-spinner]]:bg-[image:--btn-bg]",
outline:
"[--btn-border:theme(colors.zinc.950/90%)] [--btn-bg:transparent] [--btn-hover-overlay:theme(colors.white/5%)] [--btn-icon:theme(colors.zinc.200)] active:[--btn-icon:theme(colors.zinc.300)] hover:[--btn-icon:theme(colors.zinc.300)] hover:ring-1 ring-white/5",
}; };
interface Props extends /* @vue-ignore */ ButtonHTMLAttributes {} interface Props extends /* @vue-ignore */ ButtonHTMLAttributes {}

View file

@ -4,26 +4,61 @@
<Button theme="primary">Click me</Button> <Button theme="primary">Click me</Button>
<Button theme="secondary">Click me</Button> <Button theme="secondary">Click me</Button>
<Button theme="gradient">Click me</Button> <Button theme="gradient">Click me</Button>
<Button theme="outline">Click me</Button>
<Button loading>Click me</Button> <Button loading>Click me</Button>
<Button theme="primary" loading>Click me</Button> <Button theme="primary" loading>Click me</Button>
<Button theme="secondary" loading>Click me</Button> <Button theme="secondary" loading>Click me</Button>
<Button theme="gradient" loading>Click me</Button> <Button theme="gradient" loading>Click me</Button>
<Button theme="outline" loading>Click me</Button>
<Button><Icon icon="tabler:certificate" />Gamer</Button> <Button>
<Button theme="primary"><Icon icon="tabler:certificate" />Gamer</Button> <Icon icon="tabler:certificate" />Gamer
<Button theme="secondary"><Icon icon="tabler:certificate" />Gamer</Button> </Button>
<Button theme="gradient"><Icon icon="tabler:certificate" />Gamer</Button> <Button theme="primary">
<Icon icon="tabler:certificate" />Gamer
</Button>
<Button theme="secondary">
<Icon icon="tabler:certificate" />Gamer
</Button>
<Button theme="gradient">
<Icon icon="tabler:certificate" />Gamer
</Button>
<Button theme="outline">
<Icon icon="tabler:certificate" />Gamer
</Button>
<Button><Icon icon="tabler:certificate" /></Button> <Button>
<Button theme="primary"><Icon icon="tabler:certificate" /></Button> <Icon icon="tabler:certificate" />
<Button theme="secondary"><Icon icon="tabler:certificate" /></Button> </Button>
<Button theme="gradient"><Icon icon="tabler:certificate" /></Button> <Button theme="primary">
<Icon icon="tabler:certificate" />
</Button>
<Button theme="secondary">
<Icon icon="tabler:certificate" />
</Button>
<Button theme="gradient">
<Icon icon="tabler:certificate" />
</Button>
<Button theme="outline">
<Icon icon="tabler:certificate" />
</Button>
<Button>Gamer<Icon icon="tabler:certificate" /></Button> <Button>Gamer
<Button theme="primary">Gamer<Icon icon="tabler:certificate" /></Button> <Icon icon="tabler:certificate" />
<Button theme="secondary">Gamer<Icon icon="tabler:certificate" /></Button> </Button>
<Button theme="gradient">Gamer<Icon icon="tabler:certificate" /></Button> <Button theme="primary">Gamer
<Icon icon="tabler:certificate" />
</Button>
<Button theme="secondary">Gamer
<Icon icon="tabler:certificate" />
</Button>
<Button theme="gradient">Gamer
<Icon icon="tabler:certificate" />
</Button>
<Button theme="outline">Gamer
<Icon icon="tabler:certificate" />
</Button>
</div> </div>
</template> </template>

View file

@ -42,13 +42,13 @@
<div class="grid md:grid-cols-2 md:[&:has(>:last-child:nth-child(1))]:grid-cols-1 gap-4 w-full"> <div class="grid md:grid-cols-2 md:[&:has(>:last-child:nth-child(1))]:grid-cols-1 gap-4 w-full">
<a v-for="provider of ssoConfig.providers" :key="provider.id" <a v-for="provider of ssoConfig.providers" :key="provider.id"
:href="issuerRedirectUrl(provider.id)"> :href="issuerRedirectUrl(provider.id)">
<ButtonSecondary class="flex flex-row w-full items-center justify-center gap-3"> <Button theme="secondary" class="flex flex-row w-full items-center justify-center gap-3">
<img crossorigin="anonymous" :src="provider.icon" :alt="`${provider.name}'s logo'`" <img crossorigin="anonymous" :src="provider.icon" :alt="`${provider.name}'s logo'`"
class="w-6 h-6" /> class="w-6 h-6" />
<div class="flex flex-col gap-0 justify-center"> <div class="flex flex-col gap-0 justify-center">
<h3 class="font-bold">{{ provider.name }}</h3> <h3 class="font-bold">{{ provider.name }}</h3>
</div> </div>
</ButtonSecondary> </Button>
</a> </a>
</div> </div>
</div> </div>
@ -58,7 +58,7 @@
here, please close this page. here, please close this page.
</p> </p>
<ButtonPrimary type="submit" class="w-full">Sign in</ButtonPrimary> <Button theme="primary" type="submit" class="w-full">Sign in</Button>
</VeeForm> </VeeForm>
</div> </div>
<div v-else class="mx-auto max-w-md"> <div v-else class="mx-auto max-w-md">
@ -100,14 +100,13 @@
import { LysandClient } from "@lysand-org/client"; import { LysandClient } from "@lysand-org/client";
import { toTypedSchema } from "@vee-validate/zod"; import { toTypedSchema } from "@vee-validate/zod";
import { z } from "zod"; import { z } from "zod";
import ButtonPrimary from "~/components/buttons/button-primary.vue";
import ButtonSecondary from "~/components/buttons/button-secondary.vue";
import FieldError from "~/components/inputs/field-error.vue"; import FieldError from "~/components/inputs/field-error.vue";
import Field from "~/components/inputs/field.vue"; import Field from "~/components/inputs/field.vue";
import LabelAndError from "~/components/inputs/label-and-error.vue"; import LabelAndError from "~/components/inputs/label-and-error.vue";
import Label from "~/components/inputs/label.vue"; import Label from "~/components/inputs/label.vue";
import PasswordInput from "~/components/inputs/password-input.vue"; import PasswordInput from "~/components/inputs/password-input.vue";
import TextInput from "~/components/inputs/text-input.vue"; import TextInput from "~/components/inputs/text-input.vue";
import Button from "~/packages/ui/components/buttons/button.vue";
const schema = toTypedSchema( const schema = toTypedSchema(
z.object({ z.object({

View file

@ -40,9 +40,9 @@
</div> </div>
<div class="flex flex-col gap-3"> <div class="flex flex-col gap-3">
<ButtonPrimary type="submit">Authorize</ButtonPrimary> <Button theme="primary" type="submit">Authorize</Button>
<NuxtLink href="/" class="w-full"> <NuxtLink href="/" class="w-full">
<ButtonSecondary class="w-full">Cancel</ButtonSecondary> <Button theme="secondary" class="w-full">Cancel</Button>
</NuxtLink> </NuxtLink>
</div> </div>
</form> </form>
@ -83,8 +83,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import ButtonPrimary from "~/components/buttons/button-primary.vue"; import Button from "~/packages/ui/components/buttons/button.vue";
import ButtonSecondary from "~/components/buttons/button-secondary.vue";
const url = useRequestURL(); const url = useRequestURL();
const params = useUrlSearchParams(); const params = useUrlSearchParams();

View file

@ -39,7 +39,7 @@
password. Make sure to put it in a password manager. password. Make sure to put it in a password manager.
</p> </p>
<ButtonPrimary type="submit" class="w-full">Reset</ButtonPrimary> <Button theme="primary" type="submit" class="w-full">Reset</Button>
</VeeForm> </VeeForm>
</div> </div>
<div v-else-if="params.success"> <div v-else-if="params.success">
@ -69,12 +69,12 @@
<script setup lang="ts"> <script setup lang="ts">
import { toTypedSchema } from "@vee-validate/zod"; import { toTypedSchema } from "@vee-validate/zod";
import { z } from "zod"; import { z } from "zod";
import ButtonPrimary from "~/components/buttons/button-primary.vue";
import FieldError from "~/components/inputs/field-error.vue"; import FieldError from "~/components/inputs/field-error.vue";
import Field from "~/components/inputs/field.vue"; import Field from "~/components/inputs/field.vue";
import LabelAndError from "~/components/inputs/label-and-error.vue"; import LabelAndError from "~/components/inputs/label-and-error.vue";
import Label from "~/components/inputs/label.vue"; import Label from "~/components/inputs/label.vue";
import PasswordInput from "~/components/inputs/password-input.vue"; import PasswordInput from "~/components/inputs/password-input.vue";
import Button from "~/packages/ui/components/buttons/button.vue";
const identity = useCurrentIdentity(); const identity = useCurrentIdentity();
identity.value = null; identity.value = null;

View file

@ -70,7 +70,7 @@
<Collapsible.Root> <Collapsible.Root>
<Collapsible.Trigger class="w-full"> <Collapsible.Trigger class="w-full">
<ButtonSecondary type="button" class="w-full">View Terms of Service</ButtonSecondary> <Button theme="secondary" class="w-full">View Terms of Service</Button>
</Collapsible.Trigger> </Collapsible.Trigger>
<Collapsible.Content <Collapsible.Content
class="prose prose-invert prose-sm p-4 ring-1 ring-white/10 bg-dark-700 rounded mt-3"> class="prose prose-invert prose-sm p-4 ring-1 ring-white/10 bg-dark-700 rounded mt-3">
@ -84,8 +84,8 @@
cannot see your password. cannot see your password.
</p> </p>
<ButtonPrimary type="submit" class="w-full" :disabled="isLoading">{{ isLoading ? "Registering..." : <Button theme="primary" type="submit" class="w-full" :disabled="isLoading">{{ isLoading ? "Registering..." :
"Register" }}</ButtonPrimary> "Register" }}</Button>
</VeeForm> </VeeForm>
</div> </div>
<div v-else> <div v-else>
@ -104,8 +104,6 @@ import { Collapsible } from "@ark-ui/vue";
import type { ResponseError } from "@lysand-org/client"; import type { ResponseError } from "@lysand-org/client";
import { toTypedSchema } from "@vee-validate/zod"; import { toTypedSchema } from "@vee-validate/zod";
import { z } from "zod"; import { z } from "zod";
import ButtonPrimary from "~/components/buttons/button-primary.vue";
import ButtonSecondary from "~/components/buttons/button-secondary.vue";
import CheckboxInput from "~/components/inputs/checkbox-input.vue"; import CheckboxInput from "~/components/inputs/checkbox-input.vue";
import FieldError from "~/components/inputs/field-error.vue"; import FieldError from "~/components/inputs/field-error.vue";
import Field from "~/components/inputs/field.vue"; import Field from "~/components/inputs/field.vue";
@ -113,6 +111,7 @@ import LabelAndError from "~/components/inputs/label-and-error.vue";
import Label from "~/components/inputs/label.vue"; import Label from "~/components/inputs/label.vue";
import PasswordInput from "~/components/inputs/password-input.vue"; import PasswordInput from "~/components/inputs/password-input.vue";
import TextInput from "~/components/inputs/text-input.vue"; import TextInput from "~/components/inputs/text-input.vue";
import Button from "~/packages/ui/components/buttons/button.vue";
const schema = toTypedSchema( const schema = toTypedSchema(
z z