Merge pull request #910 from PR0M3TH3AN/codex/add-moderation-badge-slot-and-refactor

Keep moderation badges outside hidden card content
This commit is contained in:
thePR0M3TH3AN authored and GitHub committed 2025-10-25 21:28:48 -04:00
commit 0cb5018b76
5 files changed
+130 -89

No files matched your search

+22 -20
View File
@@ -66,33 +66,35 @@
hidden
>
<div
class="moderation-badge flex flex-wrap items-center gap-sm"
class="moderation-badge"
data-moderation-badge="true"
data-variant="warning"
role="status"
aria-live="polite"
aria-atomic="true"
>
<span class="moderation-badge__icon" aria-hidden="true">
<svg
class="moderation-badge__icon-mark"
viewBox="0 0 20 20"
focusable="false"
aria-hidden="true"
<span class="moderation-badge__label inline-flex items-center gap-xs">
<span class="moderation-badge__icon" aria-hidden="true">
<svg
class="moderation-badge__icon-mark"
viewBox="0 0 20 20"
focusable="false"
aria-hidden="true"
>
<path
fill="currentColor"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm-.75-11.75a.75.75 0 011.5 0v4.5a.75.75 0 01-1.5 0v-4.5zm.75 8.5a1 1 0 100-2 1 1 0 000 2z"
fill-rule="evenodd"
clip-rule="evenodd"
/>
</svg>
</span>
<span
class="moderation-badge__text"
data-moderation-text
>
<path
fill="currentColor"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm-.75-11.75a.75.75 0 011.5 0v4.5a.75.75 0 01-1.5 0v-4.5zm.75 8.5a1 1 0 100-2 1 1 0 000 2z"
fill-rule="evenodd"
clip-rule="evenodd"
/>
</svg>
</span>
<span
class="moderation-badge__text whitespace-nowrap"
data-moderation-text
>
Content reported by trusted contacts
Content reported by trusted contacts
</span>
</span>
<button
type="button"
+10 -4
View File
@@ -799,7 +799,11 @@
}
.moderation-badge {
@apply inline-flex items-center gap-2 rounded-xl border px-3 py-1.5 text-xs font-semibold normal-case tracking-tight shadow-sm;
@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__label {
@apply inline-flex items-center gap-xs whitespace-nowrap;
}
.moderation-badge[data-variant="warning"] {
@@ -813,12 +817,13 @@
}
.moderation-badge__icon {
@apply inline-flex h-5 w-5 items-center justify-center rounded-full;
@apply inline-flex h-5 w-5 items-center justify-center rounded-full bg-status-warning-surface text-status-warning-on;
background-color: color-mix(in srgb, var(--status-warning) 18%, transparent);
color: var(--status-warning-on);
}
.moderation-badge[data-variant="neutral"] .moderation-badge__icon {
@apply bg-status-neutral-surface text-status-neutral-on;
background-color: color-mix(in srgb, var(--status-neutral) 18%, transparent);
color: var(--status-neutral-on);
}
@@ -828,11 +833,11 @@
}
.moderation-badge__text {
@apply text-xs font-semibold tracking-tight;
@apply text-xs font-semibold tracking-tight whitespace-nowrap;
}
.moderation-badge__action {
@apply inline-flex items-center gap-1.5 rounded-full border px-3 py-1 text-xs font-semibold normal-case tracking-tight transition duration-150 ease-out;
@apply inline-flex items-center gap-1.5 rounded-full border px-3 py-1 text-xs font-semibold normal-case tracking-tight transition duration-150 ease-out bg-status-warning-surface border-status-warning-border text-status-warning-on;
background-color: color-mix(in srgb, var(--status-warning) 16%, transparent);
border-color: color-mix(in srgb, var(--status-warning-border) 80%, transparent);
color: var(--status-warning-on);
@@ -843,6 +848,7 @@
}
.moderation-badge[data-variant="neutral"] .moderation-badge__action {
@apply bg-status-neutral-surface border-status-neutral-border text-status-neutral-on;
background-color: color-mix(in srgb, var(--status-neutral) 16%, transparent);
border-color: color-mix(in srgb, var(--status-neutral-border) 80%, transparent);
color: var(--status-neutral-on);
+10 -4
View File
@@ -71,6 +71,7 @@ let currentChannelModerationSourceVideo = null;
const channelModerationBadgeState = {
container: null,
badgeEl: null,
labelEl: null,
textEl: null,
iconSvg: null,
iconWrapper: null,
@@ -429,14 +430,18 @@ function ensureChannelModerationBadgeResources(doc) {
if (!channelModerationBadgeState.badgeEl) {
const badge = doc.createElement("div");
badge.className = "moderation-badge flex flex-wrap items-center gap-sm";
badge.className = "moderation-badge";
badge.dataset.moderationBadge = "true";
const label = doc.createElement("span");
label.className = "moderation-badge__label inline-flex items-center gap-xs";
const { wrapper, svg } = createChannelModerationBadgeIcon(doc, "blocked");
badge.appendChild(wrapper);
label.appendChild(wrapper);
const text = doc.createElement("span");
text.className = "moderation-badge__text whitespace-nowrap";
badge.appendChild(text);
text.className = "moderation-badge__text";
label.appendChild(text);
badge.appendChild(label);
channelModerationBadgeState.badgeEl = badge;
channelModerationBadgeState.labelEl = label;
channelModerationBadgeState.textEl = text;
channelModerationBadgeState.iconWrapper = wrapper;
channelModerationBadgeState.iconSvg = svg;
@@ -481,6 +486,7 @@ function teardownChannelModerationBadge() {
if (ref.badgeEl && ref.badgeEl.parentNode) {
ref.badgeEl.parentNode.removeChild(ref.badgeEl);
}
ref.labelEl = null;
if (ref.container) {
ref.container.removeAttribute("data-moderation-active");
}
+10 -4
View File
@@ -569,14 +569,18 @@ function createHistoryCardBadge(ref, context) {
return null;
}
const badge = doc.createElement("div");
badge.className = "moderation-badge flex flex-wrap items-center gap-sm";
badge.className = "moderation-badge";
badge.dataset.moderationBadge = "true";
const label = doc.createElement("span");
label.className = "moderation-badge__label inline-flex items-center gap-xs";
const { wrapper, svg } = createHistoryCardBadgeIcon(doc, getHistoryCardModerationState(context));
badge.appendChild(wrapper);
label.appendChild(wrapper);
const text = doc.createElement("span");
text.className = "moderation-badge__text whitespace-nowrap";
badge.appendChild(text);
text.className = "moderation-badge__text";
label.appendChild(text);
badge.appendChild(label);
ref.badgeEl = badge;
ref.badgeLabelEl = label;
ref.badgeTextEl = text;
ref.badgeIconSvg = svg;
ref.badgeIconWrapper = wrapper;
@@ -620,6 +624,7 @@ function updateHistoryCardBadge(ref, context) {
ref.badgeEl.parentNode.removeChild(ref.badgeEl);
}
ref.badgeEl = null;
ref.badgeLabelEl = null;
ref.badgeTextEl = null;
ref.badgeIconSvg = null;
ref.badgeIconWrapper = null;
@@ -1218,6 +1223,7 @@ export function buildHistoryCard({ item, video, profile, metadataPreference }) {
video: video || null,
hiddenContainer: null,
badgeEl: null,
badgeLabelEl: null,
badgeTextEl: null,
badgeIconSvg: null,
badgeIconWrapper: null,
+78 -57
View File
@@ -123,6 +123,7 @@ export class VideoCard {
};
this.moderationBadgeEl = null;
this.moderationBadgeLabelEl = null;
this.moderationBadgeTextEl = null;
this.moderationBadgeIconWrapper = null;
this.moderationBadgeIconSvg = null;
@@ -130,6 +131,7 @@ export class VideoCard {
this.moderationHideButton = null;
this.moderationBadgeId = "";
this.badgesContainerEl = null;
this.moderationBadgeSlot = null;
this.hiddenSummaryEl = null;
this.boundShowAnywayHandler = (event) => this.handleShowAnywayClick(event);
this.boundModerationHideHandler = (event) => this.handleModerationHideClick(event);
@@ -470,7 +472,15 @@ export class VideoCard {
content.appendChild(warning);
}
const moderationBadgeSlot = this.createElement("div", {
classNames: ["video-card__moderation-slot", "px-md", "pt-md"],
});
moderationBadgeSlot.hidden = true;
moderationBadgeSlot.setAttribute("aria-hidden", "true");
this.moderationBadgeSlot = moderationBadgeSlot;
root.appendChild(anchor);
root.appendChild(moderationBadgeSlot);
root.appendChild(content);
this.applyPlaybackDatasets();
@@ -945,11 +955,6 @@ export class VideoCard {
pieces.push(badge);
}
const moderationBadge = this.buildModerationBadge();
if (moderationBadge) {
pieces.push(moderationBadge);
}
if (!pieces.length) {
return null;
}
@@ -1209,6 +1214,7 @@ export class VideoCard {
buildModerationBadge(context = this.getModerationContext()) {
if (!context.shouldShow) {
this.moderationBadgeEl = null;
this.moderationBadgeLabelEl = null;
this.moderationBadgeTextEl = null;
this.moderationBadgeIconWrapper = null;
this.moderationBadgeIconSvg = null;
@@ -1230,7 +1236,7 @@ export class VideoCard {
}
const badge = this.createElement("div", {
classNames: ["moderation-badge", "flex", "flex-wrap", "items-center", "gap-sm"],
classNames: ["moderation-badge"],
});
badge.dataset.variant = context.overrideActive ? "neutral" : "warning";
badge.dataset.moderationBadge = "true";
@@ -1254,16 +1260,26 @@ export class VideoCard {
badge.setAttribute("aria-live", "polite");
badge.setAttribute("aria-atomic", "true");
const label = this.createElement("span", {
classNames: [
"moderation-badge__label",
"inline-flex",
"items-center",
"gap-xs",
],
});
const icon = this.createModerationBadgeIcon(state);
if (icon) {
badge.appendChild(icon);
label.appendChild(icon);
}
const text = this.createElement("span", {
classNames: ["moderation-badge__text", "whitespace-nowrap"],
classNames: ["moderation-badge__text"],
textContent: buildModerationBadgeText(context, { variant: "card" }),
});
badge.appendChild(text);
label.appendChild(text);
badge.appendChild(label);
const muteNames = Array.isArray(context.trustedMuteDisplayNames)
? context.trustedMuteDisplayNames
@@ -1304,6 +1320,7 @@ export class VideoCard {
}
this.moderationBadgeEl = badge;
this.moderationBadgeLabelEl = label;
this.moderationBadgeTextEl = text;
if (!context.overrideActive && context.allowOverride) {
@@ -1337,9 +1354,30 @@ export class VideoCard {
updateModerationBadge(context = this.getModerationContext()) {
const badge = this.moderationBadgeEl;
let slot = this.moderationBadgeSlot;
const hiddenActive = context.activeHidden && !context.overrideActive;
if (!slot && this.root) {
slot = this.createElement("div", {
classNames: ["video-card__moderation-slot", "px-md", "pt-md"],
});
slot.hidden = true;
slot.setAttribute("aria-hidden", "true");
this.moderationBadgeSlot = slot;
if (this.anchorEl && this.anchorEl.parentElement === this.root) {
this.root.insertBefore(slot, this.anchorEl.nextSibling);
} else {
this.root.appendChild(slot);
}
}
if (badge) {
if (!this.moderationBadgeLabelEl) {
const label = badge.querySelector(".moderation-badge__label");
if (label) {
this.moderationBadgeLabelEl = label;
}
}
if (!this.moderationBadgeIconWrapper) {
const iconWrapper = badge.querySelector(".moderation-badge__icon");
if (iconWrapper) {
@@ -1352,12 +1390,22 @@ export class VideoCard {
this.moderationBadgeIconSvg = iconSvg;
}
}
if (!this.moderationBadgeTextEl) {
const textEl = badge.querySelector(".moderation-badge__text");
if (textEl) {
this.moderationBadgeTextEl = textEl;
}
}
}
if (!context.shouldShow) {
if (badge && badge.parentElement) {
badge.parentElement.removeChild(badge);
}
if (slot) {
slot.hidden = true;
slot.setAttribute("aria-hidden", "true");
}
if (this.moderationActionButton) {
this.moderationActionButton.removeEventListener(
"click",
@@ -1372,6 +1420,7 @@ export class VideoCard {
this.moderationHideButton = null;
}
this.moderationBadgeEl = null;
this.moderationBadgeLabelEl = null;
this.moderationBadgeTextEl = null;
this.moderationBadgeIconWrapper = null;
this.moderationBadgeIconSvg = null;
@@ -1385,33 +1434,10 @@ export class VideoCard {
if (!badge) {
const nextBadge = this.buildModerationBadge(context);
if (nextBadge) {
if (hiddenActive) {
const container = this.ensureHiddenSummaryContainer();
if (container) {
container.appendChild(nextBadge);
}
} else {
if (!this.badgesContainerEl) {
this.badgesContainerEl = this.createElement("div", {
classNames: ["flex", "flex-wrap", "items-center", "gap-sm"],
});
if (this.contentEl) {
if (
this.discussionCountEl &&
this.discussionCountEl.parentElement === this.contentEl
) {
this.contentEl.insertBefore(
this.badgesContainerEl,
this.discussionCountEl,
);
} else {
this.contentEl.appendChild(this.badgesContainerEl);
}
}
}
this.badgesContainerEl.appendChild(nextBadge);
}
if (nextBadge && slot) {
slot.appendChild(nextBadge);
slot.hidden = false;
slot.removeAttribute("aria-hidden");
}
this.updateModerationAria();
return;
@@ -1426,6 +1452,7 @@ export class VideoCard {
? "trusted-mute"
: "blocked";
badge.dataset.moderationState = state;
this.updateModerationBadgeIcon(state);
if (hiddenActive && context.effectiveHideReason) {
badge.dataset.moderationHideReason = context.effectiveHideReason;
} else if (badge.dataset.moderationHideReason) {
@@ -1475,30 +1502,14 @@ export class VideoCard {
badge.setAttribute("aria-label", `${textContent}.`);
}
if (hiddenActive) {
const container = this.ensureHiddenSummaryContainer();
if (container && badge.parentElement !== container) {
if (slot) {
slot.hidden = false;
slot.removeAttribute("aria-hidden");
if (badge.parentElement !== slot) {
if (badge.parentElement) {
badge.parentElement.removeChild(badge);
}
container.appendChild(badge);
}
} else if (this.badgesContainerEl) {
if (!this.badgesContainerEl.parentElement && this.contentEl) {
if (
this.discussionCountEl &&
this.discussionCountEl.parentElement === this.contentEl
) {
this.contentEl.insertBefore(this.badgesContainerEl, this.discussionCountEl);
} else {
this.contentEl.appendChild(this.badgesContainerEl);
}
}
if (badge.parentElement !== this.badgesContainerEl) {
if (badge.parentElement) {
badge.parentElement.removeChild(badge);
}
this.badgesContainerEl.appendChild(badge);
slot.appendChild(badge);
}
}
@@ -1579,6 +1590,7 @@ export class VideoCard {
updateHiddenState(context = this.getModerationContext()) {
const hiddenActive = context.activeHidden && !context.overrideActive;
const container = this.hiddenSummaryEl;
const badgeSlot = this.moderationBadgeSlot;
if (hiddenActive) {
if (this.anchorEl) {
@@ -1590,6 +1602,11 @@ export class VideoCard {
this.contentEl.setAttribute("aria-hidden", "true");
}
if (badgeSlot && this.moderationBadgeEl) {
badgeSlot.hidden = false;
badgeSlot.removeAttribute("aria-hidden");
}
const summaryContainer = this.ensureHiddenSummaryContainer();
if (summaryContainer) {
const description = buildModerationBadgeText(context, { variant: "card" });
@@ -1608,6 +1625,10 @@ export class VideoCard {
this.contentEl.removeAttribute("hidden");
this.contentEl.removeAttribute("aria-hidden");
}
if (badgeSlot && !this.moderationBadgeEl) {
badgeSlot.hidden = true;
badgeSlot.setAttribute("aria-hidden", "true");
}
if (container) {
container.hidden = true;
container.setAttribute("aria-hidden", "true");