Show topbar loading indicator while scores are loading
This commit is contained in:
parent
d97de91978
commit
d5140267b1
8 changed files with 213 additions and 2 deletions
|
|
@ -105,4 +105,156 @@
|
||||||
/* Make LiveView wrapper divs transparent for layout */
|
/* Make LiveView wrapper divs transparent for layout */
|
||||||
[data-phx-session], [data-phx-teleported-src] { display: contents }
|
[data-phx-session], [data-phx-teleported-src] { display: contents }
|
||||||
|
|
||||||
|
/* Markdown rendered content */
|
||||||
|
.markdown-content {
|
||||||
|
max-width: 64rem;
|
||||||
|
margin: 2rem auto;
|
||||||
|
padding: 0 1rem;
|
||||||
|
line-height: 1.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content h1 {
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: 700;
|
||||||
|
margin: 2rem 0 1rem;
|
||||||
|
border-bottom: 1px solid oklch(80% 0 0);
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content h2 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
margin: 1.75rem 0 0.75rem;
|
||||||
|
border-bottom: 1px solid oklch(85% 0 0);
|
||||||
|
padding-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content h3 {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 1.5rem 0 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content h4 {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 1.25rem 0 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content p {
|
||||||
|
margin: 0.75rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content ul, .markdown-content ol {
|
||||||
|
margin: 0.75rem 0;
|
||||||
|
padding-left: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content ul { list-style-type: disc; }
|
||||||
|
.markdown-content ol { list-style-type: decimal; }
|
||||||
|
|
||||||
|
.markdown-content li {
|
||||||
|
margin: 0.25rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content code {
|
||||||
|
font-family: ui-monospace, monospace;
|
||||||
|
font-size: 0.875em;
|
||||||
|
background: oklch(92% 0 0);
|
||||||
|
padding: 0.15em 0.35em;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content pre {
|
||||||
|
margin: 1rem 0;
|
||||||
|
padding: 1rem;
|
||||||
|
background: oklch(20% 0.01 260);
|
||||||
|
color: oklch(95% 0 0);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content pre code {
|
||||||
|
background: none;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 0.85em;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 1rem 0;
|
||||||
|
font-size: 0.9em;
|
||||||
|
overflow-x: auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content th, .markdown-content td {
|
||||||
|
border: 1px solid oklch(80% 0 0);
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content th {
|
||||||
|
background: oklch(94% 0 0);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content tr:nth-child(even) {
|
||||||
|
background: oklch(97% 0 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content hr {
|
||||||
|
border: none;
|
||||||
|
border-top: 2px solid oklch(85% 0 0);
|
||||||
|
margin: 2rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content strong {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content blockquote {
|
||||||
|
border-left: 4px solid oklch(70% 0.15 260);
|
||||||
|
margin: 1rem 0;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
background: oklch(96% 0.005 260);
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content a {
|
||||||
|
color: oklch(55% 0.2 260);
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
:where([data-theme=dark]) .markdown-content code {
|
||||||
|
background: oklch(30% 0.01 260);
|
||||||
|
}
|
||||||
|
|
||||||
|
:where([data-theme=dark]) .markdown-content th {
|
||||||
|
background: oklch(30% 0.01 260);
|
||||||
|
}
|
||||||
|
|
||||||
|
:where([data-theme=dark]) .markdown-content tr:nth-child(even) {
|
||||||
|
background: oklch(25% 0.01 260);
|
||||||
|
}
|
||||||
|
|
||||||
|
:where([data-theme=dark]) .markdown-content th,
|
||||||
|
:where([data-theme=dark]) .markdown-content td {
|
||||||
|
border-color: oklch(40% 0 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
:where([data-theme=dark]) .markdown-content h1,
|
||||||
|
:where([data-theme=dark]) .markdown-content h2 {
|
||||||
|
border-color: oklch(40% 0 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
:where([data-theme=dark]) .markdown-content hr {
|
||||||
|
border-color: oklch(40% 0 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
:where([data-theme=dark]) .markdown-content blockquote {
|
||||||
|
background: oklch(25% 0.01 260);
|
||||||
|
}
|
||||||
|
|
||||||
/* This file is for your main application CSS */
|
/* This file is for your main application CSS */
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import topbar from "../vendor/topbar"
|
||||||
|
|
||||||
export const PropagationMap = {
|
export const PropagationMap = {
|
||||||
mounted() {
|
mounted() {
|
||||||
this.map = L.map(this.el, {
|
this.map = L.map(this.el, {
|
||||||
|
|
@ -24,9 +26,13 @@ export const PropagationMap = {
|
||||||
]
|
]
|
||||||
|
|
||||||
this.handleEvent("update_scores", ({ scores }) => {
|
this.handleEvent("update_scores", ({ scores }) => {
|
||||||
|
topbar.hide()
|
||||||
this.renderScores(scores)
|
this.renderScores(scores)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Show topbar whenever the server is processing a band change
|
||||||
|
this.el.addEventListener("show-loading", () => topbar.show(300))
|
||||||
|
|
||||||
// Send bounds to server on load and on pan/zoom
|
// Send bounds to server on load and on pan/zoom
|
||||||
this.sendBounds()
|
this.sendBounds()
|
||||||
this.map.on("moveend", () => this.sendBounds())
|
this.map.on("moveend", () => this.sendBounds())
|
||||||
|
|
@ -168,6 +174,7 @@ export const PropagationMap = {
|
||||||
},
|
},
|
||||||
|
|
||||||
sendBounds() {
|
sendBounds() {
|
||||||
|
topbar.show(300)
|
||||||
const b = this.map.getBounds()
|
const b = this.map.getBounds()
|
||||||
this.pushEvent("map_bounds", {
|
this.pushEvent("map_bounds", {
|
||||||
south: b.getSouth(),
|
south: b.getSouth(),
|
||||||
|
|
|
||||||
25
lib/microwaveprop_web/live/algo_live.ex
Normal file
25
lib/microwaveprop_web/live/algo_live.ex
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
defmodule MicrowavepropWeb.AlgoLive do
|
||||||
|
@moduledoc false
|
||||||
|
use MicrowavepropWeb, :live_view
|
||||||
|
|
||||||
|
@algo_path Path.join(:code.priv_dir(:microwaveprop), "../algo.md")
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def mount(_params, _session, socket) do
|
||||||
|
content =
|
||||||
|
@algo_path
|
||||||
|
|> File.read!()
|
||||||
|
|> Earmark.as_html!()
|
||||||
|
|
||||||
|
{:ok, assign(socket, page_title: "Algorithm", content: content)}
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def render(assigns) do
|
||||||
|
~H"""
|
||||||
|
<div class="markdown-content">
|
||||||
|
{raw(@content)}
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -117,7 +117,8 @@ defmodule MicrowavepropWeb.MapLive do
|
||||||
<li :for={band <- @bands}>
|
<li :for={band <- @bands}>
|
||||||
<button
|
<button
|
||||||
phx-click={
|
phx-click={
|
||||||
JS.push("select_band", value: %{value: band.freq_mhz})
|
JS.dispatch("show-loading", to: "#propagation-map")
|
||||||
|
|> JS.push("select_band", value: %{value: band.freq_mhz})
|
||||||
|> JS.dispatch("click", to: "#propagation-map")
|
|> JS.dispatch("click", to: "#propagation-map")
|
||||||
}
|
}
|
||||||
class={[if(@selected_band == band.freq_mhz, do: "active")]}
|
class={[if(@selected_band == band.freq_mhz, do: "active")]}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ defmodule MicrowavepropWeb.Router do
|
||||||
live "/map", MapLive
|
live "/map", MapLive
|
||||||
live "/qsos", QsoLive.Index
|
live "/qsos", QsoLive.Index
|
||||||
live "/qsos/:id", QsoLive.Show
|
live "/qsos/:id", QsoLive.Show
|
||||||
|
live "/algo", AlgoLive
|
||||||
end
|
end
|
||||||
|
|
||||||
# Other scopes may use custom stacks.
|
# Other scopes may use custom stacks.
|
||||||
|
|
|
||||||
3
mix.exs
3
mix.exs
|
|
@ -63,7 +63,8 @@ defmodule Microwaveprop.MixProject do
|
||||||
{:bandit, "~> 1.5"},
|
{:bandit, "~> 1.5"},
|
||||||
{:styler, "~> 1.11", only: [:dev, :test], runtime: false},
|
{:styler, "~> 1.11", only: [:dev, :test], runtime: false},
|
||||||
{:oban, "~> 2.19"},
|
{:oban, "~> 2.19"},
|
||||||
{:credo, "~> 1.7", only: [:dev, :test], runtime: false}
|
{:credo, "~> 1.7", only: [:dev, :test], runtime: false},
|
||||||
|
{:earmark, "~> 1.4"}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
1
mix.lock
1
mix.lock
|
|
@ -6,6 +6,7 @@
|
||||||
"db_connection": {:hex, :db_connection, "2.9.0", "a6a97c5c958a2d7091a58a9be40caf41ab496b0701d21e1d1abff3fa27a7f371", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "17d502eacaf61829db98facf6f20808ed33da6ccf495354a41e64fe42f9c509c"},
|
"db_connection": {:hex, :db_connection, "2.9.0", "a6a97c5c958a2d7091a58a9be40caf41ab496b0701d21e1d1abff3fa27a7f371", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "17d502eacaf61829db98facf6f20808ed33da6ccf495354a41e64fe42f9c509c"},
|
||||||
"decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"},
|
"decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"},
|
||||||
"dns_cluster": {:hex, :dns_cluster, "0.2.0", "aa8eb46e3bd0326bd67b84790c561733b25c5ba2fe3c7e36f28e88f384ebcb33", [:mix], [], "hexpm", "ba6f1893411c69c01b9e8e8f772062535a4cf70f3f35bcc964a324078d8c8240"},
|
"dns_cluster": {:hex, :dns_cluster, "0.2.0", "aa8eb46e3bd0326bd67b84790c561733b25c5ba2fe3c7e36f28e88f384ebcb33", [:mix], [], "hexpm", "ba6f1893411c69c01b9e8e8f772062535a4cf70f3f35bcc964a324078d8c8240"},
|
||||||
|
"earmark": {:hex, :earmark, "1.4.48", "5f41e579d85ef812351211842b6e005f6e0cef111216dea7d4b9d58af4608434", [:mix], [], "hexpm", "a461a0ddfdc5432381c876af1c86c411fd78a25790c75023c7a4c035fdc858f9"},
|
||||||
"ecto": {:hex, :ecto, "3.13.5", "9d4a69700183f33bf97208294768e561f5c7f1ecf417e0fa1006e4a91713a834", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "df9efebf70cf94142739ba357499661ef5dbb559ef902b68ea1f3c1fabce36de"},
|
"ecto": {:hex, :ecto, "3.13.5", "9d4a69700183f33bf97208294768e561f5c7f1ecf417e0fa1006e4a91713a834", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "df9efebf70cf94142739ba357499661ef5dbb559ef902b68ea1f3c1fabce36de"},
|
||||||
"ecto_sql": {:hex, :ecto_sql, "3.13.5", "2f8282b2ad97bf0f0d3217ea0a6fff320ead9e2f8770f810141189d182dc304e", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.13.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "aa36751f4e6a2b56ae79efb0e088042e010ff4935fc8684e74c23b1f49e25fdc"},
|
"ecto_sql": {:hex, :ecto_sql, "3.13.5", "2f8282b2ad97bf0f0d3217ea0a6fff320ead9e2f8770f810141189d182dc304e", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.13.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "aa36751f4e6a2b56ae79efb0e088042e010ff4935fc8684e74c23b1f49e25fdc"},
|
||||||
"elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"},
|
"elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"},
|
||||||
|
|
|
||||||
23
test/microwaveprop_web/live/algo_live_test.exs
Normal file
23
test/microwaveprop_web/live/algo_live_test.exs
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
defmodule MicrowavepropWeb.AlgoLiveTest do
|
||||||
|
use MicrowavepropWeb.ConnCase, async: true
|
||||||
|
|
||||||
|
import Phoenix.LiveViewTest
|
||||||
|
|
||||||
|
describe "mount" do
|
||||||
|
test "renders algo page with markdown content as HTML", %{conn: conn} do
|
||||||
|
{:ok, _lv, html} = live(conn, ~p"/algo")
|
||||||
|
|
||||||
|
# algo.md starts with this heading
|
||||||
|
assert html =~ "Microwave Propagation Algorithm"
|
||||||
|
# Earmark should convert ## headings to h2 tags
|
||||||
|
assert html =~ "<h2>"
|
||||||
|
# Should contain code blocks rendered as HTML
|
||||||
|
assert html =~ "<code"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "sets page title", %{conn: conn} do
|
||||||
|
{:ok, _lv, html} = live(conn, ~p"/algo")
|
||||||
|
assert html =~ "Algorithm"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
Add table
Reference in a new issue