# `Masu.Column`
[🔗](https://github.com/hattoriai/masu/blob/v0.7.0/lib/masu/column.ex#L1)

A column of a grid: which value of a row it shows, how, and what a
person may do with it.

A grid makes its columns from its `:col` slots (see
`Masu.Components.data_grid/1`):

    <:col field={:name} />
    <:col field={:total} type={:decimal} label="Total" width={140} />
    <:col :let={order} field={:status} type={:enum}>
      <.badge status={order.status} />
    </:col>

## Types

The type says how the browser formats a value, in the grid's locale,
how a filter reads the value it is given, and where the cell aligns:

  * `:text` (the default) and `:enum`, as they are;
  * `:integer`, `:number` and `:decimal`, as numbers, aligned to the end
    of the cell. A decimal goes to the browser as a string, so no digit
    is lost on the way;
  * `:date`, `:datetime` and `:time`;
  * `:boolean`, as a check mark.

A column with an inner block is rendered by it, on the server, for each
row that the browser shows; the type still says how it sorts and
filters.

`filters`, a list of `Masu.Filter` operators, limits a column's filters
to those: the operators the app runs, in a controlled grid
(`on_query`) whose app's queries have fewer than Masu (as a source's
`c:Masu.Source.filters/2` does). Masu adds `:between` for a column that
has `:gte` and `:lte`.

`type={:auto}` is a column whose type the grid learns from its values,
in the first block of rows of its source: integers make an `:integer`
column, `Decimal`s a `:decimal` one, `Date`s a `:date` one, and so on;
values of several types, or of none of these, make it `:text`. The
grid keeps the type until its source changes. A column whose rows are
a query tool's results, of no types, takes it.

## Rows as lists

`field={2}` reads the value at that place of a row that is a list (from
0): a query tool's rows, which come as lists of values in the order of
its columns. Its key is the place as a string unless given.

## Fields of related rows

`field={[:customer, :name]}` reads a field of a related row: a path of
the row's associations (or keys of maps in it) to a field. Its key is
the path joined with dots (`"customer.name"`) and its label the path in
words ("Customer name") unless given. An Ecto source sorts, filters,
searches and groups by it in the database, with a join; a list follows
the path in each row. The column is read-only.

A path through several related rows (a `has_many` or `many_to_many`:
`[:tags, :name]`) is a column of many values: a list of the values of
the rows that are loaded. It does not sort or group; it filters by
"has any of" (`:in`), "has all of" (`:all_of`), "has none of"
(`:none_of`), "has none" (`:is_nil`) and "has some" (`:not_nil`). The
source says whether a path is of many values (an Ecto source from its
associations, a list from its rows); `many={true}` says so for a
source of your own.

## Editing

In a grid that edits, `values` lists the values of an `:enum` column,
which its editor offers (a schema's `Ecto.Enum` field has them unless
given); `decimals` shows a `:decimal` or `:number` column with that
many digits after the point, whatever the value has.

`editor` gives the column an editor of your own, in place of the one of
its type; see `t:editor/0` and the [Editors of your own](editors.md)
guide.

## Layout

`width` is the column's width in pixels, of its type unless given, and
at least `min_width` (48 unless given), the narrowest a person resizes
it to.

`grow` makes the column take the room the columns leave in a grid wider
than them, from its width up: a title or a description, rather than
empty room after the last column. `grow={2}` takes twice the share of a
`grow` column. A grid narrower than its columns scrolls sideways, and a
column that grows is its width there. A person who resizes the column
gives it their width, and it grows no more (in a view, too); see
`Masu.Layout`.

`hidden` starts the column hidden (a person shows it from the columns
menu), and `pinned={:start}` or `pinned={:end}` keeps it in view while
the others scroll sideways. See `Masu.Layout`.

## Totals

`aggregate={:sum}` totals the column in the row of each group and in a
row of totals at the bottom of the grid; see `Masu.Group`.

# `aggregate`

```elixir
@type aggregate() ::
  :sum | :avg | :min | :max | :count | :count_distinct | ([term()] -&gt; term())
```

