mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-28 16:32:28 +00:00
Align modals with refactored markup
This commit is contained in:
1 parent
ea7df786d2
commit
22cb57ccec
4 files changed
+137
-58
No files matched your search
+20
-25
@@ -345,16 +345,15 @@ export function buildHistoryCard({
|
||||
metadataPreference,
|
||||
}) {
|
||||
const article = document.createElement("article");
|
||||
article.className = "card flex flex-wrap items-stretch gap-6 p-5";
|
||||
article.className = "watch-history-card";
|
||||
article.dataset.pointerKey = item.pointerKey;
|
||||
article.dataset.historyCard = "true";
|
||||
if (video?.isPrivate) {
|
||||
article.dataset.state = "private";
|
||||
} else {
|
||||
delete article.dataset.state;
|
||||
article.classList.add("watch-history-card--private");
|
||||
}
|
||||
|
||||
const primary = document.createElement("div");
|
||||
primary.className = "flex min-w-0 flex-1 gap-4";
|
||||
primary.className = "watch-history-card__primary";
|
||||
|
||||
const playbackData = (() => {
|
||||
if (!video) {
|
||||
@@ -373,8 +372,7 @@ export function buildHistoryCard({
|
||||
const pointerVideoId = getPointerVideoId(video, item.pointer);
|
||||
|
||||
const thumbnailLink = document.createElement("a");
|
||||
thumbnailLink.className =
|
||||
"focus-ring block max-w-[14rem] flex-shrink-0 overflow-hidden rounded-lg bg-slate-900 transition hover:opacity-90";
|
||||
thumbnailLink.className = "watch-history-card__thumbnail";
|
||||
thumbnailLink.href = "#";
|
||||
thumbnailLink.dataset.historyAction = "play";
|
||||
thumbnailLink.dataset.pointerKey = item.pointerKey;
|
||||
@@ -389,9 +387,8 @@ export function buildHistoryCard({
|
||||
}
|
||||
|
||||
const thumbnailInner = document.createElement("div");
|
||||
thumbnailInner.className = "aspect-video w-full overflow-hidden rounded-lg bg-slate-900";
|
||||
thumbnailInner.className = "watch-history-card__thumbnailInner";
|
||||
const thumbnailImg = document.createElement("img");
|
||||
thumbnailImg.className = "h-full w-full object-cover";
|
||||
thumbnailImg.alt = video?.title || "Video thumbnail";
|
||||
const thumbnailSrc =
|
||||
(video && typeof video.thumbnail === "string" && video.thumbnail.trim())
|
||||
@@ -404,11 +401,10 @@ export function buildHistoryCard({
|
||||
thumbnailLink.appendChild(thumbnailInner);
|
||||
|
||||
const details = document.createElement("div");
|
||||
details.className = "flex min-w-0 flex-col justify-center gap-2";
|
||||
details.className = "watch-history-card__details";
|
||||
|
||||
const titleLink = document.createElement("a");
|
||||
titleLink.className =
|
||||
"focus-ring text-left text-lg font-semibold text-white transition hover:text-blue-400";
|
||||
titleLink.className = "watch-history-card__title";
|
||||
titleLink.href = "#";
|
||||
titleLink.dataset.historyAction = "play";
|
||||
titleLink.dataset.pointerKey = item.pointerKey;
|
||||
@@ -424,7 +420,7 @@ export function buildHistoryCard({
|
||||
titleLink.textContent = video?.title || "Untitled video";
|
||||
|
||||
const created = document.createElement("p");
|
||||
created.className = "text-sm text-gray-400";
|
||||
created.className = "watch-history-card__created";
|
||||
const createdAt = Number.isFinite(video?.created_at) ? video.created_at : null;
|
||||
created.textContent = createdAt
|
||||
? `Published ${formatRelativeTime(createdAt)}`
|
||||
@@ -437,29 +433,27 @@ export function buildHistoryCard({
|
||||
primary.appendChild(details);
|
||||
|
||||
const meta = document.createElement("div");
|
||||
meta.className = "flex min-w-[14rem] flex-col justify-between gap-4";
|
||||
meta.className = "watch-history-card__meta";
|
||||
|
||||
const watched = document.createElement("p");
|
||||
watched.className = "text-sm text-gray-500";
|
||||
watched.className = "watch-history-card__watched";
|
||||
watched.textContent = item.watchedAt
|
||||
? `Watched ${formatRelativeTime(item.watchedAt)}`
|
||||
: "Watched time unavailable";
|
||||
meta.appendChild(watched);
|
||||
|
||||
const creator = document.createElement("div");
|
||||
creator.className = "flex items-center gap-3";
|
||||
creator.className = "watch-history-card__creator";
|
||||
|
||||
const creatorAvatarButton = document.createElement("button");
|
||||
creatorAvatarButton.type = "button";
|
||||
creatorAvatarButton.className =
|
||||
"focus-ring flex h-10 w-10 items-center justify-center overflow-hidden rounded-full bg-slate-800";
|
||||
creatorAvatarButton.className = "watch-history-card__creatorAvatar";
|
||||
creatorAvatarButton.dataset.historyAction = "channel";
|
||||
creatorAvatarButton.dataset.pointerKey = item.pointerKey;
|
||||
if (video?.pubkey) {
|
||||
creatorAvatarButton.dataset.author = video.pubkey;
|
||||
}
|
||||
const avatarImg = document.createElement("img");
|
||||
avatarImg.className = "h-full w-full object-cover";
|
||||
const avatarSrc =
|
||||
(profile && typeof profile.picture === "string" && profile.picture.trim())
|
||||
? profile.picture.trim()
|
||||
@@ -470,8 +464,7 @@ export function buildHistoryCard({
|
||||
|
||||
const creatorNameButton = document.createElement("button");
|
||||
creatorNameButton.type = "button";
|
||||
creatorNameButton.className =
|
||||
"focus-ring text-left text-sm font-medium text-gray-200 transition hover:text-blue-400";
|
||||
creatorNameButton.className = "watch-history-card__creatorName";
|
||||
creatorNameButton.dataset.historyAction = "channel";
|
||||
creatorNameButton.dataset.pointerKey = item.pointerKey;
|
||||
if (video?.pubkey) {
|
||||
@@ -491,11 +484,12 @@ export function buildHistoryCard({
|
||||
meta.appendChild(creator);
|
||||
|
||||
const actions = document.createElement("div");
|
||||
actions.className = "flex flex-wrap items-center gap-3";
|
||||
actions.className = "watch-history-card__actions";
|
||||
|
||||
const playButton = document.createElement("button");
|
||||
playButton.type = "button";
|
||||
playButton.className = "btn focus-ring";
|
||||
playButton.className =
|
||||
"watch-history-card__action watch-history-card__action--primary";
|
||||
playButton.dataset.historyAction = "play";
|
||||
playButton.dataset.pointerKey = item.pointerKey;
|
||||
if (pointerVideoId) {
|
||||
@@ -511,7 +505,7 @@ export function buildHistoryCard({
|
||||
|
||||
const channelButton = document.createElement("button");
|
||||
channelButton.type = "button";
|
||||
channelButton.className = "btn-ghost focus-ring";
|
||||
channelButton.className = "watch-history-card__action";
|
||||
channelButton.dataset.historyAction = "channel";
|
||||
channelButton.dataset.pointerKey = item.pointerKey;
|
||||
if (video?.pubkey) {
|
||||
@@ -521,7 +515,8 @@ export function buildHistoryCard({
|
||||
|
||||
const removeButton = document.createElement("button");
|
||||
removeButton.type = "button";
|
||||
removeButton.className = "btn-ghost focus-ring";
|
||||
removeButton.className =
|
||||
"watch-history-card__action watch-history-card__action--danger";
|
||||
removeButton.dataset.variant = "danger";
|
||||
removeButton.dataset.historyAction = "remove";
|
||||
removeButton.dataset.pointerKey = item.pointerKey;
|
||||
|
||||
@@ -43,6 +43,7 @@ export class EditModal {
|
||||
|
||||
this.root = null;
|
||||
this.overlay = null;
|
||||
this.panel = null;
|
||||
this.form = null;
|
||||
this.closeButton = null;
|
||||
this.cancelButton = null;
|
||||
@@ -140,7 +141,14 @@ export class EditModal {
|
||||
}
|
||||
|
||||
cacheElements(context) {
|
||||
this.overlay = context.querySelector("#editVideoModalOverlay") || null;
|
||||
this.overlay =
|
||||
context.querySelector("#editVideoModalOverlay") ||
|
||||
context.querySelector(".bv-modal-backdrop") ||
|
||||
null;
|
||||
this.panel =
|
||||
context.querySelector(".bv-modal__panel") ||
|
||||
context.querySelector(".modal-content") ||
|
||||
null;
|
||||
this.form = context.querySelector("#editVideoForm") || null;
|
||||
this.closeButton = context.querySelector("#closeEditVideoModal") || null;
|
||||
this.cancelButton = context.querySelector("#cancelEditVideo") || null;
|
||||
@@ -388,8 +396,19 @@ export class EditModal {
|
||||
|
||||
if (this.root) {
|
||||
this.root.classList.remove("hidden");
|
||||
this.root.setAttribute("aria-hidden", "false");
|
||||
this.isVisible = true;
|
||||
this.setGlobalModalState("editVideo", true);
|
||||
|
||||
const focusTarget =
|
||||
this.form?.querySelector("[data-initial-focus]") ||
|
||||
this.panel ||
|
||||
this.root;
|
||||
window.requestAnimationFrame(() => {
|
||||
if (typeof focusTarget?.focus === "function") {
|
||||
focusTarget.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -532,6 +551,7 @@ export class EditModal {
|
||||
close({ emitCancel = false } = {}) {
|
||||
if (this.root) {
|
||||
this.root.classList.add("hidden");
|
||||
this.root.setAttribute("aria-hidden", "true");
|
||||
}
|
||||
this.isVisible = false;
|
||||
this.setGlobalModalState("editVideo", false);
|
||||
|
||||
@@ -655,6 +655,9 @@ export class ProfileModalController {
|
||||
};
|
||||
|
||||
this.profileModal = null;
|
||||
this.profileModalRoot = null;
|
||||
this.profileModalPanel = null;
|
||||
this.profileModalBackdrop = null;
|
||||
this.profileAvatar = null;
|
||||
this.profileName = null;
|
||||
this.profileNpub = null;
|
||||
@@ -743,6 +746,7 @@ export class ProfileModalController {
|
||||
this.boundKeydown = null;
|
||||
this.boundFocusIn = null;
|
||||
this.focusableElements = [];
|
||||
this.focusTrapContainer = null;
|
||||
this.profileSwitcherSelectionPubkey = null;
|
||||
this.previouslyFocusedElement = null;
|
||||
this.setActivePane(this.getActivePane());
|
||||
@@ -771,7 +775,12 @@ export class ProfileModalController {
|
||||
}
|
||||
|
||||
this.modalContainer.appendChild(template.content);
|
||||
this.profileModalRoot = modalRoot;
|
||||
this.profileModal = modalRoot;
|
||||
this.profileModalPanel =
|
||||
modalRoot.querySelector(".bv-modal__panel") || modalRoot;
|
||||
this.profileModalBackdrop =
|
||||
modalRoot.querySelector(".bv-modal-backdrop") || null;
|
||||
|
||||
this.cacheDomReferences();
|
||||
this.registerEventListeners();
|
||||
@@ -782,7 +791,12 @@ export class ProfileModalController {
|
||||
}
|
||||
|
||||
cacheDomReferences() {
|
||||
this.profileModal = document.getElementById("profileModal") || null;
|
||||
this.profileModalRoot = document.getElementById("profileModal") || null;
|
||||
this.profileModalPanel =
|
||||
this.profileModalRoot?.querySelector(".bv-modal__panel") || null;
|
||||
this.profileModalBackdrop =
|
||||
this.profileModalRoot?.querySelector(".bv-modal-backdrop") || null;
|
||||
this.profileModal = this.profileModalRoot;
|
||||
this.closeButton = document.getElementById("closeProfileModal") || null;
|
||||
this.logoutButton = document.getElementById("profileLogoutBtn") || null;
|
||||
this.channelLink = document.getElementById("profileChannelLink") || null;
|
||||
@@ -1284,7 +1298,6 @@ export class ProfileModalController {
|
||||
normalizedSelection && normalizedPubkey === normalizedSelection;
|
||||
if (isSelected) {
|
||||
button.dataset.state = "active";
|
||||
button.classList.add("ring-1", "ring-blue-400/60", "bg-blue-500/10");
|
||||
button.setAttribute("aria-pressed", "true");
|
||||
} else {
|
||||
delete button.dataset.state;
|
||||
@@ -3598,16 +3611,21 @@ export class ProfileModalController {
|
||||
}
|
||||
|
||||
updateFocusTrap() {
|
||||
if (!(this.profileModal instanceof HTMLElement)) {
|
||||
const container =
|
||||
this.profileModalPanel instanceof HTMLElement
|
||||
? this.profileModalPanel
|
||||
: this.profileModal instanceof HTMLElement
|
||||
? this.profileModal
|
||||
: null;
|
||||
|
||||
if (!container) {
|
||||
this.focusableElements = [];
|
||||
return;
|
||||
}
|
||||
|
||||
const selector =
|
||||
'a[href], button:not([disabled]), textarea, input, select, [tabindex]:not([tabindex="-1"])';
|
||||
const nodes = Array.from(
|
||||
this.profileModal.querySelectorAll(selector),
|
||||
);
|
||||
const nodes = Array.from(container.querySelectorAll(selector));
|
||||
this.focusableElements = nodes.filter((node) => {
|
||||
if (!(node instanceof HTMLElement)) {
|
||||
return false;
|
||||
@@ -3625,11 +3643,19 @@ export class ProfileModalController {
|
||||
return true;
|
||||
});
|
||||
|
||||
this.bindFocusTrap();
|
||||
this.bindFocusTrap(container);
|
||||
}
|
||||
|
||||
bindFocusTrap() {
|
||||
if (!(this.profileModal instanceof HTMLElement)) {
|
||||
bindFocusTrap(container) {
|
||||
const targetContainer =
|
||||
container ||
|
||||
(this.profileModalPanel instanceof HTMLElement
|
||||
? this.profileModalPanel
|
||||
: this.profileModal instanceof HTMLElement
|
||||
? this.profileModal
|
||||
: null);
|
||||
|
||||
if (!targetContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3647,8 +3673,10 @@ export class ProfileModalController {
|
||||
|
||||
if (!this.focusableElements.length) {
|
||||
event.preventDefault();
|
||||
if (typeof this.profileModal.focus === "function") {
|
||||
this.profileModal.focus();
|
||||
const fallback =
|
||||
this.profileModalPanel || this.profileModal || targetContainer;
|
||||
if (typeof fallback?.focus === "function") {
|
||||
fallback.focus();
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -3658,7 +3686,7 @@ export class ProfileModalController {
|
||||
const active = document.activeElement;
|
||||
|
||||
if (event.shiftKey) {
|
||||
if (active === first || !this.profileModal.contains(active)) {
|
||||
if (active === first || !targetContainer.contains(active)) {
|
||||
event.preventDefault();
|
||||
if (typeof last?.focus === "function") {
|
||||
last.focus();
|
||||
@@ -3678,22 +3706,36 @@ export class ProfileModalController {
|
||||
|
||||
if (!this.boundFocusIn) {
|
||||
this.boundFocusIn = (event) => {
|
||||
const modalRoot = this.profileModalRoot || this.profileModal;
|
||||
if (
|
||||
!this.profileModal ||
|
||||
this.profileModal.classList.contains("hidden") ||
|
||||
this.profileModal.contains(event.target)
|
||||
!modalRoot ||
|
||||
modalRoot.classList.contains("hidden") ||
|
||||
modalRoot.contains(event.target)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const target = this.focusableElements[0] || this.profileModal;
|
||||
if (typeof target?.focus === "function") {
|
||||
target.focus();
|
||||
const fallback =
|
||||
this.focusableElements[0] ||
|
||||
this.profileModalPanel ||
|
||||
this.profileModal;
|
||||
if (typeof fallback?.focus === "function") {
|
||||
fallback.focus();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
this.profileModal.addEventListener("keydown", this.boundKeydown);
|
||||
if (
|
||||
this.focusTrapContainer &&
|
||||
this.focusTrapContainer !== targetContainer &&
|
||||
this.boundKeydown
|
||||
) {
|
||||
this.focusTrapContainer.removeEventListener("keydown", this.boundKeydown);
|
||||
}
|
||||
|
||||
targetContainer.addEventListener("keydown", this.boundKeydown);
|
||||
this.focusTrapContainer = targetContainer;
|
||||
|
||||
document.addEventListener("focusin", this.boundFocusIn);
|
||||
}
|
||||
|
||||
@@ -3707,11 +3749,14 @@ export class ProfileModalController {
|
||||
document.activeElement instanceof HTMLElement
|
||||
? document.activeElement
|
||||
: null;
|
||||
if (
|
||||
activeElement &&
|
||||
this.profileModal instanceof HTMLElement &&
|
||||
this.profileModal.contains(activeElement)
|
||||
) {
|
||||
const modalRoot =
|
||||
this.profileModalRoot instanceof HTMLElement
|
||||
? this.profileModalRoot
|
||||
: this.profileModal instanceof HTMLElement
|
||||
? this.profileModal
|
||||
: null;
|
||||
|
||||
if (activeElement && modalRoot && modalRoot.contains(activeElement)) {
|
||||
this.previouslyFocusedElement = null;
|
||||
} else {
|
||||
this.previouslyFocusedElement = activeElement;
|
||||
@@ -3757,16 +3802,26 @@ export class ProfileModalController {
|
||||
}
|
||||
|
||||
open(pane = "account") {
|
||||
if (!(this.profileModal instanceof HTMLElement)) {
|
||||
const modalRoot =
|
||||
this.profileModalRoot instanceof HTMLElement
|
||||
? this.profileModalRoot
|
||||
: this.profileModal instanceof HTMLElement
|
||||
? this.profileModal
|
||||
: null;
|
||||
|
||||
if (!modalRoot) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.profileModal.classList.remove("hidden");
|
||||
this.profileModal.setAttribute("aria-hidden", "false");
|
||||
modalRoot.classList.remove("hidden");
|
||||
modalRoot.setAttribute("aria-hidden", "false");
|
||||
this.setGlobalModalState("profile", true);
|
||||
this.selectPane(pane);
|
||||
|
||||
const focusTarget = this.focusableElements[0] || this.profileModal;
|
||||
const focusTarget =
|
||||
this.focusableElements[0] ||
|
||||
this.profileModalPanel ||
|
||||
modalRoot;
|
||||
window.requestAnimationFrame(() => {
|
||||
if (typeof focusTarget?.focus === "function") {
|
||||
focusTarget.focus();
|
||||
@@ -3779,15 +3834,22 @@ export class ProfileModalController {
|
||||
options && typeof options === "object" ? options : {};
|
||||
|
||||
const modalElement =
|
||||
this.profileModal instanceof HTMLElement ? this.profileModal : null;
|
||||
this.profileModalRoot instanceof HTMLElement
|
||||
? this.profileModalRoot
|
||||
: this.profileModal instanceof HTMLElement
|
||||
? this.profileModal
|
||||
: null;
|
||||
|
||||
if (modalElement) {
|
||||
modalElement.classList.add("hidden");
|
||||
modalElement.setAttribute("aria-hidden", "true");
|
||||
this.setGlobalModalState("profile", false);
|
||||
|
||||
if (this.boundKeydown) {
|
||||
modalElement.removeEventListener("keydown", this.boundKeydown);
|
||||
if (this.boundKeydown && this.focusTrapContainer) {
|
||||
this.focusTrapContainer.removeEventListener(
|
||||
"keydown",
|
||||
this.boundKeydown,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3795,6 +3857,8 @@ export class ProfileModalController {
|
||||
document.removeEventListener("focusin", this.boundFocusIn);
|
||||
}
|
||||
|
||||
this.focusTrapContainer = null;
|
||||
|
||||
if (
|
||||
this.boundProfileHistoryVisibility &&
|
||||
typeof document !== "undefined" &&
|
||||
|
||||
@@ -10,7 +10,7 @@ function resolveCardElement(trigger) {
|
||||
if (!(trigger instanceof HTMLElement)) {
|
||||
return null;
|
||||
}
|
||||
const candidate = trigger.closest(".video-card");
|
||||
const candidate = trigger.closest("[data-history-card]");
|
||||
return candidate instanceof HTMLElement ? candidate : null;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user