Newer
Older
Sakayaki / Pages / Folder.cshtml
@fabre fabre 10 hours ago 5 KB 超级缩略图
@page
@model FolderModel
@{
    ViewData["Title"] = Model.Folder?.Title ?? "Folder";
}

<div class="mt-3">
    <div class="d-flex flex-wrap gap-2 align-items-center mb-3">
        <div class="d-flex flex-wrap gap-2 align-items-center flex-grow-1">
            <h2 class="h5 mb-0">@Model.Folder?.Title</h2>
            @if (Model.Folder is not null)
            {
                <span class="text-muted small">@Model.Folder.Date.ToString("yyyy-MM-dd") · @Model.Folder.Author · @((Model.Folder.FileCount ?? 0).ToString()) files</span>
            }
            @if (Model.Keywords.Count > 0)
            {
                <div class="d-flex flex-wrap gap-1">
                    @foreach (var tag in Model.Keywords)
                    {
                        <a class="badge text-bg-light text-decoration-none" href="@Url.Page("Index", null, new { keyword = tag })">@tag</a>
                    }
                </div>
            }
        </div>
        <a class="btn btn-outline-secondary" href="@Model.BackUrl">Back</a>
    </div>

    @if (Model.Folder is null)
    {
        <div class="alert alert-secondary">Folder not found.</div>
    }
    else if (Model.Images.Count == 0)
    {
        <div class="alert alert-secondary">No images found.</div>
    }
    else
    {
        <div id="thumbs" class="row g-3">
            @foreach (var image in Model.Images)
            {
                <div class="col-6 col-md-4 col-lg-3">
                    <button class="card text-start border-0 shadow-sm w-100"
                            type="button"
                            data-thumb="true"
                            data-url="@image.Url"
                            data-name="@image.FileName">
                        <img class="card-img-top" src="@image.ThumbnailUrl" alt="@image.FileName" loading="lazy" style="aspect-ratio: 1 / 1; object-fit: cover;" />
                    </button>
                </div>
            }
        </div>
    }
</div>

<div class="modal fade" id="folderImageModal" tabindex="-1" aria-labelledby="folderImageModalLabel" aria-hidden="true">
    <div class="modal-dialog modal-fullscreen modal-xl modal-dialog-centered">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title" id="folderImageModalLabel">Image</h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>
            <div class="modal-body">
                <img id="folderModalImage" class="img-fluid d-block mx-auto" alt="image" />
            </div>
        </div>
    </div>
</div>

@section Scripts {
    <script>
        const modalEl = document.getElementById("folderImageModal");
        const modalImage = document.getElementById("folderModalImage");
        const modalTitle = document.getElementById("folderImageModalLabel");
        const modalBody = modalEl?.querySelector(".modal-body");
        const modal = new bootstrap.Modal(modalEl);
        const thumbs = Array.from(document.querySelectorAll("[data-thumb='true']"));
        let currentIndex = 0;

        function setModalByIndex(index) {
            if (thumbs.length === 0) {
                return;
            }

            const clamped = Math.max(0, Math.min(index, thumbs.length - 1));
            const target = thumbs[clamped];
            const url = target.dataset.url;
            const name = target.dataset.name || "image";
            if (url) {
                modalImage.src = url;
                modalImage.alt = name;
                modalTitle.textContent = name;
                currentIndex = clamped;
            }
        }

        thumbs.forEach((button, index) => {
            button.addEventListener("click", () => {
                currentIndex = index;
                setModalByIndex(currentIndex);
                modal.show();
            });
        });

        document.addEventListener("keydown", event => {
            if (!modalEl?.classList.contains("show")) {
                return;
            }

            if (event.key === "ArrowLeft") {
                event.preventDefault();
                setModalByIndex(currentIndex - 1);
            } else if (event.key === "ArrowRight") {
                event.preventDefault();
                setModalByIndex(currentIndex + 1);
            }
        });

        if (modalBody) {
            let touchStartX = 0;
            let touchStartY = 0;

            modalBody.addEventListener("touchstart", event => {
                const touch = event.touches[0];
                if (!touch) {
                    return;
                }
                touchStartX = touch.clientX;
                touchStartY = touch.clientY;
            }, { passive: true });

            modalBody.addEventListener("touchend", event => {
                const touch = event.changedTouches[0];
                if (!touch) {
                    return;
                }

                const dx = touch.clientX - touchStartX;
                const dy = touch.clientY - touchStartY;
                if (Math.abs(dx) < 40 || Math.abs(dx) < Math.abs(dy)) {
                    return;
                }

                if (dx > 0) {
                    setModalByIndex(currentIndex - 1);
                } else {
                    setModalByIndex(currentIndex + 1);
                }
            }, { passive: true });
        }
    </script>
}