Compos With Componentsing

Learn how to combine simple graphics to generate complex shapes.

We’ll use the <Point> and <Canvas> components to render more complex graphics. We’ll roll up a collection of points with common colors and names into shapes represented by a <Shape> component. Then, we’ll assemble shapes to display the palette of pentominoes for a game. We’ll also use a single rectangular shape of a given size to represent the game’s puzzle board. When we’re done, the design of our components will look something like this:

The <Board> component contains a <Canvas> that renders a single <Shape> depicting our puzzle board. It also renders the <Palette> containing a <Canvas> to display the set of <Shape> components representing the game’s pentominoes. Let’s start building out this structure now.

Rendering shapes with multiple points

First up, we start by building a <Shape> component that knows how to render the list of points that make up a given shape. To do so, we create a new file, pento/lib/pento_web/live/pento/shape.ex and define our stateless component like this:

Press + to interact
defmodule PentoWeb.Pento.Shape do
use Surface.Component
alias PentoWeb.Pento.Point

Then, we declare the points, fill, and name props in pento/lib/pento_web/live/pento/shape.ex, that our <Shape> needs:

Press + to interact
prop points, :list
prop fill, :string
prop name, :string

Each shape will have a :list of points, a shape name of type :string, and a fill of type :string representing the color. Like the core Shape module, a <Shape> component has a name, color, and collection of points. The fill property is a little different than the color attribute in the core Shape struct though. We’ll translate the color field from the core struct into HTML-friendly hex codes in the PentoWeb.Pento.Colors helper module. To do so, we create pento/lib/pento_web/live/pento/colors.ex and add the following code like this:

Press + to interact
defmodule PentoWeb.Pento.Colors do
def color(c), do: color(c, false)
def color(_color, true), do: "#B86EF0"
def color(:green, _active), do: "#8BBF57"
def color(:dark_green, _active), do: "#689042"
def color(:light_green, _active), do: "#C1D6AC"
def color(:orange, _active), do: "#B97328"
def color(:dark_orange, _active), do: "#8D571E"
def color(:light_orange, _active), do: "#F4CCA1"
def color(:gray, _active), do: "#848386"
def color(:dark_gray, _active), do: "#5A595A"
def color(:light_gray, _active), do: "#B1B1B1"
def color(:blue, _active), do: "#83C7CE"
def color(:dark_blue, _active), do: "#63969B"
def color(:light_blue, _active), do: "#B9D7DA"
def color(:purple, _active), do: "#240054"
end

The pentominoes all have their own color mappings. In addition, a user will place one pentomino on the board at a time, and later we’ll apply a highlighted color to this active shape.

The Colors module ...