From e29a7a12043401aa2793f9b5613afee9ac03fd7f Mon Sep 17 00:00:00 2001 From: Tamara Steinwender <tamara.steinwender@tugraz.at> Date: Tue, 15 Jun 2021 15:32:42 +0200 Subject: [PATCH] Add documentation to clipboard --- packages/file-handling/README.md | 69 ++++++++- packages/file-handling/src/clipboard.js | 187 ++++++++++++++---------- 2 files changed, 176 insertions(+), 80 deletions(-) diff --git a/packages/file-handling/README.md b/packages/file-handling/README.md index 85882f0c..67327e04 100644 --- a/packages/file-handling/README.md +++ b/packages/file-handling/README.md @@ -40,7 +40,7 @@ and the file sink dialog will open, so you are able to store the file again. ## FileSource This web component allows the selection of local files via file dialog or drag and drop and to select and download -files from a [Nextcloud](https://nextcloud.com/) instance. +files from a [Nextcloud](https://nextcloud.com/) instance or to a dbp-clipboard. ### Usage @@ -58,7 +58,7 @@ files from a [Nextcloud](https://nextcloud.com/) instance. - example `<dbp-file-source allowed-mime-types='image/png,text/plain'></dbp-file-source>` ... PNGs or TXTs only - example `<dbp-file-source allowed-mime-types='*/*'></dbp-file-source>` ... all file types (default) - `enabled-targets` (optional, default: `local`): sets which sources are enabled - - you can use `local` and `nextcloud` + - you can use `local`, `nextcloud` and `clipboard` - example `<dbp-file-source enabled-targets="local,nextcloud"></dbp-file-source>` - `disabled` (optional): disable input control - example `<dbp-file-source disabled></dbp-file-source>` @@ -115,7 +115,7 @@ This event is sent from nextcloudfilepicker and is send when files are picked an ## FileSink This web component is able to receive files and present as them as ZIP file download or upload -files to a [Nextcloud](https://nextcloud.com/) instance. +files to a [Nextcloud](https://nextcloud.com/) instance or to a dbp-clipboard. ### Usage @@ -128,7 +128,7 @@ files to a [Nextcloud](https://nextcloud.com/) instance. - `lang` (optional, default: `de`): set to `de` or `en` for German or English - example `<dbp-file-sink lang="de"></dbp-file-sink>` - `enabled-targets` (optional, default: `local`): sets which destination are enabled - - you can use `local` and `nextcloud` + - you can use `local`, `nextcloud` and `clipboard` - example `<dbp-file-sink enabled-targets="local,nextcloud"></dbp-file-sink>` - `filename` (optional, default: `files.zip`): sets a file name to use for downloading the zip file - example `<dbp-file-sink filename="signed-documents.zip"></dbp-file-sink>` @@ -167,6 +167,67 @@ The component emits a `dbp-set-property` event for the attribute `initial-file-h - `--dbp-override-image-nextcloud` is used to override the cloud image on the connection screen - example CSS: `html { --dbp-override-image-nextcloud: url(/icons/nextcloud.svg); }` +## Clipboard + +This web component is for a clipboard which saves the files to a provider. These files are available till the page is reload or the browser is closed. + +### Depencencies +This web component can only used if a `dbp-provider` is around it.<br> +This web component depends on: +- `dbp-file-sink` +- `dbp-file-source` + +### Usage + +```html + <dbp-clipboard subscribe="clipboard-files:clipboard-files-global-name"> + </dbp-clipboard> +``` + +### Attributes + +- `clipboardFiles` is the object which should be subscribed to the provider to recieve and send the clipboard files + to the provider. + - exmaple `<dbp-clipboard subscribe="clipboard-files:clipboard-files-global-name"></dbp-clipboard>` +- `lang` (optional, default: `de`): set to `de` or `en` for German or English + - example `<dbp-clipboard subscribe="clipboard-files:clipboard-files-global-name" lang="de"></dbp-clipboard>` +- `enabled-targets` (optional, default: `local`): sets which destination are enabled + - you can use `local` and `nextcloud` + - example `<dbp-clipboard subscribe="clipboard-files:clipboard-files-global-name" enabled-targets="local,nextcloud"></dbp-clipboard>` +- `allowed-mime-types` (optional): if set accepts only files matching mime types + - example `<dbp-clipboard subscribe="clipboard-files:clipboard-files-global-name" allowed-mime-types='application/pdf'></dbp-clipboard>` ... PDFs only + - example `<dbp-clipboard subscribe="clipboard-files:clipboard-files-global-name" allowed-mime-types='image/*'></dbp-clipboard>` ... images (of all sub types) only + - example `<dbp-clipboard subscribe="clipboard-files:clipboard-files-global-name"e allowed-mime-types='image/png,text/plain'></dbp-clipboard>` ... PNGs or TXTs only + - example `<dbp-clipboard subscribe="clipboard-files:clipboard-files-global-name" allowed-mime-types='*/*'></dbp-clipboard>` ... all file types (default) +- `mode` (optional, default: `MODE_TABLE_ONLY`): the clipboard can used in three different contexts: + only showing the clipboard content, in the file-source context and in the file-sink context. + - you can use `MODE_TABLE_ONLY`, `MODE_FILE_SINK` or `MODE_FILE_SOURCE` + - example `<dbp-clipboard mode="MODE_TABLE_ONLY></dbp-clipboard>` ... only the table will shown + - example `<dbp-clipboard mode="MODE_FILE_SINK></dbp-clipboard>` ... the file-sink text and functionality is turned on + - example `<dbp-clipboard mode="MODE_FILE_SOURCE></dbp-clipboard>` ... the file-source text and functionality is turned on +- `filesToSave` this attribute is used by the `dbp-file-sink` to set the files in an array, + which should be saved to the clipboard to the `dbp-clipboard` web component +- `allow-nesting` (optional): is an boolean for demo purposes or special use cases. + It availables the clipboard in the file-source and file-sink in the clipboard itself. + - example `<dbp-clipboard subscribe="clipboard-files:clipboard-files-global-name" allow-nesting></dbp-clipboard>` ... all file types (default) +- `nextcloud-auth-url` (optional): Nextcloud Auth Url to use with the Nextcloud file picker + - example `<dbp-clipboard subscribe="clipboard-files:clipboard-files-global-name" nextcloud-auth-url="http://localhost:8081/index.php/apps/webapppassword"></dbp-clipboard>` + - `nextcloud-web-dav-url` also needs to be set for the Nextcloud file picker to be active +- `nextcloud-web-dav-url` (optional): Nextcloud WebDav Url to use with the Nextcloud file picker + - example `<dbp-clipboard subscribe="clipboard-files:clipboard-files-global-name" nextcloud-web-dav-url="http://localhost:8081/remote.php/dav/files"></dbp-clipboard>` + - `nextcloud-auth-url` also needs to be set for the Nextcloud file picker to be active +- `nextcloud-file-url` (optional): Nextcloud File Url to use with the Nextcloud file picker + - example `<dbp-clipboard subscribe="clipboard-files:clipboard-files-global-name" nextcloud-file-url="http://localhost:8081/apps/files/?dir="></dbp-clipboard>` +- `nextcloud-auth-info` (optional): Additional authentication information text that is shown in the Nextcloud file picker + - example `<dbp-clipboard subscribe="clipboard-files:clipboard-files-global-name" nextcloud-auth-info="You need special permissions for this function"></dbp-clipboard>` + + +### Exposed CSS variables + +- `--dbp-override-image-nextcloud` is used to override the cloud image on the connection screen + - example CSS: `html { --dbp-override-image-nextcloud: url(/icons/nextcloud.svg); }` + + ## Local development ```bash diff --git a/packages/file-handling/src/clipboard.js b/packages/file-handling/src/clipboard.js index 4db1aed7..5b2665a4 100644 --- a/packages/file-handling/src/clipboard.js +++ b/packages/file-handling/src/clipboard.js @@ -34,8 +34,9 @@ export class Clipboard extends ScopedElementsMixin(AdapterLitElement) { this.nextcloudWebDavURL = ""; this.nextcloudName = ""; this.nextcloudFileURL = ""; + this.authInfo = ''; - this.demo = false; + this.allowNesting = false; // To avoid a cyclic dependency import('./file-sink').then(({ FileSink }) => this.defineScopedElement('dbp-file-sink', FileSink)); @@ -56,8 +57,8 @@ export class Clipboard extends ScopedElementsMixin(AdapterLitElement) { lang: { type: String }, allowedMimeTypes: { type: String, attribute: 'allowed-mime-types' }, clipboardSelectBtnDisabled: { type: Boolean, attribute: true }, - clipboardFiles: {type: Object, attribute: 'clipboard-files'}, - filesToSave: {type: Array, attribute: 'files-to-save'}, + clipboardFiles: {type: Object, attribute: 'clipboard-files' }, + filesToSave: {type: Array, attribute: 'files-to-save' }, numberOfSelectedFiles: {type: Number, attribute: false }, enabledTargets: {type: String, attribute: 'enabled-targets'}, @@ -65,10 +66,11 @@ export class Clipboard extends ScopedElementsMixin(AdapterLitElement) { nextcloudWebDavURL: { type: String, attribute: 'nextcloud-web-dav-url' }, nextcloudName: { type: String, attribute: 'nextcloud-name' }, nextcloudFileURL: { type: String, attribute: 'nextcloud-file-url' }, + nextcloudAuthInfo: {type: String, attribute: 'nextcloud-auth-info'}, mode: {type: String, attribute: 'mode'}, - demo: {type: Boolean, attribute: 'demo-clipboard' }, + allowNesting: {type: Boolean, attribute: 'allow-nesting' }, }; } @@ -206,7 +208,9 @@ export class Clipboard extends ScopedElementsMixin(AdapterLitElement) { that.generateClipboardTable(); }); - if(!window.clipboardWarning) { + + //Register only one beforeunload Event for the clipboard warning + if (!window.clipboardWarning) { window.addEventListener('beforeunload', this._onReceiveBeforeUnload, false); window.clipboardWarning = true; } @@ -218,7 +222,6 @@ export class Clipboard extends ScopedElementsMixin(AdapterLitElement) { //We doesn't want to deregister this event, because we want to use this event over activities //window.removeEventListener('beforeunload', this._onReceiveBeforeUnload); - super.disconnectedCallback(); } @@ -237,6 +240,12 @@ export class Clipboard extends ScopedElementsMixin(AdapterLitElement) { } } + /** + * Checks if all files are already selected + * Returns true if all files are selected + * + * @return Boolean + */ checkAllSelected() { if (this.tabulatorTable) { let maxSelected = this.tabulatorTable.getRows().filter(row => row.getData().type != 'directory' && this.checkFileType(row.getData(), this.allowedMimeTypes)).length; @@ -249,8 +258,13 @@ export class Clipboard extends ScopedElementsMixin(AdapterLitElement) { } + /** + * Check mime type of a file, returns true if this.allowedMimeTypes contains the mime type of the file + * + * @param file + * @return Boolean + */ checkFileType(file) { - // check if file is allowed const [fileMainType, fileSubType] = file.type.split('/'); const mimeTypes = this.allowedMimeTypes.split(','); @@ -268,11 +282,14 @@ export class Clipboard extends ScopedElementsMixin(AdapterLitElement) { return true; } + /** + * If clipboard files and the tabulator table exists, then clear the table and sets the new data + * + */ generateClipboardTable() { - if (this.clipboardFiles.files) { let data = []; - for (let i = 0; i < this.clipboardFiles.files.length; i++){ + for (let i = 0; i < this.clipboardFiles.files.length; i++) { data[i] = { name: this.clipboardFiles.files[i].name, size: this.clipboardFiles.files[i].size, @@ -282,17 +299,20 @@ export class Clipboard extends ScopedElementsMixin(AdapterLitElement) { }; } - if (this.tabulatorTable !== null){ + if (this.tabulatorTable !== null) { this.tabulatorTable.clearData(); this.tabulatorTable.setData(data); } } } + /** + * Sends the files to a provider and throws a notification + * + * @param files + */ async sendClipboardFiles(files) { - - for(let i = 0; i < files.length; i ++) - { + for (let i = 0; i < files.length; i ++) { await this.sendFileEvent(files[i].file); } this.tabulatorTable.deselectRow(); @@ -305,13 +325,10 @@ export class Clipboard extends ScopedElementsMixin(AdapterLitElement) { } async sendFileEvent(file) { - const data = {"file": file, "data": file}; - const event = new CustomEvent("dbp-clipboard-file-picker-file-downloaded", { "detail": data, bubbles: true, composed: true }); this.dispatchEvent(event); - } @@ -320,7 +337,7 @@ export class Clipboard extends ScopedElementsMixin(AdapterLitElement) { * * @param event */ - onReceiveBeforeUnload(event){ + onReceiveBeforeUnload(event) { // we don't need to stop if there are no signed files if (this.clipboardFiles.files.length === 0) { @@ -328,8 +345,7 @@ export class Clipboard extends ScopedElementsMixin(AdapterLitElement) { } // we need to handle custom events ourselves - if(event.target && event.target.activeElement && event.target.activeElement.nodeName) { - + if (event.target && event.target.activeElement && event.target.activeElement.nodeName) { send({ "summary": i18n.t('clipboard.file-warning'), "body": i18n.t('clipboard.file-warning-body', {count: this.clipboardFiles.files.length}), @@ -352,16 +368,21 @@ export class Clipboard extends ScopedElementsMixin(AdapterLitElement) { } } - saveFilesToClipboardEvent(ev) + /** + * Saves files from an event to the clipboard + * + * @param event + */ + saveFilesToClipboardEvent(event) { //save it let data = {}; let files = []; if (this.clipboardFiles && this.clipboardFiles.files.length !== 0) { files = files.concat(this.clipboardFiles.files); - files = files.concat(ev.detail.file); + files = files.concat(event.detail.file); } else { - files = files.concat(ev.detail.file); + files = files.concat(event.detail.file); } this.filesToSave = files; if (files && files.length !== 0) { @@ -373,6 +394,10 @@ export class Clipboard extends ScopedElementsMixin(AdapterLitElement) { } } + /** + * Saves all files from this.filesToSave in clipboard and throws a notification + * + */ saveFilesToClipboard() { //save it @@ -399,17 +424,25 @@ export class Clipboard extends ScopedElementsMixin(AdapterLitElement) { } } - - finishedSaveFilesToClipboard(ev) { + /** + * Throws a finish notification with the count from the event.detail + * + * @param event + */ + finishedSaveFilesToClipboard(event) { send({ - "summary": i18n.t('clipboard.saved-files-title', {count: ev.detail.count}), - "body": i18n.t('clipboard.saved-files-body', {count: ev.detail.count}), + "summary": i18n.t('clipboard.saved-files-title', {count: event.detail.count}), + "body": i18n.t('clipboard.saved-files-body', {count: event.detail.count}), "type": "success", "timeout": 5, }); } - saveFilesFromClipboard() { + /** + * Open the file sink with clipboardfiles + * + */ + openFileSink() { const fileSink = this._("#file-sink-clipboard"); if ( fileSink ) { this._("#file-sink-clipboard").files = Object.create(this.tabulatorTable.getSelectedData().length > 0 ? this.tabulatorTable.getSelectedData() : this.clipboardFiles.files); @@ -417,26 +450,21 @@ export class Clipboard extends ScopedElementsMixin(AdapterLitElement) { } } - getClipboardFileList() { - let files = []; - for (let i = 0; i < this.clipboardFiles.files.length; i ++) - { - files[i] = html`<div class="clipboard-list"><strong>${this.clipboardFiles.files[i].name}</strong> ${humanFileSize(this.clipboardFiles.files[i].size)}</div>`; - } - return files; - } - /** - * Open Filesink for multiple files + * Open the file source with clipboardfiles + * */ - async openClipboardFileSink() { - const fileSink = this._("#file-sink-clipboard"); - if (fileSink) { - this._("#file-sink-clipboard").files = Object.create(this.clipboardFiles.files); - this._("#file-sink-clipboard").openDialog(); + openFileSource() { + const fileSource = this._("#file-source-clipboard"); + if (fileSource) { + this._("#file-source-clipboard").openDialog(); } } + /** + * Delete all or only selected files from clipboard and throws a notification + * + */ clearClipboard() { if (this.tabulatorTable && this.tabulatorTable.getSelectedData().length > 0) { let count = this.tabulatorTable.getSelectedData().length; @@ -465,18 +493,18 @@ export class Clipboard extends ScopedElementsMixin(AdapterLitElement) { } } - openFilesource() { - const fileSource = this._("#file-source-clipboard"); - if (fileSource) { - this._("#file-source-clipboard").openDialog(); - } - } - + /** + * Get the additional clipboard buttons + * If this.mode === MODE_FILE_SINK or MODE_FILE_SOURCE then there are only delete and save files buttons available + * Else there are the add, delete and save files buttons available + * + * @return html + */ getAdditionalButtons() { return html` <div class="flex-container additional-button-container"> <div class="btn-flex-container-mobile"> - <button @click="${() => { this.openFilesource(); }}" + <button @click="${() => { this.openFileSource(); }}" class="button ${classMap({hidden: this.mode === MODE_FILE_SINK || this.mode === MODE_FILE_SOURCE})}" title="${i18n.t('clipboard.add-files')}"> <dbp-icon class="nav-icon" name="clipboard"></dbp-icon> ${i18n.t('clipboard.add-files-btn')} </button> @@ -487,7 +515,7 @@ export class Clipboard extends ScopedElementsMixin(AdapterLitElement) { </button> </div> <div class="btn-flex-container-mobile"> - <button @click="${() => { this.saveFilesFromClipboard(); }}" + <button @click="${() => { this.openFileSink(); }}" ?disabled="${this.clipboardFiles.files.length === 0}" class="button" title="${(this.numberOfSelectedFiles > 0) ? i18n.t('clipboard.save-count', {count: this.numberOfSelectedFiles}) : i18n.t('clipboard.save-all')}"> ${(this.numberOfSelectedFiles > 0) ? i18n.t('clipboard.save-count-btn', {count: this.numberOfSelectedFiles}) : i18n.t('clipboard.save-all-btn')} @@ -503,7 +531,8 @@ export class Clipboard extends ScopedElementsMixin(AdapterLitElement) { nextcloud-web-dav-url="${this.nextcloudWebDavURL}" nextcloud-name="${this.nextcloudName}" nextcloud-file-url="${this.nextcloudFileURL}" - enabled-targets="${this.demo ? this.enabledTargets : this.enabledTargets.replace('clipboard', '')}" + nexcloud-auth-info="${this.nextcloudAuthInfo}" + enabled-targets="${this.allowNesting ? this.enabledTargets : this.enabledTargets.replace('clipboard', '')}" decompress-zip lang="${this.lang}" text="${i18n.t('clipboard.upload-area-text')}" @@ -512,22 +541,28 @@ export class Clipboard extends ScopedElementsMixin(AdapterLitElement) { @dbp-file-source-file-selected="${this.saveFilesToClipboardEvent}" @dbp-nextcloud-file-picker-number-files="${this.finishedSaveFilesToClipboard}" @dbp-file-source-file-upload-finished="${this.finishedSaveFilesToClipboard}" - ></dbp-file-source> + ></dbp-file-source> <dbp-file-sink id="file-sink-clipboard" context="${(this.numberOfSelectedFiles > 0) ? i18n.t('clipboard.save-count', {count: this.numberOfSelectedFiles}) : i18n.t('clipboard.save-all')}" filename="clipboard-documents.zip" allowed-mime-types="${this.allowedMimeTypes}" - enabled-targets="${this.demo ? this.enabledTargets : this.enabledTargets.replace('clipboard', '')}" + enabled-targets="${this.allowNesting ? this.enabledTargets : this.enabledTargets.replace('clipboard', '')}" show-clipboard nextcloud-auth-url="${this.nextcloudWebAppPasswordURL}" nextcloud-web-dav-url="${this.nextcloudWebDavURL}" nextcloud-name="${this.nextcloudName}" nextcloud-file-url="${this.nextcloudFileURL}" + nexcloud-auth-info="${this.nextcloudAuthInfo}" lang="${this.lang}" ></dbp-file-sink> `; } + /** + * Get the clipboard sink html + * + * @return html + */ getClipboardSink() { const tabulatorCss = commonUtils.getAssetURL(pkgName, 'tabulator-tables/css/tabulator.min.css'); return html` @@ -560,7 +595,11 @@ export class Clipboard extends ScopedElementsMixin(AdapterLitElement) { } - + /** + * Get the clipboard source html + * + * @return html + */ getClipboardSource() { const tabulatorCss = commonUtils.getAssetURL(pkgName, 'tabulator-tables/css/tabulator.min.css'); return html` @@ -622,7 +661,6 @@ export class Clipboard extends ScopedElementsMixin(AdapterLitElement) { font-style: italic; padding-left: 2em; margin-top: -1px; - /*line-height: 1.8;*/ margin-bottom: 1.2em; } @@ -789,30 +827,27 @@ export class Clipboard extends ScopedElementsMixin(AdapterLitElement) { render() { const tabulatorCss = commonUtils.getAssetURL(pkgName, 'tabulator-tables/css/tabulator.min.css'); - if (this.mode === MODE_FILE_SINK) - { + if (this.mode === MODE_FILE_SINK) { return this.getClipboardSink(); } - if (this.mode === MODE_FILE_SOURCE) - { + else if (this.mode === MODE_FILE_SOURCE) { return this.getClipboardSource(); + } else { + return html` + <div> + + ${this.getAdditionalButtons()} + <link rel="stylesheet" href="${tabulatorCss}"> + <div class="table-wrapper"> + <label class="button-container select-all-icon"> + <input type="checkbox" id="select_all" name="select_all" value="select_all" @click="${() => {this.selectAllFiles();}}"> + <span class="checkmark" title="${this.checkAllSelected() ? i18n.t("clipboard.select-nothing") : i18n.t("clipboard.select-all")}"></span> + </label> + <table id="clipboard-content-table" class="force-no-select"></table> + </div> + </div> + ` ; } - return html` - <div> - - ${this.getAdditionalButtons()} - - <link rel="stylesheet" href="${tabulatorCss}"> - - <div class="table-wrapper"> - <label class="button-container select-all-icon"> - <input type="checkbox" id="select_all" name="select_all" value="select_all" @click="${() => {this.selectAllFiles();}}"> - <span class="checkmark" title="${this.checkAllSelected() ? i18n.t("clipboard.select-nothing") : i18n.t("clipboard.select-all")}"></span> - </label> - <table id="clipboard-content-table" class="force-no-select"></table> - </div> - </div> - `; } } \ No newline at end of file -- GitLab