Skip to content

Commit

Permalink
web: use SVG for inline plots
Browse files Browse the repository at this point in the history
  • Loading branch information
vincent-octo committed Nov 21, 2024
1 parent 358f253 commit ccf9a4e
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 30 deletions.
73 changes: 68 additions & 5 deletions web/lib/risteys_web/controllers/lab_test_html.ex
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,35 @@ defmodule RisteysWeb.LabTestHTML do
defp plot_sex(nil), do: ""

defp plot_sex(female_percent) do
assigns = %{female_percent: female_percent}
width = 60
height = 5
viewbox = "0 0 #{width} #{height}"

assigns = %{
female_percent: female_percent,
width: width,
height: height,
viewbox: viewbox,
split_position: female_percent / 100 * width
}

# Making the plot stretch but keep its height is done by setting the following on <svg>:
# - fixed `height`
# - `width="100%"`
# - `preserveAspectRatio="none"`
~H"""
<div style="width: 100%; height: 0.3em; background-color: #bfcde6;">
<div style={"width: #{@female_percent}%; height: 100%; background-color: #dd9fbd; border-right: 1px solid #000;"}>
</div>
</div>
<svg
viewBox={@viewbox}
height={@height}
width="100%"
preserveAspectRatio="none"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect width={@width} height={@height} fill="#bfcde6" />
<rect width={@split_position} height={@height} fill="#dd9fbd" />
<rect x={@split_position} width="1" height={@height} fill="black" />
</svg>
"""
end

Expand Down Expand Up @@ -120,6 +142,47 @@ defmodule RisteysWeb.LabTestHTML do
<% end %>
</div>
"""

width = 60
height = 5
viewbox = "0 0 #{width} #{height}"

tick_x_positions =
case tick_every do
nil ->
[]

_ ->
last = round(npeople_max)
step = round(tick_every)
Range.to_list(step..last//step)
end
|> Enum.map(&(&1 / npeople_max * width))

assigns = %{
width: width,
height: height,
viewbox: viewbox,
width_npeople: npeople / npeople_max * width,
tick_x_positions: tick_x_positions
}

~H"""
<svg
viewBox={@viewbox}
height={@height}
width="100%"
preserveAspectRatio="none"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect width={@width} height={@height} fill="var(--bg-color-plot-empty)" />
<rect width={@width_npeople} height={@height} fill="var(--bg-color-plot)" />
<%= for tick_x <- @tick_x_positions do %>
<rect x={tick_x} width="1" height={@height} fill="var(--bg-color-plot-empty)" />
<% end %>
</svg>
"""
end

def show_prettify_stats(lab_test) do
Expand Down
33 changes: 8 additions & 25 deletions web/priv/static/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -207,14 +207,15 @@ TODO:
.search-results {
& {
position: absolute;
/* NOTE(Vincent 2024-11-18) The table header of the page "Index of lab tests" has a
z-index of 1, so by default this table header will display on top of the search
results.
For now I set the z-index to 2 on the search results to make it appear on top, but
a better solution would be to rework the table of Index of lab tests to make the
z-index unecessary.
/* NOTE(Vincent 2024-11-21) The search results element being early in the DOM makes
it easy to be overlapped by later positioned element (i.e. their position set to
something else that static, e.g. sticky), masking the results.
Two options:
1. Make the search results a modal dialog and put its element at the end of the DOM,
removing the need for z-index.
2. Use z-index, as it's currently done here.
*/
z-index: 2;
z-index: 1;
color: black;
background: white;
box-shadow: 0 0 30px rgba(0, 0, 0, 0.15);
Expand Down Expand Up @@ -718,24 +719,6 @@ TODO:
& [role="row"]:has([role="columnheader"]) {
position: sticky;
top: 0;
/* NOTE(Vincent 2024-05-17) On why we need to set z-index here.
* Without setting z-index: 1 here, there will be a UI bug that puts the
* bar plots on top of the table header.
* This bug happens because the bar plots are made with position:
* relative and absolute. In CSS, that creates a new stacking context
* (can think of it as a group layer in design softwares), and since the
* bar plots appear later on the DOM these stacking contexts are on top
* of the stacking context of the table header.
* One way to rearrange these stacking contexts is to set a higher
* z-index for the table header, so that its stacking context is on top
* of the bar plots stacking contexts.
*
* Other ways to solve this bug:
* - implement the bar plots without using 'position' properties that
* create new stacking contexts.
* - implement the bar plots in SVG.
*/
z-index: 1;

background: linear-gradient(
to top,
Expand Down

0 comments on commit ccf9a4e

Please sign in to comment.