195 lines
6.4 KiB
JavaScript
195 lines
6.4 KiB
JavaScript
|
import { mergeProps as _mergeProps, resolveDirective as _resolveDirective, createVNode as _createVNode } from "vue";
|
||
|
// Styles
|
||
|
import "./VSnackbar.css";
|
||
|
|
||
|
// Components
|
||
|
import { VDefaultsProvider } from "../VDefaultsProvider/index.mjs";
|
||
|
import { VOverlay } from "../VOverlay/index.mjs";
|
||
|
import { makeVOverlayProps } from "../VOverlay/VOverlay.mjs";
|
||
|
import { VProgressLinear } from "../VProgressLinear/index.mjs"; // Composables
|
||
|
import { forwardRefs } from "../../composables/forwardRefs.mjs";
|
||
|
import { makeLocationProps, useLocation } from "../../composables/location.mjs";
|
||
|
import { makePositionProps, usePosition } from "../../composables/position.mjs";
|
||
|
import { useProxiedModel } from "../../composables/proxiedModel.mjs";
|
||
|
import { makeRoundedProps, useRounded } from "../../composables/rounded.mjs";
|
||
|
import { useScopeId } from "../../composables/scopeId.mjs";
|
||
|
import { makeThemeProps, provideTheme } from "../../composables/theme.mjs";
|
||
|
import { genOverlays, makeVariantProps, useVariant } from "../../composables/variant.mjs"; // Utilities
|
||
|
import { mergeProps, nextTick, onMounted, onScopeDispose, ref, shallowRef, watch } from 'vue';
|
||
|
import { genericComponent, omit, propsFactory, refElement, useRender } from "../../util/index.mjs";
|
||
|
function useCountdown(milliseconds) {
|
||
|
const time = shallowRef(milliseconds);
|
||
|
let timer = -1;
|
||
|
function clear() {
|
||
|
clearInterval(timer);
|
||
|
}
|
||
|
function reset() {
|
||
|
clear();
|
||
|
nextTick(() => time.value = milliseconds);
|
||
|
}
|
||
|
function start(el) {
|
||
|
const style = el ? getComputedStyle(el) : {
|
||
|
transitionDuration: 0.2
|
||
|
};
|
||
|
const interval = parseFloat(style.transitionDuration) * 1000 || 200;
|
||
|
clear();
|
||
|
if (time.value <= 0) return;
|
||
|
const startTime = performance.now();
|
||
|
timer = window.setInterval(() => {
|
||
|
const elapsed = performance.now() - startTime + interval;
|
||
|
time.value = Math.max(milliseconds - elapsed, 0);
|
||
|
if (time.value <= 0) clear();
|
||
|
}, interval);
|
||
|
}
|
||
|
onScopeDispose(clear);
|
||
|
return {
|
||
|
clear,
|
||
|
time,
|
||
|
start,
|
||
|
reset
|
||
|
};
|
||
|
}
|
||
|
export const makeVSnackbarProps = propsFactory({
|
||
|
multiLine: Boolean,
|
||
|
text: String,
|
||
|
timer: [Boolean, String],
|
||
|
timeout: {
|
||
|
type: [Number, String],
|
||
|
default: 5000
|
||
|
},
|
||
|
vertical: Boolean,
|
||
|
...makeLocationProps({
|
||
|
location: 'bottom'
|
||
|
}),
|
||
|
...makePositionProps(),
|
||
|
...makeRoundedProps(),
|
||
|
...makeVariantProps(),
|
||
|
...makeThemeProps(),
|
||
|
...omit(makeVOverlayProps({
|
||
|
transition: 'v-snackbar-transition'
|
||
|
}), ['persistent', 'noClickAnimation', 'scrim', 'scrollStrategy'])
|
||
|
}, 'VSnackbar');
|
||
|
export const VSnackbar = genericComponent()({
|
||
|
name: 'VSnackbar',
|
||
|
props: makeVSnackbarProps(),
|
||
|
emits: {
|
||
|
'update:modelValue': v => true
|
||
|
},
|
||
|
setup(props, _ref) {
|
||
|
let {
|
||
|
slots
|
||
|
} = _ref;
|
||
|
const isActive = useProxiedModel(props, 'modelValue');
|
||
|
const {
|
||
|
locationStyles
|
||
|
} = useLocation(props);
|
||
|
const {
|
||
|
positionClasses
|
||
|
} = usePosition(props);
|
||
|
const {
|
||
|
scopeId
|
||
|
} = useScopeId();
|
||
|
const {
|
||
|
themeClasses
|
||
|
} = provideTheme(props);
|
||
|
const {
|
||
|
colorClasses,
|
||
|
colorStyles,
|
||
|
variantClasses
|
||
|
} = useVariant(props);
|
||
|
const {
|
||
|
roundedClasses
|
||
|
} = useRounded(props);
|
||
|
const countdown = useCountdown(Number(props.timeout));
|
||
|
const overlay = ref();
|
||
|
const timerRef = ref();
|
||
|
const isHovering = shallowRef(false);
|
||
|
watch(isActive, startTimeout);
|
||
|
watch(() => props.timeout, startTimeout);
|
||
|
onMounted(() => {
|
||
|
if (isActive.value) startTimeout();
|
||
|
});
|
||
|
let activeTimeout = -1;
|
||
|
function startTimeout() {
|
||
|
countdown.reset();
|
||
|
window.clearTimeout(activeTimeout);
|
||
|
const timeout = Number(props.timeout);
|
||
|
if (!isActive.value || timeout === -1) return;
|
||
|
const element = refElement(timerRef.value);
|
||
|
countdown.start(element);
|
||
|
activeTimeout = window.setTimeout(() => {
|
||
|
isActive.value = false;
|
||
|
}, timeout);
|
||
|
}
|
||
|
function clearTimeout() {
|
||
|
countdown.reset();
|
||
|
window.clearTimeout(activeTimeout);
|
||
|
}
|
||
|
function onPointerenter() {
|
||
|
isHovering.value = true;
|
||
|
clearTimeout();
|
||
|
}
|
||
|
function onPointerleave() {
|
||
|
isHovering.value = false;
|
||
|
startTimeout();
|
||
|
}
|
||
|
useRender(() => {
|
||
|
const overlayProps = VOverlay.filterProps(props);
|
||
|
const hasContent = !!(slots.default || slots.text || props.text);
|
||
|
return _createVNode(VOverlay, _mergeProps({
|
||
|
"ref": overlay,
|
||
|
"class": ['v-snackbar', {
|
||
|
'v-snackbar--active': isActive.value,
|
||
|
'v-snackbar--multi-line': props.multiLine && !props.vertical,
|
||
|
'v-snackbar--timer': !!props.timer,
|
||
|
'v-snackbar--vertical': props.vertical
|
||
|
}, positionClasses.value, props.class],
|
||
|
"style": props.style
|
||
|
}, overlayProps, {
|
||
|
"modelValue": isActive.value,
|
||
|
"onUpdate:modelValue": $event => isActive.value = $event,
|
||
|
"contentProps": mergeProps({
|
||
|
class: ['v-snackbar__wrapper', themeClasses.value, colorClasses.value, roundedClasses.value, variantClasses.value],
|
||
|
style: [locationStyles.value, colorStyles.value],
|
||
|
onPointerenter,
|
||
|
onPointerleave
|
||
|
}, overlayProps.contentProps),
|
||
|
"persistent": true,
|
||
|
"noClickAnimation": true,
|
||
|
"scrim": false,
|
||
|
"scrollStrategy": "none",
|
||
|
"_disableGlobalStack": true
|
||
|
}, scopeId), {
|
||
|
default: () => [genOverlays(false, 'v-snackbar'), props.timer && !isHovering.value && _createVNode("div", {
|
||
|
"key": "timer",
|
||
|
"class": "v-snackbar__timer"
|
||
|
}, [_createVNode(VProgressLinear, {
|
||
|
"ref": timerRef,
|
||
|
"color": typeof props.timer === 'string' ? props.timer : 'info',
|
||
|
"max": props.timeout,
|
||
|
"model-value": countdown.time.value
|
||
|
}, null)]), hasContent && _createVNode("div", {
|
||
|
"key": "content",
|
||
|
"class": "v-snackbar__content",
|
||
|
"role": "status",
|
||
|
"aria-live": "polite"
|
||
|
}, [slots.text?.() ?? props.text, slots.default?.()]), slots.actions && _createVNode(VDefaultsProvider, {
|
||
|
"defaults": {
|
||
|
VBtn: {
|
||
|
variant: 'text',
|
||
|
ripple: false,
|
||
|
slim: true
|
||
|
}
|
||
|
}
|
||
|
}, {
|
||
|
default: () => [_createVNode("div", {
|
||
|
"class": "v-snackbar__actions"
|
||
|
}, [slots.actions()])]
|
||
|
})],
|
||
|
activator: slots.activator
|
||
|
});
|
||
|
});
|
||
|
return forwardRefs({}, overlay);
|
||
|
}
|
||
|
});
|
||
|
//# sourceMappingURL=VSnackbar.mjs.map
|