mobile
This commit is contained in:
parent
8446303399
commit
03c20ebfd8
3 changed files with 315 additions and 5 deletions
|
|
@ -34,6 +34,25 @@ main {
|
||||||
height: 100vh !important;
|
height: 100vh !important;
|
||||||
width: 100vw !important;
|
width: 100vw !important;
|
||||||
z-index: 1 !important;
|
z-index: 1 !important;
|
||||||
|
transition: right 0.3s ease-in-out !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Desktop slideover behavior for map */
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
#aprs-map.slideover-open {
|
||||||
|
right: 352px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#aprs-map.slideover-closed {
|
||||||
|
right: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile slideover behavior for map */
|
||||||
|
@media (max-width: 1023px) {
|
||||||
|
#aprs-map {
|
||||||
|
right: 0 !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ensure the map container div has proper dimensions */
|
/* Ensure the map container div has proper dimensions */
|
||||||
|
|
@ -273,3 +292,113 @@ body.home-page main > div {
|
||||||
.historical-dot-marker {
|
.historical-dot-marker {
|
||||||
z-index: 15 !important;
|
z-index: 15 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Slideover toggle button styles */
|
||||||
|
.slideover-toggle {
|
||||||
|
position: fixed !important;
|
||||||
|
right: 10px;
|
||||||
|
top: 10px;
|
||||||
|
z-index: 1001;
|
||||||
|
background: white;
|
||||||
|
border: 2px solid rgba(0, 0, 0, 0.2);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slideover-toggle:hover {
|
||||||
|
background: #f8fafc;
|
||||||
|
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slideover-toggle:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide toggle on desktop when slideover is open */
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
.slideover-toggle.slideover-open {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile toggle adjustments */
|
||||||
|
@media (max-width: 1023px) {
|
||||||
|
.slideover-toggle {
|
||||||
|
right: 15px;
|
||||||
|
top: 15px;
|
||||||
|
padding: 12px;
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Slideover panel responsive styles */
|
||||||
|
.slideover-panel {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 100vh;
|
||||||
|
width: 352px;
|
||||||
|
background: white;
|
||||||
|
box-shadow: -4px 0 24px rgba(0, 0, 0, 0.15);
|
||||||
|
z-index: 1000;
|
||||||
|
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Desktop slideover behavior */
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
.slideover-panel.slideover-open {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slideover-panel.slideover-closed {
|
||||||
|
transform: translateX(100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile slideover behavior */
|
||||||
|
@media (max-width: 1023px) {
|
||||||
|
.slideover-panel {
|
||||||
|
width: 100vw;
|
||||||
|
max-width: 400px;
|
||||||
|
box-shadow: -8px 0 32px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slideover-panel.slideover-open {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slideover-panel.slideover-closed {
|
||||||
|
transform: translateX(100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile panel improvements */
|
||||||
|
@media (max-width: 1023px) {
|
||||||
|
.slideover-panel {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Improve scrolling on mobile */
|
||||||
|
@media (max-width: 1023px) {
|
||||||
|
.slideover-panel {
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
overscroll-behavior: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slideover-panel .flex-1 {
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,52 @@ let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("
|
||||||
// Import minimal APRS map hook
|
// Import minimal APRS map hook
|
||||||
import MapAPRSMap from "./map";
|
import MapAPRSMap from "./map";
|
||||||
|
|
||||||
|
// Responsive Slideover Hook
|
||||||
|
let ResponsiveSlideoverHook = {
|
||||||
|
mounted() {
|
||||||
|
this.isInitialized = false;
|
||||||
|
|
||||||
|
this.handleResize = () => {
|
||||||
|
const isDesktop = window.innerWidth >= 1024;
|
||||||
|
const isMobile = window.innerWidth < 1024;
|
||||||
|
|
||||||
|
// Set initial state based on screen size
|
||||||
|
if (!this.isInitialized) {
|
||||||
|
this.isInitialized = true;
|
||||||
|
if (isDesktop) {
|
||||||
|
this.pushEvent("set_slideover_state", { open: true });
|
||||||
|
} else {
|
||||||
|
this.pushEvent("set_slideover_state", { open: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initial check after a brief delay to ensure LiveView is ready
|
||||||
|
setTimeout(() => {
|
||||||
|
this.handleResize();
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
// Listen for resize events with debouncing
|
||||||
|
let resizeTimer;
|
||||||
|
this.debouncedResize = () => {
|
||||||
|
clearTimeout(resizeTimer);
|
||||||
|
resizeTimer = setTimeout(this.handleResize, 150);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("resize", this.debouncedResize);
|
||||||
|
},
|
||||||
|
|
||||||
|
destroyed() {
|
||||||
|
if (this.debouncedResize) {
|
||||||
|
window.removeEventListener("resize", this.debouncedResize);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
// APRS Map Hook
|
// APRS Map Hook
|
||||||
let Hooks = {};
|
let Hooks = {};
|
||||||
Hooks.APRSMap = MapAPRSMap;
|
Hooks.APRSMap = MapAPRSMap;
|
||||||
|
Hooks.ResponsiveSlideoverHook = ResponsiveSlideoverHook;
|
||||||
|
|
||||||
let liveSocket = new LiveSocket("/live", Socket, {
|
let liveSocket = new LiveSocket("/live", Socket, {
|
||||||
longPollFallbackMs: 2500,
|
longPollFallbackMs: 2500,
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,9 @@ defmodule AprsWeb.MapLive.Index do
|
||||||
# Overlay controls
|
# Overlay controls
|
||||||
overlay_callsign: "",
|
overlay_callsign: "",
|
||||||
trail_duration: "1",
|
trail_duration: "1",
|
||||||
historical_hours: "1"
|
historical_hours: "1",
|
||||||
|
# Slideover state - will be set based on screen size
|
||||||
|
slideover_open: true
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -248,6 +250,16 @@ defmodule AprsWeb.MapLive.Index do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def handle_event("toggle_slideover", _params, socket) do
|
||||||
|
{:noreply, assign(socket, slideover_open: !socket.assigns.slideover_open)}
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def handle_event("set_slideover_state", %{"open" => open}, socket) do
|
||||||
|
{:noreply, assign(socket, slideover_open: open)}
|
||||||
|
end
|
||||||
|
|
||||||
@spec handle_bounds_update(map(), Socket.t()) :: {:noreply, Socket.t()}
|
@spec handle_bounds_update(map(), Socket.t()) :: {:noreply, Socket.t()}
|
||||||
defp handle_bounds_update(bounds, socket) do
|
defp handle_bounds_update(bounds, socket) do
|
||||||
# Update the map bounds from the client
|
# Update the map bounds from the client
|
||||||
|
|
@ -474,10 +486,28 @@ defmodule AprsWeb.MapLive.Index do
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 352px;
|
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
transition: right 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Desktop styles */
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
#aprs-map.slideover-open {
|
||||||
|
right: 352px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#aprs-map.slideover-closed {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile styles */
|
||||||
|
@media (max-width: 1023px) {
|
||||||
|
#aprs-map {
|
||||||
|
right: 0 !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.locate-button {
|
.locate-button {
|
||||||
|
|
@ -550,6 +580,7 @@ defmodule AprsWeb.MapLive.Index do
|
||||||
|
|
||||||
<div
|
<div
|
||||||
id="aprs-map"
|
id="aprs-map"
|
||||||
|
class={if @slideover_open, do: "slideover-open", else: "slideover-closed"}
|
||||||
phx-hook="APRSMap"
|
phx-hook="APRSMap"
|
||||||
phx-update="ignore"
|
phx-update="ignore"
|
||||||
data-center={Jason.encode!(@map_center)}
|
data-center={Jason.encode!(@map_center)}
|
||||||
|
|
@ -575,10 +606,63 @@ defmodule AprsWeb.MapLive.Index do
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<!-- Slideover Toggle Button -->
|
||||||
|
<button
|
||||||
|
class={["slideover-toggle", if(@slideover_open, do: "slideover-open", else: "slideover-closed")]}
|
||||||
|
phx-click="toggle_slideover"
|
||||||
|
title={if @slideover_open, do: "Hide controls", else: "Show controls"}
|
||||||
|
>
|
||||||
|
<%= if @slideover_open do %>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path d="m9 18 6-6-6-6" />
|
||||||
|
</svg>
|
||||||
|
<% else %>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path d="m15 18-6-6 6-6" />
|
||||||
|
</svg>
|
||||||
|
<% end %>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Mobile Backdrop -->
|
||||||
|
<%= if @slideover_open do %>
|
||||||
|
<div
|
||||||
|
class="fixed inset-0 bg-black bg-opacity-50 z-[999] lg:hidden backdrop-blur-sm"
|
||||||
|
phx-click="toggle_slideover"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<!-- Control Panel Overlay -->
|
<!-- Control Panel Overlay -->
|
||||||
<div class="fixed top-0 right-0 h-full w-88 bg-white shadow-2xl z-[1000] backdrop-blur-sm">
|
<div
|
||||||
|
id="slideover-panel"
|
||||||
|
class={[
|
||||||
|
"slideover-panel",
|
||||||
|
if(@slideover_open, do: "slideover-open", else: "slideover-closed")
|
||||||
|
]}
|
||||||
|
phx-hook="ResponsiveSlideoverHook"
|
||||||
|
>
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="flex items-center justify-center p-6 border-b border-slate-200 bg-gradient-to-r from-indigo-600 to-purple-600 text-white">
|
<div class="flex items-center justify-between p-6 border-b border-slate-200 bg-gradient-to-r from-indigo-600 to-purple-600 text-white">
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-2">
|
||||||
<svg
|
<svg
|
||||||
class="w-6 h-6"
|
class="w-6 h-6"
|
||||||
|
|
@ -596,10 +680,32 @@ defmodule AprsWeb.MapLive.Index do
|
||||||
</svg>
|
</svg>
|
||||||
<h2 class="text-xl font-bold">APRS.me</h2>
|
<h2 class="text-xl font-bold">APRS.me</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Close button for mobile -->
|
||||||
|
<button
|
||||||
|
class="lg:hidden text-white hover:text-slate-200 transition-colors"
|
||||||
|
phx-click="toggle_slideover"
|
||||||
|
title="Close controls"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path d="m18 6-12 12" />
|
||||||
|
<path d="m6 6 12 12" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Content -->
|
<!-- Content -->
|
||||||
<div class="p-6 space-y-6 bg-slate-50 h-full overflow-y-auto">
|
<div class="p-6 space-y-6 bg-slate-50 flex-1 overflow-y-auto">
|
||||||
<!-- Callsign Search -->
|
<!-- Callsign Search -->
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<label class="block text-sm font-semibold text-slate-700 flex items-center space-x-2">
|
<label class="block text-sm font-semibold text-slate-700 flex items-center space-x-2">
|
||||||
|
|
@ -742,6 +848,38 @@ defmodule AprsWeb.MapLive.Index do
|
||||||
<span>How many hours of historical packets to load</span>
|
<span>How many hours of historical packets to load</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Navigation Links (Mobile) -->
|
||||||
|
<div class="lg:hidden pt-4 border-t border-slate-200 space-y-3">
|
||||||
|
<.link
|
||||||
|
navigate={~p"/packets"}
|
||||||
|
class="flex items-center space-x-2 text-sm text-blue-600 hover:text-blue-800 transition-colors"
|
||||||
|
>
|
||||||
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<span>View All Packets</span>
|
||||||
|
</.link>
|
||||||
|
<.link
|
||||||
|
navigate={~p"/badpackets"}
|
||||||
|
class="flex items-center space-x-2 text-sm text-blue-600 hover:text-blue-800 transition-colors"
|
||||||
|
>
|
||||||
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.082 16.5c-.77.833.192 2.5 1.732 2.5z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<span>View Bad Packets</span>
|
||||||
|
</.link>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue