cell-collection
TypeScript icon, indicating that this package has built-in type declarations

1.1.7 • Public • Published

cell-collection



A collection built on top of javascript Array to help with handling 1D to 3D arrays.

Why and what

When working with HTML tables, one often needs to select one or more 'cells' and to navigate between them according to usual behaviors of mainstream softwares and operating systems. The initial motivation of cell-collection was to handle this navigation in Electron applications, like if tables were native ones. By 'table', we mean any set of data that has contiguous row, column and tube numbers/positions and belong to the same collection.

cell-collection reflects most of javascript's Array methods, while offering custom handlers to help with managing selection, multiselection, navigation, focus, etc. Even if it is fully usable in server environment, by providing a basic Cell implementation, it will demonstrate its full power when using it client-side, in a DOM. For this purpose, an HTMLCell implementation is provided (see below).

CellCollection object or its cells are not aimed to handle data by themselves. They are just representations of a topology. However, cells have an userData property that can help maintaining everything in one place. Despite that, both cells and their userData are stored by reference to the initial data. Like with a native javascript array, it's up to the user to manage their own objects.

Basic Concepts

Cells

It's up to the user to decide how to create its cells. Basically, they must be created with an initial position at row (height), eventually column (width) and eventually tube (depth). Below are some examples on how to create basic cells and cells from HTML elements. cell-collection package provides two implementations of a cell : Cell (which can be used server-side) and HTMLCell (which relies on and manage with the DOM).

Multidimensional

CellCollection accepts 1D to 3D array of cells. Once instantiated it will have internally flatten the array to a single dimension one, and will allow access to the cells by their row / col / tube numbers. It's up to the user to chose to create cells with a specific number at given position slot.

// Cells created with tubes only.
collection.has(0, 0, 1);

// Cells created with cols only.
collection.has(0, 1);

// Cells created with rows only.
collection.has(1);

// 2D cells array.
collection.has(1, 1);

// 3D cells array.
collection.has(1, 1, 1);

'Coordinate' system

The convention for the collection is that:

  • rows are going from the top to the bottom (index 0 ↓ Infinity).
  • columns are going from the left to the right (index 0 → Inifinity).
  • tubes are going from the front to the back (index 0 ↑ Infinity).

According to this, for example, the right(cell: AbstractCell): AbstractCell | undefined method of CellCollection will return the cell with same row and tube values, but next column according to the passed cell.

Duplicates

Since version 1.0.4, CellCollection implementation allows duplicate cells. It means that cells with same row, column and tube can stand in the same collection. This was done to allow switching between two cells according to user provided actions / parameters. E.g. two cells at the same positon have different userData or element.

Abstractions

The three implementations provided by cell-collection package (Cell, HTMLCell and CellCollection) are implementing abstractions (named AbstractCell and AbstractCellCollection) that can be used to create custom objects (e.g. a cell collection that would be based on a Set instead of an array). Although they are quite strict they allow to inject, for example different objects when testing specific implementations.

As an example of this, HTMLCell has been left as a specific implementation of AbstractCell instead of a subclass of Cell, and element accessor has been left as optional in AbstractCell.

Install

with npm

npm install cell-collection

with yarn

yarn add cell-collection

Examples

Creating basic cells from data

import { Cell } from 'cell-collection';

// A 3D array with 100 rows of 100 columns of 100 tubes, filled with random numbers.
const data = new Array(100).fill(
  new Array(100).fill(new Array(100).fill(Math.random()))
);

// The collection.
const collection = new Collection();

// Create cells from the array and push them in the collection.
for (let row = 0; row < 100; row++) {
  for (let col = 0; col < 100; col++) {
    for (let tube = 0; tube < 100; tube++) {
      const cell = new Cell({
        index: {
          row,
          col,
          tube,
        },
        size: {
          width: 1,
          height: 1,
          depth: 1,
        },
      });

      // Just for the example: assign our data random number as `userData` of the cell.
      cell.userData = data[row][col][tube];

      // Add the cell to the collection.
      collection.push(cell);
    }
  }
}

// Get a range of cells of 3 * 3 * 3 beginning at [2, 4, 3].
const rangeA: CellCollection = collection.in({
  index: {
    row: 2,
    col: 4,
    tube: 3,
  },
  size: {
    width: 3,
    height: 3,
    depth: 3,
  },
});

// Get cells at given position in the range.
const cellA: Cell | undefined = rangeA.at(2, 4, 3);
const cellB: Cell | undefined = rangeA.at(3, 4, 4);

// Get a new range by passing the cells.
const rangeB: CellCollection = rangeA.in(cellA, cellB);

// Get the intersecting cells as a new collection.
console.log(rangeB.intersection(rangeA));

// Get the cell at the next row from cellA.
const nextRow: Cell | undefined = collection.bottom(cellA);

// Get the cell at the next column from cellA.
const nextCol: Cell | undefined = collection.right(cellA);

// Get the cell at the next tube from cellA.
const nextCol: Cell | undefined = collection.back(cellA);

Creating HTML cells from elements

<div id="container">
  <div class="row">
    <div class="col"></div>
    <div class="col"></div>
    <div class="col"></div>
  </div>
  <div class="row">
    <div class="col"></div>
    <div class="col"></div>
    <div class="col"></div>
  </div>
  <div class="row">
    <div class="col"></div>
    <div class="col"></div>
    <div class="col"></div>
  </div>
</div>
const collection = new CellCollection();
const onPointer = (cell: HTMLCell, event: PointerEvent) => {
  console.log(cell, event);
  cell.isSelected ? cell.unselect() : cell.select();
};

// Get container element.
const container = document.getElementById('container');

// Get elements that contain 'row' selector.
const rows = Array.from(container.children).filter((el: HTMLElement) =>
  el.classList.contains('row')
);

let rowIndex = 0;

// Iterate the children that contain 'row' selector.
for (const element of rows) {
  // Get elements that contain 'col' selector.
  const cols = Array.from(container.children).filter((el: HTMLElement) =>
    el.classList.contains('col')
  );

  let colIndex = 0;

  // Iterate the children that contain 'col' selector.
  for (const col of cols) {
    const cell = new HTMLCell(
      col,
      {
        index: {
          row: rowIndex,
          col: colIndex,
        },
        size: {
          width: 1,
          height: 1,
        },
      },
      // Element will be marked by adding or removing these classes when selecting/unselecting, focusing/unfocusing.
      {
        selectedSelector: 'selected',
        focusSelector: 'focused',
        // To which event the cell will be reactive to selection / focus.
        pointerEventChannel: 'pointerdown',
      }
    );

    // Add pointer listener to cell's element: clicking down will toggle the cell selected state.
    cell.addPointerListener(onPointer.bind(null, cell) as EventListener);

    collection.push(cell);
    colIndex++;
  }

  rowIndex++;
}

console.log(collection.length); // 9 = 3 * 3

Package Sidebar

Install

npm i cell-collection

Weekly Downloads

3

Version

1.1.7

License

MIT

Unpacked Size

63.6 kB

Total Files

13

Last publish

Collaborators

  • benoitlahoz