feat(ui): add opt-in filled variants to OIcon for rail icons

This commit is contained in:
Ashish Kolhe
2026-11-03 17:58:31 +05:30
parent 8b30a30fd0
commit 76fe67621d
3 changed files with 52 additions and 2 deletions
+44
View File
@@ -365,6 +365,27 @@ import AndroidIcon from "~icons/material-symbols/android";
import PhoneIphoneIcon from "~icons/material-symbols/phone-iphone-outline";
import CallIcon from "~icons/material-symbols/call-outline";
// Filled twins of the rail icons, shown on the selected rail tile (see filledIconRegistry).
import AccountTreeFilled from "~icons/material-symbols/account-tree";
import AutoAwesomeFilled from "~icons/material-symbols/auto-awesome";
import DashboardFilled from "~icons/material-symbols/dashboard";
import DataPlusFilled from "~icons/majesticons/data-plus";
import DatabaseFilled from "~icons/material-symbols/database";
import DescriptionFilled from "~icons/material-symbols/description";
import DevicesFilled from "~icons/material-symbols/devices";
import DnsFilled from "~icons/material-symbols/dns";
import HomeFilled from "~icons/mdi/home";
import ManageAccountsFilled from "~icons/material-symbols/manage-accounts";
import MemoryFilled from "~icons/material-symbols/memory";
import NavigationFilled from "~icons/material-symbols/navigation";
import NotificationsActiveFilled from "~icons/material-symbols/notifications-active";
import NotificationsPausedFilled from "~icons/material-symbols/notifications-paused";
import SchemaFilled from "~icons/material-symbols/schema";
import SettingsFilled from "~icons/material-symbols/settings";
import ShieldAlertFilled from "~icons/mdi/shield-alert";
import ShieldFilled from "~icons/material-symbols/shield";
import WindowFilled from "~icons/material-symbols/window";
// Brand mark (not a Material Symbol) — a local inline SVG component. See McpLogo.vue.
import McpLogo from "./McpLogo.vue";
@@ -742,3 +763,26 @@ export const iconRegistry = {
} as const satisfies Record<string, Component>;
export type IconName = keyof typeof iconRegistry;
/** Filled variants for `<OIcon filled>`; a name without one keeps its outlined glyph. */
export const filledIconRegistry: Partial<Record<IconName, Component>> = {
"account-tree": AccountTreeFilled,
"auto-awesome": AutoAwesomeFilled,
dashboard: DashboardFilled,
"data-plus-line": DataPlusFilled,
database: DatabaseFilled,
description: DescriptionFilled,
devices: DevicesFilled,
dns: DnsFilled,
home: HomeFilled,
"manage-accounts": ManageAccountsFilled,
memory: MemoryFilled,
navigation: NavigationFilled,
"notifications-active": NotificationsActiveFilled,
"notifications-paused": NotificationsPausedFilled,
schema: SchemaFilled,
settings: SettingsFilled,
"shield-alert-outline": ShieldAlertFilled,
shield: ShieldFilled,
window: WindowFilled,
};
+2
View File
@@ -18,6 +18,8 @@ export interface IconProps {
size?: "xs" | "sm" | "md" | "lg" | "xl";
/** Accessible label. When provided the icon has role="img". When omitted it is aria-hidden. */
label?: I18nText;
/** Render the filled variant when the registry has one (selected nav state). */
filled?: boolean;
}
export interface IconSlots {
+6 -2
View File
@@ -1,7 +1,7 @@
<script setup lang="ts">
import type { IconProps, IconSlots } from "./OIcon.types";
import { computed } from "vue";
import { iconRegistry } from "./OIcon.icons";
import { filledIconRegistry, iconRegistry } from "./OIcon.icons";
const props = withDefaults(defineProps<IconProps>(), {
size: "md",
@@ -15,7 +15,11 @@ const isImgIcon = computed<boolean>(() => Boolean(props.name?.startsWith("img:")
/** The src path stripped of the `img:` prefix */
const imgSrc = computed<string>(() => (props.name?.startsWith("img:") ? props.name.slice(4) : ""));
const iconComponent = computed(() => iconRegistry[props.name as keyof typeof iconRegistry]);
const iconComponent = computed(
() =>
(props.filled && filledIconRegistry[props.name as keyof typeof iconRegistry]) ||
iconRegistry[props.name as keyof typeof iconRegistry],
);
const RTL_MIRRORED_ICONS = new Set([
"arrow-back",