Refactor photo/menu form components
This commit is contained in:
parent
741bcf32f7
commit
dddf1f39c5
@ -1,26 +1,60 @@
|
||||
import AdminAboutEditPage from '@/about/AdminAboutEditPage';
|
||||
import { getAbout } from '@/about/query';
|
||||
import { PRESERVE_ORIGINAL_UPLOADS } from '@/app/config';
|
||||
import { getPhotoNoStore } from '@/photo/cache';
|
||||
import { feedQueryOptions } from '@/feed';
|
||||
import {
|
||||
getPhotosCached,
|
||||
getPhotosMetaCached,
|
||||
} from '@/photo/cache';
|
||||
import { getPhoto } from '@/photo/query';
|
||||
|
||||
const PHOTO_CHOOSER_QUERY_OPTIONS = feedQueryOptions({
|
||||
isGrid: true,
|
||||
excludeFromFeeds: false,
|
||||
});
|
||||
|
||||
export default async function AboutEditPage() {
|
||||
const about = await getAbout().catch(() => undefined);
|
||||
const [
|
||||
{
|
||||
about,
|
||||
photoAvatar,
|
||||
photoHero,
|
||||
},
|
||||
photos,
|
||||
photosCount,
|
||||
photosHidden,
|
||||
] = await Promise.all([
|
||||
getAbout().then(async about => {
|
||||
const photoAvatar = about?.photoIdAvatar
|
||||
? await getPhoto(about?.photoIdAvatar ?? '', true)
|
||||
.catch(() => undefined)
|
||||
: undefined;
|
||||
|
||||
const photoAvatar = about?.photoIdAvatar
|
||||
? await getPhotoNoStore(about?.photoIdAvatar ?? '', true)
|
||||
.catch(() => undefined)
|
||||
: undefined;
|
||||
const photoHero = about?.photoIdHero
|
||||
? await getPhoto(about?.photoIdHero ?? '', true)
|
||||
.catch(() => undefined)
|
||||
: undefined;
|
||||
|
||||
const photoHero = about?.photoIdHero
|
||||
? await getPhotoNoStore(about?.photoIdHero ?? '', true)
|
||||
.catch(() => undefined)
|
||||
: undefined;
|
||||
return {
|
||||
about,
|
||||
photoAvatar,
|
||||
photoHero,
|
||||
};
|
||||
}),
|
||||
getPhotosCached(PHOTO_CHOOSER_QUERY_OPTIONS),
|
||||
getPhotosMetaCached(PHOTO_CHOOSER_QUERY_OPTIONS)
|
||||
.then(({ count }) => count),
|
||||
getPhotosCached({ hidden: 'only', limit: 1000 }),
|
||||
]);
|
||||
|
||||
return (
|
||||
<AdminAboutEditPage {...{
|
||||
about,
|
||||
photoAvatar,
|
||||
photoHero,
|
||||
photos,
|
||||
photosCount,
|
||||
photosHidden,
|
||||
shouldResizeImages: !PRESERVE_ORIGINAL_UPLOADS,
|
||||
}} />
|
||||
);
|
||||
|
||||
@ -8,12 +8,12 @@ import { getPhotosMetaCached } from '@/photo/cache';
|
||||
import { SortProps } from '@/photo/sort';
|
||||
import { getSortOptionsFromParams } from '@/photo/sort/path';
|
||||
import { PhotoQueryOptions } from '@/db';
|
||||
import { FEED_META_QUERY_OPTIONS, getFeedQueryOptions } from '@/feed';
|
||||
import { FEED_META_QUERY_OPTIONS, feedQueryOptions } from '@/feed';
|
||||
|
||||
export const maxDuration = 60;
|
||||
|
||||
const getPhotosCached = cache((options: PhotoQueryOptions) =>
|
||||
getPhotos(getFeedQueryOptions({
|
||||
getPhotos(feedQueryOptions({
|
||||
isGrid: false,
|
||||
...options,
|
||||
})));
|
||||
|
||||
@ -6,12 +6,12 @@ import { getPhotos } from '@/photo/query';
|
||||
import PhotoFullPage from '@/photo/PhotoFullPage';
|
||||
import { getPhotosMetaCached } from '@/photo/cache';
|
||||
import { USER_DEFAULT_SORT_OPTIONS } from '@/app/config';
|
||||
import { FEED_META_QUERY_OPTIONS, getFeedQueryOptions } from '@/feed';
|
||||
import { FEED_META_QUERY_OPTIONS, feedQueryOptions } from '@/feed';
|
||||
|
||||
export const dynamic = 'force-static';
|
||||
export const maxDuration = 60;
|
||||
|
||||
const getPhotosCached = cache(() => getPhotos(getFeedQueryOptions({
|
||||
const getPhotosCached = cache(() => getPhotos(feedQueryOptions({
|
||||
isGrid: false,
|
||||
})));
|
||||
|
||||
|
||||
@ -8,13 +8,13 @@ import { getDataForCategoriesCached } from '@/category/cache';
|
||||
import { getPhotosMetaCached } from '@/photo/cache';
|
||||
import { SortProps } from '@/photo/sort';
|
||||
import { getSortOptionsFromParams } from '@/photo/sort/path';
|
||||
import { FEED_META_QUERY_OPTIONS, getFeedQueryOptions } from '@/feed';
|
||||
import { FEED_META_QUERY_OPTIONS, feedQueryOptions } from '@/feed';
|
||||
import { PhotoQueryOptions } from '@/db';
|
||||
|
||||
export const maxDuration = 60;
|
||||
|
||||
const getPhotosCached = cache((options: PhotoQueryOptions) =>
|
||||
getPhotos(getFeedQueryOptions({
|
||||
getPhotos(feedQueryOptions({
|
||||
isGrid: true,
|
||||
...options,
|
||||
})));
|
||||
|
||||
@ -7,12 +7,12 @@ import PhotoGridPage from '@/photo/PhotoGridPage';
|
||||
import { getDataForCategoriesCached } from '@/category/cache';
|
||||
import { getPhotosMetaCached } from '@/photo/cache';
|
||||
import { USER_DEFAULT_SORT_OPTIONS } from '@/app/config';
|
||||
import { FEED_META_QUERY_OPTIONS, getFeedQueryOptions } from '@/feed';
|
||||
import { FEED_META_QUERY_OPTIONS, feedQueryOptions } from '@/feed';
|
||||
|
||||
export const dynamic = 'force-static';
|
||||
export const maxDuration = 60;
|
||||
|
||||
const getPhotosCached = cache(() => getPhotos(getFeedQueryOptions({
|
||||
const getPhotosCached = cache(() => getPhotos(feedQueryOptions({
|
||||
isGrid: true,
|
||||
})));
|
||||
|
||||
|
||||
@ -9,12 +9,12 @@ import PhotoFullPage from '@/photo/PhotoFullPage';
|
||||
import PhotoGridPage from '@/photo/PhotoGridPage';
|
||||
import { getDataForCategoriesCached } from '@/category/cache';
|
||||
import { getPhotosMetaCached } from '@/photo/cache';
|
||||
import { FEED_META_QUERY_OPTIONS, getFeedQueryOptions } from '@/feed';
|
||||
import { FEED_META_QUERY_OPTIONS, feedQueryOptions } from '@/feed';
|
||||
|
||||
export const dynamic = 'force-static';
|
||||
export const maxDuration = 60;
|
||||
|
||||
const getPhotosCached = cache(() => getPhotos(getFeedQueryOptions({
|
||||
const getPhotosCached = cache(() => getPhotos(feedQueryOptions({
|
||||
isGrid: GRID_HOMEPAGE_ENABLED,
|
||||
})));
|
||||
|
||||
|
||||
@ -14,6 +14,7 @@ import PhotoMedium from '@/photo/PhotoMedium';
|
||||
import clsx from 'clsx/lite';
|
||||
import useDynamicPhoto from '@/photo/useDynamicPhoto';
|
||||
import { useAppText } from '@/i18n/state/client';
|
||||
import FieldsetPhotoChooser from '@/photo/form/FieldsetPhotoChooser';
|
||||
|
||||
export default function AdminAboutEditPage({
|
||||
about,
|
||||
@ -23,6 +24,9 @@ export default function AdminAboutEditPage({
|
||||
about?: About
|
||||
photoAvatar?: Photo
|
||||
photoHero?: Photo
|
||||
photos?: Photo[]
|
||||
photosCount?: number
|
||||
photosHidden?: Photo[]
|
||||
shouldResizeImages?: boolean
|
||||
}) {
|
||||
const appText = useAppText();
|
||||
@ -58,6 +62,13 @@ export default function AdminAboutEditPage({
|
||||
action={updateAboutAction}
|
||||
>
|
||||
<div className="space-y-4">
|
||||
<FieldsetPhotoChooser
|
||||
label="Avatar Photo"
|
||||
value={aboutForm?.photoIdAvatar ?? ''}
|
||||
onChange={photoIdAvatar => setAboutForm(form =>
|
||||
({ ...form, photoIdAvatar: convertUrlToPhotoId(photoIdAvatar) }))}
|
||||
photo={photoAvatar}
|
||||
/>
|
||||
<PhotoAvatar photo={photoAvatar} />
|
||||
<FieldsetWithStatus
|
||||
id="photoIdAvatar"
|
||||
|
||||
@ -10,8 +10,8 @@ import StatusIcon from '@/components/StatusIcon';
|
||||
import clsx from 'clsx/lite';
|
||||
import { useState } from 'react';
|
||||
import { Photo } from '@/photo';
|
||||
import FieldsetPhotoQuery from '@/photo/FieldsetPhotoQuery';
|
||||
import FieldsetPhotoChooser from '@/photo/FieldsetPhotoChooser';
|
||||
import FieldsetPhotoQuery from '@/photo/form/FieldsetPhotoQuery';
|
||||
import FieldsetPhotoChooser from '@/photo/form/FieldsetPhotoChooser';
|
||||
|
||||
export default function ComponentsPageClient({
|
||||
photo,
|
||||
|
||||
@ -11,21 +11,7 @@ import { FiMoreHorizontal } from 'react-icons/fi';
|
||||
import MoreMenuItem from './MoreMenuItem';
|
||||
import { clearGlobalFocus } from '@/utility/dom';
|
||||
import { FaChevronRight } from 'react-icons/fa6';
|
||||
|
||||
const surfaceStyles = (className?: string) => clsx(
|
||||
'z-10',
|
||||
'min-w-[8rem]',
|
||||
'component-surface',
|
||||
'py-1',
|
||||
'not-dark:shadow-lg not-dark:shadow-gray-900/10',
|
||||
'data-[side=top]:dark:shadow-[0_0px_40px_rgba(0,0,0,0.6)]',
|
||||
'data-[side=bottom]:dark:shadow-[0_10px_40px_rgba(0,0,0,0.6)]',
|
||||
'data-[side=right]:dark:shadow-[0_10px_40px_rgba(0,0,0,0.6)]',
|
||||
'data-[side=top]:animate-fade-in-from-bottom',
|
||||
'data-[side=bottom]:animate-fade-in-from-top',
|
||||
'data-[side=right]:animate-fade-in-from-top',
|
||||
className,
|
||||
);
|
||||
import { menuSurfaceStyles } from '../primitives/surface';
|
||||
|
||||
export type MoreMenuSection = {
|
||||
label?: string
|
||||
@ -115,7 +101,7 @@ export default function MoreMenu({
|
||||
onCloseAutoFocus={e => e.preventDefault()}
|
||||
align={align}
|
||||
sideOffset={sideOffset}
|
||||
className={surfaceStyles(className)}
|
||||
className={menuSurfaceStyles(className)}
|
||||
>
|
||||
{header && <div className={clsx(
|
||||
'px-3 pt-3 pb-2 text-dim uppercase',
|
||||
@ -171,7 +157,7 @@ export default function MoreMenu({
|
||||
</DropdownMenu.SubTrigger>
|
||||
<DropdownMenu.Portal>
|
||||
<DropdownMenu.SubContent
|
||||
className={surfaceStyles()}
|
||||
className={menuSurfaceStyles()}
|
||||
>
|
||||
{item.items.map(item =>
|
||||
<div key={item.label} className="px-1">
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
import { ReactNode, useRef, useState, ComponentProps } from 'react';
|
||||
import * as Tooltip from '@radix-ui/react-tooltip';
|
||||
import MenuSurface from './MenuSurface';
|
||||
import ComponentSurface from './surface/ComponentSurface';
|
||||
import clsx from 'clsx/lite';
|
||||
import useClickInsideOutside from '@/utility/useClickInsideOutside';
|
||||
import KeyCommand from './KeyCommand';
|
||||
@ -31,7 +31,7 @@ export default function TooltipPrimitive({
|
||||
children: ReactNode
|
||||
className?: string
|
||||
classNameTrigger?: string
|
||||
color?: ComponentProps<typeof MenuSurface>['color']
|
||||
color?: ComponentProps<typeof ComponentSurface>['color']
|
||||
keyCommand?: string
|
||||
keyCommandModifier?: ComponentProps<typeof KeyCommand>['modifier']
|
||||
supportMobile?: boolean
|
||||
@ -126,9 +126,9 @@ export default function TooltipPrimitive({
|
||||
)}
|
||||
>
|
||||
{content &&
|
||||
<MenuSurface {...{ color, className }}>
|
||||
<ComponentSurface {...{ color, className }}>
|
||||
{content}
|
||||
</MenuSurface>}
|
||||
</ComponentSurface>}
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Portal>
|
||||
</Tooltip.Root>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { ReactNode, RefObject } from 'react';
|
||||
import clsx from 'clsx/lite';
|
||||
|
||||
export default function MenuSurface({
|
||||
export default function ComponentSurface({
|
||||
ref,
|
||||
children,
|
||||
className,
|
||||
16
src/components/primitives/surface/index.ts
Normal file
16
src/components/primitives/surface/index.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import clsx from 'clsx/lite';
|
||||
|
||||
export const menuSurfaceStyles = (className?: string) => clsx(
|
||||
'z-10',
|
||||
'min-w-[8rem]',
|
||||
'component-surface',
|
||||
'py-1',
|
||||
'not-dark:shadow-lg not-dark:shadow-gray-900/10',
|
||||
'data-[side=top]:dark:shadow-[0_0px_40px_rgba(0,0,0,0.6)]',
|
||||
'data-[side=bottom]:dark:shadow-[0_10px_40px_rgba(0,0,0,0.6)]',
|
||||
'data-[side=right]:dark:shadow-[0_10px_40px_rgba(0,0,0,0.6)]',
|
||||
'data-[side=top]:animate-fade-in-from-bottom',
|
||||
'data-[side=bottom]:animate-fade-in-from-top',
|
||||
'data-[side=right]:animate-fade-in-from-top',
|
||||
className,
|
||||
);
|
||||
@ -10,7 +10,7 @@ import {
|
||||
} from 'react';
|
||||
import { SharedHoverContext, SharedHoverProps } from './state';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import MenuSurface from '../primitives/MenuSurface';
|
||||
import ComponentSurface from '../primitives/surface/ComponentSurface';
|
||||
import clsx from 'clsx/lite';
|
||||
|
||||
const WINDOW_CHANGE_EVENTS = ['mouseup', 'mousewheel', 'resize'];
|
||||
@ -133,7 +133,7 @@ export default function SharedHoverProvider({
|
||||
className="fixed"
|
||||
style={hoverStyle}
|
||||
>
|
||||
<MenuSurface
|
||||
<ComponentSurface
|
||||
className="max-w-none p-1!"
|
||||
color={hoverProps.color}
|
||||
>
|
||||
@ -158,7 +158,7 @@ export default function SharedHoverProvider({
|
||||
: 'border-medium',
|
||||
)} />
|
||||
</div>
|
||||
</MenuSurface>
|
||||
</ComponentSurface>
|
||||
</motion.div>}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
|
||||
@ -6,7 +6,7 @@ import {
|
||||
SetStateAction,
|
||||
use,
|
||||
} from 'react';
|
||||
import MenuSurface from '../primitives/MenuSurface';
|
||||
import ComponentSurface from '../primitives/surface/ComponentSurface';
|
||||
|
||||
export type SharedHoverProps = {
|
||||
key: string
|
||||
@ -14,7 +14,7 @@ export type SharedHoverProps = {
|
||||
height: number
|
||||
offsetAbove: number
|
||||
offsetBelow: number
|
||||
color?: ComponentProps<typeof MenuSurface>['color']
|
||||
color?: ComponentProps<typeof ComponentSurface>['color']
|
||||
}
|
||||
|
||||
export type SharedHoverState = {
|
||||
|
||||
@ -13,21 +13,23 @@ const FEED_BASE_QUERY_OPTIONS: PhotoQueryOptions = {
|
||||
|
||||
// PAGE FEED QUERY OPTIONS
|
||||
|
||||
export const getFeedQueryOptions = ({
|
||||
export const feedQueryOptions = ({
|
||||
isGrid,
|
||||
sortBy = USER_DEFAULT_SORT_OPTIONS.sortBy,
|
||||
sortWithPriority = USER_DEFAULT_SORT_OPTIONS.sortWithPriority,
|
||||
...options
|
||||
}: {
|
||||
isGrid: boolean,
|
||||
sortBy?: SortBy,
|
||||
sortWithPriority?: boolean,
|
||||
}): PhotoQueryOptions => ({
|
||||
} & PhotoQueryOptions): PhotoQueryOptions => ({
|
||||
...FEED_BASE_QUERY_OPTIONS,
|
||||
sortBy,
|
||||
sortWithPriority,
|
||||
limit: isGrid
|
||||
? INFINITE_SCROLL_GRID_INITIAL
|
||||
: INFINITE_SCROLL_FULL_INITIAL,
|
||||
...options,
|
||||
});
|
||||
|
||||
export const FEED_META_QUERY_OPTIONS: PhotoQueryOptions = {
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import FieldsetWithStatus from '@/components/FieldsetWithStatus';
|
||||
import { altTextForPhoto, doesPhotoNeedBlurCompatibility, Photo } from '.';
|
||||
import { altTextForPhoto, doesPhotoNeedBlurCompatibility, Photo } from '..';
|
||||
import clsx from 'clsx/lite';
|
||||
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
|
||||
import ImageMedium from '@/components/image/ImageMedium';
|
||||
import PhotoGridInfinite from './PhotoGridInfinite';
|
||||
import PhotoGridInfinite from '../PhotoGridInfinite';
|
||||
import { menuSurfaceStyles } from '@/components/primitives/surface';
|
||||
|
||||
export default function FieldsetPhotoChooser({
|
||||
label,
|
||||
@ -24,7 +25,7 @@ export default function FieldsetPhotoChooser({
|
||||
<button type="button" className="p-1.5">
|
||||
{photo &&
|
||||
<span className={clsx(
|
||||
'flex w-[8rem]',
|
||||
'flex size-[6rem]',
|
||||
'border border-medium rounded-[4px]',
|
||||
'overflow-hidden select-none active:opacity-75',
|
||||
)}>
|
||||
@ -43,20 +44,7 @@ export default function FieldsetPhotoChooser({
|
||||
onCloseAutoFocus={e => e.preventDefault()}
|
||||
align="start"
|
||||
sideOffset={10}
|
||||
// alignOffset={-10}
|
||||
className={clsx(
|
||||
'z-20',
|
||||
'min-w-[8rem]',
|
||||
'component-surface',
|
||||
'p-1.5',
|
||||
'not-dark:shadow-lg not-dark:shadow-gray-900/10',
|
||||
'data-[side=top]:dark:shadow-[0_0px_40px_rgba(0,0,0,0.6)]',
|
||||
'data-[side=bottom]:dark:shadow-[0_10px_40px_rgba(0,0,0,0.6)]',
|
||||
'data-[side=right]:dark:shadow-[0_10px_40px_rgba(0,0,0,0.6)]',
|
||||
'data-[side=top]:animate-fade-in-from-bottom',
|
||||
'data-[side=bottom]:animate-fade-in-from-top',
|
||||
'data-[side=right]:animate-fade-in-from-top',
|
||||
)}>
|
||||
className={menuSurfaceStyles('z-20 px-1.5 py-1.5')}>
|
||||
<div className={clsx(
|
||||
'w-[14rem] max-h-[20rem] rounded-[3px] overflow-y-auto',
|
||||
'space-y-1',
|
||||
@ -1,12 +1,12 @@
|
||||
'use client';
|
||||
|
||||
import FieldsetWithStatus from '@/components/FieldsetWithStatus';
|
||||
import { Photo } from '.';
|
||||
import { Photo } from '..';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { AnnotatedTag } from './form';
|
||||
import { AnnotatedTag } from '../form';
|
||||
import { useDebounce } from 'use-debounce';
|
||||
import PhotoSmall from './PhotoSmall';
|
||||
import { getPhotosAction } from './actions';
|
||||
import PhotoSmall from '../PhotoSmall';
|
||||
import { getPhotosAction } from '../actions';
|
||||
|
||||
const convertPhotoToAnnotatedTag = (photo: Photo): AnnotatedTag => ({
|
||||
value: photo.id,
|
||||
Loading…
Reference in New Issue
Block a user