What may go where: the same rules in the browser, as people drag (a drop
that breaks them is not offered), and on the server, as Narabe.Ecto
applies a move (one that breaks them is refused).
rules = Narabe.Rules.new(nest: true, max_depth: 4, leaves: ["file"])Options:
:nest- whether an item may hold others (a tree). A list's and a board's items do not nest (false, the default for them).:max_depth- the deepest level an item may be at, the top level being 1. A moved item's own children count: a page with two levels under it cannot go to the deepest level.:leaves- the kinds of items that hold nothing (["file"]).:accepts- which kinds of items a kind of parent takes:%{"section" => ["page"], "root" => ["section"]}."root"is the top level; a board's columns are parents of their kind ("column"). A kind with no entry takes anything.
An item's kind is its kind (data-kind in the browser, the :kind
field of Narabe.Ecto's schema); without one it is "item".
The browser and the server give the same answers, held by
test/fixtures/rules.json.
Summary
Types
A move, as the rules see it: the kinds of the items, the kind of their new
parent (nil at the top level) and its depth (0 for the top level or a
board's column), the height of the tallest item's subtree (1 for an item
with no children), and whether the parent is one of the items or under
one.
Why a move is refused.
Functions
Checks a move: :ok, or {:error, reason}.
Rules from options (see the moduledoc), the rules themselves, or those
of a schema with use Narabe.Ordered (so that a component and
Narabe.Ecto have the same: <.tree rules={Page} ...>).
The rules as the browser takes them (data-config), with keys in camel
case and nothing that is a default.
Types
@type move() :: %{ kinds: [String.t()], parent_kind: String.t() | nil, parent_depth: non_neg_integer(), height: pos_integer(), into_itself: boolean() }
A move, as the rules see it: the kinds of the items, the kind of their new
parent (nil at the top level) and its depth (0 for the top level or a
board's column), the height of the tallest item's subtree (1 for an item
with no children), and whether the parent is one of the items or under
one.
@type reason() :: :into_itself | :not_accepted | :too_deep
Why a move is refused.
@type t() :: %Narabe.Rules{ accepts: %{required(String.t()) => [String.t()]} | nil, leaves: [String.t()], max_depth: pos_integer() | nil, nest: boolean() }
Functions
Checks a move: :ok, or {:error, reason}.
iex> rules = Narabe.Rules.new(nest: true, max_depth: 2)
iex> Narabe.Rules.check(rules, %{kinds: ["page"], parent_kind: "page", parent_depth: 1, height: 1, into_itself: false})
:ok
iex> Narabe.Rules.check(rules, %{kinds: ["page"], parent_kind: "page", parent_depth: 1, height: 2, into_itself: false})
{:error, :too_deep}
Rules from options (see the moduledoc), the rules themselves, or those
of a schema with use Narabe.Ordered (so that a component and
Narabe.Ecto have the same: <.tree rules={Page} ...>).
iex> Narabe.Rules.new(nest: true, max_depth: 3)
%Narabe.Rules{nest: true, max_depth: 3, leaves: [], accepts: nil}
The rules as the browser takes them (data-config), with keys in camel
case and nothing that is a default.
iex> Narabe.Rules.to_client(Narabe.Rules.new(nest: true, max_depth: 3))
%{"nest" => true, "maxDepth" => 3}