import { Controller } from "stimulus" ; import { reactive } from "../utilities/reactive" ; export type Serializer < T > = { deserialize ( raw ) : T ; serialize ( value ) : string ; isEmpty ( value ) : boolean ; } ; export interface LocalStorageProxy < T > { get value ( ) : T ; set value ( value ) ; read ( ) : T ; write ( value ) : void ; clear ( ) : void ; isEmpty ( ) : boolean ; } export const StorageSerializers = { boolean : { deserialize : ( v ) => v === 'true' , serialize : ( v ) => String ( v ) , isEmpty : ( v ) => v === '' || v === null , } , object : { deserialize : ( v ) => JSON . parse ( v ) , serialize : ( v ) => JSON . stringify ( v ) , isEmpty : ( v ) => { const values = Object . values ( JSON . parse ( v ) ) ; return values . length === 0 || values . every ( v => v === '' || v === null ) ; } , } , number : { deserialize : ( v ) => Number . parseFloat ( v ) , serialize : ( v ) => String ( v ) , isEmpty : ( v ) => v === '' || v === null , } , any : { deserialize : ( v ) => v , serialize : ( v ) => String ( v ) , isEmpty : ( v ) => v === '' || v === null , } , string : { deserialize : ( v ) => v , serialize : ( v ) => String ( v ) , isEmpty : ( v ) => v === '' || v === null , } , map : { deserialize : ( v ) => new Map ( JSON . parse ( v ) ) , serialize : ( v ) => JSON . stringify ( Array . from ( ( v as Map < any , any > ) . entries ( ) ) ) , isEmpty : ( v ) => { const values = Array . from ( ( v as Map < any , any > ) . values ( ) ) ; return values . length === 0 || values . every ( v => v === '' || v === null ) ; } , } , set : { deserialize : ( v ) => new Set ( JSON . parse ( v ) ) , serialize : ( v ) => JSON . stringify ( Array . from ( ( v as Set < any > ) . entries ( ) ) ) , isEmpty : ( v ) => { const values = Array . from ( ( v as Set < any > ) . values ( ) ) ; return values . length === 0 || values . every ( v => v === '' || v === null ) ; } , } , } ; export function useLocalStorage < T > ( controller , key , defaultValue , opts = { writeDefaults : true } ) { let type ; let { writeDefaults } = opts ; if ( defaultValue == null ) { type = 'any' ; } else if ( defaultValue instanceof Set ) { type = 'set' ; } else if ( defaultValue instanceof Map ) { type = 'map' ; } else if ( typeof defaultValue === 'boolean' ) { type = 'boolean' ; } else if ( typeof defaultValue === 'string' ) { type = 'string' ; } else if ( typeof defaultValue === 'object' ) { type = 'object' ; } else if ( Array . isArray ( defaultValue ) ) { type = 'object' ; } else if ( ! Number . isNaN ( defaultValue ) ) { type = 'number' ; } else { type = 'any' ; } let data = reactive ( { value : defaultValue , } ) ; let storage = localStorage ; const serializer = StorageSerializers [ type ] ; const read = ( ) => { const rawValue = storage . getItem ( key ) ; if ( rawValue == null ) { data . value = defaultValue ; if ( writeDefaults && defaultValue !== null ) { storage . setItem ( key , serializer . serialize ( defaultValue ) ) ; } } else { data . value = serializer . deserialize ( rawValue ) ; } return data . value ; } ; const write = ( value ) => { storage . setItem ( key , serializer . serialize ( value ) ) ; data . value = value ; } ; const clear = ( ) => { storage . removeItem ( key ) ; data . value = defaultValue ; return data . value ; } ; const isEmpty = ( ) : boolean => { let rawValue = storage . getItem ( key ) ; return serializer . isEmpty ( rawValue ) ; } ; read ( ) ; return { get value ( ) { return read ( ) ; } , set value ( value ) { write ( value ) ; } , read , clear , write , isEmpty , } ; }