import hr from 'hyperactiv' ; import { render , nothing } from 'lit-html' ; import { attrToVal , camelCase , kebabCase } from './utils' ; import type * as Type from './types' ; const { observe , computed , dispose } = hr ; export * from './utils' ; export * from './types' ; export * from './directives' ; export * from 'lit-html' ; export { observe , computed , dispose } ; export const $ = ( { render : template = ( ) => nothing , state : data = { } , target = document . body , ... options } , ... context ) : Type . Widget => { const model = ( typeof data === 'function' ) ? data ( ... context ) : data ; Object . entries ( target . dataset ) . forEach ( ( [ key , value ] ) => { if ( key in model ) model [ key ] = attrToVal ( value ) ; } ) ; const state = observe ( model , { batch : true , deep : true , bind : true , ... options } ) ; const emit = ( type , detail , { bubbles = false , cancelable = true } = { } ) => { target . dispatchEvent ( new CustomEvent ( type , { detail , bubbles , cancelable } ) ) ; } ; let mounted = false ; const rerender = ( ) => { render ( template ( state , emit , ... context ) , target ) ; if ( ! mounted ) { emit ( 'mount' , model ) ; mounted = true ; } emit ( 'update' , model ) ; } ; const renderer = computed ( ( { computeAsync } ) => { if ( mounted && ! document . contains ( target ) ) return destroy ( ) ; emit ( 'state' , model ) ; return Promise . resolve ( ) . then ( ( ) => computeAsync ( rerender ) ) . catch ( err => emit ( 'error' , err ) ) ; } ) ; const events = new Set ( ) ; const on = ( type , fn , opts ? ) => { target . addEventListener ( type , fn , opts ) ; const off = ( ) => { target . removeEventListener ( type , fn , opts ) ; return events . delete ( off ) ; } ; events . add ( off ) ; return off ; } ; const effects = new Set ( ) ; const effect = ( fn , opts ? ) => { const handle = computed ( fn , opts ) ; const cancel = ( ) => { dispose ( handle ) ; return effects . delete ( cancel ) ; } ; effects . add ( cancel ) ; return cancel ; } ; const observer = new MutationObserver ( ( mutations ) => { mutations . forEach ( ( mutation ) => { if ( mutation . type !== 'attributes' ) return ; const el = mutation . target as Element ; const key = camelCase ( mutation . attributeName . replace ( 'data-' , '' ) ) ; if ( ! ( key in state ) ) return ; const value = el . getAttribute ( mutation . attributeName ) ; if ( value !== mutation . oldValue ) { const val = attrToVal ( value ) ; if ( state [ key ] !== val ) state [ key ] = val ; } } ) ; } ) ; observer . observe ( target , { attributeFilter : Object . entries ( model ) . reduce ( ( attrs , [ key , val ] ) => { if ( typeof val !== 'function' ) { attrs . push ( `data-${ kebabCase ( key ) }` ) ; } return attrs ; } , [ ] ) , attributeOldValue : true , characterData : false , childList : false , subtree : false } ) ; const destroy = ( ) => { observer . disconnect ( ) ; dispose ( renderer ) ; effects . forEach ( ( cancel ) => cancel ( ) ) ; effects . clear ( ) ; events . forEach ( ( off ) => off ( ) ) ; events . clear ( ) ; target . innerHTML = '' ; emit ( 'destroy' , model ) ; } ; const ctx = ( fn ) => fn ( ... context ) ; return { on , ctx , model , state , effect , target , destroy , render : rerender , } ; } ; export const $$ = ( { target , ... config } , ... context ) : Type . Widgets => { if ( ! ( target as NodeList | Node [ ] ) . length ) { target = [ target ] as Node [ ] ; } const widgets = Array . prototype . map . call ( target , ( target ) => { return $ ( { ... config , target } as Type . Config , ... context ) ; } ) ; return { ... widgets , effect : ( fn , opts ) : ( ) => void => { const cancels = widgets . map ( ( widget ) => widget . effect ( fn ( widget . state ) , opts ) ) ; return ( ) => cancels . forEach ( cancel => cancel ( ) ) ; } , on : ( ... args ) : ( ) => void => { const offs = widgets . map ( ( widget ) => widget . on ( ... args ) ) ; return ( ) => offs . forEach ( off => off ( ) ) ; } , destroy : ( ) : void => widgets . forEach ( ( widget ) => widget . destroy ( ) ) , render : ( ) : void => widgets . forEach ( ( widget ) => widget . render ( ) ) , state : ( fn ) : void => { widgets . forEach ( ( widget ) => fn ( widget . state ) ) ; } , ctx : ( fn ) => fn ( ... context ) , forEach : Array . prototype . forEach . bind ( widgets ) , target , } ; } ;