1- import { getOwner , onCleanup , createSignal , Accessor , DEV , untrack } from "solid-js" ;
2- import type { BaseOptions } from "solid-js/types/reactive/signal" ;
1+ import { getOwner , onCleanup , createSignal , Accessor , DEV , untrack , batch } from "solid-js" ;
2+ import type { BaseOptions , Signal } from "solid-js/types/reactive/signal" ;
33import { isServer } from "solid-js/web" ;
44import type {
55 AnyClass ,
@@ -10,7 +10,9 @@ import type {
1010 Trigger ,
1111 TriggerCache ,
1212 AnyObject ,
13- AnyFunction
13+ AnyFunction ,
14+ SetterValue ,
15+ AnyStatic
1416} from "./types" ;
1517
1618export * from "./types" ;
@@ -45,6 +47,8 @@ export function isObject(value: any): value is AnyObject {
4547
4648export const compare = ( a : any , b : any ) : number => ( a < b ? - 1 : a > b ? 1 : 0 ) ;
4749
50+ export const clamp = ( n :number , min : number , max : number ) => Math . min ( Math . max ( n , min ) , max )
51+
4852/**
4953 * Accesses the value of a MaybeAccessor
5054 * @example
@@ -303,3 +307,63 @@ export function createTriggerCache<T>(options?: BaseOptions): TriggerCache<T> {
303307 }
304308 } ;
305309}
310+
311+ export type StaticStoreSetter < T extends Readonly < AnyStatic > > = {
312+ ( setter : ( prev : T ) => Partial < T > ) : T ;
313+ ( state : Partial < T > ) : T ;
314+ < K extends keyof T > ( key : K , state : SetterValue < T [ K ] > ) : T ;
315+ } ;
316+
317+ /**
318+ * A shallowly wrapped reactive store object. It behaves similarly to the creatStore, but with limited features to keep it simple. Designed to be used for reactive objects with static keys, but dynamic values, like reactive Event State, location, etc.
319+ * @param init initial value of the store
320+ * @returns
321+ * ```ts
322+ * [access: Readonly<T>, write: StaticStoreSetter<T>]
323+ * ```
324+ */
325+ export function createStaticStore < T extends Readonly < AnyStatic > > (
326+ init : T
327+ ) : [ access : T , write : StaticStoreSetter < T > ] {
328+ const copy = { ...init } ;
329+ const store = { } as T ;
330+ const cache = new Map < PropertyKey , Signal < any > > ( ) ;
331+
332+ const getValue = < K extends keyof T > ( key : K ) : T [ K ] => {
333+ const saved = cache . get ( key ) ;
334+ if ( saved ) return saved [ 0 ] ( ) ;
335+ const signal = createSignal < any > ( copy [ key ] , {
336+ name : typeof key === "string" ? key : undefined
337+ } ) ;
338+ cache . set ( key , signal ) ;
339+ delete copy [ key ] ;
340+ return signal [ 0 ] ( ) ;
341+ } ;
342+
343+ const setValue = < K extends keyof T > ( key : K , value : SetterValue < any > ) : void => {
344+ const saved = cache . get ( key ) ;
345+ if ( saved ) return saved [ 1 ] ( value ) ;
346+ if ( key in copy ) copy [ key ] = accessWith ( value , [ copy [ key ] ] ) ;
347+ } ;
348+
349+ for ( const key of keys ( init ) ) {
350+ store [ key ] = undefined as any ;
351+ Object . defineProperty ( store , key , {
352+ get : getValue . bind ( void 0 , key )
353+ } ) ;
354+ }
355+
356+ const setter = ( a : ( ( prev : T ) => Partial < T > ) | Partial < T > | keyof T , b ?: SetterValue < any > ) => {
357+ if ( isObject ( a ) )
358+ untrack ( ( ) => {
359+ batch ( ( ) => {
360+ for ( const [ key , value ] of entries ( accessWith ( a , store ) as Partial < T > ) )
361+ setValue ( key as keyof T , ( ) => value ) ;
362+ } ) ;
363+ } ) ;
364+ else setValue ( a , b ) ;
365+ return store ;
366+ } ;
367+
368+ return [ store , setter ] ;
369+ }
0 commit comments