diff --git a/l10n/messages.pot b/l10n/messages.pot
index a6fdf926..308079f3 100644
--- a/l10n/messages.pot
+++ b/l10n/messages.pot
@@ -18,10 +18,12 @@ msgstr ""
msgid "All files"
msgstr ""
+#: lib/filepicker.ts:183
#: lib/legacy.ts:135
msgid "Choose"
msgstr ""
+#: lib/filepicker.ts:171
#: lib/legacy.ts:141
msgid "Copy"
msgstr ""
@@ -43,6 +45,8 @@ msgstr ""
msgid "Modified"
msgstr ""
+#: lib/filepicker.ts:177
+#: lib/filepicker.ts:191
#: lib/legacy.ts:147
msgid "Move"
msgstr ""
diff --git a/lib/filepicker.ts b/lib/filepicker.ts
index e4f9ae33..b34dc56a 100644
--- a/lib/filepicker.ts
+++ b/lib/filepicker.ts
@@ -1,5 +1,40 @@
-///
+/**
+ * @copyright Copyright (c) 2023 Ferdinand Thiessen
+ *
+ * @author Ferdinand Thiessen
+ *
+ * @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 .
+ *
+ */
+
+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 {
+ 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,
)
}