mirror of
https://github.com/ClaperCo/Claper.git
synced 2026-08-29 10:09:08 +02:00
Adjust interaction list paging on resize
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
|
||||
- Fix datetime being reset at every changes
|
||||
- Fix account creation with a soft-deleted email
|
||||
- Fix interaction list paging on resize
|
||||
|
||||
## v.3.0.0
|
||||
|
||||
|
||||
@@ -515,6 +515,18 @@ Hooks.SlideSortable = {
|
||||
};
|
||||
Hooks.InteractionDrag = {
|
||||
mounted() {
|
||||
this.lastInteractionListHeight = null;
|
||||
this.interactionListResizeObserver = new ResizeObserver(([entry]) => {
|
||||
const height = window.matchMedia("(min-width: 1024px)").matches
|
||||
? Math.round(entry.contentRect.height)
|
||||
: 0;
|
||||
|
||||
if (height === this.lastInteractionListHeight) return;
|
||||
this.lastInteractionListHeight = height;
|
||||
this.pushEventTo(this.el, "interaction-list-resized", { height });
|
||||
});
|
||||
this.interactionListResizeObserver.observe(this.el);
|
||||
|
||||
this.el.addEventListener("dragstart", (e) => {
|
||||
const item = e.target.closest("[data-interaction-id]");
|
||||
if (!item) return;
|
||||
@@ -546,6 +558,9 @@ Hooks.InteractionDrag = {
|
||||
.forEach((n) => n.classList.remove("opacity-40"));
|
||||
});
|
||||
},
|
||||
destroyed() {
|
||||
this.interactionListResizeObserver.disconnect();
|
||||
},
|
||||
};
|
||||
Hooks.OpenPresenter = {
|
||||
open(e) {
|
||||
|
||||
@@ -540,7 +540,10 @@
|
||||
<!-- Bottom Section - Interactions & Options -->
|
||||
<div class="flex-1 flex flex-col lg:flex-row overflow-y-auto lg:overflow-hidden">
|
||||
<!-- Interactions Panel -->
|
||||
<div class="lg:w-1/3 flex-shrink-0 p-4">
|
||||
<div
|
||||
id="interactions-pane"
|
||||
class="lg:w-1/3 flex-shrink-0 p-4 lg:min-h-0 lg:flex lg:flex-col lg:overflow-hidden"
|
||||
>
|
||||
<.live_component
|
||||
id="interaction-list"
|
||||
module={ClaperWeb.EventLive.ManageInteractionListComponent}
|
||||
|
||||
@@ -2,17 +2,21 @@ defmodule ClaperWeb.EventLive.ManageInteractionListComponent do
|
||||
use ClaperWeb, :live_component
|
||||
|
||||
@per_page 6
|
||||
@max_per_page 20
|
||||
@fixed_content_height 200
|
||||
@interaction_row_height 64
|
||||
|
||||
def update(assigns, socket) do
|
||||
page = Map.get(socket.assigns, :page, 0)
|
||||
per_page = Map.get(socket.assigns, :per_page, @per_page)
|
||||
total = length(assigns.interactions)
|
||||
max_page = max(0, ceil(total / @per_page) - 1)
|
||||
max_page = max_page(total, per_page)
|
||||
page = min(page, max_page)
|
||||
|
||||
{:ok,
|
||||
socket
|
||||
|> assign(assigns)
|
||||
|> assign(page: page, per_page: @per_page)}
|
||||
|> assign(page: page, per_page: per_page)}
|
||||
end
|
||||
|
||||
def handle_event("prev-page", _, socket) do
|
||||
@@ -20,16 +24,38 @@ defmodule ClaperWeb.EventLive.ManageInteractionListComponent do
|
||||
end
|
||||
|
||||
def handle_event("next-page", _, socket) do
|
||||
max_page = max(0, ceil(length(socket.assigns.interactions) / @per_page) - 1)
|
||||
max_page = max_page(length(socket.assigns.interactions), socket.assigns.per_page)
|
||||
{:noreply, assign(socket, page: min(max_page, socket.assigns.page + 1))}
|
||||
end
|
||||
|
||||
def handle_event("interaction-list-resized", %{"height" => height}, socket)
|
||||
when is_integer(height) do
|
||||
per_page =
|
||||
height
|
||||
|> Kernel.-(@fixed_content_height)
|
||||
|> div(@interaction_row_height)
|
||||
|> max(@per_page)
|
||||
|> min(@max_per_page)
|
||||
|
||||
first_interaction = socket.assigns.page * socket.assigns.per_page
|
||||
|
||||
page =
|
||||
min(
|
||||
div(first_interaction, per_page),
|
||||
max_page(length(socket.assigns.interactions), per_page)
|
||||
)
|
||||
|
||||
{:noreply, assign(socket, page: page, per_page: per_page)}
|
||||
end
|
||||
|
||||
defp paginated_interactions(interactions, page, per_page) do
|
||||
interactions
|
||||
|> Enum.drop(page * per_page)
|
||||
|> Enum.take(per_page)
|
||||
end
|
||||
|
||||
defp max_page(total, per_page), do: max(0, ceil(total / per_page) - 1)
|
||||
|
||||
def render(assigns) do
|
||||
assigns =
|
||||
assign(assigns,
|
||||
@@ -41,7 +67,8 @@ defmodule ClaperWeb.EventLive.ManageInteractionListComponent do
|
||||
<div
|
||||
id="interaction-drag-list"
|
||||
phx-hook="InteractionDrag"
|
||||
class="relative flex flex-col gap-2 border border-gray-200 rounded-2xl p-2"
|
||||
phx-target={@myself}
|
||||
class="relative flex flex-col gap-2 border border-gray-200 rounded-2xl p-2 lg:flex-1 lg:min-h-0 lg:overflow-y-auto"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<svg
|
||||
|
||||
@@ -2,7 +2,7 @@ defmodule ClaperWeb.EventLiveTest do
|
||||
use ClaperWeb.ConnCase
|
||||
|
||||
import Phoenix.LiveViewTest
|
||||
import Claper.{FormsFixtures, PresentationsFixtures}
|
||||
import Claper.{FormsFixtures, PollsFixtures, PresentationsFixtures}
|
||||
|
||||
@update_attrs %{name: "some updated name"}
|
||||
|
||||
@@ -194,6 +194,79 @@ defmodule ClaperWeb.EventLiveTest do
|
||||
describe "Manage" do
|
||||
setup [:register_and_log_in_user, :create_event]
|
||||
|
||||
test "keeps interaction pagination reachable on short screens", %{
|
||||
conn: conn,
|
||||
presentation_file: presentation_file
|
||||
} do
|
||||
for index <- 1..7 do
|
||||
poll_fixture(%{
|
||||
presentation_file_id: presentation_file.id,
|
||||
title: "Poll #{index}"
|
||||
})
|
||||
end
|
||||
|
||||
{:ok, manage_live, html} = live(conn, ~p"/e/#{presentation_file.event.code}/manage")
|
||||
document = Floki.parse_document!(html)
|
||||
|
||||
interaction_pane_classes =
|
||||
document
|
||||
|> Floki.attribute("#interactions-pane", "class")
|
||||
|> List.first()
|
||||
|> String.split()
|
||||
|
||||
interaction_list_classes =
|
||||
document
|
||||
|> Floki.attribute("#interaction-drag-list", "class")
|
||||
|> List.first()
|
||||
|> String.split()
|
||||
|
||||
assert "lg:flex" in interaction_pane_classes
|
||||
assert "lg:min-h-0" in interaction_pane_classes
|
||||
assert "lg:overflow-hidden" in interaction_pane_classes
|
||||
assert "lg:flex-1" in interaction_list_classes
|
||||
assert "lg:overflow-y-auto" in interaction_list_classes
|
||||
assert has_element?(manage_live, ~s(#interaction-drag-list button[phx-click="next-page"]))
|
||||
|
||||
manage_live
|
||||
|> element(~s(#interaction-drag-list button[phx-click="next-page"]))
|
||||
|> render_click()
|
||||
|
||||
assert render(manage_live) =~ "2 / 2"
|
||||
end
|
||||
|
||||
test "shows more interactions when the panel is taller", %{
|
||||
conn: conn,
|
||||
presentation_file: presentation_file
|
||||
} do
|
||||
for index <- 1..13 do
|
||||
poll_fixture(%{
|
||||
presentation_file_id: presentation_file.id,
|
||||
title: "Poll #{index}"
|
||||
})
|
||||
end
|
||||
|
||||
{:ok, manage_live, _html} = live(conn, ~p"/e/#{presentation_file.event.code}/manage")
|
||||
|
||||
assert manage_live
|
||||
|> render()
|
||||
|> Floki.parse_document!()
|
||||
|> Floki.find("[data-interaction-id]")
|
||||
|> length() == 6
|
||||
|
||||
manage_live
|
||||
|> element("#interaction-drag-list")
|
||||
|> render_hook("interaction-list-resized", %{"height" => 900})
|
||||
|
||||
html = render(manage_live)
|
||||
|
||||
assert html
|
||||
|> Floki.parse_document!()
|
||||
|> Floki.find("[data-interaction-id]")
|
||||
|> length() == 10
|
||||
|
||||
assert html =~ "1 / 2"
|
||||
end
|
||||
|
||||
test "prompts to regenerate missing thumbnails and starts regeneration", %{
|
||||
conn: conn,
|
||||
presentation_file: presentation_file
|
||||
|
||||
Reference in New Issue
Block a user