All files / src/custom-element local-storage.js

100% Statements 56/56
100% Branches 21/21
100% Functions 14/14
100% Lines 51/51

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 931x 128x 26x 102x 18x 8x 84x 84x 84x 18x 18x   66x 18x 15x 15x   48x 48x           98x 98x     4x 4x       3x 3x       10x 9x 1x 1x 1x 1x 1x 1x   1x     98x         1x               239x 12x       818x 12x 116x 116x   12x   12x 2x   10x   12x 558x 9x 9x 9x     5x     1x    
const     string2value = (type, v) =>
{   if( type === 'text')
        return v;
    if( type === 'json')
        try{ return JSON.parse( v );}
        catch(err){ return null }
    const el = document.createElement('input');
    el.setAttribute('type',type);
    if( 'number' === type )
    {   el.value = v;
        return el.valueAsNumber;
    }
    if( 'date' === type )
    {   if(!v) return null;
        el.valueAsDate = new Date( v );
        return el.value;
    }
    el.value = v;
    return el.value;
};
 
let originalSetItem,originalRemoveItem,originalClear;
 
export function localStorage_setItem( key, value )
{   originalSetItem.call(localStorage, key, value);
    window.dispatchEvent( new CustomEvent('local-storage',{detail:{key,value}}) );
}
export function localStorage_removeItem( key )
{   originalRemoveItem.call(localStorage, key);
    window.dispatchEvent( new CustomEvent('local-storage',{detail:{key}}) );
}
 
export function localStorage_clear()
{   originalClear.call(localStorage);
    window.dispatchEvent( new CustomEvent('local-storage',{detail:{}}) );
}
 
function ensureTrackLocalStorage()
{   if( originalSetItem )
        return;
    originalSetItem = localStorage.setItem;
    localStorage.setItem = localStorage_setItem;
    originalRemoveItem = localStorage.removeItem;
    localStorage.removeItem = localStorage_removeItem;
    originalClear = localStorage.clear;
    localStorage.clear = localStorage_clear;
}
ensureTrackLocalStorage();
 
export function localStorageSetItem(key, value)
{   localStorage_setItem(key, value);
}
export class LocalStorageElement extends HTMLElement
{
    static observedAttributes=
                [   'value' // populated from localStorage, if defined initially, sets the value in storage
                ,   'slice'
                ,   'key'
                ,   'type' // `text|json`, defaults to text, other types are compatible with INPUT field
                ,   'live' // monitors localStorage change
                ];
 
    #value;
    get value(){ return this.#value ===null ? undefined: this.#value }
    set value(o){ return this.#value = o; }
 
    async connectedCallback()
    {
        const    attr = attr => this.getAttribute(attr)
        , fromStorage = ()=>
        {   this.#value = string2value( attr('type'), localStorage.getItem( attr( 'key' ) ) );
            this.dispatchEvent( new Event('change') )
        }
        this.#value = string2value( attr('type'), localStorage.getItem( attr( 'key' ) ) );
 
        if( this.hasAttribute('value'))
            localStorageSetItem( attr( 'key' ), this.#value = attr( 'value' )  )
        else
            fromStorage()
 
        if( this.hasAttribute('live') )
        {   const listener = (e => (e.detail.key === attr( 'key' ) || !e.detail.key ) && fromStorage());
            window.addEventListener( 'local-storage', listener );
            ensureTrackLocalStorage();
            this._destroy = ()=> window.removeEventListener('local-storage', listener );
        }
    }
    disconnectedCallback(){ this._destroy?.(); }
}
 
window.customElements.define( 'local-storage', LocalStorageElement );
export default LocalStorageElement;