75 lines
		
	
	
		
			1.9 KiB
		
	
	
	
		
			TypeScript
		
	
	
			
		
		
	
	
			75 lines
		
	
	
		
			1.9 KiB
		
	
	
	
		
			TypeScript
		
	
	
| import type { Ref, UnwrapRef } from 'vue';
 | |
| import { onBeforeUnmount, ref } from 'vue';
 | |
| 
 | |
| export type Updater<State> = (prev: State) => State;
 | |
| 
 | |
| /**
 | |
|  * Execute code before next frame but async
 | |
|  */
 | |
| export function useLayoutState<State>(
 | |
|   defaultState: State,
 | |
| ): [Ref<State>, (updater: Updater<State>) => void] {
 | |
|   const stateRef = ref<State>(defaultState);
 | |
|   // const [, forceUpdate] = useState({});
 | |
| 
 | |
|   const lastPromiseRef = ref<Promise<void>>(null);
 | |
|   const updateBatchRef = ref<Updater<State>[]>([]);
 | |
|   function setFrameState(updater: Updater<State>) {
 | |
|     updateBatchRef.value.push(updater);
 | |
| 
 | |
|     const promise = Promise.resolve();
 | |
|     lastPromiseRef.value = promise;
 | |
| 
 | |
|     promise.then(() => {
 | |
|       if (lastPromiseRef.value === promise) {
 | |
|         const prevBatch = updateBatchRef.value;
 | |
|         // const prevState = stateRef.value;
 | |
|         updateBatchRef.value = [];
 | |
| 
 | |
|         prevBatch.forEach(batchUpdater => {
 | |
|           stateRef.value = batchUpdater(stateRef.value as State) as UnwrapRef<State>;
 | |
|         });
 | |
| 
 | |
|         lastPromiseRef.value = null;
 | |
|       }
 | |
|     });
 | |
|   }
 | |
|   onBeforeUnmount(() => {
 | |
|     lastPromiseRef.value = null;
 | |
|   });
 | |
| 
 | |
|   return [stateRef as Ref<State>, setFrameState];
 | |
| }
 | |
| 
 | |
| /** Lock frame, when frame pass reset the lock. */
 | |
| export function useTimeoutLock<State>(
 | |
|   defaultState?: State,
 | |
| ): [(state: UnwrapRef<State>) => void, () => UnwrapRef<State> | null] {
 | |
|   const frameRef = ref<State | null>(defaultState || null);
 | |
|   const timeoutRef = ref<number>();
 | |
| 
 | |
|   function cleanUp() {
 | |
|     window.clearTimeout(timeoutRef.value);
 | |
|   }
 | |
| 
 | |
|   function setState(newState: UnwrapRef<State>) {
 | |
|     frameRef.value = newState;
 | |
|     cleanUp();
 | |
| 
 | |
|     timeoutRef.value = window.setTimeout(() => {
 | |
|       frameRef.value = null;
 | |
|       timeoutRef.value = undefined;
 | |
|     }, 100);
 | |
|   }
 | |
| 
 | |
|   function getState() {
 | |
|     return frameRef.value;
 | |
|   }
 | |
| 
 | |
|   onBeforeUnmount(() => {
 | |
|     cleanUp();
 | |
|   });
 | |
| 
 | |
|   return [setState, getState];
 | |
| }
 |