Merge pull request #1207 from PR0M3TH3AN/codex/audit-and-refactor-.style-usages

Replace inline .style usage with CSS classes/variables and stylesheet-driven state
This commit is contained in:
thePR0M3TH3AN authored and GitHub committed 2026-01-13 21:15:16 -05:00
commit 7719815d78
14 files changed
+579 -224

No files matched your search

+71 -3
View File
@@ -448,6 +448,11 @@
.popover-panel,
.popover__panel {
@apply focus-ring pointer-events-none z-overlay-popover min-w-menu max-w-popover-safe scale-95 rounded-overlay border border-border border-opacity-40 bg-overlay-panel px-overlay-panel-inline py-overlay-panel-block text-sm text-text opacity-0 shadow-overlay-panel transition-opacity transition-transform duration-base ease-out;
position: var(--popover-strategy, fixed);
left: var(--popover-left, 0px);
top: var(--popover-top, 0px);
max-width: var(--popover-max-width, none);
max-height: var(--popover-max-height, none);
}
.popover-panel[data-state="open"],
@@ -460,6 +465,13 @@
@apply pointer-events-none scale-95 opacity-0;
}
[data-popover-arrow="true"] {
left: var(--popover-arrow-left, auto);
right: var(--popover-arrow-right, auto);
top: var(--popover-arrow-top, auto);
bottom: var(--popover-arrow-bottom, auto);
}
.p-overlay-panel {
@apply px-overlay-panel-inline py-overlay-panel-block;
}
@@ -808,6 +820,10 @@
@apply inline-flex flex-col items-start gap-sm rounded-xl border px-3 py-1.5 text-xs font-semibold normal-case tracking-tight shadow-sm;
}
.moderation-badge--interactive {
pointer-events: auto;
}
.moderation-badge__actions {
@apply inline-flex flex-nowrap items-center gap-sm;
}
@@ -2104,6 +2120,9 @@
@apply relative block shrink-0 overflow-hidden rounded-lg bg-surface-alt;
width: 168px;
aspect-ratio: 16 / 9;
background-image: var(--similar-card-thumb-url, none);
background-position: center;
background-size: cover;
}
@media (max-width: theme("screens.sm")) {
@@ -2126,6 +2145,7 @@
.player-modal__similar-card-title {
@apply text-sm font-semibold leading-snug text-text transition-colors duration-base ease-out;
min-width: 0;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
@@ -2160,6 +2180,7 @@
.player-modal__similar-card-meta {
@apply flex flex-wrap items-center gap-1 text-2xs text-muted-strong;
min-width: 0;
}
.player-modal__similar-card-timestamp {
@@ -2442,6 +2463,8 @@
inset-block-start: 0;
block-size: 100%;
max-inline-size: 100%;
inline-size: var(--video-modal-reaction-fill, 0%);
width: var(--video-modal-reaction-fill, 0%);
background-image: linear-gradient(
90deg,
var(--color-accent) 0%,
@@ -3338,6 +3361,7 @@
.qr-code__table {
border: 0;
border-collapse: collapse;
margin: var(--qr-code-table-margin, 0);
}
.qr-code__cell {
@@ -3354,6 +3378,22 @@
background-color: var(--qr-code-color-dark);
}
.qr-code__container[data-qr-code-mode="image"] .qr-code__canvas {
display: none;
}
.qr-code__container[data-qr-code-mode="image"] .qr-code__image {
display: block;
}
.qr-code__container[data-qr-code-mode="canvas"] .qr-code__canvas {
display: block;
}
.qr-code__container[data-qr-code-mode="canvas"] .qr-code__image {
display: none;
}
.fade-in {
opacity: 0;
animation: interface-fade-in 280ms ease-out forwards;
@@ -3568,18 +3608,26 @@
z-index: var(--z-modal-content);
}
#modalContainer > #profileModal {
#modalContainer > #profileModal,
#profileModal[data-modal-stack="top"] {
z-index: var(--z-modal-top-root);
}
#modalContainer > #profileModal > .bv-modal-backdrop {
#modalContainer > #profileModal > .bv-modal-backdrop,
#profileModal[data-modal-stack="top"] > .bv-modal-backdrop {
z-index: var(--z-modal-top-overlay);
}
#modalContainer > #profileModal .bv-modal__panel {
#modalContainer > #profileModal .bv-modal__panel,
#profileModal[data-modal-stack="top"] .bv-modal__panel {
z-index: var(--z-modal-top-content);
}
.bv-modal[data-nested-modal-active="true"] {
pointer-events: none;
visibility: hidden;
}
#modalContainer > #loginModal {
z-index: var(--z-modal-peak-root);
}
@@ -4389,6 +4437,26 @@
scrollbar-gutter: stable both-edges;
}
.sidebar-dropup-panel__inner.is-measuring {
position: absolute;
width: max-content;
max-width: none;
visibility: hidden;
pointer-events: none;
left: -9999px;
right: auto;
top: auto;
bottom: auto;
}
.sidebar-dropup-measure {
position: absolute;
visibility: hidden;
pointer-events: none;
height: 0;
overflow: hidden;
}
.sidebar-dropup-panel__inner > * {
width: 100%;
min-width: 0;
+12 -6
View File
@@ -34,6 +34,16 @@
color: #cbd5f5;
margin-bottom: 1rem;
}
.fixture-toggle {
margin-top: 0.75rem;
font-size: 0.875rem;
color: #cbd5f5;
}
.fixture-toggle__label {
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
</style>
</head>
<body data-ds="new">
@@ -581,14 +591,10 @@
if (fixture.blockToggle && fixture.blockToggle.pubkey) {
const toggleWrapper = document.createElement("div");
toggleWrapper.style.marginTop = "0.75rem";
toggleWrapper.style.fontSize = "0.875rem";
toggleWrapper.style.color = "#cbd5f5";
toggleWrapper.classList.add("fixture-toggle");
const label = document.createElement("label");
label.style.display = "inline-flex";
label.style.alignItems = "center";
label.style.gap = "0.5rem";
label.classList.add("fixture-toggle__label");
const input = document.createElement("input");
input.type = "checkbox";
+48 -4
View File
@@ -90,6 +90,53 @@ const channelModerationBadgeState = {
};
const channelVideoCardsById = new Map();
let channelBannerStyleCounter = 0;
const channelBannerStyleRegistry = new WeakMap();
function getChannelBannerStyleState(doc) {
if (!doc || typeof doc.createElement !== "function") {
return null;
}
const existing = channelBannerStyleRegistry.get(doc);
if (existing) {
return existing;
}
let styleNode = doc.getElementById?.("channelBannerStyles") || null;
if (!(styleNode instanceof HTMLStyleElement)) {
styleNode = doc.createElement("style");
styleNode.id = "channelBannerStyles";
doc.head?.appendChild(styleNode);
}
const state = {
styleNode,
rules: new Map(),
};
channelBannerStyleRegistry.set(doc, state);
return state;
}
function updateChannelBannerBackground(el, url) {
const doc = el?.ownerDocument || (typeof document !== "undefined" ? document : null);
const state = getChannelBannerStyleState(doc);
if (!state || !el) {
return;
}
if (!el.dataset.channelBannerStyleId) {
channelBannerStyleCounter += 1;
el.dataset.channelBannerStyleId = `channel-banner-${channelBannerStyleCounter}`;
}
const selector = `[data-channel-banner-style-id="${el.dataset.channelBannerStyleId}"]`;
if (url) {
const escaped = url.replace(/(["\\])/g, "\\$1");
state.rules.set(selector, `${selector} { background-image: url("${escaped}"); }`);
} else {
state.rules.set(selector, `${selector} { background-image: none; }`);
}
state.styleNode.textContent = [...state.rules.values()].join("\n");
}
export function clearChannelVideoCardRegistry() {
channelVideoCardsById.clear();
@@ -1432,10 +1479,7 @@ function setBannerVisual(el, url, { referrerPolicy } = {}) {
el.src = resolvedUrl;
}
} else {
const value = resolvedUrl ? `url("${resolvedUrl}")` : "";
if (el.style.backgroundImage !== value) {
el.style.backgroundImage = value;
}
updateChannelBannerBackground(el, resolvedUrl || "");
}
if (el.dataset.bannerSrc !== resolvedUrl) {
+55 -46
View File
@@ -885,22 +885,67 @@ async function bootstrapInterface() {
};
};
const sidebarDropupStyleState = (() => {
let styleNode = null;
let rootWidthRule = "";
const measureRules = new Map();
const ensureStyleNode = () => {
if (styleNode && styleNode.isConnected) {
return styleNode;
}
styleNode = document.getElementById("sidebarDropupStyles");
if (!(styleNode instanceof HTMLStyleElement)) {
styleNode = document.createElement("style");
styleNode.id = "sidebarDropupStyles";
document.head.appendChild(styleNode);
}
return styleNode;
};
const updateStyles = () => {
const node = ensureStyleNode();
const rules = [rootWidthRule, ...measureRules.values()].filter(Boolean);
node.textContent = rules.join("\n");
};
return {
setRootWidth: (value) => {
rootWidthRule = `:root { --sidebar-dropup-content-width: ${value}; }`;
updateStyles();
},
setMeasureWidth: (id, value) => {
measureRules.set(
id,
`.sidebar-dropup-measure[data-measure-id="${id}"] { width: ${value}; }`,
);
updateStyles();
},
clearMeasure: (id) => {
measureRules.delete(id);
updateStyles();
},
};
})();
let sidebarDropupMeasureId = 0;
const resolveCssLengthToPixels = (value, container) => {
if (!(container instanceof HTMLElement) || typeof value !== "string") {
return 0;
}
const measurementNode = container.ownerDocument.createElement("div");
measurementNode.style.position = "absolute";
measurementNode.style.visibility = "hidden";
measurementNode.style.pointerEvents = "none";
measurementNode.style.height = "0";
measurementNode.style.width = value;
measurementNode.style.overflow = "hidden";
sidebarDropupMeasureId += 1;
const measureId = `sidebar-dropup-measure-${sidebarDropupMeasureId}`;
measurementNode.classList.add("sidebar-dropup-measure");
measurementNode.dataset.measureId = measureId;
sidebarDropupStyleState.setMeasureWidth(measureId, value);
container.appendChild(measurementNode);
const pixels = measurementNode.getBoundingClientRect().width;
container.removeChild(measurementNode);
sidebarDropupStyleState.clearMeasure(measureId);
return Number.isFinite(pixels) ? pixels : 0;
};
@@ -910,46 +955,13 @@ async function bootstrapInterface() {
const panel = panelInner?.closest(".sidebar-dropup-panel");
if (!(panelInner instanceof HTMLElement) || !(panel instanceof HTMLElement)) {
document.documentElement.style.setProperty(
"--sidebar-dropup-content-width",
"0px",
);
sidebarDropupStyleState.setRootWidth("0px");
return;
}
const originalStyles = {
position: panelInner.style.position,
width: panelInner.style.width,
maxWidth: panelInner.style.maxWidth,
visibility: panelInner.style.visibility,
pointerEvents: panelInner.style.pointerEvents,
left: panelInner.style.left,
right: panelInner.style.right,
top: panelInner.style.top,
bottom: panelInner.style.bottom,
};
panelInner.style.position = "absolute";
panelInner.style.width = "max-content";
panelInner.style.maxWidth = "none";
panelInner.style.visibility = "hidden";
panelInner.style.pointerEvents = "none";
panelInner.style.left = "-9999px";
panelInner.style.right = "auto";
panelInner.style.top = "auto";
panelInner.style.bottom = "auto";
panelInner.classList.add("is-measuring");
const measuredInnerWidth = Math.ceil(panelInner.scrollWidth);
panelInner.style.position = originalStyles.position;
panelInner.style.width = originalStyles.width;
panelInner.style.maxWidth = originalStyles.maxWidth;
panelInner.style.visibility = originalStyles.visibility;
panelInner.style.pointerEvents = originalStyles.pointerEvents;
panelInner.style.left = originalStyles.left;
panelInner.style.right = originalStyles.right;
panelInner.style.top = originalStyles.top;
panelInner.style.bottom = originalStyles.bottom;
panelInner.classList.remove("is-measuring");
const panelStyles = window.getComputedStyle(panel);
const paddingInlineStart = Number.parseFloat(panelStyles.paddingInlineStart) || 0;
@@ -965,10 +977,7 @@ async function bootstrapInterface() {
measuredInnerWidth + paddingInlineStart + paddingInlineEnd + scrollReserve,
);
document.documentElement.style.setProperty(
"--sidebar-dropup-content-width",
`${Math.ceil(totalWidth)}px`,
);
sidebarDropupStyleState.setRootWidth(`${Math.ceil(totalWidth)}px`);
};
const debouncedSidebarDropupResize = debounce(updateSidebarDropupContentWidth, 150);
+50 -17
View File
@@ -6,6 +6,48 @@ import { buildModerationBadgeText } from "../moderationCopy.js";
const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
const DEFAULT_PROFILE_AVATAR = "assets/svg/default-profile.svg";
let similarCardIdCounter = 0;
const similarCardStyleRegistry = new WeakMap();
function getSimilarCardStyleState(doc) {
if (!doc || typeof doc.createElement !== "function") {
return null;
}
const existing = similarCardStyleRegistry.get(doc);
if (existing) {
return existing;
}
let styleNode = doc.getElementById?.("similarContentCardStyles") || null;
if (!(styleNode instanceof HTMLStyleElement)) {
styleNode = doc.createElement("style");
styleNode.id = "similarContentCardStyles";
doc.head?.appendChild(styleNode);
}
const state = {
styleNode,
rules: new Map(),
};
similarCardStyleRegistry.set(doc, state);
return state;
}
function updateSimilarCardBackdrop(doc, cardId, url) {
const state = getSimilarCardStyleState(doc);
if (!state || !cardId) {
return;
}
if (url) {
state.rules.set(
cardId,
`[data-similar-card-id="${cardId}"] { --similar-card-thumb-url: url("${url}"); }`,
);
} else {
state.rules.delete(cardId);
}
state.styleNode.textContent = [...state.rules.values()].join("\n");
}
export class SimilarContentCard {
constructor({
@@ -60,6 +102,7 @@ export class SimilarContentCard {
this.callbacks = { onPlay: null };
this.root = null;
this.cardStyleId = "";
this.mediaLinkEl = null;
this.thumbnailEl = null;
this.contentEl = null;
@@ -330,6 +373,9 @@ export class SimilarContentCard {
if (this.video.id) {
root.dataset.videoId = this.video.id;
}
similarCardIdCounter += 1;
this.cardStyleId = `similar-card-${similarCardIdCounter}`;
root.dataset.similarCardId = this.cardStyleId;
const dsMode = this.designSystem?.getMode?.();
if (dsMode) {
root.setAttribute("data-ds", dsMode);
@@ -378,14 +424,11 @@ export class SimilarContentCard {
}
const badge = this.document.createElement("div");
badge.className = "moderation-badge opacity-95";
badge.className = "moderation-badge moderation-badge--interactive opacity-95";
badge.dataset.variant = "warning";
badge.dataset.moderationBadge = "true";
badge.dataset.moderationState = "trusted-mute";
// Position it centrally over the thumbnail like in VideoCard
badge.style.pointerEvents = "auto";
const label = this.document.createElement("span");
label.className = "moderation-badge__label inline-flex items-center gap-xs";
@@ -556,14 +599,12 @@ export class SimilarContentCard {
buildContentSection() {
const content = this.document.createElement("div");
content.classList.add("player-modal__similar-card-content");
content.style.minWidth = "0";
const titleLink = this.document.createElement("a");
titleLink.classList.add("player-modal__similar-card-title");
titleLink.href = this.shareUrl;
titleLink.textContent = this.video.title || "Untitled";
titleLink.title = this.video.title || "Untitled";
titleLink.style.minWidth = "0";
const authorStack = this.buildAuthorStack();
const metaRow = this.buildMetaRow();
@@ -588,11 +629,9 @@ export class SimilarContentCard {
buildAuthorStack() {
const wrapper = this.document.createElement("div");
wrapper.classList.add("player-modal__similar-card-author");
wrapper.style.minWidth = "0";
const textWrapper = this.document.createElement("span");
textWrapper.classList.add("player-modal__similar-card-author-meta");
textWrapper.style.minWidth = "0";
const nameEl = this.document.createElement("span");
nameEl.classList.add("author-name", "player-modal__similar-card-author-name");
@@ -653,7 +692,6 @@ export class SimilarContentCard {
buildMetaRow() {
const row = this.document.createElement("div");
row.classList.add("player-modal__similar-card-meta");
row.style.minWidth = "0";
const timeEl = this.document.createElement("time");
timeEl.classList.add("player-modal__similar-card-timestamp");
@@ -870,12 +908,10 @@ export class SimilarContentCard {
}
setCardBackdropImage(src) {
if (!this.root || !this.root.style) {
if (!this.root || !this.cardStyleId) {
return;
}
const style = this.root.style;
const normalizeSource = (raw) => {
if (typeof raw !== "string") {
return "";
@@ -919,12 +955,9 @@ export class SimilarContentCard {
const sanitized = normalizeSource(src);
if (sanitized) {
const escaped = sanitized.replace(/(["\\])/g, "\\$1");
style.setProperty(
"--similar-card-thumb-url",
`url("${escaped}")`
);
updateSimilarCardBackdrop(this.document, this.cardStyleId, escaped);
} else {
style.removeProperty("--similar-card-thumb-url");
updateSimilarCardBackdrop(this.document, this.cardStyleId, "");
}
}
}
+27 -6
View File
@@ -153,6 +153,7 @@ export class VideoModal {
"+": null,
"-": null,
};
this.reactionMeterStyleNode = null;
this.reactionState = {
counts: { "+": 0, "-": 0 },
total: 0,
@@ -3116,9 +3117,32 @@ export class VideoModal {
this.reactionMeterAssistive.textContent = assistiveLabel;
}
if (this.reactionMeterFill) {
this.reactionMeterFill.style.inlineSize = `${likeRatio}%`;
this.reactionMeterFill.style.width = `${likeRatio}%`;
this.updateReactionMeterFill(likeRatio);
}
updateReactionMeterFill(likeRatio) {
const doc = this.playerModal?.ownerDocument || this.document;
if (!doc) {
return;
}
const clamped = Math.max(0, Math.min(100, Math.round(likeRatio)));
const widthValue = `${clamped}%`;
if (!this.reactionMeterStyleNode || !this.reactionMeterStyleNode.isConnected) {
const existing = doc.getElementById?.("videoModalReactionStyles");
if (existing instanceof HTMLStyleElement) {
this.reactionMeterStyleNode = existing;
} else {
const styleNode = doc.createElement("style");
styleNode.id = "videoModalReactionStyles";
doc.head?.appendChild(styleNode);
this.reactionMeterStyleNode = styleNode;
}
}
if (this.reactionMeterStyleNode) {
this.reactionMeterStyleNode.textContent =
`#playerModal { --video-modal-reaction-fill: ${widthValue}; }`;
}
}
@@ -4021,9 +4045,6 @@ export class VideoModal {
this.videoTitle.textContent = titleText;
// Ensure element is visible
this.videoTitle.hidden = false;
if (this.videoTitle.style) {
this.videoTitle.style.display = "";
}
} else {
// Log for debugging if element cannot be found
this.logger?.log?.(
+140 -19
View File
@@ -20,6 +20,7 @@ const DEFAULT_MAX_WIDTH_TOKEN = "--popover-inline-safe-max";
const MENU_TYPEAHEAD_RESET_MS = 500;
let popoverPanelIdCounter = 0;
let popoverArrowIdCounter = 0;
let activePopoverInstance = null;
@@ -115,22 +116,39 @@ function resolveArrow(option, panel) {
return null;
}
function applyArrowStyles({ arrowElement, placement, middlewareData }) {
function ensureArrowId(arrowElement) {
if (!arrowElement) {
return "";
}
if (arrowElement.id) {
return arrowElement.id;
}
popoverArrowIdCounter += 1;
const arrowId = `popover-arrow-${popoverArrowIdCounter}`;
arrowElement.id = arrowId;
return arrowId;
}
function buildArrowStyleState({ arrowElement, placement, middlewareData }) {
const state = {
basePlacement: "",
values: {
left: "auto",
right: "auto",
top: "auto",
bottom: "auto",
},
};
if (!arrowElement || typeof placement !== "string") {
return;
return state;
}
const basePlacement = placement.split("-")[0];
const arrowData = middlewareData?.arrow || null;
arrowElement.style.left = "";
arrowElement.style.right = "";
arrowElement.style.top = "";
arrowElement.style.bottom = "";
if (!arrowData || !basePlacement) {
delete arrowElement.dataset.popoverArrowSide;
return;
return state;
}
const staticSideMap = {
@@ -141,11 +159,11 @@ function applyArrowStyles({ arrowElement, placement, middlewareData }) {
};
if (Number.isFinite(arrowData.x)) {
arrowElement.style.left = `${Math.round(arrowData.x)}px`;
state.values.left = `${Math.round(arrowData.x)}px`;
}
if (Number.isFinite(arrowData.y)) {
arrowElement.style.top = `${Math.round(arrowData.y)}px`;
state.values.top = `${Math.round(arrowData.y)}px`;
}
const staticSide = staticSideMap[basePlacement] || null;
@@ -161,10 +179,11 @@ function applyArrowStyles({ arrowElement, placement, middlewareData }) {
basePlacement === "top" || basePlacement === "bottom"
? arrowHeight
: arrowWidth;
arrowElement.style[staticSide] = `${-Math.round(offsetValue)}px`;
state.values[staticSide] = `${-Math.round(offsetValue)}px`;
}
arrowElement.dataset.popoverArrowSide = basePlacement;
state.basePlacement = basePlacement;
return state;
}
function containsTarget(root, target) {
@@ -238,6 +257,67 @@ export function createPopover(trigger, render, options = {}) {
typeaheadBuffer: "",
typeaheadTimeout: null,
};
let popoverStyleNode = null;
let arrowStyleState = {
left: "auto",
right: "auto",
top: "auto",
bottom: "auto",
};
let panelStyleState = {
left: 0,
top: 0,
strategy,
maxWidth: "none",
maxHeight: "none",
};
let arrowId = "";
function ensurePopoverStyleNode(panelId) {
if (popoverStyleNode && popoverStyleNode.isConnected) {
return popoverStyleNode;
}
const styleId = `popover-style-${panelId}`;
const existing = documentRef?.getElementById?.(styleId) || null;
if (existing instanceof HTMLStyleElement) {
popoverStyleNode = existing;
return popoverStyleNode;
}
if (!documentRef?.createElement) {
return null;
}
const styleNode = documentRef.createElement("style");
styleNode.id = styleId;
documentRef.head?.appendChild(styleNode);
popoverStyleNode = styleNode;
return popoverStyleNode;
}
function syncPopoverStyles() {
if (!panel || !panel.id) {
return;
}
const styleNode = ensurePopoverStyleNode(panel.id);
if (!styleNode) {
return;
}
const panelRule = `#${panel.id}{` +
`--popover-left:${Math.round(panelStyleState.left)}px;` +
`--popover-top:${Math.round(panelStyleState.top)}px;` +
`--popover-strategy:${panelStyleState.strategy || strategy};` +
`--popover-max-width:${panelStyleState.maxWidth || "none"};` +
`--popover-max-height:${panelStyleState.maxHeight || "none"};` +
`}`;
const arrowRule = arrowId
? `#${arrowId}{` +
`--popover-arrow-left:${arrowStyleState.left};` +
`--popover-arrow-right:${arrowStyleState.right};` +
`--popover-arrow-top:${arrowStyleState.top};` +
`--popover-arrow-bottom:${arrowStyleState.bottom};` +
`}`
: "";
styleNode.textContent = [panelRule, arrowRule].filter(Boolean).join("\n");
}
function getView() {
return documentRef?.defaultView || globalThis;
@@ -610,13 +690,21 @@ export function createPopover(trigger, render, options = {}) {
if (maxWidthToken) {
const widthValue = resolveTokenValue(maxWidthToken, documentRef);
panel.style.maxWidth = widthValue || "";
panelStyleState = {
...panelStyleState,
maxWidth: widthValue || "none",
};
}
if (maxHeightToken) {
const heightValue = resolveTokenValue(maxHeightToken, documentRef);
panel.style.maxHeight = heightValue || "";
panelStyleState = {
...panelStyleState,
maxHeight: heightValue || "none",
};
}
syncPopoverStyles();
}
function ensurePanel() {
@@ -672,6 +760,12 @@ export function createPopover(trigger, render, options = {}) {
applyPanelTokens();
if (arrowElement) {
arrowElement.dataset.popoverArrow = "true";
arrowId = ensureArrowId(arrowElement);
syncPopoverStyles();
}
return panel;
}
@@ -720,18 +814,39 @@ export function createPopover(trigger, render, options = {}) {
? resolvedPlacement
: placement;
panel.style.position = resolvedStrategy || strategy;
panel.style.left = `${Math.round(nextX)}px`;
panel.style.top = `${Math.round(nextY)}px`;
panelStyleState = {
...panelStyleState,
left: nextX,
top: nextY,
strategy: resolvedStrategy || strategy,
};
panel.dataset.popoverPlacement = finalPlacement;
if (arrowElement) {
applyArrowStyles({
const arrowState = buildArrowStyleState({
arrowElement,
placement: finalPlacement,
middlewareData,
});
arrowStyleState = { ...arrowState.values };
if (arrowState.basePlacement) {
arrowElement.dataset.popoverArrowSide = arrowState.basePlacement;
} else {
delete arrowElement.dataset.popoverArrowSide;
}
arrowElement.dataset.popoverArrow = "true";
arrowId = ensureArrowId(arrowElement);
} else {
arrowStyleState = {
left: "auto",
right: "auto",
top: "auto",
bottom: "auto",
};
arrowId = "";
}
syncPopoverStyles();
} catch (error) {
logger.user.error("[popover] positioning failed", error);
}
@@ -908,8 +1023,14 @@ export function createPopover(trigger, render, options = {}) {
portal.parentNode.removeChild(portal);
}
if (popoverStyleNode?.parentNode) {
popoverStyleNode.parentNode.removeChild(popoverStyleNode);
}
panel = null;
arrowElement = null;
popoverStyleNode = null;
arrowId = "";
if (anchor) {
anchor.removeAttribute("aria-controls");
+13 -59
View File
@@ -1135,8 +1135,7 @@ export class ProfileModalController {
this.focusTrapSuspended = false;
this.focusTrapSuspendCount = 0;
this.focusTrapAriaHiddenBeforeSuspend = null;
this.focusTrapPointerEventsBeforeSuspend = null;
this.focusTrapVisibilityBeforeSuspend = null;
this.focusTrapNestedModalActiveBeforeSuspend = null;
this.profileSwitcherSelectionPubkey = null;
this.previouslyFocusedElement = null;
this.largeLayoutQuery = null;
@@ -8332,25 +8331,16 @@ export class ProfileModalController {
this.focusTrapAriaHiddenBeforeSuspend = modalRoot.getAttribute(
"aria-hidden",
);
this.focusTrapPointerEventsBeforeSuspend =
typeof modalRoot.style.pointerEvents === "string" &&
modalRoot.style.pointerEvents
? modalRoot.style.pointerEvents
: null;
this.focusTrapVisibilityBeforeSuspend =
typeof modalRoot.style.visibility === "string" &&
modalRoot.style.visibility
? modalRoot.style.visibility
this.focusTrapNestedModalActiveBeforeSuspend =
typeof modalRoot.dataset.nestedModalActive === "string"
? modalRoot.dataset.nestedModalActive
: null;
modalRoot.dataset.nestedModalActive = "true";
modalRoot.setAttribute("aria-hidden", "true");
modalRoot.style.setProperty("pointer-events", "none");
modalRoot.style.setProperty("visibility", "hidden");
} else {
this.focusTrapAriaHiddenBeforeSuspend = null;
this.focusTrapPointerEventsBeforeSuspend = null;
this.focusTrapVisibilityBeforeSuspend = null;
this.focusTrapNestedModalActiveBeforeSuspend = null;
}
const panel = this.getModalPanelElement();
@@ -8391,30 +8381,11 @@ export class ProfileModalController {
}
}
if (
this.focusTrapPointerEventsBeforeSuspend === null ||
this.focusTrapPointerEventsBeforeSuspend === undefined ||
this.focusTrapPointerEventsBeforeSuspend === ""
) {
modalRoot.style.removeProperty("pointer-events");
if (this.focusTrapNestedModalActiveBeforeSuspend === null) {
delete modalRoot.dataset.nestedModalActive;
} else {
modalRoot.style.setProperty(
"pointer-events",
this.focusTrapPointerEventsBeforeSuspend,
);
}
if (
this.focusTrapVisibilityBeforeSuspend === null ||
this.focusTrapVisibilityBeforeSuspend === undefined ||
this.focusTrapVisibilityBeforeSuspend === ""
) {
modalRoot.style.removeProperty("visibility");
} else {
modalRoot.style.setProperty(
"visibility",
this.focusTrapVisibilityBeforeSuspend,
);
modalRoot.dataset.nestedModalActive =
this.focusTrapNestedModalActiveBeforeSuspend;
}
}
@@ -8424,8 +8395,7 @@ export class ProfileModalController {
}
this.focusTrapAriaHiddenBeforeSuspend = null;
this.focusTrapPointerEventsBeforeSuspend = null;
this.focusTrapVisibilityBeforeSuspend = null;
this.focusTrapNestedModalActiveBeforeSuspend = null;
this.updateFocusTrap();
@@ -8503,21 +8473,7 @@ export class ProfileModalController {
: null;
if (modalRoot) {
modalRoot.style.setProperty("z-index", "var(--z-modal-top-root)");
}
if (this.profileModalBackdrop instanceof HTMLElement) {
this.profileModalBackdrop.style.setProperty(
"z-index",
"var(--z-modal-top-overlay)",
);
}
if (this.profileModalPanel instanceof HTMLElement) {
this.profileModalPanel.style.setProperty(
"z-index",
"var(--z-modal-top-content)",
);
modalRoot.dataset.modalStack = "top";
}
}
@@ -8685,8 +8641,7 @@ export class ProfileModalController {
modalElement.classList.add("hidden");
modalElement.setAttribute("aria-hidden", "true");
delete modalElement.dataset.nestedModalActive;
modalElement.style.removeProperty("pointer-events");
modalElement.style.removeProperty("visibility");
delete modalElement.dataset.modalStack;
this.setGlobalModalState("profile", false);
this.setMobileView("menu", { skipFocusTrap: true });
@@ -8712,8 +8667,7 @@ export class ProfileModalController {
this.focusTrapSuspendCount = 0;
this.focusTrapSuspended = false;
this.focusTrapAriaHiddenBeforeSuspend = null;
this.focusTrapPointerEventsBeforeSuspend = null;
this.focusTrapVisibilityBeforeSuspend = null;
this.focusTrapNestedModalActiveBeforeSuspend = null;
if (
this.boundProfileHistoryVisibility &&
+107 -9
View File
@@ -235,6 +235,88 @@ if (hasDocument) {
})();
var useSVG = document.documentElement.tagName.toLowerCase() === "svg";
var qrCodeStyleRegistry = new WeakMap();
var qrCodeStyleCounter = 0;
function ensureQrCodeStyleState(doc) {
if (!doc || typeof doc.createElement !== "function") {
return null;
}
var existing = qrCodeStyleRegistry.get(doc);
if (existing) {
return existing;
}
var styleNode = doc.getElementById("qrCodeStyles");
if (!(styleNode instanceof HTMLStyleElement)) {
styleNode = doc.createElement("style");
styleNode.id = "qrCodeStyles";
doc.head && doc.head.appendChild(styleNode);
}
var state = {
styleNode: styleNode,
values: new Map()
};
qrCodeStyleRegistry.set(doc, state);
return state;
}
function buildQrCodeRule(selector, values) {
var declarations = [];
if (values.colorDark) {
declarations.push("--qr-code-color-dark: " + values.colorDark + ";");
}
if (values.colorLight) {
declarations.push("--qr-code-color-light: " + values.colorLight + ";");
}
if (values.cellWidth) {
declarations.push("--qr-code-cell-width: " + values.cellWidth + ";");
}
if (values.cellHeight) {
declarations.push("--qr-code-cell-height: " + values.cellHeight + ";");
}
if (values.tableMargin) {
declarations.push("--qr-code-table-margin: " + values.tableMargin + ";");
}
return selector + " { " + declarations.join(" ") + " }";
}
function updateQrCodeContainerStyles(el, values) {
if (!el) {
return;
}
var doc = el.ownerDocument || document;
var state = ensureQrCodeStyleState(doc);
if (!state) {
return;
}
if (!el.dataset.qrCodeStyleId) {
qrCodeStyleCounter += 1;
el.dataset.qrCodeStyleId = "qr-code-" + qrCodeStyleCounter;
}
var selector = "[data-qr-code-style-id=\"" + el.dataset.qrCodeStyleId + "\"]";
var currentValues = state.values.get(selector) || {};
var nextValues = Object.assign({}, currentValues, values || {});
state.values.set(selector, nextValues);
var rules = [];
state.values.forEach(function (entryValues, entrySelector) {
rules.push(buildQrCodeRule(entrySelector, entryValues));
});
state.styleNode.textContent = rules.join("\n");
}
function setQrCodeMode(el, mode) {
if (!el) {
return;
}
if (el.classList) {
el.classList.add("qr-code__container");
}
if (mode) {
el.dataset.qrCodeMode = mode;
} else {
delete el.dataset.qrCodeMode;
}
}
// Drawing in DOM by using Table tag
var Drawing = useSVG ? svgDrawer : !_isSupportCanvas() ? (function () {
@@ -256,10 +338,13 @@ if (hasDocument) {
var nHeight = Math.floor(_htOption.height / nCount);
var aHTML = ['<table class="qr-code__table">'];
_el.style.setProperty("--qr-code-color-dark", _htOption.colorDark);
_el.style.setProperty("--qr-code-color-light", _htOption.colorLight);
_el.style.setProperty("--qr-code-cell-width", nWidth + "px");
_el.style.setProperty("--qr-code-cell-height", nHeight + "px");
updateQrCodeContainerStyles(_el, {
colorDark: _htOption.colorDark,
colorLight: _htOption.colorLight,
cellWidth: nWidth + "px",
cellHeight: nHeight + "px",
tableMargin: "0px"
});
for (var row = 0; row < nCount; row++) {
aHTML.push('<tr>');
@@ -283,7 +368,9 @@ if (hasDocument) {
var nTopMarginTable = (_htOption.height - elTable.offsetHeight) / 2;
if (nLeftMarginTable > 0 && nTopMarginTable > 0) {
elTable.style.margin = nTopMarginTable + "px " + nLeftMarginTable + "px";
updateQrCodeContainerStyles(_el, {
tableMargin: nTopMarginTable + "px " + nLeftMarginTable + "px"
});
}
};
@@ -298,8 +385,7 @@ if (hasDocument) {
})() : (function () { // Drawing in Canvas
function _onMakeImage() {
this._elImage.src = this._elCanvas.toDataURL("image/png");
this._elImage.style.display = "block";
this._elCanvas.style.display = "none";
setQrCodeMode(this._el, "image");
}
// Android 2.1 bug workaround
@@ -398,13 +484,22 @@ if (hasDocument) {
this._elCanvas.height = htOption.height;
el.appendChild(this._elCanvas);
this._el = el;
if (this._el && this._el.classList) {
this._el.classList.add("qr-code__container");
}
this._oContext = this._elCanvas.getContext("2d");
this._bIsPainted = false;
this._elImage = document.createElement("img");
this._elImage.alt = "Scan me!";
this._elImage.style.display = "none";
if (this._elImage.classList) {
this._elImage.classList.add("qr-code__image");
}
if (this._elCanvas && this._elCanvas.classList) {
this._elCanvas.classList.add("qr-code__canvas");
}
this._el.appendChild(this._elImage);
this._bSupportDataURI = null;
setQrCodeMode(this._el, "canvas");
};
/**
@@ -423,7 +518,7 @@ if (hasDocument) {
var nRoundedWidth = Math.round(nWidth);
var nRoundedHeight = Math.round(nHeight);
_elImage.style.display = "none";
setQrCodeMode(this._el, "canvas");
this.clear();
for (var row = 0; row < nCount; row++) {
@@ -604,6 +699,9 @@ if (hasDocument) {
this._android = _getAndroid();
this._el = el;
if (this._el && this._el.classList) {
this._el.classList.add("qr-code__container");
}
this._oQRCode = null;
this._oDrawing = new Drawing(this._el, this._htOption);
+13 -5
View File
@@ -16,6 +16,10 @@ test.describe("popover layout scenarios", () => {
.getComputedStyle(document.documentElement)
.getPropertyValue("--popover-inline-safe-max")
.trim();
const popoverMaxWidth = window
.getComputedStyle(node)
.getPropertyValue("--popover-max-width")
.trim();
return {
rect: {
@@ -29,7 +33,7 @@ test.describe("popover layout scenarios", () => {
viewport: { width: viewportWidth, height: viewportHeight },
placement: node.dataset.popoverPlacement || "",
state: node.dataset.popoverState || "",
inlineMaxWidth: node.style.maxWidth,
popoverMaxWidth,
tokenMaxWidth: tokenValue,
};
});
@@ -51,6 +55,10 @@ test.describe("popover layout scenarios", () => {
.getComputedStyle(document.documentElement)
.getPropertyValue("--popover-inline-safe-max")
.trim();
const popoverMaxWidth = window
.getComputedStyle(node)
.getPropertyValue("--popover-max-width")
.trim();
return {
rect: {
@@ -72,7 +80,7 @@ test.describe("popover layout scenarios", () => {
viewport: { width: viewportWidth, height: viewportHeight },
placement: node.dataset.popoverPlacement || "",
state: node.dataset.popoverState || "",
inlineMaxWidth: node.style.maxWidth,
popoverMaxWidth,
tokenMaxWidth: tokenValue,
};
}, triggerHandle);
@@ -102,7 +110,7 @@ test.describe("popover layout scenarios", () => {
assertWithinViewport(metrics);
expect(metrics.state).toBe("open");
expect(metrics.placement.startsWith("top")).toBeTruthy();
expect(metrics.inlineMaxWidth).toBe(metrics.tokenMaxWidth);
expect(metrics.popoverMaxWidth).toBe(metrics.tokenMaxWidth);
expect(metrics.tokenMaxWidth.length).toBeGreaterThan(0);
});
@@ -119,7 +127,7 @@ test.describe("popover layout scenarios", () => {
assertWithinViewport(metrics);
expect(metrics.state).toBe("open");
expect(metrics.placement.includes("bottom")).toBeTruthy();
expect(metrics.inlineMaxWidth).toBe(metrics.tokenMaxWidth);
expect(metrics.popoverMaxWidth).toBe(metrics.tokenMaxWidth);
expect(metrics.tokenMaxWidth.length).toBeGreaterThan(0);
});
@@ -137,7 +145,7 @@ test.describe("popover layout scenarios", () => {
assertWithinViewport(metrics);
expect(metrics.state).toBe("open");
expect(metrics.inlineMaxWidth).toBe(metrics.tokenMaxWidth);
expect(metrics.popoverMaxWidth).toBe(metrics.tokenMaxWidth);
expect(metrics.tokenMaxWidth.length).toBeGreaterThan(0);
});
});
+19 -14
View File
@@ -81,14 +81,15 @@ beforeEach(async () => {
global.PointerEvent = windowRef.PointerEvent || windowRef.Event;
}
windowRef.scrollTo = () => {};
documentRef.documentElement.style.setProperty(
"--popover-inline-safe-max",
"calc(100vw - var(--space-xl))",
);
documentRef.documentElement.style.setProperty(
"--overlay-panel-padding-block",
"240px",
);
const rootStyle = documentRef.createElement("style");
rootStyle.id = "testRootVars";
rootStyle.textContent = `
:root {
--popover-inline-safe-max: calc(100vw - var(--space-xl));
--overlay-panel-padding-block: 240px;
}
`;
documentRef.head.appendChild(rootStyle);
setMockedPosition();
@@ -205,16 +206,20 @@ test("flips placement when bottom placement would collide with viewport", async
const panel = documentRef.querySelector('[data-test-panel="collision"]');
assert.ok(panel, "panel should render");
const panelStyles = windowRef.getComputedStyle(panel);
assert.equal(panel.dataset.popoverState, "open");
assert.equal(panel.dataset.state, "open");
assert.equal(panel.dataset.popoverPlacement, "top-start");
assert.equal(panel.style.position, "fixed");
assert.equal(panel.style.left, "96px");
assert.equal(panel.style.top, "81px");
assert.equal(panel.style.maxWidth, "calc(100vw - var(--space-xl))");
assert.equal(panelStyles.getPropertyValue("--popover-strategy").trim(), "fixed");
assert.equal(panelStyles.getPropertyValue("--popover-left").trim(), "96px");
assert.equal(panelStyles.getPropertyValue("--popover-top").trim(), "81px");
assert.equal(
panelStyles.getPropertyValue("--popover-max-width").trim(),
"calc(100vw - var(--space-xl))",
);
const top = Number.parseInt(panel.style.top, 10);
const left = Number.parseInt(panel.style.left, 10);
const top = Number.parseInt(panelStyles.getPropertyValue("--popover-top"), 10);
const left = Number.parseInt(panelStyles.getPropertyValue("--popover-left"), 10);
assert.ok(Number.isFinite(top));
assert.ok(Number.isFinite(left));
assert.ok(top >= 0, "top should stay within viewport");
-22
View File
@@ -458,17 +458,6 @@ for (const _ of [0]) {
profileModalRoot?.querySelector('.bv-modal__panel') || profileModalRoot;
assert.ok(panel?.hasAttribute('inert'));
assert.equal(
profileModalRoot?.style.visibility,
'hidden',
'profile modal should hide visually while login modal is active',
);
assert.equal(
profileModalRoot?.style.pointerEvents,
'none',
'profile modal should not intercept pointer events while login modal is active',
);
assert.strictEqual(
container.lastElementChild,
loginModal,
@@ -491,16 +480,6 @@ for (const _ of [0]) {
'profile modal should restore aria-hidden',
);
assert.equal(panel?.hasAttribute('inert'), false);
assert.equal(
profileModalRoot?.style.visibility ?? '',
'',
'profile modal visibility should restore after login',
);
assert.equal(
profileModalRoot?.style.pointerEvents ?? '',
'',
'profile modal pointer events should restore after login',
);
} finally {
cleanup();
}
@@ -1292,4 +1271,3 @@ test('history pane lazily initializes the watch history renderer', async () => {
assert.equal(controller.profileHistoryRenderer, null);
assert.equal(controller.boundProfileHistoryVisibility, null);
});
+20 -10
View File
@@ -176,9 +176,10 @@ test("opens a popover in the overlay root and positions the panel", async () =>
assert.equal(panel.dataset.popoverState, "open");
assert.equal(panel.dataset.state, "open");
assert.equal(panel.dataset.popoverPlacement, "bottom-start");
assert.equal(panel.style.position, "fixed");
assert.equal(panel.style.left, "173px");
assert.equal(panel.style.top, "248px");
const panelStyles = documentRef.defaultView.getComputedStyle(panel);
assert.equal(panelStyles.getPropertyValue("--popover-strategy").trim(), "fixed");
assert.equal(panelStyles.getPropertyValue("--popover-left").trim(), "173px");
assert.equal(panelStyles.getPropertyValue("--popover-top").trim(), "248px");
assert.equal(panel.getAttribute("role"), "menu");
assert.equal(documentRef.activeElement, panel);
assert.equal(trigger.getAttribute("aria-expanded"), "true");
@@ -238,12 +239,15 @@ test("positions bottom-end panels flush with the trigger's right edge", async ()
const panel = documentRef.getElementById("popover-bottom-end");
assert.ok(panel);
const panelStyles = documentRef.defaultView.getComputedStyle(panel);
assert.equal(panel.dataset.popoverPlacement, "bottom-end");
assert.equal(panel.style.left, "120px");
assert.equal(panel.style.top, "265px");
assert.equal(panelStyles.getPropertyValue("--popover-left").trim(), "120px");
assert.equal(panelStyles.getPropertyValue("--popover-top").trim(), "265px");
const triggerRight = Math.round(trigger.getBoundingClientRect().right);
const panelRight = Number.parseInt(panel.style.left, 10) + panel.offsetWidth;
const panelRight =
Number.parseInt(panelStyles.getPropertyValue("--popover-left"), 10) +
panel.offsetWidth;
assert.equal(panelRight, triggerRight);
popover.destroy();
@@ -687,8 +691,14 @@ test("applies token-based sizing and arrow positioning", async () => {
height: 40,
});
documentRef.documentElement.style.setProperty("--popover-inline-safe-max", "320px");
documentRef.documentElement.style.setProperty("--overlay-panel-padding-block", "240px");
const rootStyle = documentRef.createElement("style");
rootStyle.textContent = `
:root {
--popover-inline-safe-max: 320px;
--overlay-panel-padding-block: 240px;
}
`;
documentRef.head.appendChild(rootStyle);
const popover = createPopover(
trigger,
@@ -742,8 +752,8 @@ test("applies token-based sizing and arrow positioning", async () => {
const arrow = panel.querySelector(".arrow");
const panelStyles = getComputedStyle(panel);
assert.equal(panelStyles.maxWidth, "320px");
assert.equal(panelStyles.maxHeight, "240px");
assert.equal(panelStyles.getPropertyValue("--popover-max-width").trim(), "320px");
assert.equal(panelStyles.getPropertyValue("--popover-max-height").trim(), "240px");
assert.equal(arrow.dataset.popoverArrowSide, "bottom");
const arrowStyles = getComputedStyle(arrow);
assert.notEqual(arrowStyles.top, "");
+4 -4
View File
@@ -34,7 +34,7 @@ test("cached thumbnails reuse existing src without lazy-loading", (t) => {
const cachedSrc = "https://cdn.example.com/thumb.jpg";
const cache = new Map([["video-123", cachedSrc]]);
const { card } = renderCard(t, {
const { card, window } = renderCard(t, {
thumbnailCache: cache,
fallbackThumbnailSrc: "https://cdn.example.com/fallback.jpg",
});
@@ -46,7 +46,7 @@ test("cached thumbnails reuse existing src without lazy-loading", (t) => {
assert.equal(img.src, cachedSrc);
assert.equal(img.dataset.lazy, undefined);
assert.equal(
root.style.getPropertyValue("--similar-card-thumb-url"),
window.getComputedStyle(root).getPropertyValue("--similar-card-thumb-url").trim(),
`url("${cachedSrc}")`,
);
});
@@ -75,7 +75,7 @@ test("uncached thumbnails use fallback, cache on load, and retain blur state", (
assert.equal(img.dataset.lazy, remoteThumb);
assert.equal(img.dataset.thumbnailState, "blurred");
assert.equal(
root.style.getPropertyValue("--similar-card-thumb-url"),
window.getComputedStyle(root).getPropertyValue("--similar-card-thumb-url").trim(),
`url("${fallbackSrc}")`,
);
@@ -84,7 +84,7 @@ test("uncached thumbnails use fallback, cache on load, and retain blur state", (
assert.equal(cache.get("video-456"), remoteThumb);
assert.equal(
root.style.getPropertyValue("--similar-card-thumb-url"),
window.getComputedStyle(root).getPropertyValue("--similar-card-thumb-url").trim(),
`url("${remoteThumb}")`,
);
assert.equal(img.dataset.thumbnailState, "blurred");