mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-31 01:42:27 +00:00
feat: implement marquee scrolling for video card titles
- Replaced CSS truncation with a custom marquee structure in `VideoCard.js` for titles and author names. - Added `css/tailwind.source.css` utilities for `.marquee-host`, `.marquee-static`, and `.marquee-track` animations. - Implemented `ResizeObserver` (shared singleton) to detect overflow and enable marquee only when needed. - Implemented `IntersectionObserver` (shared singleton) to trigger marquee on mobile when the card is in the center of the viewport. - Configured dynamic animation duration based on text content width to ensure constant scrolling speed. - Verified visual behavior via Playwright screenshots.
This commit is contained in:
1 parent
34d57c2072
commit
c719bdf558
5 files changed
+194
-65
No files matched your search
@@ -11,4 +11,4 @@
|
||||
--player-modal-primary-inline-padding-lg,clamp(var(--space-sm),2.75vw,var(--space-lg))
|
||||
)}}.revert-modal__layout{display:grid;gap:1.5rem}@media (min-width:1024px){.revert-modal__layout{grid-template-columns:minmax(var(--revert-modal-sidebar-min),var(--revert-modal-sidebar-width)) minmax(0,1fr)}}#playerModal.video-modal__primary{display:block}.video-modal__primary{display:flex;flex-direction:column;gap:1.5rem;min-width:0;min-height:0}#playerModal.video-modal__secondary{display:block}.video-modal__secondary{display:flex;flex-direction:column;gap:1rem;min-width:0;min-height:0;width:100%;align-self:stretch}.video-modal__similar{display:none}@media (min-width:1024px){#playerModal.video-modal__similar{display:block}.video-modal__similar{display:flex;flex-direction:column;gap:1.5rem;align-self:stretch;flex:1 1 auto;min-height:0}#playerModal .video-modal__similar .player-modal__module{height:100%;display:flex;flex:1 1 auto;min-height:0;flex-direction:column}#playerModal .video-modal__similar .player-modal__module-list{flex:1 1 auto;min-height:0}#playerModal .video-modal__similar .player-modal__module-scroll{height:100%;max-height:none;min-height:0}}#playerModal .player-modal__module-item{width:100%}#playerModal#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card){display:block}#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card){display:flex;width:100%;flex-direction:row;flex-wrap:nowrap;align-items:stretch;gap:var(--space-md)}#playerModal#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card){display:block}#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card){display:flex;width:100%;flex-direction:row;flex-wrap:nowrap;align-items:stretch;gap:var(--space-md)}#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__media{width:clamp(var(--video-modal-item-media-min),18vw,var(--video-modal-item-media-max))}#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__media{width:clamp(var(--video-modal-item-media-min),18vw,var(--video-modal-item-media-max))!important}#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__media,#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__media{flex-shrink:0;overflow:hidden;border-radius:var(--radius-lg)}#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__content{padding:clamp(var(--space-sm),1.25vw,var(--space-md))}#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__content{padding:clamp(var(--space-sm),1.25vw,var(--space-md))!important}#playerModal#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__content{display:block}#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__content{display:flex;min-width:0;flex:1 1 0%;flex-direction:column;gap:var(--space-sm)}#playerModal#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__content{display:block}#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__content{display:flex;min-width:0;flex:1 1 0%;flex-direction:column;gap:var(--space-sm)}#playerModal#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__meta{display:block}#playerModal .player-modal__module-item>.card:not(.player-modal__similar-card) .video-card__meta{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;font-size:var(--font-size-xs);line-height:var(--line-height-tight);color:var(--text-muted-strong)}#playerModal#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__meta{display:block}#playerModal .player-modal__module-item>.\!card:not(.player-modal__similar-card) .video-card__meta{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;font-size:var(--font-size-xs);line-height:var(--line-height-tight);color:var(--text-muted-strong)}#playerModal .video-modal__similar .player-modal__module-list{gap:clamp(var(--space-2xs),.85vw,var(--space-sm))}.player-modal__similar-card{outline:2px solid transparent;outline-offset:2px}.player-modal__similar-card:focus-visible{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-opacity:0.7;--tw-ring-offset-width:2px;--tw-ring-offset-color:var(--surface-base);transition-property:box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s;transition-duration:var(--motion-duration-fast);transition-timing-function:cubic-bezier(0,0,.2,1);--tw-ring-color:var(--color-accent)}#playerModal.player-modal__similar-card{display:block}.player-modal__similar-card{position:relative;displayLine truncated
|
||||
--motion-video-card-backdrop-translate
|
||||
);--video-card-backdrop-scale:1.12;--video-card-backdrop-blur:var(--blur-video-card-backdrop-strong);--tw-shadow:var(--shadow-lg);--tw-shadow-colored:var(--shadow-lg);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.watch-history-card:focus-visible{border-color:var(--color-accent);--tw-border-opacity:0.4;--tw-shadow:var(--shadow-lg);--tw-shadow-colored:var(--shadow-lg);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.watch-history-card[data-state=critical]{border-width:1px;border-color:var(--color-critical);--tw-border-opacity:0.4;background-color:var(--surface-raised-critical)}.watch-history-card[data-state=private]{background-color:var(--surface-raised-private);--tw-shadow:var(--shadow-lg);--tw-shadow-colored:var(--shadow-lg)}.watch-history-card[data-state=disabled],.watch-history-card[data-state=private]{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.watch-history-card[data-state=disabled]{opacity:.6;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000}.watch-history-card[data-alert=nsfw-owner]{border-width:1px;border-color:var(--color-critical);--tw-border-opacity:0.4;background-color:var(--color-black)}.watch-history-card[data-alert=nsfw-owner]:hover{--tw-border-opacity:0.6;--tw-shadow:var(--shadow-lg);--tw-shadow-colored:var(--shadow-lg);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.watch-history-card img[data-thumbnail-state=blurred],.watch-history-card img[data-visual-state=blurred]{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-duration:.15s;transition-duration:var(--motion-duration-base);transition-timing-function:cubic-bezier(.4,0,.2,1);filter:blur(var(--blur-thumbnail-blur));transform:scale(1.05)}.watch-history-card[data-owner-is-viewer=true][data-url-health-state=offline][data-stream-health-state=unhealthy]{border-width:1px;border-color:var(--color-critical);--tw-border-opacity:0.4;background-color:var(--surface-raised-critical)}#playerModal#playerModal .player-modal__module-item>.watch-history-card:not(.player-modal__similar-card){display:block}#playerModal .player-modal__module-item>.watch-history-card:not(.player-modal__similar-card){display:flex;width:100%;flex-direction:row;flex-wrap:nowrap;align-items:stretch;gap:var(--space-md)}#playerModal .player-modal__module-item>.watch-history-card:not(.player-modal__similar-card) .video-card__media{width:clamp(var(--video-modal-item-media-min),18vw,var(--video-modal-item-media-max));flex-shrink:0;overflow:hidden;border-radius:var(--radius-lg)}#playerModal .player-modal__module-item>.watch-history-card:not(.player-modal__similar-card) .video-card__content{padding:clamp(var(--space-sm),1.25vw,var(--space-md))}#playerModal#playerModal .player-modal__module-item>.watch-history-card:not(.player-modal__similar-card) .video-card__content{display:block}#playerModal .player-modal__module-item>.watch-history-card:not(.player-modal__similar-card) .video-card__content{display:flex;min-width:0;flex:1 1 0%;flex-direction:column;gap:var(--space-sm)}#playerModal#playerModal .player-modal__module-item>.watch-history-card:not(.player-modal__similar-card) .video-card__meta{display:block}#playerModal .player-modal__module-item>.watch-history-card:not(.player-modal__similar-card) .video-card__meta{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;font-size:var(--font-size-xs);line-height:var(--line-height-tight);color:var(--text-muted-strong)}.watch-history-card[data-motion=enter]{opacity:0;animation:card-motion-enter .22s ease-out forwards}#playerModal.watch-history-card{display:block}.watch-history-card{display:flex;flex-wrap:wrap;align-items:stretch;gap:1.25rem;padding:1rem;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s;transition-duration:var(--motion-duration-base);transition-timing-function:cubic-bezier(0,0,.2,1)}@media (prefers-reduced-motion:reduce){.watch-history-card,.watch-history-card[data-motion=enter]{transition-property:none}.watch-history-card[data-motion=enter]{opacity:1;animation:none}}.watch-history-card:hover{--tw-translate-y:-0.125rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));--tw-shadow:var(--shadow-lg);--tw-shadow-colored:var(--shadow-lg);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}@media (min-width:640px){.watch-history-card{gap:1.5rem;padding:1.25rem}}.watch-history-card--private{border-color:var(--color-info);--tw-border-opacity:0.6}#playerModal.watch-histoLine truncated
|
||||
);--video-card-backdrop-scale:1.12;--video-card-backdrop-blur:var(--blur-video-card-backdrop-strong);--tw-shadow:var(--shadow-lg);--tw-shadow-colored:var(--shadow-lg);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.watch-history-card:focus-visible{border-color:var(--color-accent);--tw-border-opacity:0.4;--tw-shadow:var(--shadow-lg);--tw-shadow-colored:var(--shadow-lg);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.watch-history-card[data-state=critical]{border-width:1px;border-color:var(--color-critical);--tw-border-opacity:0.4;background-color:var(--surface-raised-critical)}.watch-history-card[data-state=private]{background-color:var(--surface-raised-private);--tw-shadow:var(--shadow-lg);--tw-shadow-colored:var(--shadow-lg)}.watch-history-card[data-state=disabled],.watch-history-card[data-state=private]{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.watch-history-card[data-state=disabled]{opacity:.6;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000}.watch-history-card[data-alert=nsfw-owner]{border-width:1px;border-color:var(--color-critical);--tw-border-opacity:0.4;background-color:var(--color-black)}.watch-history-card[data-alert=nsfw-owner]:hover{--tw-border-opacity:0.6;--tw-shadow:var(--shadow-lg);--tw-shadow-colored:var(--shadow-lg);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.watch-history-card img[data-thumbnail-state=blurred],.watch-history-card img[data-visual-state=blurred]{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-duration:.15s;transition-duration:var(--motion-duration-base);transition-timing-function:cubic-bezier(.4,0,.2,1);filter:blur(var(--blur-thumbnail-blur));transform:scale(1.05)}.watch-history-card[data-owner-is-viewer=true][data-url-health-state=offline][data-stream-health-state=unhealthy]{border-width:1px;border-color:var(--color-critical);--tw-border-opacity:0.4;background-color:var(--surface-raised-critical)}#playerModal#playerModal .player-modal__module-item>.watch-history-card:not(.player-modal__similar-card){display:block}#playerModal .player-modal__module-item>.watch-history-card:not(.player-modal__similar-card){display:flex;width:100%;flex-direction:row;flex-wrap:nowrap;align-items:stretch;gap:var(--space-md)}#playerModal .player-modal__module-item>.watch-history-card:not(.player-modal__similar-card) .video-card__media{width:clamp(var(--video-modal-item-media-min),18vw,var(--video-modal-item-media-max));flex-shrink:0;overflow:hidden;border-radius:var(--radius-lg)}#playerModal .player-modal__module-item>.watch-history-card:not(.player-modal__similar-card) .video-card__content{padding:clamp(var(--space-sm),1.25vw,var(--space-md))}#playerModal#playerModal .player-modal__module-item>.watch-history-card:not(.player-modal__similar-card) .video-card__content{display:block}#playerModal .player-modal__module-item>.watch-history-card:not(.player-modal__similar-card) .video-card__content{display:flex;min-width:0;flex:1 1 0%;flex-direction:column;gap:var(--space-sm)}#playerModal#playerModal .player-modal__module-item>.watch-history-card:not(.player-modal__similar-card) .video-card__meta{display:block}#playerModal .player-modal__module-item>.watch-history-card:not(.player-modal__similar-card) .video-card__meta{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;font-size:var(--font-size-xs);line-height:var(--line-height-tight);color:var(--text-muted-strong)}.watch-history-card[data-motion=enter]{opacity:0;animation:card-motion-enter .22s ease-out forwards}#playerModal.watch-history-card{display:block}@media (min-width:1024px){.watch-history-card:hover .marquee-host.can-marquee .marquee-static{opacity:0}.watch-history-card:hover .marquee-host.can-marquee .marquee-anim{opacity:1}.watch-history-card:hover .marquee-host.can-marquee .marquee-track{animation-play-state:running}}@media (max-width:1023.98px){.watch-history-card[data-mobile-active=true] .marquee-host.can-marquee .marquee-static{opacity:0}.watch-history-card[data-mobile-active=true] .marquee-host.can-marquee .marquee-anim{opacity:1}.watch-history-card[data-mobile-active=true] .marquee-host.can-marquee .marquee-track{animation-play-state:running}}.watch-history-card{display:flex;flex-wrap:wrap;align-items:stretch;gap:1.25rem;padding:1rem;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s;transition-duration:var(--motion-duration-base);transition-timing-function:cubic-bezier(0,0,.2,1)}@media (prefers-reduced-motion:reduce){.watch-history-card,.watch-history-card[data-motion=enter]{transition-property:none}.watch-history-card[data-motion=enter]{oLine truncated
|
||||
@@ -5849,3 +5849,75 @@
|
||||
max-block-size: none;
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.marquee-host {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
/* Optional: Ensure it takes layout space */
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.marquee-static {
|
||||
@apply truncate;
|
||||
opacity: 1;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.marquee-anim {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
pointer-events: none;
|
||||
mask-image: linear-gradient(to right, transparent, black 4%, black 96%, transparent);
|
||||
-webkit-mask-image: linear-gradient(to right, transparent, black 4%, black 96%, transparent);
|
||||
}
|
||||
|
||||
.marquee-track {
|
||||
display: flex;
|
||||
width: max-content;
|
||||
animation: marquee-scroll linear infinite;
|
||||
animation-duration: var(--marquee-duration, 10s);
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
.marquee-item {
|
||||
flex-shrink: 0;
|
||||
padding-right: 2rem; /* Gap between loop items */
|
||||
}
|
||||
|
||||
@keyframes marquee-scroll {
|
||||
0% { transform: translateX(0); }
|
||||
100% { transform: translateX(-50%); }
|
||||
}
|
||||
|
||||
/* Desktop Hover State */
|
||||
@media (min-width: 1024px) {
|
||||
.card:hover .marquee-host.can-marquee .marquee-static {
|
||||
opacity: 0;
|
||||
}
|
||||
.card:hover .marquee-host.can-marquee .marquee-anim {
|
||||
opacity: 1;
|
||||
}
|
||||
.card:hover .marquee-host.can-marquee .marquee-track {
|
||||
animation-play-state: running;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mobile Active State */
|
||||
@media (max-width: 1023.98px) {
|
||||
.card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-static {
|
||||
opacity: 0;
|
||||
}
|
||||
.card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-anim {
|
||||
opacity: 1;
|
||||
}
|
||||
.card[data-mobile-active="true"] .marquee-host.can-marquee .marquee-track {
|
||||
animation-play-state: running;
|
||||
}
|
||||
}
|
||||
}
|
||||
+121
-10
@@ -386,6 +386,8 @@ export class VideoCard {
|
||||
|
||||
this.root = root;
|
||||
|
||||
VideoCard.observeViewport(this.root);
|
||||
|
||||
this.root.dataset.component = "video-card";
|
||||
this.syncCardState();
|
||||
this.syncMotionState();
|
||||
@@ -449,17 +451,17 @@ export class VideoCard {
|
||||
isCompact ? "text-base" : "text-lg",
|
||||
isCompact ? "font-semibold" : "font-bold",
|
||||
"text-text",
|
||||
"line-clamp-2",
|
||||
"cursor-pointer",
|
||||
];
|
||||
if (isCompact) {
|
||||
titleClassNames.push("leading-snug");
|
||||
}
|
||||
|
||||
const title = this.createElement("h3", {
|
||||
classNames: titleClassNames,
|
||||
textContent: this.video.title
|
||||
});
|
||||
const title = this.buildMarqueeStructure(
|
||||
"h3",
|
||||
titleClassNames,
|
||||
this.video.title
|
||||
);
|
||||
title.dataset.videoId = this.video.id;
|
||||
this.titleEl = title;
|
||||
|
||||
@@ -645,7 +647,7 @@ export class VideoCard {
|
||||
applyIdentityToAuthorElements() {
|
||||
const nameLabel = this.identity?.name || "Unknown";
|
||||
if (this.authorNameEl) {
|
||||
this.authorNameEl.textContent = nameLabel || "Unknown";
|
||||
this.updateMarqueeText(this.authorNameEl, nameLabel || "Unknown");
|
||||
}
|
||||
|
||||
if (this.authorPicEl) {
|
||||
@@ -918,16 +920,17 @@ export class VideoCard {
|
||||
|
||||
const authorMeta = this.createElement("div", { classNames: ["min-w-0"] });
|
||||
|
||||
const authorName = this.createElement("p", {
|
||||
classNames: [
|
||||
const authorName = this.buildMarqueeStructure(
|
||||
"p",
|
||||
[
|
||||
isCompact ? "text-xs" : "text-sm",
|
||||
"text-muted",
|
||||
"author-name",
|
||||
"cursor-pointer",
|
||||
"video-card__author-name",
|
||||
],
|
||||
textContent: "Loading name...",
|
||||
});
|
||||
"Loading name..."
|
||||
);
|
||||
if (this.video.pubkey) {
|
||||
authorName.dataset.pubkey = this.video.pubkey;
|
||||
}
|
||||
@@ -2945,6 +2948,114 @@ export class VideoCard {
|
||||
return false;
|
||||
}
|
||||
|
||||
buildMarqueeStructure(tagName, classNames, textContent, attrs = {}) {
|
||||
const host = this.createElement(tagName, {
|
||||
classNames: [...classNames, "marquee-host"],
|
||||
attrs,
|
||||
});
|
||||
|
||||
// Use span instead of div to ensure valid HTML when nested in h3/p
|
||||
const staticEl = this.createElement("span", {
|
||||
classNames: ["marquee-static", "block"],
|
||||
textContent,
|
||||
});
|
||||
|
||||
const animEl = this.createElement("span", {
|
||||
classNames: ["marquee-anim"],
|
||||
attrs: { "aria-hidden": "true" },
|
||||
});
|
||||
const track = this.createElement("span", { classNames: ["marquee-track"] });
|
||||
|
||||
const item1 = this.createElement("span", {
|
||||
classNames: ["marquee-item"],
|
||||
textContent,
|
||||
});
|
||||
const item2 = this.createElement("span", {
|
||||
classNames: ["marquee-item"],
|
||||
textContent,
|
||||
});
|
||||
|
||||
track.appendChild(item1);
|
||||
track.appendChild(item2);
|
||||
animEl.appendChild(track);
|
||||
|
||||
host.appendChild(staticEl);
|
||||
host.appendChild(animEl);
|
||||
|
||||
host._marqueeRefs = { staticEl, item1, item2, track };
|
||||
|
||||
VideoCard.observeMarquee(host);
|
||||
|
||||
return host;
|
||||
}
|
||||
|
||||
updateMarqueeText(host, text) {
|
||||
if (!host) return;
|
||||
if (!host._marqueeRefs) {
|
||||
host.textContent = text;
|
||||
return;
|
||||
}
|
||||
const { staticEl, item1, item2 } = host._marqueeRefs;
|
||||
if (staticEl) staticEl.textContent = text;
|
||||
if (item1) item1.textContent = text;
|
||||
if (item2) item2.textContent = text;
|
||||
// Trigger check immediately if possible, or wait for observer
|
||||
VideoCard.checkMarqueeOverflow(host);
|
||||
}
|
||||
|
||||
// Moved checkMarqueeOverflow to static to be shared by observer
|
||||
static checkMarqueeOverflow(host) {
|
||||
if (!host || !host._marqueeRefs) return;
|
||||
const { staticEl, track, item1 } = host._marqueeRefs;
|
||||
|
||||
// Use item1 width (full content width) vs host width
|
||||
const contentWidth = item1 ? item1.offsetWidth : staticEl.scrollWidth;
|
||||
const availableWidth = host.clientWidth;
|
||||
|
||||
const isOverflowing = contentWidth > availableWidth;
|
||||
|
||||
if (isOverflowing) {
|
||||
host.classList.add("can-marquee");
|
||||
// Speed: 50px/s
|
||||
// Distance is contentWidth (one item width including padding)
|
||||
const distance = contentWidth;
|
||||
const duration = distance / 50;
|
||||
const safeDuration = Math.max(duration, 2);
|
||||
track.style.setProperty("--marquee-duration", `${safeDuration}s`);
|
||||
} else {
|
||||
host.classList.remove("can-marquee");
|
||||
}
|
||||
}
|
||||
|
||||
static observeMarquee(element) {
|
||||
if (!VideoCard.marqueeResizeObserver) {
|
||||
VideoCard.marqueeResizeObserver = new ResizeObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
VideoCard.checkMarqueeOverflow(entry.target);
|
||||
}
|
||||
});
|
||||
}
|
||||
VideoCard.marqueeResizeObserver.observe(element);
|
||||
}
|
||||
|
||||
static observeViewport(element) {
|
||||
if (!VideoCard.viewportObserver) {
|
||||
VideoCard.viewportObserver = new IntersectionObserver(
|
||||
(entries) => {
|
||||
for (const entry of entries) {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.setAttribute("data-mobile-active", "true");
|
||||
} else {
|
||||
entry.target.removeAttribute("data-mobile-active");
|
||||
}
|
||||
}
|
||||
},
|
||||
{ rootMargin: "-40% 0px -40% 0px", threshold: 0 }
|
||||
);
|
||||
}
|
||||
VideoCard.viewportObserver.observe(element);
|
||||
}
|
||||
|
||||
createElement(tagName, { classNames = [], attrs = {}, textContent } = {}) {
|
||||
const el = this.document.createElement(tagName);
|
||||
classNames
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 43 KiB |
@@ -1,54 +0,0 @@
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
def verify_feed_info(page):
|
||||
print("Pre-seeding localStorage...")
|
||||
page.goto("http://localhost:8000") # Go to root first to set storage origin
|
||||
page.evaluate("window.localStorage.setItem('hasSeenDisclaimer', 'true')")
|
||||
|
||||
print("Navigating to For You view...")
|
||||
page.goto("http://localhost:8000/#view=for-you")
|
||||
|
||||
print("Waiting for feed info trigger...")
|
||||
trigger = page.locator("#feedInfoTrigger")
|
||||
try:
|
||||
trigger.wait_for(state="visible", timeout=10000)
|
||||
except Exception:
|
||||
page.screenshot(path="verification/debug_not_found.png")
|
||||
raise
|
||||
|
||||
# Ensure no modals are blocking
|
||||
# We can try to force click or wait for potential overlays to disappear
|
||||
# But pre-seeding should handle the main one.
|
||||
|
||||
print("Clicking trigger...")
|
||||
trigger.click()
|
||||
|
||||
print("Waiting for popover...")
|
||||
popover = page.locator(".popover-panel")
|
||||
popover.wait_for(state="visible", timeout=5000)
|
||||
|
||||
text = popover.inner_text()
|
||||
print(f"Popover text: {text}")
|
||||
|
||||
expected = "Most Recent Video - Disinterests Tags - Watch History + Interests Tags"
|
||||
if expected not in text:
|
||||
raise Exception(f"Unexpected text: {text}")
|
||||
|
||||
page.screenshot(path="verification/feed_info.png")
|
||||
print("Screenshot saved to verification/feed_info.png")
|
||||
|
||||
if __name__ == "__main__":
|
||||
with sync_playwright() as p:
|
||||
browser = p.chromium.launch(headless=True)
|
||||
context = browser.new_context()
|
||||
page = context.new_page()
|
||||
try:
|
||||
verify_feed_info(page)
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
sys.exit(1)
|
||||
finally:
|
||||
browser.close()
|
||||
Reference in new issue
Block a user