mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-14 12:14:41 +01:00
feat(Slideover): open programmatically (#122)
This commit is contained in:
18
playground/components/SlideoverProgrammaticExample.vue
Normal file
18
playground/components/SlideoverProgrammaticExample.vue
Normal file
@@ -0,0 +1,18 @@
|
||||
<script lang="ts" setup>
|
||||
const slideover = useSlideover()
|
||||
|
||||
defineProps<{
|
||||
count: number
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<USlideover title="This slideover was opened programmatically">
|
||||
<template #footer>
|
||||
<div class="flex w-full justify-between">
|
||||
<p>Count: {{ count }}</p>
|
||||
<UButton color="gray" label="Close" @click="slideover.close()" />
|
||||
</div>
|
||||
</template>
|
||||
</USlideover>
|
||||
</template>
|
||||
@@ -1,5 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import { LazySlideoverProgrammaticExample } from '#components'
|
||||
|
||||
const open = ref(false)
|
||||
|
||||
const slideover = useSlideover()
|
||||
const count = ref(0)
|
||||
const openSlideover = () => {
|
||||
count.value++
|
||||
slideover.open(LazySlideoverProgrammaticExample, {
|
||||
description: 'And you can even provide a description!',
|
||||
count: count.value
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -105,5 +117,7 @@ const open = ref(false)
|
||||
<Placeholder class="h-full w-full" />
|
||||
</template>
|
||||
</USlideover>
|
||||
|
||||
<UButton label="Open programmatically" color="white" @click="openSlideover" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -68,6 +68,9 @@ export default defineNuxtModule<ModuleOptions>({
|
||||
addPlugin({
|
||||
src: resolve('./runtime/plugins/modal')
|
||||
})
|
||||
addPlugin({
|
||||
src: resolve('./runtime/plugins/slideover')
|
||||
})
|
||||
|
||||
addComponentsDir({
|
||||
path: resolve('./runtime/components'),
|
||||
|
||||
@@ -13,7 +13,7 @@ import { toRef } from 'vue'
|
||||
import { ConfigProvider, TooltipProvider, useForwardProps } from 'radix-vue'
|
||||
import { reactivePick } from '@vueuse/core'
|
||||
import { useId } from '#imports'
|
||||
import { UToaster, UModalProvider } from '#components'
|
||||
import { UToaster, UModalProvider, USlideoverProvider } from '#components'
|
||||
|
||||
const props = withDefaults(defineProps<ProviderProps>(), {
|
||||
useId: () => useId()
|
||||
@@ -33,5 +33,6 @@ const toasterProps = toRef(() => props.toaster)
|
||||
</TooltipProvider>
|
||||
|
||||
<UModalProvider />
|
||||
<USlideoverProvider />
|
||||
</ConfigProvider>
|
||||
</template>
|
||||
|
||||
12
src/runtime/components/SlideoverProvider.vue
Normal file
12
src/runtime/components/SlideoverProvider.vue
Normal file
@@ -0,0 +1,12 @@
|
||||
<script lang="ts" setup>
|
||||
import { inject } from 'vue'
|
||||
import { slideoverInjectionKey, useSlideover } from '../composables/useSlideover'
|
||||
|
||||
const slideoverState = inject(slideoverInjectionKey)
|
||||
|
||||
const { isOpen } = useSlideover()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<component :is="slideoverState.component" v-if="slideoverState" v-bind="slideoverState.props" v-model:open="isOpen" />
|
||||
</template>
|
||||
71
src/runtime/composables/useSlideover.ts
Normal file
71
src/runtime/composables/useSlideover.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { ref, inject } from 'vue'
|
||||
import type { ShallowRef, Component, InjectionKey } from 'vue'
|
||||
import { createSharedComposable } from '@vueuse/core'
|
||||
import type { SlideoverProps } from '#ui/types'
|
||||
import type { ComponentProps } from '#ui/types/component'
|
||||
|
||||
export interface SlideoverState {
|
||||
component: Component | string
|
||||
props: SlideoverProps
|
||||
}
|
||||
|
||||
export const slideoverInjectionKey: InjectionKey<ShallowRef<SlideoverState>> = Symbol('nuxt-ui.slideover')
|
||||
|
||||
function _useSlideover() {
|
||||
const slideoverState = inject(slideoverInjectionKey)
|
||||
|
||||
const isOpen = ref(false)
|
||||
|
||||
function open<T extends Component>(component: T, props?: SlideoverProps & ComponentProps<T>) {
|
||||
if (!slideoverState) {
|
||||
throw new Error('useSlideover() is called without provider')
|
||||
}
|
||||
|
||||
slideoverState.value = {
|
||||
component,
|
||||
props: props ?? {}
|
||||
}
|
||||
|
||||
isOpen.value = true
|
||||
}
|
||||
|
||||
async function close() {
|
||||
if (!slideoverState) return
|
||||
|
||||
isOpen.value = false
|
||||
}
|
||||
|
||||
function reset() {
|
||||
if (!slideoverState) return
|
||||
|
||||
slideoverState.value = {
|
||||
component: 'div',
|
||||
props: {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows updating the slideover props
|
||||
*/
|
||||
function patch<T extends Component = Record<string, never>>(props: Partial<SlideoverState & ComponentProps<T>>) {
|
||||
if (!slideoverState) return
|
||||
|
||||
slideoverState.value = {
|
||||
...slideoverState.value,
|
||||
props: {
|
||||
...slideoverState.value.props,
|
||||
...props
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
open,
|
||||
close,
|
||||
reset,
|
||||
patch,
|
||||
isOpen
|
||||
}
|
||||
}
|
||||
|
||||
export const useSlideover = createSharedComposable(_useSlideover)
|
||||
12
src/runtime/plugins/slideover.ts
Normal file
12
src/runtime/plugins/slideover.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { shallowRef } from 'vue'
|
||||
import { defineNuxtPlugin } from '#imports'
|
||||
import { slideoverInjectionKey, type SlideoverState } from '../composables/useSlideover'
|
||||
|
||||
export default defineNuxtPlugin((nuxtApp) => {
|
||||
const slideoverState = shallowRef<SlideoverState>({
|
||||
component: 'div',
|
||||
props: {}
|
||||
})
|
||||
|
||||
nuxtApp.vueApp.provide(slideoverInjectionKey, slideoverState)
|
||||
})
|
||||
Reference in New Issue
Block a user