# `Issho.Components`
[🔗](https://github.com/hattoriai/issho/blob/v0.4.0/lib/issho/components.ex#L2)

Issho's pieces of a page, in HEEx. Import them where your app imports
its own components (`html_helpers` in `MyAppWeb`), and use them as
yours, `<.people>` and `<.pointers>`:

    defp html_helpers do
      quote do
        import Issho.Components
        # ...
      end
    end

(`import Issho.Components, only: [pointers: 1]` when your app has a
component of the same name.) Style them with Issho's theme:
`issho.css`, and `issho-sumi.css` after it for the Sumi theme (in the
package's `priv/static`).

## The people of a room

One list for the page, whatever its components (editors, grids, a
canvas): each person and agent once, in their colour, the agents with
whom they act for, the viewers as viewers, and what each says they are
doing.

With a grant (`Issho.Live.grant/4`), the browser keeps it live (the
`IsshoPeople` hook of `Hooks` from `issho`, in your LiveSocket):

    <.people id="people" grant={@grant} />

In a LiveView that is in the room itself (`Issho.Live.join/4`), the
server renders it, from `Issho.Live.people/1`:

    <.people id="people" people={Issho.Live.people(@room)} self={@me} />

## The look

Issho's theme (`issho.css`) styles every piece through custom
properties: the avatars' size, shape, edge and initials, the badges,
the pointers' names (`--issho-avatar-radius`, `--issho-avatar-text`,
`--issho-pointer-radius`... see the file), and the palette of the
people with no colour of their own (`--issho-person-1` to `-8`). Set
them in your CSS to match your app. The classes and data attributes
are stable: leave the file out to style them all yourself. And draw a
person or a pointer with your own markup: the `:person` slot of a list
the server renders, or `configure({person, pointer})` of the `issho`
package for the live list and the pointers.

## The page as a live space

Each person's pointer over the page, and their clicks, in their
colour, with their name, over any part of the page: Issho's components
and plain HEEx alike (the `IsshoPointers` hook):

    <.pointers id="pointers" grant={@grant} />

A pointer is a point of an element, not of the screen, so each person
sees it on the same part of their own page, whatever its size and
scroll: the nearest element with `data-issho-anchor`, else with an
id. Over a component that says what is under a point (`point` in the
`issho` package: a canvas's point, a grid's cell), it is a place of
the component's document, drawn where that place is on each screen
(`locate`): the same cell in another order, the same point of a
canvas panned elsewhere. So one pointer goes from block to block of
the page, and into a block within a block. Off the screen, it is a
marker at its edge that scrolls to it. Inside an element with
`data-issho-cursor="own"` (a component that draws its people itself,
and says nothing of its points) the component takes over. Over an
element with `data-issho-private`, or a password field, the browser
shares nothing, nor while its window is in the background.
`share={false}` shows the others without sharing this person's
pointer.

A block of another room on the page (a canvas shared in its own room,
in a document) is that room's: its room draws its people there, and
this layer draws no one in it. Following someone who goes into it
follows them in its room too. See the "Building a component" guide.

## Agents moving

An agent goes where it works as a hand would (`Issho.Motion`): its
pointer glides there, in the time the agent says, to a place of a
document (its caret, a node, a cell) or an element of the page
(`data-issho-anchor`), and rings where it clicks. The pointers tell
the room what each browser shows (the page in the foreground, the
components on the screen, whether its person wants motion), so that an
agent moves only for someone who sees it.

How much a person wants to see others move is the app's to say, from
its own settings: `motion="reduced"` on the pointers, `setMotion` of
the `issho` package, or `data-issho-motion` on the page's root
element; else the browser's "reduce motion" setting. `motion_switch/1`
is a switch for it, to place and style as you like, or not at all.

## Following

A click on someone in the people list follows them (`follow={false}`
for a list that does not): on a page whose blocks share the room,
each block shows where they are (the grid their view, the canvas their
viewport, the text their caret) and the page scrolls to them. A link
or a button with `data-issho-follow` that they click is clicked for
those who follow them too: the agent clicks "Edit together", and its
person, following it, is in the editor. `follow_agents="on_behalf"` on
the pointers follows an agent at work for this person as soon as it
moves (unless they stopped following it on this page). Going one's own
way (a scroll, a click, a key) stops following. A block alone in its room follows
within itself, by the same contract (`follow` of the `issho` package,
or the `issho:following` event and `data-issho-following` on the
page's root element for a block's own script).

# `agents`

The agents at work in a room (`Issho.Work`): each one's task, its steps
and how far it is, whom it works for, and whether someone paused or
stopped it, live, from the room of a grant (the `IsshoAgents` hook of
`Hooks`). It is hidden while no agent has a task.

    <.agents id="agents" grant={@grant} />

The person an agent works for has Pause, Resume and Stop. The others
have them too with `anyone` (when your policy allows them
`:interrupt`; it decides either way). A paused or stopped agent
changes nothing more in the room: its next tool says who paused or
stopped it, and what to do.

## Attributes

* `id` (`:string`) (required)
* `grant` (`:map`) (required) - a grant of the room, from `Issho.Live.grant/4`.
* `anyone` (`:boolean`) - whether to offer Pause and Stop for the agents of others too. Defaults to `false`.
* `label` (`:string`) - Defaults to `"Agents at work"`.
* `class` (`:string`) - Defaults to `nil`.
* Global attributes are accepted.

# `comments`

The threads of comments of a room (`Issho.Comments`): a panel of the
open and resolved threads, with their comments, replies, reactions,
edits and a composer, and a mark at each thread's place on the page,
where its component locates it (`locate` of the `issho` package), as
the pointers are. Comment on what you selected in a component, or on
the page. In a LiveView that is in the room (`Issho.Live.join/4`), with
the `IsshoComments` hook of `Hooks` in your LiveSocket:

    <.comments id="comments" room={@room} />

Say what a thread's place is with the `:place` slot (it gets the place,
`nil` for the page, and the default words):

    <.comments id="comments" room={@room}>
      <:place :let={%{place: place, default: default}}>
        {MyApp.Places.label(place) || default}
      </:place>
    </.comments>

