# `ElementorexCms.Block`
[🔗](https://github.com/rocket4ce/elementorex_cms/blob/v0.1.0/lib/elementorex_cms/block.ex#L1)

Defines a block: a component that can be placed in a document, configured in
the editor and rendered on your site.

    defmodule MyAppWeb.Blocks.Pricing do
      use ElementorexCms.Block,
        type: "pricing",
        label: "Pricing",
        icon: "currency-dollar",
        category: :components,
        description: "Plan with price and call to action",
        keywords: ~w(price plan precio)

      prop :plan, :string, default: "Pro", label: "Plan", inline: true
      prop :price, :string, default: "$9.990", label: "Price"
      prop :featured, :boolean, default: false, label: "Featured"

      def render(assigns) do
        ~H"""
        <div {root(assigns, class: ["card bg-base-100 p-6", @featured && "border-2 border-primary"])}>
          <h3 class="text-xl font-bold" {inline(assigns, :plan)}>{@plan}</h3>
          <p class="text-4xl font-black">{@price}</p>
        </div>
        """
      end
    end

Register it with `config :elementorex_cms, blocks: [MyAppWeb.Blocks.Pricing]`
(see `ElementorexCms.Registry`), or generate one with `mix elementorex.gen.block`.

## Options of `use`

  * `:type` (required) - stable identifier stored in documents (`"pricing"`)
  * `:label`, `:description`, `:keywords` - shown and searched in the palette
  * `:icon` - Heroicons name for the palette tile
  * `:category` - palette section: `:layout`, `:basic`, `:components`,
    `:media`, `:advanced` or your own atom
  * `:container` - `true` if the block holds child blocks
  * `:children` - types allowed as children of a container (default: any)
  * `:parents` - types this block may be placed in (default: any container)
  * `:palette` - `false` to hide the block from the palette
  * `:style` - style of new nodes, e.g. `%{"desktop" => %{"padding-top" => "24px"}}`

## Rendering

`render/1` receives every prop as an assign (with its default when missing)
plus:

  * `@node` - the node map (`"id"`, `"props"`, `"style"`, `"attrs"`, `"children"`)
  * `@ctx` - the `ElementorexCms.Renderer.Context` (`mode` is `:editor` on the
    canvas and `:public` on the site; `data` holds what the host passed)

The root element must spread `root/2`, which adds the node id (the CSS of the
node is scoped to it), the classes and id from the *Advanced* tab and the
attributes the editor needs. Containers render their children with
`<.children node={@node} ctx={@ctx} />`. See `ElementorexCms.Block.Helpers`.

# `default_children`

```elixir
@callback default_children() :: [map()]
```

Children of a new container, e.g. the two columns of a grid. Nodes are built
with `ElementorexCms.Document.node/3`.

# `render`

```elixir
@callback render(assigns :: map()) :: Phoenix.LiveView.Rendered.t()
```

Renders the block.

# `assigns`

```elixir
@spec assigns(module(), map()) :: map()
```

Props as assigns for `render/1`: atom keys, stored values cast again (so a
document edited by hand cannot inject anything) and defaults for the rest.

# `block?`

```elixir
@spec block?(module()) :: boolean()
```

Whether `module` is a block (defines `use ElementorexCms.Block`).

# `cast_props`

```elixir
@spec cast_props(module(), map()) :: map()
```

Casts a map of props (string keys, as stored) with the block's prop
definitions: known props are cast and sanitized, unknown keys are dropped and
missing ones are left out (their default applies when rendering).

# `default_props`

```elixir
@spec default_props(module()) :: map()
```

Every prop of `block` with its default value (string keys). Text defaults
are translated to the current locale (`ElementorexCms.I18n`), so a block
added from a Spanish editor starts with Spanish placeholder text.

# `find_prop`

```elixir
@spec find_prop(module(), atom() | String.t()) :: ElementorexCms.Block.Prop.t() | nil
```

The prop of `block` with the given name (atom or string), or `nil`.

# `prop`
*macro* 

Declares a prop of the block. See `ElementorexCms.Block.Prop` for the types
and options.

    prop :title, :string, default: "Hello", inline: true
    prop :size, :select, options: [{"Small", "sm"}, {"Large", "lg"}], default: "sm"
    prop :items, :list, fields: [{:title, :string, default: "Item"}], item_label: :title

---

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