Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion packages/vuetify/src/components/VOverlay/VOverlay.sass
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
right: 0
top: 0

// Modifier
.v-overlay--absolute
position: absolute

Expand All @@ -51,6 +50,20 @@
.v-overlay--scroll-blocked
padding-inline-end: var(--v-scrollbar-offset)

@include tools.layer('overrides')
.v-overlay--justify-start
justify-content: flex-start
.v-overlay--justify-center
justify-content: center
.v-overlay--justify-end
justify-content: flex-end
.v-overlay--align-start
align-items: flex-start
.v-overlay--align-center
align-items: center
.v-overlay--align-end
align-items: flex-end

@include tools.layer('trumps')
.v-overlay-scroll-blocked
padding-inline-end: var(--v-scrollbar-offset)
Expand Down
8 changes: 7 additions & 1 deletion packages/vuetify/src/components/VOverlay/VOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import './VOverlay.sass'

// Composables
import { makeLocationStrategyProps, useLocationStrategies } from './locationStrategies'
import { getStaticLocationClasses, makeLocationStrategyProps, useLocationStrategies } from './locationStrategies'
import { makeScrollStrategyProps, useScrollStrategies } from './scrollStrategies'
import { makeActivatorProps, useActivator } from './useActivator'
import { useBackgroundColor } from '@/composables/color'
Expand Down Expand Up @@ -172,6 +172,11 @@ export const VOverlay = genericComponent<OverlaySlots>()({
})
const { dimensionStyles } = useDimension(props)
const isMounted = useHydration()
const staticLocationClasses = computed(() => {
return props.locationStrategy === 'static'
? getStaticLocationClasses(props.location)
: undefined
})
const { scopeId } = useScopeId()

watch(() => props.disabled, v => {
Expand Down Expand Up @@ -364,6 +369,7 @@ export const VOverlay = genericComponent<OverlaySlots>()({
'v-overlay--active': isActive.value,
'v-overlay--contained': props.contained,
},
staticLocationClasses.value,
themeClasses.value,
rtlClasses.value,
props.class,
Expand Down
78 changes: 28 additions & 50 deletions packages/vuetify/src/components/VOverlay/locationStrategies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,7 @@ const locationStrategies = {

export interface StrategyProps {
locationStrategy: keyof typeof locationStrategies | LocationStrategyFunction
contained?: boolean
location: Anchor
location?: Anchor
origin: Anchor | 'auto' | 'overlap'
offset?: number | string | number[]
stickToTarget?: boolean
Expand All @@ -66,10 +65,7 @@ export const makeLocationStrategyProps = propsFactory({
default: 'static',
validator: (val: any) => typeof val === 'function' || val in locationStrategies,
},
location: {
type: String as PropType<StrategyProps['location']>,
default: 'bottom',
},
location: String as PropType<StrategyProps['location']>,
origin: {
type: String as PropType<StrategyProps['origin']>,
default: 'auto',
Expand Down Expand Up @@ -129,61 +125,43 @@ export function useLocationStrategies (
}
}

function staticLocationStrategy (data: LocationStrategyData, props: StrategyProps, contentStyles: Ref<Record<string, string>>) {
if (props.contained) return
export function getStaticLocationClasses (location: Anchor | undefined) {
if (!location) return undefined

const target = ref<[x: number, y: number]>()
const connectedStyles = ref<Record<string, string>>({})
const normalized = location.includes(' ') ? location : `${location} center`

// reactive equivalent of `{ ...props, origin: 'auto' }`
const connectedProps = new Proxy(props, {
get: (target, key) => key === 'origin' ? 'auto' : Reflect.get(target, key),
})
let justify = 'center'
let align = 'center'
const inline: Record<string, string> = { left: 'start', start: 'start', right: 'end', end: 'end' }
const block: Record<string, string> = { top: 'start', bottom: 'end' }

const connected = connectedLocationStrategy(
{ ...data, target },
connectedProps,
connectedStyles
)
for (const token of normalized.split(' ')) {
if (token in inline) justify = inline[token]
else if (token in block) align = block[token]
}

return {
[`v-overlay--justify-${justify}`]: true,
[`v-overlay--align-${align}`]: true,
}
}

function staticLocationStrategy (data: LocationStrategyData, props: StrategyProps, contentStyles: Ref<Record<string, string>>) {
// Positioning is handled by CSS flexbox alignment on the overlay root, keeping
// the content's insets `auto` so user utility classes (justify-*, align-*) compose.
// Here we only forward an explicit `origin` to `transform-origin` for the transition.
function updateStyles () {
if (props.origin !== 'auto' && props.origin !== 'overlap') {
const { side, align } = parseAnchor(props.origin, data.isRtl.value)
contentStyles.value = { ...connectedStyles.value, transformOrigin: `${side} ${align}` }
contentStyles.value = { transformOrigin: `${side} ${align}` }
} else {
contentStyles.value = connectedStyles.value
contentStyles.value = {}
}
}

watch(connectedStyles, updateStyles, { deep: true })
watch([() => props.origin, data.isRtl], updateStyles)

function updateTarget () {
const viewportBox = new Box({
x: visualViewport?.offsetLeft ?? 0,
y: visualViewport?.offsetTop ?? 0,
width: visualViewport?.width ?? window.innerWidth,
height: visualViewport?.height ?? window.innerHeight,
})
watch([() => props.origin, data.isRtl], updateStyles, { immediate: true })

const point = anchorToPoint(parseAnchor(props.location, data.isRtl.value), viewportBox)
target.value = [point.x, point.y]
}

function updateLocation () {
updateTarget()
connected.updateLocation()
}

watch(() => [props.location, data.isRtl.value], () => {
updateLocation()
})

nextTick(() => {
updateLocation()
})

return { updateLocation }
return { updateLocation: () => {} }
}

/** Resolve a CSS length the browser understands (calc(), min(), vw, …) to pixels */
Expand Down Expand Up @@ -244,7 +222,7 @@ function connectedLocationStrategy (data: LocationStrategyData, props: StrategyP
}

const { preferredAnchor, preferredOrigin } = destructComputed(() => {
const parsedAnchor = parseAnchor(props.location, data.isRtl.value)
const parsedAnchor = parseAnchor(props.location ?? 'bottom', data.isRtl.value)
const parsedOrigin =
props.origin === 'overlap' ? parsedAnchor
: props.origin === 'auto' ? flipSide(parsedAnchor)
Expand Down
Loading