mirror of
https://github.com/moeru-ai/airi.git
synced 2026-08-14 08:52:42 +00:00
refactor(stage-ui): improve onboarding, scrollable, and animated (#2215)
This commit is contained in:
@@ -53,10 +53,8 @@ async function handleConfigured() {
|
||||
:class="bgClass"
|
||||
>
|
||||
<div class="min-h-8 w-full flex-shrink-0 select-none drag-region" :class="bgClass" />
|
||||
<div class="onboarding-scroll min-h-0 w-full flex flex-1 flex-col overflow-hidden px-10">
|
||||
<div class="onboarding-content min-h-0 flex flex-1 flex-col overflow-hidden">
|
||||
<OnboardingScreen :extra-steps="extraSteps" @skipped="handleSkipped" @configured="handleConfigured" />
|
||||
</div>
|
||||
<div class="mx-8 mb-8 mt-2 min-h-0 flex flex-1 flex-col">
|
||||
<OnboardingScreen :extra-steps="extraSteps" @skipped="handleSkipped" @configured="handleConfigured" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -69,15 +67,6 @@ async function handleConfigured() {
|
||||
.onboarding-root::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.onboarding-content {
|
||||
padding: 8px 0 20px 0;
|
||||
}
|
||||
|
||||
.onboarding-scroll {
|
||||
padding-top: 8px;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<route lang="yaml">
|
||||
|
||||
@@ -36,7 +36,7 @@ onMounted(() => screenSafeArea.update())
|
||||
<VisuallyHidden>
|
||||
<DialogTitle>Onboarding</DialogTitle>
|
||||
</VisuallyHidden>
|
||||
<div class="min-h-0 min-w-0 w-full flex flex-1 flex-col overflow-hidden">
|
||||
<div class="min-h-0 min-w-0 w-full flex flex-1 flex-col">
|
||||
<Onboarding :extra-steps="props.extraSteps" @configured="emit('configured')" @skipped="emit('skipped')" />
|
||||
</div>
|
||||
</DialogContent>
|
||||
@@ -67,7 +67,7 @@ onMounted(() => screenSafeArea.update())
|
||||
'[div&]:bg-neutral-400 [div&]:dark:bg-neutral-600',
|
||||
]"
|
||||
/>
|
||||
<div class="min-h-0 min-w-0 w-full flex flex-1 flex-col overflow-hidden">
|
||||
<div class="min-h-0 min-w-0 w-full flex flex-1 flex-col">
|
||||
<Onboarding :extra-steps="props.extraSteps" @configured="emit('configured')" @skipped="emit('skipped')" />
|
||||
</div>
|
||||
</DrawerContent>
|
||||
|
||||
@@ -221,13 +221,13 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="onboarding-step-container" min-h-0 w-full flex flex-1 flex-col overflow-hidden>
|
||||
<div class="onboarding-step-container" min-h-0 flex flex-1 flex-col>
|
||||
<Transition :name="direction === 'next' ? 'slide-next' : 'slide-prev'" mode="out-in">
|
||||
<component
|
||||
:is="currentStep.component"
|
||||
v-if="currentStep"
|
||||
:key="currentStep.id"
|
||||
class="min-h-0 min-w-0 flex flex-1 flex-col overflow-hidden"
|
||||
class="flex flex-1 flex-col"
|
||||
v-bind="currentStepProps"
|
||||
:on-next="requestNextStep"
|
||||
:on-previous="requestPreviousStep"
|
||||
@@ -237,10 +237,6 @@ onMounted(() => {
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.onboarding-step-container {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.slide-next-enter-active,
|
||||
.slide-next-leave-active,
|
||||
.slide-prev-enter-active,
|
||||
|
||||
+3
-2
@@ -30,7 +30,7 @@ const selectedProviderIdModel = computed({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div h-full flex flex-col gap-4>
|
||||
<div h-full min-h-0 flex flex-col gap-4>
|
||||
<div sticky top-0 z-100 flex flex-shrink-0 items-center gap-2>
|
||||
<button outline-none @click="props.onPrevious">
|
||||
<div class="i-solar:alt-arrow-left-line-duotone h-5 w-5" />
|
||||
@@ -40,7 +40,7 @@ const selectedProviderIdModel = computed({
|
||||
</h2>
|
||||
<div class="h-5 w-5" />
|
||||
</div>
|
||||
<div class="flex-1 overflow-y-auto">
|
||||
<div class="min-h-0 flex-1 overflow-y-auto">
|
||||
<div class="grid grid-cols-1 gap-3 sm:grid-cols-2">
|
||||
<RadioCardDetail
|
||||
v-for="provider in props.popularProviders"
|
||||
@@ -56,6 +56,7 @@ const selectedProviderIdModel = computed({
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
class="flex-shrink-0"
|
||||
:label="t('settings.dialogs.onboarding.next')"
|
||||
:disabled="!selectedProviderIdModel"
|
||||
@click="props.onNext"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import type { OnboardingStepNextHandler } from './types'
|
||||
|
||||
import { all } from '@proj-airi/i18n'
|
||||
import { Button } from '@proj-airi/ui'
|
||||
import { AnimatedContent, Button } from '@proj-airi/ui'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import {
|
||||
DropdownMenuContent,
|
||||
@@ -55,9 +55,11 @@ function handleLocalSetup() {
|
||||
'h-8 w-8',
|
||||
'flex items-center justify-center',
|
||||
'rounded-lg',
|
||||
'outline-none',
|
||||
'text-neutral-500 transition-colors duration-200',
|
||||
'hover:bg-neutral-100/80 hover:text-neutral-700',
|
||||
'dark:text-neutral-400 dark:hover:bg-neutral-800/80 dark:hover:text-neutral-200',
|
||||
'data-[state=open]:bg-neutral-100/80 dark:data-[state=open]:bg-neutral-800/80',
|
||||
]"
|
||||
:aria-label="t('settings.language.title')"
|
||||
>
|
||||
@@ -65,29 +67,32 @@ function handleLocalSetup() {
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuPortal>
|
||||
<DropdownMenuContent
|
||||
as-child
|
||||
align="end"
|
||||
side="bottom"
|
||||
:side-offset="6"
|
||||
:class="[
|
||||
'z-10000 min-w-36 rounded-xl border p-1 shadow-lg outline-none backdrop-blur-md',
|
||||
'border-neutral-200/80 bg-neutral-100/80 text-neutral-700',
|
||||
'dark:border-neutral-700/60 dark:bg-neutral-800/80 dark:text-neutral-100',
|
||||
'border-neutral-100/80 bg-neutral-100/80 text-neutral-700',
|
||||
'dark:border-neutral-800/60 dark:bg-neutral-800/80 dark:text-neutral-100',
|
||||
]"
|
||||
>
|
||||
<DropdownMenuItem
|
||||
v-for="lang in languages"
|
||||
:key="lang.value"
|
||||
:class="[
|
||||
'flex cursor-pointer select-none items-center rounded-lg px-3 py-2',
|
||||
'text-sm leading-none outline-none',
|
||||
'data-[highlighted]:bg-primary-50/80 dark:data-[highlighted]:bg-primary-900/40',
|
||||
'transition-colors duration-150 ease-in-out',
|
||||
lang.value === language ? 'text-primary-500 dark:text-primary-300' : '',
|
||||
]"
|
||||
@select="() => language = lang.value"
|
||||
>
|
||||
{{ lang.label }}
|
||||
</DropdownMenuItem>
|
||||
<AnimatedContent>
|
||||
<DropdownMenuItem
|
||||
v-for="lang in languages"
|
||||
:key="lang.value"
|
||||
:class="[
|
||||
'flex cursor-pointer select-none items-center rounded-lg px-3 py-2',
|
||||
'text-sm leading-none outline-none',
|
||||
'data-[highlighted]:bg-primary-100/80 dark:data-[highlighted]:bg-primary-900/40',
|
||||
'transition-colors duration-150 ease-in-out',
|
||||
lang.value === language ? 'text-primary-500 dark:text-primary-300' : '',
|
||||
]"
|
||||
@select="() => language = lang.value"
|
||||
>
|
||||
{{ lang.label }}
|
||||
</DropdownMenuItem>
|
||||
</AnimatedContent>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenuPortal>
|
||||
</DropdownMenuRoot>
|
||||
@@ -100,7 +105,7 @@ function handleLocalSetup() {
|
||||
:duration="500"
|
||||
:class="['mb-1', 'flex', 'justify-center', 'md:mb-4', 'md:pt-8', 'lg:pt-16']"
|
||||
>
|
||||
<img :src="onboardingLogo" max-h="50" aspect-square h-auto w-auto object-cover>
|
||||
<img :src="onboardingLogo" max-h="50" aspect-square h-auto w-auto select-none object-cover>
|
||||
</div>
|
||||
<h2
|
||||
v-motion
|
||||
@@ -122,25 +127,28 @@ function handleLocalSetup() {
|
||||
{{ t('settings.dialogs.onboarding.description') }}
|
||||
</p>
|
||||
</div>
|
||||
<div :class="['flex', 'flex-col', 'gap-3', 'md:flex-row']">
|
||||
<div :class="['flex', 'flex-col', 'gap-3', 'md:flex-row', 'm-2']">
|
||||
<Button
|
||||
v-motion
|
||||
:initial="{ opacity: 0 }"
|
||||
:enter="{ opacity: 1 }"
|
||||
:duration="500"
|
||||
:delay="200"
|
||||
v-motion="{
|
||||
initial: { opacity: 0 },
|
||||
enter: { opacity: 1 },
|
||||
duration: 500,
|
||||
delay: 200,
|
||||
}"
|
||||
color="primary"
|
||||
variant="secondary"
|
||||
:label="t('settings.dialogs.onboarding.loginAction')"
|
||||
:class="['flex-1']"
|
||||
@click="handleLogin"
|
||||
/>
|
||||
<Button
|
||||
v-if="props.customProviderSetupEnabled"
|
||||
v-motion
|
||||
:initial="{ opacity: 0 }"
|
||||
:enter="{ opacity: 1 }"
|
||||
:duration="500"
|
||||
:delay="250"
|
||||
|
||||
v-motion="{
|
||||
initial: { opacity: 0 },
|
||||
enter: { opacity: 1 },
|
||||
duration: 500,
|
||||
delay: 250,
|
||||
}"
|
||||
:label="t('settings.dialogs.onboarding.setupWithoutSigningIn')"
|
||||
:class="['flex-1']"
|
||||
@click="handleLocalSetup"
|
||||
|
||||
Reference in New Issue
Block a user