zterton-status/src/store/store_module.ts

98 lines
2.4 KiB
TypeScript
Raw Normal View History

2021-10-14 14:42:52 +00:00
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,
}
}