LP1958581 Angular Grid Copy To Clipboard
authorBill Erickson <berickxx@gmail.com>
Thu, 20 Jan 2022 21:16:10 +0000 (16:16 -0500)
committerMike Rylander <mrylander@gmail.com>
Fri, 25 Mar 2022 20:30:57 +0000 (16:30 -0400)
To test, right click on a row in an Angular grid.  A dialog should
appear which allows the user to click on a value to select the value
into the clipboard.  Once selected, the dialog should close.

Signed-off-by: Bill Erickson <berickxx@gmail.com>
Signed-off-by: Mike Rylander <mrylander@gmail.com>

Open-ILS/src/eg2/src/app/share/clipboard/clipboard-dialog.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/share/clipboard/clipboard-dialog.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/share/common-widgets.module.ts
Open-ILS/src/eg2/src/app/share/grid/grid-body.component.html
Open-ILS/src/eg2/src/app/share/grid/grid-toolbar-actions-menu.component.html
Open-ILS/src/eg2/src/app/share/grid/grid-toolbar-actions-menu.component.ts

diff --git a/Open-ILS/src/eg2/src/app/share/clipboard/clipboard-dialog.component.html b/Open-ILS/src/eg2/src/app/share/clipboard/clipboard-dialog.component.html
new file mode 100644 (file)
index 0000000..1ff5fa2
--- /dev/null
@@ -0,0 +1,25 @@
+<ng-template #dialogContent>
+  <div class="modal-header bg-info">
+    <h4 class="modal-title" i18n>Copy Data to Clipboard</h4>
+    <button type="button" class="close" 
+      i18n-aria-label aria-label="Close" (click)="close()">
+      <span aria-hidden="true">&times;</span>
+    </button>
+  </div>
+  <div class="modal-body common-form striped-odd">
+    <div class="row" *ngFor="let value of values">
+      <div class="col-lg-4 font-weight-bold border-right">{{value.label}}</div>
+      <div class="col-lg-8">
+        <a href="javascript:;" (click)="copyValue(value.value)">{{value.value}}</a>
+      </div>
+    </div>
+
+    <!-- hidden landing spot for copy-able text -->
+    <textarea style="display:none;visibility:hidden" id='clipboard-textarea'>
+    </textarea>
+  </div>
+  <div class="modal-footer">
+    <button type="button" class="btn btn-warning" 
+      (click)="close()" i18n>Cancel</button>
+  </div>
+</ng-template>
diff --git a/Open-ILS/src/eg2/src/app/share/clipboard/clipboard-dialog.component.ts b/Open-ILS/src/eg2/src/app/share/clipboard/clipboard-dialog.component.ts
new file mode 100644 (file)
index 0000000..46e0141
--- /dev/null
@@ -0,0 +1,43 @@
+import {Component, Input, ViewChild, TemplateRef} from '@angular/core';
+import {DialogComponent} from '@eg/share/dialog/dialog.component';
+
+interface ClipboardValues {
+    label: string;
+    value: string;
+}
+
+@Component({
+  selector: 'eg-clipboard-dialog',
+  templateUrl: './clipboard-dialog.component.html'
+})
+
+/**
+ * Copy To Clipboard dialog
+ */
+export class ClipboardDialogComponent extends DialogComponent {
+
+    @Input() values: ClipboardValues[];
+
+    copyValue(value: string) {
+
+        const node =
+            document.getElementById('clipboard-textarea') as HTMLTextAreaElement;
+
+        // Un-hide the textarea just long enough to copy its data.
+        // Using node.style instead of *ngIf for snappier show/hide.
+        node.style.visibility = 'visible';
+        node.style.display = 'block';
+        node.value = value;
+        node.focus();
+        node.select();
+
+        document.execCommand('copy');
+
+        node.style.visibility = 'hidden';
+        node.style.display = 'none';
+
+        this.close();
+    }
+}
+
+
index fda671e..49fdc7b 100644 (file)
@@ -17,6 +17,7 @@ import {DateTimeSelectComponent} from '@eg/share/datetime-select/datetime-select
 import {ContextMenuModule} from '@eg/share/context-menu/context-menu.module';
 import {FileReaderComponent} from '@eg/share/file-reader/file-reader.component';
 import {IntervalInputComponent} from '@eg/share/interval-input/interval-input.component';
