intelligence/ask.ex
Ask
A question about the company, answered in plain words with the sources it used.
# in the module that renders the template
use EHVisuals
<div class="h-96 rounded-xl border border-border">
<.intelligence_ask animated />
</div>
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
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
/* 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; }