# `Drafter.Event.Object`
[🔗](https://github.com/jaman/drafter/blob/main/lib/drafter/event/event_object.ex#L1)

Rich event struct with DOM-like three-phase dispatch and propagation control.

An `Event.Object` carries an event's type and payload alongside phase tracking,
propagation control, and a monotonic timestamp. Events travel through
three phases: `:capture` (root → target), `:target`, then `:bubble`
(target → root). Any handler may call `prevent_default/1`,
`stop_propagation/1`, or `stop_immediate_propagation/1` to modify how
the event continues.

`from_tuple/1` builds one from a `Drafter.Event` tuple and `to_tuple/1` returns
that form, for code that pattern-matches on raw tuples. The round trip is not
lossless: phase, target and propagation flags are dropped by `to_tuple/1`.

Struct fields:

- `:type` — event type atom (`:key`, `:char`, `:mouse`, `:focus`, `:blur`,
  `:focus_in`, `:focus_out`, `:mount`, `:unmount`, `:show`, `:hide`,
  `:load`, `:custom`, `:resize`, `:timer`)
- `:data` — event payload, format varies by type
- `:target` — widget id that is the final event target
- `:current_target` — widget id currently processing the event
- `:phase` — current dispatch phase (`:capture`, `:target`, or `:bubble`)
- `:default_prevented` — true after `prevent_default/1`
- `:propagation_stopped` — true after `stop_propagation/1` or `stop_immediate_propagation/1`
- `:immediate_propagation_stopped` — true after `stop_immediate_propagation/1`
- `:timestamp` — `System.monotonic_time(:millisecond)` at creation

# `event_type`

```elixir
@type event_type() ::
  :key
  | :char
  | :mouse
  | :focus
  | :blur
  | :focus_in
  | :focus_out
  | :mount
  | :unmount
  | :show
  | :hide
  | :load
  | :custom
  | :resize
  | :timer
```

# `phase`

```elixir
@type phase() :: :capture | :target | :bubble
```

# `t`

```elixir
@type t() :: %Drafter.Event.Object{
  current_target: atom() | String.t() | nil,
  data: term(),
  default_prevented: boolean(),
  immediate_propagation_stopped: boolean(),
  phase: phase(),
  propagation_stopped: boolean(),
  target: atom() | String.t() | nil,
  timestamp: integer() | nil,
  type: event_type()
}
```

# `from_tuple`

```elixir
@spec from_tuple(tuple() | atom()) :: t()
```

Wrap an event tuple in an event object.

Recognised tuples are the ones listed in `Drafter.Event`; a bare atom becomes an
event of that type with `nil` data, any other two-element tuple becomes an event
whose type is its first element, and anything else becomes `:custom` carrying the
whole term. `{:key, key, modifiers}` is folded into `%{key: key, modifiers: mods}`
data and `{:resize, {w, h}}` into `%{width: w, height: h}`, both of which
`to_tuple/1` restores.

The phase starts at `:bubble` and the timestamp is taken now. `:target` and
`:current_target` are left `nil`, so a caller that needs them must set them
afterwards.

## Examples

    iex> event = Drafter.Event.Object.from_tuple({:key, :enter})
    iex> {event.type, event.data, event.phase}
    {:key, :enter, :bubble}

    iex> Drafter.Event.Object.from_tuple({:key, :a, [:ctrl]}).data
    %{key: :a, modifiers: [:ctrl]}

    iex> Drafter.Event.Object.from_tuple({:resize, {80, 24}}).data
    %{width: 80, height: 24}

    iex> event = Drafter.Event.Object.from_tuple(:tick)
    iex> {event.type, event.data}
    {:tick, nil}

    iex> event = Drafter.Event.Object.from_tuple({:scroll, :up})
    iex> {event.type, event.data}
    {:scroll, :up}

    iex> event = Drafter.Event.Object.from_tuple({:a, :b, :c, :d})
    iex> {event.type, event.data}
    {:custom, {:a, :b, :c, :d}}

# `new`

```elixir
@spec new(event_type(), term(), keyword()) :: t()
```

Build an event object of `type` carrying `data`.

Options:

  * `:target` — widget id the event is aimed at, default `nil`
  * `:current_target` — widget id currently handling it, default `nil`
  * `:phase` — `:capture`, `:target` or `:bubble`, default `:bubble`
  * `:timestamp` — default `System.monotonic_time(:millisecond)`

The propagation flags always start `false`. Nothing is validated: `type` is
stored as given, even when it is not one of `t:event_type/0`.

## Examples

    iex> Drafter.Event.Object.new(:key, :enter, timestamp: 0)
    %Drafter.Event.Object{
      type: :key,
      data: :enter,
      target: nil,
      current_target: nil,
      phase: :bubble,
      default_prevented: false,
      propagation_stopped: false,
      immediate_propagation_stopped: false,
      timestamp: 0
    }

    iex> event = Drafter.Event.Object.new(:key, :enter, target: :save, phase: :capture)
    iex> {event.target, event.phase}
    {:save, :capture}

# `prevent_default`

```elixir
@spec prevent_default(t()) :: t()
```

Mark the event's default action as prevented.

Does not stop the event travelling; handlers further along still see it and can
read `:default_prevented`.

## Examples

    iex> Drafter.Event.Object.new(:key, :enter)
    ...> |> Drafter.Event.Object.prevent_default()
    ...> |> Map.fetch!(:default_prevented)
    true

# `stop_immediate_propagation`

```elixir
@spec stop_immediate_propagation(t()) :: t()
```

Stop the event travelling further, including to other handlers on the current widget.

Sets both `:immediate_propagation_stopped` and `:propagation_stopped`.

## Examples

    iex> event = Drafter.Event.Object.new(:key, :enter)
    iex> event = Drafter.Event.Object.stop_immediate_propagation(event)
    iex> {event.propagation_stopped, event.immediate_propagation_stopped}
    {true, true}

# `stop_propagation`

```elixir
@spec stop_propagation(t()) :: t()
```

Stop the event travelling to the next widget in the dispatch path.

Handlers already reached on the current widget are unaffected, and
`:immediate_propagation_stopped` stays `false`.

## Examples

    iex> event = Drafter.Event.Object.stop_propagation(Drafter.Event.Object.new(:key, :enter))
    iex> {event.propagation_stopped, event.immediate_propagation_stopped}
    {true, false}

# `to_tuple`

```elixir
@spec to_tuple(t()) :: tuple() | atom()
```

Unwrap an event object back to the event tuple form.

The inverse of `from_tuple/1` for every tuple it recognises. Phase, target and
propagation flags are dropped, so a caller that needs them must read them from
the object before converting.

`:focus`, `:blur`, `:focus_in` and `:focus_out` with `nil` data give the
one-element tuple `{:focus}` and friends. An event of any type this function does
not name gives the bare type atom when its data is `nil`, and `{type, data}`
otherwise. A named type with `nil` data still gives a tuple: `:key` with `nil`
data is `{:key, nil}`, not `:key`.

## Examples

    iex> Drafter.Event.Object.new(:key, %{key: :a, modifiers: [:ctrl]})
    ...> |> Drafter.Event.Object.to_tuple()
    {:key, :a, [:ctrl]}

    iex> Drafter.Event.Object.new(:resize, %{width: 80, height: 24})
    ...> |> Drafter.Event.Object.to_tuple()
    {:resize, {80, 24}}

    iex> Drafter.Event.Object.to_tuple(Drafter.Event.Object.new(:focus, nil))
    {:focus}

    iex> Drafter.Event.Object.to_tuple(Drafter.Event.Object.new(:custom, nil))
    :custom

    iex> Drafter.Event.Object.to_tuple(Drafter.Event.Object.new(:key, nil))
    {:key, nil}

---

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