refactor: migrate all icons from Untitled UI to FontAwesome Pro Duotone

Replace all @untitledui/icons imports across 55 files with equivalent
@fortawesome/pro-duotone-svg-icons icons, using FontAwesomeIcon wrappers
(FC<{ className?: string }>) for prop-based usage and inline replacements
for direct JSX usage. Drops unsupported Untitled UI-specific props
(strokeWidth, numeric size). TypeScript compiles clean with no errors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-21 11:07:19 +05:30
parent 3064eeb444
commit 6f40b82579
55 changed files with 289 additions and 120 deletions

View File

@@ -1,6 +1,7 @@
import type { FocusEventHandler, PointerEventHandler, RefAttributes, RefObject } from "react";
import { useCallback, useContext, useRef, useState } from "react";
import { SearchLg as SearchIcon } from "@untitledui/icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faMagnifyingGlass } from "@fortawesome/pro-duotone-svg-icons";
import type { ComboBoxProps as AriaComboBoxProps, GroupProps as AriaGroupProps, ListBoxProps as AriaListBoxProps } from "react-aria-components";
import { ComboBox as AriaComboBox, Group as AriaGroup, Input as AriaInput, ListBox as AriaListBox, ComboBoxStateContext } from "react-aria-components";
import { HintText } from "@/components/base/input/hint-text";
@@ -51,7 +52,7 @@ const ComboBoxValue = ({ size, shortcut, placeholder, shortcutClassName, ...othe
>
{({ isDisabled }) => (
<>
<SearchIcon className="pointer-events-none size-5 shrink-0 text-fg-quaternary" />
<FontAwesomeIcon icon={faMagnifyingGlass} className="pointer-events-none size-5 shrink-0 text-fg-quaternary" />
<div className="relative flex w-full items-center gap-2">
{inputValue && (

View File

@@ -1,6 +1,9 @@
import type { FocusEventHandler, KeyboardEvent, PointerEventHandler, RefAttributes, RefObject } from "react";
import type { FC, FocusEventHandler, KeyboardEvent, PointerEventHandler, RefAttributes, RefObject } from "react";
import { createContext, useCallback, useContext, useRef, useState } from "react";
import { SearchLg } from "@untitledui/icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faMagnifyingGlass } from "@fortawesome/pro-duotone-svg-icons";
const SearchIcon: FC<{ className?: string }> = ({ className }) => <FontAwesomeIcon icon={faMagnifyingGlass} className={className} />;
import { FocusScope, useFilter, useFocusManager } from "react-aria";
import type { ComboBoxProps as AriaComboBoxProps, GroupProps as AriaGroupProps, ListBoxProps as AriaListBoxProps, Key } from "react-aria-components";
import { ComboBox as AriaComboBox, Group as AriaGroup, Input as AriaInput, ListBox as AriaListBox, ComboBoxStateContext } from "react-aria-components";
@@ -317,7 +320,7 @@ export const MultiSelectTagsValue = ({
shortcut,
placeholder,
shortcutClassName,
placeholderIcon: Icon = SearchLg,
placeholderIcon: Icon = SearchIcon,
// Omit this prop to avoid invalid HTML attribute warning
isDisabled: _isDisabled,
...otherProps

View File

@@ -1,5 +1,6 @@
import { isValidElement, useContext } from "react";
import { Check } from "@untitledui/icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCheck } from "@fortawesome/pro-duotone-svg-icons";
import type { ListBoxItemProps as AriaListBoxItemProps } from "react-aria-components";
import { ListBoxItem as AriaListBoxItem, Text as AriaText } from "react-aria-components";
import { Avatar } from "@/components/base/avatar/avatar";
@@ -79,11 +80,12 @@ export const SelectItem = ({ label, id, value, avatarUrl, supportingText, isDisa
</div>
{state.isSelected && (
<Check
<FontAwesomeIcon
icon={faCheck}
aria-hidden="true"
className={cx(
"ml-auto text-fg-brand-primary",
size === "sm" ? "size-4 stroke-[2.5px]" : "size-5",
size === "sm" ? "size-4" : "size-5",
state.isDisabled && "text-fg-disabled",
)}
/>

View File

@@ -1,5 +1,6 @@
import { type SelectHTMLAttributes, useId } from "react";
import { ChevronDown } from "@untitledui/icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faChevronDown } from "@fortawesome/pro-duotone-svg-icons";
import { HintText } from "@/components/base/input/hint-text";
import { Label } from "@/components/base/input/label";
import { cx } from "@/utils/cx";
@@ -51,9 +52,10 @@ export const NativeSelect = ({ label, hint, options, className, selectClassName,
</option>
))}
</select>
<ChevronDown
<FontAwesomeIcon
icon={faChevronDown}
aria-hidden="true"
className="pointer-events-none absolute right-3.5 size-5 text-fg-quaternary in-data-input-wrapper:right-0 in-data-input-wrapper:size-4 in-data-input-wrapper:stroke-[2.625px] in-data-input-wrapper:in-data-trailing:in-data-[input-size=sm]:right-3"
className="pointer-events-none absolute right-3.5 size-5 text-fg-quaternary in-data-input-wrapper:right-0 in-data-input-wrapper:size-4 in-data-input-wrapper:in-data-trailing:in-data-[input-size=sm]:right-3"
/>
</div>

View File

@@ -1,6 +1,7 @@
import type { FC, ReactNode, Ref, RefAttributes } from "react";
import { createContext, isValidElement } from "react";
import { ChevronDown } from "@untitledui/icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faChevronDown } from "@fortawesome/pro-duotone-svg-icons";
import type { SelectProps as AriaSelectProps } from "react-aria-components";
import { Button as AriaButton, ListBox as AriaListBox, Select as AriaSelect, SelectValue as AriaSelectValue } from "react-aria-components";
import { Avatar } from "@/components/base/avatar/avatar";
@@ -92,9 +93,10 @@ const SelectValue = ({ isOpen, isFocused, isDisabled, size, placeholder, placeho
<p className={cx("text-md text-placeholder", isDisabled && "text-disabled")}>{placeholder}</p>
)}
<ChevronDown
<FontAwesomeIcon
icon={faChevronDown}
aria-hidden="true"
className={cx("ml-auto shrink-0 text-fg-quaternary", size === "sm" ? "size-4 stroke-[2.5px]" : "size-5")}
className={cx("ml-auto shrink-0 text-fg-quaternary", size === "sm" ? "size-4" : "size-5")}
/>
</>
);