legacy/themes/table.js

import { j3pAddElt, j3pElement, j3pIsHtmlElement } from 'src/legacy/core/functions'

import './table.scss'

/** @module legacy/themes/table */

/**
 * Ajoute un tag table avec ses tr & td dans container
 * @param {HTMLElement|string} container
 * @param {Object} opts
 * @param {number} opts.nbLignes
 * @param {number} opts.nbColonnes
 * @param {string} [opts.id] si fourni cet id sera mis sur le tag table,
 *                                    les tr auront cet id avec en suffixe lY (où Y est l’index de la ligne, qui démarre donc à 0)
 *                                    et les td auront l’id de la ligne avec en suffixe cX où X est l’index de la colonne
 * @param {string} [opts.className] éventuelle classe css à appliquer au tableau (cf table.scss pour la liste des styles connus par ce modèle, tbDefault|tbWithBorder)
 * @param {Array[]} [opts.contents] contenus éventuels à mettre dans les tag td
 * @param {boolean} [opts.withHeader=false] Passer true pour que les cellules de la première ligne soient des <th> et pas des <td>
 * @return {HTMLTableElement}
 */
export function addTable (container, opts) {
  if (!Number.isInteger(opts?.nbLignes) || !Number.isInteger(opts?.nbColonnes) || opts.nbLignes < 0 || opts.nbColonnes < 0) throw Error('Arguments invalides')
  const { id, nbLignes, nbColonnes, className, withHeader } = opts
  const tableProps = { className, id }
  // on vérifie quand même que l’id n’existe pas dans le dom
  if (id && j3pElement(id, false)) delete tableProps.id // j3pElement a déjà râlé, on ajoute rien, on vire juste l’id déjà existant
  if (className === undefined) tableProps.className = 'tbDefault'
  const table = j3pAddElt(container, 'table', '', tableProps)
  const contents = Array.isArray(opts.contents) ? opts.contents : []
  while (table.childNodes.length < nbLignes) {
    const iLig = table.childNodes.length
    const tr = j3pAddElt(table, 'tr')
    if (id) tr.id = `${id}l${iLig}`
    const cellTag = (iLig === 0 && withHeader) ? 'th' : 'td'
    while (tr.childNodes.length < nbColonnes) {
      const iCol = tr.childNodes.length
      const content = (Array.isArray(contents[iLig]) && contents[iLig][iCol]) || ''
      const tdProps = {}
      if (id) tdProps.id = `${id}l${iLig}c${iCol}`
      j3pAddElt(tr, cellTag, content, tdProps)
    }
  }
  return table
}

/**
 * Ajoute un tableau et retourne ses cellules (pas l’élément table)
 * @param {HTMLElement|string} container
 * @param {number} nbLignes
 * @param {number} nbColonnes
 * @return {TableCells}
 */
export function addDefaultTable (container, nbLignes, nbColonnes) {
  const table = addTable(container, { className: 'tbDefault', nbLignes, nbColonnes })
  return getCells(table)
}

/**
 * Retourne un tableau de tableaux de tous les td de table
 * @param {HTMLTableElement} table
 * @return {TableCells}
 */
export function getCells (table) {
  if (!j3pIsHtmlElement(table)) throw Error('argument invalide')
  // on retourne pour chaque tr trouvé dans table la liste des td qu’il contient
  // attention, querySelectorAll retourne une NodeList qui n’a pas de méthode map, d’où Array.from
  // le :scope sert à désigner l’élément sur lequel on est appelé, pour ne prendre ici que ses enfants directs
  // (au cas où le <table> en contiendrait d’autres on veut pas toute la descendance
  // cf https://developer.mozilla.org/fr/docs/Web/API/Document/querySelectorAll#notes
  return Array.from(table.querySelectorAll(':scope > tr')) // tableau de ligne
    .map(tr => Array.from(tr.querySelectorAll(':scope > td'))) // pour chaque elt de la ligne on retourne le tableau des td qu’elle contient
}

export function getLines (table) {
  if (!j3pIsHtmlElement(table)) throw Error('argument invalide')
  // on retourne pour chaque tr trouvé dans table la liste des td qu’il contient
  // attention, querySelectorAll retourne une NodeList qui n’a pas de méthode map, d’où Array.from
  // le :scope sert à désigner l’élément sur lequel on est appelé, pour ne prendre ici que ses enfants directs
  // (au cas où le <table> en contiendrait d’autres on veut pas toute la descendance
  // cf https://developer.mozilla.org/fr/docs/Web/API/Document/querySelectorAll#notes
  return Array.from(table.querySelectorAll(':scope > tr')) // tableau de lignes
}

/**
 * Idem addDefaultTable mais retourne aussi l’élément table et la liste des tr
 * @param container
 * @param nbLignes
 * @param nbColonnes
 * @return {{cells: TableCells, lines: HTMLTableRowElement[], table: HTMLTableElement}}
 */
export function addDefaultTableDetailed (container, nbLignes, nbColonnes) {
  const table = addTable(container, { className: 'tbDefault', nbLignes, nbColonnes })
  return {
    cells: getCells(table),
    lines: getLines(table),
    table
  }
}

export function addDefaultTableGrid (where, nbLignes, nbColonnes) {
  where.style.display = 'grid'
  where.style.gridTemplateColumns = 'repeat(' + nbColonnes + ', auto)'
  where.style.gridTemplateRows = 'repeat(' + nbLignes + ', auto)'
  where.style.display = 'grid'
  const aret = []
  for (let i = 0; i < nbLignes; i++) {
    aret[i] = []
    for (let j = 0; j < nbColonnes; j++) {
      const lacase = j3pAddElt(where, 'div')
      lacase.style.gridColumn = (j + 1)
      lacase.style.gridRow = i
      lacase.style.display = 'flex'
      lacase.style.alignItems = 'center'
      lacase.style.justifyContent = 'center'
      lacase.style.textAlign = 'center'
      aret[i].push(lacase)
    }
  }
  aret.ligne = nbLignes
  aret.co = nbColonnes
  aret.where = where
  return aret
}

/**
 * @typedef LineCells
 * @type {HTMLTableDataCellElement[]}
 */
/**
 * @typedef TableCells
 * @type {LinesCells[]}
 */