+import {ClipboardDialogComponent} from '@eg/share/clipboard/clipboard-dialog.component';
 
 
 @NgModule({
@@ -28,6 +29,7 @@ import {IntervalInputComponent} from '@eg/share/interval-input/interval-input.co
     DateRangeSelectComponent,
     DateTimeSelectComponent,
     FileReaderComponent,
+    ClipboardDialogComponent,
     IdlClassTemplateDirective,
     IntervalInputComponent,
   ],
@@ -50,6 +52,7 @@ import {IntervalInputComponent} from '@eg/share/interval-input/interval-input.co
     OrgSelectComponent,
     DateRangeSelectComponent,
     DateTimeSelectComponent,
+    ClipboardDialogComponent,
     ContextMenuModule,
     FileReaderComponent,
     IntervalInputComponent,
index fed2276..8cf795f 100644 (file)
@@ -1,6 +1,6 @@
 <!-- uses dropdown menu CSS for easy stying, but it's not a dropdown -->
 <ng-template #contextMenu let-gridContext="gridContext">
-  <eg-grid-toolbar-actions-menu [gridContext]="gridContext">
+  <eg-grid-toolbar-actions-menu [gridContext]="gridContext" [viaContextMenu]="true">
   </eg-grid-toolbar-actions-menu>
 </ng-template>
 
index 97db338..8bc8e32 100644 (file)
@@ -1,3 +1,13 @@
+<!-- Copy To Clipboard is only displayed when using a row-specific
+     context menu as the entry point. -->
+<button *ngIf="viaContextMenu" class="dropdown-item scrollable-menu"
+  (click)="openCopyToClipboard()" tabindex="0">
+  <div i18n>Copy to Clipboard</div>
+  <div class="dropdown-divider"></div>
+</button>
+
+<eg-clipboard-dialog #clipboardDialog></eg-clipboard-dialog>
+
 <ng-container 
   *ngFor="let action of gridContext.toolbarActions; let idx = index">
   <button class="dropdown-item scrollable-menu" *ngIf="!action.hidden"
index 2d8cffd..87f4c2c 100644 (file)
@@ -1,5 +1,6 @@
-import {Component, Input, OnInit, Host} from '@angular/core';
+import {Component, Input, OnInit, Host, ViewChild} from '@angular/core';
 import {GridToolbarAction, GridContext} from '@eg/share/grid/grid';
+import {ClipboardDialogComponent} from '@eg/share/clipboard/clipboard-dialog.component';
 
 /** Models a list of toolbar action menu entries */
 
@@ -12,6 +13,10 @@ export class GridToolbarActionsMenuComponent {
 
     @Input() gridContext: GridContext;
 
+    @Input() viaContextMenu = false;
+
+    @ViewChild('clipboardDialog') clipboardDialog: ClipboardDialogComponent;
+
     performAction(action: GridToolbarAction) {
         if (action.isGroup || action.isSeparator) {
             return; // These don't perform actions
@@ -27,5 +32,21 @@ export class GridToolbarActionsMenuComponent {
         }
         return false;
     }
+
+    openCopyToClipboard() {
+        const row = this.gridContext.getSelectedRows()[0];
+        if (!row) { return; }
+        const values = [];
+        this.gridContext.columnSet.displayColumns().forEach(col => {
+            values.push({
+                label: col.label,
+                value: this.gridContext.getRowColumnValue(row, col)
+            });
+        });
+
+
+        this.clipboardDialog.values = values;
+        this.clipboardDialog.open({size: 'lg'}).toPromise();
+    }
 }