threed-garden
Version:
ThreeD Garden: WebGL 3D Environment Interface for Next.JS React TypeScript Three.JS React-Three Physics, 2D Paper.JS; APIs: Apollo GraphQL, WordPress; CSS: Tailwind, Radix-UI; Libraries: FarmBot 3D; AI: OpenAI, DeepSeek
172 lines (147 loc) • 5.06 kB
text/typescript
// 🫙 Store is a Jar, with States inside
// ** "apollo-reactive-store": "0.0.4"
import { makeVar, ReactiveVar, useReactiveVar } from '@apollo/client'
// ** HELPER Components
import ccm from '#/lib/utils/console-colors'
type State<T> = Record<string | symbol, T>
type Store<T> = Record<string | symbol, ReactiveVar<T>>
type TypePolicies = {
Query: {
fields: {
[key: string]: {
read(): any
}
}
}
}
type Updater<Value> = (state: Value) => Value | Value
export interface StoreApi<Value> {
getState<T>(store: string | symbol): Object
get<T>(key: string | symbol): Value
update<StateSlice>(key: string | symbol, value: Updater<Value>): Value
useStore<T>(key: string | symbol): Value
getTypePolicies(): TypePolicies
reset(): void
}
export default function create<Value> (
initialState: State<Value>,
options = { debug: true }
): StoreApi<Value> {
// ** CREATE STORE SINGLETON
const createStore = () => {
return Object.keys(initialState).reduce<Store<Value>>((sum, key) => {
return {
...sum,
[key]: makeVar<Value>(initialState[key]),
}
}, {})
}
// ** CREATE AN INSTANCE OF STORE
let store = createStore()
// DEBUGGING
if (options.debug) {
// console.debug(`store`, store)
// console.debug(`initialState`, initialState)
// console.debug(`initialState._type`, initialState._type)
if (initialState._type === 'scene') {
// returns Object {fields}
const fields: Object | any = {} // starts blank
const keys: string[] = Object.keys(store)
// console.debug('keys', keys)
keys.forEach((key: string, index: number) => {
fields[key] = store[key]()
// console.debug(`${index}: ${key}: ${store[key]()} = ${fields[key]}`)
})
// returns Array of Objects {fields}[]
// const fields = Object.keys(store).map((key: string) => {
// return { [key]: store[key]() }
// })
// not working here, for reference only
// returns Array of Objects {fields}[]
// const fields = Object.keys(store).reduce((sum, key) => {
// return {
// ...sum,
// [key]: {
// read() {
// return store[key]()
// },
// },
// }
// })
// console.debug(`${initialState._type}Store current state {fields}`, fields)
// console.debug('%c🫙====================================', ccm5)
}
}
const debug = (key: string, value: Updater<Value>): void => {
if (options.debug) {
// console.debug(`%c🫙===================================================`, ccm.blueAlert)
// console.debug(`store update(key) "${key}" with value: ${JSON.stringify(value)}`)
console.debug(`%c🫙 store update(key) "${key}"`, ccm.blueAlert, value)
// console.debug(`%c🫙===================================================`, ccm.blueAlert)
}
}
return {
getState(): Object {
// returns Object {fields}
const fields: Object | any = {} // starts blank
const keys: string[] = Object.keys(store)
// console.debug('keys', keys)
keys.forEach((key: string, index: number) => {
fields[key] = store[key]()
// console.debug(`${index}: ${key}: ${store[key]()} = ${fields[key]}`)
})
if (!fields) {
throw new Error(`store getState(): "${JSON.stringify(store)}" is invalid`, store)
}
// console.debug(`%c🫙===================================================`, ccm.blueAlert)
console.debug(`%c🫙 store getState(): {fields}`, ccm.blueAlert, fields)
// console.debug(`%c🫙===================================================`, ccm.blueAlert)
return fields
},
get(key: string) {
const reactiveVar = store[key]
if (!reactiveVar) {
throw new Error(`store get(key) "${key}" is invalid`)
}
return reactiveVar()
},
update(key: string, value) {
const reactiveVar = store[key]
if (!reactiveVar) {
throw new Error(`store update(key) "${key}" is invalid`)
}
debug(key, value)
if (value instanceof Function) {
return reactiveVar(value(reactiveVar()))
} else {
return reactiveVar(value)
}
},
useStore(key: string) {
const reactiveVar = store[key]
if (!reactiveVar) {
throw new Error(`useStore: key "${key}" is invalid`)
}
return useReactiveVar(reactiveVar)
},
getTypePolicies() {
return {
Query: {
fields: Object.keys(store).reduce((sum, key) => {
return {
...sum,
[key]: {
read() {
return store[key]()
},
},
}
}, {}),
},
}
},
reset: () => {
store = createStore()
},
}
}