mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-07-06 23:11:43 +08:00
fix: adjust dialog fullscreen breakpoint by width
This commit is contained in:
89
src/components/dialog/AppDialog.ts
Normal file
89
src/components/dialog/AppDialog.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import { defineComponent, h } from 'vue'
|
||||
import { useDisplay } from 'vuetify'
|
||||
import { VDialog as VuetifyDialog } from 'vuetify/components/VDialog'
|
||||
|
||||
const DEFAULT_ROOT_FONT_SIZE = 16
|
||||
|
||||
/**
|
||||
* 判断传入的 fullscreen 属性是否已请求全屏展示。
|
||||
*/
|
||||
function isFullscreenRequested(value: unknown) {
|
||||
return value !== undefined && value !== null && value !== false
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文档根字号,用于把 rem/em 宽度限制换算成像素。
|
||||
*/
|
||||
function getRootFontSize() {
|
||||
if (typeof window === 'undefined') return DEFAULT_ROOT_FONT_SIZE
|
||||
|
||||
const rootFontSize = Number.parseFloat(window.getComputedStyle(document.documentElement).fontSize)
|
||||
return Number.isFinite(rootFontSize) ? rootFontSize : DEFAULT_ROOT_FONT_SIZE
|
||||
}
|
||||
|
||||
/**
|
||||
* 将弹窗宽度限制换算成像素,无法可靠解析的复杂表达式返回空值。
|
||||
*/
|
||||
function parseDialogWidthLimit(value: unknown, viewportWidth: number) {
|
||||
if (typeof value === 'number') return Number.isFinite(value) ? value : undefined
|
||||
if (typeof value !== 'string') return undefined
|
||||
|
||||
const trimmedValue = value.trim()
|
||||
if (!trimmedValue) return undefined
|
||||
|
||||
const numericValue = Number(trimmedValue)
|
||||
if (Number.isFinite(numericValue)) return numericValue
|
||||
|
||||
const lengthMatch = trimmedValue.match(/^(-?\d*\.?\d+)(px|rem|em|vw|%)$/i)
|
||||
if (!lengthMatch) return undefined
|
||||
|
||||
const [, rawAmount, rawUnit] = lengthMatch
|
||||
const amount = Number(rawAmount)
|
||||
if (!Number.isFinite(amount)) return undefined
|
||||
|
||||
const unit = rawUnit.toLowerCase()
|
||||
if (unit === 'px') return amount
|
||||
if (unit === 'rem' || unit === 'em') return amount * getRootFontSize()
|
||||
if (unit === 'vw') return (viewportWidth * amount) / 100
|
||||
if (unit === '%') return (viewportWidth * amount) / 100
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 maxWidth 与 width 的有效上限,收窄 fullscreen 的生效宽度。
|
||||
*/
|
||||
function resolveFullscreen(
|
||||
fullscreen: unknown,
|
||||
maxWidth: unknown,
|
||||
width: unknown,
|
||||
viewportWidth: number,
|
||||
) {
|
||||
if (!isFullscreenRequested(fullscreen)) return false
|
||||
|
||||
const widthLimits = [maxWidth, width]
|
||||
.map(value => parseDialogWidthLimit(value, viewportWidth))
|
||||
.filter((value): value is number => value !== undefined)
|
||||
|
||||
if (!widthLimits.length) return true
|
||||
|
||||
return viewportWidth <= Math.min(...widthLimits)
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AppDialog',
|
||||
inheritAttrs: false,
|
||||
/**
|
||||
* 渲染项目统一弹窗,并在宽度上限小于移动全屏断点时收窄 fullscreen 条件。
|
||||
*/
|
||||
setup(_, { attrs, slots }) {
|
||||
const display = useDisplay()
|
||||
|
||||
return () => {
|
||||
const maxWidth = attrs.maxWidth ?? attrs['max-width']
|
||||
const resolvedFullscreen = resolveFullscreen(attrs.fullscreen, maxWidth, attrs.width, display.width.value)
|
||||
|
||||
return h(VuetifyDialog, { ...attrs, fullscreen: resolvedFullscreen }, slots)
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -2,6 +2,7 @@ import { createVuetify } from 'vuetify'
|
||||
import * as components from 'vuetify/components'
|
||||
import { VBtn } from 'vuetify/components/VBtn'
|
||||
import * as labsComponents from 'vuetify/labs/components'
|
||||
import AppDialog from '@/components/dialog/AppDialog'
|
||||
import defaults from './defaults'
|
||||
import { icons } from './icons'
|
||||
import theme from './theme'
|
||||
@@ -15,6 +16,7 @@ export default createVuetify({
|
||||
theme,
|
||||
components: {
|
||||
...components,
|
||||
VDialog: AppDialog,
|
||||
...labsComponents,
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user