changed
CHANGELOG.md
|
@@ -1,5 +1,18 @@
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+ ## v3.2.0 (2023-06-28)
|
4
|
+
|
5
|
+ New features:
|
6
|
+
|
7
|
+ * CJK/Unicode character support at the cell level. Non-ascii characters
|
8
|
+ should no longer break rendering.
|
9
|
+
|
10
|
+ * We now have row-level support for colored backgrounds and foregrounds,
|
11
|
+ see some [examples](https://github.com/djm/table_rex/pull/44).
|
12
|
+
|
13
|
+ * Tables can now have no rows if they wish; that constraint has been relaxed
|
14
|
+ & tables will render fine without any rows (e.g if they just have a header).
|
15
|
+
|
3
16
|
## v3.1.1 (2021-01-30)
|
4
17
|
|
5
18
|
Bugfix: Fixes a multiline text rendering crash when inputs had differing
|
changed
README.md
|
@@ -6,7 +6,7 @@
|
6
6
|
|
7
7
|
**An Elixir app which generates text-based tables for display**
|
8
8
|
|
9
|
- <img src="https://raw.githubusercontent.com/djm/table_rex/master/assets/examples.gif" width="500" alt="Layout Examples" />
|
9
|
+ <img src="https://raw.githubusercontent.com/djm/table_rex/main/assets/examples.gif" width="500" alt="Layout Examples" />
|
10
10
|
|
11
11
|
|
12
12
|
#### Features
|
|
@@ -21,8 +21,9 @@ The data structures support:
|
21
21
|
* column & cell level text alignment: left, center, right.
|
22
22
|
* table titles & column headers.
|
23
23
|
* sorting based on raw input data (rather than just strings).
|
24
|
- * column, header & cell level <img src="http://i.imgur.com/LCfvYYM.png" width="44" /> support.
|
24
|
+ * column, header & cell level <img src="http://i.imgur.com/LCfvYYM.png" width="44" /> support, including backgrounds.
|
25
25
|
* automatic but defineable column & cell level padding.
|
26
|
+ * CJK & Unicode support in the cells.
|
26
27
|
* styling the table with various vertical & horizontal framing.
|
27
28
|
* styling the table with custom separator symbols.
|
28
29
|
* multi-line cell support.
|
|
@@ -60,7 +61,7 @@ The package is [available on Hex](https://hex.pm/packages/table_rex), therefore:
|
60
61
|
|
61
62
|
```elixir
|
62
63
|
def deps do
|
63
|
- [{:table_rex, "~> 3.1.1"}]
|
64
|
+ [{:table_rex, "~> 3.2.0"}]
|
64
65
|
end
|
65
66
|
```
|
66
67
|
|
|
@@ -305,10 +306,10 @@ Table.new(rows, header)
|
305
306
|
|
306
307
|
We have an extensive test suite which helps showcase project usage. For example:
|
307
308
|
the
|
308
|
- [quick render functions](https://github.com/djm/table_rex/blob/master/test/table_rex_test.exs),
|
309
|
- [table manipulation API](https://github.com/djm/table_rex/blob/master/test/table_rex/table_test.exs)
|
309
|
+ [quick render functions](https://github.com/djm/table_rex/blob/main/test/table_rex_test.exs),
|
310
|
+ [table manipulation API](https://github.com/djm/table_rex/blob/main/test/table_rex/table_test.exs)
|
310
311
|
or
|
311
|
- [the text renderer module](https://github.com/djm/table_rex/blob/master/test/table_rex/renderer/text_test.exs).
|
312
|
+ [the text renderer module](https://github.com/djm/table_rex/blob/main/test/table_rex/renderer/text_test.exs).
|
312
313
|
|
313
314
|
To run the test suite, from the project directory, do:
|
changed
hex_metadata.config
|
@@ -15,4 +15,4 @@
|
15
15
|
{<<"links">>,[{<<"GitHub">>,<<"https://github.com/djm/table_rex">>}]}.
|
16
16
|
{<<"name">>,<<"table_rex">>}.
|
17
17
|
{<<"requirements">>,[]}.
|
18
|
- {<<"version">>,<<"3.1.1">>}.
|
18
|
+ {<<"version">>,<<"3.2.0">>}.
|
changed
lib/table_rex.ex
|
@@ -25,7 +25,6 @@ defmodule TableRex do
|
25
25
|
def quick_render!(rows, header \\ [], title \\ nil) when is_list(rows) and is_list(header) do
|
26
26
|
case quick_render(rows, header, title) do
|
27
27
|
{:ok, rendered} -> rendered
|
28
|
- {:error, reason} -> raise TableRex.Error, message: reason
|
29
28
|
end
|
30
29
|
end
|
31
30
|
end
|
changed
lib/table_rex/column.ex
|
@@ -5,7 +5,7 @@ defmodule TableRex.Column do
|
5
5
|
The align field can be one of :left, :center or :right.
|
6
6
|
"""
|
7
7
|
|
8
|
- defstruct align: :left, padding: 1, color: nil
|
8
|
+ defstruct align: :left, padding: 1, color: nil, width_calc: &String.length/1
|
9
9
|
|
10
10
|
@type t :: %__MODULE__{}
|
11
11
|
end
|
changed
lib/table_rex/renderer/text.ex
|
@@ -295,23 +295,29 @@ defmodule TableRex.Renderer.Text do
|
295
295
|
cell_align = Map.get(cell, :align) || Table.get_column_meta(table, col_index, :align)
|
296
296
|
cell_color = Map.get(cell, :color) || Table.get_column_meta(table, col_index, :color)
|
297
297
|
|
298
|
- do_render_cell(cell.rendered_value, col_width, col_padding, align: cell_align)
|
298
|
+ cell_width_calc =
|
299
|
+ Map.get(cell, :width_calc, nil) || Table.get_column_meta(table, col_index, :width_calc)
|
300
|
+
|
301
|
+ do_render_cell(cell.rendered_value, col_width, col_padding,
|
302
|
+ align: cell_align,
|
303
|
+ width_calc: cell_width_calc
|
304
|
+ )
|
299
305
|
|> format_with_color(cell.rendered_value, cell_color)
|
300
306
|
end
|
301
307
|
|
302
308
|
defp do_render_cell(value, inner_width) do
|
303
|
- do_render_cell(value, inner_width, 0, align: :center)
|
309
|
+ do_render_cell(value, inner_width, 0, align: :center, width_calc: &String.length/1)
|
304
310
|
end
|
305
311
|
|
306
|
- defp do_render_cell(value, inner_width, _padding, align: :center) do
|
307
|
- value_len = String.length(strip_ansi_color_codes(value))
|
312
|
+ defp do_render_cell(value, inner_width, _padding, align: :center, width_calc: width_calc) do
|
313
|
+ value_len = width_calc.(strip_ansi_color_codes(value))
|
308
314
|
post_value = ((inner_width - value_len) / 2) |> round
|
309
315
|
pre_value = inner_width - (post_value + value_len)
|
310
316
|
String.duplicate(" ", pre_value) <> value <> String.duplicate(" ", post_value)
|
311
317
|
end
|
312
318
|
|
313
|
- defp do_render_cell(value, inner_width, padding, align: align) do
|
314
|
- value_len = String.length(strip_ansi_color_codes(value))
|
319
|
+ defp do_render_cell(value, inner_width, padding, align: align, width_calc: width_calc) do
|
320
|
+ value_len = width_calc.(strip_ansi_color_codes(value))
|
315
321
|
alt_side_padding = inner_width - value_len - padding
|
316
322
|
|
317
323
|
{pre_value, post_value} =
|
|
@@ -389,20 +395,22 @@ defmodule TableRex.Renderer.Text do
|
389
395
|
row_index
|
390
396
|
) do
|
391
397
|
padding = Table.get_column_meta(table, col_index, :padding)
|
392
|
- {width, height} = content_dimensions(cell.rendered_value, padding)
|
398
|
+ width_calc = Table.get_column_meta(table, col_index, :width_calc)
|
399
|
+ {width, height} = content_dimensions(cell.rendered_value, padding, width_calc)
|
393
400
|
col_widths = Map.update(col_widths, col_index, width, &Enum.max([&1, width]))
|
394
401
|
row_heights = Map.update(row_heights, row_index, height, &Enum.max([&1, height]))
|
395
402
|
{col_widths, row_heights}
|
396
403
|
end
|
397
404
|
|
398
|
- defp content_dimensions(value, padding) when is_binary(value) and is_number(padding) do
|
405
|
+ defp content_dimensions(value, padding, width_calc)
|
406
|
+ when is_binary(value) and is_number(padding) do
|
399
407
|
lines =
|
400
408
|
value
|
401
409
|
|> strip_ansi_color_codes()
|
402
410
|
|> String.split("\n")
|
403
411
|
|
404
412
|
height = Enum.count(lines)
|
405
|
- width = lines |> Enum.map(&String.length/1) |> Enum.max()
|
413
|
+ width = lines |> Enum.map(width_calc) |> Enum.max()
|
406
414
|
{width + padding * 2, height}
|
407
415
|
end
|
changed
lib/table_rex/table.ex
|
@@ -243,13 +243,6 @@ defmodule TableRex.Table do
|
243
243
|
|> Map.fetch!(key)
|
244
244
|
end
|
245
245
|
|
246
|
- @doc """
|
247
|
- Returns a boolean detailing if the passed table has any row data set.
|
248
|
- """
|
249
|
- @spec has_rows?(Table.t()) :: boolean
|
250
|
- def has_rows?(%Table{rows: []}), do: false
|
251
|
- def has_rows?(%Table{rows: rows}) when is_list(rows), do: true
|
252
|
-
|
253
246
|
@doc """
|
254
247
|
Returns a boolean detailing if the passed table has a header row set.
|
255
248
|
"""
|
|
@@ -272,12 +265,7 @@ defmodule TableRex.Table do
|
272
265
|
def render(%Table{} = table, opts \\ []) when is_list(opts) do
|
273
266
|
{renderer, opts} = Keyword.pop(opts, :renderer, @default_renderer)
|
274
267
|
opts = opts |> Enum.into(renderer.default_options)
|
275
|
-
|
276
|
- if Table.has_rows?(table) do
|
277
|
- renderer.render(table, opts)
|
278
|
- else
|
279
|
- {:error, "Table must have at least one row before being rendered"}
|
280
|
- end
|
268
|
+ renderer.render(table, opts)
|
281
269
|
end
|
282
270
|
|
283
271
|
@doc """
|
|
@@ -294,4 +282,21 @@ defmodule TableRex.Table do
|
294
282
|
{:error, reason} -> raise TableRex.Error, message: reason
|
295
283
|
end
|
296
284
|
end
|
285
|
+
|
286
|
+ def row_colors(result, colors) do
|
287
|
+ %{result | rows: map_colors(Map.get(result, :rows), colors)}
|
288
|
+ end
|
289
|
+
|
290
|
+ defp map_colors(rows, colors) do
|
291
|
+ n = Enum.count(colors)
|
292
|
+
|
293
|
+ rows
|
294
|
+ |> Enum.with_index()
|
295
|
+ |> Enum.map(fn {list, i} ->
|
296
|
+ Enum.map(list, fn x ->
|
297
|
+ color = Enum.at(colors, rem(i, n))
|
298
|
+ %{x | color: color}
|
299
|
+ end)
|
300
|
+ end)
|
301
|
+ end
|
297
302
|
end
|
changed
mix.exs
|
@@ -2,12 +2,13 @@ defmodule TableRex.Mixfile do
|
2
2
|
use Mix.Project
|
3
3
|
|
4
4
|
@source_url "https://github.com/djm/table_rex"
|
5
|
- @version "3.1.1"
|
5
|
+ @version "3.2.0"
|
6
6
|
|
7
7
|
def project do
|
8
8
|
[
|
9
9
|
app: :table_rex,
|
10
10
|
name: "table_rex",
|
11
|
+ source_url: @source_url,
|
11
12
|
description: "Generate configurable text-based tables for display (ASCII & more)",
|
12
13
|
version: @version,
|
13
14
|
elixir: "~> 1.7",
|
|
@@ -20,13 +21,14 @@ defmodule TableRex.Mixfile do
|
20
21
|
end
|
21
22
|
|
22
23
|
def application do
|
23
|
- [applications: [:logger]]
|
24
|
+ [extra_applications: [:logger]]
|
24
25
|
end
|
25
26
|
|
26
27
|
defp deps do
|
27
28
|
[
|
28
29
|
{:earmark, ">= 0.0.0", only: :docs},
|
29
|
- {:ex_doc, ">= 0.0.0", only: :docs}
|
30
|
+ {:ex_doc, ">= 0.0.0", only: :docs},
|
31
|
+ {:unicode, ">= 0.0.0", only: :test}
|
30
32
|
]
|
31
33
|
end
|