mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-30 11:47:55 +01:00
feat(Slideover): open programmatically (#1465)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
This commit is contained in:
@@ -23,6 +23,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</UNotifications>
|
</UNotifications>
|
||||||
<UModals />
|
<UModals />
|
||||||
|
<USlideovers />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
count: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emits = defineEmits<{
|
||||||
|
close: [];
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<USlideover>
|
||||||
|
<UCard class="flex flex-col flex-1" :ui="{ body: { base: 'flex-1' }, ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
|
||||||
|
Opened programmatically: {{ props.count }} times
|
||||||
|
</h3>
|
||||||
|
<UButton color="gray" variant="ghost" icon="i-heroicons-x-mark-20-solid" class="-my-1" @click="emits('close')" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<Placeholder class="h-full" />
|
||||||
|
</UCard>
|
||||||
|
</USlideover>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { SlideoverExampleComponent } from '#components'
|
||||||
|
const slideover = useSlideover()
|
||||||
|
const count = ref(0)
|
||||||
|
function openSlideover () {
|
||||||
|
count.value += 1
|
||||||
|
slideover.open(SlideoverExampleComponent, {
|
||||||
|
count: count.value,
|
||||||
|
onClose: slideover.close
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UButton label="Reveal slideover" @click="openSlideover" />
|
||||||
|
</template>
|
||||||
@@ -33,7 +33,7 @@ Set the `transition` prop to `false` to disable it.
|
|||||||
|
|
||||||
### Prevent close
|
### Prevent close
|
||||||
|
|
||||||
Use the `prevent-close` prop to disable the outside click alongside the `esc` keyboard shortcut. A `close-prevented` event will be emitted when the user tries to close the modal.
|
Use the `prevent-close` prop to disable the outside click alongside the `esc` keyboard shortcut. A `close-prevented` event will be emitted when the user tries to close the slideover.
|
||||||
|
|
||||||
:component-example{component="slideover-example-prevent-close"}
|
:component-example{component="slideover-example-prevent-close"}
|
||||||
|
|
||||||
@@ -53,6 +53,24 @@ defineShortcuts({
|
|||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Control programmatically
|
||||||
|
|
||||||
|
First of all, add the `USlideovers` component to your app, preferably inside `app.vue`.
|
||||||
|
|
||||||
|
```vue [app.vue]
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<UContainer>
|
||||||
|
<NuxtPage />
|
||||||
|
</UContainer>
|
||||||
|
<USlideovers />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, you can use the `useSlideover` composable to control your slideovers within your app.
|
||||||
|
|
||||||
|
:component-example{component="slideover-example-composable"}
|
||||||
## Props
|
## Props
|
||||||
|
|
||||||
:component-props
|
:component-props
|
||||||
|
|||||||
@@ -200,6 +200,10 @@ export default defineNuxtModule<ModuleOptions>({
|
|||||||
src: resolve(runtimeDir, 'plugins', 'modals')
|
src: resolve(runtimeDir, 'plugins', 'modals')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
addPlugin({
|
||||||
|
src: resolve(runtimeDir, 'plugins', 'slideovers')
|
||||||
|
})
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
|
|
||||||
addComponentsDir({
|
addComponentsDir({
|
||||||
|
|||||||
17
src/runtime/components/overlays/Slideovers.client.vue
Normal file
17
src/runtime/components/overlays/Slideovers.client.vue
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<template>
|
||||||
|
<component
|
||||||
|
:is="slideoverState.component"
|
||||||
|
v-if="slideoverState"
|
||||||
|
v-bind="slideoverState.props"
|
||||||
|
v-model="isOpen"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { inject } from 'vue'
|
||||||
|
import { useSlideover, slidOverInjectionKey } from '../../composables/useSlideover'
|
||||||
|
|
||||||
|
const slideoverState = inject(slidOverInjectionKey)
|
||||||
|
|
||||||
|
const { isOpen } = useSlideover()
|
||||||
|
</script>
|
||||||
55
src/runtime/composables/useSlideover.ts
Normal file
55
src/runtime/composables/useSlideover.ts
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import { ref, inject } from 'vue'
|
||||||
|
import { createSharedComposable } from '@vueuse/core'
|
||||||
|
import type { ShallowRef, Component, InjectionKey } from 'vue'
|
||||||
|
import type { ComponentProps } from '../types/component'
|
||||||
|
import type { Slideover, SlideoverState } from '../types/slideover'
|
||||||
|
|
||||||
|
export const slidOverInjectionKey: InjectionKey<ShallowRef<SlideoverState>> =
|
||||||
|
Symbol('nuxt-ui.slideover')
|
||||||
|
|
||||||
|
function _useSlideover () {
|
||||||
|
const slideoverState = inject(slidOverInjectionKey)
|
||||||
|
const isOpen = ref(false)
|
||||||
|
|
||||||
|
function open<T extends Component> (component: T, props?: Slideover & ComponentProps<T>) {
|
||||||
|
if (!slideoverState) {
|
||||||
|
throw new Error('useSlideover() is called without provider')
|
||||||
|
}
|
||||||
|
|
||||||
|
slideoverState.value = {
|
||||||
|
component,
|
||||||
|
props: props ?? {}
|
||||||
|
}
|
||||||
|
|
||||||
|
isOpen.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function close () {
|
||||||
|
if (!slideoverState) return
|
||||||
|
|
||||||
|
isOpen.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows updating the slideover props
|
||||||
|
*/
|
||||||
|
function patch<T extends Component = {}> (props: Partial<Slideover & ComponentProps<T>>) {
|
||||||
|
if (!slideoverState) return
|
||||||
|
|
||||||
|
slideoverState.value = {
|
||||||
|
...slideoverState.value,
|
||||||
|
props: {
|
||||||
|
...slideoverState.value.props,
|
||||||
|
...props
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
open,
|
||||||
|
close,
|
||||||
|
patch,
|
||||||
|
isOpen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useSlideover = createSharedComposable(_useSlideover)
|
||||||
13
src/runtime/plugins/slideovers.ts
Normal file
13
src/runtime/plugins/slideovers.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { defineNuxtPlugin } from '#imports'
|
||||||
|
import { shallowRef } from 'vue'
|
||||||
|
import { slidOverInjectionKey } from '../composables/useSlideover'
|
||||||
|
import type { SlideoverState } from '../types/slideover'
|
||||||
|
|
||||||
|
export default defineNuxtPlugin((nuxtApp) => {
|
||||||
|
const slideoverState = shallowRef<SlideoverState>({
|
||||||
|
component: 'div',
|
||||||
|
props: {}
|
||||||
|
})
|
||||||
|
|
||||||
|
nuxtApp.vueApp.provide(slidOverInjectionKey, slideoverState)
|
||||||
|
})
|
||||||
1
src/runtime/types/index.d.ts
vendored
1
src/runtime/types/index.d.ts
vendored
@@ -17,6 +17,7 @@ export * from './kbd'
|
|||||||
export * from './link'
|
export * from './link'
|
||||||
export * from './meter'
|
export * from './meter'
|
||||||
export * from './modal'
|
export * from './modal'
|
||||||
|
export * from './slideover'
|
||||||
export * from './notification'
|
export * from './notification'
|
||||||
export * from './popper'
|
export * from './popper'
|
||||||
export * from './progress'
|
export * from './progress'
|
||||||
|
|||||||
17
src/runtime/types/slideover.d.ts
vendored
Normal file
17
src/runtime/types/slideover.d.ts
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import type { Component } from 'vue'
|
||||||
|
|
||||||
|
interface Slideover {
|
||||||
|
ui?: any;
|
||||||
|
side?: 'right' | 'left';
|
||||||
|
transition?: boolean;
|
||||||
|
appear?: boolean;
|
||||||
|
overlay?: boolean;
|
||||||
|
preventClose?: boolean;
|
||||||
|
modelValue?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SlideoverState {
|
||||||
|
component: Component | string;
|
||||||
|
props: Slideover;
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user