Skip to content

Commit

Permalink
feat: add pretty StepIndicator layout
Browse files Browse the repository at this point in the history
  • Loading branch information
icai committed Nov 28, 2024
1 parent 74496b6 commit 9be611d
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 14 deletions.
27 changes: 13 additions & 14 deletions src/components/InstallationWizard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,17 @@
<div class="p-8">
<!-- Layout with Setup Guide on the Top Side -->
<div class="mb-8">
<div class="flex justify-between items-center">
<div class="flex justify-between items-start">
<!-- Setup Guide Header -->
<div class="text-lg font-semibold text-gray-700">Setup Guide</div>

<div class="min-w-[120px] text-lg mt-3 font-semibold text-gray-700">Setup Guide</div>
<!-- Steps Navigation -->
<div class="flex space-x-4">
<div v-for="(step, index) in stepTexts" :key="index" :class="{ 'text-blue-600': currentStep === index }"
class="cursor-pointer" aria-current="step">
{{ t(step) }}
<div class="flex-1 flex justify-end">
<div class="ml-4 w-auto">
<StepIndicator :current-step="currentStep" :steps="stepTexts" />
</div>
</div>
</div>
</div>

<div class="flex justify-center mb-8">
<div class="w-24 h-24 bg-blue-100 rounded-full flex items-center justify-center">
<img v-if="logo" :src="logo" alt="Logo" class="max-w-full max-h-full p-4" />
Expand Down Expand Up @@ -90,7 +87,7 @@
</div>
</div>
</StepSection>
<StepSection v-else :data="steps[currentStep]" :title="t(steps[currentStep].title)" >
<StepSection v-else :data="steps[currentStep]" :title="t(steps[currentStep].title)">
<!-- Configuration Step -->
<template #fields="{ fields, stepKey }">
<div v-for="field in fields" :key="field.name" class="mb-4 flex items-center">
Expand Down Expand Up @@ -162,6 +159,7 @@ import { useI18n } from 'vue-i18n'
import { CheckCircleIcon, XCircleIcon } from 'lucide-vue-next'
import Notification from './Notification.vue'
import StepSection from './StepSection.vue'
import StepIndicator from './StepIndicator.vue'
import { languageLabels, defaultConfig, stepsConfig } from '../config'
import { postData, getData } from '../utils/request'
import { Step, Config } from '../types'
Expand All @@ -186,13 +184,14 @@ const currentLanguage = ref(locale.value)
const systemOverview = ref({})
const stepTexts = [
'wizard.step1',
'wizard.step2',
'wizard.step3',
'wizard.step4',
'wizard.finish',
t('wizard.step1'),
t('wizard.step2'),
t('wizard.step3'),
t('wizard.step4'),
t('wizard.finish'),
]
const steps: Step[] = stepsConfig
const notifyData = reactive({
Expand Down
121 changes: 121 additions & 0 deletions src/components/StepIndicator.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
<template>
<div
class="flex w-full mb-8"
:class="[
direction === 'horizontal' ? 'flex-row' : 'flex-col space-y-4'
]"
>
<div
v-for="(step, index) in steps"
:key="index"
class="flex items-center"
:class="[
direction === 'horizontal' ? 'flex-1' : '',
direction === 'horizontal' && index !== steps.length - 1 ? 'mr-4' : ''
]"
>
<div class="flex items-center" :class="[direction === 'vertical' ? 'flex-1' : '']">
<div
class="flex min-w-[50px] whitespace-nowrap"
:class="[direction === 'vertical' ? 'flex-row' : 'flex-col']"
>
<!-- Default Number Circle -->
<div
class="w-10 h-10 rounded-full flex items-center justify-center transition-colors duration-200"
:class="[
currentStep === index
? 'bg-blue-500 text-white'
: currentStep > index
? 'bg-green-500 text-white'
: 'bg-gray-200 text-gray-500'
]"
:aria-current="currentStep === index ? 'step' : undefined"
>
<template v-if="itemRender">
<template v-if="currentStep <= index">
<component
:is="getStepContent(step, index).icon"
class="w-5 h-5"
/>
</template>
<CheckIcon v-else class="w-5 h-5" />
</template>
<template v-else>
<span v-if="currentStep <= index">{{ index + 1 }}</span>
<CheckIcon v-else class="w-5 h-5" />
</template>
</div>
<!-- Step Text -->
<span
class="text-sm font-medium transition-colors duration-200 truncate"
:class="[
direction === 'vertical' ? 'ml-3' : 'mt-2',
currentStep === index
? 'text-blue-500'
: currentStep > index
? 'text-blue-500'
: 'text-gray-500'
]"
:title="itemRender ? getStepContent(step, index).text : step"
>
{{ itemRender ? getStepContent(step, index).text : step }}
</span>
</div>

<!-- Connector Line -->
<div
v-if="index < steps.length - 1"
:class="[
direction === 'horizontal' ? 'flex-1 h-0.5 mx-4' : 'w-0.5 h-full ml-5 mt-2',
currentStep > index ? 'bg-blue-500' : 'bg-gray-200',
direction === 'vertical' ? 'absolute top-10 bottom-0' : ''
]"
aria-hidden="true"
></div>
</div>
</div>
</div>
</template>

<script setup lang="ts">
import { CheckIcon } from 'lucide-vue-next'
import { FunctionalComponent } from 'vue'
interface Step {
text: string
[key: string]: any
}
interface ItemRenderResult {
icon: FunctionalComponent
text: string
}
interface Props {
currentStep: number
direction?: 'horizontal' | 'vertical'
steps: Step[] | string[]
itemRender?: (props: { step: Step; index: number; current: number }) => ItemRenderResult
}
const props = withDefaults(defineProps<Props>(), {
direction: 'horizontal',
itemRender: undefined
})
// Helper function to get step content
const getStepContent = (step: Step | string, index: number): ItemRenderResult => {
if (props.itemRender) {
return props.itemRender({
step: typeof step === 'string' ? { text: step } : step,
index,
current: props.currentStep
})
}
return {
icon: CheckIcon,
text: typeof step === 'string' ? step : step.text
}
}
</script>

0 comments on commit 9be611d

Please sign in to comment.