From 7b4f2d0c0ab7a61f1eb05d7f29b398e892314ac9 Mon Sep 17 00:00:00 2001 From: robonen Date: Sat, 19 Oct 2024 06:40:32 +0700 Subject: [PATCH] feat(packages/vue): add tryOnScopeDispose composable --- .../tryOnScopeDispose/index.test.ts | 57 +++++++++++++++++++ .../composables/tryOnScopeDispose/index.ts | 22 +++++++ 2 files changed, 79 insertions(+) create mode 100644 packages/vue/src/composables/tryOnScopeDispose/index.test.ts create mode 100644 packages/vue/src/composables/tryOnScopeDispose/index.ts diff --git a/packages/vue/src/composables/tryOnScopeDispose/index.test.ts b/packages/vue/src/composables/tryOnScopeDispose/index.test.ts new file mode 100644 index 0000000..22ea7c9 --- /dev/null +++ b/packages/vue/src/composables/tryOnScopeDispose/index.test.ts @@ -0,0 +1,57 @@ +import { describe, expect, it, vi } from 'vitest'; +import { defineComponent, effectScope, type PropType } from 'vue'; +import { tryOnScopeDispose } from '.'; +import { mount } from '@vue/test-utils'; +import type { VoidFunction } from '@robonen/stdlib'; + +const ComponentStub = defineComponent({ + props: { + callback: { + type: Function as PropType, + required: true + } + }, + setup(props) { + tryOnScopeDispose(props.callback); + }, +}); + +describe('tryOnScopeDispose', () => { + it('returns false when the scope is not active', () => { + const callback = vi.fn(); + const detectedScope = tryOnScopeDispose(callback); + + expect(detectedScope).toBe(false); + expect(callback).not.toHaveBeenCalled(); + }); + + it('run the callback when the scope is disposed', () => { + const callback = vi.fn(); + const scope = effectScope(); + let detectedScope: boolean | undefined; + + scope.run(() => { + detectedScope = tryOnScopeDispose(callback); + }); + + expect(detectedScope).toBe(true); + expect(callback).not.toHaveBeenCalled(); + + scope.stop(); + + expect(callback).toHaveBeenCalled(); + }); + + it('run callback when the component is unmounted', () => { + const callback = vi.fn(); + const component = mount(ComponentStub, { + props: { callback }, + }); + + expect(callback).not.toHaveBeenCalled(); + + component.unmount(); + + expect(callback).toHaveBeenCalled(); + }); +}); \ No newline at end of file diff --git a/packages/vue/src/composables/tryOnScopeDispose/index.ts b/packages/vue/src/composables/tryOnScopeDispose/index.ts new file mode 100644 index 0000000..5e309c0 --- /dev/null +++ b/packages/vue/src/composables/tryOnScopeDispose/index.ts @@ -0,0 +1,22 @@ +import type { VoidFunction } from '@robonen/stdlib'; +import { getCurrentScope, onScopeDispose } from 'vue'; + +/** + * @name tryOnScopeDispose + * @category Components + * @description A composable that will run a callback when the scope is disposed or do nothing if the scope isn't available. + * + * @param {VoidFunction} callback - The callback to run when the scope is disposed. + * @returns {boolean} - Returns true if the callback was run, otherwise false. + * + * @example + * tryOnScopeDispose(() => console.log('Scope disposed')); + */ +export function tryOnScopeDispose(callback: VoidFunction) { + if (getCurrentScope()) { + onScopeDispose(callback); + return true; + } + + return false; +}