File

src/app/spreadsheet/spreadsheet-components/components/spreadsheet/ui/spreadsheet-ui.component.ts

Metadata

changeDetection ChangeDetectionStrategy.OnPush
selector spreadsheet-ui
styleUrls ./spreadsheet-ui.component.scss
templateUrl ./spreadsheet-ui.component.html

Index

Properties
Methods
Inputs
Outputs

Inputs

activeCellFormGroup
Type : FormGroup
activeCellIndex
Type : number
activeCellPosition
Type : CellPosition
activeCellValue
Type : CellValue
cells
Type : Cell[]
columns
Type : number
rows
Type : number

Outputs

addColumnClicked
Type : EventEmitter<void>
addRowClicked
Type : EventEmitter<void>
cellClicked
Type : EventEmitter<number>

Methods

cellClick
cellClick(index: number)
Parameters :
Name Type Optional
index number No
Returns : void
counter
counter(number: number | string)
Parameters :
Name Type Optional
number number | string No
Returns : any[]
getCellValue
getCellValue(index: number)
Parameters :
Name Type Optional
index number No
Returns : CellValue
isCellActive
isCellActive(cellIndex: number)
Parameters :
Name Type Optional
cellIndex number No
Returns : boolean

Properties

activeCellInput
Type : ElementRef
Decorators :
@ViewChild('active_cell_input')
getColumnLabel
Default value : getColumnLabel
Parameters :
Name
index
import { Component, Input, ChangeDetectionStrategy, EventEmitter, Output, ViewChild, ElementRef } from "@angular/core"
import { FormGroup } from '@angular/forms'

import { Cell, CellValue, CellPosition } from '../../../../spreadsheet-data/models/cell.interfaces'
import { getCellPositionByIndex, getColumnLabel } from '../../../../spreadsheet-data/helpers/cell.helpers'

