{"id":24616245,"url":"https://github.com/benoitlahoz/cell-collection","last_synced_at":"2026-05-18T19:39:07.696Z","repository":{"id":239468589,"uuid":"799614501","full_name":"benoitlahoz/cell-collection","owner":"benoitlahoz","description":"A zero-dependencies collection of 1D to 3D 'cells' built on top of Javascript `Array` to help multidimensional array handling.","archived":false,"fork":false,"pushed_at":"2024-05-16T08:13:19.000Z","size":1668,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-09T02:58:45.667Z","etag":null,"topics":["array","cell","multidimensional-arrays"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/benoitlahoz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-05-12T16:58:40.000Z","updated_at":"2024-05-16T08:13:22.000Z","dependencies_parsed_at":"2025-03-18T19:42:03.957Z","dependency_job_id":"b8395c5b-12fc-4905-9c72-58ba0ffc5dc3","html_url":"https://github.com/benoitlahoz/cell-collection","commit_stats":null,"previous_names":["benoitlahoz/cell-collection"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/benoitlahoz/cell-collection","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benoitlahoz%2Fcell-collection","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benoitlahoz%2Fcell-collection/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benoitlahoz%2Fcell-collection/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benoitlahoz%2Fcell-collection/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/benoitlahoz","download_url":"https://codeload.github.com/benoitlahoz/cell-collection/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benoitlahoz%2Fcell-collection/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":272419591,"owners_count":24931910,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-28T02:00:10.768Z","response_time":74,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["array","cell","multidimensional-arrays"],"created_at":"2025-01-24T22:16:31.421Z","updated_at":"2026-05-18T19:39:02.669Z","avatar_url":"https://github.com/benoitlahoz.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# cell-collection\n\n---\n\n\u003cdiv style=\"display: flex\"\u003e\n\u003cimg src=\"https://img.shields.io/badge/statements-100%25-brightgreen.svg?style=flat\" style=\"height:100%; margin-right: 0.5rem;\"/\u003e \n\u003cimg src=\"https://img.shields.io/badge/branches-86.55%25-yellow.svg?style=flat\" style=\"height:100%; margin-right: 0.5rem;\"/\u003e \n\u003cimg src=\"https://img.shields.io/badge/functions-100%25-brightgreen.svg?style=flat\" style=\"height:100%; margin-right: 0.5rem;\"/\u003e \n\u003cimg src=\"https://img.shields.io/badge/lines-100%25-brightgreen.svg?style=flat\" style=\"height:100%; margin-right: 0.5rem;\"/\u003e \n\u003c/div\u003e\n\u003cbr /\u003e\n\nA collection built on top of javascript `Array` to help with handling 1D to 3D arrays.\n\n## Why and what\n\nWhen 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](https://www.electronjs.org/) 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.\n\n`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).\n\n`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.\n\n## Basic Concepts\n\n### Cells\n\nIt'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).\nBelow are some examples on how to create basic cells and cells from HTML elements.\n`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).\n\n### Multidimensional\n\n`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.\n\n```typescript\n// Cells created with tubes only.\ncollection.has(0, 0, 1);\n\n// Cells created with cols only.\ncollection.has(0, 1);\n\n// Cells created with rows only.\ncollection.has(1);\n\n// 2D cells array.\ncollection.has(1, 1);\n\n// 3D cells array.\ncollection.has(1, 1, 1);\n```\n\n#### 'Coordinate' system\n\nThe convention for the collection is that:\n\n- `row`s are going from the top to the bottom (index 0 \u0026darr; Infinity).\n- `column`s are going from the left to the right (index 0 \u0026rarr; Inifinity).\n- `tube`s are going from the front to the back (index 0 \u0026uarr; Infinity).\n\nAccording 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.\n\n### Duplicates\n\nSince 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`.\n\n### Abstractions\n\nThe 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.\n\nAs 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`.\n\n## Install\n\n### with npm\n\n```sh\nnpm install cell-collection\n```\n\n### with yarn\n\n```sh\nyarn add cell-collection\n```\n\n## Examples\n\n### Creating basic cells from data\n\n```typescript\nimport { Cell } from 'cell-collection';\n\n// A 3D array with 100 rows of 100 columns of 100 tubes, filled with random numbers.\nconst data = new Array(100).fill(\n  new Array(100).fill(new Array(100).fill(Math.random()))\n);\n\n// The collection.\nconst collection = new Collection();\n\n// Create cells from the array and push them in the collection.\nfor (let row = 0; row \u003c 100; row++) {\n  for (let col = 0; col \u003c 100; col++) {\n    for (let tube = 0; tube \u003c 100; tube++) {\n      const cell = new Cell({\n        index: {\n          row,\n          col,\n          tube,\n        },\n        size: {\n          width: 1,\n          height: 1,\n          depth: 1,\n        },\n      });\n\n      // Just for the example: assign our data random number as `userData` of the cell.\n      cell.userData = data[row][col][tube];\n\n      // Add the cell to the collection.\n      collection.push(cell);\n    }\n  }\n}\n\n// Get a range of cells of 3 * 3 * 3 beginning at [2, 4, 3].\nconst rangeA: CellCollection = collection.in({\n  index: {\n    row: 2,\n    col: 4,\n    tube: 3,\n  },\n  size: {\n    width: 3,\n    height: 3,\n    depth: 3,\n  },\n});\n\n// Get cells at given position in the range.\nconst cellA: Cell | undefined = rangeA.at(2, 4, 3);\nconst cellB: Cell | undefined = rangeA.at(3, 4, 4);\n\n// Get a new range by passing the cells.\nconst rangeB: CellCollection = rangeA.in(cellA, cellB);\n\n// Get the intersecting cells as a new collection.\nconsole.log(rangeB.intersection(rangeA));\n\n// Get the cell at the next row from cellA.\nconst nextRow: Cell | undefined = collection.bottom(cellA);\n\n// Get the cell at the next column from cellA.\nconst nextCol: Cell | undefined = collection.right(cellA);\n\n// Get the cell at the next tube from cellA.\nconst nextCol: Cell | undefined = collection.back(cellA);\n```\n\n### Creating HTML cells from elements\n\n```html\n\u003cdiv id=\"container\"\u003e\n  \u003cdiv class=\"row\"\u003e\n    \u003cdiv class=\"col\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"col\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"col\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"row\"\u003e\n    \u003cdiv class=\"col\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"col\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"col\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"row\"\u003e\n    \u003cdiv class=\"col\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"col\"\u003e\u003c/div\u003e\n    \u003cdiv class=\"col\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n```\n\n```typescript\nconst collection = new CellCollection();\nconst onPointer = (cell: HTMLCell, event: PointerEvent) =\u003e {\n  console.log(cell, event);\n  cell.isSelected ? cell.unselect() : cell.select();\n};\n\n// Get container element.\nconst container = document.getElementById('container');\n\n// Get elements that contain 'row' selector.\nconst rows = Array.from(container.children).filter((el: HTMLElement) =\u003e\n  el.classList.contains('row')\n);\n\nlet rowIndex = 0;\n\n// Iterate the children that contain 'row' selector.\nfor (const element of rows) {\n  // Get elements that contain 'col' selector.\n  const cols = Array.from(container.children).filter((el: HTMLElement) =\u003e\n    el.classList.contains('col')\n  );\n\n  let colIndex = 0;\n\n  // Iterate the children that contain 'col' selector.\n  for (const col of cols) {\n    const cell = new HTMLCell(\n      col,\n      {\n        index: {\n          row: rowIndex,\n          col: colIndex,\n        },\n        size: {\n          width: 1,\n          height: 1,\n        },\n      },\n      // Element will be marked by adding or removing these classes when selecting/unselecting, focusing/unfocusing.\n      {\n        selectedSelector: 'selected',\n        focusSelector: 'focused',\n        // To which event the cell will be reactive to selection / focus.\n        pointerEventChannel: 'pointerdown',\n      }\n    );\n\n    // Add pointer listener to cell's element: clicking down will toggle the cell selected state.\n    cell.addPointerListener(onPointer.bind(null, cell) as EventListener);\n\n    collection.push(cell);\n    colIndex++;\n  }\n\n  rowIndex++;\n}\n\nconsole.log(collection.length); // 9 = 3 * 3\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenoitlahoz%2Fcell-collection","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbenoitlahoz%2Fcell-collection","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenoitlahoz%2Fcell-collection/lists"}