fix: restore translucent dashboard styling (#6664)

* fix: restore translucent dashboard styling

* fix: restore dashboard widget transparency

* fix: address dashboard review feedback

* fix: apply board opacity to containers

* fix: flatten demo board container

* fix: default board roundness to sm

* fix: restore assistant widget transparency

* Revert "fix: default board roundness to sm"

This reverts commit c235cbdf2b.

* fix: address remaining dashboard review comments

* test: align grid geometry expectations
This commit is contained in:
Thomas authored and GitHub committed 2026-08-22 15:51:43 +02:00
1 parent 2570de25a6
commit 19760e5722
88 files changed
+940 -620

No files matched your search

@@ -4,23 +4,19 @@
display: flex;
text-decoration: none;
color: inherit;
border: 1px solid var(--border-color);
background-color: light-dark(var(--mantine-color-white), var(--mantine-color-dark-7));
border: 1px solid
rgb(from var(--mantine-color-default-border) r g b / calc(var(--opacity, 1) * 0.45));
background-color: transparent;
transition:
border-color 120ms ease,
background-color 120ms ease;
@mixin dark {
--border-color: var(--mantine-color-dark-4);
}
@mixin light {
--border-color: var(--mantine-color-gray-3);
}
&:hover,
&:focus-visible {
border-color: var(--mantine-primary-color-filled);
background-color: var(--mantine-color-default-hover);
background-color: rgb(
from var(--mantine-color-default-hover) r g b / calc(var(--opacity, 1) * 0.14)
);
}
&:focus-visible {
@@ -29,6 +25,16 @@
}
}
:global(.mediaMissing-wrapper) :global(.mantine-SimpleGrid-root) > :global(.mantine-Paper-root) {
border-color: rgb(from var(--mantine-color-default-border) r g b / calc(var(--opacity, 1) * 0.45));
background-color: transparent;
}
:global(.mediaMissing-wrapper) :global(.mantine-Tabs-list) {
--tab-border-color: rgb(from var(--mantine-color-default-border) r g b / calc(var(--opacity, 1) * 0.45));
--tab-hover-color: rgb(from var(--mantine-color-default-hover) r g b / calc(var(--opacity, 1) * 0.14));
}
.content {
position: relative;
z-index: 1;
@@ -1,7 +1,7 @@
"use client";
import type { ReactNode } from "react";
import { useEffect, useRef, useState } from "react";
import { useEffect, useMemo, useRef, useState } from "react";
import {
Badge,
Box,
@@ -36,11 +36,37 @@ import classes from "./component.module.css";
import type { MediaMissingTab } from "./tabs";
import { resolveMediaMissingTab } from "./tabs";
type Density = "thin" | "compact" | "comfortable";
const MIN_CARD_WIDTH = 280;
const MAX_COLUMNS = 4;
const GRID_HORIZONTAL_PADDING = 20;
const GRID_COLUMN_GAP = 10;
const columnBreakpoints = Array.from({ length: MAX_COLUMNS }, (_, index) => {
const columns = MAX_COLUMNS - index;
return {
columns,
minimumWidth: columns * MIN_CARD_WIDTH + (columns - 1) * GRID_COLUMN_GAP,
};
});
const getMediaMissingGridLayout = (panelWidth: number) => {
const contentWidth = Math.max(0, panelWidth - GRID_HORIZONTAL_PADDING);
const breakpoint = columnBreakpoints.find((candidate) => contentWidth >= candidate.minimumWidth);
return { columns: breakpoint?.columns ?? 1, contentWidth };
};
const getMediaMissingDensity = (contentWidth: number, columns: number, isThin: boolean): Density => {
if (isThin) return "thin";
if (contentWidth > 0 && contentWidth / columns < 180) return "compact";
return "comfortable";
};
export default function MediaMissingWidget({
integrationIds,
options,
width,
height,
displayMode,
widgetRuntimeRef,
}: WidgetComponentProps<"mediaMissing">) {
@@ -68,6 +94,12 @@ export default function MediaMissingWidget({
if (activeTab !== null && activeTab !== selectedTab) setSelectedTab(activeTab);
}, [activeTab, selectedTab]);
const enabledPanelCount = Number(showMissing) + Number(showQueued);
const panelWidth = isAdvanced && enabledPanelCount > 1 ? width / enabledPanelCount : width;
const isThin = !isAdvanced && panelWidth > 0 && panelWidth < 160;
const gridLayout = useMemo(() => getMediaMissingGridLayout(panelWidth), [panelWidth]);
const density = getMediaMissingDensity(gridLayout.contentWidth, gridLayout.columns, isThin);
if (!data) return <WidgetEmptyState />;
if (data.length === 0) throw new NoIntegrationDataError();
if (!showMissing && !showQueued)
@@ -85,14 +117,6 @@ export default function MediaMissingWidget({
const queuedCount = data.reduce((sum, entry) => sum + entry.queuedCount, 0);
const failedIntegrations = data.filter((entry): entry is typeof entry & { error: string } => Boolean(entry.error));
const enabledPanelCount = Number(showMissing) + Number(showQueued);
const panelWidth = isAdvanced && enabledPanelCount > 1 ? width / enabledPanelCount : width;
const isThin = !isAdvanced && panelWidth > 0 && panelWidth < 160;
const isShort = !isAdvanced && height > 0 && height < 180;
const targetCardWidth = isShort ? 130 : 200;
const columns = panelWidth > 0 ? Math.max(1, Math.min(Math.floor(panelWidth / targetCardWidth), 4)) : 1;
const density: Density = isThin ? "thin" : panelWidth > 0 && panelWidth / columns < 180 ? "compact" : "comfortable";
const tabLabel = (label: string, shown: number, total: number) => (isThin ? total : `${label} (${shown}/${total})`);
const renderPanel = (
@@ -114,7 +138,7 @@ export default function MediaMissingWidget({
{emptyLabel}
</Text>
) : (
<SimpleGrid cols={columns} spacing="xs" verticalSpacing="xs">
<SimpleGrid cols={Math.min(gridLayout.columns, entries.length)} spacing="xs" verticalSpacing="xs">
{entries.map(({ item, integrationId }) => (
<MediaCard
key={`${integrationId}-${item.type}-${item.id}`}
@@ -226,8 +250,6 @@ export default function MediaMissingWidget({
);
}
type Density = "thin" | "compact" | "comfortable";
const CARD_HEIGHT: Record<Density, number> = { thin: 52, compact: 56, comfortable: 68 };
const posterSizes: Record<Density, number> = { thin: 34, compact: 40, comfortable: 52 };