98 lines
2.4 KiB
TypeScript
98 lines
2.4 KiB
TypeScript
|
import { useStore, Store } from 'vuex'
|
||
|
|
||
|
interface InternalModule<S, A, M, G> {
|
||
|
state: S
|
||
|
actions: A
|
||
|
mutations: M
|
||
|
getters: G
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* This function allows us to access the internal vuex properties and
|
||
|
* maps them in a way which removes the module prefix.
|
||
|
*/
|
||
|
function getFromStoreByType<T>(
|
||
|
moduleName: string,
|
||
|
type: unknown,
|
||
|
isNamespaced: boolean,
|
||
|
) {
|
||
|
if (isNamespaced) {
|
||
|
return Object.keys(type)
|
||
|
.filter(t => t.startsWith(`${moduleName}/`))
|
||
|
.reduce((acc, curr) => {
|
||
|
const typeName = curr.split('/').pop()
|
||
|
const typeValue = type[curr][0]
|
||
|
|
||
|
return { [typeName]: typeValue, ...acc }
|
||
|
}, {}) as T
|
||
|
}
|
||
|
|
||
|
return Object.keys(type).reduce((acc, curr) => {
|
||
|
const typeValue = type[curr][0]
|
||
|
|
||
|
return { [curr]: typeValue, ...acc }
|
||
|
}, {}) as T
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* We have to wrap the getters in a Proxy because we only want to
|
||
|
* "access" the getter if it actually being accessed.
|
||
|
*
|
||
|
* We could technically use the getFromStoreByType function but
|
||
|
* the getter would be invoked multiple types on store instantiation.
|
||
|
*
|
||
|
* This is just a little cheeky workaround. Proxy <3
|
||
|
*/
|
||
|
function wrapGettersInProxy<G>(
|
||
|
moduleName: string,
|
||
|
getters: G,
|
||
|
isNamespaced: boolean,
|
||
|
) {
|
||
|
return new Proxy(getters as Object, {
|
||
|
get(_, prop: string) {
|
||
|
if (isNamespaced)
|
||
|
return getters[`${moduleName}/${prop}`]
|
||
|
|
||
|
return getters[prop]
|
||
|
},
|
||
|
}) as G
|
||
|
}
|
||
|
|
||
|
function isModuleNamespaced<S>(moduleName: string, store: Store<S>): boolean {
|
||
|
// @ts-ignore internal Vuex object that isn't typed.
|
||
|
return Boolean(store._modulesNamespaceMap[`${moduleName}/`])
|
||
|
}
|
||
|
|
||
|
export default function useStoreModule<S = any, A = any, M = any, G = any>(
|
||
|
moduleName: string,
|
||
|
storeName?: string,
|
||
|
): InternalModule<S, A, M, G> {
|
||
|
// @ts-ignore useStore doesn't yet accept a key as arg
|
||
|
const store = storeName ? useStore(storeName) : useStore()
|
||
|
const state = store.state[moduleName]
|
||
|
const isNamespaced = isModuleNamespaced(moduleName, store)
|
||
|
|
||
|
const actions = getFromStoreByType<A>(
|
||
|
moduleName,
|
||
|
// @ts-ignore internal Vuex object that isn't typed.
|
||
|
store._actions,
|
||
|
isNamespaced,
|
||
|
)
|
||
|
|
||
|
const mutations = getFromStoreByType<M>(
|
||
|
moduleName,
|
||
|
// @ts-ignore internal Vuex object that isn't typed.
|
||
|
store._mutations,
|
||
|
isNamespaced,
|
||
|
)
|
||
|
|
||
|
const getters = wrapGettersInProxy<G>(moduleName, store.getters, isNamespaced)
|
||
|
|
||
|
return {
|
||
|
actions,
|
||
|
mutations,
|
||
|
state,
|
||
|
getters,
|
||
|
}
|
||
|
}
|