289 lines
8.8 KiB
JavaScript
289 lines
8.8 KiB
JavaScript
import { withDirectives as _withDirectives, resolveDirective as _resolveDirective, vShow as _vShow, Fragment as _Fragment, createVNode as _createVNode, mergeProps as _mergeProps } from "vue";
|
|
// Styles
|
|
import "./VOverlay.css";
|
|
|
|
// Composables
|
|
import { makeLocationStrategyProps, useLocationStrategies } from "./locationStrategies.mjs";
|
|
import { makeScrollStrategyProps, useScrollStrategies } from "./scrollStrategies.mjs";
|
|
import { makeActivatorProps, useActivator } from "./useActivator.mjs";
|
|
import { useBackgroundColor } from "../../composables/color.mjs";
|
|
import { makeComponentProps } from "../../composables/component.mjs";
|
|
import { makeDimensionProps, useDimension } from "../../composables/dimensions.mjs";
|
|
import { useHydration } from "../../composables/hydration.mjs";
|
|
import { makeLazyProps, useLazy } from "../../composables/lazy.mjs";
|
|
import { useRtl } from "../../composables/locale.mjs";
|
|
import { useProxiedModel } from "../../composables/proxiedModel.mjs";
|
|
import { useBackButton, useRouter } from "../../composables/router.mjs";
|
|
import { useScopeId } from "../../composables/scopeId.mjs";
|
|
import { useStack } from "../../composables/stack.mjs";
|
|
import { useTeleport } from "../../composables/teleport.mjs";
|
|
import { makeThemeProps, provideTheme } from "../../composables/theme.mjs";
|
|
import { useToggleScope } from "../../composables/toggleScope.mjs";
|
|
import { makeTransitionProps, MaybeTransition } from "../../composables/transition.mjs"; // Directives
|
|
import { ClickOutside } from "../../directives/click-outside/index.mjs"; // Utilities
|
|
import { computed, mergeProps, onBeforeUnmount, ref, Teleport, toRef, Transition, watch } from 'vue';
|
|
import { animate, convertToUnit, genericComponent, getScrollParent, IN_BROWSER, propsFactory, standardEasing, useRender } from "../../util/index.mjs"; // Types
|
|
function Scrim(props) {
|
|
const {
|
|
modelValue,
|
|
color,
|
|
...rest
|
|
} = props;
|
|
return _createVNode(Transition, {
|
|
"name": "fade-transition",
|
|
"appear": true
|
|
}, {
|
|
default: () => [props.modelValue && _createVNode("div", _mergeProps({
|
|
"class": ['v-overlay__scrim', props.color.backgroundColorClasses.value],
|
|
"style": props.color.backgroundColorStyles.value
|
|
}, rest), null)]
|
|
});
|
|
}
|
|
export const makeVOverlayProps = propsFactory({
|
|
absolute: Boolean,
|
|
attach: [Boolean, String, Object],
|
|
closeOnBack: {
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
contained: Boolean,
|
|
contentClass: null,
|
|
contentProps: null,
|
|
disabled: Boolean,
|
|
opacity: [Number, String],
|
|
noClickAnimation: Boolean,
|
|
modelValue: Boolean,
|
|
persistent: Boolean,
|
|
scrim: {
|
|
type: [Boolean, String],
|
|
default: true
|
|
},
|
|
zIndex: {
|
|
type: [Number, String],
|
|
default: 2000
|
|
},
|
|
...makeActivatorProps(),
|
|
...makeComponentProps(),
|
|
...makeDimensionProps(),
|
|
...makeLazyProps(),
|
|
...makeLocationStrategyProps(),
|
|
...makeScrollStrategyProps(),
|
|
...makeThemeProps(),
|
|
...makeTransitionProps()
|
|
}, 'VOverlay');
|
|
export const VOverlay = genericComponent()({
|
|
name: 'VOverlay',
|
|
directives: {
|
|
ClickOutside
|
|
},
|
|
inheritAttrs: false,
|
|
props: {
|
|
_disableGlobalStack: Boolean,
|
|
...makeVOverlayProps()
|
|
},
|
|
emits: {
|
|
'click:outside': e => true,
|
|
'update:modelValue': value => true,
|
|
afterLeave: () => true
|
|
},
|
|
setup(props, _ref) {
|
|
let {
|
|
slots,
|
|
attrs,
|
|
emit
|
|
} = _ref;
|
|
const model = useProxiedModel(props, 'modelValue');
|
|
const isActive = computed({
|
|
get: () => model.value,
|
|
set: v => {
|
|
if (!(v && props.disabled)) model.value = v;
|
|
}
|
|
});
|
|
const {
|
|
teleportTarget
|
|
} = useTeleport(computed(() => props.attach || props.contained));
|
|
const {
|
|
themeClasses
|
|
} = provideTheme(props);
|
|
const {
|
|
rtlClasses,
|
|
isRtl
|
|
} = useRtl();
|
|
const {
|
|
hasContent,
|
|
onAfterLeave: _onAfterLeave
|
|
} = useLazy(props, isActive);
|
|
const scrimColor = useBackgroundColor(computed(() => {
|
|
return typeof props.scrim === 'string' ? props.scrim : null;
|
|
}));
|
|
const {
|
|
globalTop,
|
|
localTop,
|
|
stackStyles
|
|
} = useStack(isActive, toRef(props, 'zIndex'), props._disableGlobalStack);
|
|
const {
|
|
activatorEl,
|
|
activatorRef,
|
|
target,
|
|
targetEl,
|
|
targetRef,
|
|
activatorEvents,
|
|
contentEvents,
|
|
scrimEvents
|
|
} = useActivator(props, {
|
|
isActive,
|
|
isTop: localTop
|
|
});
|
|
const {
|
|
dimensionStyles
|
|
} = useDimension(props);
|
|
const isMounted = useHydration();
|
|
const {
|
|
scopeId
|
|
} = useScopeId();
|
|
watch(() => props.disabled, v => {
|
|
if (v) isActive.value = false;
|
|
});
|
|
const root = ref();
|
|
const contentEl = ref();
|
|
const {
|
|
contentStyles,
|
|
updateLocation
|
|
} = useLocationStrategies(props, {
|
|
isRtl,
|
|
contentEl,
|
|
target,
|
|
isActive
|
|
});
|
|
useScrollStrategies(props, {
|
|
root,
|
|
contentEl,
|
|
targetEl,
|
|
isActive,
|
|
updateLocation
|
|
});
|
|
function onClickOutside(e) {
|
|
emit('click:outside', e);
|
|
if (!props.persistent) isActive.value = false;else animateClick();
|
|
}
|
|
function closeConditional() {
|
|
return isActive.value && globalTop.value;
|
|
}
|
|
IN_BROWSER && watch(isActive, val => {
|
|
if (val) {
|
|
window.addEventListener('keydown', onKeydown);
|
|
} else {
|
|
window.removeEventListener('keydown', onKeydown);
|
|
}
|
|
}, {
|
|
immediate: true
|
|
});
|
|
onBeforeUnmount(() => {
|
|
if (!IN_BROWSER) return;
|
|
window.removeEventListener('keydown', onKeydown);
|
|
});
|
|
function onKeydown(e) {
|
|
if (e.key === 'Escape' && globalTop.value) {
|
|
if (!props.persistent) {
|
|
isActive.value = false;
|
|
if (contentEl.value?.contains(document.activeElement)) {
|
|
activatorEl.value?.focus();
|
|
}
|
|
} else animateClick();
|
|
}
|
|
}
|
|
const router = useRouter();
|
|
useToggleScope(() => props.closeOnBack, () => {
|
|
useBackButton(router, next => {
|
|
if (globalTop.value && isActive.value) {
|
|
next(false);
|
|
if (!props.persistent) isActive.value = false;else animateClick();
|
|
} else {
|
|
next();
|
|
}
|
|
});
|
|
});
|
|
const top = ref();
|
|
watch(() => isActive.value && (props.absolute || props.contained) && teleportTarget.value == null, val => {
|
|
if (val) {
|
|
const scrollParent = getScrollParent(root.value);
|
|
if (scrollParent && scrollParent !== document.scrollingElement) {
|
|
top.value = scrollParent.scrollTop;
|
|
}
|
|
}
|
|
});
|
|
|
|
// Add a quick "bounce" animation to the content
|
|
function animateClick() {
|
|
if (props.noClickAnimation) return;
|
|
contentEl.value && animate(contentEl.value, [{
|
|
transformOrigin: 'center'
|
|
}, {
|
|
transform: 'scale(1.03)'
|
|
}, {
|
|
transformOrigin: 'center'
|
|
}], {
|
|
duration: 150,
|
|
easing: standardEasing
|
|
});
|
|
}
|
|
function onAfterLeave() {
|
|
_onAfterLeave();
|
|
emit('afterLeave');
|
|
}
|
|
useRender(() => _createVNode(_Fragment, null, [slots.activator?.({
|
|
isActive: isActive.value,
|
|
props: mergeProps({
|
|
ref: activatorRef,
|
|
targetRef
|
|
}, activatorEvents.value, props.activatorProps)
|
|
}), isMounted.value && hasContent.value && _createVNode(Teleport, {
|
|
"disabled": !teleportTarget.value,
|
|
"to": teleportTarget.value
|
|
}, {
|
|
default: () => [_createVNode("div", _mergeProps({
|
|
"class": ['v-overlay', {
|
|
'v-overlay--absolute': props.absolute || props.contained,
|
|
'v-overlay--active': isActive.value,
|
|
'v-overlay--contained': props.contained
|
|
}, themeClasses.value, rtlClasses.value, props.class],
|
|
"style": [stackStyles.value, {
|
|
'--v-overlay-opacity': props.opacity,
|
|
top: convertToUnit(top.value)
|
|
}, props.style],
|
|
"ref": root
|
|
}, scopeId, attrs), [_createVNode(Scrim, _mergeProps({
|
|
"color": scrimColor,
|
|
"modelValue": isActive.value && !!props.scrim
|
|
}, scrimEvents.value), null), _createVNode(MaybeTransition, {
|
|
"appear": true,
|
|
"persisted": true,
|
|
"transition": props.transition,
|
|
"target": target.value,
|
|
"onAfterLeave": onAfterLeave
|
|
}, {
|
|
default: () => [_withDirectives(_createVNode("div", _mergeProps({
|
|
"ref": contentEl,
|
|
"class": ['v-overlay__content', props.contentClass],
|
|
"style": [dimensionStyles.value, contentStyles.value]
|
|
}, contentEvents.value, props.contentProps), [slots.default?.({
|
|
isActive
|
|
})]), [[_vShow, isActive.value], [_resolveDirective("click-outside"), {
|
|
handler: onClickOutside,
|
|
closeConditional,
|
|
include: () => [activatorEl.value]
|
|
}]])]
|
|
})])]
|
|
})]));
|
|
return {
|
|
activatorEl,
|
|
target,
|
|
animateClick,
|
|
contentEl,
|
|
globalTop,
|
|
localTop,
|
|
updateLocation
|
|
};
|
|
}
|
|
});
|
|
//# sourceMappingURL=VOverlay.mjs.map
|