All files / src local-storage.js

96.96% Statements 64/66
68.75% Branches 11/16
100% Functions 10/10
96.96% Lines 64/66

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 661x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x     1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x
const     attr = (el, attr)=> el.getAttribute(attr)
, string2value = (type, v) =>
{   if( type === 'text')
        return v;
    if( type === 'json')
        return JSON.parse( v );
    const el = document.createElement('input');
    el.setAttribute('type',type);
    el.setAttribute('value', v );
    return type==='number'? el.valueAsNumber : 'date|time|dateTimeLocal'.includes(type)? el.valueAsDate: el.value;
};
 
let originalSetItem;
 
function ensureTrackLocalStorage()
{   if( originalSetItem )
        return;
    originalSetItem = localStorage.setItem;
    localStorage.setItem = function( key, value, ...rest )
        {   originalSetItem.apply(this, [ key, value, ...rest ]);
            window.dispatchEvent( new CustomEvent('local-storage',{detail:{key,value}}) );
        };
}
 
export class LocalStorageElement extends HTMLElement
{
    // @attribute live - monitors localStorage change
    // @attribute type - `text|json`, defaults to text, other types are compatible with INPUT field
    constructor()
    {
        super();
        const      state = {}
        ,           type = attr(this, 'type') || 'text'
        ,       listener = e=> e.detail.key === attr( this,'key' ) && propagateSlice()
        , propagateSlice = ()=>
        {   for( let parent = this.parentElement; parent; parent = parent.parentElement)
                if( parent.onSlice )
                    return parent.onSlice(
                        {     detail: string2value( type, localStorage.getItem( attr( this, 'key' ) ) )
                        ,     target: this
                        } );
                console.error(`${this.localName} used outside of custom-element`)
                debugger;
        };
        this.sliceInit = s =>
        {   if( !state.listener && this.hasAttribute('live') )
            {   state.listener = 1;
                window.addEventListener( 'local-storage', listener );
                ensureTrackLocalStorage();
            }
            propagateSlice();
            return s || {}
        }
        this._destroy = ()=>
        {
            if( !state.listener )
                return;
            state.listener && window.removeEventListener('local-storage', listener );
            delete state.listener;
        };
    }
    disconnectedCallback(){ this._destroy(); }
}
 
window.customElements.define( 'local-storage', LocalStorageElement );
export default LocalStorageElement;