tabular function

String tabular(
  1. List<List> rows, {
  2. Map<dynamic, Side>? align,
  3. Map<dynamic, FormatCell>? format,
  4. List<Sort>? sort,
  5. dynamic markdownAlign = false,
  6. Border border = Border.none,
  7. Style style = Style.markdown,
  8. List<int>? rowDividers = const [1],
  9. @Deprecated('Use border=Border.vertical argument') dynamic outerBorder = false,
})

Converts a set of cells defined by a two-dimensional list to a Markdown formatted ASCII table. Returns a string with the table that is ready for printing.

rows is the source data for the table. The first of the lists will be the header, the others - ordinary rows. The values can be Strings, nums, null, or any objects. Either way, they end up being converted to strings.

align specifies which way all cells in a particular column should be aligned. The keys of this Map can be of type int then they denote the index of the column, or of type String - then they denote the name of the column.

format specifies how to convert each cell of a particular column to a row. This conversion occurs after sorting, but before alignment. The keys of this Map can be of type int then they denote the index of the column, or of type String - then they denote the name of the column.

sort determines the sorting order. Sorting can take place in several columns at once. Priority will be given to the ones at the beginning of the list.

markdownAlign determines whether to add the ':' characters to the delimiter under the header. These symbols tell services like GitHub which way to align the columns after converting the table to HTML.

border determines whether an outer border should be added to the table and what parts it should consist of.

rowDividers contains the indices of the rows, which must be preceded by a horizontal divider. By default, there is only index 1, which corresponds to the divider between the header and the body of the table. rowDividers: null will cause dividers to be added between all rows.

Implementation

String tabular(
    List<List<dynamic>> rows,
    {Map<dynamic, Side>? align,
    Map<dynamic, FormatCell>? format,
    List<Sort>? sort,
    markdownAlign = false,
    Border border = Border.none,
    Style style = Style.markdown,
    List<int>? rowDividers = const [1],
    @Deprecated('Use border=Border.vertical argument') // since 2021-04-28
        outerBorder = false}) {
  if (rows.isEmpty) {
    throw ArgumentError.value(rows, 'rows', 'Must not be empty');
  }

  if (outerBorder && border == Border.none) {
    border = Border.vertical;
  }

  bool borderV = border == Border.vertical || border == Border.all;
  bool borderH = border == Border.horizontal || border == Border.all;

  final matrix = CellsMatrix(rows, format);

  if (sort != null) {
    matrix.sortBy(sort);
  }

  List<Side> colToAlign = createColToAlign(matrix, align);

  String cross;
  switch (style) {
    case Style.markdown:
      cross = '|';
      break;
    case Style.mysql:
      cross = '+';
      break;
  }

  String bar = '';
  if (borderV) {
    bar += cross;
  }
  for (int i = 0; i < matrix.columns.length; ++i) {
    final align = markdownAlign ? colToAlign[i] : null;
    int width = matrix.columns[i].textWidth;

    // if (width<=0) {
    //   width = 1;
    // }

    final bool isSingleColumn = matrix.columns.length == 1;
    final bool isFirstColumn = i == 0;
    final bool isLastColumn = i == matrix.columns.length - 1;

    int extra = 0;
    if (!borderV) {
      if (isSingleColumn) {
        extra = -2;
      } else if (isFirstColumn || isLastColumn) {
        extra = -1;
      }
    }

    //int extra = ((isFirstColumn || isLastColumn) && !borderV) ? -1 : 0;

    switch (align) {
      case null:
      case Side.start:
        //print("A $width $extra");
        bar += ('-' * (width + 2 + extra));
        break;
      case Side.end:
        //print("B");
        bar += ('-' * (width + 1 + extra) + ':');
        break;
      case Side.center:
        //print("C");
        bar += (':' + '-' * width + ':');
    }

    if (borderV || !isLastColumn) {
      bar += cross;
    }

    //print("width $width $bar");
  }

  String dashBar() => bar; // '+'+('-'*(bar.length-2))+'+';

  final formattedRows = <String>[];

  if (borderH) {
    formattedRows.add(dashBar());
  }

  final aligner = Aligner();

  var iRow = -1;
  for (var row in matrix.rows) {
    iRow++;

    if (iRow > 0 && (rowDividers == null || rowDividers.contains(iRow))) {
      formattedRows.add(bar);
    }

    var formatted = '';

    var iCol = -1;

    for (final me in enumerate(row)) {
      final cell = me.value;
      if (me.index == 0) {
        if (borderV) {
          formatted += '| ';
        }
      } else {
        formatted += ' | ';
      }
      iCol++;
      formatted += aligner.alignText(cell.toFinalString(),
          matrix.columns[iCol].textWidth, colToAlign[iCol]);
    }

    if (borderV) {
      formatted += ' |';
    }

    formattedRows.add(formatted);
  }

  if (borderH) {
    formattedRows.add(dashBar());
  }

  return formattedRows.join('\n');
}