Typing @ in a comment opens a list of the people of the room and of
`mentionable` (people who are not here: `[%{key: "user:42", name:
"Ada"}]`), filtered as one types: the arrows, Enter or Tab, Escape.
Whom one picks is mentioned, and hears of it (`"comment.created"`'s
`mentions`); an @ and a name typed in full mentions them too.

Write the comments with an input of your own, a Kotoba editor with its
own mentions menu, say, with the `:input` slot (it gets the input's
`id`, `name` (`"body"`), `value`, `placeholder`, and a form `field` of
them) and `read`, a function of the composer's form params that gives
the comment's words and the keys of whom they mention:

    <.comments id="comments" room={@room} read={&MyApp.Comments.read/1}>
      <:input :let={input}>
        <Kotoba.Components.kotoba field={input.field} features={~w(bold italic links mentions)} prompts={@people_prompts} />
      </:input>
    </.comments>

    def read(%{"body" => json}) do
      {:ok, content} = Kotoba.Content.cast(json)
      {:ok, doc} = Kotoba.Document.parse(content.doc)
      %{body: content.text, mentions: Enum.map(Kotoba.Document.mentions(doc), & &1.id)}
    end

(Kotoba's mention ids are then the actors' keys.) A comment keeps its
words as text.

## Attributes

* `id` (`:string`) (required)
* `room` (`:string`) (required) - the room, which the LiveView has joined.
* `mentionable` (`:list`) - people one may mention who are not in the room. Defaults to `[]`.
* `marks` (`:boolean`) - whether the page marks each thread's place. Defaults to `true`.
* `class` (`:string`) - Defaults to `nil`.
* `read` (`:any`) - with `:input`, a function of a composer's form params that gives `%{body: text, mentions: [key]}`. Defaults to `nil`.
## Slots

* `place` - a thread's place, in words: gets `%{place: place, default: words}`.
* `input` - the composers' input, of your own (a Kotoba editor): gets `%{id: id, name: "body", value: text, placeholder: words}`; with `read`.

# `motion_switch`

A switch for how much this person wants to see others move: pressed,
pointers glide, carets travel and selections sweep; not, they are
there at once. Optional: place it where your settings are, style it
(`.issho-motion-switch`, `aria-pressed`), or use your own control with
`setMotion` of the `issho` package (the `IsshoMotion` hook).

## Attributes

* `id` (`:string`) (required)
* `label` (`:string`) - Defaults to `"Show others moving"`.
* `event` (`:string`) - an event pushed to the LiveView with `%{"motion" => "full" | "reduced"}` when it changes, to keep it in the person's settings. Defaults to `nil`.
* `class` (`:string`) - Defaults to `nil`.
* Global attributes are accepted.

# `people`

## Attributes

* `id` (`:string`) (required)
* `grant` (`:map`) - a grant of the room, from `Issho.Live.grant/4`. Defaults to `nil`.
* `people` (`:map`) - the room's people, from `Issho.Live.people/1` (without a grant). Defaults to `nil`.
* `self` (`:string`) - this actor's key, with `people`. Defaults to `nil`.
* `max` (`:integer`) - how many avatars before "+N". Defaults to `8`.
* `label` (`:string`) - Defaults to `"People here"`.
* `follow` (`:boolean`) - with a grant: whether a click on someone follows them. Defaults to `true`.
* `class` (`:string`) - Defaults to `nil`.
* Global attributes are accepted.
## Slots

* `person` - with `people`: draws a person in place of the avatar (`:let`: `key`, `name`, `avatar`, `initials`, `color`, `kind`, `you`); with a grant, the browser draws them: `configure({person})` of the `issho` package.

# `pointers`

The pointers of the room's people over the page: a layer over the viewport.
## Attributes

* `id` (`:string`) (required)
* `grant` (`:map`) (required) - a grant of the room, from `Issho.Live.grant/4`.
* `share` (`:boolean`) - whether this person's pointer is shared. Defaults to `true`.
* `motion` (`:string`) - how much this person wants to see others move, from the app's settings; the browser's setting unless given. Defaults to `nil`. Must be one of `nil`, `"full"`, or `"reduced"`.
* `follow_agents` (`:string`) - `"on_behalf"`: an agent at work for this person is followed when it starts to move, unless they stopped following it. Defaults to `nil`. Must be one of `nil`, or `"on_behalf"`.
* `class` (`:string`) - Defaults to `nil`.
* Global attributes are accepted.

---

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