mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
## Problem Some destination marks need different assets to maintain contrast across light and dark surfaces. Their paths are also duplicated between Pipelines and Wrappers. ## Solution Add a shared brand-icon registry that supports either one asset or light and dark variants. Pipeline destination logos resolve against the active theme, while Wrappers continue using the light variant for their white logo tiles. This keeps single-asset destinations unchanged and preserves the existing monogram treatment where applicable. | Light | Dark | | --- | --- | | <img width="926" height="742" alt="CleanShot 2026-09-30 at 15 03 31@2x" src="https://github.com/user-attachments/assets/d76e5995-1bb8-4cb7-b991-c5dc1a5d8cb0" /> | <img width="926" height="732" alt="CleanShot 2026-09-30 at 15 03 03@2x" src="https://github.com/user-attachments/assets/83d45f30-3bf6-4ddc-8607-e27628cb4966" /> | ## Review instructions 1. Open `/project/<ref>/database/replication` with pipelines using the themed destination marks. 2. Switch between light and dark themes from the account menu. 3. Confirm each themed mark swaps assets and remains legible in the pipeline list, diagram, and child-route header. 4. Open Database Integrations and confirm the corresponding Wrapper tiles continue using their light-surface assets. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Style** * Updated the BigQuery, ClickHouse, DuckLake, and Snowflake icons in integration and replication views to use branded marks. Icons now use theme-appropriate variants where available, helping them display consistently across light and dark themes. ClickHouse’s previous “CH” monogram is replaced with its branded mark. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
79 lines
2.7 KiB
TypeScript
79 lines
2.7 KiB
TypeScript
import { useTheme } from 'next-themes'
|
|
import { cn, StatusIcon } from 'ui'
|
|
|
|
import { DestinationIcon } from './DestinationIcon'
|
|
import type { DestinationType } from './DestinationPanel/DestinationPanel.types'
|
|
import { BRAND_ICONS, resolveThemedIconSrc, type ThemedIconSrc } from '@/lib/brand-icons'
|
|
import { resolveThemeOverrideMode } from '@/lib/theme-overrides'
|
|
|
|
type DestinationBrand = { type: 'mark'; src: ThemedIconSrc } | { type: 'monogram'; label: string }
|
|
|
|
const BRAND_BY_TYPE: Partial<Record<DestinationType, DestinationBrand>> = {
|
|
BigQuery: { type: 'mark', src: BRAND_ICONS.bigquery },
|
|
ClickHouse: { type: 'mark', src: BRAND_ICONS.clickhouse },
|
|
DuckLake: { type: 'mark', src: BRAND_ICONS.ducklake },
|
|
Snowflake: { type: 'monogram', label: 'SF' },
|
|
}
|
|
|
|
const SIZE_CLASS_NAME = {
|
|
small: { frame: 'h-8 w-8 rounded-md', mark: 'h-4 w-4', icon: 16 },
|
|
large: { frame: 'h-14 w-14 rounded-lg', mark: 'h-8 w-8', icon: 32 },
|
|
} as const
|
|
|
|
interface DestinationLogoProps {
|
|
type: DestinationType
|
|
size?: keyof typeof SIZE_CLASS_NAME
|
|
className?: string
|
|
/** Destructive badge on the bottom-right corner (e.g. table replication errors). */
|
|
hasErrors?: boolean
|
|
}
|
|
|
|
/**
|
|
* A destination's brand mark in a square app-icon frame, used wherever a destination is the
|
|
* subject: the list rows, the pipeline header, and the replication diagram.
|
|
*/
|
|
export const DestinationLogo = ({
|
|
type,
|
|
size = 'small',
|
|
className,
|
|
hasErrors = false,
|
|
}: DestinationLogoProps) => {
|
|
const { resolvedTheme } = useTheme()
|
|
const isDark = resolveThemeOverrideMode(resolvedTheme) === 'dark'
|
|
const sizing = SIZE_CLASS_NAME[size]
|
|
const brand = BRAND_BY_TYPE[type]
|
|
const brandMarkSrc = brand?.type === 'mark' ? resolveThemedIconSrc(brand.src, isDark) : undefined
|
|
|
|
return (
|
|
<span className={cn('relative inline-flex shrink-0', className)}>
|
|
<span className={cn('flex items-center justify-center border bg-surface-100', sizing.frame)}>
|
|
{brand === undefined && (
|
|
<DestinationIcon type={type} size={sizing.icon} className="text-foreground-light" />
|
|
)}
|
|
{brand?.type === 'mark' && (
|
|
<img src={brandMarkSrc} alt="" aria-hidden className={sizing.mark} />
|
|
)}
|
|
{brand?.type === 'monogram' && (
|
|
<span
|
|
className={cn(
|
|
'font-mono font-medium leading-none text-foreground-muted',
|
|
size === 'small' ? 'text-xs' : 'text-lg'
|
|
)}
|
|
aria-hidden
|
|
>
|
|
{brand.label}
|
|
</span>
|
|
)}
|
|
</span>
|
|
{hasErrors && (
|
|
<span
|
|
className="absolute -bottom-1 -right-1 rounded-sm bg-background outline outline-2 outline-background"
|
|
aria-hidden
|
|
>
|
|
<StatusIcon variant="destructive" />
|
|
</span>
|
|
)}
|
|
</span>
|
|
)
|
|
}
|