-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #904 from nextcloud-libraries/feat/modernize-filep…
…icker-builder feat!(FilePicker): Modernize `FilePickerBuilder` to directly make use of the Vue based file picker
- Loading branch information
Showing
2 changed files
with
153 additions
and
68 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
|
@@ -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) | ||
}) | ||
} | ||
|
||
|
@@ -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, | ||
) | ||
} | ||
|
||
|