diff --git a/web/default/src/components/config-drawer.tsx b/web/default/src/components/config-drawer.tsx index 5eda5152..6e0d18cc 100644 --- a/web/default/src/components/config-drawer.tsx +++ b/web/default/src/components/config-drawer.tsx @@ -19,7 +19,7 @@ For commercial licensing, please contact support@quantumnous.com import { Radio as RadioPrimitive } from '@base-ui/react/radio' import { RadioGroup as Radio } from '@base-ui/react/radio-group' import { CircleCheck, Palette, RotateCcw } from 'lucide-react' -import { type SVGProps } from 'react' +import type { SVGProps } from 'react' import { useTranslation } from 'react-i18next' import { IconDir } from '@/assets/custom/icon-dir' @@ -277,16 +277,12 @@ function PresetConfig() { ) } diff --git a/web/default/src/components/drawer-layout.ts b/web/default/src/components/drawer-layout.ts index 4dd02511..12ef7ee6 100644 --- a/web/default/src/components/drawer-layout.ts +++ b/web/default/src/components/drawer-layout.ts @@ -18,6 +18,7 @@ For commercial licensing, please contact support@quantumnous.com */ import { createElement, type ReactNode } from 'react' +import { IconBadge, type IconBadgeTone } from '@/components/ui/icon-badge' import { cn } from '@/lib/utils' export const sideDrawerContentClassName = (className?: string) => @@ -71,6 +72,7 @@ export function SideDrawerSectionHeader(props: { title: ReactNode description?: ReactNode icon?: ReactNode + iconTone?: IconBadgeTone className?: string }) { return createElement( @@ -78,11 +80,8 @@ export function SideDrawerSectionHeader(props: { { className: cn('flex items-start gap-3', props.className) }, props.icon ? createElement( - 'span', - { - className: - 'bg-muted text-muted-foreground flex size-8 shrink-0 items-center justify-center rounded-md', - }, + IconBadge, + { tone: props.iconTone, size: 'md' }, props.icon ) : null, diff --git a/web/default/src/components/ui/icon-badge.tsx b/web/default/src/components/ui/icon-badge.tsx new file mode 100644 index 00000000..494889b4 --- /dev/null +++ b/web/default/src/components/ui/icon-badge.tsx @@ -0,0 +1,85 @@ +/* +Copyright (C) 2023-2026 QuantumNous + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . + +For commercial licensing, please contact support@quantumnous.com +*/ +import { cva, type VariantProps } from 'class-variance-authority' +import type { ReactNode } from 'react' + +import { cn } from '@/lib/utils' + +const iconBadgeVariants = cva( + 'flex shrink-0 items-center justify-center [&>svg]:shrink-0', + { + variants: { + tone: { + neutral: 'bg-muted text-muted-foreground', + primary: 'bg-primary/10 text-primary', + success: 'bg-success/10 text-success', + warning: 'bg-warning/10 text-warning', + info: 'bg-info/10 text-info', + destructive: 'bg-destructive/10 text-destructive', + 'chart-1': 'bg-chart-1/10 text-chart-1', + 'chart-2': 'bg-chart-2/10 text-chart-2', + 'chart-3': 'bg-chart-3/10 text-chart-3', + 'chart-4': 'bg-chart-4/10 text-chart-4', + 'chart-5': 'bg-chart-5/10 text-chart-5', + }, + size: { + xs: 'size-5 rounded-md [&>svg]:size-3', + sm: 'size-7 rounded-md [&>svg]:size-3.5', + md: 'size-8 rounded-lg [&>svg]:size-4', + title: 'size-8 rounded-lg sm:size-9 [&>svg]:size-4', + lg: 'size-10 rounded-xl [&>svg]:size-5', + stat: 'size-5 rounded-md sm:size-7 [&>svg]:size-3 sm:[&>svg]:size-3.5', + }, + }, + defaultVariants: { + tone: 'neutral', + size: 'md', + }, + } +) + +export type IconBadgeTone = NonNullable< + VariantProps['tone'] +> + +export type IconBadgeSize = NonNullable< + VariantProps['size'] +> + +interface IconBadgeProps { + children?: ReactNode + tone?: IconBadgeTone + size?: IconBadgeSize + className?: string + decorative?: boolean +} + +export function IconBadge(props: IconBadgeProps) { + return ( + + {props.children} + + ) +} diff --git a/web/default/src/components/ui/sidebar.tsx b/web/default/src/components/ui/sidebar.tsx index 30175e87..c1ab863b 100644 --- a/web/default/src/components/ui/sidebar.tsx +++ b/web/default/src/components/ui/sidebar.tsx @@ -477,7 +477,7 @@ function SidebarMenu({ className, ...props }: React.ComponentProps<'ul'>) {
    ) diff --git a/web/default/src/components/ui/table.tsx b/web/default/src/components/ui/table.tsx index 2163c119..ababb9a5 100644 --- a/web/default/src/components/ui/table.tsx +++ b/web/default/src/components/ui/table.tsx @@ -54,7 +54,7 @@ function TableBody({ className, ...props }: React.ComponentProps<'tbody'>) { return ( tr]:h-15 [&_tr:last-child]:border-0', className)} {...props} /> ) diff --git a/web/default/src/components/ui/titled-card.tsx b/web/default/src/components/ui/titled-card.tsx index 131ffb05..0c75691f 100644 --- a/web/default/src/components/ui/titled-card.tsx +++ b/web/default/src/components/ui/titled-card.tsx @@ -27,6 +27,7 @@ import { CardHeader, CardTitle, } from './card' +import { IconBadge, type IconBadgeTone } from './icon-badge' type TitledCardProps = { title: ReactNode @@ -39,6 +40,7 @@ type TitledCardProps = { headerClassName?: string contentClassName?: string iconClassName?: string + iconTone?: IconBadgeTone titleClassName?: string descriptionClassName?: string } @@ -54,6 +56,7 @@ export function TitledCard({ headerClassName, contentClassName, iconClassName, + iconTone, titleClassName, descriptionClassName, }: TitledCardProps) { @@ -68,14 +71,9 @@ export function TitledCard({
    {icon != null && ( -
    + {icon} -
    + )}
    - - + } >
    {/* Current Balance Display */}
    - + + + {t('Current Balance')}
    diff --git a/web/default/src/features/channels/components/drawers/channel-mutate-drawer.tsx b/web/default/src/features/channels/components/drawers/channel-mutate-drawer.tsx index e3b1aa7b..b3aa0672 100644 --- a/web/default/src/features/channels/components/drawers/channel-mutate-drawer.tsx +++ b/web/default/src/features/channels/components/drawers/channel-mutate-drawer.tsx @@ -78,6 +78,7 @@ import { FormLabel, FormMessage, } from '@/components/ui/form' +import { IconBadge, type IconBadgeTone } from '@/components/ui/icon-badge' import { Input } from '@/components/ui/input' import { Select, @@ -365,25 +366,37 @@ function formatUnixTime(timestamp: unknown): string { return new Date(seconds * 1000).toLocaleString() } -function CardHeading({ title, icon }: { title: string; icon?: ReactNode }) { +function CardHeading(props: { + title: string + icon?: ReactNode + iconTone?: IconBadgeTone +}) { return (
    - {icon && ( - - {icon} - + {props.icon && ( + + {props.icon} + )} -

    {title}

    +

    {props.title}

    ) } -function SubHeading({ title, icon }: { title: string; icon?: ReactNode }) { +function SubHeading(props: { + title: string + icon?: ReactNode + iconTone?: IconBadgeTone +}) { return (
    - {icon && {icon}} + {props.icon && ( + + {props.icon} + + )}

    - {title} + {props.title}

    ) @@ -1823,9 +1836,9 @@ export function ChannelMutateDrawer({
    - + - + {isEditing ? t('Edit Channel') : t('Create Channel')} @@ -3583,6 +3596,7 @@ export function ChannelMutateDrawer({ } + iconTone='info' />
    } + iconTone='info' />
    } + iconTone='chart-3' />
    } + iconTone='chart-4' /> } + iconTone='chart-3' /> {sensitiveLocked && ( @@ -4231,6 +4249,7 @@ export function ChannelMutateDrawer({ } + iconTone='chart-4' />
    } + iconTone='info' />