UNPKG

vuetify

Version:

Vue Material Component Framework

234 lines (233 loc) 7.15 kB
import { Fragment as _Fragment, createVNode as _createVNode, mergeProps as _mergeProps, createElementVNode as _createElementVNode } from "vue"; // Components import { VBtn } from "../VBtn/index.js"; import { VDefaultsProvider } from "../VDefaultsProvider/index.js"; import { makeVSnackbarProps, VSnackbar } from "../VSnackbar/VSnackbar.js"; // Composables import { useSnackbarQueue } from "./queue.js"; import { useDelay } from "../../composables/delay.js"; import { useDocumentVisibility } from "../../composables/documentVisibility.js"; import { useLocale } from "../../composables/locale.js"; // Utilities import { computed, mergeProps, ref, shallowRef, toRef, triggerRef, watch } from 'vue'; import { genericComponent, omit, propsFactory, useRender } from "../../util/index.js"; // Types export const makeVSnackbarQueueProps = propsFactory({ // TODO: Port this to Snackbar on dev closable: [Boolean, String], closeText: { type: String, default: '$vuetify.dismiss' }, collapsed: Boolean, displayStrategy: { type: String, default: 'hold' }, modelValue: { type: Array, default: () => [] }, totalVisible: { type: [Number, String], default: 1 }, gap: { type: [Number, String], default: 8 }, ...omit(makeVSnackbarProps(), ['modelValue', 'collapsed', 'queueIndex', 'queueGap']) }, 'VSnackbarQueue'); export const VSnackbarQueue = genericComponent()({ name: 'VSnackbarQueue', inheritAttrs: false, props: makeVSnackbarQueueProps(), emits: { 'update:modelValue': val => true }, setup(props, { attrs, emit, slots }) { const { t } = useLocale(); const documentVisibility = useDocumentVisibility(); const queue = useSnackbarQueue(props); const isHovered = shallowRef(false); const { runOpenDelay, runCloseDelay } = useDelay({ openDelay: 0, closeDelay: 500 }, val => { isHovered.value = val; updateDynamicProps(); }); let _lastId = 0; const visibleItems = ref([]); const limit = toRef(() => Number(props.totalVisible)); watch(() => props.modelValue.length, showNext); function removeItem(id) { visibleItems.value = visibleItems.value.filter(x => x.id !== id); if (visibleItems.value.length === 0) { isHovered.value = false; } showNext(); } function showNext() { if (!props.modelValue.length) return; const activeCount = visibleItems.value.filter(x => x.active).length; if (activeCount >= limit.value) { if (props.displayStrategy !== 'overflow') return; // Dismiss oldest active items to make room visibleItems.value.filter(x => x.active).slice(limit.value - 1).forEach(item => { item.active = false; item.onDismiss?.('overflow'); }); } const [next, ...rest] = props.modelValue; emit('update:modelValue', rest); const item = typeof next === 'string' ? { text: next } : next; const { promise, success, error, onDismiss, ...itemProps } = item; const newItem = { id: _lastId++, item: { ...(promise ? { timeout: -1, loading: true } : {}), ...itemProps }, active: true, onDismiss }; visibleItems.value.unshift(newItem); updateDynamicProps(); promise?.then(data => { if (!newItem.active) return; newItem.item = success?.(data) ?? { ...newItem.item, timeout: 1 }; updateDynamicProps(); triggerRef(visibleItems); }, data => { if (!newItem.active) return; newItem.item = error?.(data) ?? { ...newItem.item, timeout: 1 }; updateDynamicProps(); triggerRef(visibleItems); }); } function dismiss(id, reason) { const item = visibleItems.value.find(x => x.id === id); if (!item) return; item.active = false; item.onDismiss?.(reason); updateDynamicProps(); } function clear() { emit('update:modelValue', []); visibleItems.value.toReversed().forEach((item, i) => setTimeout(() => { item.active = false; item.onDismiss?.('cleared'); }, 100 * i)); } const btnProps = computed(() => ({ color: typeof props.closable === 'string' ? props.closable : undefined, text: t(props.closeText) })); function updateDynamicProps() { let activeIndex = 0; visibleItems.value.forEach(({ item, active }) => { item.queueIndex = activeIndex; if (active) activeIndex++; }); if (!props.collapsed || isHovered.value) { visibleItems.value.forEach(({ item }) => item.collapsed = undefined); return; } for (const { item } of visibleItems.value) { item.collapsed = item.queueIndex > 0 ? { width: queue.lastItemSize.value.width, height: queue.lastItemSize.value.height } : undefined; } } watch(queue.lastItemSize, updateDynamicProps); watch(() => props.collapsed, updateDynamicProps); useRender(() => { const hasActions = !!(props.closable || slots.actions); const snackbarProps = omit(VSnackbar.filterProps(props), ['modelValue', 'collapsed']); const pauseAll = documentVisibility.value === 'hidden' || props.collapsed && isHovered.value; return _createElementVNode(_Fragment, null, [visibleItems.value.map(({ id, item, active }) => slots.item ? _createVNode(VDefaultsProvider, { "defaults": { VSnackbar: item } }, { default: () => [slots.item({ item })] }) : _createVNode(VSnackbar, _mergeProps({ "key": id }, attrs, snackbarProps, item, pauseAll ? { timeout: -1 } : {}, { "queueGap": Number(props.gap), "contentProps": mergeProps(snackbarProps.contentProps, { onMouseenter: runOpenDelay, onMouseleave: () => runCloseDelay() }), "modelValue": active, "onUpdate:modelValue": () => dismiss(id, 'auto'), "onAfterLeave": () => removeItem(id) }), { header: slots.header ? () => slots.header?.({ item }) : undefined, text: slots.text ? () => slots.text?.({ item }) : undefined, actions: hasActions ? () => _createElementVNode(_Fragment, null, [!slots.actions ? _createVNode(VBtn, _mergeProps(btnProps.value, { "onClick": () => dismiss(id, 'dismissed') }), null) : _createVNode(VDefaultsProvider, { "defaults": { VBtn: btnProps.value } }, { default: () => [slots.actions({ item, props: { onClick: () => dismiss(id, 'dismissed') } })] })]) : undefined }))]); }); return { clear }; } }); //# sourceMappingURL=VSnackbarQueue.js.map