Files
Modrinth-plus/packages/ui/src/stories/base/ReadyTransition.stories.ts
Calum H. 176d4301c3 feat: shared loading state + cleanup loading state management (#5835)
* feat: implement shared loading bar component and polished loading states across the app

* feat: align loading states + ensureQueryData changes

* fix: lint + bugs

* fix: skeleton for manage servers page

* fix: merge conflict fix
2026-04-18 18:46:39 +00:00

152 lines
4.3 KiB
TypeScript

import type { Meta, StoryObj } from '@storybook/vue3-vite'
import { onMounted, ref } from 'vue'
import LoadingBar from '../../components/base/LoadingBar.vue'
import ReadyTransition from '../../components/base/ReadyTransition.vue'
import { createLoadingStateCore } from '../../composables/use-loading-state-core'
import { provideLoadingState } from '../../providers/loading-state'
const meta = {
title: 'Base/ReadyTransition',
component: ReadyTransition,
} satisfies Meta<typeof ReadyTransition>
export default meta
type Story = StoryObj<typeof meta>
export const Idle: Story = {
render: () => ({
components: { ReadyTransition, LoadingBar },
setup() {
provideLoadingState(createLoadingStateCore())
return { pending: ref(false) }
},
template: `
<div class="relative">
<LoadingBar />
<ReadyTransition :pending="pending">
<div class="rounded bg-bg-raised p-4 text-contrast">Slot content (already ready).</div>
</ReadyTransition>
</div>
`,
}),
}
/** Pending false from mount — no enter fade (cache-hit path). */
export const CacheHit: Story = {
render: () => ({
components: { ReadyTransition, LoadingBar },
setup() {
provideLoadingState(createLoadingStateCore())
return { pending: ref(false) }
},
template: `
<div class="relative">
<LoadingBar />
<p class="text-secondary mb-4">pending stays false — content should appear with no fade-in.</p>
<ReadyTransition :pending="pending">
<div class="rounded bg-bg-raised p-4 text-contrast">Cached content visible immediately.</div>
</ReadyTransition>
</div>
`,
}),
}
/** Cold load: pending true then false — fade-in runs. */
export const ColdLoad: Story = {
render: () => ({
components: { ReadyTransition, LoadingBar },
setup() {
provideLoadingState(createLoadingStateCore())
const pending = ref(true)
onMounted(() => {
setTimeout(() => (pending.value = false), 600)
})
return { pending }
},
template: `
<div class="relative">
<LoadingBar />
<p class="text-secondary mb-4">Pending 600ms then ready — content fades in; bar runs while pending.</p>
<ReadyTransition :pending="pending">
<div class="rounded bg-bg-raised p-4 text-contrast">Content after cold load.</div>
</ReadyTransition>
</div>
`,
}),
}
export const PendingThenReady: Story = {
render: () => ({
components: { ReadyTransition, LoadingBar },
setup() {
provideLoadingState(createLoadingStateCore())
const pending = ref(true)
onMounted(() => {
setTimeout(() => (pending.value = false), 2000)
})
return { pending }
},
template: `
<div class="relative">
<LoadingBar />
<p class="text-secondary mb-4">Pending for 2s, then content fades in. Bar runs at top.</p>
<ReadyTransition :pending="pending">
<div class="rounded bg-bg-raised p-4 text-contrast">Slot content revealed.</div>
</ReadyTransition>
</div>
`,
}),
}
export const StackedTwoTransitions: Story = {
render: () => ({
components: { ReadyTransition, LoadingBar },
setup() {
provideLoadingState(createLoadingStateCore())
const a = ref(true)
const b = ref(true)
onMounted(() => {
setTimeout(() => (a.value = false), 1500)
setTimeout(() => (b.value = false), 3000)
})
return { a, b }
},
template: `
<div class="relative grid gap-4">
<LoadingBar />
<ReadyTransition :pending="a">
<div class="rounded bg-bg-raised p-4 text-contrast">Panel A (ready at 1.5s).</div>
</ReadyTransition>
<ReadyTransition :pending="b">
<div class="rounded bg-bg-raised p-4 text-contrast">Panel B (ready at 3s).</div>
</ReadyTransition>
<p class="text-secondary">Bar stays visible until BOTH panels resolve.</p>
</div>
`,
}),
}
export const Silent: Story = {
render: () => ({
components: { ReadyTransition, LoadingBar },
setup() {
provideLoadingState(createLoadingStateCore())
const pending = ref(true)
onMounted(() => {
setTimeout(() => (pending.value = false), 1500)
})
return { pending }
},
template: `
<div class="relative">
<LoadingBar />
<p class="text-secondary mb-4">silent=true — fades locally but does NOT raise the loading bar.</p>
<ReadyTransition :pending="pending" silent>
<div class="rounded bg-bg-raised p-4 text-contrast">Silent slot content.</div>
</ReadyTransition>
</div>
`,
}),
}