File

src/app/modules/playground/playground.component.ts

Implements

AfterViewInit

Metadata

Index

Properties
Methods

Constructor

constructor(store: Store, ga: GoogleAnalyticsService)
Parameters :
Name Type Optional
store Store No
ga GoogleAnalyticsService No

Methods

checkLinkFormat
checkLinkFormat(url: string)

Link validation function

Parameters :
Name Type Optional
url string No
Returns : { sheetID: any; gid: any; csvUrl: string; }
generateColumns
generateColumns(len: number)

Add colums

Parameters :
Name Type Optional
len number No
Returns : jexcel.Column[]
initTable
initTable(data: string[][])

Initialize jexcel table

Parameters :
Name Type Optional Description
data string[][] No

table data

Returns : void
tabChange
tabChange(tab: MatTabChangeEvent)

Change tabs

Parameters :
Name Type Optional Description
tab MatTabChangeEvent No

table change event

Returns : void
upload
upload(data: UploadForm)

Read the google sheet link and upload

Parameters :
Name Type Optional
data UploadForm No
Returns : void

Properties

currentSheet
Type : Sheet

Selected sheet

data$
Type : Observable<[][]>
Decorators :
@Select(SheetState.getParsedData)
Public Readonly ga
Type : GoogleAnalyticsService
linkFormControl
Default value : new UntypedFormControl('', [ Validators.compose([ Validators.required, Validators.pattern(/\/([\w-_]{15,})\/(.*?gid=(\d+))?|\w*csv$/), ]) as ValidatorFn, ])

Controller for entering the link

prevTab
Type : number
Default value : 0

Keep track of previous tab. Default to 0

sheet$
Type : Observable<Sheet>
Decorators :
@Select(SheetState.getSheet)
spreadsheet
Type : ElementRef
Decorators :
@ViewChild('spreadsheet')
spreadSheetData
Type : string[][]
Default value : []

Data for the table view

Public Readonly store
Type : Store
tabIndex
Type : number

Keeps track of the tab index

table
Type : jexcel.JspreadsheetInstance

Instance of jspreadsheet-ce(Earlier: jexcel) table

import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core';
import { UntypedFormControl, ValidatorFn, Validators } from '@angular/forms';
import { MatTabChangeEvent } from '@angular/material/tabs';
import { Select, Store } from '@ngxs/store';
import * as jexcel from 'jspreadsheet-ce';
import { GoogleAnalyticsService } from 'ngx-google-analytics';
import { Observable } from 'rxjs';
import { FetchSheetData, UpdatePlaygroundData } from '../../actions/sheet.actions';
import { GaAction, GaCategory } from '../../models/ga.model';
import { Sheet, UploadForm } from '../../models/sheet.model';
import { SheetState } from '../../store/sheet.state';

@Component({
  selector: 'app-playground',
  templateUrl: './playground.component.html',
  styleUrls: ['./playground.component.scss'],
})
export class PlaygroundComponent implements AfterViewInit {
  @ViewChild('spreadsheet') spreadsheet!: ElementRef;

  @Select(SheetState.getParsedData) data$!: Observable<string[][]>;
  @Select(SheetState.getSheet) sheet$!: Observable<Sheet>;

  /**
   * Data for the table view
   */
  spreadSheetData: string[][] = [];
  /**
   * Instance of jspreadsheet-ce(Earlier: jexcel) table
   */
  table!: jexcel.JspreadsheetInstance;
  /**
   * Keep track of previous tab. Default to 0
   */
  prevTab = 0;
  /**
   * Selected sheet
   */
  currentSheet!: Sheet;
  /**
   * Keeps track of the tab index
   */
  tabIndex!: number;

  /**
   * Controller for entering the link
   */
  linkFormControl = new UntypedFormControl('', [
    Validators.compose([
      Validators.required,
      Validators.pattern(/\/([\w-_]{15,})\/(.*?gid=(\d+))?|\w*csv$/),
    ]) as ValidatorFn,
  ]);

  constructor(
    public readonly store: Store,
    public readonly ga: GoogleAnalyticsService,
  ) {
    this.sheet$.subscribe((sheet) => {
      this.currentSheet = sheet;
    });
  }

  ngAfterViewInit() {
    this.data$.subscribe((data) => {
      if (data.length) {
        this.spreadSheetData = data;
        if (!this.table) {
          this.initTable(data);
        } else {
          this.table.destroy();
          this.initTable(data);
        }
      }
    });
  }

  /**
   * Add colums
   */
  generateColumns(len: number): jexcel.Column[] {
    const columns = [];
    for (let i = 0; i < len; i++) {
      columns.push({
        type: 'text' as const,
        width: 125,
      });
    }
    return columns;
  }

