# `Plushie.ScopedId`
[🔗](https://github.com/plushie-ui/plushie-elixir/blob/v0.7.2/lib/plushie/scoped_id.ex#L1)

Structured representation of a scoped widget ID.

Wire IDs use the canonical format `window#scope/path/id`:

    "main#form/email"     widget in window
    "main#users/u1"       table row
    "main"                the window itself

`ScopedId` parses this format into its components for
programmatic manipulation. Event structs use flat fields
(`id`, `scope`, `window`) for ergonomic pattern matching;
use `from_event/1` to convert when the structured form
is needed.

## Examples

    iex> Plushie.ScopedId.parse("main#sidebar/form/email")
    %Plushie.ScopedId{
      id: "email",
      scope: ["form", "sidebar"],
      window_id: "main",
      full: "main#sidebar/form/email"
    }

    iex> Plushie.ScopedId.parse("main")
    %Plushie.ScopedId{
      id: "main",
      scope: [],
      window_id: nil,
      full: "main"
    }

    iex> Plushie.ScopedId.parse("main#email")
    %Plushie.ScopedId{
      id: "email",
      scope: [],
      window_id: "main",
      full: "main#email"
    }

# `t`

```elixir
@type t() :: %Plushie.ScopedId{
  full: String.t(),
  id: String.t(),
  scope: [String.t()],
  window_id: String.t() | nil
}
```

# `from_event`

```elixir
@spec from_event(event :: map()) :: t()
```

Build a ScopedId from event fields.

    from_event(%WidgetEvent{id: "email", scope: ["form"], window_id: "main"})

# `in_window?`

```elixir
@spec in_window?(sid :: t(), window :: String.t()) :: boolean()
```

True if the ID is in the given window.

# `matches_local?`

```elixir
@spec matches_local?(sid :: t(), name :: String.t()) :: boolean()
```

True if the local ID matches the given name.

# `matches_scope?`

```elixir
@spec matches_scope?(sid :: t(), ancestor :: String.t()) :: boolean()
```

True if the ancestor appears anywhere in the scope chain.

# `parent`

```elixir
@spec parent(sid :: t()) :: String.t() | nil
```

Returns the immediate parent (nearest ancestor), or nil.

# `parse`

```elixir
@spec parse(canonical :: String.t()) :: t()
```

Parse a canonical wire ID into its components.

The `#` separates the window from the widget path. The `/`
separates scope segments within the path. The last segment
is the local `id`.

    parse("main#sidebar/form/email")
    #=> %ScopedId{id: "email", scope: ["form", "sidebar"], window_id: "main", ...}

    parse("form/email")
    #=> %ScopedId{id: "email", scope: ["form"], window_id: nil, ...}

    parse("email")
    #=> %ScopedId{id: "email", scope: [], window_id: nil, ...}

Edge cases: `"main#"` produces `id: ""` (the window itself with
no widget path). `"#foo"` is treated as bare id `"#foo"` (empty
window part is ignored). Empty string produces `id: ""`.

---

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