98 lines
2.4 KiB
TypeScript
98 lines
2.4 KiB
TypeScript
import type { Ref } from 'vue';
|
|
import { onBeforeUnmount, ref, watch, onMounted } from 'vue';
|
|
import raf from '../../_util/raf';
|
|
|
|
/**
|
|
* Popup should follow the steps for each component work correctly:
|
|
* measure - check for the value stretch size
|
|
* align - let component align the position
|
|
* aligned - re-align again in case additional className changed the size
|
|
* afterAlign - choice next step is trigger motion or finished
|
|
* beforeMotion - should reset motion to invisible so that CSSMotion can do normal motion
|
|
* motion - play the motion
|
|
* stable - everything is done
|
|
*/
|
|
type PopupStatus = null | 'measure' | 'align' | 'aligned' | 'motion' | 'stable';
|
|
|
|
type Func = () => void;
|
|
|
|
const StatusQueue: PopupStatus[] = ['measure', 'align', null, 'motion'];
|
|
|
|
export default (
|
|
visible: Ref<boolean>,
|
|
doMeasure: Func,
|
|
): [Ref<PopupStatus>, (callback?: () => void) => void] => {
|
|
const status = ref<PopupStatus>(null);
|
|
const rafRef = ref<number>();
|
|
const destroyRef = ref(false);
|
|
function setStatus(nextStatus: PopupStatus) {
|
|
if (!destroyRef.value) {
|
|
status.value = nextStatus;
|
|
}
|
|
}
|
|
|
|
function cancelRaf() {
|
|
raf.cancel(rafRef.value);
|
|
}
|
|
|
|
function goNextStatus(callback?: () => void) {
|
|
cancelRaf();
|
|
rafRef.value = raf(() => {
|
|
// Only align should be manually trigger
|
|
let newStatus = status.value;
|
|
switch (status.value) {
|
|
case 'align':
|
|
newStatus = 'motion';
|
|
break;
|
|
case 'motion':
|
|
newStatus = 'stable';
|
|
break;
|
|
default:
|
|
}
|
|
setStatus(newStatus);
|
|
|
|
callback?.();
|
|
});
|
|
}
|
|
|
|
watch(
|
|
visible,
|
|
() => {
|
|
setStatus('measure');
|
|
},
|
|
{ immediate: true, flush: 'post' },
|
|
);
|
|
onMounted(() => {
|
|
// Go next status
|
|
watch(
|
|
status,
|
|
() => {
|
|
switch (status.value) {
|
|
case 'measure':
|
|
doMeasure();
|
|
break;
|
|
default:
|
|
}
|
|
|
|
if (status.value) {
|
|
rafRef.value = raf(async () => {
|
|
const index = StatusQueue.indexOf(status.value);
|
|
const nextStatus = StatusQueue[index + 1];
|
|
if (nextStatus && index !== -1) {
|
|
setStatus(nextStatus);
|
|
}
|
|
});
|
|
}
|
|
},
|
|
{ immediate: true, flush: 'post' },
|
|
);
|
|
});
|
|
|
|
onBeforeUnmount(() => {
|
|
destroyRef.value = true;
|
|
cancelRaf();
|
|
});
|
|
|
|
return [status, goNextStatus];
|
|
};
|