How a column totals its values, in a group's row and the totals row:
`:sum`, `:avg`, `:min`, `:max`, `:count` (of the values that are not
`nil`), `:count_distinct`, or, for a list, a function of the rows.

# `editor`

```elixir
@type editor() ::
  {:form, (map() -&gt; Phoenix.LiveView.Rendered.t())} | {:js, String.t(), map()}
```

An editor of your own (`editor` on a `:col`):

  * a function component, of a form field: the grid renders it on the
    server, in a popover under the cell, and saves what its form posts
    (a component of Koyomi or Kotoba, a search of the server, any HEEx);
  * the name of an editor of the browser's, given to `configure` in the
    app's JavaScript (`{editors: {color: ...}}`), or `{name, options}`
    with options of it, which go to it as JSON.

# `t`

```elixir
@type t() :: %Masu.Column{
  aggregate: aggregate() | nil,
  align: :start | :center | :end,
  auto: map() | nil,
  creatable: boolean(),
  decimals: non_neg_integer() | nil,
  editable: boolean() | nil,
  editable_if: (term() -&gt; boolean()) | nil,
  editor: editor() | nil,
  field: atom() | String.t() | non_neg_integer() | [atom()] | nil,
  filter_ops: [Masu.Filter.op()] | nil,
  filterable: boolean(),
  grow: non_neg_integer(),
  hidden: boolean(),
  key: String.t(),
  label: String.t(),
  many: boolean() | nil,
  min_width: pos_integer(),
  pinned: :start | :end | nil,
  raw: boolean(),
  searchable: boolean(),
  slot: map() | nil,
  sortable: boolean(),
  type: type(),
  value: (term() -&gt; term()) | nil,
  values: [term()] | nil,
  width: pos_integer()
}
```

# `type`

```elixir
@type type() ::
  :text
  | :enum
  | :integer
  | :number
  | :decimal
  | :date
  | :datetime
  | :time
  | :boolean
```

# `editable?`

```elixir
@spec editable?(t()) :: boolean()
```

Whether a person may edit the column's cells: its grid edits (it has a
`changeset` or an `update`), the column names a field (not a `value`
function), and it does not say `editable={false}`. A column whose
`editable` is a function of the row edits the cells of the rows for
which it gives `true` (see `editable?/2`).

# `editable?`

```elixir
@spec editable?(t(), term()) :: boolean()
```

Whether a person may edit the column's cell of a row: the column edits
(`editable?/1`), and its `editable` function, if it has one, says so for
the row.

# `find`

```elixir
@spec find([t()], String.t()) :: t() | nil
```

Finds a column by its key.

# `get`

```elixir
@spec get(t(), term()) :: term()
```

The value of the column for a row.

# `infer`

```elixir
@spec infer([term()]) :: type()
```

The type of a column of these values: the type that every value which
is not `nil` has (an integer and a float make `:number`), else `:text`.

# `many?`

```elixir
@spec many?(t()) :: boolean()
```

Whether the column's value is a list: a path through several related rows.

# `new`

```elixir
@spec new(map() | keyword()) :: t()
```

Makes a column from the attributes of a `:col` slot (or a keyword list
or map of the same names).

`field` names the value of a row (a key of a map or a field of a
struct), or is a path to a field of a related row (`[:customer,
:name]`); `value`, a function of the row, computes it instead. `key`
names the column on the wire and in `Masu.Test`; it is the field as a
string unless given. `label` is the field in words unless given.

Raises `ArgumentError` for a column with neither `field` nor `value`,
or with an unknown type.

# `path?`

```elixir
@spec path?(t()) :: boolean()
```

Whether the column reads a field of a related row (`field={[:customer, :name]}`).

# `stored?`

```elixir
@spec stored?(t()) :: boolean()
```

Whether the column's values can be sorted and filtered by a database:
it reads a field, or a path to a field of a related row, not a function
of the row.

# `typed`

```elixir
@spec typed(t(), type()) :: t()
```

A column of `type={:auto}` made again with a type (`infer/1`); any
other column as it is.

# `types`

```elixir
@spec types() :: [type()]
```

The types a column can have.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
