Skip to content

Commit

Permalink
Improved support for material-symbols (#43)
Browse files Browse the repository at this point in the history
* Improved support for material-symbols

* Added changeset
  • Loading branch information
seanrcollings authored Nov 20, 2024
1 parent c076cd9 commit 9b182df
Show file tree
Hide file tree
Showing 6 changed files with 2,341 additions and 43 deletions.
5 changes: 5 additions & 0 deletions .changeset/pretty-chairs-smash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@atomicjolt/atomic-elements": major
---

Completed implementation of MaterialSymbol component
Original file line number Diff line number Diff line change
@@ -1,27 +1,20 @@
import React from "react";
import {
ExtendedSize,
IconComponentBase,
MaterialIcons,
MaterialIconVariants,
RenderBaseProps,
} from "../../../types";
import { useRenderProps } from "../../../hooks";
import { StyledIcon } from "../Icons.styles";
import { filterDOMProps, mergeProps } from "@react-aria/utils";
import { filterDOMProps, mergeProps, useObjectRef } from "@react-aria/utils";
import { useFocusable } from "@react-aria/focus";
import useForwardedRef from "@hooks/useForwardedRef";

export interface MaterialIconProps
extends Omit<React.ComponentProps<"i">, "className" | "style" | "children">,
RenderBaseProps<never> {
export interface MaterialIconProps extends IconComponentBase<never> {
icon: MaterialIcons;
/** The type of material icon to
* render. Note that the font for that
* style needs to be in scope for it to render properly */
variant?: MaterialIconVariants;
size?: ExtendedSize;

isDisabled?: boolean;

/** @deprecated - use isDisabled */
disabled?: boolean;
Expand All @@ -30,7 +23,7 @@ export interface MaterialIconProps
/** Small Utility component for rendering out
* material-icons with some sensible defaults */
export const MaterialIcon = React.forwardRef<HTMLElement, MaterialIconProps>(
function MaterialProps(props, ref) {
function MaterialProps(props, forwardedRef) {
const {
icon,
className,
Expand All @@ -42,7 +35,7 @@ export const MaterialIcon = React.forwardRef<HTMLElement, MaterialIconProps>(
...rest
} = props;

const internalRef = useForwardedRef(ref);
const ref = useObjectRef(forwardedRef);

const materialIconClass =
variant === "default" ? "material-icons" : `material-icons-${variant}`;
Expand All @@ -59,7 +52,7 @@ export const MaterialIcon = React.forwardRef<HTMLElement, MaterialIconProps>(

// We use the focusable hook so that the icon supports tooltips
// when the icon itself isn't actually focusable
const { focusableProps } = useFocusable({}, internalRef);
const { focusableProps } = useFocusable({}, ref);

const componentProps = mergeProps(
filterDOMProps(rest),
Expand All @@ -68,7 +61,7 @@ export const MaterialIcon = React.forwardRef<HTMLElement, MaterialIconProps>(
);

return (
<StyledIcon ref={internalRef} aria-hidden {...componentProps}>
<StyledIcon ref={ref} aria-hidden {...componentProps}>
{icon}
</StyledIcon>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,48 +1,46 @@
import React, { RefObject } from "react";
import { ExtendedSize, MaterialIcons, RenderBaseProps } from "../../../types";
import { useRenderProps } from "../../../hooks";
import { StyledIcon } from "../Icons.styles";
import React from "react";
import { useFocusable } from "@react-aria/focus";
import { filterDOMProps, mergeProps } from "@react-aria/utils";
import useForwardedRef from "@hooks/useForwardedRef";

export type MaterialSymbolVariants = "outlined" | "rounded" | "sharp";
import { filterDOMProps, mergeProps, useObjectRef } from "@react-aria/utils";
import {
IconComponentBase,
MaterialSymbolFontSettingsOptions,
MaterialSymbols,
MaterialSymbolVariants,
} from "../../../types";
import { useRenderProps } from "@hooks/useRenderProps";
import { useMaterialSymbolFontVariationSettings } from "@hooks/useMaterialSymbolFontVariationSettings";
import { StyledIcon } from "../Icons.styles";

export interface MaterialSymbolProps
extends Omit<React.ComponentProps<"i">, "className" | "style" | "children">,
RenderBaseProps<never> {
// TODO: this probably isn't the right type
symbol: MaterialIcons;
extends IconComponentBase<never>,
MaterialSymbolFontSettingsOptions {
symbol: MaterialSymbols;
/** The type of material symbol to
* render. Note that the font for that style needs to
* be in scope for it to render properly */
variant?: MaterialSymbolVariants;

isDisabled?: boolean;

/** @deprecated - use isDisabled */
disabled?: boolean;

size?: ExtendedSize;
}

/** Render out material-symbols with sensible defaults. */
export const MaterialSymbol = React.forwardRef<
HTMLElement,
MaterialSymbolProps
>(function MaterialSymbol(props, ref) {
>(function MaterialSymbol(props, forwardedRef) {
const {
symbol,
className,
variant = "outlined",
size = "medium",
disabled = false,
isDisabled = disabled,
isDisabled = false,
style,
weight,
grade,
opticalSize,
fill,
...rest
} = props;

const internalRef = useForwardedRef(ref);
const ref = useObjectRef(forwardedRef);

const materialSymbolClass = `material-symbols-${variant}`;

Expand All @@ -56,9 +54,16 @@ export const MaterialSymbol = React.forwardRef<
},
});

const fontVariationSettings = useMaterialSymbolFontVariationSettings({
fill,
weight,
grade,
opticalSize,
});

// We use the focusable hook so that the icon supports tooltips
// the icon itself isn't actually focusable
const { focusableProps } = useFocusable({}, internalRef);
const { focusableProps } = useFocusable({}, ref);

const componentProps = mergeProps(
filterDOMProps(rest),
Expand All @@ -67,7 +72,15 @@ export const MaterialSymbol = React.forwardRef<
);

return (
<StyledIcon ref={internalRef} aria-hidden {...componentProps}>
<StyledIcon
ref={ref}
aria-hidden
{...componentProps}
style={{
fontVariationSettings: fontVariationSettings || undefined,
...componentProps.style,
}}
>
{symbol}
</StyledIcon>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useMemo } from "react";
import { MaterialSymbolFontSettingsOptions } from "../types";

export function useMaterialSymbolFontVariationSettings(
props: MaterialSymbolFontSettingsOptions
): string {
const { fill, weight, grade, opticalSize } = props;

return useMemo(() => {
const settings: Record<string, any> = {};

if (fill) settings["FILL"] = fill;
if (weight) settings["wght"] = weight;
if (grade) settings["GRAD"] = grade;
if (opticalSize) settings["opsz"] = opticalSize;

return Object.entries(settings)
.map(([key, value]) => `'${key}' ${value}`)
.join(", ");
}, [fill, weight, grade, opticalSize]);
}
9 changes: 5 additions & 4 deletions packages/atomic-elements/src/styles/fonts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,13 @@ const fontUrls: Record<SupportedFont, string> = {
"material-icons-two-tone":
"https://fonts.googleapis.com/icon?family=Material+Icons%7CMaterial+Icons+Two+Tone",
"material-symbols-outlined":
"https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,300,0,0",
"https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200",
"material-symbols-rounded":
"https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@24,300,0,0",
"https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200",
"material-symbols-sharp":
"https://fonts.googleapis.com/css2?family=Material+Symbols+Sharp:opsz,wght,FILL,GRAD@24,300,0,0",
"https://fonts.googleapis.com/css2?family=Material+Symbols+Sharp:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200",
};

const FontCSS = createGlobalStyle`
/* Hide the icon fonts until they fully load */
.material-icons {
Expand Down Expand Up @@ -77,7 +78,7 @@ export function LoadFonts(props: LoadFontsProps) {
link.rel = "stylesheet";
document.head.appendChild(link);
});
}, [])
}, []);

return <FontCSS />;
}
Loading

0 comments on commit 9b182df

Please sign in to comment.