diff --git a/apps/docs/src/app/components/material-icons-and-symbols/AdditionalChanges.tsx b/apps/docs/src/app/components/material-icons-and-symbols/AdditionalChanges.tsx index 8a19566c3f..437cb736a2 100644 --- a/apps/docs/src/app/components/material-icons-and-symbols/AdditionalChanges.tsx +++ b/apps/docs/src/app/components/material-icons-and-symbols/AdditionalChanges.tsx @@ -6,6 +6,7 @@ import styles from "./AdditionalChanges.module.scss"; import { AdditionalChangesPanels } from "./AdditionalChangesPanels.js"; import { MaterialConfigChanges } from "./MaterialConfigChanges.js"; import { useMaterialIconsAndSymbols } from "./MaterialIconsAndSymbolsProvider.js"; +import { isMaterialIconType } from "./searchParams.js"; export interface AdditionalChangesProps { isSvg?: boolean; @@ -17,8 +18,9 @@ export function AdditionalChanges( const { isSvg } = props; const { iconType } = useMaterialIconsAndSymbols(); - const configDescription = - iconType === "icon" ? "icon family" : "symbol customizations"; + const configDescription = isMaterialIconType(iconType) + ? "icon family" + : "symbol customizations"; return ( <> diff --git a/apps/docs/src/app/components/material-icons-and-symbols/FilterCategoryPanel.tsx b/apps/docs/src/app/components/material-icons-and-symbols/FilterCategoryPanel.tsx index 26a6ad6fb5..43836e4d3f 100644 --- a/apps/docs/src/app/components/material-icons-and-symbols/FilterCategoryPanel.tsx +++ b/apps/docs/src/app/components/material-icons-and-symbols/FilterCategoryPanel.tsx @@ -12,6 +12,7 @@ import { MATERIAL_ICON_CATEGORIES, MATERIAL_SYMBOL_CATEGORIES, } from "./metadata.js"; +import { isMaterialIconType } from "./searchParams.js"; export function FilterCategoryPanel( props: ProvidedExpansionPanelProps @@ -19,8 +20,9 @@ export function FilterCategoryPanel( const { isDesktop } = useAppSize(); const { iconType, iconCategory, setIconCategory } = useMaterialIconsAndSymbols(); - const iconCategories = - iconType === "icon" ? MATERIAL_ICON_CATEGORIES : MATERIAL_SYMBOL_CATEGORIES; + const iconCategories = isMaterialIconType(iconType) + ? MATERIAL_ICON_CATEGORIES + : MATERIAL_SYMBOL_CATEGORIES; return ( } name="Style"> @@ -32,7 +32,8 @@ export function FilterStylePanel( onChange={(event) => setIconType(event.currentTarget.value)} > Material Symbols - Material Icons + Material Icons (SVG) + Material Icons (Font) { - dispatch({ type: "resetSymbols" }); - }} + onClick={resetSymbols} className={styles.button} > diff --git a/apps/docs/src/app/components/material-icons-and-symbols/HowToUseSheetContent.tsx b/apps/docs/src/app/components/material-icons-and-symbols/HowToUseSheetContent.tsx index d2f825fddb..1b380c13fb 100644 --- a/apps/docs/src/app/components/material-icons-and-symbols/HowToUseSheetContent.tsx +++ b/apps/docs/src/app/components/material-icons-and-symbols/HowToUseSheetContent.tsx @@ -1,4 +1,5 @@ import { + DialogContent, Slide, SlideContainer, Tab, @@ -12,34 +13,38 @@ import { useMaterialIconsAndSymbols } from "./MaterialIconsAndSymbolsProvider.js import { MaterialSymbolStylesheets } from "./MaterialSymbolStylesheets.jsx"; import { MaterialSymbolUsage } from "./MaterialSymbolUsage.jsx"; import { SVGIconImportAndUsage } from "./SVGIconImportAndUsage.jsx"; +import { isMaterialIconType } from "./searchParams.js"; export function HowToUseSheetContent(): ReactElement | null { + const { selectedIconName, iconType } = useMaterialIconsAndSymbols(); const { getTabListProps, getTabProps, getTabPanelProps, getTabPanelsProps } = useTabs(); - const { selectedIconName, iconType } = useMaterialIconsAndSymbols(); if (!selectedIconName) { return null; } - const isSymbol = iconType === "symbol"; + if (isMaterialIconType(iconType)) { + const isSvg = iconType === "icon"; + return ( + + {isSvg ? : } + + ); + } return ( <> - {isSymbol ? "Usage" : "SVG Icon Usage"} - - {isSymbol ? "Stylesheet" : "Font Icon Usage"} - + Usage + Stylesheet - {isSymbol && } - {!isSymbol && } + - {isSymbol && } - {!isSymbol && } + > diff --git a/apps/docs/src/app/components/material-icons-and-symbols/IconImportAndUsage.tsx b/apps/docs/src/app/components/material-icons-and-symbols/IconImportAndUsage.tsx index 619fe61c53..a4b0f67680 100644 --- a/apps/docs/src/app/components/material-icons-and-symbols/IconImportAndUsage.tsx +++ b/apps/docs/src/app/components/material-icons-and-symbols/IconImportAndUsage.tsx @@ -1,22 +1,16 @@ import { type ReactElement } from "react"; import { CopyCode } from "./CopyCode.jsx"; import { useMaterialIconsAndSymbols } from "./MaterialIconsAndSymbolsProvider.jsx"; -import { getMaterialIconComponentName } from "./utils.js"; export function IconImportAndUsage(): ReactElement | null { - const { iconFamily, selectedIconName, iconType } = - useMaterialIconsAndSymbols(); + const { selectedIconName, iconType } = useMaterialIconsAndSymbols(); if (!selectedIconName) { return null; } - const importCode = `import { Material${ - iconType === "symbol" ? "Symbol" : "Icon" - } } from "@react-md/core"`; - const usageCode = `<${getMaterialIconComponentName({ - iconName: selectedIconName, - iconFamily, - })} name="${selectedIconName}" />`; + const component = `Material${iconType === "symbol" ? "Symbol" : "Icon"}`; + const importCode = `import { Material${component} } from "@react-md/core"`; + const usageCode = `<${component} name="${selectedIconName}" />`; return ( <> diff --git a/apps/docs/src/app/components/material-icons-and-symbols/MaterialIconsAndSymbolsProvider.tsx b/apps/docs/src/app/components/material-icons-and-symbols/MaterialIconsAndSymbolsProvider.tsx index 42ba528024..927d8041f3 100644 --- a/apps/docs/src/app/components/material-icons-and-symbols/MaterialIconsAndSymbolsProvider.tsx +++ b/apps/docs/src/app/components/material-icons-and-symbols/MaterialIconsAndSymbolsProvider.tsx @@ -25,7 +25,11 @@ import { MATERIAL_ICON_FAMILY_TYPES, MATERIAL_SYMBOL_FAMILY_TYPES, } from "./metadata.js"; -import { getIconUrl, getInitialState } from "./searchParams.js"; +import { + getIconUrl, + getInitialState, + isMaterialIconType, +} from "./searchParams.js"; import { type MaterialIconsAndSymbolsAction, type MaterialIconsAndSymbolsContext, @@ -70,10 +74,9 @@ export function MaterialIconsAndSymbolsProvider({ case "setIconType": { const iconType = action.payload; let { iconFamily, iconCategory } = state; - const nextFamilyTypes = - iconType === "icon" - ? MATERIAL_ICON_FAMILY_TYPES - : MATERIAL_SYMBOL_FAMILY_TYPES; + const nextFamilyTypes = isMaterialIconType(iconType) + ? MATERIAL_ICON_FAMILY_TYPES + : MATERIAL_SYMBOL_FAMILY_TYPES; if (!nextFamilyTypes.includes(iconFamily as "outlined")) { iconFamily = "outlined"; } @@ -143,6 +146,11 @@ export function MaterialIconsAndSymbolsProvider({ ? state.selectedIconName : null, }; + case "changeSvgToFont": + return { + ...state, + iconType: "icon-font" as const, + }; default: throw new Error("Unreachable"); } @@ -177,7 +185,6 @@ export function MaterialIconsAndSymbolsProvider({ iconType, iconFamily, iconCategory, - dispatch, fill: indexToMaterialFill(symbolFill), weight: indexToMaterialWeight(symbolWeight), grade: indexToMaterialGrade(symbolGrade), @@ -201,6 +208,12 @@ export function MaterialIconsAndSymbolsProvider({ deselectIcon() { dispatch({ type: "deselectIcon" }); }, + resetSymbols() { + dispatch({ type: "resetSymbols" }); + }, + resetFilters() { + dispatch({ type: "resetFilters" }); + }, setFill(value) { const payload = typeof value === "number" ? value : value(symbolFill); dispatch({ type: "setFill", payload }); @@ -233,6 +246,9 @@ export function MaterialIconsAndSymbolsProvider({ setIconCategory(iconCategory) { dispatch({ type: "setIconCategory", payload: iconCategory }); }, + changeSvgToFont() { + dispatch({ type: "changeSvgToFont" }); + }, }), [ filtersVisible, diff --git a/apps/docs/src/app/components/material-icons-and-symbols/NoMatches.tsx b/apps/docs/src/app/components/material-icons-and-symbols/NoMatches.tsx index c9385e655d..4b270766c8 100644 --- a/apps/docs/src/app/components/material-icons-and-symbols/NoMatches.tsx +++ b/apps/docs/src/app/components/material-icons-and-symbols/NoMatches.tsx @@ -4,17 +4,14 @@ import type { ReactElement } from "react"; import { useMaterialIconsAndSymbols } from "./MaterialIconsAndSymbolsProvider.jsx"; export function NoMatches(): ReactElement { - const { dispatch, search } = useMaterialIconsAndSymbols(); + const { search, resetFilters } = useMaterialIconsAndSymbols(); return ( {`No icons found for '${search}'`} - dispatch({ type: "resetFilters" })} - > + resetFilters()}> Clear your filters and try again diff --git a/apps/docs/src/app/components/material-icons-and-symbols/SVGIconImportAndUsage.tsx b/apps/docs/src/app/components/material-icons-and-symbols/SVGIconImportAndUsage.tsx index 43fcd3856a..5dc77903e1 100644 --- a/apps/docs/src/app/components/material-icons-and-symbols/SVGIconImportAndUsage.tsx +++ b/apps/docs/src/app/components/material-icons-and-symbols/SVGIconImportAndUsage.tsx @@ -3,17 +3,22 @@ import { PackageManagerCodeBlock } from "@/components/PackageManagerCodeBlock.js import { highlightCode } from "@/utils/highlightCode.js"; import { Typography } from "@react-md/core"; import { cnb } from "cnbuilder"; +import Link from "next/link.js"; +import { usePathname } from "next/navigation.js"; import { type ReactElement } from "react"; import { AdditionalChanges } from "./AdditionalChanges.jsx"; import { CopyCode } from "./CopyCode.js"; import { useMaterialIconsAndSymbols } from "./MaterialIconsAndSymbolsProvider.js"; import styles from "./SVGIconImportAndUsage.module.scss"; import { TwoToneIconWarning } from "./TwoToneIconWarning.jsx"; +import { getIconUrl } from "./searchParams.js"; import { getMaterialIconComponentName } from "./utils.js"; export function SVGIconImportAndUsage(): ReactElement | null { - const { selectedIconName, iconFamily, isFontFamilyChanged } = - useMaterialIconsAndSymbols(); + const pathname = usePathname(); + const context = useMaterialIconsAndSymbols(); + const { selectedIconName, iconFamily, isFontFamilyChanged, changeSvgToFont } = + context; if (!selectedIconName) { return null; } @@ -32,7 +37,7 @@ export function SVGIconImportAndUsage(): ReactElement | null { margin="top" className={cnb(styles.heading, styles.noMarginTop)} > - Import and usage + SVG Icon import and usage {importCode} {usageCode} @@ -43,7 +48,18 @@ export function SVGIconImportAndUsage(): ReactElement | null { If you use a lot of icons in your app, you might be able to reduce - your bundle size and dependencies by using the font icons instead. + your bundle size and dependencies by using the{" "} + + font icons + {" "} + instead. = N extends number ? `${N}` : never; + +export function isMaterialIconType( iconType: unknown -): iconType is MaterialIconType { - return iconType === "icon" || iconType === "symbol"; +): iconType is "icon" | "icon-font" { + return iconType === "icon" || iconType === "icon-font"; +} + +export function isValidIconType(iconType: unknown): iconType is IconType { + return isMaterialIconType(iconType) || iconType === "symbol"; } export function isValidIconFamily( iconFamily: unknown, - iconType: MaterialIconType + iconType: IconType ): iconFamily is MaterialIconFamily { return ( typeof iconFamily === "string" && - ((iconType === "icon" && ["two-tone", "filled"].includes(iconFamily)) || + ((isMaterialIconType(iconType) && + ["two-tone", "filled"].includes(iconFamily)) || ["outlined", "rounded", "sharp"].includes(iconFamily)) ); } export function isValidIconCategory( iconCategory: unknown, - iconType: MaterialIconType, + iconType: IconType, iconFamily: MaterialIconFamily ): iconCategory is IconCategoryFilter { if (typeof iconCategory !== "string" || !iconCategory) { return false; } - const icons = iconType === "icon" ? MATERIAL_ICONS : MATERIAL_SYMBOLS; + const icons = isMaterialIconType(iconType) + ? MATERIAL_ICONS + : MATERIAL_SYMBOLS; const categories = icons[iconFamily]; return Object.keys(categories).includes(iconCategory); } export function isValidSelectedIconName( name: unknown, - iconType: MaterialIconType + iconType: IconType ): name is MaterialIconAndSymbolName { if (typeof name !== "string" || !name) { return false; } - const namesRecord = iconType === "icon" ? MATERIAL_ICONS : MATERIAL_SYMBOLS; + const namesRecord = isMaterialIconType(iconType) + ? MATERIAL_ICONS + : MATERIAL_SYMBOLS; const names = Object.values(namesRecord); for (const iconFamilyNamesRecord of names) { const iconFamilyNames = Object.values(iconFamilyNamesRecord); @@ -79,28 +96,21 @@ export function isValidSelectedIconName( export function isValidSymbolGrade( grade: unknown -): grade is "-25" | "0" | "200" { +): grade is NumberToString { return ( typeof grade === "string" && !grade && ["-25", "0", "200"].includes(grade) ); } -export function isValidSymbolFill(fill: unknown): fill is "0" | "1" { +export function isValidSymbolFill( + fill: unknown +): fill is NumberToString { return fill === "0" || fill === "1"; } export function isValidSymbolWeight( weight: unknown -): weight is - | "100" - | "200" - | "300" - | "400" - | "500" - | "600" - | "700" - | "800" - | "900" { +): weight is NumberToString { return ( typeof weight === "string" && !!weight && @@ -112,7 +122,7 @@ export function isValidSymbolWeight( export function isValidSymbolOpticalSize( opticalSize: unknown -): opticalSize is "20" | "24" | "40" | "48" { +): opticalSize is NumberToString { return ( typeof opticalSize === "string" && !!opticalSize && diff --git a/apps/docs/src/app/components/material-icons-and-symbols/types.ts b/apps/docs/src/app/components/material-icons-and-symbols/types.ts index 278bd25e34..b725f9e6a9 100644 --- a/apps/docs/src/app/components/material-icons-and-symbols/types.ts +++ b/apps/docs/src/app/components/material-icons-and-symbols/types.ts @@ -6,7 +6,6 @@ import { type MaterialSymbolWeight, type UseStateSetter, } from "@react-md/core"; -import { type Dispatch } from "react"; import { type MaterialIconAndSymbolName, type MaterialIconCategory, @@ -19,9 +18,11 @@ export type IconCategoryFilter = | MaterialSymbolCategory | ""; +export type IconType = MaterialIconType | "icon-font"; + export interface MaterialIconsAndSymbolsRef { search: string; - iconType: MaterialIconType; + iconType: IconType; iconFamily: MaterialIconFamily; iconCategory: IconCategoryFilter; @@ -40,7 +41,7 @@ export interface MaterialIconsAndSymbolsState export type MaterialIconsAndSymbolsAction = | { type: "setSearch"; payload: string } - | { type: "setIconType"; payload: MaterialIconType } + | { type: "setIconType"; payload: IconType } | { type: "setIconFamily"; payload: MaterialIconFamily } | { type: "setIconCategory"; payload: IconCategoryFilter } | { @@ -49,7 +50,8 @@ export type MaterialIconsAndSymbolsAction = | "resetFilters" | "resetSymbols" | "toggleFilters" - | "deselectIcon"; + | "deselectIcon" + | "changeSvgToFont"; } | { type: "selectIcon"; payload: MaterialIconAndSymbolName } | { @@ -63,10 +65,11 @@ export interface MaterialIconsAndSymbolsContext weight: MaterialSymbolWeight; grade: MaterialSymbolGrade; opticalSize: MaterialSymbolOpticalSize; - dispatch: Dispatch; selectIcon(name: MaterialIconAndSymbolName): void; deselectIcon(): void; toggleFilters(): void; + resetSymbols(): void; + resetFilters(): void; isFillChanged: boolean; isGradeChanged: boolean; isWeightChanged: boolean; @@ -79,7 +82,8 @@ export interface MaterialIconsAndSymbolsContext isSymbolCustomizationChanged: boolean; isResettable: boolean; setSearch(search: string): void; - setIconType(iconType: MaterialIconType): void; + setIconType(iconType: IconType): void; setIconFamily(iconFamily: MaterialIconFamily): void; setIconCategory(iconCategory: IconCategoryFilter): void; + changeSvgToFont(): void; } diff --git a/apps/docs/src/app/components/material-icons-and-symbols/utils.ts b/apps/docs/src/app/components/material-icons-and-symbols/utils.ts index 283d63e974..76367fec0c 100644 --- a/apps/docs/src/app/components/material-icons-and-symbols/utils.ts +++ b/apps/docs/src/app/components/material-icons-and-symbols/utils.ts @@ -11,9 +11,9 @@ import { MATERIAL_SYMBOLS, type IconsByCategory, type MaterialIconAndSymbolName, - type MaterialIconType, } from "./metadata.js"; -import { type IconCategoryFilter } from "./types.js"; +import { isMaterialIconType } from "./searchParams.js"; +import { type IconCategoryFilter, type IconType } from "./types.js"; export function getCategoryName(category: string): string { if (category.includes("&")) { @@ -37,13 +37,13 @@ export function getCategoryName(category: string): string { export function isMaterialSymbol( iconName: MaterialIconAndSymbolName, - iconType: MaterialIconType + iconType: IconType ): iconName is MaterialSymbolName { return iconType === "symbol"; } export interface IconsByCategoryOptions { - iconType: MaterialIconType; + iconType: IconType; iconFamily: MaterialIconFamily; iconCategory: IconCategoryFilter; } @@ -55,7 +55,7 @@ export function getIconsByCategory( const icon = MATERIAL_ICONS[iconFamily]; const symbol = MATERIAL_SYMBOLS[iconFamily]; - let iconsByCategory = iconType === "icon" ? icon : symbol; + let iconsByCategory = isMaterialIconType(iconType) ? icon : symbol; // TODO: Revert this (?) once the swc minifier no longer considers this // "dead code" for the `MATERIAL_SYMBOLS` and `MATERIAL_ICONS` constants. // let iconsByCategory = ( @@ -105,7 +105,7 @@ export function getMaterialIconComponentName( type DefinedSpecs = Omit; interface FontStylesheetOptions { - iconType: MaterialIconType; + iconType: IconType; iconFamily: MaterialIconFamily; } @@ -131,7 +131,7 @@ export function getFontStylesheet( .map((part) => lodash.upperFirst(part)) .join("+"); - if (iconType === "icon") { + if (isMaterialIconType(iconType)) { familyName = familyName === "Rounded" ? "Round" @@ -142,9 +142,8 @@ export function getFontStylesheet( specs = `:opsz,wght,FILL,GRAD@${opticalSize},${weight},${fill},${grade}`; } - const suffix = `${lodash.upperFirst(iconType)}s${ - familyName ? `+${familyName}` : "" - }${specs}`; + const name = lodash.upperFirst(iconType.split("-")[0]); + const suffix = `${name}s${familyName ? `+${familyName}` : ""}${specs}`; return `https://fonts.googleapis.com/css2?family=Material+${suffix}`; }
If you use a lot of icons in your app, you might be able to reduce - your bundle size and dependencies by using the font icons instead. + your bundle size and dependencies by using the{" "} + + font icons + {" "} + instead.