234 lines
7.0 KiB
JavaScript
234 lines
7.0 KiB
JavaScript
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
|