Skip to content

Commit

Permalink
feat!(FilePicker): Modernize FilePickerBuilder to directly make use…
Browse files Browse the repository at this point in the history
… of the Vue based file picker

Signed-off-by: Ferdinand Thiessen <[email protected]>
  • Loading branch information
susnux committed Aug 16, 2023
1 parent 64ab23b commit b575cf7
Showing 1 changed file with 149 additions and 68 deletions.
217 changes: 149 additions & 68 deletions lib/filepicker.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,40 @@
/// <reference types="@nextcloud/typings" />
/**
* @copyright Copyright (c) 2023 Ferdinand Thiessen <[email protected]>
*
* @author Ferdinand Thiessen <[email protected]>
*
* @license AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

import type { IFilePickerButton } from './components/types'
import type { Node } from '@nextcloud/files'

import { spawnDialog } from './utils/dialogs'
import { FilePickerVue } from './components/FilePicker/index'
import { t } from './utils/l10n'

/**
* Type of filter functions to filter the FilePicker's file list
*/
export type FilePickerFilter = (node: Node) => boolean

/**
* @deprecated
*/
export enum FilePickerType {
Choose = 1,
Move = 2,
Expand All @@ -8,70 +43,56 @@ export enum FilePickerType {
Custom = 5,
}

export interface FilePickerButton {
text: string
type?: 'primary' | 'secondary'
/** Passed on the callback as second argument */
id: number
}

export class FilePicker {

private title: string
private multiSelect: boolean
private mimeTypeFiler: string[]
private modal: boolean
private type: FilePickerType
private mimeTypeFilter: string[]
private directoriesAllowed: boolean
private buttons?: FilePickerButton[]
private buttons: IFilePickerButton[]
private path?: string
private filter?: Nextcloud.v24.FilePickerFilter
private filter?: FilePickerFilter

public constructor(title: string,
multiSelect: boolean,
mimeTypeFilter: string[],
modal: boolean,
type: FilePickerType,
directoriesAllowed: boolean,
buttons: IFilePickerButton[],
path?: string,
filter?: Nextcloud.v24.FilePickerFilter,
buttons?: FilePickerButton[]) {
filter?: FilePickerFilter) {
this.title = title
this.multiSelect = multiSelect
this.mimeTypeFiler = mimeTypeFilter
this.modal = modal
this.type = type
this.mimeTypeFilter = mimeTypeFilter
this.directoriesAllowed = directoriesAllowed
this.path = path
this.filter = filter
this.buttons = buttons
}

public async pick(): Promise<[string|string[], FilePickerType]> {
// Async import for module splitting (treeshaking)
const filepicker = (await import('./legacy')).filepicker

return new Promise((resolve) => {
const buttons = this.buttons?.map(button => ({
defaultButton: button.type === 'primary',
label: button.text,
type: button.id,
/**
* Pick files using the FilePicker
*
* @return Promise with array of picked files or rejected promise on close without picking
*/
public async pick(): Promise<string[]> {
return new Promise((resolve, reject) => {
const buttons = this.buttons.map((button) => ({
...button,
callback: (nodes: Node[]) => {
button.callback(nodes)
resolve(nodes.map((node) => node.path))
},
}))

filepicker(
this.title,
(path: string | string[], type: FilePickerType) => resolve([path, type]),
this.multiSelect,
this.mimeTypeFiler,
this.modal,
this.type,
this.path,
{
allowDirectoryChooser: this.directoriesAllowed,
filter: this.filter,
buttons,
},
)
spawnDialog(FilePickerVue, {
allowPickDirectory: this.directoriesAllowed,
buttons,
name: this.title,
path: this.path,
mimetypeFilter: this.mimeTypeFilter,
multiselect: this.multiSelect,
filterFn: this.filter,
}, reject)
})
}

Expand All @@ -81,82 +102,142 @@ export class FilePickerBuilder {

private title: string
private multiSelect = false
private mimeTypeFiler: string[] = []
private modal = true
private type: FilePickerType = FilePickerType.Choose
private mimeTypeFilter: string[] = []
private directoriesAllowed = false
private path?: string
private filter?: Nextcloud.v24.FilePickerFilter
private buttons: FilePickerButton[] = []
private filter?: FilePickerFilter
private buttons: IFilePickerButton[] = []

/**
* Construct a new FilePicker
*
* @param title Title of the FilePicker
*/
public constructor(title: string) {
this.title = title
}

/**
* Enable or disable picking multiple files
*
* @param ms True to enable picking multiple files, false otherwise
*/
public setMultiSelect(ms: boolean): FilePickerBuilder {
this.multiSelect = ms
return this
}

/**
* Add allowed MIME type
*
* @param filter MIME type to allow
*/
public addMimeTypeFilter(filter: string): FilePickerBuilder {
this.mimeTypeFiler.push(filter)
this.mimeTypeFilter.push(filter)
return this
}

/**
* Set allowed MIME types
*
* @param filter Array of allowed MIME types
*/
public setMimeTypeFilter(filter: string[]): FilePickerBuilder {
this.mimeTypeFiler = filter
this.mimeTypeFilter = filter
return this
}

public addButton(button: FilePickerButton): FilePickerBuilder {
/**
* Add a button to the FilePicker
*
* @param button The button
*/
public addButton(button: IFilePickerButton): FilePickerBuilder {
this.buttons.push(button)
return this
}

/**
* @param modal no function
* @deprecated Does not have any effect as the dialog is always modal
* Set FilePicker type based on legacy file picker types
* @param type The legacy filepicker type to emulate
* @deprecated Use `addButton` instead as with setType you do not know which button was pressed
*/
public setModal(modal: boolean): FilePickerBuilder {
this.modal = modal
return this
}

public setType(type: FilePickerType): FilePickerBuilder {
this.type = type
this.buttons = []

if (type === FilePickerType.CopyMove || type === FilePickerType.Copy) {
this.buttons.push({
callback: () => {},
label: t('Copy'),
type: 'primary',
})
} else if (type === FilePickerType.Move) {
this.buttons.push({
callback: () => {},
label: t('Move'),
type: 'primary',
})
} else if (type === FilePickerType.Choose) {
this.buttons.push({
callback: () => {},
label: t('Choose'),
type: 'primary',
})
}

if (type === FilePickerType.CopyMove) {
this.buttons.push({
callback: () => {},
label: t('Move'),
type: 'secondary',
})
}

return this
}

/**
* Allow to pick directories besides files
*
* @param allow True to allow picking directories
*/
public allowDirectories(allow = true): FilePickerBuilder {
this.directoriesAllowed = allow
return this
}

/**
* Set starting path of the FilePicker
*
* @param path Path to start from picking
*/
public startAt(path: string): FilePickerBuilder {
this.path = path
return this
}

public setFilter(filter: Nextcloud.v24.FilePickerFilter): FilePickerBuilder {
/**
* Add filter function to filter file list of FilePicker
*
* @param filter Filter function to apply
*/
public setFilter(filter: FilePickerFilter): FilePickerBuilder {
this.filter = filter
return this
}

/**
* Construct the configured FilePicker
*/
public build(): FilePicker {
if (this.buttons && this.type !== FilePickerType.Custom) {
console.error('FilePickerBuilder: When adding custom buttons the `type` must be set to `FilePickerType.Custom`.')
}

return new FilePicker(
this.title,
this.multiSelect,
this.mimeTypeFiler,
this.modal,
this.type,
this.mimeTypeFilter,
this.directoriesAllowed,
this.buttons,
this.path,
this.filter,
this.buttons,
)
}

Expand Down

0 comments on commit b575cf7

Please sign in to comment.