Centralize icon system

This commit is contained in:
Sam Becker 2025-03-16 15:44:55 -05:00
parent 4849d591d3
commit 0ca8823dae
44 changed files with 252 additions and 124 deletions

View File

@ -11,19 +11,20 @@ import {
PATH_GRID_INFERRED,
} from '@/app/paths';
import { useAppState } from '@/state/AppState';
import { ImCheckboxUnchecked } from 'react-icons/im';
import { IoArrowDown, IoArrowUp, IoCloseSharp } from 'react-icons/io5';
import { IoArrowDown, IoArrowUp } from 'react-icons/io5';
import { clsx } from 'clsx/lite';
import { TbChecklist, TbPhoto } from 'react-icons/tb';
import { FiTag } from 'react-icons/fi';
import { BiLockAlt } from 'react-icons/bi';
import AdminAppInfoIcon from './AdminAppInfoIcon';
import { PiSignOutBold } from 'react-icons/pi';
import { signOutAction } from '@/auth/actions';
import { ComponentProps } from 'react';
import { FaRegFolderOpen } from 'react-icons/fa';
import { FiUploadCloud } from 'react-icons/fi';
import useIsKeyBeingPressed from '@/utility/useIsKeyBeingPressed';
import IconSelectMultiple from '@/components/icons/IconSelectMultiple';
import IconPhoto from '@/components/icons/IconPhoto';
import IconUpload from '@/components/icons/IconUpload';
import IconRecipe from '@/components/icons/IconRecipe';
import IconTag from '@/components/icons/IconTag';
import IconFolder from '@/components/icons/IconFolder';
import IconSignOut from '@/components/icons/IconSignOut';
import IconLock from '@/components/icons/IconLock';
export default function AdminAppMenu({
active,
@ -54,7 +55,7 @@ export default function AdminAppMenu({
const items: ComponentProps<typeof MoreMenu>['items'] = [{
label: 'Upload Photos',
icon: <FiUploadCloud
icon: <IconUpload
size={15}
className="translate-x-[0.5px] translate-y-[0.5px]"
/>,
@ -73,7 +74,7 @@ export default function AdminAppMenu({
...photosCountTotal && {
annotation: `${photosCountTotal}`,
},
icon: <TbPhoto
icon: <IconPhoto
size={15}
className="translate-x-[-0.5px] translate-y-[0.5px]"
/>,
@ -85,7 +86,7 @@ export default function AdminAppMenu({
items.push({
label: 'Uploads',
annotation: `${uploadsCount}`,
icon: <FaRegFolderOpen
icon: <IconFolder
size={16}
className="translate-y-[0.5px]"
/>,
@ -97,7 +98,7 @@ export default function AdminAppMenu({
items.push({
label: 'Manage Tags',
annotation: `${tagsCount}`,
icon: <FiTag
icon: <IconTag
size={15}
className="translate-y-[0.5px]"
/>,
@ -109,7 +110,7 @@ export default function AdminAppMenu({
items.push({
label: 'Manage Recipes',
annotation: `${recipesCount}`,
icon: <TbChecklist
icon: <IconRecipe
size={17}
className="translate-x-[-0.5px] translate-y-[0.5px]"
/>,
@ -122,13 +123,7 @@ export default function AdminAppMenu({
label: isSelecting
? 'Exit Select'
: 'Edit Multiple',
icon: isSelecting
? <IoCloseSharp
className="text-[18px] translate-x-[-1px] translate-y-[1px]"
/>
: <ImCheckboxUnchecked
className="translate-x-[-0.5px] text-[0.75rem]"
/>,
icon: <IconSelectMultiple {...{ isSelecting }} />,
href: PATH_GRID_INFERRED,
action: () => {
if (isSelecting) {
@ -157,14 +152,17 @@ export default function AdminAppMenu({
: PATH_ADMIN_CONFIGURATION,
}, {
label: 'Sign Out',
icon: <PiSignOutBold size={15} />,
icon: <IconSignOut size={15} />,
action: () => signOutAction().then(clearAuthStateAndRedirect),
});
return (
<MoreMenu
header={<div className="flex items-center select-none">
<BiLockAlt size={17} className="inline-block w-5 mr-2" />
<IconLock
size={15}
className="inline-block w-5 mr-2 translate-x-[1px]"
/>
<span className="grow">Admin menu</span>
</div>}
icon={<div className={clsx(

View File

@ -15,8 +15,10 @@ import { tagMultiplePhotosAction } from '@/photo/actions';
import { toastSuccess } from '@/toast';
import DeletePhotosButton from './DeletePhotosButton';
import { photoQuantityText } from '@/photo';
import { FaArrowDown, FaCheck, FaRegStar } from 'react-icons/fa6';
import { FaArrowDown, FaCheck } from 'react-icons/fa6';
import ResponsiveText from '@/components/primitives/ResponsiveText';
import IconFavs from '@/components/icons/IconFavs';
import IconTag from '@/components/icons/IconTag';
export default function AdminBatchEditPanelClient({
uniqueTags,
@ -113,7 +115,7 @@ export default function AdminBatchEditPanelClient({
onFinish={() => setIsPerformingSelectEdit?.(false)}
/>
<LoaderButton
icon={<FaRegStar />}
icon={<IconFavs />}
disabled={isPerformingSelectEdit}
confirmText={`Are you sure you want to favorite ${photosText}?`}
onClick={() => {
@ -132,10 +134,9 @@ export default function AdminBatchEditPanelClient({
<LoaderButton
onClick={() => setTags('')}
disabled={isPerformingSelectEdit}
icon={<IconTag size={15} className="translate-y-[1.5px]" />}
>
<ResponsiveText shortText="Tag">
Tag ...
</ResponsiveText>
Tag ...
</LoaderButton>
</>}
<LoaderButton

View File

@ -21,8 +21,8 @@ import PhotoTagFieldset from './PhotoTagFieldset';
import DeleteUploadButton from './DeleteUploadButton';
import { useAppState } from '@/state/AppState';
import { pluralize } from '@/utility/string';
import { FaRegStar } from 'react-icons/fa6';
import { AiOutlineEyeInvisible } from 'react-icons/ai';
import IconFavs from '@/components/icons/IconFavs';
import IconHidden from '@/components/icons/IconHidden';
const UPLOAD_BATCH_SIZE = 4;
@ -147,7 +147,7 @@ export default function AdminBatchUploadActions({
<div className="flex gap-8">
<FieldSetWithStatus
label="Favorite"
icon={<FaRegStar size={14} />}
icon={<IconFavs size={14} />}
type="checkbox"
value={favorite}
onChange={setFavorite}
@ -155,7 +155,7 @@ export default function AdminBatchUploadActions({
/>
<FieldSetWithStatus
label="Hidden"
icon={<AiOutlineEyeInvisible size={16} />}
icon={<IconHidden size={16} />}
type="checkbox"
value={hidden}
onChange={setHidden}

View File

@ -3,7 +3,7 @@
import { Photo } from '@/photo';
import AdminPhotosTable from '@/admin/AdminPhotosTable';
import LoaderButton from '@/components/primitives/LoaderButton';
import IconGrSync from '@/app/IconGrSync';
import IconGrSync from '@/components/icons/IconGrSync';
import Note from '@/components/Note';
import AdminChildPage from '@/components/AdminChildPage';
import { PATH_ADMIN_PHOTOS } from '@/app/paths';

View File

@ -7,7 +7,6 @@ import {
syncPhotoAction,
toggleFavoritePhotoAction,
} from '@/photo/actions';
import { FaRegEdit, FaRegStar, FaStar } from 'react-icons/fa';
import {
Photo,
deleteConfirmationTextForPhoto,
@ -21,9 +20,11 @@ import { useAppState } from '@/state/AppState';
import { RevalidatePhoto } from '@/photo/InfinitePhotoScroll';
import { MdOutlineFileDownload } from 'react-icons/md';
import MoreMenuItem from '@/components/more/MoreMenuItem';
import IconGrSync from '@/app/IconGrSync';
import IconGrSync from '@/components/icons/IconGrSync';
import { isPhotoOutdated } from '@/photo/outdated';
import InsightsIndicatorDot from './insights/InsightsIndicatorDot';
import IconFavs from '@/components/icons/IconFavs';
import IconEdit from '@/components/icons/IconEdit';
export default function AdminPhotoMenuClient({
photo,
@ -42,12 +43,10 @@ export default function AdminPhotoMenuClient({
const shouldRedirectFav = isPathFavs(path) && isFav;
const shouldRedirectDelete = pathForPhoto({ photo: photo.id }) === path;
const favIconClass = 'translate-x-[-1px] translate-y-[0.5px]';
const items = useMemo(() => {
const items: ComponentProps<typeof MoreMenuItem>[] = [{
label: 'Edit',
icon: <FaRegEdit
icon: <IconEdit
size={15}
className="translate-x-[0.5px] translate-y-[-0.5px]"
/>,
@ -56,15 +55,11 @@ export default function AdminPhotoMenuClient({
if (includeFavorite) {
items.push({
label: isFav ? 'Unfavorite' : 'Favorite',
icon: isFav
? <FaStar
size={14}
className={`text-amber-500 ${favIconClass}`}
/>
: <FaRegStar
size={14}
className={favIconClass}
/>,
icon: <IconFavs
size={14}
className="translate-x-[-1px]"
highlight={isFav}
/>,
action: () => toggleFavoritePhotoAction(
photo.id,
shouldRedirectFav,

View File

@ -1,5 +1,5 @@
import IconEdit from '@/components/icons/IconEdit';
import PathLoaderButton from '@/components/primitives/PathLoaderButton';
import { FaRegEdit } from 'react-icons/fa';
export default function EditButton ({
path,
@ -9,7 +9,7 @@ export default function EditButton ({
return (
<PathLoaderButton
path={path}
icon={<FaRegEdit size={15} className="translate-y-[0.5px]" />}
icon={<IconEdit size={15} className="translate-y-[0.5px]" />}
>
Edit
</PathLoaderButton>

View File

@ -1,6 +1,6 @@
import LoaderButton from '@/components/primitives/LoaderButton';
import { syncPhotoAction } from '@/photo/actions';
import IconGrSync from '@/app/IconGrSync';
import IconGrSync from '@/components/icons/IconGrSync';
import { toastSuccess } from '@/toast';
import { ComponentProps, useState } from 'react';
import Tooltip from '@/components/Tooltip';

View File

@ -1,23 +1,26 @@
import Switcher from '@/components/Switcher';
import SwitcherItem from '@/components/SwitcherItem';
import IconFeed from '@/app/IconFeed';
import IconGrid from '@/app/IconGrid';
import IconFeed from '@/components/icons/IconFeed';
import IconGrid from '@/components/icons/IconGrid';
import {
PATH_FEED_INFERRED,
PATH_GRID_INFERRED,
} from '@/app/paths';
import IconSearch from './IconSearch';
import IconSearch from '../components/icons/IconSearch';
import { useAppState } from '@/state/AppState';
import { GRID_HOMEPAGE_ENABLED } from './config';
import AdminAppMenu from '@/admin/AdminAppMenu';
import Spinner from '@/components/Spinner';
import clsx from 'clsx/lite';
export type SwitcherSelection = 'feed' | 'grid' | 'admin';
export default function ViewSwitcher({
currentSelection,
className,
}: {
currentSelection?: SwitcherSelection
className?: string
}) {
const {
isUserSignedIn,
@ -42,7 +45,10 @@ export default function ViewSwitcher({
/>;
return (
<div className="flex gap-1 sm:gap-2">
<div className={clsx(
'flex gap-1 sm:gap-2',
className,
)}>
<Switcher>
{GRID_HOMEPAGE_ENABLED ? renderItemGrid : renderItemFeed}
{GRID_HOMEPAGE_ENABLED ? renderItemFeed : renderItemGrid}

View File

@ -19,8 +19,8 @@ import {
import { useSearchParams } from 'next/navigation';
import { useAppState } from '@/state/AppState';
import { clsx } from 'clsx/lite';
import { FiLock } from 'react-icons/fi';
import { PATH_ADMIN_PHOTOS } from '@/app/paths';
import IconLock from '@/components/icons/IconLock';
export default function SignInForm({
includeTitle = true,
@ -71,7 +71,7 @@ export default function SignInForm({
'flex gap-3 items-center justify-center',
'self-start text-2xl mb-3.5',
)}>
<FiLock className="text-main translate-y-[0.5px]" />
<IconLock className="text-main translate-y-[0.5px]" />
<span className="text-main">
Sign in
</span>

View File

@ -4,7 +4,7 @@ import { Camera, formatCameraText, isCameraApple } from '.';
import EntityLink, {
EntityLinkExternalProps,
} from '@/components/primitives/EntityLink';
import { TbCamera } from 'react-icons/tb';
import IconCamera from '@/components/icons/IconCamera';
export default function PhotoCamera({
camera,
@ -33,7 +33,7 @@ export default function PhotoCamera({
className="translate-x-[-1px] translate-y-[-0.5px]"
size={15}
/>
: <TbCamera
: <IconCamera
size={15}
className="translate-x-[-0.5px]"
/>}

View File

@ -0,0 +1,6 @@
import { IconBaseProps } from 'react-icons';
import { TbCamera } from 'react-icons/tb';
export default function IconCamera(props: IconBaseProps) {
return <TbCamera {...props} />;
}

View File

@ -0,0 +1,6 @@
import { IconBaseProps } from 'react-icons';
import { FaRegEdit } from 'react-icons/fa';
export default function IconEdit(props: IconBaseProps) {
return <FaRegEdit {...props} />;
}

View File

@ -0,0 +1,16 @@
import { clsx } from 'clsx/lite';
import { IconBaseProps } from 'react-icons';
import { FaRegStar, FaStar } from 'react-icons/fa6';
export default function IconFavs({
highlight,
className,
...props
}: IconBaseProps & { highlight?: boolean}) {
return highlight
? <FaStar
{...props}
className={clsx('text-amber-500', className)}
/>
: <FaRegStar {...{ ...props, className }} />;
}

View File

@ -0,0 +1,6 @@
import { IconBaseProps } from 'react-icons';
import { IoFilmOutline } from 'react-icons/io5';
export default function IconFilmSimulation(props: IconBaseProps) {
return <IoFilmOutline {...props} />;
}

View File

@ -0,0 +1,17 @@
import { IconBaseProps } from 'react-icons';
import { TbCone } from 'react-icons/tb';
export default function IconFocalLength({
style,
...props
}: IconBaseProps) {
return <TbCone {...{
...props,
style: {
...style,
transform: `rotate(270deg)${style?.transform
? ` ${style.transform}`
: ''}`,
},
}} />;
}

View File

@ -0,0 +1,6 @@
import { IconBaseProps } from 'react-icons';
import { FaRegFolderOpen } from 'react-icons/fa';
export default function IconFolder(props: IconBaseProps) {
return <FaRegFolderOpen {...props} />;
}

View File

@ -0,0 +1,6 @@
import { IconBaseProps } from 'react-icons';
import { AiOutlineEyeInvisible } from 'react-icons/ai';
export default function IconHidden(props: IconBaseProps) {
return <AiOutlineEyeInvisible {...props} />;
}

View File

@ -0,0 +1,6 @@
import { IconBaseProps } from 'react-icons';
import { RiCameraLensLine } from 'react-icons/ri';
export default function IconLens(props: IconBaseProps) {
return <RiCameraLensLine {...props} />;
}

View File

@ -0,0 +1,6 @@
import { IconBaseProps } from 'react-icons';
import { FiLock } from 'react-icons/fi';
export default function IconLock(props: IconBaseProps) {
return <FiLock {...props} />;
}

View File

@ -0,0 +1,6 @@
import { IconBaseProps } from 'react-icons';
import { TbPhoto } from 'react-icons/tb';
export default function IconPhoto(props: IconBaseProps) {
return <TbPhoto {...props} />;
}

View File

@ -0,0 +1,6 @@
import { IconBaseProps } from 'react-icons';
import { TbChecklist } from 'react-icons/tb';
export default function IconRecipe(props: IconBaseProps) {
return <TbChecklist {...props} />;
}

View File

@ -0,0 +1,28 @@
import clsx from 'clsx/lite';
import { IconBaseProps } from 'react-icons';
import { ImCheckboxUnchecked } from 'react-icons/im';
import { IoCloseSharp } from 'react-icons/io5';
export default function IconSelectMultiple({
isSelecting,
className,
...props
}: IconBaseProps & { isSelecting: boolean }) {
return isSelecting
? <IoCloseSharp {...{
...props,
className: clsx(
'text-[18px] translate-x-[-1px] translate-y-[0.5px]',
className,
),
}} />
: <ImCheckboxUnchecked
{...{
...props,
className: clsx(
'translate-x-[-0.5px] translate-y-[0.5px] text-[0.75rem]',
className,
),
}}
/>;
}

View File

@ -0,0 +1,6 @@
import { IconBaseProps } from 'react-icons';
import { PiSignOutBold } from 'react-icons/pi';
export default function IconSignOut(props: IconBaseProps) {
return <PiSignOutBold {...props} />;
}

View File

@ -0,0 +1,6 @@
import { IconBaseProps } from 'react-icons';
import { FiTag } from 'react-icons/fi';
export default function IconTag(props: IconBaseProps) {
return <FiTag {...props} />;
}

View File

@ -0,0 +1,6 @@
import { IconBaseProps } from 'react-icons';
import { FiUploadCloud } from 'react-icons/fi';
export default function IconUpload(props: IconBaseProps) {
return <FiUploadCloud {...props} />;
}

View File

@ -2,8 +2,8 @@ import { pathForFocalLength } from '@/app/paths';
import EntityLink, {
EntityLinkExternalProps,
} from '@/components/primitives/EntityLink';
import { TbCone } from 'react-icons/tb';
import { formatFocalLength } from '.';
import IconFocalLength from '@/components/icons/IconFocalLength';
export default function PhotoFocalLength({
focal,
@ -21,7 +21,7 @@ export default function PhotoFocalLength({
<EntityLink
label={formatFocalLength(focal)}
href={pathForFocalLength(focal)}
icon={<TbCone className="rotate-[270deg]" />}
icon={<IconFocalLength />}
type={type}
className={className}
badged={badged}

View File

@ -8,9 +8,9 @@ import {
formatCameraText,
isCameraApple,
} from '@/camera';
import { IoMdCamera } from 'react-icons/io';
import { NextImageSize } from '@/platforms/next-image';
import { AiFillApple } from 'react-icons/ai';
import IconCamera from '@/components/icons/IconCamera';
export default function CameraImageResponse({
camera: cameraProp,
@ -47,10 +47,11 @@ export default function CameraImageResponse({
transform: `translateY(${-height * .002}px)`,
}}
/>
: <IoMdCamera
size={height * .079}
: <IconCamera
size={height * .09}
style={{
marginRight: height * .015,
transform: `translateY(${height * .001}px)`,
}}
/>,
title: formatCameraText(camera).toLocaleUpperCase(),

View File

@ -3,8 +3,8 @@ import ImageCaption from './components/ImageCaption';
import ImagePhotoGrid from './components/ImagePhotoGrid';
import ImageContainer from './components/ImageContainer';
import type { NextImageSize } from '@/platforms/next-image';
import { TbCone } from 'react-icons/tb';
import { formatFocalLength } from '@/focal';
import IconFocalLength from '@/components/icons/IconFocalLength';
export default function FocalLengthImageResponse({
focal,
@ -32,13 +32,13 @@ export default function FocalLengthImageResponse({
width,
height,
fontFamily,
icon: <TbCone
size={height * .075}
style={{
transform: `translateY(${height * .002}px) rotate(270deg)`,
marginRight: height * .01,
}}
/>,
icon: <span style={{
display: 'flex',
transform: `translateY(${height * .002}px)`,
marginRight: height * .01,
}}>
<IconFocalLength size={height * .075} />
</span>,
title: formatFocalLength(focal),
}} />
</ImageContainer>

View File

@ -4,7 +4,7 @@ import ImagePhotoGrid from './components/ImagePhotoGrid';
import ImageContainer from './components/ImageContainer';
import { NextImageSize } from '@/platforms/next-image';
import { formatLensText, Lens, lensFromPhoto } from '@/lens';
import { RiCameraLensLine } from 'react-icons/ri';
import IconLens from '@/components/icons/IconLens';
export default function LensImageResponse({
lens: lensProp,
@ -33,7 +33,7 @@ export default function LensImageResponse({
width,
height,
fontFamily,
icon: <RiCameraLensLine
icon: <IconLens
size={height * .079}
style={{
marginRight: height * .015,

View File

@ -4,10 +4,10 @@ import ImagePhotoGrid from './components/ImagePhotoGrid';
import ImageContainer from './components/ImageContainer';
import type { NextImageSize } from '@/platforms/next-image';
import { formatTag } from '@/tag';
import { TbChecklist } from 'react-icons/tb';
import { generateRecipeText, getPhotoWithRecipeFromPhotos } from '@/recipe';
import PhotoFilmSimulationIcon from '@/simulation/PhotoFilmSimulationIcon';
import { isStringFilmSimulation } from '@/platforms/fujifilm/simulation';
import IconRecipe from '@/components/icons/IconRecipe';
const MAX_RECIPE_LINES = 8;
export default function RecipeImageResponse({
@ -49,7 +49,6 @@ export default function RecipeImageResponse({
photos,
width,
height,
gap: 0,
}}
/>
<div
@ -65,10 +64,10 @@ export default function RecipeImageResponse({
fontFamily,
legacyBottomAlignment: false,
gap: '0',
icon: <TbChecklist
icon: <IconRecipe
size={height * .087}
style={{
transform: `translateY(${height * .003}px)`,
transform: `translateY(${height * .002}px)`,
marginRight: height * .02,
}}
/>,

View File

@ -1,10 +1,11 @@
import type { Photo } from '../photo';
import { FaStar, FaTag } from 'react-icons/fa';
import ImageCaption from './components/ImageCaption';
import ImagePhotoGrid from './components/ImagePhotoGrid';
import ImageContainer from './components/ImageContainer';
import type { NextImageSize } from '@/platforms/next-image';
import { formatTag, isTagFavs } from '@/tag';
import IconTag from '@/components/icons/IconTag';
import IconFavs from '@/components/icons/IconFavs';
export default function TagImageResponse({
tag,
@ -33,7 +34,7 @@ export default function TagImageResponse({
height,
fontFamily,
icon: isTagFavs(tag)
? <FaStar
? <IconFavs
size={height * .066}
style={{
// Fix horizontal distortion in icon size
@ -41,11 +42,11 @@ export default function TagImageResponse({
marginRight: height * .015,
}}
/>
: <FaTag
size={height * .06}
: <IconTag
size={height * .0725}
style={{
transform: `translateY(${height * .006}px)`,
marginRight: height * .02,
transform: `translateY(${height * .007}px)`,
marginRight: height * .01,
}}
/>,
title: formatTag(tag).toLocaleUpperCase(),

View File

@ -1,6 +1,6 @@
import { Photo } from '../photo';
import IconFeed from '@/app/IconFeed';
import IconGrid from '@/app/IconGrid';
import IconFeed from '@/components/icons/IconFeed';
import IconGrid from '@/components/icons/IconGrid';
import ImagePhotoGrid from './components/ImagePhotoGrid';
import { NextImageSize } from '@/platforms/next-image';

View File

@ -13,7 +13,7 @@ export default function ImagePhotoGrid({
widthArbitrary,
height,
imagePosition = 'center',
gap = 4,
gap = 0,
imageStyle,
}: ({
photos: Photo[]

View File

@ -3,7 +3,7 @@ import { Lens, formatLensText } from '.';
import EntityLink, {
EntityLinkExternalProps,
} from '@/components/primitives/EntityLink';
import { RiCameraLensLine } from 'react-icons/ri';
import IconLens from '@/components/icons/IconLens';
export default function PhotoLens({
lens,
@ -21,7 +21,7 @@ export default function PhotoLens({
<EntityLink
label={formatLensText(lens, 'short')}
href={pathForLens(lens)}
icon={<RiCameraLensLine
icon={<IconLens
size={14}
className="translate-x-[-0.5px] translate-y-[-0.5px]"
/>}

View File

@ -4,12 +4,9 @@ import { Cameras, sortCamerasWithCount } from '@/camera';
import PhotoCamera from '@/camera/PhotoCamera';
import HeaderList from '@/components/HeaderList';
import PhotoTag from '@/tag/PhotoTag';
import { FaTag } from 'react-icons/fa';
import { IoMdCamera } from 'react-icons/io';
import { PhotoDateRange, dateRangeForPhotos, photoQuantityText } from '.';
import { TAG_FAVS, TAG_HIDDEN, Tags, addHiddenToTags } from '@/tag';
import PhotoFilmSimulation from '@/simulation/PhotoFilmSimulation';
import PhotoFilmSimulationIcon from '@/simulation/PhotoFilmSimulationIcon';
import { FilmSimulations, sortFilmSimulationsWithCount } from '@/simulation';
import FavsTag from '../tag/FavsTag';
import { useAppState } from '@/state/AppState';
@ -23,7 +20,10 @@ import {
import { clsx } from 'clsx/lite';
import { Recipes, sortRecipesWithCount } from '@/recipe';
import PhotoRecipe from '@/recipe/PhotoRecipe';
import { TbChecklist } from 'react-icons/tb';
import IconCamera from '@/components/icons/IconCamera';
import IconRecipe from '@/components/icons/IconRecipe';
import IconTag from '@/components/icons/IconTag';
import IconFilmSimulation from '@/components/icons/IconFilmSimulation';
export default function PhotoGridSidebar({
tags,
@ -52,8 +52,8 @@ export default function PhotoGridSidebar({
? <HeaderList
key="tags"
title='Tags'
icon={<FaTag
size={12}
icon={<IconTag
size={14}
className="translate-y-[1px]"
/>}
items={tagsIncludingHidden.map(({ tag, count }) => {
@ -95,10 +95,7 @@ export default function PhotoGridSidebar({
? <HeaderList
key="cameras"
title="Cameras"
icon={<IoMdCamera
size={13}
className="translate-y-[-0.25px]"
/>}
icon={<IconCamera size={15} />}
items={cameras
.sort(sortCamerasWithCount)
.map(({ cameraKey, camera, count }) =>
@ -119,7 +116,7 @@ export default function PhotoGridSidebar({
? <HeaderList
key="recipes"
title="Recipes"
icon={<TbChecklist
icon={<IconRecipe
size={16}
className="translate-x-[-1px]"
/>}
@ -141,9 +138,7 @@ export default function PhotoGridSidebar({
? <HeaderList
key="films"
title="Films"
icon={<PhotoFilmSimulationIcon
className="translate-y-[0.5px]"
/>}
icon={<IconFilmSimulation size={15} />}
items={simulations
.sort(sortFilmSimulationsWithCount)
.map(({ simulation, count }) =>

View File

@ -284,11 +284,12 @@ export default function PhotoLarge({
<div>
{(showCameraContent || showLensContent) &&
<div className="flex flex-col">
<PhotoCamera
camera={camera}
contrast="medium"
prefetch={prefetchRelatedLinks}
/>
{showCameraContent &&
<PhotoCamera
camera={camera}
contrast="medium"
prefetch={prefetchRelatedLinks}
/>}
{showLensContent &&
<PhotoLens
lens={lens}

View File

@ -42,17 +42,17 @@ import { convertRecipesForForm, Recipes } from '@/recipe';
import deepEqual from 'fast-deep-equal/es6/react';
import ApplyRecipeTitleGloballyCheckbox from './ApplyRecipesGloballyCheckbox';
import { FilmSimulation } from '@/simulation';
import { FaRegStar } from 'react-icons/fa6';
import { AiOutlineEyeInvisible } from 'react-icons/ai';
import IconFavs from '@/components/icons/IconFavs';
import IconHidden from '@/components/icons/IconHidden';
const THUMBNAIL_SIZE = 300;
const iconForKey = (key: keyof PhotoFormData) => {
switch (key) {
case 'favorite':
return <FaRegStar size={14} />;
return <IconFavs size={14} />;
case 'hidden':
return <AiOutlineEyeInvisible size={16} />;
return <IconHidden size={16} />;
}
};

View File

@ -2,10 +2,10 @@ import { pathForRecipe } from '@/app/paths';
import EntityLink, {
EntityLinkExternalProps,
} from '@/components/primitives/EntityLink';
import { TbChecklist } from 'react-icons/tb';
import { formatRecipe } from '.';
import clsx from 'clsx/lite';
import { RefObject } from 'react';
import IconRecipe from '@/components/icons/IconRecipe';
export default function PhotoRecipe({
recipe,
@ -31,7 +31,7 @@ export default function PhotoRecipe({
title="Recipe"
label={formatRecipe(recipe)}
href={pathForRecipe(recipe)}
icon={<TbChecklist
icon={<IconRecipe
size={16}
className={clsx(
badged

View File

@ -1,10 +1,9 @@
import { FaStar } from 'react-icons/fa';
import { TAG_FAVS } from '.';
import { pathForTag } from '@/app/paths';
import { clsx } from 'clsx/lite';
import EntityLink, {
EntityLinkExternalProps,
} from '@/components/primitives/EntityLink';
import IconFavs from '@/components/icons/IconFavs';
export default function FavsTag({
type,
@ -21,20 +20,19 @@ export default function FavsTag({
label={badged
? <span className="inline-flex gap-1 items-center">
{TAG_FAVS}
<FaStar
<IconFavs
size={10}
className="text-amber-500 translate-y-[-0.5px]"
className="translate-y-[-0.5px]"
highlight
/>
</span>
: TAG_FAVS}
href={pathForTag(TAG_FAVS)}
icon={!badged &&
<FaStar
size={12}
className={clsx(
'text-amber-500',
'translate-x-[-1px]',
)}
<IconFavs
size={13}
className="translate-x-[-0.5px] translate-y-[-0.5px]"
highlight
/>}
type={type}
className={className}

View File

@ -3,7 +3,7 @@ import { formatTag } from '.';
import EntityLink, {
EntityLinkExternalProps,
} from '@/components/primitives/EntityLink';
import { FiTag } from 'react-icons/fi';
import IconTag from '@/components/icons/IconTag';
export default function PhotoTag({
tag,
@ -21,7 +21,7 @@ export default function PhotoTag({
<EntityLink
label={formatTag(tag)}
href={pathForTag(tag)}
icon={<FiTag size={14} className="translate-x-[0.5px]" />}
icon={<IconTag size={14} className="translate-x-[0.5px]" />}
type={type}
className={className}
badged={badged}