@Component({
  selector: 'spreadsheet-ui',
  templateUrl: './spreadsheet-ui.component.html',
  styleUrls: ['./spreadsheet-ui.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class SpreadsheetUiComponent {
  // Form
  @ViewChild('active_cell_input') activeCellInput: ElementRef;

  // Grid
  @Input() rows: number
  @Input() columns: number

  // Active
  @Input() activeCellIndex: number
  @Input() activeCellPosition: CellPosition
  @Input() activeCellValue: CellValue
  @Input() activeCellFormGroup: FormGroup

  //
  @Output() cellClicked: EventEmitter<number> = new EventEmitter<number>()
  @Output() addRowClicked: EventEmitter<void> = new EventEmitter<void>()
  @Output() addColumnClicked: EventEmitter<void> = new EventEmitter<void>()

  /**
   * @description    pre-sorted data so on render DOM, the lookup calls are O(1), instead of O(n^2)
   */
  @Input() cells: Cell[]

  /**
   * @description    Helper method to generate an array from a number/string representing the length of the array
   * @param number 
   */
  counter(number: number|string): any[] {
    if (typeof number === 'number') {
      return Array(number)
    }
    return Array(parseInt(number))
  }

  /**
   * 
   * @param index 
   */
  cellClick(index: number) {
    this.cellClicked.emit(index)
    setTimeout(() => this.activeCellInput.nativeElement.focus(), 0)
  }

  /**
   * @description   Convert Index number to Column Label ie A, AA, CT, etc
   * @param index 
   */
  getColumnLabel = getColumnLabel

  /**
   * @description   Gets the value of the cell based on the cell index (it wraps like regular text, in the grid)
   * @param index 
   */
  getCellValue(index: number): CellValue {
    if (this.cells.length === 0) {
      return ''
    }

    const cell = this.cells.filter(cell => {
      const indexCellPosition = getCellPositionByIndex(this.rows, this.columns, index)

      return (cell.position.column === indexCellPosition.column && 
          cell.position.row === indexCellPosition.row)
    })

    return cell[0] && cell[0].value ? cell[0].value : ''
  }

  /**
   * 
   * @param rows 
   * @param columns 
   * @param cellIndex 
   */
  isCellActive(cellIndex: number): boolean {
    const cellPosition = getCellPositionByIndex(this.rows, this.columns, cellIndex)

    return (cellPosition.column === this.activeCellPosition.column &&
            cellPosition.row === this.activeCellPosition.row)
  }
}
<section class="container"
  gdAreas="nav | spreadsheet"
  gdRows="2rem calc(100vh - 64px - 2rem)">

    <section gdArea="nav" 
        gdAreas="add_row label input add_column"
        gdColumns="2rem 2rem auto 3rem">
        
        <div gdArea="add_row">
            <button (click)="addRowClicked.emit()" class="tool" title="Add Row">
                <svg viewBox="0 0 360.61 431.32" class="rotate-clockwise-180">
                    <g id="g3484">
                        <path id="path3486" style="fill: #000000;" d="m0.213 180.2l180.2-180.2 180.2 180.2v118.85l-137.07-137.06v269.33h-87.22v-268.38l-136.32 136.32 0.213-119.06z"/>
                    </g>
                </svg>
            </button>
        </div>
        <div gdArea="label">
            {{ getColumnLabel(activeCellPosition.column) }}{{ activeCellPosition.row }}:
        </div>
        <div gdArea="input">
            <form [formGroup]="activeCellFormGroup">
                <input type="text" formControlName="active_cell" #active_cell_input autofocus id="active-cell-editor" />
                <label for="active-cell-editor" class="hide-element">Edit the value of the spreadsheet's active square</label>
            </form>
        </div>
        <div gdArea="add_column">
            <button (click)="addColumnClicked.emit()" class="tool" title="Add Column">
                <svg viewBox="0 0 360.61 431.32" class="rotate-clockwise-90">
                    <g id="g3484">
                        <path id="path3486" style="fill: #000000;" d="m0.213 180.2l180.2-180.2 180.2 180.2v118.85l-137.07-137.06v269.33h-87.22v-268.38l-136.32 136.32 0.213-119.06z"/>
                    </g>
                </svg>
            </button>
        </div>
        
    </section>

    <section 
        gdArea="spreadsheet" 
        gdAreas="filler column_labels | row_labels sheet"
        gdRows="1.5rem auto"
        gdColumns="2.75rem auto">

        <div gdArea="filler"></div>

        <section 
            gdArea="column_labels"
            gdColumns="repeat({{columns}}, 6rem)">
            <div *ngFor="let column of counter(columns); let i = index">
                {{ getColumnLabel(i + 1) }}
            </div>
        </section>

        <section 
            gdArea="row_labels"
            gdRows="repeat({{rows}}, 1.5rem)">
            <div *ngFor="let row of counter(rows); let i = index">
                {{ i + 1 }}
            </div>
        </section>

        <section 
            gdArea="sheet"
            gdRows="repeat({{rows}}, 1.5rem)"
            gdColumns="repeat({{columns}}, 6rem)">
            <div 
                *ngFor="let cell of counter(rows * columns); let i = index" 
                [class.active]="isCellActive(i)"
                (click)="cellClick(i)">
                {{ getCellValue(i) | expression | async | conditionalNumber }}
            </div>
        </section>

    </section>
</section>

./spreadsheet-ui.component.scss

@import './../../../../../../styles/helpers/transforms';
@import './../../../../../../styles/accessibility';

$blue: rgb(26, 115, 232);
$light_blue: rgba(26, 115, 232, .2);

$gray: #F8F9FA;
$light_gray: #E2E3E3;

$spreadsheet_label_font: tahoma;

html {
    font-family: 'Courier New', Courier, monospace;
}
section, div, form, input {
    box-sizing: border-box;
}

.container {
    height: calc(100vh - 64px);
    width: 100vw;
}

[gdarea="nav"] {
    form {
        height: 100%;
        input {
            width: 100%;
            height: 100%;
            padding: .25rem;
            border: 0;
            background: none;
            outline: 0;
            padding-left: .5rem;
        }
    }
    [gdarea="add_row"], [gdarea="add_column"] {
        display: grid;
        justify-items: center;
        align-items: center;
    }
    .tool {
        background: none;
        border: none !important;
        &:hover {
            cursor: pointer;
        }
    }
    [gdarea="add_row"], [gdarea="add_column"] {
        button {
            outline: 0;
            svg {
                width: .8rem;
                &.special {
                    // todo remove this with proper container that aligns children
                    position: relative;
                    left: 6px;
                    top: 1px;
                }
            }
        }
    }
    [gdarea="add_row"] button {
        justify-self: flex-start;
        margin-left: .5rem;
    }
    [gdarea="add_column"] button {
        justify-self: flex-end;
        margin-right: .5rem;
    }
    [gdarea="label"] {
        width: 100%;
        display: inline-grid;
        justify-items: end;
        align-items: center;
        font-size: 11px;
        text-transform: uppercase;
        font-weight: 600;
        font-family: $spreadsheet_label_font;
    }
}

[gdarea="column_labels"], [gdarea="row_labels"] {
    text-align: center;
    text-transform: uppercase;
}

[gdarea="spreadsheet"] {
    overflow: auto;
    height: calc(100vh - 64px - 2rem);
    div {
        display: inline-grid;
        justify-items: center;
        align-items: center;
        border-right: 1px solid $light_gray;
        border-bottom: 1px solid $light_gray;
    }
    [gdarea="sheet"] {
        div {
            width: 6rem;
            height: 1.5rem;
            font-family: $spreadsheet_label_font;
            font-size: .8rem;
            &.active {
                border: 2px solid $blue;
            }
            &:hover {
                cursor: cell;
                background-color: $light_blue;
            }
        }
    }
    [gdarea="row_labels"], [gdarea="column_labels"] {
        div {
            background-color: $gray;
            font-family: $spreadsheet_label_font;
            font-size: .7rem;
            height: 1.5rem;
        }
    }
    [gdarea="row_labels"] {
        div {
            &:first-child {
                border-top: 1px solid $light_gray;
            }
            width: 2.75rem;
            font-size: .65rem;
        }
    }
    [gdarea="column_labels"] {
        div {
            &:first-child {
                border-left: 1px solid $light_gray;
            }
            border-top: 1px solid $light_gray;
            width: 6rem;
        }
    }
}
Legend
Html element
Component
Html element with directive

result-matching ""

    No results matching ""