/** * EGroupware eTemplate2 - Button to open a vfs select dialog * * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @package api * @link https://www.egroupware.org * @author Nathan Gray */ import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget"; import {html, LitElement, nothing} from "lit"; import {HasSlotController} from "../Et2Widget/slot"; import {property} from "lit/decorators/property.js"; import {Et2VfsSelectDialog, FileInfo} from "./Et2VfsSelectDialog"; import {waitForEvent} from "../Et2Widget/event"; /** * @summary Button to open a file selection dialog, and return the selected path(s) as a value * @since 23.1 * * @dependency et2-vfs-select-dialog * @dependency et2-button * * @slot prefix - Before the toolbar * @slot suffix - Like prefix, but after * * @event change - Emitted when the control's value changes. * * @csspart form-control-input - The textbox's wrapper. * @csspart form-control-help-text - The help text's wrapper. * @csspart prefix - The container that wraps the prefix slot. * @csspart suffix - The container that wraps the suffix slot. * */ export class Et2VfsSelectButton extends Et2InputWidget(LitElement) { /** Icon for the button */ @property() image : string; /** Currently selected files */ @property() value : string[] | FileInfo[] = []; /** * The dialog’s label as displayed in the header. * You should always include a relevant label, as it is required for proper accessibility. */ @property() title : string = "Select"; /** * Dialog mode * Quickly sets button label, multiple, selection and for "select-dir", mime-type **/ @property({type: String}) mode : "open" | "open-multiple" | "saveas" | "select-dir"; /** Button label */ @property({type: String}) buttonLabel : string = "Select"; /** Provide a suggested filename for saving */ @property() filename : string = ""; /** Allow selecting multiple files */ @property({type: Boolean}) multiple = false; /** Start path in VFS. Leave unset to use the last used path. */ @property() path : string = ""; /** Limit display to the given mime-type */ @property() mime : string | string[] | RegExp = ""; /** Server side callback to process selected value(s) in * app.class.method or class::method format. The first parameter will * be Method ID, the second the file list. 'download' is reserved and it * means it should use download_baseUrl instead of path in value (no method * will be actually executed). */ @property() method : string = ""; /** ID passed to method */ @property({type: String, reflect: true}) methodId : string; protected readonly hasSlotController = new HasSlotController(this, ''); protected processingPromise : Promise = null; get _dialog() : Et2VfsSelectDialog {return this.shadowRoot.querySelector("et2-vfs-select-dialog") ?? null}; constructor() { super(); this.handleClick = this.handleClick.bind(this); } protected handleClick(event) { if(this._dialog && typeof this._dialog.show == "function") { this._dialog.show(); // Avoids dialog showing old value if reused this._dialog.requestUpdate("value"); // This is were we bind to get informed when user closes the dialog waitForEvent(this._dialog, "sl-after-show").then(async() => { this.processDialogComplete(await this._dialog.getComplete()); }); } } protected processDialogComplete([button, paths]) { // Cancel or close do nothing if(!button) { return; } const oldValue = this.value; this.value = paths ?? []; this.requestUpdate("value", oldValue); if(this.method && this.method == "download") { // download this.value.forEach(path => { this.egw().open_link(this._dialog.fileInfo(path)?.downloadUrl, "blank", "view", 'download'); }); } else if(this.method) { this.sendFiles(); } this.updateComplete.then(() => { this.dispatchEvent(new Event("change", {bubbles: true})); // Reset value after processing if(this.method) { this.value = []; this.requestUpdate("value"); } }) } protected sendFiles() { this.processingPromise = this.egw().request( this.method, [this.methodId, this.value/*, submit_button_id, savemode*/] ).then((data) => { this.processingPromise = null; // UI update now that we're done this.requestUpdate(); return {success: true}; } ); // UI update, we're busy this.requestUpdate(); } protected dialogTemplate() { return html` `; } render() { const hasUserDialog = this.hasSlotController.test("[default]"); const processing = this.processingPromise !== null; const image = processing ? "" : (this.image || "filemanager/navbar"); return html` ${processing ? html` ` : nothing} ${hasUserDialog ? nothing : this.dialogTemplate()} `; } } customElements.define("et2-vfs-select", Et2VfsSelectButton); export interface FileActionResult { success : boolean, message? : string }