empowered.house ↗

intelligence/ask.ex

Ask

A question about the company, answered in plain words with the sources it used.

Use it
# in the module that renders the template
use EHVisuals

<div class="h-96 rounded-xl border border-border">
  <.intelligence_ask animated />
</div>
default
isometric
custom question · two lines
hover · no glow
light surface
isometric · fade out · still

Props

Prop Type Default What it does
question string "Why did returns go up in March?"
answer list ["Two carriers moved their cut-off to 14:00,", "so late orders missed the truck. 61% of the", "extra returns were late deliveries."] Up to three lines of about 44 characters.
sources list ["Wiki · Returns", "Table · orders", "Meeting · 12 Mar"] Up to three source chips.
placeholder string "Ask about your company"
Frame props, shared by every composition
Prop Type Default What it does
animated boolean false Turns motion on. Without it the composition renders in its final, still state.
trigger "mount" | "in-view" | "in-view-repeat" "in-view" When the entrance plays: on page load, once when scrolled into view, or every time it comes back into view.
hover boolean false Enables the hover micro-interactions the composition supports.
glow boolean true The warm incandescent halo behind the composition.
fade_out boolean false Fades the bottom edge into the surface underneath.
isometric boolean false Tilts the composition into the isometric plane of the platform stack.
theme "dark" | "light" "dark" `dark` for the site canvas, `light` for the cream reversed surface.
class string nil Extra classes for the frame.
rest global — Any other HTML attribute, set on the frame.

Source

Two files. Copy them into your app if you would rather own the code than depend on the library.

lib/eh_visuals/intelligence/ask.ex
ask.ex
defmodule EHVisuals.Intelligence.Ask do
  @moduledoc """
  A question about the company and an answer that names its sources.
  """

  use EHVisuals.Composition,
    name: "intelligence/ask",
    title: "Ask",
    description: "A question about the company, answered in plain words with the sources it used."

  @answer [
    "Two carriers moved their cut-off to 14:00,",
    "so late orders missed the truck. 61% of the",
    "extra returns were late deliveries."
  ]

  attr(:question, :string, default: "Why did returns go up in March?")
  attr(:answer, :list, default: @answer, doc: "Up to three lines of about 44 characters.")

  attr(:sources, :list,
    default: ["Wiki · Returns", "Table · orders", "Meeting · 12 Mar"],
    doc: "Up to three source chips."
  )

  attr(:placeholder, :string, default: "Ask about your company")
  attr(:label, :string, default: "A question about returns answered with three cited sources")
  frame_attrs()

  @spec intelligence_ask(map()) :: Phoenix.LiveView.Rendered.t()
  def intelligence_ask(assigns) do
    lines = assigns.answer |> Enum.take(3) |> Enum.with_index()
    chips_y = 78 + length(lines) * 19 + 2
    bubble_w = min(300, 30 + String.length(assigns.question) * 6.3)

    assigns =
      assigns
      |> assign(:lines, lines)
      |> assign(:bubble_w, bubble_w)
      |> assign(:chips, chips(Enum.take(assigns.sources, 3)))
      |> assign(:chips_y, chips_y)
      |> assign(:input_y, chips_y + 40)
      |> assign(:height, chips_y + 40 + 36 + 20)

    ~H"""
    <.frame name="intelligence/ask" label={@label} {frame_opts(assigns)}>
      <svg viewBox={"0 0 360 #{@height}"}>
        <g class="ehv-h-lift">
          <rect class="ehv-card ehv-a-rise" x="0.5" y="0.5" width="359" height={@height - 1} rx="12" />
          <g class="ehv-a-rise ehv-i-1">
            <rect class="ehv-card-3" x={340 - @bubble_w} y="18" width={@bubble_w} height="34" rx="10" />
            <text x={340 - @bubble_w + 15} y="39.5" font-size="12.5">{@question}</text>
          </g>
          <g class="ehv-a-pop ehv-i-4">
            <rect class="ehv-card-3" x="20" y="68" width="24" height="24" rx="7" />
            <rect class="ehv-fill-lime" x="28" y="76" width="8" height="8" rx="2.5" />
          </g>
          <text
            :for={{line, index} <- @lines}
            class={"ehv-a-fade ehv-i-#{index * 2 + 5}"}
            x="56"
            y={84 + index * 19}
            font-size="12.5"
          >
            {line}
          </text>
          <g :for={{chip, index} <- Enum.with_index(@chips)} class={"ehv-a-pop ehv-i-#{index + 11}"}>
            <rect
              class="ehv-well"
              x={chip.x + 0.5}
              y={@chips_y + 0.5}
              width={chip.w}
              height="19"
              rx="6"
            />
            <text class="ehv-mono ehv-t-2" x={chip.x + 8} y={@chips_y + 13} font-size="7.5">
              {String.upcase(chip.text)}
            </text>
          </g>
          <g class="ehv-a-rise ehv-i-3">
            <rect class="ehv-well" x="20.5" y={@input_y + 0.5} width="319" height="35" rx="8" />
            <line
              class="ehv-line-lime ehv-intelligence-ask__caret"
              x1="34"
              x2="34"
              y1={@input_y + 11}
              y2={@input_y + 25}
            />
            <text class="ehv-t-3" x="41" y={@input_y + 22.5} font-size="12">{@placeholder}</text>
            <rect class="ehv-fill-lime" x="308" y={@input_y + 6} width="24" height="24" rx="6" />
            <path
              class="ehv-intelligence-ask__arrow ehv-round"
              d={"M320 #{@input_y + 23.5} v-11 m-4.5 4.5 l4.5 -4.5 l4.5 4.5"}
            />
          </g>
        </g>
      </svg>
    </.frame>
    """
  end

  # Lays the source chips out left to right from x = 56.
  defp chips(sources) do
    {chips, _x} =
      Enum.map_reduce(sources, 56, fn text, x ->
        width = Float.round(16 + String.length(text) * 5.4, 1)
        {%{text: text, x: x, w: width}, x + width + 6}
      end)

    chips
  end
end
assets/css/intelligence/ask.css
ask.css
/* intelligence/ask */
.ehv-intelligence-ask { --ehv-max-w: 380px; --ehv-step: 110ms; }

.ehv-intelligence-ask__arrow { fill: none; stroke: var(--ehv-on-lime); stroke-width: 1.5; }

@keyframes ehv-intelligence-ask-blink { 50% { opacity: 0; } }
.ehv--animated .ehv-intelligence-ask__caret { animation: ehv-intelligence-ask-blink 1.1s steps(1) infinite; }