Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow column style to be set in addition to column width #81

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion lib/elixlsx/compiler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,21 @@ defmodule Elixlsx.Compiler do
end
end

def compinfo_col_pass_style(wci, %{width: w} = props) when props == %{width: w}, do: wci

def compinfo_col_pass_style wci, props do
update_in wci.cellstyledb,
&CellStyleDB.register_style(&1,
Elixlsx.Style.CellStyle.from_props(props))
end

@spec compinfo_from_cols(WorkbookCompInfo.t, list(list(any()))) :: WorkbookCompInfo.t
def compinfo_from_cols wci, cols do
Enum.sort(cols)
|> List.foldl(wci, fn ({_, props}, wci) ->
compinfo_col_pass_style(wci, props)
end)
end

@spec compinfo_from_rows(WorkbookCompInfo.t, list(list(any()))) :: WorkbookCompInfo.t
def compinfo_from_rows wci, rows do
Expand All @@ -65,7 +80,9 @@ defmodule Elixlsx.Compiler do
@spec compinfo_from_sheets(WorkbookCompInfo.t, list(Sheet.t)) :: WorkbookCompInfo.t
def compinfo_from_sheets wci, sheets do
List.foldl sheets, wci, fn (sheet, wci) ->
compinfo_from_rows wci, sheet.rows
wci
|> compinfo_from_rows(sheet.rows)
|> compinfo_from_cols(sheet.cols)
end
end

Expand Down
29 changes: 25 additions & 4 deletions lib/elixlsx/sheet.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ defmodule Elixlsx.Sheet do
The property list describes formatting options for that
cell. See Font.from_props/1 for a list of options.
"""
defstruct name: "", rows: [], col_widths: %{}, row_heights: %{}, merge_cells: [], pane_freeze: nil, show_grid_lines: true
defstruct name: "", rows: [], cols: %{}, row_heights: %{}, merge_cells: [], pane_freeze: nil, show_grid_lines: true
@type t :: %Sheet {
name: String.t,
rows: list(list(any())),
col_widths: %{pos_integer => number},
cols: %{pos_integer => map},
row_heights: %{pos_integer => number},
merge_cells: [],
pane_freeze: {number, number} | nil,
Expand Down Expand Up @@ -121,14 +121,35 @@ defmodule Elixlsx.Sheet do
end
end

@spec set_col(Sheet.t, String.t, Keyword.t) :: Sheet.t
@doc ~S"""
Set various attributes for a given column. Column is indexed by
name ("A", ...)
"""
def set_col(sheet, column, [{k, v}]) do
index = Util.decode_col(column)
cols = Map.update(sheet.cols, index, %{k => v}, (&Map.put &1, k, v))
Map.put(sheet, :cols, cols)
end

def set_col(sheet, column, opts) do
index = Util.decode_col(column)

cols =
Enum.reduce(opts, sheet.cols, fn {k, v}, acc ->
Map.update(acc, index, %{k => v}, &Map.put(&1, k, v))
end)

Map.put(sheet, :cols, cols)
end

@spec set_col_width(Sheet.t, String.t, number) :: Sheet.t
@doc ~S"""
Set the column width for a given column. Column is indexed by
name ("A", ...)
"""
def set_col_width(sheet, column, width) do
update_in sheet.col_widths,
&(Map.put &1, Util.decode_col(column), width)
set_col(sheet, column, width: width)
end

@spec set_row_height(Sheet.t, number, number) :: Sheet.t
Expand Down
54 changes: 39 additions & 15 deletions lib/elixlsx/xml_templates.ex
Original file line number Diff line number Diff line change
Expand Up @@ -272,20 +272,44 @@ defmodule Elixlsx.XMLTemplates do
end
end

defp make_col_width({k, v}) do
'<col min="#{k}" max="#{k}" width="#{v}" customWidth="1" />'
end

defp make_col_widths(sheet) do
if Kernel.map_size(sheet.col_widths) != 0 do
cols = Map.to_list(sheet.col_widths)
|> Enum.sort
|> Enum.map_join(&make_col_width/1)

"<cols>#{cols}</cols>"
else
""
end
defp make_col_range(key) do
key_str = Integer.to_string(key)
[" min=\"", key_str, "\" max=\"", key_str, "\""]
end

defp make_col_style(props, _) when props == %{}, do: []
defp make_col_style(%{width: w} = props, _) when props == %{width: w}, do: []
defp make_col_style(props, wci) do
style_id = CellStyleDB.get_id(wci.cellstyledb, CellStyle.from_props(props))
[" style=\"", Integer.to_string(style_id), "\""]
end

defp make_col_width(%{width: width}) when is_integer(width) do
[" width=\"", Integer.to_string(width), "\" customWidth=\"1\""]
end
defp make_col_width(%{width: width}) when is_float(width) do
[" width=\"", Float.to_string(width), "\" customWidth=\"1\""]
end
defp make_col_width(_), do: []

defp make_cols(%{cols: cols}, _) when cols == %{}, do: ""
defp make_cols(%{cols: cols}, wci) do
[
"<cols>",
Map.to_list(cols)
|> Enum.sort()
|> Enum.map(fn {key, props} -> [
"<col",
make_col_range(key),
make_col_width(props),
make_col_style(props, wci),
"/>"
]
end),
"</cols>"
]
# remove to output as iolist instead of string
|> Enum.join()
end

@spec make_sheet(Sheet.t, WorkbookCompInfo.t) :: String.t
Expand Down Expand Up @@ -313,7 +337,7 @@ defmodule Elixlsx.XMLTemplates do
</sheetViews>
<sheetFormatPr defaultRowHeight="12.8"/>
"""
<> make_col_widths(sheet) <>
<> make_cols(sheet, wci) <>
"""
<sheetData>
"""
Expand Down
22 changes: 22 additions & 0 deletions test/sheet_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
defmodule Elixlsx.SheetTest do
use ExUnit.Case, async: false
use ExCheck

