mirror of
https://github.com/robonen/tools.git
synced 2026-03-20 19:04:46 +00:00
feat(monorepo): migrate vue packages and apply oxlint refactors
This commit is contained in:
4
vue/toolkit/src/composables/lifecycle/index.ts
Normal file
4
vue/toolkit/src/composables/lifecycle/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from './tryOnBeforeMount';
|
||||
export * from './tryOnMounted';
|
||||
export * from './tryOnScopeDispose';
|
||||
export * from './useMounted';
|
||||
@@ -0,0 +1,46 @@
|
||||
import { onBeforeMount, nextTick } from 'vue';
|
||||
import type { ComponentInternalInstance } from 'vue';
|
||||
import { getLifeCycleTarger } from '@/utils';
|
||||
import type { VoidFunction } from '@robonen/stdlib';
|
||||
|
||||
// TODO: test
|
||||
|
||||
export interface TryOnBeforeMountOptions {
|
||||
sync?: boolean;
|
||||
target?: ComponentInternalInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @name tryOnBeforeMount
|
||||
* @category Lifecycle
|
||||
* @description Call onBeforeMount if it's inside a component lifecycle hook, otherwise just calls it
|
||||
*
|
||||
* @param {VoidFunction} fn - The function to run on before mount.
|
||||
* @param {TryOnBeforeMountOptions} options - The options for the function.
|
||||
* @param {boolean} [options.sync=true] - If true, the function will run synchronously, otherwise it will run asynchronously.
|
||||
* @param {ComponentInternalInstance} [options.target] - The target component instance to run the function on.
|
||||
* @returns {void}
|
||||
*
|
||||
* @example
|
||||
* tryOnBeforeMount(() => console.log('Before mount'));
|
||||
*
|
||||
* @example
|
||||
* tryOnBeforeMount(() => console.log('Before mount async'), { sync: false });
|
||||
*
|
||||
* @since 0.0.1
|
||||
*/
|
||||
export function tryOnBeforeMount(fn: VoidFunction, options: TryOnBeforeMountOptions = {}) {
|
||||
const {
|
||||
sync = true,
|
||||
target,
|
||||
} = options;
|
||||
|
||||
const instance = getLifeCycleTarger(target);
|
||||
|
||||
if (instance)
|
||||
onBeforeMount(fn, instance);
|
||||
else if (sync)
|
||||
fn();
|
||||
else
|
||||
nextTick(fn);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import { describe, it, vi, expect } from 'vitest';
|
||||
import { defineComponent, nextTick } from 'vue';
|
||||
import type { PropType } from 'vue';
|
||||
import { tryOnMounted } from '.';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import type { VoidFunction } from '@robonen/stdlib';
|
||||
|
||||
const ComponentStub = defineComponent({
|
||||
props: {
|
||||
callback: {
|
||||
type: Function as PropType<VoidFunction>,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
if (props.callback) {
|
||||
tryOnMounted(props.callback);
|
||||
}
|
||||
},
|
||||
template: `<div></div>`,
|
||||
});
|
||||
|
||||
describe(tryOnMounted, () => {
|
||||
it('run the callback when mounted', () => {
|
||||
const callback = vi.fn();
|
||||
|
||||
mount(ComponentStub, {
|
||||
props: { callback },
|
||||
});
|
||||
|
||||
expect(callback).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('run the callback outside of a component lifecycle', () => {
|
||||
const callback = vi.fn();
|
||||
|
||||
tryOnMounted(callback);
|
||||
|
||||
expect(callback).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('run the callback asynchronously', async () => {
|
||||
const callback = vi.fn();
|
||||
|
||||
tryOnMounted(callback, { sync: false });
|
||||
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
await nextTick();
|
||||
expect(callback).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.skip('run the callback with a specific target', () => {
|
||||
const callback = vi.fn();
|
||||
|
||||
const component = mount(ComponentStub);
|
||||
|
||||
tryOnMounted(callback, { target: component.vm.$ });
|
||||
|
||||
expect(callback).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
46
vue/toolkit/src/composables/lifecycle/tryOnMounted/index.ts
Normal file
46
vue/toolkit/src/composables/lifecycle/tryOnMounted/index.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { onMounted, nextTick } from 'vue';
|
||||
import type { ComponentInternalInstance } from 'vue';
|
||||
import { getLifeCycleTarger } from '@/utils';
|
||||
import type { VoidFunction } from '@robonen/stdlib';
|
||||
|
||||
// TODO: tests
|
||||
|
||||
export interface TryOnMountedOptions {
|
||||
sync?: boolean;
|
||||
target?: ComponentInternalInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @name tryOnMounted
|
||||
* @category Lifecycle
|
||||
* @description Call onMounted if it's inside a component lifecycle hook, otherwise just calls it
|
||||
*
|
||||
* @param {VoidFunction} fn The function to call
|
||||
* @param {TryOnMountedOptions} options The options to use
|
||||
* @param {boolean} [options.sync=true] If the function should be called synchronously
|
||||
* @param {ComponentInternalInstance} [options.target] The target instance to use
|
||||
* @returns {void}
|
||||
*
|
||||
* @example
|
||||
* tryOnMounted(() => console.log('Mounted!'));
|
||||
*
|
||||
* @example
|
||||
* tryOnMounted(() => console.log('Mounted!'), { sync: false });
|
||||
*
|
||||
* @since 0.0.1
|
||||
*/
|
||||
export function tryOnMounted(fn: VoidFunction, options: TryOnMountedOptions = {}) {
|
||||
const {
|
||||
sync = true,
|
||||
target,
|
||||
} = options;
|
||||
|
||||
const instance = getLifeCycleTarger(target);
|
||||
|
||||
if (instance)
|
||||
onMounted(fn, instance);
|
||||
else if (sync)
|
||||
fn();
|
||||
else
|
||||
nextTick(fn);
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { defineComponent, effectScope } from 'vue';
|
||||
import 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<VoidFunction>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
tryOnScopeDispose(props.callback);
|
||||
},
|
||||
template: '<div></div>',
|
||||
});
|
||||
|
||||
describe(tryOnScopeDispose, () => {
|
||||
it('returns false when the scope is not active', () => {
|
||||
const callback = vi.fn();
|
||||
const detectedScope = tryOnScopeDispose(callback);
|
||||
|
||||
expect(detectedScope).toBeFalsy();
|
||||
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).toBeTruthy();
|
||||
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();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
import type { VoidFunction } from '@robonen/stdlib';
|
||||
import { getCurrentScope, onScopeDispose } from 'vue';
|
||||
|
||||
/**
|
||||
* @name tryOnScopeDispose
|
||||
* @category Lifecycle
|
||||
* @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'));
|
||||
*
|
||||
* @since 0.0.1
|
||||
*/
|
||||
export function tryOnScopeDispose(callback: VoidFunction) {
|
||||
if (getCurrentScope()) {
|
||||
onScopeDispose(callback);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { defineComponent, nextTick, ref } from 'vue';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { useMounted } from '.';
|
||||
|
||||
const ComponentStub = defineComponent({
|
||||
setup() {
|
||||
const isMounted = useMounted();
|
||||
|
||||
return { isMounted };
|
||||
},
|
||||
template: `<div>{{ isMounted }}</div>`,
|
||||
});
|
||||
|
||||
describe(useMounted, () => {
|
||||
it('return the mounted state of the component', async () => {
|
||||
const component = mount(ComponentStub);
|
||||
|
||||
// Initial render
|
||||
expect(component.text()).toBe('false');
|
||||
|
||||
await nextTick();
|
||||
|
||||
// Will trigger a render
|
||||
expect(component.text()).toBe('true');
|
||||
});
|
||||
});
|
||||
30
vue/toolkit/src/composables/lifecycle/useMounted/index.ts
Normal file
30
vue/toolkit/src/composables/lifecycle/useMounted/index.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { onMounted, readonly, ref } from 'vue';
|
||||
import type { ComponentInternalInstance } from 'vue';
|
||||
import { getLifeCycleTarger } from '@/utils';
|
||||
|
||||
/**
|
||||
* @name useMounted
|
||||
* @category Lifecycle
|
||||
* @description Returns a ref that tracks the mounted state of the component (doesn't track the unmounted state)
|
||||
*
|
||||
* @param {ComponentInternalInstance} [instance] The component instance to track the mounted state for
|
||||
* @returns {Readonly<Ref<boolean>>} The mounted state of the component
|
||||
*
|
||||
* @example
|
||||
* const isMounted = useMounted();
|
||||
*
|
||||
* @example
|
||||
* const isMounted = useMounted(getCurrentInstance());
|
||||
*
|
||||
* @since 0.0.1
|
||||
*/
|
||||
export function useMounted(instance?: ComponentInternalInstance) {
|
||||
const isMounted = ref(false);
|
||||
const targetInstance = getLifeCycleTarger(instance);
|
||||
|
||||
onMounted(() => {
|
||||
isMounted.value = true;
|
||||
}, targetInstance);
|
||||
|
||||
return readonly(isMounted);
|
||||
}
|
||||
Reference in New Issue
Block a user