mirror of
https://github.com/versia-pub/frontend.git
synced 2026-01-26 04:16:02 +01:00
style: 🎨 Format code with Biome
This commit is contained in:
parent
7ff9d2302a
commit
3627ac0ef8
17
app/app.vue
17
app/app.vue
|
|
@ -1,15 +1,13 @@
|
||||||
<template>
|
<template>
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<Component is="style">
|
<Component is="style">{{ preferences.custom_css }}</Component>
|
||||||
{{ preferences.custom_css }}
|
<NuxtPwaAssets/>
|
||||||
</Component>
|
|
||||||
<NuxtPwaAssets />
|
|
||||||
<NuxtLayout>
|
<NuxtLayout>
|
||||||
<NuxtPage />
|
<NuxtPage/>
|
||||||
</NuxtLayout>
|
</NuxtLayout>
|
||||||
<ConfirmationModal />
|
<ConfirmationModal/>
|
||||||
<!-- pointer-events-auto fixes https://github.com/unovue/shadcn-vue/issues/462 -->
|
<!-- pointer-events-auto fixes https://github.com/unovue/shadcn-vue/issues/462 -->
|
||||||
<Toaster class="pointer-events-auto" />
|
<Toaster class="pointer-events-auto"/>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -91,7 +89,10 @@ body {
|
||||||
|
|
||||||
html.theme-changing * {
|
html.theme-changing * {
|
||||||
/* Stroke and fill aren't animatable */
|
/* Stroke and fill aren't animatable */
|
||||||
transition: background-color 1s ease, border 1s ease, color 1s ease,
|
transition:
|
||||||
|
background-color 1s ease,
|
||||||
|
border 1s ease,
|
||||||
|
color 1s ease,
|
||||||
box-shadow 1s ease !important;
|
box-shadow 1s ease !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,69 +1,105 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-if="relation" class="overflow-auto max-h-72">
|
<div v-if="relation" class="overflow-auto max-h-72">
|
||||||
<Note :note="relation.note" :hide-actions="true" :small-layout="true" />
|
<Note :note="relation.note" :hide-actions="true" :small-layout="true"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<InputGroup class="p-1">
|
<InputGroup class="p-1">
|
||||||
<InputGroupAddon v-if="store.sensitive" align="block-start" class="pt-3">
|
<InputGroupAddon
|
||||||
<Input v-model:model-value="store.contentWarning" placeholder="Put your content warning here" />
|
v-if="store.sensitive"
|
||||||
|
align="block-start"
|
||||||
|
class="pt-3"
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
v-model:model-value="store.contentWarning"
|
||||||
|
placeholder="Put your content warning here"
|
||||||
|
/>
|
||||||
</InputGroupAddon>
|
</InputGroupAddon>
|
||||||
|
|
||||||
<EditorContent data-slot="input-group-control" @paste-files="uploadFiles" v-model:content="store.content"
|
<EditorContent
|
||||||
v-model:raw-content="store.rawContent" :placeholder="getRandomSplash()"
|
data-slot="input-group-control"
|
||||||
|
@paste-files="uploadFiles"
|
||||||
|
v-model:content="store.content"
|
||||||
|
v-model:raw-content="store.rawContent"
|
||||||
|
:placeholder="getRandomSplash()"
|
||||||
class=" placeholder:text-muted-foreground flex field-sizing-content min-h-58 w-full px-4 text-base disabled:opacity-50 md:text-sm flex-1 resize-none rounded-none border-0 bg-transparent py-3 shadow-none"
|
class=" placeholder:text-muted-foreground flex field-sizing-content min-h-58 w-full px-4 text-base disabled:opacity-50 md:text-sm flex-1 resize-none rounded-none border-0 bg-transparent py-3 shadow-none"
|
||||||
:disabled="store.sending" :mode="store.contentType === 'text/html' ? 'rich' : 'plain'" />
|
:disabled="store.sending"
|
||||||
|
:mode="store.contentType === 'text/html' ? 'rich' : 'plain'"
|
||||||
|
/>
|
||||||
|
|
||||||
<InputGroupAddon v-if="store.files.length > 0" align="block-end" class="overflow-x-auto *:shrink-0">
|
<InputGroupAddon
|
||||||
<Files v-model:files="store.files" :composer-key="composerKey" />
|
v-if="store.files.length > 0"
|
||||||
|
align="block-end"
|
||||||
|
class="overflow-x-auto *:shrink-0"
|
||||||
|
>
|
||||||
|
<Files v-model:files="store.files" :composer-key="composerKey"/>
|
||||||
</InputGroupAddon>
|
</InputGroupAddon>
|
||||||
|
|
||||||
<InputGroupAddon align="block-end">
|
<InputGroupAddon align="block-end">
|
||||||
<Select v-model:model-value="store.contentType">
|
<Select v-model:model-value="store.contentType">
|
||||||
<SelectTrigger as-child disable-default-classes disable-select-icon>
|
<SelectTrigger
|
||||||
|
as-child
|
||||||
|
disable-default-classes
|
||||||
|
disable-select-icon
|
||||||
|
>
|
||||||
<InputGroupButton variant="ghost" size="icon-sm">
|
<InputGroupButton variant="ghost" size="icon-sm">
|
||||||
<LetterText v-if="store.contentType === 'text/html'" />
|
<LetterText v-if="store.contentType === 'text/html'"/>
|
||||||
<Type v-else />
|
<Type v-else/>
|
||||||
</InputGroupButton>
|
</InputGroupButton>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value="text/plain">
|
<SelectItem value="text/plain">Plain text</SelectItem>
|
||||||
Plain text
|
<SelectItem value="text/html">Rich text</SelectItem>
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="text/html">
|
|
||||||
Rich text
|
|
||||||
</SelectItem>
|
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
<VisibilityPicker v-model:visibility="store.visibility">
|
<VisibilityPicker v-model:visibility="store.visibility">
|
||||||
<InputGroupButton variant="ghost" size="icon-sm" :disabled="store.relation?.type === 'edit'">
|
<InputGroupButton
|
||||||
<component :is="visibilities[store.visibility].icon" />
|
variant="ghost"
|
||||||
|
size="icon-sm"
|
||||||
|
:disabled="store.relation?.type === 'edit'"
|
||||||
|
>
|
||||||
|
<component :is="visibilities[store.visibility].icon"/>
|
||||||
</InputGroupButton>
|
</InputGroupButton>
|
||||||
</VisibilityPicker>
|
</VisibilityPicker>
|
||||||
<InputGroupButton variant="ghost" size="icon-sm" @click="fileInput?.click()">
|
<InputGroupButton
|
||||||
<FilePlus2 />
|
variant="ghost"
|
||||||
|
size="icon-sm"
|
||||||
|
@click="fileInput?.click()"
|
||||||
|
>
|
||||||
|
<FilePlus2/>
|
||||||
</InputGroupButton>
|
</InputGroupButton>
|
||||||
<Toggle size="sm" v-model="store.sensitive">
|
<Toggle size="sm" v-model="store.sensitive">
|
||||||
<TriangleAlert />
|
<TriangleAlert/>
|
||||||
</Toggle>
|
</Toggle>
|
||||||
<InputGroupText :class="['ml-auto', charactersLeft < 0 && 'text-destructive']">
|
<InputGroupText
|
||||||
|
:class="['ml-auto', charactersLeft < 0 && 'text-destructive']"
|
||||||
|
>
|
||||||
{{ charactersLeft.toLocaleString(getLocale(), {
|
{{ charactersLeft.toLocaleString(getLocale(), {
|
||||||
maximumFractionDigits: 2,
|
maximumFractionDigits: 2,
|
||||||
notation: 'compact',
|
notation: 'compact',
|
||||||
compactDisplay: 'short',
|
compactDisplay: 'short',
|
||||||
}) }}
|
}) }}
|
||||||
</InputGroupText>
|
</InputGroupText>
|
||||||
<Separator orientation="vertical" class="h-4!" />
|
<Separator orientation="vertical" class="h-4!"/>
|
||||||
<InputGroupButton variant="default" size="icon-sm" :disabled="store.sending || !store.canSend"
|
<InputGroupButton
|
||||||
@click="send">
|
variant="default"
|
||||||
<Spinner v-if="store.sending" />
|
size="icon-sm"
|
||||||
<ArrowUp v-else />
|
:disabled="store.sending || !store.canSend"
|
||||||
|
@click="send"
|
||||||
|
>
|
||||||
|
<Spinner v-if="store.sending"/>
|
||||||
|
<ArrowUp v-else/>
|
||||||
<span class="sr-only">Send</span>
|
<span class="sr-only">Send</span>
|
||||||
</InputGroupButton>
|
</InputGroupButton>
|
||||||
</InputGroupAddon>
|
</InputGroupAddon>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
|
|
||||||
<input type="file" ref="fileInput" @change="uploadFileFromEvent" class="hidden" multiple />
|
<input
|
||||||
|
type="file"
|
||||||
|
ref="fileInput"
|
||||||
|
@change="uploadFileFromEvent"
|
||||||
|
class="hidden"
|
||||||
|
multiple
|
||||||
|
>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ const relation = ref(
|
||||||
: m.brief_cool_capybara_fear()
|
: m.brief_cool_capybara_fear()
|
||||||
}}
|
}}
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
<Composer :relation="relation ?? undefined" />
|
<Composer :relation="relation ?? undefined"/>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -5,26 +5,37 @@
|
||||||
:disabled="file.uploading || file.updating"
|
:disabled="file.uploading || file.updating"
|
||||||
class="block bg-card text-card-foreground shadow-sm h-28 overflow-hidden rounded relative min-w-28 *:disabled:opacity-50"
|
class="block bg-card text-card-foreground shadow-sm h-28 overflow-hidden rounded relative min-w-28 *:disabled:opacity-50"
|
||||||
>
|
>
|
||||||
<img v-if="file.file?.type.startsWith('image/')" :src="createObjectURL(file.file)" class="object-contain h-28 w-full" :alt="file.alt" />
|
<img
|
||||||
<FileIcon v-else class="size-6 m-auto text-muted-foreground" />
|
v-if="file.file?.type.startsWith('image/')"
|
||||||
|
:src="createObjectURL(file.file)"
|
||||||
|
class="object-contain h-28 w-full"
|
||||||
|
:alt="file.alt"
|
||||||
|
>
|
||||||
|
<FileIcon v-else class="size-6 m-auto text-muted-foreground"/>
|
||||||
<Badge
|
<Badge
|
||||||
v-if="file.file && !(file.uploading || file.updating)"
|
v-if="file.file && !(file.uploading || file.updating)"
|
||||||
class="absolute bottom-1 right-1"
|
class="absolute bottom-1 right-1"
|
||||||
variant="default"
|
variant="default"
|
||||||
>{{ formatBytes(file.file.size) }}</Badge
|
|
||||||
>
|
>
|
||||||
<Spinner v-else-if="file.file" class="absolute bottom-1 right-1 size-8 p-1.5" />
|
{{ formatBytes(file.file.size) }}
|
||||||
|
</Badge>
|
||||||
|
<Spinner
|
||||||
|
v-else-if="file.file"
|
||||||
|
class="absolute bottom-1 right-1 size-8 p-1.5"
|
||||||
|
/>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent class="min-w-48">
|
<DropdownMenuContent class="min-w-48">
|
||||||
<DropdownMenuLabel v-if="file.file">{{ file.file.name }}</DropdownMenuLabel>
|
<DropdownMenuLabel v-if="file.file">
|
||||||
<DropdownMenuSeparator />
|
{{ file.file.name }}
|
||||||
|
</DropdownMenuLabel>
|
||||||
|
<DropdownMenuSeparator/>
|
||||||
<DropdownMenuItem @click="editCaption">
|
<DropdownMenuItem @click="editCaption">
|
||||||
<Captions />
|
<Captions/>
|
||||||
Add caption
|
Add caption
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator/>
|
||||||
<DropdownMenuItem @click="emit('remove')">
|
<DropdownMenuItem @click="emit('remove')">
|
||||||
<Delete />
|
<Delete/>
|
||||||
Remove
|
Remove
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<FilePreview v-for="(file, index) in files" :key="file.apiId" :file="file" @update:file="files[index] = $event" :composer-key="composerKey"
|
<FilePreview
|
||||||
@remove="files.splice(index, 1)" />
|
v-for="(file, index) in files"
|
||||||
|
:key="file.apiId"
|
||||||
|
:file="file"
|
||||||
|
@update:file="files[index] = $event"
|
||||||
|
:composer-key="composerKey"
|
||||||
|
@remove="files.splice(index, 1)"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,19 @@
|
||||||
<template>
|
<template>
|
||||||
<Select v-model:model-value="visibility">
|
<Select v-model:model-value="visibility">
|
||||||
<SelectTrigger as-child disable-default-classes disable-select-icon>
|
<SelectTrigger as-child disable-default-classes disable-select-icon>
|
||||||
<slot />
|
<slot/>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem v-for="(v, k) in visibilities" :key="k" @click="visibility = k" :value="k">
|
<SelectItem
|
||||||
<div class="flex flex-row gap-3 items-center w-full justify-between">
|
v-for="(v, k) in visibilities"
|
||||||
<component :is="v.icon" class="size-4" />
|
:key="k"
|
||||||
|
@click="visibility = k"
|
||||||
|
:value="k"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="flex flex-row gap-3 items-center w-full justify-between"
|
||||||
|
>
|
||||||
|
<component :is="v.icon" class="size-4"/>
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<span class="font-semibold">{{ v.name }}</span>
|
<span class="font-semibold">{{ v.name }}</span>
|
||||||
<span>{{ v.text }}</span>
|
<span>{{ v.text }}</span>
|
||||||
|
|
|
||||||
|
|
@ -69,30 +69,31 @@ watch(active, (value) => {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<BubbleMenu :editor="editor">
|
<BubbleMenu :editor="editor">
|
||||||
<ToggleGroup type="multiple"
|
<ToggleGroup
|
||||||
|
type="multiple"
|
||||||
v-model="active"
|
v-model="active"
|
||||||
class="bg-popover rounded-md"
|
class="bg-popover rounded-md"
|
||||||
>
|
>
|
||||||
<ToggleGroupItem value="bold">
|
<ToggleGroupItem value="bold">
|
||||||
<BoldIcon />
|
<BoldIcon/>
|
||||||
</ToggleGroupItem>
|
</ToggleGroupItem>
|
||||||
<ToggleGroupItem value="italic">
|
<ToggleGroupItem value="italic">
|
||||||
<ItalicIcon />
|
<ItalicIcon/>
|
||||||
</ToggleGroupItem>
|
</ToggleGroupItem>
|
||||||
<ToggleGroupItem value="underline">
|
<ToggleGroupItem value="underline">
|
||||||
<UnderlineIcon />
|
<UnderlineIcon/>
|
||||||
</ToggleGroupItem>
|
</ToggleGroupItem>
|
||||||
<ToggleGroupItem value="code">
|
<ToggleGroupItem value="code">
|
||||||
<CurlyBracesIcon />
|
<CurlyBracesIcon/>
|
||||||
</ToggleGroupItem>
|
</ToggleGroupItem>
|
||||||
<ToggleGroupItem value="strike">
|
<ToggleGroupItem value="strike">
|
||||||
<StrikethroughIcon />
|
<StrikethroughIcon/>
|
||||||
</ToggleGroupItem>
|
</ToggleGroupItem>
|
||||||
<ToggleGroupItem value="subscript">
|
<ToggleGroupItem value="subscript">
|
||||||
<SubscriptIcon />
|
<SubscriptIcon/>
|
||||||
</ToggleGroupItem>
|
</ToggleGroupItem>
|
||||||
<ToggleGroupItem value="superscript">
|
<ToggleGroupItem value="superscript">
|
||||||
<SuperscriptIcon />
|
<SuperscriptIcon/>
|
||||||
</ToggleGroupItem>
|
</ToggleGroupItem>
|
||||||
</ToggleGroup>
|
</ToggleGroup>
|
||||||
</BubbleMenu>
|
</BubbleMenu>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<BubbleMenu :editor="editor" />
|
<BubbleMenu :editor="editor"/>
|
||||||
<EditorContent :editor="editor"
|
<EditorContent :editor="editor" v-bind="$attrs" :class="$style.content"/>
|
||||||
v-bind="$attrs"
|
|
||||||
:class="$style.content" />
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
@ -122,7 +120,7 @@ onUnmounted(() => {
|
||||||
@apply font-bold rounded-sm text-primary-foreground bg-primary px-1 py-0.5;
|
@apply font-bold rounded-sm text-primary-foreground bg-primary px-1 py-0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tiptap .emoji>img {
|
.tiptap .emoji > img {
|
||||||
@apply h-lh align-middle inline hover:scale-110 transition-transform duration-75 ease-in-out;
|
@apply h-lh align-middle inline hover:scale-110 transition-transform duration-75 ease-in-out;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,23 @@
|
||||||
<template>
|
<template>
|
||||||
<Command class="rounded border shadow-md min-w-[200px] h-fit not-prose" :selected-value="emojis[selectedIndex]?.id">
|
<Command
|
||||||
|
class="rounded border shadow-md min-w-[200px] h-fit not-prose"
|
||||||
|
:selected-value="emojis[selectedIndex]?.id"
|
||||||
|
>
|
||||||
<CommandList>
|
<CommandList>
|
||||||
<CommandEmpty>No results found.</CommandEmpty>
|
<CommandEmpty>No results found.</CommandEmpty>
|
||||||
<CommandGroup class="emojis-group" heading="Emojis">
|
<CommandGroup class="emojis-group" heading="Emojis">
|
||||||
<CommandItem :value="emoji.id" v-for="emoji, index in emojis" :key="emoji.id" @click="selectItem(index)" class="scroll-m-10">
|
<CommandItem
|
||||||
<img class="h-[1lh] align-middle inline hover:scale-110 transition-transform duration-75 ease-in-out" :src="emoji.url" :title="emoji.shortcode" />
|
:value="emoji.id"
|
||||||
|
v-for="emoji, index in emojis"
|
||||||
|
:key="emoji.id"
|
||||||
|
@click="selectItem(index)"
|
||||||
|
class="scroll-m-10"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
class="h-[1lh] align-middle inline hover:scale-110 transition-transform duration-75 ease-in-out"
|
||||||
|
:src="emoji.url"
|
||||||
|
:title="emoji.shortcode"
|
||||||
|
>
|
||||||
<span>{{ emoji.shortcode }}</span>
|
<span>{{ emoji.shortcode }}</span>
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
</CommandGroup>
|
</CommandGroup>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,26 @@
|
||||||
<template>
|
<template>
|
||||||
<Command class="rounded border shadow-md min-w-[200px] h-fit not-prose" :selected-value="items[selectedIndex]?.key">
|
<Command
|
||||||
|
class="rounded border shadow-md min-w-[200px] h-fit not-prose"
|
||||||
|
:selected-value="items[selectedIndex]?.key"
|
||||||
|
>
|
||||||
<CommandList>
|
<CommandList>
|
||||||
<CommandEmpty>No results found.</CommandEmpty>
|
<CommandEmpty>No results found.</CommandEmpty>
|
||||||
<CommandGroup class="mentions-group" heading="Users">
|
<CommandGroup class="mentions-group" heading="Users">
|
||||||
<CommandItem :value="user.key" v-for="user, index in items" :key="user.key" @click="selectItem(index)" class="scroll-m-10">
|
<CommandItem
|
||||||
<Avatar class="size-4" :src="user.value.avatar" :name="user.value.display_name" />
|
:value="user.key"
|
||||||
<span v-render-emojis="user.value.emojis">{{ user.value.display_name }}</span>
|
v-for="user, index in items"
|
||||||
|
:key="user.key"
|
||||||
|
@click="selectItem(index)"
|
||||||
|
class="scroll-m-10"
|
||||||
|
>
|
||||||
|
<Avatar
|
||||||
|
class="size-4"
|
||||||
|
:src="user.value.avatar"
|
||||||
|
:name="user.value.display_name"
|
||||||
|
/>
|
||||||
|
<span v-render-emojis="user.value.emojis"
|
||||||
|
>{{ user.value.display_name }}</span
|
||||||
|
>
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
</CommandGroup>
|
</CommandGroup>
|
||||||
</CommandList>
|
</CommandList>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<Alert>
|
<Alert>
|
||||||
<AlertTitle>{{ m.fine_arable_lemming_fold() }}</AlertTitle>
|
<AlertTitle>{{ m.fine_arable_lemming_fold() }}</AlertTitle>
|
||||||
<AlertDescription>
|
<AlertDescription>{{ m.petty_honest_fish_stir() }}</AlertDescription>
|
||||||
{{ m.petty_honest_fish_stir() }}
|
|
||||||
</AlertDescription>
|
|
||||||
</Alert>
|
</Alert>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<Alert>
|
<Alert>
|
||||||
<AlertTitle>{{ m.empty_awful_lark_dart() }}</AlertTitle>
|
<AlertTitle>{{ m.empty_awful_lark_dart() }}</AlertTitle>
|
||||||
<AlertDescription>
|
<AlertDescription>{{ m.clean_even_mayfly_tap() }}</AlertDescription>
|
||||||
{{ m.clean_even_mayfly_tap() }}
|
|
||||||
</AlertDescription>
|
|
||||||
</Alert>
|
</Alert>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<Alert>
|
<Alert>
|
||||||
<AlertTitle>{{ m.steep_suave_fish_snap() }}</AlertTitle>
|
<AlertTitle>{{ m.steep_suave_fish_snap() }}</AlertTitle>
|
||||||
<AlertDescription>
|
<AlertDescription>{{ m.muddy_bland_shark_accept() }}</AlertDescription>
|
||||||
{{ m.muddy_bland_shark_accept() }}
|
|
||||||
</AlertDescription>
|
|
||||||
</Alert>
|
</Alert>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,23 +3,33 @@ import SquarePattern from "../graphics/square-pattern.vue";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="grid min-h-screen place-items-center px-6 py-24 sm:py-32 lg:px-8 fixed inset-0 z-[1000000] bg-dark-900">
|
<div
|
||||||
<SquarePattern />
|
class="grid min-h-screen place-items-center px-6 py-24 sm:py-32 lg:px-8 fixed inset-0 z-[1000000] bg-dark-900"
|
||||||
|
>
|
||||||
|
<SquarePattern/>
|
||||||
<div class="prose prose-invert max-w-lg">
|
<div class="prose prose-invert max-w-lg">
|
||||||
<h1 class="mt-4 text-3xl font-bold tracking-tight text-gray-100 sm:text-5xl">JavaScript is disabled
|
<h1
|
||||||
|
class="mt-4 text-3xl font-bold tracking-tight text-gray-100 sm:text-5xl"
|
||||||
|
>
|
||||||
|
JavaScript is disabled
|
||||||
</h1>
|
</h1>
|
||||||
<p class="mt-6 text-base leading-7 text-gray-400">
|
<p class="mt-6 text-base leading-7 text-gray-400">
|
||||||
This website requires JavaScript to function properly. Please enable JavaScript in your browser
|
This website requires JavaScript to function properly. Please
|
||||||
settings.
|
enable JavaScript in your browser settings.
|
||||||
</p>
|
</p>
|
||||||
<p class="mt-6 text-base leading-7 text-gray-400">
|
<p class="mt-6 text-base leading-7 text-gray-400">
|
||||||
If you are using a browser that does not support JavaScript, please consider using a modern browser
|
If you are using a browser that does not support JavaScript,
|
||||||
like <a href="https://www.mozilla.org/firefox/new/" class="underline">Firefox</a> or <a
|
please consider using a modern browser like
|
||||||
href="https://www.google.com/chrome/" class="underline">Chrome</a>.
|
<a href="https://www.mozilla.org/firefox/new/" class="underline"
|
||||||
|
>Firefox</a
|
||||||
|
>or <a href="https://www.google.com/chrome/" class="underline"
|
||||||
|
>Chrome</a
|
||||||
|
>.
|
||||||
</p>
|
</p>
|
||||||
<p class="mt-6 text-base leading-7 text-gray-400">
|
<p class="mt-6 text-base leading-7 text-gray-400">
|
||||||
This application does not track you, collect user data, use cookies of any kind or send requests to
|
This application does not track you, collect user data, use
|
||||||
servers outside of your account's instance.
|
cookies of any kind or send requests to servers outside of your
|
||||||
|
account's instance.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,23 @@
|
||||||
<template>
|
<template>
|
||||||
<Card>
|
<Card>
|
||||||
<FormItem class="grid grid-cols-[minmax(0,1fr)_auto] items-center gap-2">
|
<FormItem
|
||||||
|
class="grid grid-cols-[minmax(0,1fr)_auto] items-center gap-2"
|
||||||
|
>
|
||||||
<CardHeader class="flex flex-col gap-1.5 p-0">
|
<CardHeader class="flex flex-col gap-1.5 p-0">
|
||||||
<FormLabel class="font-semibold tracking-tight" :as="CardTitle">
|
<FormLabel class="font-semibold tracking-tight" :as="CardTitle">
|
||||||
{{ title }}
|
{{ title }}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormDescription class="text-xs leading-none" v-if="description">
|
<FormDescription
|
||||||
|
class="text-xs leading-none"
|
||||||
|
v-if="description"
|
||||||
|
>
|
||||||
{{ description }}
|
{{ description }}
|
||||||
</FormDescription>
|
</FormDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<slot />
|
<slot/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</Card>
|
</Card>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>
|
<FormLabel>{{ title }}</FormLabel>
|
||||||
{{ title }}
|
|
||||||
</FormLabel>
|
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<slot />
|
<slot/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormDescription v-if="description">
|
<FormDescription v-if="description">{{ description }}</FormDescription>
|
||||||
{{ description }}
|
<FormMessage/>
|
||||||
</FormDescription>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<Card class="flex items-center justify-center">
|
<Card class="flex items-center justify-center">
|
||||||
<Loader class="size-6 animate-spin" />
|
<Loader class="size-6 animate-spin"/>
|
||||||
</Card>
|
</Card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,34 @@
|
||||||
<template>
|
<template>
|
||||||
<svg class="absolute inset-x-0 top-0 h-full w-full stroke-primary/[0.07] [mask-image:radial-gradient(100%_100%_at_top_right,var(--primary-foreground),transparent)] pointer-events-none"
|
<svg
|
||||||
aria-hidden="true">
|
class="absolute inset-x-0 top-0 h-full w-full stroke-primary/[0.07] [mask-image:radial-gradient(100%_100%_at_top_right,var(--primary-foreground),transparent)] pointer-events-none"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
<defs>
|
<defs>
|
||||||
<pattern id="983e3e4c-de6d-4c3f-8d64-b9761d1534cc" width="200" height="200" x="50%" y="-1"
|
<pattern
|
||||||
patternUnits="userSpaceOnUse">
|
id="983e3e4c-de6d-4c3f-8d64-b9761d1534cc"
|
||||||
|
width="200"
|
||||||
|
height="200"
|
||||||
|
x="50%"
|
||||||
|
y="-1"
|
||||||
|
patternUnits="userSpaceOnUse"
|
||||||
|
>
|
||||||
<path d="M.5 200V.5H200" fill="none"></path>
|
<path d="M.5 200V.5H200" fill="none"></path>
|
||||||
</pattern>
|
</pattern>
|
||||||
</defs><svg x="50%" y="-1" class="overflow-visible fill-primary/[0.03]">
|
</defs>
|
||||||
<path d="M-200 0h201v201h-201Z M600 0h201v201h-201Z M-400 600h201v201h-201Z M200 800h201v201h-201Z"
|
<svg x="50%" y="-1" class="overflow-visible fill-primary/[0.03]">
|
||||||
stroke-width="0"></path>
|
<path
|
||||||
|
d="M-200 0h201v201h-201Z M600 0h201v201h-201Z M-400 600h201v201h-201Z M200 800h201v201h-201Z"
|
||||||
|
stroke-width="0"
|
||||||
|
></path>
|
||||||
</svg>
|
</svg>
|
||||||
<rect width="100%" height="100%" stroke-width="0" fill="url(#983e3e4c-de6d-4c3f-8d64-b9761d1534cc)"></rect>
|
<rect
|
||||||
|
width="100%"
|
||||||
|
height="100%"
|
||||||
|
stroke-width="0"
|
||||||
|
fill="url(#983e3e4c-de6d-4c3f-8d64-b9761d1534cc)"
|
||||||
|
></rect>
|
||||||
</svg>
|
</svg>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<Card class="grid grid-cols-[auto_1fr] gap-2">
|
<Card class="grid grid-cols-[auto_1fr] gap-2">
|
||||||
<Avatar :src="instance.thumbnail?.url ??
|
<Avatar
|
||||||
|
:src="instance.thumbnail?.url ??
|
||||||
'https://cdn.versia.pub/branding/icon.svg'
|
'https://cdn.versia.pub/branding/icon.svg'
|
||||||
" :name="instance.title" />
|
"
|
||||||
|
:name="instance.title"
|
||||||
|
/>
|
||||||
<div class="grid text-sm leading-tight *:line-clamp-1">
|
<div class="grid text-sm leading-tight *:line-clamp-1">
|
||||||
<span class="truncate font-semibold">
|
<span class="truncate font-semibold">
|
||||||
{{
|
{{
|
||||||
|
|
@ -14,7 +17,6 @@
|
||||||
instance.versia_version || instance.version
|
instance.versia_version || instance.version
|
||||||
}}
|
}}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<h1 class="line-clamp-1 text-sm font-semibold col-span-2">
|
<h1 class="line-clamp-1 text-sm font-semibold col-span-2">
|
||||||
{{
|
{{
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ const inputValue = ref<string>("");
|
||||||
<template>
|
<template>
|
||||||
<Dialog>
|
<Dialog>
|
||||||
<DialogTrigger :as-child="true">
|
<DialogTrigger :as-child="true">
|
||||||
<slot />
|
<slot/>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent class="sm:max-w-[425px]">
|
<DialogContent class="sm:max-w-[425px]">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
|
|
@ -40,17 +40,35 @@ const inputValue = ref<string>("");
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<div v-if="modalOptions.inputType === 'text'" class="grid gap-4 py-4">
|
<div
|
||||||
|
v-if="modalOptions.inputType === 'text'"
|
||||||
|
class="grid gap-4 py-4"
|
||||||
|
>
|
||||||
<div class="grid grid-cols-4 items-center gap-4">
|
<div class="grid grid-cols-4 items-center gap-4">
|
||||||
<Label for="confirmInput" class="text-right">{{ m.mean_mean_badger_inspire() }}</Label>
|
<Label for="confirmInput" class="text-right">
|
||||||
<Input id="confirmInput" v-model="inputValue" class="col-span-3" />
|
{{ m.mean_mean_badger_inspire() }}
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
id="confirmInput"
|
||||||
|
v-model="inputValue"
|
||||||
|
class="col-span-3"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="modalOptions.inputType === 'textarea'" class="grid gap-4 py-4">
|
<div
|
||||||
|
v-else-if="modalOptions.inputType === 'textarea'"
|
||||||
|
class="grid gap-4 py-4"
|
||||||
|
>
|
||||||
<div class="grid grid-cols-4 items-center gap-4">
|
<div class="grid grid-cols-4 items-center gap-4">
|
||||||
<Label for="confirmTextarea" class="text-right">{{ m.mean_mean_badger_inspire() }}</Label>
|
<Label for="confirmTextarea" class="text-right">
|
||||||
<Textarea id="confirmTextarea" v-model="inputValue" class="col-span-3" />
|
{{ m.mean_mean_badger_inspire() }}
|
||||||
|
</Label>
|
||||||
|
<Textarea
|
||||||
|
id="confirmTextarea"
|
||||||
|
v-model="inputValue"
|
||||||
|
class="col-span-3"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -58,10 +76,12 @@ const inputValue = ref<string>("");
|
||||||
<Button variant="outline" @click="() => emit('cancel')">
|
<Button variant="outline" @click="() => emit('cancel')">
|
||||||
{{ modalOptions.cancelText }}
|
{{ modalOptions.cancelText }}
|
||||||
</Button>
|
</Button>
|
||||||
<Button @click="() => emit('confirm', {
|
<Button
|
||||||
|
@click="() => emit('confirm', {
|
||||||
confirmed: true,
|
confirmed: true,
|
||||||
value: inputValue,
|
value: inputValue,
|
||||||
})">
|
})"
|
||||||
|
>
|
||||||
{{ modalOptions.confirmText }}
|
{{ modalOptions.confirmText }}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,11 @@ const isValid = ref(false);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<AlertDialog :key="String(isOpen)" :open="isOpen" @update:open="isOpen = false">
|
<AlertDialog
|
||||||
|
:key="String(isOpen)"
|
||||||
|
:open="isOpen"
|
||||||
|
@update:open="isOpen = false"
|
||||||
|
>
|
||||||
<AlertDialogContent class="sm:max-w-[425px] flex flex-col">
|
<AlertDialogContent class="sm:max-w-[425px] flex flex-col">
|
||||||
<AlertDialogHeader>
|
<AlertDialogHeader>
|
||||||
<AlertDialogTitle>{{ modalOptions.title }}</AlertDialogTitle>
|
<AlertDialogTitle>{{ modalOptions.title }}</AlertDialogTitle>
|
||||||
|
|
@ -84,11 +88,23 @@ const isValid = ref(false);
|
||||||
</AlertDialogDescription>
|
</AlertDialogDescription>
|
||||||
</AlertDialogHeader>
|
</AlertDialogHeader>
|
||||||
|
|
||||||
<Input v-if="modalOptions.inputType === 'text'" v-model="inputValue" />
|
<Input
|
||||||
|
v-if="modalOptions.inputType === 'text'"
|
||||||
|
v-model="inputValue"
|
||||||
|
/>
|
||||||
|
|
||||||
<UrlInput v-if="modalOptions.inputType === 'url'" v-model="inputValue" placeholder="google.com" v-model:is-valid="isValid" />
|
<UrlInput
|
||||||
|
v-if="modalOptions.inputType === 'url'"
|
||||||
|
v-model="inputValue"
|
||||||
|
placeholder="google.com"
|
||||||
|
v-model:is-valid="isValid"
|
||||||
|
/>
|
||||||
|
|
||||||
<Textarea v-else-if="modalOptions.inputType === 'textarea'" v-model="inputValue" rows="6" />
|
<Textarea
|
||||||
|
v-else-if="modalOptions.inputType === 'textarea'"
|
||||||
|
v-model="inputValue"
|
||||||
|
rows="6"
|
||||||
|
/>
|
||||||
|
|
||||||
<AlertDialogFooter class="w-full">
|
<AlertDialogFooter class="w-full">
|
||||||
<AlertDialogCancel :as-child="true">
|
<AlertDialogCancel :as-child="true">
|
||||||
|
|
@ -97,7 +113,10 @@ const isValid = ref(false);
|
||||||
</Button>
|
</Button>
|
||||||
</AlertDialogCancel>
|
</AlertDialogCancel>
|
||||||
<AlertDialogAction :as-child="true">
|
<AlertDialogAction :as-child="true">
|
||||||
<Button @click="handleConfirm" :disabled="!isValid && modalOptions.inputType === 'url'">
|
<Button
|
||||||
|
@click="handleConfirm"
|
||||||
|
:disabled="!isValid && modalOptions.inputType === 'url'"
|
||||||
|
>
|
||||||
{{ modalOptions.confirmText }}
|
{{ modalOptions.confirmText }}
|
||||||
</Button>
|
</Button>
|
||||||
</AlertDialogAction>
|
</AlertDialogAction>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<DrawerContent class="flex flex-col gap-2 px-4 mb-4 [&>:nth-child(2)]:mt-4">
|
<DrawerContent class="flex flex-col gap-2 px-4 mb-4 [&>:nth-child(2)]:mt-4">
|
||||||
<slot />
|
<slot/>
|
||||||
</DrawerContent>
|
</DrawerContent>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
class="fixed md:hidden bottom-0 inset-x-0 border-t h-16 bg-background z-10 flex items-center justify-around *:h-full *:w-full gap-6 px-4 py-2 [&>a>svg]:size-5 [&>button>svg]:size-5"
|
class="fixed md:hidden bottom-0 inset-x-0 border-t h-16 bg-background z-10 flex items-center justify-around *:h-full *:w-full gap-6 px-4 py-2 [&>a>svg]:size-5 [&>button>svg]:size-5"
|
||||||
>
|
>
|
||||||
<Button :as="NuxtLink" href="/" variant="ghost" size="icon">
|
<Button :as="NuxtLink" href="/" variant="ghost" size="icon">
|
||||||
<Home />
|
<Home/>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
:as="NuxtLink"
|
:as="NuxtLink"
|
||||||
|
|
@ -11,10 +11,10 @@
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="icon"
|
||||||
>
|
>
|
||||||
<Bell />
|
<Bell/>
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="ghost" size="icon">
|
<Button variant="ghost" size="icon">
|
||||||
<User />
|
<User/>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="default"
|
variant="default"
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
:title="m.salty_aloof_turkey_nudge()"
|
:title="m.salty_aloof_turkey_nudge()"
|
||||||
@click="useEvent('composer:open')"
|
@click="useEvent('composer:open')"
|
||||||
>
|
>
|
||||||
<Pen />
|
<Pen/>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<Tabs v-model:model-value="current">
|
<Tabs v-model:model-value="current">
|
||||||
<TabsList>
|
<TabsList>
|
||||||
<TabsTrigger v-for="timeline in timelines.filter(
|
<TabsTrigger
|
||||||
|
v-for="timeline in timelines.filter(
|
||||||
i => i.requiresLogin ? authStore.isSignedIn : true,
|
i => i.requiresLogin ? authStore.isSignedIn : true,
|
||||||
)" :key="timeline.value" :value="timeline.value" :as="NuxtLink" :href="timeline.url">
|
)"
|
||||||
|
:key="timeline.value"
|
||||||
|
:value="timeline.value"
|
||||||
|
:as="NuxtLink"
|
||||||
|
:href="timeline.url"
|
||||||
|
>
|
||||||
{{ timeline.name }}
|
{{ timeline.name }}
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<Button variant="ghost" class="max-w-14 w-full" size="sm">
|
<Button variant="ghost" class="max-w-14 w-full" size="sm">
|
||||||
<component :is="icon" class="size-4" />
|
<component :is="icon" class="size-4"/>
|
||||||
<slot />
|
<slot/>
|
||||||
</Button>
|
</Button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,58 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-row w-full max-w-sm items-stretch justify-between">
|
<div class="flex flex-row w-full max-w-sm items-stretch justify-between">
|
||||||
<ActionButton :icon="Reply" @click="emit('reply')" :title="m.drab_tense_turtle_comfort()" :disabled="!authStore.isSignedIn">
|
<ActionButton
|
||||||
|
:icon="Reply"
|
||||||
|
@click="emit('reply')"
|
||||||
|
:title="m.drab_tense_turtle_comfort()"
|
||||||
|
:disabled="!authStore.isSignedIn"
|
||||||
|
>
|
||||||
{{ numberFormat(replyCount) }}
|
{{ numberFormat(replyCount) }}
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
<ActionButton :icon="Heart" @click="liked ? unlike() : like()" :title="liked ? m.vexed_fluffy_clownfish_dance() : m.royal_close_samuel_scold()" :disabled="!authStore.isSignedIn" :class="liked && '*:fill-red-600 *:text-red-600'">
|
<ActionButton
|
||||||
|
:icon="Heart"
|
||||||
|
@click="liked ? unlike() : like()"
|
||||||
|
:title="liked ? m.vexed_fluffy_clownfish_dance() : m.royal_close_samuel_scold()"
|
||||||
|
:disabled="!authStore.isSignedIn"
|
||||||
|
:class="liked && '*:fill-red-600 *:text-red-600'"
|
||||||
|
>
|
||||||
{{ numberFormat(likeCount) }}
|
{{ numberFormat(likeCount) }}
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
<ActionButton :icon="Repeat" @click="reblogged ? unreblog() : reblog()" :title="reblogged ? m.lime_neat_ox_stab() : m.aware_helpful_marlin_drop()" :disabled="!authStore.isSignedIn" :class="reblogged && '*:text-green-600'">
|
<ActionButton
|
||||||
|
:icon="Repeat"
|
||||||
|
@click="reblogged ? unreblog() : reblog()"
|
||||||
|
:title="reblogged ? m.lime_neat_ox_stab() : m.aware_helpful_marlin_drop()"
|
||||||
|
:disabled="!authStore.isSignedIn"
|
||||||
|
:class="reblogged && '*:text-green-600'"
|
||||||
|
>
|
||||||
{{ numberFormat(reblogCount) }}
|
{{ numberFormat(reblogCount) }}
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
<ActionButton :icon="Quote" @click="emit('quote')" :title="m.true_shy_jackal_drip()" :disabled="!authStore.isSignedIn" />
|
<ActionButton
|
||||||
|
:icon="Quote"
|
||||||
|
@click="emit('quote')"
|
||||||
|
:title="m.true_shy_jackal_drip()"
|
||||||
|
:disabled="!authStore.isSignedIn"
|
||||||
|
/>
|
||||||
<Picker @pick="react">
|
<Picker @pick="react">
|
||||||
<ActionButton :icon="Smile" :title="m.bald_cool_kangaroo_jump()" :disabled="!authStore.isSignedIn" />
|
<ActionButton
|
||||||
|
:icon="Smile"
|
||||||
|
:title="m.bald_cool_kangaroo_jump()"
|
||||||
|
:disabled="!authStore.isSignedIn"
|
||||||
|
/>
|
||||||
</Picker>
|
</Picker>
|
||||||
<Menu :api-note-string="apiNoteString" :url="url" :remote-url="remoteUrl" :is-remote="isRemote" :author-id="authorId" @edit="emit('edit')" :note-id="noteId" @delete="emit('delete')">
|
<Menu
|
||||||
<ActionButton :icon="Ellipsis" :title="m.busy_merry_cowfish_absorb()" />
|
:api-note-string="apiNoteString"
|
||||||
|
:url="url"
|
||||||
|
:remote-url="remoteUrl"
|
||||||
|
:is-remote="isRemote"
|
||||||
|
:author-id="authorId"
|
||||||
|
@edit="emit('edit')"
|
||||||
|
:note-id="noteId"
|
||||||
|
@delete="emit('delete')"
|
||||||
|
>
|
||||||
|
<ActionButton
|
||||||
|
:icon="Ellipsis"
|
||||||
|
:title="m.busy_merry_cowfish_absorb()"
|
||||||
|
/>
|
||||||
</Menu>
|
</Menu>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,17 @@
|
||||||
<template>
|
<template>
|
||||||
<ImageAttachment v-if="attachment.type === 'image'" :attachment="attachment" />
|
<ImageAttachment
|
||||||
<VideoAttachment v-else-if="attachment.type === 'video' || attachment.type === 'gifv'" :attachment="attachment" />
|
v-if="attachment.type === 'image'"
|
||||||
<AudioAttachment v-else-if="attachment.type === 'audio'" :attachment="attachment" />
|
:attachment="attachment"
|
||||||
<FileAttachment v-else :attachment="attachment" />
|
/>
|
||||||
|
<VideoAttachment
|
||||||
|
v-else-if="attachment.type === 'video' || attachment.type === 'gifv'"
|
||||||
|
:attachment="attachment"
|
||||||
|
/>
|
||||||
|
<AudioAttachment
|
||||||
|
v-else-if="attachment.type === 'audio'"
|
||||||
|
:attachment="attachment"
|
||||||
|
/>
|
||||||
|
<FileAttachment v-else :attachment="attachment"/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,13 @@
|
||||||
<template>
|
<template>
|
||||||
<!-- [&:has(>:last-child:nth-child(1))] means "when this element has 1 child" -->
|
<!-- [&:has(>:last-child:nth-child(1))] means "when this element has 1 child" -->
|
||||||
<div class="grid gap-4 grid-cols-2 *:max-h-56 [&:has(>:last-child:nth-child(1))]:grid-cols-1 sm:[&:has(>:last-child:nth-child(1))>*]:max-h-72">
|
<div
|
||||||
<Attachment v-for="attachment in attachments" :key="attachment.id" :attachment="attachment" />
|
class="grid gap-4 grid-cols-2 *:max-h-56 [&:has(>:last-child:nth-child(1))]:grid-cols-1 sm:[&:has(>:last-child:nth-child(1))>*]:max-h-72"
|
||||||
|
>
|
||||||
|
<Attachment
|
||||||
|
v-for="attachment in attachments"
|
||||||
|
:key="attachment.id"
|
||||||
|
:attachment="attachment"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,22 @@
|
||||||
<template>
|
<template>
|
||||||
<Dialog>
|
<Dialog>
|
||||||
<Card class="w-full h-full overflow-hidden relative p-0 *:first:w-full *:first:h-full *:first:object-contain *:first:bg-muted/20">
|
<Card
|
||||||
|
class="w-full h-full overflow-hidden relative p-0 *:first:w-full *:first:h-full *:first:object-contain *:first:bg-muted/20"
|
||||||
|
>
|
||||||
<DialogTrigger v-if="lightbox" :as-child="true">
|
<DialogTrigger v-if="lightbox" :as-child="true">
|
||||||
<slot />
|
<slot/>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<slot v-else />
|
<slot v-else/>
|
||||||
<!-- Alt text viewer -->
|
<!-- Alt text viewer -->
|
||||||
<Popover v-if="attachment.description">
|
<Popover v-if="attachment.description">
|
||||||
<div class="absolute top-0 right-0 p-2">
|
<div class="absolute top-0 right-0 p-2">
|
||||||
<PopoverTrigger :as-child="true">
|
<PopoverTrigger :as-child="true">
|
||||||
<Button variant="outline" size="icon" title="View alt text">
|
<Button
|
||||||
<Captions />
|
variant="outline"
|
||||||
|
size="icon"
|
||||||
|
title="View alt text"
|
||||||
|
>
|
||||||
|
<Captions/>
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -19,26 +25,42 @@
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
</Card>
|
</Card>
|
||||||
<DialogContent :hide-close="true"
|
<DialogContent
|
||||||
class="duration-200 bg-transparent border-none overflow-hidden !animate-none gap-6 w-screen h-screen !max-w-none">
|
:hide-close="true"
|
||||||
|
class="duration-200 bg-transparent border-none overflow-hidden !animate-none gap-6 w-screen h-screen !max-w-none"
|
||||||
|
>
|
||||||
<div class="grid grid-rows-[auto_1fr_auto]">
|
<div class="grid grid-rows-[auto_1fr_auto]">
|
||||||
<div class="flex flex-row gap-2 w-full">
|
<div class="flex flex-row gap-2 w-full">
|
||||||
<DialogTitle class="sr-only">{{ attachment.type }}</DialogTitle>
|
<DialogTitle class="sr-only">
|
||||||
<Button as="a" :href="attachment?.url" target="_blank" :download="true" variant="outline" size="icon"
|
{{ attachment.type }}
|
||||||
class="ml-auto">
|
</DialogTitle>
|
||||||
<Download />
|
<Button
|
||||||
|
as="a"
|
||||||
|
:href="attachment?.url"
|
||||||
|
target="_blank"
|
||||||
|
:download="true"
|
||||||
|
variant="outline"
|
||||||
|
size="icon"
|
||||||
|
class="ml-auto"
|
||||||
|
>
|
||||||
|
<Download/>
|
||||||
</Button>
|
</Button>
|
||||||
<DialogClose :as-child="true">
|
<DialogClose :as-child="true">
|
||||||
<Button variant="outline" size="icon">
|
<Button variant="outline" size="icon">
|
||||||
<X />
|
<X/>
|
||||||
</Button>
|
</Button>
|
||||||
</DialogClose>
|
</DialogClose>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center justify-center overflow-hidden *:max-h-[80vh] *:max-w-[80vw] *:w-full *:h-full *:object-contain">
|
<div
|
||||||
<slot />
|
class="flex items-center justify-center overflow-hidden *:max-h-[80vh] *:max-w-[80vw] *:w-full *:h-full *:object-contain"
|
||||||
|
>
|
||||||
|
<slot/>
|
||||||
</div>
|
</div>
|
||||||
<DialogDescription class="flex items-center justify-center">
|
<DialogDescription class="flex items-center justify-center">
|
||||||
<Card v-if="attachment.description" class="max-w-md max-h-48 overflow-auto text-sm">
|
<Card
|
||||||
|
v-if="attachment.description"
|
||||||
|
class="max-w-md max-h-48 overflow-auto text-sm"
|
||||||
|
>
|
||||||
<p>{{ attachment.description }}</p>
|
<p>{{ attachment.description }}</p>
|
||||||
</Card>
|
</Card>
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
<template>
|
<template>
|
||||||
<Base :attachment="attachment">
|
<AttachmentBase :attachment="attachment">
|
||||||
<audio :src="attachment.url" :alt="attachment.description ?? undefined" controls />
|
<audio
|
||||||
</Base>
|
:src="attachment.url"
|
||||||
|
:alt="attachment.description ?? undefined"
|
||||||
|
controls
|
||||||
|
/>
|
||||||
|
</AttachmentBase>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { Attachment } from "@versia/client/schemas";
|
import type { Attachment } from "@versia/client/schemas";
|
||||||
import type { z } from "zod";
|
import type { z } from "zod";
|
||||||
import Base from "./base.vue";
|
import AttachmentBase from "./attachment-base.vue";
|
||||||
|
|
||||||
const { attachment } = defineProps<{
|
const { attachment } = defineProps<{
|
||||||
attachment: z.infer<typeof Attachment>;
|
attachment: z.infer<typeof Attachment>;
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,19 @@
|
||||||
<template>
|
<template>
|
||||||
<Base :attachment="attachment" lightbox>
|
<AttachmentBase :attachment="attachment" lightbox>
|
||||||
<div class="flex flex-col items-center justify-center min-h-48 text-sm gap-2">
|
<div
|
||||||
<File class="size-12" />
|
class="flex flex-col items-center justify-center min-h-48 text-sm gap-2"
|
||||||
|
>
|
||||||
|
<File class="size-12"/>
|
||||||
<span>File attachment</span>
|
<span>File attachment</span>
|
||||||
</div>
|
</div>
|
||||||
</Base>
|
</AttachmentBase>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { Attachment } from "@versia/client/schemas";
|
import type { Attachment } from "@versia/client/schemas";
|
||||||
import { File } from "lucide-vue-next";
|
import { File } from "lucide-vue-next";
|
||||||
import type { z } from "zod";
|
import type { z } from "zod";
|
||||||
import Base from "./base.vue";
|
import AttachmentBase from "./attachment-base.vue";
|
||||||
|
|
||||||
const { attachment } = defineProps<{
|
const { attachment } = defineProps<{
|
||||||
attachment: z.infer<typeof Attachment>;
|
attachment: z.infer<typeof Attachment>;
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
<template>
|
<template>
|
||||||
<Base :attachment="attachment" lightbox>
|
<AttachmentBase :attachment="attachment" lightbox>
|
||||||
<img :src="attachment.url" :alt="attachment.description ?? undefined" />
|
<img :src="attachment.url" :alt="attachment.description ?? undefined">
|
||||||
</Base>
|
</AttachmentBase>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { Attachment } from "@versia/client/schemas";
|
import type { Attachment } from "@versia/client/schemas";
|
||||||
import type { z } from "zod";
|
import type { z } from "zod";
|
||||||
import Base from "./base.vue";
|
import AttachmentBase from "./attachment-base.vue";
|
||||||
|
|
||||||
const { attachment } = defineProps<{
|
const { attachment } = defineProps<{
|
||||||
attachment: z.infer<typeof Attachment>;
|
attachment: z.infer<typeof Attachment>;
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,19 @@
|
||||||
<template>
|
<template>
|
||||||
<Base :attachment="attachment">
|
<AttachmentBase :attachment="attachment">
|
||||||
<video :src="attachment.url" :alt="attachment.description ?? undefined" controls />
|
<video
|
||||||
</Base>
|
:src="attachment.url"
|
||||||
|
:alt="attachment.description ?? undefined"
|
||||||
|
controls
|
||||||
|
>
|
||||||
|
Your browser does not support the video tag.
|
||||||
|
</video>
|
||||||
|
</AttachmentBase>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { Attachment } from "@versia/client/schemas";
|
import type { Attachment } from "@versia/client/schemas";
|
||||||
import type { z } from "zod";
|
import type { z } from "zod";
|
||||||
import Base from "./base.vue";
|
import AttachmentBase from "./attachment-base.vue";
|
||||||
|
|
||||||
const { attachment } = defineProps<{
|
const { attachment } = defineProps<{
|
||||||
attachment: z.infer<typeof Attachment>;
|
attachment: z.infer<typeof Attachment>;
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,18 @@
|
||||||
<p class="text-sm leading-6 wrap-anywhere">
|
<p class="text-sm leading-6 wrap-anywhere">
|
||||||
{{ contentWarning || m.sour_seemly_bird_hike() }}
|
{{ contentWarning || m.sour_seemly_bird_hike() }}
|
||||||
</p>
|
</p>
|
||||||
<Button @click="hidden = !hidden" variant="outline" size="sm" class="col-span-2">
|
<Button
|
||||||
|
@click="hidden = !hidden"
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
class="col-span-2"
|
||||||
|
>
|
||||||
{{ hidden ? m.bald_direct_turtle_win() :
|
{{ hidden ? m.bald_direct_turtle_win() :
|
||||||
m.known_flaky_cockroach_dash() }} {{ characterCount > 0 ? ` (${characterCount} characters` : "" }}{{
|
m.known_flaky_cockroach_dash() }}
|
||||||
attachmentCount > 0 ? `${characterCount > 0 ? " · " : " ("}${attachmentCount} file(s)` : "" }}{{ (characterCount > 0 || attachmentCount > 0) ? ")" : "" }}
|
{{ characterCount > 0 ? ` (${characterCount} characters` : "" }}
|
||||||
|
{{
|
||||||
|
attachmentCount > 0 ? `${characterCount > 0 ? " · " : " ("}${attachmentCount} file(s)` : "" }}
|
||||||
|
{{ (characterCount > 0 || attachmentCount > 0) ? ")" : "" }}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,28 @@
|
||||||
<template>
|
<template>
|
||||||
<ContentWarning v-if="(sensitive || contentWarning) && preferences.show_content_warning" :content-warning="contentWarning" :character-count="characterCount ?? 0" :attachment-count="attachments.length" v-model="hidden" />
|
<ContentWarning
|
||||||
|
v-if="(sensitive || contentWarning) && preferences.show_content_warning"
|
||||||
|
:content-warning="contentWarning"
|
||||||
|
:character-count="characterCount ?? 0"
|
||||||
|
:attachment-count="attachments.length"
|
||||||
|
v-model="hidden"
|
||||||
|
/>
|
||||||
|
|
||||||
<OverflowGuard v-if="content" :character-count="characterCount" :class="(hidden && preferences.show_content_warning) && 'hidden'">
|
<OverflowGuard
|
||||||
|
v-if="content"
|
||||||
|
:character-count="characterCount"
|
||||||
|
:class="(hidden && preferences.show_content_warning) && 'hidden'"
|
||||||
|
>
|
||||||
<Prose v-html="content" v-render-emojis="emojis"></Prose>
|
<Prose v-html="content" v-render-emojis="emojis"></Prose>
|
||||||
</OverflowGuard>
|
</OverflowGuard>
|
||||||
|
|
||||||
<Attachments v-if="attachments.length > 0" :attachments="attachments" :class="(hidden && preferences.show_content_warning) && 'hidden'" />
|
<Attachments
|
||||||
|
v-if="attachments.length > 0"
|
||||||
|
:attachments="attachments"
|
||||||
|
:class="(hidden && preferences.show_content_warning) && 'hidden'"
|
||||||
|
/>
|
||||||
|
|
||||||
<div v-if="quote" class="mt-4 rounded border overflow-hidden">
|
<div v-if="quote" class="mt-4 rounded border overflow-hidden">
|
||||||
<Note :note="quote" :hide-actions="true" :small-layout="true" />
|
<Note :note="quote" :hide-actions="true" :small-layout="true"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,61 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="rounded grid grid-cols-[auto_1fr_auto] items-center gap-3">
|
<div class="rounded grid grid-cols-[auto_1fr_auto] items-center gap-3">
|
||||||
<HoverCard v-model:open="popupOpen" @update:open="() => {
|
<HoverCard
|
||||||
|
v-model:open="popupOpen"
|
||||||
|
@update:open="() => {
|
||||||
if (!preferences.popup_avatar_hover) {
|
if (!preferences.popup_avatar_hover) {
|
||||||
popupOpen = false;
|
popupOpen = false;
|
||||||
}
|
}
|
||||||
}" :open-delay="2000">
|
}"
|
||||||
|
:open-delay="2000"
|
||||||
|
>
|
||||||
<HoverCardTrigger :as-child="true">
|
<HoverCardTrigger :as-child="true">
|
||||||
<NuxtLink :href="urlAsPath" :class="cn('relative size-12', smallLayout && 'size-8')">
|
<NuxtLink
|
||||||
<Avatar :class="cn('size-12 border border-card', smallLayout && 'size-8')" :src="author.avatar"
|
:href="urlAsPath"
|
||||||
:name="author.display_name" />
|
:class="cn('relative size-12', smallLayout && 'size-8')"
|
||||||
<Avatar v-if="cornerAvatar" class="size-6 border absolute -bottom-1 -right-1" :src="cornerAvatar" />
|
>
|
||||||
|
<Avatar
|
||||||
|
:class="cn('size-12 border border-card', smallLayout && 'size-8')"
|
||||||
|
:src="author.avatar"
|
||||||
|
:name="author.display_name"
|
||||||
|
/>
|
||||||
|
<Avatar
|
||||||
|
v-if="cornerAvatar"
|
||||||
|
class="size-6 border absolute -bottom-1 -right-1"
|
||||||
|
:src="cornerAvatar"
|
||||||
|
/>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</HoverCardTrigger>
|
</HoverCardTrigger>
|
||||||
<HoverCardContent class="w-96">
|
<HoverCardContent class="w-96">
|
||||||
<SmallCard :account="author" />
|
<SmallCard :account="author"/>
|
||||||
</HoverCardContent>
|
</HoverCardContent>
|
||||||
</HoverCard>
|
</HoverCard>
|
||||||
<Col
|
<Column :class="smallLayout && 'text-sm'">
|
||||||
:class="smallLayout && 'text-sm'">
|
<Text class="font-semibold" v-render-emojis="author.emojis">
|
||||||
<Text class="font-semibold" v-render-emojis="author.emojis">{{
|
{{
|
||||||
author.display_name
|
author.display_name
|
||||||
}}</Text>
|
}}
|
||||||
|
</Text>
|
||||||
<div class="-mt-1">
|
<div class="-mt-1">
|
||||||
<Address as="span" :username="username" :domain="instance" />
|
<Address as="span" :username="username" :domain="instance"/>
|
||||||
·
|
·
|
||||||
<Text as="span" muted class="ml-auto tracking-normal" :title="fullTime">{{ timeAgo }}</Text>
|
<Text
|
||||||
|
as="span"
|
||||||
|
muted
|
||||||
|
class="ml-auto tracking-normal"
|
||||||
|
:title="fullTime"
|
||||||
|
>
|
||||||
|
{{ timeAgo }}
|
||||||
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
</Col>
|
</Column>
|
||||||
<div v-if="!smallLayout">
|
<div v-if="!smallLayout">
|
||||||
<NuxtLink :href="noteUrlAsPath" class="text-xs text-muted-foreground"
|
<NuxtLink
|
||||||
:title="visibilities[visibility].text">
|
:href="noteUrlAsPath"
|
||||||
<component :is="visibilities[visibility].icon" class="size-4" />
|
class="text-xs text-muted-foreground"
|
||||||
|
:title="visibilities[visibility].text"
|
||||||
|
>
|
||||||
|
<component :is="visibilities[visibility].icon" class="size-4"/>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -49,7 +74,7 @@ import { getLocale } from "~~/paraglide/runtime";
|
||||||
import Address from "../profiles/address.vue";
|
import Address from "../profiles/address.vue";
|
||||||
import Avatar from "../profiles/avatar.vue";
|
import Avatar from "../profiles/avatar.vue";
|
||||||
import SmallCard from "../profiles/small-card.vue";
|
import SmallCard from "../profiles/small-card.vue";
|
||||||
import Col from "../typography/layout/col.vue";
|
import Column from "../typography/layout/col.vue";
|
||||||
import Text from "../typography/text.vue";
|
import Text from "../typography/text.vue";
|
||||||
import {
|
import {
|
||||||
HoverCard,
|
HoverCard,
|
||||||
|
|
|
||||||
|
|
@ -79,57 +79,71 @@ const _delete = async () => {
|
||||||
<template>
|
<template>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger as-child>
|
<DropdownMenuTrigger as-child>
|
||||||
<slot />
|
<slot/>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent class="min-w-56">
|
<DropdownMenuContent class="min-w-56">
|
||||||
<DropdownMenuGroup>
|
<DropdownMenuGroup>
|
||||||
<DropdownMenuItem v-if="authorIsMe" as="button" @click="emit('edit')">
|
<DropdownMenuItem
|
||||||
<Pencil />
|
v-if="authorIsMe"
|
||||||
|
as="button"
|
||||||
|
@click="emit('edit')"
|
||||||
|
>
|
||||||
|
<Pencil/>
|
||||||
{{ m.front_lime_grizzly_persist() }}
|
{{ m.front_lime_grizzly_persist() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem as="button" @click="copyText(apiNoteString)">
|
<DropdownMenuItem as="button" @click="copyText(apiNoteString)">
|
||||||
<Code />
|
<Code/>
|
||||||
{{ m.yummy_moving_scallop_sail() }}
|
{{ m.yummy_moving_scallop_sail() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem as="button" @click="copyText(noteId)">
|
<DropdownMenuItem as="button" @click="copyText(noteId)">
|
||||||
<Hash />
|
<Hash/>
|
||||||
{{ m.sunny_zany_jellyfish_pop() }}
|
{{ m.sunny_zany_jellyfish_pop() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuGroup>
|
</DropdownMenuGroup>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator/>
|
||||||
<DropdownMenuGroup>
|
<DropdownMenuGroup>
|
||||||
<DropdownMenuItem as="button" @click="copyText(url)">
|
<DropdownMenuItem as="button" @click="copyText(url)">
|
||||||
<Link />
|
<Link/>
|
||||||
{{ m.ago_new_pelican_drip() }}
|
{{ m.ago_new_pelican_drip() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem as="button" v-if="isRemote && remoteUrl" @click="copyText(remoteUrl)">
|
<DropdownMenuItem
|
||||||
<Link />
|
as="button"
|
||||||
|
v-if="isRemote && remoteUrl"
|
||||||
|
@click="copyText(remoteUrl)"
|
||||||
|
>
|
||||||
|
<Link/>
|
||||||
{{ m.solid_witty_zebra_walk() }}
|
{{ m.solid_witty_zebra_walk() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem as="a" v-if="isRemote" target="_blank" rel="noopener noreferrer" :href="remoteUrl">
|
<DropdownMenuItem
|
||||||
<ExternalLink />
|
as="a"
|
||||||
|
v-if="isRemote"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
:href="remoteUrl"
|
||||||
|
>
|
||||||
|
<ExternalLink/>
|
||||||
{{ m.active_trite_lark_inspire() }}
|
{{ m.active_trite_lark_inspire() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuGroup>
|
</DropdownMenuGroup>
|
||||||
<DropdownMenuSeparator v-if="authorIsMe" />
|
<DropdownMenuSeparator v-if="authorIsMe"/>
|
||||||
<DropdownMenuGroup v-if="authorIsMe">
|
<DropdownMenuGroup v-if="authorIsMe">
|
||||||
<DropdownMenuItem as="button" :disabled="true">
|
<DropdownMenuItem as="button" :disabled="true">
|
||||||
<Delete />
|
<Delete/>
|
||||||
{{ m.real_green_clownfish_pet() }}
|
{{ m.real_green_clownfish_pet() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem as="button" @click="_delete">
|
<DropdownMenuItem as="button" @click="_delete">
|
||||||
<Trash />
|
<Trash/>
|
||||||
{{ m.tense_quick_cod_favor() }}
|
{{ m.tense_quick_cod_favor() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuGroup>
|
</DropdownMenuGroup>
|
||||||
<DropdownMenuSeparator v-if="authStore.isSignedIn && !authorIsMe" />
|
<DropdownMenuSeparator v-if="authStore.isSignedIn && !authorIsMe"/>
|
||||||
<DropdownMenuGroup v-if="authStore.isSignedIn && !authorIsMe">
|
<DropdownMenuGroup v-if="authStore.isSignedIn && !authorIsMe">
|
||||||
<DropdownMenuItem as="button" :disabled="true">
|
<DropdownMenuItem as="button" :disabled="true">
|
||||||
<Flag />
|
<Flag/>
|
||||||
{{ m.great_few_jaguar_rise() }}
|
{{ m.great_few_jaguar_rise() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem as="button" @click="blockUser(authorId)">
|
<DropdownMenuItem as="button" @click="blockUser(authorId)">
|
||||||
<Ban />
|
<Ban/>
|
||||||
{{ m.misty_soft_sparrow_vent() }}
|
{{ m.misty_soft_sparrow_vent() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuGroup>
|
</DropdownMenuGroup>
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,12 @@
|
||||||
:sensitive="noteToUse.sensitive"
|
:sensitive="noteToUse.sensitive"
|
||||||
:content-warning="noteToUse.spoiler_text"
|
:content-warning="noteToUse.spoiler_text"
|
||||||
/>
|
/>
|
||||||
<Reactions v-if="noteToUse.reactions && noteToUse.reactions.length > 0" :reactions="noteToUse.reactions" :emojis="noteToUse.emojis" :status-id="noteToUse.id" />
|
<Reactions
|
||||||
|
v-if="noteToUse.reactions && noteToUse.reactions.length > 0"
|
||||||
|
:reactions="noteToUse.reactions"
|
||||||
|
:emojis="noteToUse.emojis"
|
||||||
|
:status-id="noteToUse.id"
|
||||||
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardFooter v-if="!hideActions">
|
<CardFooter v-if="!hideActions">
|
||||||
<Actions
|
<Actions
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,29 @@
|
||||||
<template>
|
<template>
|
||||||
<div ref="container" class="overflow-y-hidden relative duration-200" :style="{
|
<div
|
||||||
|
ref="container"
|
||||||
|
class="overflow-y-hidden relative duration-200"
|
||||||
|
:style="{
|
||||||
maxHeight: collapsed ? '18rem' : `${container?.scrollHeight}px`,
|
maxHeight: collapsed ? '18rem' : `${container?.scrollHeight}px`,
|
||||||
}">
|
}"
|
||||||
<slot />
|
>
|
||||||
<div v-if="isOverflowing && collapsed"
|
<slot/>
|
||||||
class="absolute inset-x-0 bottom-0 h-36 bg-gradient-to-t from-black/5 to-transparent rounded-b"></div>
|
<div
|
||||||
<Button v-if="isOverflowing" @click="collapsed = !collapsed"
|
v-if="isOverflowing && collapsed"
|
||||||
class="absolute bottom-2 right-1/2 translate-x-1/2">{{
|
class="absolute inset-x-0 bottom-0 h-36 bg-gradient-to-t from-black/5 to-transparent rounded-b"
|
||||||
|
></div>
|
||||||
|
<Button
|
||||||
|
v-if="isOverflowing"
|
||||||
|
@click="collapsed = !collapsed"
|
||||||
|
class="absolute bottom-2 right-1/2 translate-x-1/2"
|
||||||
|
>
|
||||||
|
{{
|
||||||
collapsed
|
collapsed
|
||||||
? `${m.lazy_honest_mammoth_bump()}${formattedCharacterCount ? ` • ${m.dark_spare_goldfish_charm({
|
? `${m.lazy_honest_mammoth_bump()}${formattedCharacterCount ? ` • ${m.dark_spare_goldfish_charm({
|
||||||
count: formattedCharacterCount,
|
count: formattedCharacterCount,
|
||||||
})}` : ""}`
|
})}` : ""}`
|
||||||
: m.that_misty_mule_arrive()
|
: m.that_misty_mule_arrive()
|
||||||
}}</Button>
|
}}
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<div :class="[
|
<div
|
||||||
|
:class="[
|
||||||
'prose prose-sm block relative dark:prose-invert duration-200 !max-w-full break-words prose-a:no-underline hover:prose-a:underline',
|
'prose prose-sm block relative dark:prose-invert duration-200 !max-w-full break-words prose-a:no-underline hover:prose-a:underline',
|
||||||
$style.content,
|
$style.content,
|
||||||
]">
|
]"
|
||||||
<slot />
|
>
|
||||||
|
<slot/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-row gap-2 flex-wrap">
|
<div class="flex flex-row gap-2 flex-wrap">
|
||||||
<Reaction v-for="reaction in reactions" :key="reaction.name" :reaction="reaction" :emoji="emojis.find(e => `:${e.shortcode}:` === reaction.name)" :status-id="statusId" />
|
<Reaction
|
||||||
|
v-for="reaction in reactions"
|
||||||
|
:key="reaction.name"
|
||||||
|
:reaction="reaction"
|
||||||
|
:emoji="emojis.find(e => `:${e.shortcode}:` === reaction.name)"
|
||||||
|
:status-id="statusId"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="sticky top-2 z-10 flex items-center justify-center p-2">
|
<div class="sticky top-2 z-10 flex items-center justify-center p-2">
|
||||||
<Badge variant="secondary">
|
<Badge variant="secondary">{{ categoryName }}</Badge>
|
||||||
{{ categoryName }}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,17 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="p-2 text-sm font-semibold border-0 rounded-none text-center flex flex-row items-center gap-2 truncate">
|
<div
|
||||||
<img v-if="(emoji as InferredEmoji)?.url" :src="(emoji as InferredEmoji)?.url"
|
class="p-2 text-sm font-semibold border-0 rounded-none text-center flex flex-row items-center gap-2 truncate"
|
||||||
:alt="(emoji as InferredEmoji)?.shortcode" class="h-8 align-middle inline not-prose" />
|
>
|
||||||
<span v-else-if="(emoji as UnicodeEmoji)?.unicode" class="text-2xl align-middle inline not-prose">
|
<img
|
||||||
|
v-if="(emoji as InferredEmoji)?.url"
|
||||||
|
:src="(emoji as InferredEmoji)?.url"
|
||||||
|
:alt="(emoji as InferredEmoji)?.shortcode"
|
||||||
|
class="h-8 align-middle inline not-prose"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
v-else-if="(emoji as UnicodeEmoji)?.unicode"
|
||||||
|
class="text-2xl align-middle inline not-prose"
|
||||||
|
>
|
||||||
{{ (emoji as UnicodeEmoji)?.unicode }}
|
{{ (emoji as UnicodeEmoji)?.unicode }}
|
||||||
</span>
|
</span>
|
||||||
{{ (emoji as InferredEmoji)?.shortcode || (emoji as UnicodeEmoji)?.shortcode }}
|
{{ (emoji as InferredEmoji)?.shortcode || (emoji as UnicodeEmoji)?.shortcode }}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,22 @@
|
||||||
<template>
|
<template>
|
||||||
<Button @focus="() => emit('select', emoji)" @mouseenter="() => emit('select', emoji)" @click="() => emit('pick', emoji)" size="icon" variant="ghost"
|
<Button
|
||||||
class="size-12">
|
@focus="() => emit('select', emoji)"
|
||||||
<img v-if="(emoji as InferredEmoji).url" :src="(emoji as InferredEmoji).url"
|
@mouseenter="() => emit('select', emoji)"
|
||||||
:alt="(emoji as InferredEmoji).shortcode" class="h-8 align-middle inline not-prose" />
|
@click="() => emit('pick', emoji)"
|
||||||
<span v-else-if="(emoji as UnicodeEmoji).unicode" class="text-2xl align-middle inline not-prose">
|
size="icon"
|
||||||
|
variant="ghost"
|
||||||
|
class="size-12"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
v-if="(emoji as InferredEmoji).url"
|
||||||
|
:src="(emoji as InferredEmoji).url"
|
||||||
|
:alt="(emoji as InferredEmoji).shortcode"
|
||||||
|
class="h-8 align-middle inline not-prose"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
v-else-if="(emoji as UnicodeEmoji).unicode"
|
||||||
|
class="text-2xl align-middle inline not-prose"
|
||||||
|
>
|
||||||
{{ (emoji as UnicodeEmoji).unicode }}
|
{{ (emoji as UnicodeEmoji).unicode }}
|
||||||
</span>
|
</span>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,57 @@
|
||||||
<template>
|
<template>
|
||||||
<Popover v-model:open="open">
|
<Popover v-model:open="open">
|
||||||
<PopoverTrigger as-child>
|
<PopoverTrigger as-child>
|
||||||
<slot />
|
<slot/>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent class="p-0 w-fit">
|
<PopoverContent class="p-0 w-fit">
|
||||||
<div class="grid-cols-[minmax(0,1fr)_auto] gap-0 grid divide-x *:h-112 *:overflow-y-auto"
|
<div
|
||||||
orientation="vertical">
|
class="grid-cols-[minmax(0,1fr)_auto] gap-0 grid divide-x *:h-112 *:overflow-y-auto"
|
||||||
<div class="grid grid-rows-[auto_minmax(0,1fr)_auto] gap-0" ref="emojiContainer">
|
orientation="vertical"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="grid grid-rows-[auto_minmax(0,1fr)_auto] gap-0"
|
||||||
|
ref="emojiContainer"
|
||||||
|
>
|
||||||
<div class="p-2">
|
<div class="p-2">
|
||||||
<Input placeholder="Search" v-model="filter" />
|
<Input placeholder="Search" v-model="filter"/>
|
||||||
</div>
|
</div>
|
||||||
<VList :data="virtualizedItems" #default="{ item }" class="relative" :style="{
|
<VList
|
||||||
|
:data="virtualizedItems"
|
||||||
|
#default="{ item }"
|
||||||
|
class="relative"
|
||||||
|
:style="{
|
||||||
width: `calc(var(--spacing) * ((12 * ${EMOJI_PER_ROW}) + (${EMOJI_PER_ROW} - 1)) + var(--spacing) * 4)`,
|
width: `calc(var(--spacing) * ((12 * ${EMOJI_PER_ROW}) + (${EMOJI_PER_ROW} - 1)) + var(--spacing) * 4)`,
|
||||||
}">
|
}"
|
||||||
<CategoryHeader :key="item.headerId" v-if="item.type === 'header'" :category-name="item.name" />
|
>
|
||||||
<div v-else-if="item.type === 'emoji-row'" :key="item.rowId" class="flex gap-1 p-2">
|
<CategoryHeader
|
||||||
<Emoji v-for="emoji in item.emojis" :key="getEmojiKey(emoji)" :emoji="emoji"
|
:key="item.headerId"
|
||||||
@select="(e) => selectedEmoji = e" @pick="e => {
|
v-if="item.type === 'header'"
|
||||||
|
:category-name="item.name"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
v-else-if="item.type === 'emoji-row'"
|
||||||
|
:key="item.rowId"
|
||||||
|
class="flex gap-1 p-2"
|
||||||
|
>
|
||||||
|
<Emoji
|
||||||
|
v-for="emoji in item.emojis"
|
||||||
|
:key="getEmojiKey(emoji)"
|
||||||
|
:emoji="emoji"
|
||||||
|
@select="(e) => selectedEmoji = e"
|
||||||
|
@pick="e => {
|
||||||
emit('pick', e); open = false;
|
emit('pick', e); open = false;
|
||||||
}" />
|
}"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</VList>
|
</VList>
|
||||||
<EmojiDisplay :emoji="selectedEmoji" :style="{
|
<EmojiDisplay
|
||||||
|
:emoji="selectedEmoji"
|
||||||
|
:style="{
|
||||||
width: `calc(var(--spacing) * ((12 * ${EMOJI_PER_ROW}) + (${EMOJI_PER_ROW} - 1)) + var(--spacing) * 4)`,
|
width: `calc(var(--spacing) * ((12 * ${EMOJI_PER_ROW}) + (${EMOJI_PER_ROW} - 1)) + var(--spacing) * 4)`,
|
||||||
}" />
|
}"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Sidebar :categories="categories" @select="scrollToCategory" />
|
<Sidebar :categories="categories" @select="scrollToCategory"/>
|
||||||
</div>
|
</div>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,23 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="grid gap-1 bg-transparent p-2">
|
<div class="grid gap-1 bg-transparent p-2">
|
||||||
<Button v-for="category in categories" :key="category.name" size="icon" variant="ghost" @click="() => emit('select', category)">
|
<Button
|
||||||
<component v-if="category.groupId" :is="emojiGroupIconMap[category.groupId]" class="size-6 text-primary" />
|
v-for="category in categories"
|
||||||
<img v-else-if="category.src" :src="category.src" class="size-6 align-middle inline not-prose" role="presentation" />
|
:key="category.name"
|
||||||
|
size="icon"
|
||||||
|
variant="ghost"
|
||||||
|
@click="() => emit('select', category)"
|
||||||
|
>
|
||||||
|
<component
|
||||||
|
v-if="category.groupId"
|
||||||
|
:is="emojiGroupIconMap[category.groupId]"
|
||||||
|
class="size-6 text-primary"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
v-else-if="category.src"
|
||||||
|
:src="category.src"
|
||||||
|
class="size-6 align-middle inline not-prose"
|
||||||
|
role="presentation"
|
||||||
|
>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,38 @@
|
||||||
<template>
|
<template>
|
||||||
<HoverCard @update:open="(open) => open && accounts === null && refreshReactions()">
|
<HoverCard
|
||||||
|
@update:open="(open) => open && accounts === null && refreshReactions()"
|
||||||
|
>
|
||||||
<HoverCardTrigger as-child>
|
<HoverCardTrigger as-child>
|
||||||
<Button @click="reaction.me ? !reaction.remote && unreact() : !reaction.remote && react()" :variant="reaction.me ? 'secondary' : reaction.remote ? 'ghost' : 'outline'" size="sm" class="gap-2">
|
<Button
|
||||||
<img v-if="emoji" :src="emoji.url" :alt="emoji.shortcode"
|
@click="reaction.me ? !reaction.remote && unreact() : !reaction.remote && react()"
|
||||||
class="h-[1lh] align-middle inline not-prose" />
|
:variant="reaction.me ? 'secondary' : reaction.remote ? 'ghost' : 'outline'"
|
||||||
<span v-else>
|
size="sm"
|
||||||
{{ reaction.name }}
|
class="gap-2"
|
||||||
</span>
|
>
|
||||||
|
<img
|
||||||
|
v-if="emoji"
|
||||||
|
:src="emoji.url"
|
||||||
|
:alt="emoji.shortcode"
|
||||||
|
class="h-[1lh] align-middle inline not-prose"
|
||||||
|
>
|
||||||
|
<span v-else> {{ reaction.name }}</span>
|
||||||
{{ formatNumber(reaction.count) }}
|
{{ formatNumber(reaction.count) }}
|
||||||
</Button>
|
</Button>
|
||||||
</HoverCardTrigger>
|
</HoverCardTrigger>
|
||||||
<HoverCardContent class="p-3">
|
<HoverCardContent class="p-3">
|
||||||
<Spinner v-if="accounts === null" class="border-0" />
|
<Spinner v-if="accounts === null" class="border-0"/>
|
||||||
<ul v-else class="flex flex-col gap-4">
|
<ul v-else class="flex flex-col gap-4">
|
||||||
<li
|
<li v-for="account in accounts">
|
||||||
v-for="account in accounts">
|
<NuxtLink
|
||||||
<NuxtLink :to="`/@${account.acct}`" class="flex items-center gap-2">
|
:to="`/@${account.acct}`"
|
||||||
<Avatar class="size-6" :key="account.id" :src="account.avatar"
|
class="flex items-center gap-2"
|
||||||
:name="account.display_name || account.username" />
|
>
|
||||||
|
<Avatar
|
||||||
|
class="size-6"
|
||||||
|
:key="account.id"
|
||||||
|
:src="account.avatar"
|
||||||
|
:name="account.display_name || account.username"
|
||||||
|
/>
|
||||||
<span class="text-sm font-semibold line-clamp-1">
|
<span class="text-sm font-semibold line-clamp-1">
|
||||||
{{ account.display_name || account.username }}
|
{{ account.display_name || account.username }}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,13 @@
|
||||||
<template>
|
<template>
|
||||||
<NuxtLink :href="urlAsPath">
|
<NuxtLink :href="urlAsPath">
|
||||||
<Card class="flex-row px-2 py-1 items-center gap-2 hover:bg-muted duration-100 text-sm">
|
<Card
|
||||||
<Repeat class="size-4 text-primary" />
|
class="flex-row px-2 py-1 items-center gap-2 hover:bg-muted duration-100 text-sm"
|
||||||
<Avatar class="size-6 border" :src="avatar" :name="displayName" />
|
>
|
||||||
<span class="font-semibold" v-render-emojis="emojis">{{ displayName }}</span>
|
<Repeat class="size-4 text-primary"/>
|
||||||
|
<Avatar class="size-6 border" :src="avatar" :name="displayName"/>
|
||||||
|
<span class="font-semibold" v-render-emojis="emojis"
|
||||||
|
>{{ displayName }}</span
|
||||||
|
>
|
||||||
{{ m.large_vivid_horse_catch() }}
|
{{ m.large_vivid_horse_catch() }}
|
||||||
</Card>
|
</Card>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,55 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-if="relationship?.requested_by !== false" class="flex flex-row items-center gap-3 p-4">
|
<div
|
||||||
|
v-if="relationship?.requested_by !== false"
|
||||||
|
class="flex flex-row items-center gap-3 p-4"
|
||||||
|
>
|
||||||
<NuxtLink :href="followerUrl" class="relative size-10">
|
<NuxtLink :href="followerUrl" class="relative size-10">
|
||||||
<Avatar class="size-10 border border-border" :src="follower.avatar" :name="follower.display_name" />
|
<Avatar
|
||||||
|
class="size-10 border border-border"
|
||||||
|
:src="follower.avatar"
|
||||||
|
:name="follower.display_name"
|
||||||
|
/>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<div class="flex flex-col gap-0.5 justify-center flex-1 text-left leading-tight text-sm">
|
<div
|
||||||
<span class="truncate font-semibold" v-render-emojis="follower.emojis">{{
|
class="flex flex-col gap-0.5 justify-center flex-1 text-left leading-tight text-sm"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="truncate font-semibold"
|
||||||
|
v-render-emojis="follower.emojis"
|
||||||
|
>{{
|
||||||
follower.display_name
|
follower.display_name
|
||||||
}}</span>
|
}}</span
|
||||||
|
>
|
||||||
<span class="truncate tracking-tight">
|
<span class="truncate tracking-tight">
|
||||||
<Address :username="username" :domain="domain" />
|
<Address :username="username" :domain="domain"/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="loading" class="flex p-2 items-center justify-center h-12">
|
<div v-if="loading" class="flex p-2 items-center justify-center h-12">
|
||||||
<Loader class="size-4 animate-spin" />
|
<Loader class="size-4 animate-spin"/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="relationship?.requested_by === false" class="flex p-2 items-center justify-center h-12">
|
<div
|
||||||
<Check class="size-4" />
|
v-else-if="relationship?.requested_by === false"
|
||||||
|
class="flex p-2 items-center justify-center h-12"
|
||||||
|
>
|
||||||
|
<Check class="size-4"/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="grid grid-cols-2 p-2 gap-2">
|
<div v-else class="grid grid-cols-2 p-2 gap-2">
|
||||||
<Button variant="secondary" size="sm" @click="accept" :title="m.slow_these_kestrel_sail()">
|
<Button
|
||||||
<Check />
|
variant="secondary"
|
||||||
|
size="sm"
|
||||||
|
@click="accept"
|
||||||
|
:title="m.slow_these_kestrel_sail()"
|
||||||
|
>
|
||||||
|
<Check/>
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="ghost" size="sm" @click="reject" :title="m.weary_steep_yak_embrace()">
|
<Button
|
||||||
<X />
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
@click="reject"
|
||||||
|
:title="m.weary_steep_yak_embrace()"
|
||||||
|
>
|
||||||
|
<X/>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
v-if="notification.account"
|
v-if="notification.account"
|
||||||
class="flex flex-row items-center gap-2 px-2"
|
class="flex flex-row items-center gap-2 px-2"
|
||||||
>
|
>
|
||||||
<component :is="icon" class="size-5 shrink-0" />
|
<component :is="icon" class="size-5 shrink-0"/>
|
||||||
<Avatar
|
<Avatar
|
||||||
class="size-5 border border-card"
|
class="size-5 border border-card"
|
||||||
:src="notification.account.avatar"
|
:src="notification.account.avatar"
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
class="ml-auto [&_svg]:data-[state=open]:-rotate-180"
|
class="ml-auto [&_svg]:data-[state=open]:-rotate-180"
|
||||||
:title="open ? 'Collapse' : 'Expand'"
|
:title="open ? 'Collapse' : 'Expand'"
|
||||||
>
|
>
|
||||||
<ChevronDown class="duration-200" />
|
<ChevronDown class="duration-200"/>
|
||||||
</Button>
|
</Button>
|
||||||
</CollapsibleTrigger>
|
</CollapsibleTrigger>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,43 @@
|
||||||
<template>
|
<template>
|
||||||
<section class="space-y-2">
|
<section class="space-y-2">
|
||||||
<CardTitle class="text-xs">
|
<CardTitle class="text-xs">{{ name }}</CardTitle>
|
||||||
{{ name }}
|
|
||||||
</CardTitle>
|
|
||||||
<Card class="p-0 gap-0">
|
<Card class="p-0 gap-0">
|
||||||
<div v-for="preference of preferences" :key="preference">
|
<div v-for="preference of preferences" :key="preference">
|
||||||
<TextPreferenceVue v-if="(prefs[preference] instanceof TextPreference)" :pref="(prefs[preference] as TextPreference)" :name="preference" />
|
<TextPreferenceVue
|
||||||
<BooleanPreferenceVue v-else-if="(prefs[preference] instanceof BooleanPreference)" :pref="(prefs[preference] as BooleanPreference)" :name="preference" />
|
v-if="(prefs[preference] instanceof TextPreference)"
|
||||||
<SelectPreferenceVue v-else-if="(prefs[preference] instanceof SelectPreference)" :pref="(prefs[preference] as SelectPreference<string>)" :name="preference" />
|
:pref="(prefs[preference] as TextPreference)"
|
||||||
<NumberPreferenceVue v-else-if="(prefs[preference] instanceof NumberPreference)" :pref="(prefs[preference] as NumberPreference)" :name="preference" />
|
:name="preference"
|
||||||
<MultiSelectPreferenceVue v-else-if="(prefs[preference] instanceof MultiSelectPreference)" :pref="(prefs[preference] as MultiSelectPreference<string>)" :name="preference" />
|
/>
|
||||||
<CodePreferenceVue v-else-if="(prefs[preference] instanceof CodePreference)" :pref="(prefs[preference] as CodePreference)" :name="preference" />
|
<BooleanPreferenceVue
|
||||||
<UrlPreferenceVue v-else-if="(prefs[preference] instanceof UrlPreference)" :pref="(prefs[preference] as UrlPreference)" :name="preference" />
|
v-else-if="(prefs[preference] instanceof BooleanPreference)"
|
||||||
|
:pref="(prefs[preference] as BooleanPreference)"
|
||||||
|
:name="preference"
|
||||||
|
/>
|
||||||
|
<SelectPreferenceVue
|
||||||
|
v-else-if="(prefs[preference] instanceof SelectPreference)"
|
||||||
|
:pref="(prefs[preference] as SelectPreference<string>)"
|
||||||
|
:name="preference"
|
||||||
|
/>
|
||||||
|
<NumberPreferenceVue
|
||||||
|
v-else-if="(prefs[preference] instanceof NumberPreference)"
|
||||||
|
:pref="(prefs[preference] as NumberPreference)"
|
||||||
|
:name="preference"
|
||||||
|
/>
|
||||||
|
<MultiSelectPreferenceVue
|
||||||
|
v-else-if="(prefs[preference] instanceof MultiSelectPreference)"
|
||||||
|
:pref="(prefs[preference] as MultiSelectPreference<string>)"
|
||||||
|
:name="preference"
|
||||||
|
/>
|
||||||
|
<CodePreferenceVue
|
||||||
|
v-else-if="(prefs[preference] instanceof CodePreference)"
|
||||||
|
:pref="(prefs[preference] as CodePreference)"
|
||||||
|
:name="preference"
|
||||||
|
/>
|
||||||
|
<UrlPreferenceVue
|
||||||
|
v-else-if="(prefs[preference] instanceof UrlPreference)"
|
||||||
|
:pref="(prefs[preference] as UrlPreference)"
|
||||||
|
:name="preference"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
<template>
|
<template>
|
||||||
<Card class="grid gap-3 text-sm">
|
<Card class="grid gap-3 text-sm">
|
||||||
<dl class="grid gap-3">
|
<dl class="grid gap-3">
|
||||||
<div v-for="[key, value] of data" :key="key" class="flex flex-row items-baseline justify-between gap-4 truncate">
|
<div
|
||||||
<dt class="text-muted-foreground">
|
v-for="[key, value] of data"
|
||||||
{{ key }}
|
:key="key"
|
||||||
</dt>
|
class="flex flex-row items-baseline justify-between gap-4 truncate"
|
||||||
<dd class="font-mono" v-if="typeof value === 'string'">{{ value }}</dd>
|
>
|
||||||
|
<dt class="text-muted-foreground">{{ key }}</dt>
|
||||||
|
<dd class="font-mono" v-if="typeof value === 'string'">
|
||||||
|
{{ value }}
|
||||||
|
</dd>
|
||||||
<dd class="font-mono" v-else>
|
<dd class="font-mono" v-else>
|
||||||
<component :is="value" />
|
<component :is="value"/>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
|
||||||
|
|
@ -77,46 +77,70 @@ useListen("preferences:open", () => {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Dialog v-model:open="open" v-if="authStore.isSignedIn">
|
<Dialog v-model:open="open" v-if="authStore.isSignedIn">
|
||||||
<DialogContent class="md:max-w-5xl w-full h-full p-0 md:max-h-[70dvh] overflow-hidden">
|
<DialogContent
|
||||||
<Tabs class="md:grid-cols-[auto_minmax(0,1fr)] !grid gap-2 *:p-4 overflow-hidden *:overflow-y-auto *:h-full" orientation="vertical"
|
class="md:max-w-5xl w-full h-full p-0 md:max-h-[70dvh] overflow-hidden"
|
||||||
:default-value="pages[0]">
|
>
|
||||||
<DialogHeader class="gap-6 grid grid-rows-[auto_minmax(0,1fr)] border-b md:border-b-0 md:border-r min-w-60 text-left">
|
<Tabs
|
||||||
<div class="grid gap-3 items-center grid-cols-[auto_minmax(0,1fr)]">
|
class="md:grid-cols-[auto_minmax(0,1fr)] !grid gap-2 *:p-4 overflow-hidden *:overflow-y-auto *:h-full"
|
||||||
<Avatar :name="authStore.account!.display_name || authStore.account!.username"
|
orientation="vertical"
|
||||||
:src="authStore.account!.avatar" />
|
:default-value="pages[0]"
|
||||||
|
>
|
||||||
|
<DialogHeader
|
||||||
|
class="gap-6 grid grid-rows-[auto_minmax(0,1fr)] border-b md:border-b-0 md:border-r min-w-60 text-left"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="grid gap-3 items-center grid-cols-[auto_minmax(0,1fr)]"
|
||||||
|
>
|
||||||
|
<Avatar
|
||||||
|
:name="authStore.account!.display_name || authStore.account!.username"
|
||||||
|
:src="authStore.account!.avatar"
|
||||||
|
/>
|
||||||
<DialogTitle>Preferences</DialogTitle>
|
<DialogTitle>Preferences</DialogTitle>
|
||||||
</div>
|
</div>
|
||||||
<DialogDescription class="sr-only">
|
<DialogDescription class="sr-only">
|
||||||
Make changes to your preferences here.
|
Make changes to your preferences here.
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
<TabsList class="md:grid md:grid-cols-1 w-full h-fit *:justify-start !justify-start">
|
<TabsList
|
||||||
<TabsTrigger v-for="page in pages" :key="page" :value="page">
|
class="md:grid md:grid-cols-1 w-full h-fit *:justify-start !justify-start"
|
||||||
<component :is="icons[page]" class="size-4 mr-2" />
|
>
|
||||||
|
<TabsTrigger
|
||||||
|
v-for="page in pages"
|
||||||
|
:key="page"
|
||||||
|
:value="page"
|
||||||
|
>
|
||||||
|
<component :is="icons[page]" class="size-4 mr-2"/>
|
||||||
{{ page }}
|
{{ page }}
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<TabsContent v-for="page in pages.filter(p => !extraPages.includes(p))" :key="page" :value="page"
|
<TabsContent
|
||||||
as-child>
|
v-for="page in pages.filter(p => !extraPages.includes(p))"
|
||||||
|
:key="page"
|
||||||
|
:value="page"
|
||||||
|
as-child
|
||||||
|
>
|
||||||
<Page :title="page">
|
<Page :title="page">
|
||||||
<Category v-for="category in categories[page]" :key="category"
|
<Category
|
||||||
|
v-for="category in categories[page]"
|
||||||
|
:key="category"
|
||||||
:preferences="Object.entries(preferences).filter(([, p]) => p.options.category === `${page}/${category}`).map(([k,]) => k as keyof typeof preferences)"
|
:preferences="Object.entries(preferences).filter(([, p]) => p.options.category === `${page}/${category}`).map(([k,]) => k as keyof typeof preferences)"
|
||||||
:name="category" />
|
:name="category"
|
||||||
|
/>
|
||||||
</Page>
|
</Page>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
<TabsContent value="Emojis" as-child>
|
<TabsContent value="Emojis" as-child>
|
||||||
<Page title="Emojis">
|
<Page title="Emojis">
|
||||||
<Emojis />
|
<Emojis/>
|
||||||
</Page>
|
</Page>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
<TabsContent value="Account" as-child>
|
<TabsContent value="Account" as-child>
|
||||||
<Page title="Account">
|
<Page title="Account">
|
||||||
<Profile />
|
<Profile/>
|
||||||
</Page>
|
</Page>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
<TabsContent value="Developer" as-child>
|
<TabsContent value="Developer" as-child>
|
||||||
<Page title="Developer">
|
<Page title="Developer">
|
||||||
<Developer />
|
<Developer/>
|
||||||
</Page>
|
</Page>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
<TabsContent value="About" as-child>
|
<TabsContent value="About" as-child>
|
||||||
|
|
@ -126,25 +150,53 @@ useListen("preferences:open", () => {
|
||||||
{{ pkg.description }}
|
{{ pkg.description }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<Stats />
|
<Stats/>
|
||||||
</section>
|
</section>
|
||||||
<Separator />
|
<Separator/>
|
||||||
<section class="space-y-2">
|
<section class="space-y-2">
|
||||||
<h3 class="text-lg font-semibold tracking-tight">Developers</h3>
|
<h3 class="text-lg font-semibold tracking-tight">
|
||||||
<div class="grid lg:grid-cols-3 md:grid-cols-2 grid-cols-1 gap-4">
|
Developers
|
||||||
<TinyCard v-if="author1" :account="author1" domain="vs.cpluspatch.com" />
|
</h3>
|
||||||
<TinyCard v-if="author2" :account="author2" domain="social.lysand.org" />
|
<div
|
||||||
<TinyCard v-if="author3" :account="author3" domain="social.lysand.org" />
|
class="grid lg:grid-cols-3 md:grid-cols-2 grid-cols-1 gap-4"
|
||||||
<TinyCard v-if="author4" :account="author4" domain="v.everypizza.im" />
|
>
|
||||||
|
<TinyCard
|
||||||
|
v-if="author1"
|
||||||
|
:account="author1"
|
||||||
|
domain="vs.cpluspatch.com"
|
||||||
|
/>
|
||||||
|
<TinyCard
|
||||||
|
v-if="author2"
|
||||||
|
:account="author2"
|
||||||
|
domain="social.lysand.org"
|
||||||
|
/>
|
||||||
|
<TinyCard
|
||||||
|
v-if="author3"
|
||||||
|
:account="author3"
|
||||||
|
domain="social.lysand.org"
|
||||||
|
/>
|
||||||
|
<TinyCard
|
||||||
|
v-if="author4"
|
||||||
|
:account="author4"
|
||||||
|
domain="v.everypizza.im"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<Separator />
|
<Separator/>
|
||||||
<section class="space-y-2">
|
<section class="space-y-2">
|
||||||
<h3 class="text-lg font-semibold tracking-tight">Dependencies</h3>
|
<h3 class="text-lg font-semibold tracking-tight">
|
||||||
<ul class="grid lg:grid-cols-2 gap-2 grid-cols-1 items-center justify-center list-disc ml-6">
|
Dependencies
|
||||||
<li v-for="[dep, version] in Object.entries(pkg.dependencies)" :key="dep">
|
</h3>
|
||||||
|
<ul
|
||||||
|
class="grid lg:grid-cols-2 gap-2 grid-cols-1 items-center justify-center list-disc ml-6"
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
v-for="[dep, version] in Object.entries(pkg.dependencies)"
|
||||||
|
:key="dep"
|
||||||
|
>
|
||||||
<code
|
<code
|
||||||
class="rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-xs font-semibold">
|
class="rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-xs font-semibold"
|
||||||
|
>
|
||||||
{{ dep }}@{{ version }}
|
{{ dep }}@{{ version }}
|
||||||
</code>
|
</code>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger as-child>
|
<DropdownMenuTrigger as-child>
|
||||||
<slot />
|
<slot/>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent class="min-w-48">
|
<DropdownMenuContent class="min-w-48">
|
||||||
<DropdownMenuItem @click="deleteAll" :disabled="!canEdit">
|
<DropdownMenuItem @click="deleteAll" :disabled="!canEdit">
|
||||||
<Delete />
|
<Delete/>
|
||||||
{{ m.tense_quick_cod_favor() }}
|
{{ m.tense_quick_cod_favor() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger as-child>
|
<DropdownMenuTrigger as-child>
|
||||||
<Button variant="ghost" size="icon" title="Open menu" class="size-8 p-0">
|
<Button
|
||||||
<MoreHorizontal class="size-4" />
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
title="Open menu"
|
||||||
|
class="size-8 p-0"
|
||||||
|
>
|
||||||
|
<MoreHorizontal class="size-4"/>
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent class="min-w-48">
|
<DropdownMenuContent class="min-w-48">
|
||||||
<DropdownMenuItem @click="editName">
|
<DropdownMenuItem @click="editName">
|
||||||
<TextCursorInput />
|
<TextCursorInput/>
|
||||||
{{ m.cuddly_such_swallow_hush() }}
|
{{ m.cuddly_such_swallow_hush() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<!-- <DropdownMenuItem @click="editCaption">
|
<!-- <DropdownMenuItem @click="editCaption">
|
||||||
|
|
@ -16,7 +21,7 @@
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuSeparator /> -->
|
<DropdownMenuSeparator /> -->
|
||||||
<DropdownMenuItem @click="_delete">
|
<DropdownMenuItem @click="_delete">
|
||||||
<Delete />
|
<Delete/>
|
||||||
{{ m.tense_quick_cod_favor() }}
|
{{ m.tense_quick_cod_favor() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-if="authStore.emojis.length > 0" class="grow">
|
<div v-if="authStore.emojis.length > 0" class="grow">
|
||||||
<Table :emojis="authStore.emojis" :can-upload="canUpload" />
|
<Table :emojis="authStore.emojis" :can-upload="canUpload"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -282,27 +282,34 @@ const table = useVueTable({
|
||||||
<template>
|
<template>
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<div class="flex gap-2 items-center py-4">
|
<div class="flex gap-2 items-center py-4">
|
||||||
<Input class="max-w-52 mr-auto" placeholder="Filter emojis..."
|
<Input
|
||||||
|
class="max-w-52 mr-auto"
|
||||||
|
placeholder="Filter emojis..."
|
||||||
:model-value="(table.getColumn('shortcode')?.getFilterValue() as string)"
|
:model-value="(table.getColumn('shortcode')?.getFilterValue() as string)"
|
||||||
@update:model-value="table.getColumn('shortcode')?.setFilterValue($event)" />
|
@update:model-value="table.getColumn('shortcode')?.setFilterValue($event)"
|
||||||
|
/>
|
||||||
<Uploader v-if="props.canUpload">
|
<Uploader v-if="props.canUpload">
|
||||||
<Button variant="outline" size="icon" title="Upload emoji">
|
<Button variant="outline" size="icon" title="Upload emoji">
|
||||||
<Plus class="size-4" />
|
<Plus class="size-4"/>
|
||||||
</Button>
|
</Button>
|
||||||
</Uploader>
|
</Uploader>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger as-child>
|
<DropdownMenuTrigger as-child>
|
||||||
<Button variant="outline">
|
<Button variant="outline">
|
||||||
Columns
|
Columns
|
||||||
<ChevronDown class="ml-2 size-4" />
|
<ChevronDown class="ml-2 size-4"/>
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
<DropdownMenuCheckboxItem
|
<DropdownMenuCheckboxItem
|
||||||
v-for="column in table.getAllColumns().filter((column) => column.getCanHide())" :key="column.id"
|
v-for="column in table.getAllColumns().filter((column) => column.getCanHide())"
|
||||||
class="capitalize" :model-value="column.getIsVisible()" @update:model-value="(value) => {
|
:key="column.id"
|
||||||
|
class="capitalize"
|
||||||
|
:model-value="column.getIsVisible()"
|
||||||
|
@update:model-value="(value) => {
|
||||||
column.toggleVisibility(!!value)
|
column.toggleVisibility(!!value)
|
||||||
}">
|
}"
|
||||||
|
>
|
||||||
{{ column.id }}
|
{{ column.id }}
|
||||||
</DropdownMenuCheckboxItem>
|
</DropdownMenuCheckboxItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
|
|
@ -311,19 +318,40 @@ const table = useVueTable({
|
||||||
<div class="rounded-md border">
|
<div class="rounded-md border">
|
||||||
<Table>
|
<Table>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow v-for="headerGroup in table.getHeaderGroups()" :key="headerGroup.id">
|
<TableRow
|
||||||
<TableHead v-for="header in headerGroup.headers" :key="header.id" class="">
|
v-for="headerGroup in table.getHeaderGroups()"
|
||||||
<FlexRender v-if="!header.isPlaceholder" :render="header.column.columnDef.header"
|
:key="headerGroup.id"
|
||||||
:props="header.getContext()" />
|
>
|
||||||
|
<TableHead
|
||||||
|
v-for="header in headerGroup.headers"
|
||||||
|
:key="header.id"
|
||||||
|
class=""
|
||||||
|
>
|
||||||
|
<FlexRender
|
||||||
|
v-if="!header.isPlaceholder"
|
||||||
|
:render="header.column.columnDef.header"
|
||||||
|
:props="header.getContext()"
|
||||||
|
/>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
<template v-if="table.getRowModel().rows?.length">
|
<template v-if="table.getRowModel().rows?.length">
|
||||||
<template v-for="row in table.getRowModel().rows" :key="row.id">
|
<template
|
||||||
<TableRow :data-state="row.getIsSelected() && 'selected'">
|
v-for="row in table.getRowModel().rows"
|
||||||
<TableCell v-for="cell in row.getVisibleCells()" :key="cell.id">
|
:key="row.id"
|
||||||
<FlexRender :render="cell.column.columnDef.cell" :props="cell.getContext()" />
|
>
|
||||||
|
<TableRow
|
||||||
|
:data-state="row.getIsSelected() && 'selected'"
|
||||||
|
>
|
||||||
|
<TableCell
|
||||||
|
v-for="cell in row.getVisibleCells()"
|
||||||
|
:key="cell.id"
|
||||||
|
>
|
||||||
|
<FlexRender
|
||||||
|
:render="cell.column.columnDef.cell"
|
||||||
|
:props="cell.getContext()"
|
||||||
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
<TableRow v-if="row.getIsExpanded()">
|
<TableRow v-if="row.getIsExpanded()">
|
||||||
|
|
@ -335,7 +363,10 @@ const table = useVueTable({
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<TableRow v-else>
|
<TableRow v-else>
|
||||||
<TableCell :colspan="columns.length" class="h-24 text-center">
|
<TableCell
|
||||||
|
:colspan="columns.length"
|
||||||
|
class="h-24 text-center"
|
||||||
|
>
|
||||||
No results.
|
No results.
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|
@ -345,15 +376,24 @@ const table = useVueTable({
|
||||||
|
|
||||||
<div class="flex items-center justify-end space-x-2 py-4">
|
<div class="flex items-center justify-end space-x-2 py-4">
|
||||||
<div class="flex-1 text-sm text-muted-foreground">
|
<div class="flex-1 text-sm text-muted-foreground">
|
||||||
{{ table.getFilteredSelectedRowModel().rows.length }} of
|
{{ table.getFilteredSelectedRowModel().rows.length }}of
|
||||||
{{ table.getFilteredRowModel().rows.length }} row(s) selected.
|
{{ table.getFilteredRowModel().rows.length }}row(s) selected.
|
||||||
</div>
|
</div>
|
||||||
<div class="space-x-2">
|
<div class="space-x-2">
|
||||||
<Button variant="outline" size="sm" :disabled="!table.getCanPreviousPage()"
|
<Button
|
||||||
@click="table.previousPage()">
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
:disabled="!table.getCanPreviousPage()"
|
||||||
|
@click="table.previousPage()"
|
||||||
|
>
|
||||||
Previous
|
Previous
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="outline" size="sm" :disabled="!table.getCanNextPage()" @click="table.nextPage()">
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
:disabled="!table.getCanNextPage()"
|
||||||
|
@click="table.nextPage()"
|
||||||
|
>
|
||||||
Next
|
Next
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<Dialog v-model:open="open">
|
<Dialog v-model:open="open">
|
||||||
<DialogTrigger>
|
<DialogTrigger>
|
||||||
<slot />
|
<slot/>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogTitle>
|
<DialogTitle>{{ m.whole_icy_puffin_smile() }}</DialogTitle>
|
||||||
{{ m.whole_icy_puffin_smile() }}
|
|
||||||
</DialogTitle>
|
|
||||||
<DialogDescription class="sr-only">
|
<DialogDescription class="sr-only">
|
||||||
{{ m.frail_great_marten_pet() }}
|
{{ m.frail_great_marten_pet() }}
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
|
|
@ -20,28 +18,28 @@
|
||||||
class="h-full object-cover"
|
class="h-full object-cover"
|
||||||
:src="createObjectURL(values.image as File)"
|
:src="createObjectURL(values.image as File)"
|
||||||
:alt="values.alt"
|
:alt="values.alt"
|
||||||
/>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="bg-zinc-700">
|
<div class="bg-zinc-700">
|
||||||
<img
|
<img
|
||||||
class="h-full object-cover"
|
class="h-full object-cover"
|
||||||
:src="createObjectURL(values.image as File)"
|
:src="createObjectURL(values.image as File)"
|
||||||
:alt="values.alt"
|
:alt="values.alt"
|
||||||
/>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="bg-zinc-400">
|
<div class="bg-zinc-400">
|
||||||
<img
|
<img
|
||||||
class="h-full object-cover"
|
class="h-full object-cover"
|
||||||
:src="createObjectURL(values.image as File)"
|
:src="createObjectURL(values.image as File)"
|
||||||
:alt="values.alt"
|
:alt="values.alt"
|
||||||
/>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="bg-foreground">
|
<div class="bg-foreground">
|
||||||
<img
|
<img
|
||||||
class="h-full object-cover"
|
class="h-full object-cover"
|
||||||
:src="createObjectURL(values.image as File)"
|
:src="createObjectURL(values.image as File)"
|
||||||
:alt="values.alt"
|
:alt="values.alt"
|
||||||
/>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -68,15 +66,13 @@
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
{{ m.lime_late_millipede_urge() }}
|
{{ m.lime_late_millipede_urge() }}
|
||||||
</FormDescription>
|
</FormDescription>
|
||||||
<FormMessage />
|
<FormMessage/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
<FormField v-slot="{ componentField }" name="shortcode">
|
<FormField v-slot="{ componentField }" name="shortcode">
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>
|
<FormLabel>{{ m.happy_mild_fox_gleam() }}</FormLabel>
|
||||||
{{ m.happy_mild_fox_gleam() }}
|
|
||||||
</FormLabel>
|
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
v-bind="componentField"
|
v-bind="componentField"
|
||||||
|
|
@ -86,7 +82,7 @@
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
{{ m.glad_day_kestrel_amaze() }}
|
{{ m.glad_day_kestrel_amaze() }}
|
||||||
</FormDescription>
|
</FormDescription>
|
||||||
<FormMessage />
|
<FormMessage/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
|
|
@ -101,7 +97,7 @@
|
||||||
:disabled="isSubmitting"
|
:disabled="isSubmitting"
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
|
|
@ -120,7 +116,7 @@
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
{{ m.weird_fun_jurgen_arise() }}
|
{{ m.weird_fun_jurgen_arise() }}
|
||||||
</FormDescription>
|
</FormDescription>
|
||||||
<FormMessage />
|
<FormMessage/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
|
|
@ -130,7 +126,10 @@
|
||||||
name="global"
|
name="global"
|
||||||
as-child
|
as-child
|
||||||
>
|
>
|
||||||
<FormSwitch :title="m.pink_sharp_carp_work()" :description="m.dark_pretty_hyena_link()">
|
<FormSwitch
|
||||||
|
:title="m.pink_sharp_carp_work()"
|
||||||
|
:description="m.dark_pretty_hyena_link()"
|
||||||
|
>
|
||||||
<Switch
|
<Switch
|
||||||
:model-value="value"
|
:model-value="value"
|
||||||
@update:model-value="handleChange"
|
@update:model-value="handleChange"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<Dialog />
|
<Dialog/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<section class="gap-4 flex flex-col">
|
<section class="gap-4 flex flex-col">
|
||||||
<h2 class="text-xl font-bold tracking-tight">
|
<h2 class="text-xl font-bold tracking-tight">{{ title }}</h2>
|
||||||
{{ title }}
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<slot />
|
<slot/>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,20 @@
|
||||||
<template>
|
<template>
|
||||||
<form class="grid gap-6" @submit="save">
|
<form class="grid gap-6" @submit="save">
|
||||||
<Transition name="slide-up">
|
<Transition name="slide-up">
|
||||||
<Alert v-if="dirty" class="absolute bottom-2 z-10 inset-x-2 w-[calc(100%-1rem)] grid-cols-[calc(var(--spacing)*4)_1fr_auto]!">
|
<Alert
|
||||||
<SaveOff class="size-4" />
|
v-if="dirty"
|
||||||
|
class="absolute bottom-2 z-10 inset-x-2 w-[calc(100%-1rem)] grid-cols-[calc(var(--spacing)*4)_1fr_auto]!"
|
||||||
|
>
|
||||||
|
<SaveOff class="size-4"/>
|
||||||
<AlertTitle>Unsaved changes</AlertTitle>
|
<AlertTitle>Unsaved changes</AlertTitle>
|
||||||
<Button variant="secondary" class="w-full row-span-2" type="button" :disabled="submitting">Apply</Button>
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
class="w-full row-span-2"
|
||||||
|
type="button"
|
||||||
|
:disabled="submitting"
|
||||||
|
>
|
||||||
|
Apply
|
||||||
|
</Button>
|
||||||
<AlertDescription>
|
<AlertDescription>
|
||||||
Click "apply" to save your changes.
|
Click "apply" to save your changes.
|
||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
|
|
@ -12,55 +22,101 @@
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|
||||||
<FormField v-slot="{ handleChange, handleBlur }" name="banner">
|
<FormField v-slot="{ handleChange, handleBlur }" name="banner">
|
||||||
<TextInput :title="m.bright_late_osprey_renew()" :description="m.great_level_lamb_sway()">
|
<TextInput
|
||||||
<Input type="file" accept="image/*" @change="handleChange" @blur="handleBlur" />
|
:title="m.bright_late_osprey_renew()"
|
||||||
|
:description="m.great_level_lamb_sway()"
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
type="file"
|
||||||
|
accept="image/*"
|
||||||
|
@change="handleChange"
|
||||||
|
@blur="handleBlur"
|
||||||
|
/>
|
||||||
</TextInput>
|
</TextInput>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
<FormField v-slot="{ setValue }" name="avatar">
|
<FormField v-slot="{ setValue }" name="avatar">
|
||||||
<TextInput :title="m.safe_icy_bulldog_quell()">
|
<TextInput :title="m.safe_icy_bulldog_quell()">
|
||||||
<ImageUploader v-model:image="authStore.account!.avatar" @submit-file="(file) => setValue(file)"
|
<ImageUploader
|
||||||
@submit-url="(url) => setValue(url)" />
|
v-model:image="authStore.account!.avatar"
|
||||||
|
@submit-file="(file) => setValue(file)"
|
||||||
|
@submit-url="(url) => setValue(url)"
|
||||||
|
/>
|
||||||
</TextInput>
|
</TextInput>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
<FormField v-slot="{ componentField }" name="name">
|
<FormField v-slot="{ componentField }" name="name">
|
||||||
<TextInput :title="m.mild_known_mallard_jolt()" :description="m.lime_dry_skunk_loop()">
|
<TextInput
|
||||||
<Input v-bind="componentField" />
|
:title="m.mild_known_mallard_jolt()"
|
||||||
|
:description="m.lime_dry_skunk_loop()"
|
||||||
|
>
|
||||||
|
<Input v-bind="componentField"/>
|
||||||
</TextInput>
|
</TextInput>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
<FormField v-slot="{ componentField }" name="username">
|
<FormField v-slot="{ componentField }" name="username">
|
||||||
<TextInput :title="m.neat_silly_dog_prosper()" :description="m.petty_plane_tadpole_earn()">
|
<TextInput
|
||||||
<Input v-bind="componentField" />
|
:title="m.neat_silly_dog_prosper()"
|
||||||
|
:description="m.petty_plane_tadpole_earn()"
|
||||||
|
>
|
||||||
|
<Input v-bind="componentField"/>
|
||||||
</TextInput>
|
</TextInput>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
<FormField v-slot="{ componentField }" name="bio">
|
<FormField v-slot="{ componentField }" name="bio">
|
||||||
<TextInput :title="m.next_caring_ladybug_hack()" :description="m.stale_just_anaconda_earn()">
|
<TextInput
|
||||||
<Textarea rows="10" v-bind="componentField" />
|
:title="m.next_caring_ladybug_hack()"
|
||||||
|
:description="m.stale_just_anaconda_earn()"
|
||||||
|
>
|
||||||
|
<Textarea rows="10" v-bind="componentField"/>
|
||||||
</TextInput>
|
</TextInput>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
<FormField v-slot="{ value, handleChange }" name="fields">
|
<FormField v-slot="{ value, handleChange }" name="fields">
|
||||||
<Fields :title="m.aqua_mealy_toucan_pride()" :value="value" @update:value="handleChange" />
|
<Fields
|
||||||
|
:title="m.aqua_mealy_toucan_pride()"
|
||||||
|
:value="value"
|
||||||
|
@update:value="handleChange"
|
||||||
|
/>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
<FormField v-slot="{ value, handleChange }" name="bot" as-child>
|
<FormField v-slot="{ value, handleChange }" name="bot" as-child>
|
||||||
<SwitchInput :title="m.gaudy_each_opossum_play()" :description="m.grassy_acidic_gadfly_cure()">
|
<SwitchInput
|
||||||
<Switch :model-value="value" @update:model-value="handleChange" />
|
:title="m.gaudy_each_opossum_play()"
|
||||||
|
:description="m.grassy_acidic_gadfly_cure()"
|
||||||
|
>
|
||||||
|
<Switch
|
||||||
|
:model-value="value"
|
||||||
|
@update:model-value="handleChange"
|
||||||
|
/>
|
||||||
</SwitchInput>
|
</SwitchInput>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
<FormField v-slot="{ value, handleChange }" name="locked" as-child>
|
<FormField v-slot="{ value, handleChange }" name="locked" as-child>
|
||||||
<SwitchInput :title="m.dirty_moving_shark_emerge()" :description="m.bright_fun_mouse_boil()">
|
<SwitchInput
|
||||||
<Switch :model-value="value" @update:model-value="handleChange" />
|
:title="m.dirty_moving_shark_emerge()"
|
||||||
|
:description="m.bright_fun_mouse_boil()"
|
||||||
|
>
|
||||||
|
<Switch
|
||||||
|
:model-value="value"
|
||||||
|
@update:model-value="handleChange"
|
||||||
|
/>
|
||||||
</SwitchInput>
|
</SwitchInput>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
<FormField v-slot="{ value, handleChange }" name="discoverable" as-child>
|
<FormField
|
||||||
<SwitchInput :title="m.red_vivid_cuckoo_spark()" :description="m.plain_zany_donkey_dart()">
|
v-slot="{ value, handleChange }"
|
||||||
<Switch :model-value="value" @update:model-value="handleChange" />
|
name="discoverable"
|
||||||
|
as-child
|
||||||
|
>
|
||||||
|
<SwitchInput
|
||||||
|
:title="m.red_vivid_cuckoo_spark()"
|
||||||
|
:description="m.plain_zany_donkey_dart()"
|
||||||
|
>
|
||||||
|
<Switch
|
||||||
|
:model-value="value"
|
||||||
|
@update:model-value="handleChange"
|
||||||
|
/>
|
||||||
</SwitchInput>
|
</SwitchInput>
|
||||||
</FormField>
|
</FormField>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
||||||
|
|
@ -2,30 +2,64 @@
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
{{ title }}
|
{{ title }}
|
||||||
<Button type="button" variant="secondary" size="icon" class="ml-auto" @click="addField()" :title="m.front_north_eel_gulp()">
|
<Button
|
||||||
<Plus />
|
type="button"
|
||||||
|
variant="secondary"
|
||||||
|
size="icon"
|
||||||
|
class="ml-auto"
|
||||||
|
@click="addField()"
|
||||||
|
:title="m.front_north_eel_gulp()"
|
||||||
|
>
|
||||||
|
<Plus/>
|
||||||
</Button>
|
</Button>
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
|
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<VueDraggable class="grid gap-4" v-model="list" :animation="200" handle=".drag-handle">
|
<VueDraggable
|
||||||
<div v-for="(field, index) in list" :key="field.id"
|
class="grid gap-4"
|
||||||
class="grid items-center grid-cols-[auto_repeat(3,minmax(0,1fr))_auto] gap-2">
|
v-model="list"
|
||||||
<Button as="span" variant="ghost" size="icon" class="drag-handle cursor-grab">
|
:animation="200"
|
||||||
<GripVertical />
|
handle=".drag-handle"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="(field, index) in list"
|
||||||
|
:key="field.id"
|
||||||
|
class="grid items-center grid-cols-[auto_repeat(3,minmax(0,1fr))_auto] gap-2"
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
as="span"
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
class="drag-handle cursor-grab"
|
||||||
|
>
|
||||||
|
<GripVertical/>
|
||||||
</Button>
|
</Button>
|
||||||
<Input :model-value="field.name" placeholder="Name" @update:model-value="
|
<Input
|
||||||
|
:model-value="field.name"
|
||||||
|
placeholder="Name"
|
||||||
|
@update:model-value="
|
||||||
(e) => updateKey(index, String(e))
|
(e) => updateKey(index, String(e))
|
||||||
" />
|
"
|
||||||
<Input :model-value="field.value" placeholder="Value" class="col-span-2" @update:model-value="
|
/>
|
||||||
|
<Input
|
||||||
|
:model-value="field.value"
|
||||||
|
placeholder="Value"
|
||||||
|
class="col-span-2"
|
||||||
|
@update:model-value="
|
||||||
(e) => updateValue(index, String(e))
|
(e) => updateValue(index, String(e))
|
||||||
" />
|
"
|
||||||
<Button type="button" variant="secondary" size="icon" @click="removeField(index)">
|
/>
|
||||||
<Trash />
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="secondary"
|
||||||
|
size="icon"
|
||||||
|
@click="removeField(index)"
|
||||||
|
>
|
||||||
|
<Trash/>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</VueDraggable>
|
</VueDraggable>
|
||||||
<FormMessage />
|
<FormMessage/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -6,18 +6,16 @@
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
class="h-fit w-fit p-0 m-0 relative group border overflow-hidden"
|
class="h-fit w-fit p-0 m-0 relative group border overflow-hidden"
|
||||||
>
|
>
|
||||||
<Avatar class="size-32" :src="image" :name="displayName" />
|
<Avatar class="size-32" :src="image" :name="displayName"/>
|
||||||
<div
|
<div
|
||||||
class="absolute inset-0 bg-background/80 flex group-hover:opacity-100 opacity-0 duration-200 items-center justify-center"
|
class="absolute inset-0 bg-background/80 flex group-hover:opacity-100 opacity-0 duration-200 items-center justify-center"
|
||||||
>
|
>
|
||||||
<Upload />
|
<Upload/>
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogTitle>
|
<DialogTitle>{{ m.due_hour_husky_prosper() }}</DialogTitle>
|
||||||
{{ m.due_hour_husky_prosper() }}
|
|
||||||
</DialogTitle>
|
|
||||||
<DialogDescription class="sr-only">
|
<DialogDescription class="sr-only">
|
||||||
{{ m.suave_broad_albatross_drop() }}
|
{{ m.suave_broad_albatross_drop() }}
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
|
|
@ -58,7 +56,7 @@
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
{{ m.lime_late_millipede_urge() }}
|
{{ m.lime_late_millipede_urge() }}
|
||||||
</FormDescription>
|
</FormDescription>
|
||||||
<FormMessage />
|
<FormMessage/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</FormField>
|
</FormField>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
@ -83,12 +81,14 @@
|
||||||
placeholder="peter.griffin@fox.com"
|
placeholder="peter.griffin@fox.com"
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage/>
|
||||||
<div v-if="value" class="grid gap-4 !mt-4">
|
<div v-if="value" class="grid gap-4 !mt-4">
|
||||||
<Label>{{
|
<Label>
|
||||||
|
{{
|
||||||
m.witty_honest_wallaby_support()
|
m.witty_honest_wallaby_support()
|
||||||
}}</Label>
|
}}
|
||||||
<Avatar class="size-32" :src="gravatarUrl" />
|
</Label>
|
||||||
|
<Avatar class="size-32" :src="gravatarUrl"/>
|
||||||
</div>
|
</div>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
@ -109,12 +109,14 @@
|
||||||
placeholder="https://mysite.com/avatar.webp"
|
placeholder="https://mysite.com/avatar.webp"
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage/>
|
||||||
<div v-if="value" class="grid gap-4 !mt-4">
|
<div v-if="value" class="grid gap-4 !mt-4">
|
||||||
<Label>{{
|
<Label>
|
||||||
|
{{
|
||||||
m.witty_honest_wallaby_support()
|
m.witty_honest_wallaby_support()
|
||||||
}}</Label>
|
}}
|
||||||
<Avatar class="size-32" :src="value" />
|
</Label>
|
||||||
|
<Avatar class="size-32" :src="value"/>
|
||||||
</div>
|
</div>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
<template>
|
<template>
|
||||||
<Card class="grid gap-3 text-sm max-w-sm">
|
<Card class="grid gap-3 text-sm max-w-sm">
|
||||||
<dl class="grid gap-3">
|
<dl class="grid gap-3">
|
||||||
<div v-for="[key, value] of data" :key="key" class="flex flex-row items-baseline justify-between gap-4 truncate">
|
<div
|
||||||
<dt class="text-muted-foreground">
|
v-for="[key, value] of data"
|
||||||
{{ key }}
|
:key="key"
|
||||||
</dt>
|
class="flex flex-row items-baseline justify-between gap-4 truncate"
|
||||||
<dd class="font-mono" v-if="typeof value === 'string'">{{ value }}</dd>
|
>
|
||||||
|
<dt class="text-muted-foreground">{{ key }}</dt>
|
||||||
|
<dd class="font-mono" v-if="typeof value === 'string'">
|
||||||
|
{{ value }}
|
||||||
|
</dd>
|
||||||
<dd class="font-mono" v-else>
|
<dd class="font-mono" v-else>
|
||||||
<component :is="value" />
|
<component :is="value"/>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
<template>
|
<template>
|
||||||
<Base :pref="pref" :name="name" v-slot="{ setValue, value }">
|
<TypeBase :pref="pref" :name="name" v-slot="{ setValue, value }">
|
||||||
<Switch @update:model-value="setValue" :model-value="value" />
|
<Switch @update:model-value="setValue" :model-value="value"/>
|
||||||
</Base>
|
</TypeBase>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { Switch } from "~/components/ui/switch";
|
import { Switch } from "~/components/ui/switch";
|
||||||
import type { preferences as prefs } from "../preferences";
|
import type { preferences as prefs } from "../preferences";
|
||||||
import type { BooleanPreference } from "../types";
|
import type { BooleanPreference } from "../types";
|
||||||
import Base from "./base.vue";
|
import TypeBase from "./type-base.vue";
|
||||||
|
|
||||||
const { pref, name } = defineProps<{
|
const { pref, name } = defineProps<{
|
||||||
pref: BooleanPreference;
|
pref: BooleanPreference;
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,21 @@
|
||||||
<template>
|
<template>
|
||||||
<Collapsible as-child>
|
<Collapsible as-child>
|
||||||
<Base :name="name" :pref="pref">
|
<TypeBase :name="name" :pref="pref">
|
||||||
<template #default>
|
<template #default>
|
||||||
<CollapsibleTrigger as-child>
|
<CollapsibleTrigger as-child>
|
||||||
<Button variant="outline">
|
<Button variant="outline">Open code</Button>
|
||||||
Open code
|
|
||||||
</Button>
|
|
||||||
</CollapsibleTrigger>
|
</CollapsibleTrigger>
|
||||||
</template>
|
</template>
|
||||||
<template #extra="{ setValue, value }">
|
<template #extra="{ setValue, value }">
|
||||||
<CollapsibleContent class="col-span-2 mt-2">
|
<CollapsibleContent class="col-span-2 mt-2">
|
||||||
<Textarea :rows="10" :model-value="value" @update:model-value="setValue" />
|
<Textarea
|
||||||
|
:rows="10"
|
||||||
|
:model-value="value"
|
||||||
|
@update:model-value="setValue"
|
||||||
|
/>
|
||||||
</CollapsibleContent>
|
</CollapsibleContent>
|
||||||
</template>
|
</template>
|
||||||
</Base>
|
</TypeBase>
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -27,7 +29,7 @@ import {
|
||||||
import { Textarea } from "~/components/ui/textarea";
|
import { Textarea } from "~/components/ui/textarea";
|
||||||
import type { preferences as prefs } from "../preferences";
|
import type { preferences as prefs } from "../preferences";
|
||||||
import type { CodePreference } from "../types";
|
import type { CodePreference } from "../types";
|
||||||
import Base from "./base.vue";
|
import TypeBase from "./type-base.vue";
|
||||||
|
|
||||||
const { pref, name } = defineProps<{
|
const { pref, name } = defineProps<{
|
||||||
pref: CodePreference;
|
pref: CodePreference;
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,27 @@
|
||||||
<template>
|
<template>
|
||||||
<Base :pref="pref" :name="name" v-slot="{ setValue, value }">
|
<TypeBase :pref="pref" :name="name" v-slot="{ setValue, value }">
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger as-child>
|
<DropdownMenuTrigger as-child>
|
||||||
<Button variant="outline">
|
<Button variant="outline">Pick</Button>
|
||||||
Pick
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent class="w-56">
|
<DropdownMenuContent class="w-56">
|
||||||
<DropdownMenuCheckboxItem v-for="[option, title] in Object.entries(pref.options.options)" :key="option"
|
<DropdownMenuCheckboxItem
|
||||||
:model-value="value.includes(option)" @update:model-value="checked => {
|
v-for="[option, title] in Object.entries(pref.options.options)"
|
||||||
|
:key="option"
|
||||||
|
:model-value="value.includes(option)"
|
||||||
|
@update:model-value="checked => {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
setValue([...value, option]);
|
setValue([...value, option]);
|
||||||
} else {
|
} else {
|
||||||
setValue(value.filter((v: any) => v !== option));
|
setValue(value.filter((v: any) => v !== option));
|
||||||
}
|
}
|
||||||
}">
|
}"
|
||||||
|
>
|
||||||
{{ title }}
|
{{ title }}
|
||||||
</DropdownMenuCheckboxItem>
|
</DropdownMenuCheckboxItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</Base>
|
</TypeBase>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
@ -32,7 +34,7 @@ import {
|
||||||
} from "~/components/ui/dropdown-menu";
|
} from "~/components/ui/dropdown-menu";
|
||||||
import type { preferences as prefs } from "../preferences";
|
import type { preferences as prefs } from "../preferences";
|
||||||
import type { MultiSelectPreference } from "../types";
|
import type { MultiSelectPreference } from "../types";
|
||||||
import Base from "./base.vue";
|
import TypeBase from "./type-base.vue";
|
||||||
|
|
||||||
const { pref, name } = defineProps<{
|
const { pref, name } = defineProps<{
|
||||||
pref: MultiSelectPreference<string>;
|
pref: MultiSelectPreference<string>;
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,19 @@
|
||||||
<template>
|
<template>
|
||||||
<Base :pref="pref" :name="name" v-slot="{ setValue, value }">
|
<TypeBase :pref="pref" :name="name" v-slot="{ setValue, value }">
|
||||||
<NumberField :model-value="value" @update:model-value="setValue" :min="pref.options.min" :max="pref.options.max" :step="pref.options.integer ? 1 : pref.options.step">
|
<NumberField
|
||||||
|
:model-value="value"
|
||||||
|
@update:model-value="setValue"
|
||||||
|
:min="pref.options.min"
|
||||||
|
:max="pref.options.max"
|
||||||
|
:step="pref.options.integer ? 1 : pref.options.step"
|
||||||
|
>
|
||||||
<NumberFieldContent>
|
<NumberFieldContent>
|
||||||
<NumberFieldDecrement />
|
<NumberFieldDecrement/>
|
||||||
<NumberFieldInput />
|
<NumberFieldInput/>
|
||||||
<NumberFieldIncrement />
|
<NumberFieldIncrement/>
|
||||||
</NumberFieldContent>
|
</NumberFieldContent>
|
||||||
</NumberField>
|
</NumberField>
|
||||||
</Base>
|
</TypeBase>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
@ -20,7 +26,7 @@ import {
|
||||||
} from "~/components/ui/number-field";
|
} from "~/components/ui/number-field";
|
||||||
import type { preferences as prefs } from "../preferences";
|
import type { preferences as prefs } from "../preferences";
|
||||||
import type { NumberPreference } from "../types";
|
import type { NumberPreference } from "../types";
|
||||||
import Base from "./base.vue";
|
import TypeBase from "./type-base.vue";
|
||||||
|
|
||||||
const { pref, name } = defineProps<{
|
const { pref, name } = defineProps<{
|
||||||
pref: NumberPreference;
|
pref: NumberPreference;
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,21 @@
|
||||||
<template>
|
<template>
|
||||||
<Base :pref="pref" :name="name" v-slot="{ setValue, value }">
|
<TypeBase :pref="pref" :name="name" v-slot="{ setValue, value }">
|
||||||
<Select :model-value="value" @update:model-value="setValue">
|
<Select :model-value="value" @update:model-value="setValue">
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder="Select an option" />
|
<SelectValue placeholder="Select an option"/>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectGroup>
|
<SelectGroup>
|
||||||
<SelectItem v-for="[val, title] in Object.entries(pref.options.options)" :value="val">
|
<SelectItem
|
||||||
|
v-for="[val, title] in Object.entries(pref.options.options)"
|
||||||
|
:value="val"
|
||||||
|
>
|
||||||
{{ title }}
|
{{ title }}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
</SelectGroup>
|
</SelectGroup>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</Base>
|
</TypeBase>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
@ -26,7 +29,7 @@ import {
|
||||||
} from "~/components/ui/select";
|
} from "~/components/ui/select";
|
||||||
import type { preferences as prefs } from "../preferences";
|
import type { preferences as prefs } from "../preferences";
|
||||||
import type { SelectPreference } from "../types";
|
import type { SelectPreference } from "../types";
|
||||||
import Base from "./base.vue";
|
import TypeBase from "./type-base.vue";
|
||||||
|
|
||||||
const { pref, name } = defineProps<{
|
const { pref, name } = defineProps<{
|
||||||
pref: SelectPreference<string>;
|
pref: SelectPreference<string>;
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<Base :pref="pref" :name="name" v-slot="{ setValue, value }">
|
<TypeBase :pref="pref" :name="name" v-slot="{ setValue, value }">
|
||||||
<Input placeholder="Content here..." :model-value="value" @update:model-value="setValue" />
|
<Input
|
||||||
</Base>
|
placeholder="Content here..."
|
||||||
|
:model-value="value"
|
||||||
|
@update:model-value="setValue"
|
||||||
|
/>
|
||||||
|
</TypeBase>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { Input } from "~/components/ui/input";
|
import { Input } from "~/components/ui/input";
|
||||||
import type { preferences as prefs } from "../preferences";
|
import type { preferences as prefs } from "../preferences";
|
||||||
import type { TextPreference } from "../types";
|
import type { TextPreference } from "../types";
|
||||||
import Base from "./base.vue";
|
import TypeBase from "./type-base.vue";
|
||||||
|
|
||||||
const { pref, name } = defineProps<{
|
const { pref, name } = defineProps<{
|
||||||
pref: TextPreference;
|
pref: TextPreference;
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,22 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="grid grid-cols-[minmax(0,1fr)_auto] gap-2 hover:bg-muted/40 duration-75 p-4">
|
<div
|
||||||
|
class="grid grid-cols-[minmax(0,1fr)_auto] gap-2 hover:bg-muted/40 duration-75 p-4"
|
||||||
|
>
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<h3 class="text-sm font-semibold tracking-tight">{{ pref.options.name }}</h3>
|
<h3 class="text-sm font-semibold tracking-tight">
|
||||||
<small v-if="pref.options.description" class="text-xs font-medium leading-none text-muted-foreground">{{
|
{{ pref.options.name }}
|
||||||
pref.options.description }}</small>
|
</h3>
|
||||||
|
<small
|
||||||
|
v-if="pref.options.description"
|
||||||
|
class="text-xs font-medium leading-none text-muted-foreground"
|
||||||
|
>{{
|
||||||
|
pref.options.description }}</small
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center justify-end">
|
<div class="flex items-center justify-end">
|
||||||
<slot :value="value" :set-value="setValue" />
|
<slot :value="value" :set-value="setValue"/>
|
||||||
</div>
|
</div>
|
||||||
<slot name="extra" :value="value" :set-value="setValue" />
|
<slot name="extra" :value="value" :set-value="setValue"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -1,19 +1,21 @@
|
||||||
<template>
|
<template>
|
||||||
<Collapsible as-child>
|
<Collapsible as-child>
|
||||||
<Base :pref="pref" :name="name">
|
<TypeBase :pref="pref" :name="name">
|
||||||
<template #default>
|
<template #default>
|
||||||
<CollapsibleTrigger as-child>
|
<CollapsibleTrigger as-child>
|
||||||
<Button variant="outline">
|
<Button variant="outline">Edit URL</Button>
|
||||||
Edit URL
|
|
||||||
</Button>
|
|
||||||
</CollapsibleTrigger>
|
</CollapsibleTrigger>
|
||||||
</template>
|
</template>
|
||||||
<template #extra="{ setValue, value }">
|
<template #extra="{ setValue, value }">
|
||||||
<CollapsibleContent class="col-span-2 mt-2">
|
<CollapsibleContent class="col-span-2 mt-2">
|
||||||
<UrlInput placeholder="Type URL or domain here..." :model-value="value" @update:model-value="setValue" />
|
<UrlInput
|
||||||
|
placeholder="Type URL or domain here..."
|
||||||
|
:model-value="value"
|
||||||
|
@update:model-value="setValue"
|
||||||
|
/>
|
||||||
</CollapsibleContent>
|
</CollapsibleContent>
|
||||||
</template>
|
</template>
|
||||||
</Base>
|
</TypeBase>
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -27,7 +29,7 @@ import {
|
||||||
import { Input, UrlInput } from "~/components/ui/input";
|
import { Input, UrlInput } from "~/components/ui/input";
|
||||||
import type { preferences as prefs } from "../preferences";
|
import type { preferences as prefs } from "../preferences";
|
||||||
import type { TextPreference } from "../types";
|
import type { TextPreference } from "../types";
|
||||||
import Base from "./base.vue";
|
import TypeBase from "./type-base.vue";
|
||||||
|
|
||||||
const { pref, name } = defineProps<{
|
const { pref, name } = defineProps<{
|
||||||
pref: TextPreference;
|
pref: TextPreference;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<Avatar :class="['rounded-md bg-secondary']">
|
<Avatar :class="['rounded-md bg-secondary']">
|
||||||
<AvatarFallback v-if="name">
|
<AvatarFallback v-if="name">{{ getInitials(name) }}</AvatarFallback>
|
||||||
{{ getInitials(name) }}
|
<AvatarImage v-if="src" :src="src" :alt="`${name}'s avatar`"/>
|
||||||
</AvatarFallback>
|
|
||||||
<AvatarImage v-if="src" :src="src" :alt="`${name}'s avatar`" />
|
|
||||||
</Avatar>
|
</Avatar>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,60 +1,72 @@
|
||||||
<template>
|
<template>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger as-child>
|
<DropdownMenuTrigger as-child>
|
||||||
<slot />
|
<slot/>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent class="min-w-56">
|
<DropdownMenuContent class="min-w-56">
|
||||||
<DropdownMenuGroup>
|
<DropdownMenuGroup>
|
||||||
<DropdownMenuItem as="button" @click="copyText(account.username)">
|
<DropdownMenuItem
|
||||||
<AtSign />
|
as="button"
|
||||||
|
@click="copyText(account.username)"
|
||||||
|
>
|
||||||
|
<AtSign/>
|
||||||
{{ m.cool_dark_tapir_belong() }}
|
{{ m.cool_dark_tapir_belong() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem as="button" @click="copyText(JSON.stringify(account, null, 4))">
|
<DropdownMenuItem
|
||||||
<Code />
|
as="button"
|
||||||
|
@click="copyText(JSON.stringify(account, null, 4))"
|
||||||
|
>
|
||||||
|
<Code/>
|
||||||
{{ m.yummy_moving_scallop_sail() }}
|
{{ m.yummy_moving_scallop_sail() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem as="button" @click="copyText(account.id)">
|
<DropdownMenuItem as="button" @click="copyText(account.id)">
|
||||||
<Hash />
|
<Hash/>
|
||||||
{{ m.sunny_zany_jellyfish_pop() }}
|
{{ m.sunny_zany_jellyfish_pop() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuGroup>
|
</DropdownMenuGroup>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator/>
|
||||||
<DropdownMenuGroup>
|
<DropdownMenuGroup>
|
||||||
<DropdownMenuItem as="button" @click="copyText(url)">
|
<DropdownMenuItem as="button" @click="copyText(url)">
|
||||||
<Link />
|
<Link/>
|
||||||
{{ m.ago_new_pelican_drip() }}
|
{{ m.ago_new_pelican_drip() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem as="button" @click="copyText(account.url)">
|
<DropdownMenuItem as="button" @click="copyText(account.url)">
|
||||||
<Link />
|
<Link/>
|
||||||
{{ m.solid_witty_zebra_walk() }}
|
{{ m.solid_witty_zebra_walk() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem as="a" v-if="isRemote" target="_blank" rel="noopener noreferrer" :href="account.url">
|
<DropdownMenuItem
|
||||||
<ExternalLink />
|
as="a"
|
||||||
|
v-if="isRemote"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
:href="account.url"
|
||||||
|
>
|
||||||
|
<ExternalLink/>
|
||||||
{{ m.active_trite_lark_inspire() }}
|
{{ m.active_trite_lark_inspire() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuGroup>
|
</DropdownMenuGroup>
|
||||||
<DropdownMenuSeparator v-if="authStore.isSignedIn && !isMe" />
|
<DropdownMenuSeparator v-if="authStore.isSignedIn && !isMe"/>
|
||||||
<DropdownMenuGroup v-if="authStore.isSignedIn && !isMe">
|
<DropdownMenuGroup v-if="authStore.isSignedIn && !isMe">
|
||||||
<DropdownMenuItem as="button" @click="muteUser(account.id)">
|
<DropdownMenuItem as="button" @click="muteUser(account.id)">
|
||||||
<VolumeX />
|
<VolumeX/>
|
||||||
{{ m.spare_wild_mole_intend() }}
|
{{ m.spare_wild_mole_intend() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem as="button" @click="blockUser(account.id)">
|
<DropdownMenuItem as="button" @click="blockUser(account.id)">
|
||||||
<Ban />
|
<Ban/>
|
||||||
{{ m.misty_soft_sparrow_vent() }}
|
{{ m.misty_soft_sparrow_vent() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuGroup>
|
</DropdownMenuGroup>
|
||||||
<DropdownMenuSeparator v-if="isRemote" />
|
<DropdownMenuSeparator v-if="isRemote"/>
|
||||||
<DropdownMenuGroup v-if="isRemote">
|
<DropdownMenuGroup v-if="isRemote">
|
||||||
<DropdownMenuItem as="button" @click="refresh">
|
<DropdownMenuItem as="button" @click="refresh">
|
||||||
<RefreshCw />
|
<RefreshCw/>
|
||||||
{{ m.slow_chunky_chipmunk_hush() }}
|
{{ m.slow_chunky_chipmunk_hush() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuGroup>
|
</DropdownMenuGroup>
|
||||||
<DropdownMenuSeparator v-if="authStore.isSignedIn && !isMe" />
|
<DropdownMenuSeparator v-if="authStore.isSignedIn && !isMe"/>
|
||||||
<DropdownMenuGroup v-if="authStore.isSignedIn && !isMe">
|
<DropdownMenuGroup v-if="authStore.isSignedIn && !isMe">
|
||||||
<DropdownMenuItem as="button" :disabled="true">
|
<DropdownMenuItem as="button" :disabled="true">
|
||||||
<Flag />
|
<Flag/>
|
||||||
{{ m.great_few_jaguar_rise() }}
|
{{ m.great_few_jaguar_rise() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuGroup>
|
</DropdownMenuGroup>
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger :as-child="true">
|
<TooltipTrigger :as-child="true">
|
||||||
<Badge variant="default" class="gap-1">
|
<Badge variant="default" class="gap-1">
|
||||||
<BadgeCheck v-if="verified" />
|
<BadgeCheck v-if="verified"/>
|
||||||
<img v-else-if="icon" :src="icon" alt="" class="size-4 rounded" />
|
<img v-else-if="icon" :src="icon" alt="" class="size-4 rounded">
|
||||||
{{ name }}
|
{{ name }}
|
||||||
</Badge>
|
</Badge>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<Row class="gap-2" wrap
|
<Row
|
||||||
|
class="gap-2"
|
||||||
|
wrap
|
||||||
v-if="isDeveloper || account.bot || roles.length > 0"
|
v-if="isDeveloper || account.bot || roles.length > 0"
|
||||||
>
|
>
|
||||||
<ProfileBadge
|
<ProfileBadge
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,16 @@
|
||||||
<template>
|
<template>
|
||||||
<Col class="gap-y-4">
|
<Column class="gap-y-4">
|
||||||
<Col v-for="field in fields" :key="field.name" class="gap-1 break-words">
|
<Column
|
||||||
<HeadingSmall v-render-emojis="emojis">{{ field.name }}</HeadingSmall>
|
v-for="field in fields"
|
||||||
<Html v-html="field.value" v-render-emojis="emojis" />
|
:key="field.name"
|
||||||
</Col>
|
class="gap-1 break-words"
|
||||||
</Col>
|
>
|
||||||
|
<HeadingSmall v-render-emojis="emojis">
|
||||||
|
{{ field.name }}
|
||||||
|
</HeadingSmall>
|
||||||
|
<Html v-html="field.value" v-render-emojis="emojis"/>
|
||||||
|
</Column>
|
||||||
|
</Column>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
@ -12,7 +18,7 @@ import type { CustomEmoji, Field } from "@versia/client/schemas";
|
||||||
import type { z } from "zod";
|
import type { z } from "zod";
|
||||||
import HeadingSmall from "~/components/typography/headings/small.vue";
|
import HeadingSmall from "~/components/typography/headings/small.vue";
|
||||||
import Html from "../typography/html.vue";
|
import Html from "../typography/html.vue";
|
||||||
import Col from "../typography/layout/col.vue";
|
import Column from "../typography/layout/col.vue";
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
fields: z.infer<typeof Field>[];
|
fields: z.infer<typeof Field>[];
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
:src="header"
|
:src="header"
|
||||||
alt=""
|
alt=""
|
||||||
class="object-cover w-full h-full"
|
class="object-cover w-full h-full"
|
||||||
/>
|
>
|
||||||
<!-- Shadow overlay at the bottom -->
|
<!-- Shadow overlay at the bottom -->
|
||||||
<div
|
<div
|
||||||
class="absolute bottom-0 w-full h-1/3 bg-gradient-to-b from-black/0 to-black/40"
|
class="absolute bottom-0 w-full h-1/3 bg-gradient-to-b from-black/0 to-black/40"
|
||||||
|
|
@ -15,11 +15,7 @@
|
||||||
<div
|
<div
|
||||||
class="absolute bottom-0 translate-y-1/3 left-4 flex flex-row items-start gap-2"
|
class="absolute bottom-0 translate-y-1/3 left-4 flex flex-row items-start gap-2"
|
||||||
>
|
>
|
||||||
<Avatar
|
<Avatar class="size-32 border" :src="avatar" :name="displayName"/>
|
||||||
class="size-32 border"
|
|
||||||
:src="avatar"
|
|
||||||
:name="displayName"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<Button variant="secondary" :disabled="isLoading || relationship?.requested" v-if="!isMe && authStore.isSignedIn"
|
<Button
|
||||||
@click="relationship?.following ? unfollow() : follow()">
|
variant="secondary"
|
||||||
<Loader v-if="isLoading" class="animate-spin" />
|
:disabled="isLoading || relationship?.requested"
|
||||||
|
v-if="!isMe && authStore.isSignedIn"
|
||||||
|
@click="relationship?.following ? unfollow() : follow()"
|
||||||
|
>
|
||||||
|
<Loader v-if="isLoading" class="animate-spin"/>
|
||||||
<span v-else>
|
<span v-else>
|
||||||
{{
|
{{
|
||||||
relationship?.following
|
relationship?.following
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,24 @@
|
||||||
<template>
|
<template>
|
||||||
<Row class="gap-2 w-full justify-around">
|
<Row class="gap-2 w-full justify-around">
|
||||||
<Col centered>
|
<Column centered>
|
||||||
<Bold>{{ noteCount }}</Bold>
|
<Bold>{{ noteCount }}</Bold>
|
||||||
<Small muted>{{ m.real_gray_stork_seek() }}</Small>
|
<Small muted>{{ m.real_gray_stork_seek() }}</Small>
|
||||||
</Col>
|
</Column>
|
||||||
<Col centered>
|
<Column centered>
|
||||||
<Bold>{{ followerCount }}</Bold>
|
<Bold>{{ followerCount }}</Bold>
|
||||||
<Small muted>{{ m.teal_helpful_parakeet_hike() }}</Small>
|
<Small muted>{{ m.teal_helpful_parakeet_hike() }}</Small>
|
||||||
</Col>
|
</Column>
|
||||||
<Col centered>
|
<Column centered>
|
||||||
<Bold>{{ followingCount }}</Bold>
|
<Bold>{{ followingCount }}</Bold>
|
||||||
<Small muted>{{ m.aloof_royal_samuel_startle() }}</Small>
|
<Small muted>{{ m.aloof_royal_samuel_startle() }}</Small>
|
||||||
</Col>
|
</Column>
|
||||||
</Row>
|
</Row>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import * as m from "~~/paraglide/messages.js";
|
import * as m from "~~/paraglide/messages.js";
|
||||||
import Bold from "../typography/bold.vue";
|
import Bold from "../typography/bold.vue";
|
||||||
import Col from "../typography/layout/col.vue";
|
import Column from "../typography/layout/col.vue";
|
||||||
import Row from "../typography/layout/row.vue";
|
import Row from "../typography/layout/row.vue";
|
||||||
import Small from "../typography/small.vue";
|
import Small from "../typography/small.vue";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,45 @@
|
||||||
<template>
|
<template>
|
||||||
<Card class="gap-4">
|
<Card class="gap-4">
|
||||||
<ProfileHeader :header="account.header" :avatar="account.avatar" :display-name="account.display_name" />
|
<ProfileHeader
|
||||||
|
:header="account.header"
|
||||||
|
:avatar="account.avatar"
|
||||||
|
:display-name="account.display_name"
|
||||||
|
/>
|
||||||
<Row class="justify-end gap-2">
|
<Row class="justify-end gap-2">
|
||||||
<ProfileRelationshipActions :account="account" />
|
<ProfileRelationshipActions :account="account"/>
|
||||||
<ProfileActions :account="account">
|
<ProfileActions :account="account">
|
||||||
<Button variant="secondary" size="icon">
|
<Button variant="secondary" size="icon">
|
||||||
<Ellipsis />
|
<Ellipsis/>
|
||||||
</Button>
|
</Button>
|
||||||
</ProfileActions>
|
</ProfileActions>
|
||||||
</Row>
|
</Row>
|
||||||
<Col class="justify-center">
|
<Column class="justify-center">
|
||||||
<Text class="font-bold" v-render-emojis="account.emojis">
|
<Text class="font-bold" v-render-emojis="account.emojis">
|
||||||
{{ account.display_name }}
|
{{ account.display_name }}
|
||||||
</Text>
|
</Text>
|
||||||
<Address :username="username" :domain="domain" />
|
<Address :username="username" :domain="domain"/>
|
||||||
</Col>
|
</Column>
|
||||||
<ProfileBadges :account="account" />
|
<ProfileBadges :account="account"/>
|
||||||
<Html v-html="account.note" v-render-emojis="account.emojis" />
|
<Html v-html="account.note" v-render-emojis="account.emojis"/>
|
||||||
<Separator />
|
<Separator/>
|
||||||
<ProfileFields v-if="account.fields.length > 0" :fields="account.fields" :emojis="account.emojis" />
|
<ProfileFields
|
||||||
<Separator v-if="account.fields.length > 0" />
|
v-if="account.fields.length > 0"
|
||||||
|
:fields="account.fields"
|
||||||
|
:emojis="account.emojis"
|
||||||
|
/>
|
||||||
|
<Separator v-if="account.fields.length > 0"/>
|
||||||
<Row>
|
<Row>
|
||||||
<HeadingSmall class="flex items-center gap-1">
|
<HeadingSmall class="flex items-center gap-1">
|
||||||
<CalendarDays class="size-4" /> {{ formattedCreationDate }}
|
<CalendarDays class="size-4"/>
|
||||||
|
{{ formattedCreationDate }}
|
||||||
</HeadingSmall>
|
</HeadingSmall>
|
||||||
</Row>
|
</Row>
|
||||||
<Separator />
|
<Separator/>
|
||||||
<ProfileStats :follower-count="account.followers_count" :following-count="account.following_count"
|
<ProfileStats
|
||||||
:note-count="account.statuses_count" />
|
:follower-count="account.followers_count"
|
||||||
|
:following-count="account.following_count"
|
||||||
|
:note-count="account.statuses_count"
|
||||||
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -41,7 +53,7 @@ import { Separator } from "~/components/ui/separator";
|
||||||
import { getLocale } from "~~/paraglide/runtime";
|
import { getLocale } from "~~/paraglide/runtime";
|
||||||
import HeadingSmall from "../typography/headings/small.vue";
|
import HeadingSmall from "../typography/headings/small.vue";
|
||||||
import Html from "../typography/html.vue";
|
import Html from "../typography/html.vue";
|
||||||
import Col from "../typography/layout/col.vue";
|
import Column from "../typography/layout/col.vue";
|
||||||
import Row from "../typography/layout/row.vue";
|
import Row from "../typography/layout/row.vue";
|
||||||
import Text from "../typography/text.vue";
|
import Text from "../typography/text.vue";
|
||||||
import Address from "./address.vue";
|
import Address from "./address.vue";
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
:src="account.header"
|
:src="account.header"
|
||||||
alt=""
|
alt=""
|
||||||
class="object-cover w-full h-full"
|
class="object-cover w-full h-full"
|
||||||
/>
|
>
|
||||||
<!-- Shadow overlay at the bottom -->
|
<!-- Shadow overlay at the bottom -->
|
||||||
<div
|
<div
|
||||||
class="absolute bottom-0 w-full h-1/3 bg-gradient-to-b from-black/0 to-black/40"
|
class="absolute bottom-0 w-full h-1/3 bg-gradient-to-b from-black/0 to-black/40"
|
||||||
|
|
@ -26,14 +26,14 @@
|
||||||
<Text class="font-bold" v-render-emojis="account.emojis">
|
<Text class="font-bold" v-render-emojis="account.emojis">
|
||||||
{{ account.display_name }}
|
{{ account.display_name }}
|
||||||
</Text>
|
</Text>
|
||||||
<Address :username="username" :domain="domain" />
|
<Address :username="username" :domain="domain"/>
|
||||||
</div>
|
</div>
|
||||||
<Html
|
<Html
|
||||||
v-html="account.note"
|
v-html="account.note"
|
||||||
v-render-emojis="account.emojis"
|
v-render-emojis="account.emojis"
|
||||||
class="mt-4 max-h-72 overflow-y-auto"
|
class="mt-4 max-h-72 overflow-y-auto"
|
||||||
/>
|
/>
|
||||||
<Separator v-if="account.fields.length > 0" class="mt-4" />
|
<Separator v-if="account.fields.length > 0" class="mt-4"/>
|
||||||
<ProfileFields
|
<ProfileFields
|
||||||
v-if="account.fields.length > 0"
|
v-if="account.fields.length > 0"
|
||||||
:fields="account.fields"
|
:fields="account.fields"
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,16 @@
|
||||||
class="flex-row gap-2 p-2 truncate items-center"
|
class="flex-row gap-2 p-2 truncate items-center"
|
||||||
:class="naked ? 'p-0 bg-transparent ring-0 border-none shadow-none' : ''"
|
:class="naked ? 'p-0 bg-transparent ring-0 border-none shadow-none' : ''"
|
||||||
>
|
>
|
||||||
<Avatar :src="account.avatar" :name="account.display_name" class="size-10" />
|
<Avatar
|
||||||
|
:src="account.avatar"
|
||||||
|
:name="account.display_name"
|
||||||
|
class="size-10"
|
||||||
|
/>
|
||||||
<CardContent class="leading-tight">
|
<CardContent class="leading-tight">
|
||||||
<Text class="font-semibold" v-render-emojis="account.emojis">
|
<Text class="font-semibold" v-render-emojis="account.emojis">
|
||||||
{{ account.display_name }}
|
{{ account.display_name }}
|
||||||
</Text>
|
</Text>
|
||||||
<Address :username="account.username" :domain="domain" />
|
<Address :username="account.username" :domain="domain"/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<Dialog>
|
<Dialog>
|
||||||
<DialogTrigger as-child>
|
<DialogTrigger as-child>
|
||||||
<slot />
|
<slot/>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
|
|
@ -11,16 +11,31 @@
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div v-if="authStore.identities.length > 0" class="grid gap-4 py-2">
|
<div v-if="authStore.identities.length > 0" class="grid gap-4 py-2">
|
||||||
<div v-for="identity of authStore.identities" :key="identity.account.id"
|
<div
|
||||||
class="grid grid-cols-[1fr_auto] has-[>[data-switch]]:grid-cols-[1fr_auto_auto] gap-2">
|
v-for="identity of authStore.identities"
|
||||||
<TinyCard :account="identity.account" :domain="identity.instance.domain" naked />
|
:key="identity.account.id"
|
||||||
<Button data-switch v-if="authStore.identity?.id !== identity.id"
|
class="grid grid-cols-[1fr_auto] has-[>[data-switch]]:grid-cols-[1fr_auto_auto] gap-2"
|
||||||
@click="authStore.setActiveIdentity(identity.id)" variant="outline">
|
>
|
||||||
|
<TinyCard
|
||||||
|
:account="identity.account"
|
||||||
|
:domain="identity.instance.domain"
|
||||||
|
naked
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
data-switch
|
||||||
|
v-if="authStore.identity?.id !== identity.id"
|
||||||
|
@click="authStore.setActiveIdentity(identity.id)"
|
||||||
|
variant="outline"
|
||||||
|
>
|
||||||
Switch
|
Switch
|
||||||
</Button>
|
</Button>
|
||||||
<Button @click="signOutAction(identity.id)" variant="outline" size="icon"
|
<Button
|
||||||
:title="m.sharp_big_mallard_reap()">
|
@click="signOutAction(identity.id)"
|
||||||
<LogOut />
|
variant="outline"
|
||||||
|
size="icon"
|
||||||
|
:title="m.sharp_big_mallard_reap()"
|
||||||
|
>
|
||||||
|
<LogOut/>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -31,11 +46,11 @@
|
||||||
</div>
|
</div>
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
<Button :as="NuxtLink" href="/register" variant="outline">
|
<Button :as="NuxtLink" href="/register" variant="outline">
|
||||||
<UserPlus />
|
<UserPlus/>
|
||||||
{{ m.honest_few_baboon_pop() }}
|
{{ m.honest_few_baboon_pop() }}
|
||||||
</Button>
|
</Button>
|
||||||
<Button @click="signInAction">
|
<Button @click="signInAction">
|
||||||
<LogIn />
|
<LogIn/>
|
||||||
{{ m.sunny_pink_hyena_walk() }}
|
{{ m.sunny_pink_hyena_walk() }}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
|
|
|
||||||
|
|
@ -26,32 +26,54 @@ const authStore = useAuthStore();
|
||||||
<SidebarMenu class="gap-3">
|
<SidebarMenu class="gap-3">
|
||||||
<SidebarMenuItem>
|
<SidebarMenuItem>
|
||||||
<AccountManager>
|
<AccountManager>
|
||||||
<SidebarMenuButton v-if="authStore.account && authStore.instance" size="lg">
|
<SidebarMenuButton
|
||||||
<TinyCard :account="authStore.account" :domain="authStore.instance.domain" naked />
|
v-if="authStore.account && authStore.instance"
|
||||||
<ChevronsUpDown class="ml-auto size-4" />
|
size="lg"
|
||||||
|
>
|
||||||
|
<TinyCard
|
||||||
|
:account="authStore.account"
|
||||||
|
:domain="authStore.instance.domain"
|
||||||
|
naked
|
||||||
|
/>
|
||||||
|
<ChevronsUpDown class="ml-auto size-4"/>
|
||||||
</SidebarMenuButton>
|
</SidebarMenuButton>
|
||||||
<SidebarMenuButton v-else>
|
<SidebarMenuButton v-else>
|
||||||
<UserPlus />
|
<UserPlus/>
|
||||||
{{ m.sunny_pink_hyena_walk() }}
|
{{ m.sunny_pink_hyena_walk() }}
|
||||||
<ChevronsUpDown class="ml-auto size-4" />
|
<ChevronsUpDown class="ml-auto size-4"/>
|
||||||
</SidebarMenuButton>
|
</SidebarMenuButton>
|
||||||
</AccountManager>
|
</AccountManager>
|
||||||
</SidebarMenuItem>
|
</SidebarMenuItem>
|
||||||
<SidebarMenuItem class="flex flex-col gap-2">
|
<SidebarMenuItem class="flex flex-col gap-2">
|
||||||
<Button v-if="authStore.isSignedIn" variant="default" size="lg" class="w-full group-data-[collapsible=icon]:px-4"
|
<Button
|
||||||
@click="useEvent('composer:open')">
|
v-if="authStore.isSignedIn"
|
||||||
<Pen />
|
variant="default"
|
||||||
|
size="lg"
|
||||||
|
class="w-full group-data-[collapsible=icon]:px-4"
|
||||||
|
@click="useEvent('composer:open')"
|
||||||
|
>
|
||||||
|
<Pen/>
|
||||||
<span class="group-data-[collapsible=icon]:hidden">
|
<span class="group-data-[collapsible=icon]:hidden">
|
||||||
{{ m.salty_aloof_turkey_nudge() }}
|
{{ m.salty_aloof_turkey_nudge() }}
|
||||||
</span>
|
</span>
|
||||||
</Button>
|
</Button>
|
||||||
<Button v-if="authStore.isSignedIn" size="lg" variant="secondary" @click="useEvent('preferences:open')">
|
<Button
|
||||||
<Cog />
|
v-if="authStore.isSignedIn"
|
||||||
|
size="lg"
|
||||||
|
variant="secondary"
|
||||||
|
@click="useEvent('preferences:open')"
|
||||||
|
>
|
||||||
|
<Cog/>
|
||||||
Preferences
|
Preferences
|
||||||
</Button>
|
</Button>
|
||||||
<Button v-if="$pwa?.needRefresh" variant="destructive" size="lg"
|
<Button
|
||||||
class="w-full group-data-[collapsible=icon]:px-4" @click="$pwa?.updateServiceWorker(true)">
|
v-if="$pwa?.needRefresh"
|
||||||
<DownloadCloud />
|
variant="destructive"
|
||||||
|
size="lg"
|
||||||
|
class="w-full group-data-[collapsible=icon]:px-4"
|
||||||
|
@click="$pwa?.updateServiceWorker(true)"
|
||||||
|
>
|
||||||
|
<DownloadCloud/>
|
||||||
<span class="group-data-[collapsible=icon]:hidden">
|
<span class="group-data-[collapsible=icon]:hidden">
|
||||||
{{ m.quaint_low_felix_pave() }}
|
{{ m.quaint_low_felix_pave() }}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,10 @@ const authStore = useAuthStore();
|
||||||
<SidebarMenu>
|
<SidebarMenu>
|
||||||
<SidebarMenuItem>
|
<SidebarMenuItem>
|
||||||
<NuxtLink href="/">
|
<NuxtLink href="/">
|
||||||
<InstanceSmallCard v-if="authStore.instance" :instance="authStore.instance" />
|
<InstanceSmallCard
|
||||||
|
v-if="authStore.instance"
|
||||||
|
:instance="authStore.instance"
|
||||||
|
/>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</SidebarMenuItem>
|
</SidebarMenuItem>
|
||||||
</SidebarMenu>
|
</SidebarMenu>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
<template>
|
<template>
|
||||||
<Sidebar collapsible="offcanvas">
|
<Sidebar collapsible="offcanvas">
|
||||||
<InstanceHeader />
|
<InstanceHeader/>
|
||||||
<SidebarContent>
|
<SidebarContent>
|
||||||
<SidebarGroup>
|
<SidebarGroup>
|
||||||
<SidebarGroupLabel>{{
|
<SidebarGroupLabel>
|
||||||
|
{{
|
||||||
m.trite_real_sawfish_drum()
|
m.trite_real_sawfish_drum()
|
||||||
}}</SidebarGroupLabel>
|
}}
|
||||||
|
</SidebarGroupLabel>
|
||||||
<NavItems
|
<NavItems
|
||||||
:items="
|
:items="
|
||||||
sidebarConfig.other.filter((i) =>
|
sidebarConfig.other.filter((i) =>
|
||||||
|
|
@ -15,8 +17,8 @@
|
||||||
/>
|
/>
|
||||||
</SidebarGroup>
|
</SidebarGroup>
|
||||||
</SidebarContent>
|
</SidebarContent>
|
||||||
<FooterActions />
|
<FooterActions/>
|
||||||
<SidebarRail />
|
<SidebarRail/>
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ defineProps<{
|
||||||
<SidebarMenuItem>
|
<SidebarMenuItem>
|
||||||
<CollapsibleTrigger as-child>
|
<CollapsibleTrigger as-child>
|
||||||
<SidebarMenuButton :tooltip="item.title">
|
<SidebarMenuButton :tooltip="item.title">
|
||||||
<component :is="item.icon" />
|
<component :is="item.icon"/>
|
||||||
{{ item.title }}
|
{{ item.title }}
|
||||||
<ChevronRight
|
<ChevronRight
|
||||||
class="ml-auto transition-transform group-data-[state=open]/collapsible:rotate-180"
|
class="ml-auto transition-transform group-data-[state=open]/collapsible:rotate-180"
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ defineProps<{
|
||||||
<SidebarMenuItem v-for="item in items" :key="item.title">
|
<SidebarMenuItem v-for="item in items" :key="item.title">
|
||||||
<SidebarMenuButton as-child>
|
<SidebarMenuButton as-child>
|
||||||
<NuxtLink :href="item.url">
|
<NuxtLink :href="item.url">
|
||||||
<component :is="item.icon" />
|
<component :is="item.icon"/>
|
||||||
<span>{{ item.title }}</span>
|
<span>{{ item.title }}</span>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</SidebarMenuButton>
|
</SidebarMenuButton>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
style="--sidebar-width: 24rem; --sidebar-width-mobile: 18rem"
|
style="--sidebar-width: 24rem; --sidebar-width-mobile: 18rem"
|
||||||
>
|
>
|
||||||
<SidebarContent class="overflow-y-auto *:p-2 *:gap-2">
|
<SidebarContent class="overflow-y-auto *:p-2 *:gap-2">
|
||||||
<NotificationsTimeline />
|
<NotificationsTimeline/>
|
||||||
</SidebarContent>
|
</SidebarContent>
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -14,15 +14,18 @@ const authStore = useAuthStore();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<LeftSidebar />
|
<LeftSidebar/>
|
||||||
<main class="grow h-dvh overflow-y-auto">
|
<main class="grow h-dvh overflow-y-auto">
|
||||||
<header
|
<header
|
||||||
v-if="showTimelines"
|
v-if="showTimelines"
|
||||||
class="flex h-16 items-center bg-background/80 backdrop-blur-2xl sticky top-0 inset-x-0 z-10 p-4"
|
class="flex h-16 items-center bg-background/80 backdrop-blur-2xl sticky top-0 inset-x-0 z-10 p-4"
|
||||||
>
|
>
|
||||||
<Timelines />
|
<Timelines/>
|
||||||
</header>
|
</header>
|
||||||
<slot />
|
<slot/>
|
||||||
</main>
|
</main>
|
||||||
<RightSidebar v-if="authStore.isSignedIn" v-show="preferences.display_notifications_sidebar" />
|
<RightSidebar
|
||||||
|
v-if="authStore.isSignedIn"
|
||||||
|
v-show="preferences.display_notifications_sidebar"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<Timeline type="status" :items="items" :is-loading="isLoading" :has-reached-end="hasReachedEnd"
|
<Timeline
|
||||||
:error="error" :load-next="loadNext" :load-prev="loadPrev" :remove-item="removeItem"
|
type="status"
|
||||||
:update-item="updateItem" />
|
:items="items"
|
||||||
|
:is-loading="isLoading"
|
||||||
|
:has-reached-end="hasReachedEnd"
|
||||||
|
:error="error"
|
||||||
|
:load-next="loadNext"
|
||||||
|
:load-prev="loadPrev"
|
||||||
|
:remove-item="removeItem"
|
||||||
|
:update-item="updateItem"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<Timeline type="status" :items="items" :is-loading="isLoading" :has-reached-end="hasReachedEnd"
|
<Timeline
|
||||||
:error="error" :load-next="loadNext" :load-prev="loadPrev" :remove-item="removeItem"
|
type="status"
|
||||||
:update-item="updateItem" />
|
:items="items"
|
||||||
|
:is-loading="isLoading"
|
||||||
|
:has-reached-end="hasReachedEnd"
|
||||||
|
:error="error"
|
||||||
|
:load-next="loadNext"
|
||||||
|
:load-prev="loadPrev"
|
||||||
|
:remove-item="removeItem"
|
||||||
|
:update-item="updateItem"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<Timeline type="status" :items="items" :is-loading="isLoading" :has-reached-end="hasReachedEnd"
|
<Timeline
|
||||||
:error="error" :load-next="loadNext" :load-prev="loadPrev" :remove-item="removeItem"
|
type="status"
|
||||||
:update-item="updateItem" />
|
:items="items"
|
||||||
|
:is-loading="isLoading"
|
||||||
|
:has-reached-end="hasReachedEnd"
|
||||||
|
:error="error"
|
||||||
|
:load-next="loadNext"
|
||||||
|
:load-prev="loadPrev"
|
||||||
|
:remove-item="removeItem"
|
||||||
|
:update-item="updateItem"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<Timeline type="status" :items="items" :is-loading="isLoading" :has-reached-end="hasReachedEnd"
|
<Timeline
|
||||||
:error="error" :load-next="loadNext" :load-prev="loadPrev" :remove-item="removeItem"
|
type="status"
|
||||||
:update-item="updateItem" />
|
:items="items"
|
||||||
|
:is-loading="isLoading"
|
||||||
|
:has-reached-end="hasReachedEnd"
|
||||||
|
:error="error"
|
||||||
|
:load-next="loadNext"
|
||||||
|
:load-prev="loadPrev"
|
||||||
|
:remove-item="removeItem"
|
||||||
|
:update-item="updateItem"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<Timeline type="notification" :items="items" :is-loading="isLoading"
|
<Timeline
|
||||||
:has-reached-end="hasReachedEnd" :error="error" :load-next="loadNext" :load-prev="loadPrev"
|
type="notification"
|
||||||
:remove-item="removeItem" :update-item="updateItem" />
|
:items="items"
|
||||||
|
:is-loading="isLoading"
|
||||||
|
:has-reached-end="hasReachedEnd"
|
||||||
|
:error="error"
|
||||||
|
:load-next="loadNext"
|
||||||
|
:load-prev="loadPrev"
|
||||||
|
:remove-item="removeItem"
|
||||||
|
:update-item="updateItem"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<Timeline type="status" :items="items" :is-loading="isLoading" :has-reached-end="hasReachedEnd"
|
<Timeline
|
||||||
:error="error" :load-next="loadNext" :load-prev="loadPrev" :remove-item="removeItem"
|
type="status"
|
||||||
:update-item="updateItem" />
|
:items="items"
|
||||||
|
:is-loading="isLoading"
|
||||||
|
:has-reached-end="hasReachedEnd"
|
||||||
|
:error="error"
|
||||||
|
:load-next="loadNext"
|
||||||
|
:load-prev="loadPrev"
|
||||||
|
:remove-item="removeItem"
|
||||||
|
:update-item="updateItem"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<component :is="itemComponent" :note="type === 'status' ? item : undefined" :notification="type === 'notification' ? item : (undefined as any)" @update="$emit('update', $event)"
|
<component
|
||||||
@delete="$emit('delete', item?.id)" />
|
:is="itemComponent"
|
||||||
|
:note="type === 'status' ? item : undefined"
|
||||||
|
:notification="type === 'notification' ? item : (undefined as any)"
|
||||||
|
@update="$emit('update', $event)"
|
||||||
|
@delete="$emit('delete', item?.id)"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<slot />
|
<slot/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
||||||
|
|
@ -12,17 +12,15 @@
|
||||||
@delete="removeItem"
|
@delete="removeItem"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Spinner v-if="isLoading" />
|
<Spinner v-if="isLoading"/>
|
||||||
|
|
||||||
<div v-if="error" class="timeline-error">
|
<div v-if="error" class="timeline-error">{{ error.message }}</div>
|
||||||
{{ error.message }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- If there are some posts, but the user scrolled to the end -->
|
<!-- If there are some posts, but the user scrolled to the end -->
|
||||||
<ReachedEnd v-if="hasReachedEnd && items.length > 0" />
|
<ReachedEnd v-if="hasReachedEnd && items.length > 0"/>
|
||||||
|
|
||||||
<!-- If there are no posts at all -->
|
<!-- If there are no posts at all -->
|
||||||
<NoPosts v-else-if="hasReachedEnd && items.length === 0" />
|
<NoPosts v-else-if="hasReachedEnd && items.length === 0"/>
|
||||||
|
|
||||||
<div v-else-if="!preferences.infinite_scroll" class="py-10 px-4">
|
<div v-else-if="!preferences.infinite_scroll" class="py-10 px-4">
|
||||||
<Button
|
<Button
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue