Feat: Add option to show modal not fullscreen on mobile (#2535)
This commit is contained in:
Родитель
ede566eed9
Коммит
e0294dd8a5
|
@ -4,7 +4,7 @@
|
|||
v-bind="
|
||||
isMobile ? { title: selectedMenuItem ? selectedMenuItem.title : 'Settings' } : {}
|
||||
"
|
||||
fullscreen
|
||||
fullscreen="all"
|
||||
:show-back-button="isMobile && !!selectedMenuItem"
|
||||
@back="targetMenuItem = null"
|
||||
>
|
||||
|
|
|
@ -16,24 +16,34 @@
|
|||
</TransitionChild>
|
||||
<div class="fixed top-0 left-0 z-10 h-screen !h-[100dvh] w-screen">
|
||||
<div
|
||||
class="flex md:justify-center items-end md:items-center h-full w-full md:p-6"
|
||||
class="flex md:justify-center h-full w-full md:p-6"
|
||||
:class="[
|
||||
fullscreen === 'none' || fullscreen === 'desktop'
|
||||
? 'p-4 items-center'
|
||||
: 'items-end md:items-center'
|
||||
]"
|
||||
>
|
||||
<TransitionChild
|
||||
as="template"
|
||||
enter="ease-out duration-5000"
|
||||
enter-from="md:opacity-0 translate-y-[100%] md:translate-y-4"
|
||||
:enter-from="`md:opacity-0 ${
|
||||
fullscreen === 'mobile' || fullscreen === 'all'
|
||||
? 'translate-y-[100%]'
|
||||
: 'translate-y-4'
|
||||
} md:translate-y-4`"
|
||||
enter-to="md:opacity-100 translate-y-0"
|
||||
leave="ease-in duration-5000"
|
||||
leave-from="md:opacity-100 translate-y-0"
|
||||
leave-to="md:opacity-0 translate-y-[100%] md:translate-y-4"
|
||||
:leave-to="`md:opacity-0 ${
|
||||
fullscreen === 'mobile' || fullscreen === 'all'
|
||||
? 'translate-y-[100%]'
|
||||
: 'translate-y-4'
|
||||
} md:translate-y-4`"
|
||||
@after-leave="$emit('fully-closed')"
|
||||
>
|
||||
<DialogPanel
|
||||
:class="[
|
||||
'dialog-panel transform rounded-t-lg md:rounded-xl text-foreground overflow-hidden transition-all bg-foundation text-left shadow-xl flex flex-col md:h-auto',
|
||||
fullscreen ? 'md:h-full' : 'md:max-h-[90vh]',
|
||||
widthClasses
|
||||
]"
|
||||
:class="dialogPanelClasses"
|
||||
dialog-panel-classes
|
||||
:as="isForm ? 'form' : 'div'"
|
||||
@submit.prevent="onFormSubmit"
|
||||
>
|
||||
|
@ -73,16 +83,7 @@
|
|||
>
|
||||
<XMarkIcon class="h-4 w-4 md:w-5 md:h-5" />
|
||||
</button>
|
||||
<div
|
||||
ref="slotContainer"
|
||||
class="flex-1 simple-scrollbar overflow-y-auto text-sm sm:text-base"
|
||||
:class="
|
||||
hasTitle
|
||||
? `px-6 pb-4 ${fullscreen && 'md:p-0'}`
|
||||
: !fullscreen && 'p-6'
|
||||
"
|
||||
@scroll="onScroll"
|
||||
>
|
||||
<div ref="slotContainer" :class="slotContainerClasses" @scroll="onScroll">
|
||||
<slot>Put your content here!</slot>
|
||||
</div>
|
||||
<div
|
||||
|
@ -126,6 +127,7 @@ import { throttle } from 'lodash'
|
|||
import { isClient } from '@vueuse/core'
|
||||
|
||||
type MaxWidthValue = 'sm' | 'md' | 'lg' | 'xl'
|
||||
type FullscreenValues = 'mobile' | 'desktop' | 'all' | 'none'
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:open', v: boolean): void
|
||||
|
@ -133,10 +135,11 @@ const emit = defineEmits<{
|
|||
(e: 'back'): void
|
||||
}>()
|
||||
|
||||
const props = defineProps<{
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
open: boolean
|
||||
maxWidth?: MaxWidthValue
|
||||
fullscreen?: boolean
|
||||
fullscreen?: FullscreenValues
|
||||
hideCloser?: boolean
|
||||
showBackButton?: boolean
|
||||
/**
|
||||
|
@ -153,7 +156,11 @@ const props = defineProps<{
|
|||
* If set, the modal will be wrapped in a form element and the `onSubmit` callback will be invoked when the user submits the form
|
||||
*/
|
||||
onSubmit?: (e: SubmitEvent) => void
|
||||
}>()
|
||||
}>(),
|
||||
{
|
||||
fullscreen: 'mobile'
|
||||
}
|
||||
)
|
||||
|
||||
const slots = useSlots()
|
||||
|
||||
|
@ -197,7 +204,7 @@ const maxWidthWeight = computed(() => {
|
|||
const widthClasses = computed(() => {
|
||||
const classParts: string[] = ['w-full', 'sm:w-full']
|
||||
|
||||
if (!props.fullscreen) {
|
||||
if (!isFullscreenDesktop.value) {
|
||||
classParts.push('md:max-w-2xl')
|
||||
|
||||
if (maxWidthWeight.value >= 2) {
|
||||
|
@ -214,6 +221,56 @@ const widthClasses = computed(() => {
|
|||
return classParts.join(' ')
|
||||
})
|
||||
|
||||
const isFullscreenDesktop = computed(
|
||||
() => props.fullscreen === 'desktop' || props.fullscreen === 'all'
|
||||
)
|
||||
|
||||
const dialogPanelClasses = computed(() => {
|
||||
const classParts: string[] = [
|
||||
'transform md:rounded-xl text-foreground overflow-hidden transition-all bg-foundation text-left shadow-xl flex flex-col md:h-auto'
|
||||
]
|
||||
|
||||
if (isFullscreenDesktop.value) {
|
||||
classParts.push('md:h-full md:h-[98vh] md:!h-[98dvh]')
|
||||
} else {
|
||||
classParts.push('md:max-h-[90vh]')
|
||||
}
|
||||
|
||||
if (props.fullscreen === 'mobile') {
|
||||
classParts.push('max-md:h-[98vh] max-md:!h-[98dvh]')
|
||||
}
|
||||
|
||||
if (props.fullscreen === 'all') {
|
||||
classParts.push('h-[98vh] !h-[98dvh]')
|
||||
}
|
||||
|
||||
if (props.fullscreen === 'none' || props.fullscreen === 'desktop') {
|
||||
classParts.push('rounded-lg max-h-[90vh]')
|
||||
} else {
|
||||
classParts.push('rounded-t-lg')
|
||||
}
|
||||
|
||||
classParts.push(widthClasses.value)
|
||||
return classParts.join(' ')
|
||||
})
|
||||
|
||||
const slotContainerClasses = computed(() => {
|
||||
const classParts: string[] = [
|
||||
'flex-1 simple-scrollbar overflow-y-auto text-sm sm:text-base'
|
||||
]
|
||||
|
||||
if (hasTitle.value) {
|
||||
classParts.push('px-6 pb-4')
|
||||
if (isFullscreenDesktop.value) {
|
||||
classParts.push('md:p-0')
|
||||
}
|
||||
} else if (!isFullscreenDesktop.value) {
|
||||
classParts.push('p-6')
|
||||
}
|
||||
|
||||
return classParts.join(' ')
|
||||
})
|
||||
|
||||
const onClose = () => {
|
||||
if (props.preventCloseOnClickOutside) return
|
||||
open.value = false
|
||||
|
@ -260,9 +317,4 @@ html.dialog-open {
|
|||
html.dialog-open body {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
/* Workaround because in Tailwind vh gets added after dvh */
|
||||
.dialog-panel {
|
||||
height: 98vh;
|
||||
height: 98dvh;
|
||||
}
|
||||
</style>
|
||||
|
|
Загрузка…
Ссылка в новой задаче