mirror of
https://github.com/robonen/tools.git
synced 2026-03-20 02:44:45 +00:00
feat(packages/vue): add useInjectionStore
This commit is contained in:
@@ -7,6 +7,7 @@ export * from './useClamp';
|
|||||||
export * from './useContextFactory';
|
export * from './useContextFactory';
|
||||||
export * from './useCounter';
|
export * from './useCounter';
|
||||||
export * from './useFocusGuard';
|
export * from './useFocusGuard';
|
||||||
|
export * from './useInjectionStore';
|
||||||
export * from './useLastChanged';
|
export * from './useLastChanged';
|
||||||
export * from './useMounted';
|
export * from './useMounted';
|
||||||
export * from './useOffsetPagination';
|
export * from './useOffsetPagination';
|
||||||
|
|||||||
99
packages/vue/src/composables/useInjectionStore/index.test.ts
Normal file
99
packages/vue/src/composables/useInjectionStore/index.test.ts
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { defineComponent, ref } from 'vue';
|
||||||
|
import { useInjectionStore } from '.';
|
||||||
|
import { mount } from '@vue/test-utils';
|
||||||
|
|
||||||
|
function testFactory<Args, Return>(
|
||||||
|
store: ReturnType<typeof useInjectionStore<Args[], Return>>,
|
||||||
|
) {
|
||||||
|
const { useProvidingState, useInjectedState } = store;
|
||||||
|
|
||||||
|
const Child = defineComponent({
|
||||||
|
setup() {
|
||||||
|
const state = useInjectedState();
|
||||||
|
return { state };
|
||||||
|
},
|
||||||
|
template: `{{ state }}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
const Parent = defineComponent({
|
||||||
|
components: { Child },
|
||||||
|
setup() {
|
||||||
|
const state = useProvidingState();
|
||||||
|
return { state };
|
||||||
|
},
|
||||||
|
template: `<Child />`,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
Parent,
|
||||||
|
Child,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('useInjectionState', () => {
|
||||||
|
it('provides and injects state correctly', () => {
|
||||||
|
const { Parent } = testFactory(
|
||||||
|
useInjectionStore(() => ref('base'))
|
||||||
|
);
|
||||||
|
|
||||||
|
const wrapper = mount(Parent);
|
||||||
|
expect(wrapper.text()).toBe('base');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('injects default value when state is not provided', () => {
|
||||||
|
const { Child } = testFactory(
|
||||||
|
useInjectionStore(() => ref('without provider'), {
|
||||||
|
defaultValue: ref('default'),
|
||||||
|
injectionKey: 'testKey',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const wrapper = mount(Child);
|
||||||
|
expect(wrapper.text()).toBe('default');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('provides state at app level', () => {
|
||||||
|
const injectionStore = useInjectionStore(() => ref('app level'));
|
||||||
|
const { Child } = testFactory(injectionStore);
|
||||||
|
|
||||||
|
const wrapper = mount(Child, {
|
||||||
|
global: {
|
||||||
|
plugins: [
|
||||||
|
app => {
|
||||||
|
const state = injectionStore.useAppProvidingState(app)();
|
||||||
|
expect(state.value).toBe('app level');
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.text()).toBe('app level');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works with custom injection key', () => {
|
||||||
|
const { Parent } = testFactory(
|
||||||
|
useInjectionStore(() => ref('custom key'), {
|
||||||
|
injectionKey: Symbol('customKey'),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const wrapper = mount(Parent);
|
||||||
|
expect(wrapper.text()).toBe('custom key');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles state factory with arguments', () => {
|
||||||
|
const injectionStore = useInjectionStore((arg: string) => arg);
|
||||||
|
const { Child } = testFactory(injectionStore);
|
||||||
|
|
||||||
|
const wrapper = mount(Child, {
|
||||||
|
global: {
|
||||||
|
plugins: [
|
||||||
|
app => injectionStore.useAppProvidingState(app)('with args'),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.text()).toBe('with args');
|
||||||
|
});
|
||||||
|
});
|
||||||
72
packages/vue/src/composables/useInjectionStore/index.ts
Normal file
72
packages/vue/src/composables/useInjectionStore/index.ts
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import { inject, provide, type App, type InjectionKey } from 'vue';
|
||||||
|
|
||||||
|
export interface useInjectionStoreOptions<Return> {
|
||||||
|
injectionKey: string | InjectionKey<Return>;
|
||||||
|
defaultValue?: Return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name useInjectionStore
|
||||||
|
* @category State
|
||||||
|
* @description Create a global state that can be injected into components
|
||||||
|
*
|
||||||
|
* @param {Function} stateFactory A factory function that creates the state
|
||||||
|
* @param {useInjectionStoreOptions} options An object with the following properties
|
||||||
|
* @param {string | InjectionKey} options.injectionKey The key to use for the injection
|
||||||
|
* @param {any} options.defaultValue The default value to use when the state is not provided
|
||||||
|
* @returns {Object} An object with `useProvidingState`, `useAppProvidingState`, and `useInjectedState` functions
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { useProvidingState, useInjectedState } = useInjectionStore(() => ref('Hello World'));
|
||||||
|
*
|
||||||
|
* // In a parent component
|
||||||
|
* const state = useProvidingState();
|
||||||
|
*
|
||||||
|
* // In a child component
|
||||||
|
* const state = useInjectedState();
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { useProvidingState, useInjectedState } = useInjectionStore(() => ref('Hello World'), {
|
||||||
|
* injectionKey: 'MyState',
|
||||||
|
* defaultValue: 'Default Value'
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* // In a plugin
|
||||||
|
* {
|
||||||
|
* install(app) {
|
||||||
|
* const state = useAppProvidingState(app)();
|
||||||
|
* state.value = 'Hello World';
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // In a component
|
||||||
|
* const state = useInjectedState();
|
||||||
|
*
|
||||||
|
* @since 0.0.5
|
||||||
|
*/
|
||||||
|
export function useInjectionStore<Args extends any[], Return>(
|
||||||
|
stateFactory: (...args: Args) => Return,
|
||||||
|
options?: useInjectionStoreOptions<Return>,
|
||||||
|
) {
|
||||||
|
const key = options?.injectionKey ?? Symbol(stateFactory.name ?? 'InjectionStore');
|
||||||
|
|
||||||
|
const useProvidingState = (...args: Args) => {
|
||||||
|
const state = stateFactory(...args);
|
||||||
|
provide(key, state);
|
||||||
|
return state;
|
||||||
|
};
|
||||||
|
|
||||||
|
const useAppProvidingState = (app: App) => (...args: Args) => {
|
||||||
|
const state = stateFactory(...args);
|
||||||
|
app.provide(key, state);
|
||||||
|
return state;
|
||||||
|
};
|
||||||
|
|
||||||
|
const useInjectedState = () => inject(key, options?.defaultValue);
|
||||||
|
|
||||||
|
return {
|
||||||
|
useProvidingState,
|
||||||
|
useAppProvidingState,
|
||||||
|
useInjectedState
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user