Where a grid's rows come from.
A grid never holds all its rows. It asks its source for the rows of
its query (prepare/3), how many there are (count/1), and the rows
the browser shows (rows/3): a block at a time, as the person scrolls.
Masu has two sources:
Masu.Source.List, for rows the LiveView already has (rows={@users});Masu.Source.Ecto, for an Ecto query or schema (source={User} repo={Repo}): it sorts, filters, searches and counts in the database, and reads only the rows on the screen.
A source of your own is a module with these callbacks, given as
source={{MySource, opts}}; the grid calls init/2 with the opts.
Summary
Callbacks
Takes changes into the source's rows (see Masu.Live): {:upsert, row}
and {:delete, row_or_id}, oldest first, with the grid's function that
gives a row's id. The grid calls prepare/3 after it.
The source's columns, for a grid with no :col slots: a query tool's,
whose columns are known when its query runs. Each is a key (a string),
or a keyword list or map of a :col's attributes (key, label,
type, field, width...). A column without a field reads the row
at its place: its rows are lists of values in the columns' order. A
column without a type is :auto: the grid learns it from the values
(see Masu.Column).
How many rows the query has: a number, {:at_least, n} when the
source knows only that there are at least n (a search engine's
"10,000+"), or :unknown. A grid whose source does not know scrolls
over the rows it read, and a block that comes back short says where
they end (see the Export, selection and large tables guide).
The rows with the given ids, among the rows the source may show (its query without the person's filters): the rows a person edits. A row that is not there is not given, so that no one edits a row the grid would not show them.
The operators of Masu.Filter the source filters a column by. Without
it, every operator of the column's type. A source that is not a
database Masu translates itself (an API, a search engine, another
query tool) gives those it runs: the filter menu, the chips, the menu
of a cell, a view's params (a link, a saved view) and Masu.Test
offer only them, and the server refuses the others.
Starts the source from what the grid was given.
The place (from 0) of the row with an id in the rows of the query, in
their order, or nil when the query does not have it: where another
person's cell is in this person's order (Masu.Presence).
For a tree (Masu.Tree): the rows from offset, as rows/3 gives
them, each with where it is in the tree (Masu.Tree.node_info/0), or
nil when the source is not a tree. The rows of a tree are the roots
and the children of the rows that are open (Masu.Query's expanded),
in order.
Applies a query: the rows that count/1 counts and rows/3 reads are
from now on those of the query, in its order. The query is checked
against the columns (Masu.Query).
The rows from offset (from 0), at most limit of them; or
{rows, count}, when the source learned the count of the query with
them (a total_count of the same request), which the grid takes; or
{:error, reason}, which a grid with load={:async} shows (see
Masu.Source.Error).
Every row of a query but those of except (ids, as the browser has
them): the person selected every row (Masu.Live.selected/1). Gives
their ids, or, for a database, a query of them.
For a tree: the ids of the siblings of a row (it among them) that have
children: the rows that open with the row's level (*).
Whether the source can sort and filter by a column. A database can sort only by a field it stores, not by a function of the row; the grid makes such a column one that does not sort or filter.
The totals of the rows of the query (Masu.Group): the value of each
column with an aggregate, by its key.
The values of a column among the rows of a query, with how many rows
have each, the most frequent first: what a column's filter menu offers.
search narrows them to the values that contain it, without case.
nil counts as a value: the rows with no value.
Functions
The source for what a grid was given: a list, an Ecto query or schema
(with the :repo option), or {module, opts}.
Types
Callbacks
@callback change(state(), [Masu.Live.change()], (term() -> term())) :: state()
Takes changes into the source's rows (see Masu.Live): {:upsert, row}
and {:delete, row_or_id}, oldest first, with the grid's function that
gives a row's id. The grid calls prepare/3 after it.
A source that reads its rows anew for each query (as a database does) does not need it: without it, the grid asks the query again.
The source's columns, for a grid with no :col slots: a query tool's,
whose columns are known when its query runs. Each is a key (a string),
or a keyword list or map of a :col's attributes (key, label,
type, field, width...). A column without a field reads the row
at its place: its rows are lists of values in the columns' order. A
column without a type is :auto: the grid learns it from the values
(see Masu.Column).
@callback count(state()) :: non_neg_integer() | {:at_least, non_neg_integer()} | :unknown
How many rows the query has: a number, {:at_least, n} when the
source knows only that there are at least n (a search engine's
"10,000+"), or :unknown. A grid whose source does not know scrolls
over the rows it read, and a block that comes back short says where
they end (see the Export, selection and large tables guide).
The rows with the given ids, among the rows the source may show (its query without the person's filters): the rows a person edits. A row that is not there is not given, so that no one edits a row the grid would not show them.
@callback filters(state(), Masu.Column.t()) :: [Masu.Filter.op()]
The operators of Masu.Filter the source filters a column by. Without
it, every operator of the column's type. A source that is not a
database Masu translates itself (an API, a search engine, another
query tool) gives those it runs: the filter menu, the chips, the menu
of a cell, a view's params (a link, a saved view) and Masu.Test
offer only them, and the server refuses the others.
Masu runs :between as :gte and :lte for a source that has those
and not it. [] is a column the source does not filter by. See the
Sources guide for what each operator means.
Starts the source from what the grid was given.
@callback locate(state(), id :: term(), (term() -> term())) :: non_neg_integer() | nil
The place (from 0) of the row with an id in the rows of the query, in
their order, or nil when the query does not have it: where another
person's cell is in this person's order (Masu.Presence).
@callback nodes(state(), offset :: non_neg_integer(), limit :: pos_integer()) :: [{term(), Masu.Tree.node_info()}] | nil
For a tree (Masu.Tree): the rows from offset, as rows/3 gives
them, each with where it is in the tree (Masu.Tree.node_info/0), or
nil when the source is not a tree. The rows of a tree are the roots
and the children of the rows that are open (Masu.Query's expanded),
in order.
@callback prepare(state(), Masu.Query.t(), [Masu.Column.t()]) :: state()
Applies a query: the rows that count/1 counts and rows/3 reads are
from now on those of the query, in its order. The query is checked
against the columns (Masu.Query).
@callback rows(state(), offset :: non_neg_integer(), limit :: pos_integer()) :: [term()] | {[term()], non_neg_integer()} | {:error, term()}
The rows from offset (from 0), at most limit of them; or
{rows, count}, when the source learned the count of the query with
them (a total_count of the same request), which the grid takes; or
{:error, reason}, which a grid with load={:async} shows (see
Masu.Source.Error).
@callback select_all( state(), Masu.Query.t(), [Masu.Column.t()], except :: [term()], (term() -> term()) ) :: {:ids, [term()]} | {:query, term()}
Every row of a query but those of except (ids, as the browser has
them): the person selected every row (Masu.Live.selected/1). Gives
their ids, or, for a database, a query of them.
For a tree: the ids of the siblings of a row (it among them) that have
children: the rows that open with the row's level (*).
@callback supports?(state(), Masu.Column.t()) :: boolean()
Whether the source can sort and filter by a column. A database can sort only by a field it stores, not by a function of the row; the grid makes such a column one that does not sort or filter.
The totals of the rows of the query (Masu.Group): the value of each
column with an aggregate, by its key.
@callback values(state(), Masu.Column.t(), Masu.Query.t(), [Masu.Column.t()], keyword()) :: [ {term(), non_neg_integer()} ]
The values of a column among the rows of a query, with how many rows
have each, the most frequent first: what a column's filter menu offers.
search narrows them to the values that contain it, without case.
nil counts as a value: the rows with no value.
A source without it offers no values: the menu then filters by what the person types.