Adjust interaction list paging on resize

This commit is contained in:
Alex Lion
2026-08-20 19:04:58 +07:00
parent 36a89c53dc
commit cef515c527
5 changed files with 125 additions and 6 deletions

View File

@@ -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

View File

@@ -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) {

View File

@@ -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}

View File

@@ -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

View File

@@ -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