UNPKG

@stackoverfloweth/prefect-design

Version:

A collection of low-level Vue components.

165 lines (130 loc) 4.61 kB
/* eslint-disable no-redeclare */ import { computed, inject, InjectionKey, provide, Ref, ref } from 'vue' import { WizardNotFound } from '@/models' import { WizardStep, UseWizard, ValidationState, WizardNavigation } from '@/types/wizard' import { getStepKey } from '@/utilities/wizard' export const useWizardKey: InjectionKey<UseWizard> = Symbol('UseWizard') export function createWizard(steps: WizardStep[] | Ref<WizardStep[]>): UseWizard { const loading = ref(false) const stepsRef = ref(steps) const currentStepIndex = ref(0) const furthestStepIndex = ref(0) const currentStep = computed(() => stepsRef.value[currentStepIndex.value]) function next(): Promise<WizardNavigation> { const index = getOneBasedIndex(currentStepIndex.value) const nextIndex = index + 1 return goto(nextIndex) } function previous(): Promise<WizardNavigation> { const index = getOneBasedIndex(currentStepIndex.value) const previousIndex = index - 1 return goto(previousIndex) } function goto(key: string): Promise<WizardNavigation> function goto(index: number): Promise<WizardNavigation> function goto(step: WizardStep): Promise<WizardNavigation> function goto(keyIndexOrStep: WizardStep | string | number): Promise<WizardNavigation> function goto(keyIndexOrStep: WizardStep | string | number): Promise<WizardNavigation> { const index = typeof keyIndexOrStep === 'number' ? getZeroBasedIndex(keyIndexOrStep) : getStepIndex(keyIndexOrStep) return new Promise<ValidationState[]>(resolve => { loading.value = true if (index < 0) { resolve([{ index: currentStepIndex.value, valid: false }]) } const validators = getValidators(index) return resolve(Promise.all(validators)) }) .then(validStates => { const firstFailure = validStates.find(({ valid }) => !valid) setCurrentStepIndex(firstFailure?.index ?? index) return { success: !firstFailure, newIndex: currentStepIndex.value, } }) .finally(() => loading.value = false) } function setCurrentStepIndex(index: number): void { let newIndex = index if (index < 0) { newIndex = 0 } if (index >= stepsRef.value.length) { newIndex = stepsRef.value.length - 1 } currentStepIndex.value = newIndex furthestStepIndex.value = Math.max(furthestStepIndex.value, newIndex) } function getZeroBasedIndex(index: number): number { return index - 1 } function getOneBasedIndex(index: number): number { return index + 1 } function getStepIndex(key: string): number function getStepIndex(step: WizardStep): number function getStepIndex(keyOrStep: string | WizardStep): number function getStepIndex(keyOrStep: string | WizardStep): number { const step = typeof keyOrStep === 'object' ? keyOrStep : getStep(keyOrStep) if (!step) { return -1 } return stepsRef.value.indexOf(step) } function getStep(key: string): WizardStep | undefined function getStep(index: number): WizardStep | undefined function getStep(keyOrIndex: string | number): WizardStep | undefined function getStep(keyOrIndex: string | number): WizardStep | undefined { if (typeof keyOrIndex === 'number') { return stepsRef.value[keyOrIndex] } return stepsRef.value.find(step => getStepKey(step) === keyOrIndex) } function setStep(key: string, step: WizardStep): void { const index = getStepIndex(key) if (index === -1) { return } stepsRef.value.splice(index, 1, step) } function getValidators(index?: number): Promise<ValidationState>[] { const targetIndex = index ?? currentStepIndex.value + 1 return stepsRef.value .slice(currentStepIndex.value, targetIndex) .map(async ({ validate }, index) => { const valid = validate ? await validate() : true return { valid, index: index + currentStepIndex.value, } }) } async function isValid(index?: number): Promise<boolean> { const validators = getValidators(index) const valid = await Promise.all(validators) return valid.every(({ valid }) => valid) } const wizard = { steps: stepsRef, currentStepIndex, currentStep, furthestStepIndex, loading, next, previous, goto, getStepIndex, getStep, setStep, isValid, } provide(useWizardKey, wizard) return wizard } export function useWizard(): UseWizard { const wizard = inject(useWizardKey) if (!wizard) { throw new WizardNotFound() } return wizard }