import { ElementArea } from './element-area' ; import * as Mobx from 'mobx' ; import * as Message from '@meetalva/message' ; import * as Model from '@meetalva/model' ; import { Sender } from '../sender' ; import * as Types from '@meetalva/types' ; import * as uuid from 'uuid' ; export type RequestIdleCallbackHandle = number ; export interface RequestIdleCallbackOptions { timeout : number ; } export interface RequestIdleCallbackDeadline { readonly didTimeout : boolean ; timeRemaining : ( ( ) => number ) ; } declare global { interface Window { requestIdleCallback : ( ( callback : ( ( deadline : RequestIdleCallbackDeadline ) => void ) , opts ? : RequestIdleCallbackOptions ) => RequestIdleCallbackHandle ) ; cancelIdleCallback : ( ( handle : RequestIdleCallbackHandle ) => void ) ; } } export interface PreviewStoreInit < V , T extends Types . PreviewDocumentMode > { components : Components ; highlightArea : ElementArea ; mode : T ; project : Model . Project ; selectionArea : ElementArea ; } export interface Components { [ id ] : any ; } export interface SyntheticComponents < V > { 'synthetic:box' : V ; 'synthetic:conditional' : V ; 'synthetic:page' : V ; 'synthetic:image' : V ; 'synthetic:link' : V ; 'synthetic:text' : V ; } export class PreviewStore < V > { @ Mobx . observable private app ? : Model . AlvaApp < Message . Message > ; @ Mobx . observable private highlightArea : ElementArea ; @ Mobx . observable private metaDown : boolean = false ; @ Mobx . observable private mode : Types . PreviewDocumentMode ; @ Mobx . observable private project : Model . Project ; @ Mobx . observable private selectionArea : ElementArea ; @ Mobx . observable private scrollPosition ? : Types . Point ; private sender ? : Sender ; private components : Components ; public constructor ( init ) { this . mode = init . mode ; this . project = init . project ; this . components = init . components ; this . selectionArea = init . selectionArea ; this . highlightArea = init . highlightArea ; } public getActivePage ( ) { return this . project . getPages ( ) . find ( page => page . getActive ( ) ) ; } public getChildren < T > ( element , render ) { const childContent = element . getContentBySlotType ( Types . SlotType . Children ) ; if ( ! childContent ) { return null ; } const slot = childContent . getSlot ( ) ; if ( ! slot ) { return null ; } const elements = childContent . getElements ( ) ; if ( elements . length === 0 && ! slot . getRequired ( ) ) { return null ; } return elements . map ( render ) ; } public getComponent ( element ) { const pattern = element . getPattern ( ) ; if ( ! pattern ) { return ; } const component = this . components [ pattern . getId ( ) ] ; if ( typeof component !== 'object' || component === null ) { throw new Error ( `Could not find component with id "${ pattern . getId ( ) }" for pattern "${ pattern . getName ( ) }:${ pattern . getExportName ( ) }".` ) ; } const exportName = pattern . getExportName ( ) ; if ( ! component . hasOwnProperty ( exportName ) ) { if ( typeof component !== 'object' ) { throw new Error ( `Could not find export ${ exportName } on pattern "${ pattern . getName ( ) }:${ pattern . getExportName ( ) }".` ) ; } } const c = component as { [ key ] : unknown ; } ; return c [ exportName ] as V | undefined ; } public getElementById ( id ) { return this . project . getElementById ( id ) ; } public getHighlightedElement ( ) { return this . project . getHighlightedElements ( ) [ 0 ] ; } public getHighlightedElementContent ( ) { return this . project . getHighlightedElementContents ( ) [ 0 ] ; } public getHighlightArea ( ) { return this . highlightArea ; } public getMetaDown ( ) { return this . metaDown ; } public getProperties < T > ( element ) { return element . getProperties ( ) . reduce < { [ key ] : any ; } > ( ( renderProperties , elementProperty ) => { const patternProperty = elementProperty . getPatternProperty ( ) ; if ( ! patternProperty ) { return renderProperties ; } if ( patternProperty . getType ( ) === Types . PatternPropertyType . EventHandler ) { const property = patternProperty as Model . PatternEventHandlerProperty ; const event = property . getEvent ( ) ; renderProperties [ patternProperty . getPropertyName ( ) ] = ( e ) => { if ( event . getType ( ) === Types . PatternEventType . MouseEvent ) { if ( this . mode !== Types . PreviewDocumentMode . Static && ! this . getMetaDown ( ) ) { return ; } } const actionIds = elementProperty . getValue ( ) as unknown ; if ( ! actionIds ) { return ; } const elementActions = Array . isArray ( actionIds ) ? actionIds . map ( id => this . project . getElementActionById ( id ) ) . filter ( Boolean ) : typeof actionIds === 'string' ? [ this . project . getElementActionById ( actionIds ) ] : [ ] ; elementActions . forEach ( action => { if ( ! action ) { return ; } action . execute ( { sender : this . app || this . sender , project : this . getProject ( ) , event : e } ) ; } ) ; } ; } else { renderProperties [ patternProperty . getPropertyName ( ) ] = elementProperty . getValue ( ) ; } return renderProperties ; } , { } ) ; } public getProject ( ) { return this . project ; } public getSelectedElement ( ) { return this . project . getElements ( ) . find ( element => element . getSelected ( ) ) ; } public getSelectionArea ( ) { return this . selectionArea ; } public getSlots < T > ( element , render ) { return element . getContents ( ) . filter ( content => content . getSlotType ( ) !== Types . SlotType . Children ) . reduce < { [ key ] : T | T [ ] | null ; } > ( ( renderProperties , content ) => { const slot = content . getSlot ( ) ; if ( ! slot ) { return renderProperties ; } const elements = content . getElements ( ) ; const children = elements . length === 0 && ! slot . getRequired ( ) ? null : elements . map ( render ) ; if ( slot . getQuantity ( ) === Types . SlotQuantity . Single ) { renderProperties [ slot . getPropertyName ( ) ] = children && children [ 0 ] ; return renderProperties ; } renderProperties [ slot . getPropertyName ( ) ] = children ; return renderProperties ; } , { } ) ; } public getScrollPosition ( ) { return this . scrollPosition || { x : 0 , y : 0 } ; } public getSender ( ) { return this . sender ; } public hasHighlightedItem ( ) { return Boolean ( this . getHighlightedElement ( ) || this . getHighlightedElementContent ( ) ) ; } public hasSelectedItem ( ) { return Boolean ( this . getSelectedElement ( ) ) ; } @ Mobx . action public onElementClick ( e , data ) { if ( this . mode !== Types . PreviewDocumentMode . Static ) { e . preventDefault ( ) ; } if ( e . metaKey || this . mode === Types . PreviewDocumentMode . Static ) { return ; } e . preventDefault ( ) ; e . stopPropagation ( ) ; this . updateSelectedElement ( data ) ; if ( data . element . getRole ( ) === Types . ElementRole . Root ) { this . project . unsetSelectedElement ( ) ; } else { this . project . setSelectedElement ( data . element ) ; } if ( this . sender ) { this . sender . send ( { appId : this . app ? this . app . getId ( ) : undefined , type : Message . MessageType . SelectElement , id : uuid . v4 ( ) , payload : { element : data . element . toJSON ( ) , projectId : this . project . getId ( ) } } ) ; } } @ Mobx . action public onElementMouseOver ( e , data ) { if ( this . mode !== Types . PreviewDocumentMode . Live ) { return ; } this . updateHighlightedElement ( data ) ; if ( data . element . getRole ( ) === Types . ElementRole . Root ) { this . project . unsetHighlightedElement ( ) ; } else { this . setHighlightedElement ( data . element ) ; } if ( this . sender ) { this . sender . send ( { appId : this . app ? this . app . getId ( ) : undefined , type : Message . MessageType . HighlightElement , id : uuid . v4 ( ) , payload : { element : data . element . toJSON ( ) } } ) ; } } @ Mobx . action public updateHighlightedElement ( data ) { if ( this . mode !== Types . PreviewDocumentMode . Live ) { return ; } this . highlightArea . setElement ( data . node ) ; if ( data . element . getRole ( ) === Types . ElementRole . Root ) { this . highlightArea . hide ( ) ; } else { this . highlightArea . show ( ) ; } } @ Mobx . action public updateSelectedElement ( data ) { if ( this . mode !== Types . PreviewDocumentMode . Live ) { return ; } this . selectionArea . setElement ( data . node ) ; if ( data . element . getRole ( ) === Types . ElementRole . Root ) { this . selectionArea . hide ( ) ; } else { this . selectionArea . show ( ) ; } } @ Mobx . action public onHighlightedElementRemove ( data ) { if ( this . mode !== Types . PreviewDocumentMode . Live ) { return ; } this . project . unsetHighlightedElement ( ) ; this . project . unsetHighlightedElementContent ( ) ; } public onOutsideClick ( e ) { if ( this . mode !== Types . PreviewDocumentMode . Live ) { return ; } this . project . unsetSelectedElement ( ) ; this . project . unsetHighlightedElement ( ) ; this . project . unsetHighlightedElementContent ( ) ; } @ Mobx . action public setActivePage ( page ) { this . project . getPages ( ) . forEach ( p => p . setActive ( false ) ) ; page . setActive ( true ) ; } @ Mobx . action public setComponents ( components ) { this . components = components ; } @ Mobx . action public setHighlightedElement ( element ) { if ( element . getRole ( ) === Types . ElementRole . Root ) { return ; } element . setHighlighted ( true ) ; } @ Mobx . action public setMetaDown ( metaDown ) { this . metaDown = metaDown ; } public setSender ( sender ) { this . sender = sender ; } @ Mobx . action public setScrollPosition ( scrollPosition ) { this . scrollPosition = scrollPosition ; } @ Mobx . action public setApp ( app ) { this . app = app ; } }