/**
 * -------------------------------------------------------------------------------------------
 * Copyright (c) Microsoft Corporation.  All Rights Reserved.  Licensed under the MIT License.
 * See License in the project root for license information.
 * -------------------------------------------------------------------------------------------
 */ import { arraysAreEqual , GraphPageIterator , MgtTemplatedComponent , Providers , ProviderState } from '@microsoft/mgt-element' ; import { DriveItem } from '@microsoft/microsoft-graph-types' ; import { customElement , html , internalProperty , property , TemplateResult } from 'lit-element' ; import { repeat } from 'lit-html/directives/repeat' ; import { clearFilesCache , fetchNextAndCacheForFilesPageIterator , getDriveFilesByIdIterator , getDriveFilesByPathIterator , getFilesByIdIterator , getFilesByListQueryIterator , getFilesByPathIterator , getFilesByQueries , getFilesIterator , getGroupFilesByIdIterator , getGroupFilesByPathIterator , getMyInsightsFiles , getSiteFilesByIdIterator , getSiteFilesByPathIterator , getUserFilesByIdIterator , getUserFilesByPathIterator , getUserInsightsFiles } from '../../graph/graph.files' ; import './mgt-file-upload/mgt-file-upload' ; import { OfficeGraphInsightString , ViewType } from '../../graph/types' ; import { styles } from './mgt-file-list-css' ; import { strings } from './strings' ; import { MgtFile } from '../mgt-file/mgt-file' ; import { MgtFileUploadConfig } from './mgt-file-upload/mgt-file-upload' ; export { FluentDesignSystemProvider , FluentProgressRing } from '@fluentui/web-components' ; export * from './mgt-file-upload/mgt-file-upload' ; export class extends MgtTemplatedComponent fires cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop @ customElement ( 'mgt-file-list' ) export class MgtFileList extends MgtTemplatedComponent { /**
     * Array of styles to apply to the element. The styles should be defined
     * using the `css` tag function.
     */ static get styles ( ) { return styles ; } protected get strings ( ) { return strings ; } type { string } memberof @ property ( { attribute : 'file-list-query' } ) public get fileListQuery ( ) : string { return this . _fileListQuery ; } public set fileListQuery ( value ) { if ( value === this . _fileListQuery ) { return ; } this . _fileListQuery = value ; this . requestStateUpdate ( true ) ; } type { string [ ] } memberof @ property ( { attribute : 'file-queries' , converter : ( value , type ) => { if ( value ) { return value . split ( ',' ) . map ( v => v . trim ( ) ) ; } else { return null ; } } } ) public get fileQueries ( ) : string [ ] { return this . _fileQueries ; } public set fileQueries ( value ) { if ( arraysAreEqual ( this . _fileQueries , value ) ) { return ; } this . _fileQueries = value ; this . requestStateUpdate ( true ) ; } type { MicrosoftGraph . DriveItem [ ] } memberof @ property ( { type : Object } ) public files : DriveItem [ ] ; type { string } memberof @ property ( { attribute : 'site-id' } ) public get siteId ( ) : string { return this . _siteId ; } public set siteId ( value ) { if ( value === this . _siteId ) { return ; } this . _siteId = value ; this . requestStateUpdate ( true ) ; } type { string } memberof @ property ( { attribute : 'drive-id' } ) public get driveId ( ) : string { return this . _driveId ; } public set driveId ( value ) { if ( value === this . _driveId ) { return ; } this . _driveId = value ; this . requestStateUpdate ( true ) ; } type { string } memberof @ property ( { attribute : 'group-id' } ) public get groupId ( ) : string { return this . _groupId ; } public set groupId ( value ) { if ( value === this . _groupId ) { return ; } this . _groupId = value ; this . requestStateUpdate ( true ) ; } type { string } memberof @ property ( { attribute : 'item-id' } ) public get itemId ( ) : string { return this . _itemId ; } public set itemId ( value ) { if ( value === this . _itemId ) { return ; } this . _itemId = value ; this . requestStateUpdate ( true ) ; } type { string } memberof @ property ( { attribute : 'item-path' } ) public get itemPath ( ) : string { return this . _itemPath ; } public set itemPath ( value ) { if ( value === this . _itemPath ) { return ; } this . _itemPath = value ; this . requestStateUpdate ( true ) ; } type { string } memberof @ property ( { attribute : 'user-id' } ) public get userId ( ) : string { return this . _userId ; } public set userId ( value ) { if ( value === this . _userId ) { return ; } this . _userId = value ; this . requestStateUpdate ( true ) ; } type { OfficeGraphInsightString } memberof @ property ( { attribute : 'insight-type' } ) public get insightType ( ) : OfficeGraphInsightString { return this . _insightType ; } public set insightType ( value ) { if ( value === this . _insightType ) { return ; } this . _insightType = value ; this . requestStateUpdate ( true ) ; } type { ViewType } memberof @ property ( { attribute : 'item-view' , converter : value => { if ( ! value || value . length === 0 ) { return ViewType . threelines ; } value = value . toLowerCase ( ) ; if ( typeof ViewType [ value ] === 'undefined' ) { return ViewType . threelines ; } else { return ViewType [ value ] ; } } } ) public itemView : ViewType ; type { string [ ] } memberof @ property ( { attribute : 'file-extensions' , converter : ( value , type ) => { return value . split ( ',' ) . map ( v => v . trim ( ) ) ; } } ) public get fileExtensions ( ) : string [ ] { return this . _fileExtensions ; } public set fileExtensions ( value ) { if ( arraysAreEqual ( this . _fileExtensions , value ) ) { return ; } this . _fileExtensions = value ; this . requestStateUpdate ( true ) ; } type { number } memberof @ property ( { attribute : 'page-size' , type : Number } ) public get pageSize ( ) : number { return this . _pageSize ; } public set pageSize ( value ) { if ( value === this . _pageSize ) { return ; } this . _pageSize = value ; this . requestStateUpdate ( true ) ; } type { boolean } memberof @ property ( { attribute : 'hide-more-files-button' , type : Boolean } ) public hideMoreFilesButton : boolean ; type { number } memberof @ property ( { attribute : 'max-file-size' , type : Number } ) public get maxFileSize ( ) : number { return this . _maxFileSize ; } public set maxFileSize ( value ) { if ( value === this . _maxFileSize ) { return ; } this . _maxFileSize = value ; this . requestStateUpdate ( true ) ; } type { boolean } memberof @ property ( { attribute : 'enable-file-upload' , type : Boolean } ) public enableFileUpload : boolean ; type { number } memberof @ property ( { attribute : 'max-upload-file' , type : Number } ) public get maxUploadFile ( ) : number { return this . _maxUploadFile ; } public set maxUploadFile ( value ) { if ( value === this . _maxUploadFile ) { return ; } this . _maxUploadFile = value ; this . requestStateUpdate ( true ) ; } type { string [ ] } memberof @ property ( { attribute : 'excluded-file-extensions' , converter : ( value , type ) => { return value . split ( ',' ) . map ( v => v . trim ( ) ) ; } } ) public get excludedFileExtensions ( ) : string [ ] { return this . _excludedFileExtensions ; } public set excludedFileExtensions ( value ) { if ( arraysAreEqual ( this . _excludedFileExtensions , value ) ) { return ; } this . _excludedFileExtensions = value ; this . requestStateUpdate ( true ) ; } static return { * } memberof public static get requiredScopes ( ) : string [ ] { return [ ... new Set ( [ ... MgtFile . requiredScopes ] ) ] ; } private _fileListQuery : string ; private _fileQueries : string [ ] ; private _siteId : string ; private _itemId : string ; private _driveId : string ; private _itemPath : string ; private _groupId : string ; private _insightType : OfficeGraphInsightString ; private _fileExtensions : string [ ] ; private _pageSize : number ; private _excludedFileExtensions : string [ ] ; private _maxUploadFile : number ; private _maxFileSize : number ; private _userId : string ; private _preloadedFiles : DriveItem [ ] ; private pageIterator : GraphPageIterator < DriveItem > ; private _focusedItemIndex : number = - 1 ; @ internalProperty ( ) private _isLoadingMore : boolean ; constructor ( ) { super ( ) ; this . pageSize = 10 ; this . itemView = ViewType . twolines ; this . maxUploadFile = 10 ; this . enableFileUpload = false ; this . _preloadedFiles = [ ] ; } memberof protected requestStateUpdate ( force ? ) { this . clearState ( ) ; return super . requestStateUpdate ( force ) ; } memberof protected clearState ( ) { super . clearState ( ) ; this . files = null ; } public render ( ) { if ( ! this . files && this . isLoadingState ) { return this . renderLoading ( ) ; } if ( ! this . files || this . files . length === 0 ) { return this . renderNoData ( ) ; } return this . renderTemplate ( 'default' , { files : this . files } ) || this . renderFiles ( ) ; } protected returns { TemplateResult } memberof protected renderLoading ( ) { return this . renderTemplate ( 'loading' , null ) || html `` ; } protected returns { TemplateResult } memberof protected renderNoData ( ) { return ( this . renderTemplate ( 'no-data' , null ) || ( this . enableFileUpload === true && Providers . globalProvider !== undefined ? html `
      <fluent-design-system-provider use-defaults>
        <div id="file-list-wrapper" class="file-list-wrapper" dir=${ this . direction }>
          ${ this . renderFileUpload ( ) }
        </div>
      </fluent-design-system-provider>
      ` : html `` ) ) ; } protected param { * } files returns { TemplateResult } memberof protected renderFiles ( ) { return html `
    <fluent-design-system-provider use-defaults>
      <div id="file-list-wrapper" class="file-list-wrapper" dir=${ this . direction }>
        ${ this . enableFileUpload ? this . renderFileUpload ( ) : null }
        <ul
          id="file-list"
          class="file-list"
          tabindex="0"
          @keydown="${ this . onFileListKeyDown }"
          @keyup="${ this . onFileListKeyUp }"
          @blur="${ this . onFileListOut }"
        >
          ${ repeat ( this . files , f => f . id , f => html `
              <li class="file-item">
                ${ this . renderFile ( f ) }
              </li>
            ` ) }
        </ul>
        ${ ! this . hideMoreFilesButton && this . pageIterator && ( this . pageIterator . hasNext || this . _preloadedFiles . length ) ? this . renderMoreFileButton ( ) : null }
      </div>
    </fluent-design-system-provider>
    ` ; } protected returns { TemplateResult } memberof protected renderFile ( file ) { const view = this . itemView ; return ( this . renderTemplate ( 'file' , { file } , file . id ) || html `
        <mgt-file .fileDetails=${ file } .view=${ view } @click=${ e => this . handleItemSelect ( file , e ) }></mgt-file>
      ` ) ; } protected returns { TemplateResult } memberof protected renderMoreFileButton ( ) { if ( this . _isLoadingMore ) { return html `
        <fluent-progress-ring role="progressbar" viewBox="0 0 8 8" class="progress-ring"></fluent-progress-ring>
      ` ; } else { return html `<a id="show-more" class="show-more" @click=${ ( ) => this . renderNextPage ( ) } tabindex="0" @keydown=${ this . onShowMoreKeyDown }><span>${ this . strings . showMoreSubtitle }<span></a>` ; } } returns protected renderFileUpload ( ) { const fileUploadConfig = { graph : Providers . globalProvider . graph . forComponent ( this ) , driveId : this . driveId , excludedFileExtensions : this . excludedFileExtensions , groupId : this . groupId , itemId : this . itemId , itemPath : this . itemPath , userId : this . userId , siteId : this . siteId , maxFileSize : this . maxFileSize , maxUploadFile : this . maxUploadFile } ; return html `
        <mgt-file-upload .fileUploadList=${ fileUploadConfig } ></mgt-file-upload>
      ` ; } param event private onShowMoreKeyDown ( event ) { if ( event && event . code === 'Enter' ) { event . preventDefault ( ) ; this . renderNextPage ( ) ; } } param event private onFileListKeyUp ( event ) { const fileList = this . renderRoot . querySelector ( '.file-list' ) ; const focusedItem = fileList . children [ this . _focusedItemIndex ] ; if ( event . code === 'Enter' || event . code === 'Space' ) { event . preventDefault ( ) ; focusedItem ?. classList . remove ( 'selected' ) ; focusedItem ?. classList . add ( 'focused' ) ; } } param event private onFileListKeyDown ( event ) { const fileList = this . renderRoot . querySelector ( '.file-list' ) ; let focusedItem ; if ( ! fileList || ! fileList . children . length ) { return ; } if ( event . code === 'ArrowUp' || event . code === 'ArrowDown' ) { if ( event . code === 'ArrowUp' ) { if ( this . _focusedItemIndex === - 1 ) { this . _focusedItemIndex = fileList . children . length ; } this . _focusedItemIndex = ( this . _focusedItemIndex - 1 + fileList . children . length ) % fileList . children . length ; } if ( event . code === 'ArrowDown' ) { this . _focusedItemIndex = ( this . _focusedItemIndex + 1 ) % fileList . children . length ; } focusedItem = fileList . children [ this . _focusedItemIndex ] ; this . updateItemBackgroundColor ( fileList , focusedItem , 'focused' ) ; } if ( event . code === 'Enter' || event . code === 'Space' ) { focusedItem = fileList . children [ this . _focusedItemIndex ] ; const file = focusedItem . children [ 0 ] as any ; event . preventDefault ( ) ; this . fireCustomEvent ( 'itemClick' , file . fileDetails ) ; this . updateItemBackgroundColor ( fileList , focusedItem , 'selected' ) ; } if ( event . code === 'Tab' ) { focusedItem = fileList . children [ this . _focusedItemIndex ] ; focusedItem ?. classList . remove ( 'focused' ) ; } } /**
     * Remove accessibility keyboard focused when out of file list
     *
     */ private onFileListOut ( ) { const fileList = this . renderRoot . querySelector ( '.file-list' ) ; const focusedItem = fileList . children [ this . _focusedItemIndex ] ; focusedItem ?. classList . remove ( 'focused' ) ; } protected returns memberof protected async loadState ( ) { const provider = Providers . globalProvider ; if ( ! provider || provider . state === ProviderState . Loading ) { return ; } if ( provider . state === ProviderState . SignedOut ) { this . files = null ; return ; } const graph = provider . graph . forComponent ( this ) ; let files ; let pageIterator ; const getFromMyDrive = ! this . driveId && ! this . siteId && ! this . groupId && ! this . userId ; if ( ( this . driveId && ! this . itemId && ! this . itemPath ) || ( this . groupId && ! this . itemId && ! this . itemPath ) || ( this . siteId && ! this . itemId && ! this . itemPath ) || ( this . userId && ! this . insightType && ! this . itemId && ! this . itemPath ) ) { this . files = null ; } if ( ! this . files ) { if ( this . fileListQuery ) { pageIterator = await getFilesByListQueryIterator ( graph , this . fileListQuery , this . pageSize ) ; } else if ( this . fileQueries ) { files = await getFilesByQueries ( graph , this . fileQueries ) ; } else if ( getFromMyDrive ) { if ( this . itemId ) { pageIterator = await getFilesByIdIterator ( graph , this . itemId , this . pageSize ) ; } else if ( this . itemPath ) { pageIterator = await getFilesByPathIterator ( graph , this . itemPath , this . pageSize ) ; } else if ( this . insightType ) { files = await getMyInsightsFiles ( graph , this . insightType ) ; } else { pageIterator = await getFilesIterator ( graph , this . pageSize ) ; } } else if ( this . driveId ) { if ( this . itemId ) { pageIterator = await getDriveFilesByIdIterator ( graph , this . driveId , this . itemId , this . pageSize ) ; } else if ( this . itemPath ) { pageIterator = await getDriveFilesByPathIterator ( graph , this . driveId , this . itemPath , this . pageSize ) ; } } else if ( this . groupId ) { if ( this . itemId ) { pageIterator = await getGroupFilesByIdIterator ( graph , this . groupId , this . itemId , this . pageSize ) ; } else if ( this . itemPath ) { pageIterator = await getGroupFilesByPathIterator ( graph , this . groupId , this . itemPath , this . pageSize ) ; } } else if ( this . siteId ) { if ( this . itemId ) { pageIterator = await getSiteFilesByIdIterator ( graph , this . siteId , this . itemId , this . pageSize ) ; } else if ( this . itemPath ) { pageIterator = await getSiteFilesByPathIterator ( graph , this . siteId , this . itemPath , this . pageSize ) ; } } else if ( this . userId ) { if ( this . itemId ) { pageIterator = await getUserFilesByIdIterator ( graph , this . userId , this . itemId , this . pageSize ) ; } else if ( this . itemPath ) { pageIterator = await getUserFilesByPathIterator ( graph , this . userId , this . itemPath , this . pageSize ) ; } else if ( this . insightType ) { files = await getUserInsightsFiles ( graph , this . userId , this . insightType ) ; } } if ( pageIterator ) { this . pageIterator = pageIterator ; this . _preloadedFiles = [ ... this . pageIterator . value ] ; if ( this . _preloadedFiles . length >= this . pageSize ) { files = this . _preloadedFiles . splice ( 0 , this . pageSize ) ; } else { files = this . _preloadedFiles . splice ( 0 , this . _preloadedFiles . length ) ; } } let filteredByFileExtension ; if ( this . fileExtensions && this . fileExtensions !== null ) { if ( this . pageIterator && this . pageIterator . value ) { while ( this . pageIterator . hasNext ) { await fetchNextAndCacheForFilesPageIterator ( this . pageIterator ) ; } files = this . pageIterator . value ; this . _preloadedFiles = [ ] ; } filteredByFileExtension = files . filter ( file => { for ( const e of this . fileExtensions ) { if ( e == this . getFileExtension ( file . name ) ) { return file ; } } } ) ; } if ( filteredByFileExtension && filteredByFileExtension . length >= 0 ) { this . files = filteredByFileExtension ; } else { this . files = files ; } } } protected memberof protected handleItemSelect ( item , event ) { this . fireCustomEvent ( 'itemClick' , item ) ; if ( event ) { const fileList = this . renderRoot . querySelector ( '.file-list' ) ; const nodes = Array . from ( fileList . children ) ; const li = event . target . closest ( 'li' ) ; const index = nodes . indexOf ( li ) ; this . _focusedItemIndex = index ; for ( let i = 0 ; i < fileList . children . length ; i ++ ) { fileList . children [ i ] . classList . remove ( 'focused' ) ; } } } protected memberof protected async renderNextPage ( ) { if ( this . _preloadedFiles . length > 0 ) { this . files = [ ... this . files , ... this . _preloadedFiles . splice ( 0 , Math . min ( this . pageSize , this . _preloadedFiles . length ) ) ] ; } else { if ( this . pageIterator . hasNext ) { this . _isLoadingMore = true ; const root = this . renderRoot . querySelector ( 'file-list-wrapper' ) ; if ( root && root . animate ) { root . animate ( [ { height : 'auto' , transformOrigin : 'top left' } , { height : 'auto' , transformOrigin : 'top left' } ] , { duration : 1000 , easing : 'ease-in-out' , fill : 'both' } ) ; } await fetchNextAndCacheForFilesPageIterator ( this . pageIterator ) ; this . _isLoadingMore = false ; this . files = this . pageIterator . value ; } } this . requestUpdate ( ) ; } param name returns { string } private getFileExtension ( name ) { const re = /(?:\.([^.]+))?$/ ; const fileExtension = re . exec ( name ) [ 1 ] || '' ; return fileExtension ; } param fileList param focusedItem param className private updateItemBackgroundColor ( fileList , focusedItem , className ) { for ( let i = 0 ; i < fileList . children . length ; i ++ ) { fileList . children [ i ] . classList . remove ( className ) ; } if ( focusedItem ) { focusedItem . classList . add ( className ) ; focusedItem . scrollIntoView ( { behavior : 'smooth' , block : 'nearest' , inline : 'start' } ) ; } } param clearCache public reload ( clearCache = false ) { if ( clearCache ) { clearFilesCache ( ) ; } this . requestStateUpdate ( true ) ; } }