  /**
   * Initialize jexcel table
   *
   * @param data table data
   */
  initTable(data: string[][]) {
    this.table = jexcel(this.spreadsheet.nativeElement, {
      data,
      minDimensions: [50, 50],
      onchange: () => {
        this.spreadSheetData = data;
      },
      contextMenu(obj, x, y) {
        const items = [];

        if (y === null) {
          // Insert a new column
          if (obj.options.allowInsertColumn === true) {
            items.push({
              title: obj.options.text?.insertANewColumnBefore,
              onclick() {
                obj.insertColumn(1, parseInt(x ?? '', 10), true);
              },
            });
          }

          if (obj.options.allowInsertColumn === true) {
            items.push({
              title: obj.options.text?.insertANewColumnAfter,
              onclick() {
                obj.insertColumn(1, parseInt(x ?? '', 10), false);
              },
            });
          }

          // Delete a column
          if (obj.options.allowDeleteColumn === true) {
            items.push({
              title: obj.options.text?.deleteSelectedColumns,
              onclick() {
                obj.deleteColumn(obj.getSelectedColumns().length ? undefined : parseInt(x ?? '', 10));
              },
            });
          }

          // Rename column
          if (obj.options.allowRenameColumn === true) {
            items.push({
              title: obj.options.text?.renameThisColumn,
              onclick() {
                obj.setHeader(parseInt(x ?? '', 10));
              },
            });
          }

          // Sorting
          if (obj.options.columnSorting === true) {
            // Line
            items.push({ type: 'line' });

            items.push({
              title: obj.options.text?.orderAscending,
              onclick() {
                obj.orderBy(parseInt(x ?? '', 10), 0);
              },
            });
            items.push({
              title: obj.options.text?.orderDescending,
              onclick() {
                obj.orderBy(parseInt(x ?? '', 10), 1);
              },
            });
          }
        } else {
          // Insert new row
          if (obj.options.allowInsertRow === true) {
            items.push({
              title: obj.options.text?.insertANewRowBefore,
              onclick() {
                obj.insertRow(1, parseInt(y, 10), 1);
              },
            });

            items.push({
              title: obj.options.text?.insertANewRowAfter,
              onclick() {
                obj.insertRow(1, parseInt(y, 10));
              },
            });
          }

          if (obj.options.allowDeleteRow === true) {
            items.push({
              title: obj.options.text?.deleteSelectedRows,
              onclick() {
                obj.deleteRow(obj.getSelectedRows().length ? undefined : parseInt(y, 10));
              },
            });
          }

          if (x) {
            if (obj.options.allowComments === true) {
              items.push({ type: 'line' });

              const title = obj.records[+y][+x].getAttribute('title') || '';

              items.push({
                title: title ? obj.options.text?.editComments : obj.options.text?.addComments,
                onclick() {
                  obj.setComments(
                    [parseInt(x, 10), parseInt(y, 10)],
                    prompt(obj.options.text?.comments, title) ?? '',
                    '',
                  );
                },
              });

              if (title) {
                items.push({
                  title: obj.options.text?.clearComments,
                  onclick() {
                    obj.setComments([parseInt(x, 10), parseInt(y, 10)], '', '');
                  },
                });
              }
            }
          }
        }

        // Line
        items.push({ type: 'line' });

        // Save
        if (obj.options.allowExport) {
          items.push({
            title: obj.options.text?.saveAs,
            shortcut: 'Ctrl + S',
            onclick() {
              obj.download(true);
            },
          });
        }

        return items;
      },
      columns: [...this.generateColumns(data[0].length)],
      style: {
        A1: 'width: 100px;',
      },
    });
  }

  /**
   * Change tabs
   *
   * @param tab table change event
   */
  tabChange(tab: MatTabChangeEvent) {
    if (this.prevTab === 1 && tab.index === 0) {
      this.spreadSheetData = this.spreadSheetData.filter((row) => {
        return row.some((cell) => cell.length > 0 && cell !== '\u0000');
      });
      this.store.dispatch(new UpdatePlaygroundData(this.spreadSheetData));
    }
    this.prevTab = tab.index;
    this.ga.event(GaAction.NAV, GaCategory.PLAYGROUND, 'Change playground tab', tab.index);
  }

  /**
   * Read the google sheet link and upload
   */
  upload(data: UploadForm) {
    const sheet = JSON.parse(JSON.stringify(this.currentSheet));
    sheet.gid = data.gid;
    sheet.sheetId = data.sheetId;
    sheet.csvUrl = data.csvUrl;
    this.tabIndex = 0;
    sheet.config.height = 1400;
    if (data.formData) {
      sheet.formData = data.formData;
    }
    this.store.dispatch(new FetchSheetData(sheet));
    this.ga.event(GaAction.CLICK, GaCategory.PLAYGROUND, 'Upload Playground Sheet', sheet.sheetId);
  }

  /**
   * Link validation function
   */
  checkLinkFormat(url: string) {
    if (url.startsWith('https://docs.google.com/spreadsheets/d/')) {
      const splitUrl = url.split('/');
      if (splitUrl.length === 7) {
        return {
          sheetID: splitUrl[5],
          gid: splitUrl[6].split('=')[1],
          csvUrl: '',
        };
      }
    }

    return {
      sheetID: '0',
      gid: '0',
      csvUrl: url,
    };
  }
}
<mat-tab-group
  (selectedTabChange)="tabChange($event)"
  [mat-stretch-tabs]="false"
  class="remove-border-bottom"
  [(selectedIndex)]="tabIndex"
>
  <mat-tab label="Visualization">
    <app-tree></app-tree>
  </mat-tab>
  <mat-tab label="Table">
    <div #spreadsheet></div>
  </mat-tab>
  <mat-tab label="Upload">
    <app-upload class="upload" (uploadForm)="upload($event)"></app-upload>
  </mat-tab>
</mat-tab-group>

./playground.component.scss

.jexcel_about {
  visibility: none !important;
}

::ng-deep .jexcel tbody tr:nth-child(11) {
  font-weight: bold;
}

.remove-border-bottom ::ng-deep .mat-tab-header {
  // border-bottom: none;
  border-bottom: 0.1875rem solid #444a6510;
}

::ng-deep .mat-tab-labels {
  background-color: white !important;
  border: 0rem !important;
}

::ng-deep .mat-mdc-tab.mdc-tab {
  padding: 0rem 3.75rem;
}

::ng-deep .jexcel > tbody > tr > td:nth-child(2) {
  border: 0.0625rem solid #e3e3e3;
  overflow: visible;
}

::ng-deep .jexcel > thead > tr > td {
  border: 0.0625rem solid #e3e3e3;
  overflow: visible;
}

.upload {
  margin-top: 1rem;
  display: block;
  width: 50rem;
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""