diff --git a/lib/components/GenericDialog.vue b/lib/components/GenericDialog.vue new file mode 100644 index 00000000..fc279c37 --- /dev/null +++ b/lib/components/GenericDialog.vue @@ -0,0 +1,66 @@ + + + + + diff --git a/lib/components/types.ts b/lib/components/types.ts index d5cd7bb1..a46c480f 100644 --- a/lib/components/types.ts +++ b/lib/components/types.ts @@ -22,6 +22,8 @@ import type { Node } from '@nextcloud/files' +export type ISeverity = 'info' | 'warning' | 'error' + /** * Interface for defining buttons passed to the Dialog component * See NcDialogButton diff --git a/lib/dialogs.ts b/lib/dialogs.ts new file mode 100644 index 00000000..2b07dcf2 --- /dev/null +++ b/lib/dialogs.ts @@ -0,0 +1,167 @@ +/** + * @copyright Copyright (c) 2024 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 { IDialogButton, ISeverity } from './components/types' +import type Vue from 'vue' + +import GenericDialog from './components/GenericDialog.vue' +import { spawnDialog } from './utils/dialogs' + +/** + * This class provides generic Nextcloud themed dialogs + */ +export class Dialog { + + #name: string + #text: string + #buttons: IDialogButton[] + #severity?: ISeverity + #dialog?: Vue + + /** @deprecated */ + #html?: string + + constructor( + name: string, + text: string, + buttons: IDialogButton[] = [], + severity?: ISeverity, + ) { + this.#name = name + this.#text = text + this.#buttons = buttons + this.#severity = severity + this.#dialog = undefined + this.#html = undefined + } + + /** + * @deprecated DO NOT USE! It will be removed in the near future! + * @param html HTML content + */ + setHTML(html: string) { + this.#html = html + return this + } + + /** + * Spawn and show the dialog - if already open the previous instance will be destroyed + * @return Promise that resolves when the dialog is answered successfully and rejects on close + */ + show() { + if (this.#dialog) { + this.#dialog.$destroy() + } + + return new Promise((resolve) => { + this.#dialog = spawnDialog(GenericDialog, + { + buttons: this.#buttons, + name: this.#name, + text: this.#text, + severity: this.#severity, + html: this.#html, + }, + resolve, + ) + }) + } + + /** + * Hide and destroy the current dialog instance + */ + hide() { + this.#dialog?.$destroy() + } + +} + +/** + * The DialogBuilder provides an easy to use interface for creating simple dialogs in consistent Nextcloud design + */ +export class DialogBuilder { + + #severity?: ISeverity + #text: string + #name: string + #buttons: IDialogButton[] + + constructor() { + this.#severity = undefined + this.#text = '' + this.#name = '' + this.#buttons = [] + } + + /** + * Set dialog name + * @param name The name or headline of the dialog + */ + setName(name: string) { + this.#name = name + return this + } + + /** + * Set the dialog text + * @param text Main text of the dialog + */ + setText(text: string) { + this.#text = text + return this + } + + /** + * Set the severity of the dialog + * @param severity Severity of the dialog + */ + setSeverity(severity: ISeverity) { + this.#severity = severity + return this + } + + /** + * Set buttons from array + * @param buttons Either an array of dialog buttons + */ + setButtons(buttons: IDialogButton[]) { + if (this.#buttons.length > 0) { + console.warn('[@nextcloud/dialogs] Dialog buttons are already set - this overrides previous buttons.') + } + this.#buttons = buttons + return this + } + + /** + * Add a single button + * @param button Button to add + */ + addButton(button: IDialogButton) { + this.#buttons.push(button) + return this + } + + build() { + return new Dialog(this.#name, this.#text, this.#buttons, this.#severity) + } + +} diff --git a/lib/index.ts b/lib/index.ts index 860aed72..94c83660 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -1,3 +1,25 @@ +/** + * @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 . + * + */ + export { FilePicker, FilePickerClosed, @@ -29,4 +51,9 @@ export type { export { spawnDialog } from './utils/dialogs.js' +export { + Dialog, + DialogBuilder, +} from './dialogs' + export type { IFilePickerButton, IFilePickerFilter } from './components/types.js' diff --git a/lib/utils/dialogs.ts b/lib/utils/dialogs.ts index a5b8d5bd..3630c72a 100644 --- a/lib/utils/dialogs.ts +++ b/lib/utils/dialogs.ts @@ -30,7 +30,7 @@ import Vue, { toRaw } from 'vue' * @param props Properties to pass to the dialog * @param onClose Callback when the dialog is closed */ -export const spawnDialog = (dialog: Component | AsyncComponent, props: any, onClose: (...rest: unknown[]) => void = () => {}) => { +export const spawnDialog = (dialog: Component | AsyncComponent, props: any, onClose: (...rest: unknown[]) => void = () => {}): Vue => { const el = document.createElement('div') const container: HTMLElement = document.querySelector(props?.container) || document.body @@ -50,4 +50,5 @@ export const spawnDialog = (dialog: Component | AsyncComponent, props: any, onCl }, }), }) + return vue }