alias Elixlsx.Sheet

property :sheet_name do
for_all x in binary() do
Sheet.with_name(x).name == x
end
end

property :sheet_cols do
for_all x in choose(65,90) do
sheet =
%Sheet{}
|> Sheet.set_col_width(<<x>>, 10)
|> Sheet.set_col(<<x>>, bg_color: "#FFFF00", num_format: "mmm-yyyy")
sheet.cols[x - 64] == %{bg_color: "#FFFF00", num_format: "mmm-yyyy", width: 10}
end
end
end
25 changes: 25 additions & 0 deletions test/xml_templates_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
defmodule Elixlsx.XMLTemplatesTest do
use ExUnit.Case

alias Elixlsx.{Compiler, Compiler.WorkbookCompInfo, Sheet, Workbook, XMLTemplates}

describe("make_sheet") do
setup do
[sheet: Sheet.with_name("Sheet1"), wci: %WorkbookCompInfo{}, workbook: %Workbook{}]
end

test "with default values", %{sheet: sheet, wci: wci} do
assert XMLTemplates.make_sheet(sheet, wci) =~ "</worksheet>"
end

test "with column attrs set", %{sheet: sheet, workbook: workbook} do
sheet = sheet
|> Sheet.set_col_width("A", 10)
|> Sheet.set_col("B", bg_color: "#FFFF00", num_format: "mmm-yyyy", width: 12)
wci = Compiler.make_workbook_comp_info(Workbook.insert_sheet(workbook, sheet))
xml = XMLTemplates.make_sheet(sheet, wci)
assert xml =~ "<col min=\"1\" max=\"1\" width=\"10\" customWidth=\"1\"/>"
assert xml =~ "<col min=\"2\" max=\"2\" width=\"12\" customWidth=\"1\" style=\"1\"/>"
end
end
end