Make batch edits more discoverable
This commit is contained in:
parent
ed0fe9cae9
commit
34a8f2f362
@ -11,13 +11,12 @@ import {
|
|||||||
PATH_GRID_INFERRED,
|
PATH_GRID_INFERRED,
|
||||||
} from '@/app/paths';
|
} from '@/app/paths';
|
||||||
import { useAppState } from '@/state/AppState';
|
import { useAppState } from '@/state/AppState';
|
||||||
import { IoArrowDown, IoArrowUp } from 'react-icons/io5';
|
import { IoArrowDown, IoArrowUp, IoCloseSharp } from 'react-icons/io5';
|
||||||
import { clsx } from 'clsx/lite';
|
import { clsx } from 'clsx/lite';
|
||||||
import AdminAppInfoIcon from './AdminAppInfoIcon';
|
import AdminAppInfoIcon from './AdminAppInfoIcon';
|
||||||
import { signOutAction } from '@/auth/actions';
|
import { signOutAction } from '@/auth/actions';
|
||||||
import { ComponentProps } from 'react';
|
import { ComponentProps } from 'react';
|
||||||
import useIsKeyBeingPressed from '@/utility/useIsKeyBeingPressed';
|
import useIsKeyBeingPressed from '@/utility/useIsKeyBeingPressed';
|
||||||
import IconSelectMultiple from '@/components/icons/IconSelectMultiple';
|
|
||||||
import IconPhoto from '@/components/icons/IconPhoto';
|
import IconPhoto from '@/components/icons/IconPhoto';
|
||||||
import IconUpload from '@/components/icons/IconUpload';
|
import IconUpload from '@/components/icons/IconUpload';
|
||||||
import IconRecipe from '@/components/icons/IconRecipe';
|
import IconRecipe from '@/components/icons/IconRecipe';
|
||||||
@ -25,6 +24,7 @@ import IconTag from '@/components/icons/IconTag';
|
|||||||
import IconFolder from '@/components/icons/IconFolder';
|
import IconFolder from '@/components/icons/IconFolder';
|
||||||
import IconSignOut from '@/components/icons/IconSignOut';
|
import IconSignOut from '@/components/icons/IconSignOut';
|
||||||
import IconLock from '@/components/icons/IconLock';
|
import IconLock from '@/components/icons/IconLock';
|
||||||
|
import { IoMdCheckboxOutline } from 'react-icons/io';
|
||||||
|
|
||||||
export default function AdminAppMenu({
|
export default function AdminAppMenu({
|
||||||
active,
|
active,
|
||||||
@ -121,9 +121,14 @@ export default function AdminAppMenu({
|
|||||||
if (photosCountTotal) {
|
if (photosCountTotal) {
|
||||||
items.push({
|
items.push({
|
||||||
label: isSelecting
|
label: isSelecting
|
||||||
? 'Exit Select'
|
? 'Exit Batch Edit'
|
||||||
: 'Edit Multiple',
|
: 'Batch Edit ...',
|
||||||
icon: <IconSelectMultiple {...{ isSelecting }} />,
|
icon: isSelecting
|
||||||
|
? <IoCloseSharp
|
||||||
|
size={18}
|
||||||
|
className="translate-x-[-1px] translate-y-[0.5px]"
|
||||||
|
/>
|
||||||
|
: <IoMdCheckboxOutline size={17} className="translate-x-[-0.5px]" />,
|
||||||
href: PATH_GRID_INFERRED,
|
href: PATH_GRID_INFERRED,
|
||||||
action: () => {
|
action: () => {
|
||||||
if (isSelecting) {
|
if (isSelecting) {
|
||||||
|
|||||||
@ -51,6 +51,10 @@ export default function AdminBatchEditPanelClient({
|
|||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isFormDisabled =
|
||||||
|
isPerformingSelectEdit ||
|
||||||
|
selectedPhotoIds?.length === 0;
|
||||||
|
|
||||||
const renderPhotoCTA = () => selectedPhotoIds?.length === 0
|
const renderPhotoCTA = () => selectedPhotoIds?.length === 0
|
||||||
? <>
|
? <>
|
||||||
<FaArrowDown />
|
<FaArrowDown />
|
||||||
@ -105,40 +109,37 @@ export default function AdminBatchEditPanelClient({
|
|||||||
</LoaderButton>
|
</LoaderButton>
|
||||||
</>
|
</>
|
||||||
: <>
|
: <>
|
||||||
{(selectedPhotoIds?.length ?? 0) > 0 &&
|
<DeletePhotosButton
|
||||||
<>
|
photoIds={selectedPhotoIds}
|
||||||
<DeletePhotosButton
|
disabled={isFormDisabled}
|
||||||
photoIds={selectedPhotoIds}
|
onClick={() => setIsPerformingSelectEdit?.(true)}
|
||||||
disabled={isPerformingSelectEdit}
|
onDelete={resetForm}
|
||||||
onClick={() => setIsPerformingSelectEdit?.(true)}
|
onFinish={() => setIsPerformingSelectEdit?.(false)}
|
||||||
onDelete={resetForm}
|
/>
|
||||||
onFinish={() => setIsPerformingSelectEdit?.(false)}
|
<LoaderButton
|
||||||
/>
|
icon={<IconFavs />}
|
||||||
<LoaderButton
|
disabled={isFormDisabled}
|
||||||
icon={<IconFavs />}
|
confirmText={`Are you sure you want to favorite ${photosText}?`}
|
||||||
disabled={isPerformingSelectEdit}
|
onClick={() => {
|
||||||
confirmText={`Are you sure you want to favorite ${photosText}?`}
|
setIsPerformingSelectEdit?.(true);
|
||||||
onClick={() => {
|
tagMultiplePhotosAction(
|
||||||
setIsPerformingSelectEdit?.(true);
|
TAG_FAVS,
|
||||||
tagMultiplePhotosAction(
|
selectedPhotoIds ?? [],
|
||||||
TAG_FAVS,
|
)
|
||||||
selectedPhotoIds ?? [],
|
.then(() => {
|
||||||
)
|
toastSuccess(`${photosText} favorited`);
|
||||||
.then(() => {
|
resetForm();
|
||||||
toastSuccess(`${photosText} favorited`);
|
})
|
||||||
resetForm();
|
.finally(() => setIsPerformingSelectEdit?.(false));
|
||||||
})
|
}}
|
||||||
.finally(() => setIsPerformingSelectEdit?.(false));
|
/>
|
||||||
}}
|
<LoaderButton
|
||||||
/>
|
onClick={() => setTags('')}
|
||||||
<LoaderButton
|
disabled={isFormDisabled}
|
||||||
onClick={() => setTags('')}
|
icon={<IconTag size={15} className="translate-y-[1.5px]" />}
|
||||||
disabled={isPerformingSelectEdit}
|
>
|
||||||
icon={<IconTag size={15} className="translate-y-[1.5px]" />}
|
Tag ...
|
||||||
>
|
</LoaderButton>
|
||||||
Tag ...
|
|
||||||
</LoaderButton>
|
|
||||||
</>}
|
|
||||||
<LoaderButton
|
<LoaderButton
|
||||||
icon={<IoCloseSharp size={19} />}
|
icon={<IoCloseSharp size={19} />}
|
||||||
onClick={() => setSelectedPhotoIds?.(undefined)}
|
onClick={() => setSelectedPhotoIds?.(undefined)}
|
||||||
|
|||||||
@ -18,8 +18,9 @@ export default function DeleteButton({
|
|||||||
'active:bg-red-100/50! dark:active:bg-red-950/50!',
|
'active:bg-red-100/50! dark:active:bg-red-950/50!',
|
||||||
'disabled:text-red-500/60! dark:disabled:text-red-500/60!',
|
'disabled:text-red-500/60! dark:disabled:text-red-500/60!',
|
||||||
'disabled:bg-red-100/50! dark:disabled:bg-red-950/50!',
|
'disabled:bg-red-100/50! dark:disabled:bg-red-950/50!',
|
||||||
'border-red-200! hover:border-red-300!',
|
'border-red-200! disabled:border-red-200! hover:border-red-300!',
|
||||||
'dark:border-red-900/75! dark:hover:border-red-900!',
|
// eslint-disable-next-line max-len
|
||||||
|
'dark:border-red-900/75! dark:disabled:border-red-900/75! dark:hover:border-red-900!',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { clsx } from 'clsx/lite';
|
import { clsx } from 'clsx/lite';
|
||||||
import Checkbox from './primitives/Checkbox';
|
import SimpleCheckbox from './primitives/SimpleCheckbox';
|
||||||
import { useAppState } from '@/state/AppState';
|
import { useAppState } from '@/state/AppState';
|
||||||
import Spinner from './Spinner';
|
import Spinner from './Spinner';
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ export default function SelectTileOverlay({
|
|||||||
className="m-[1px]"
|
className="m-[1px]"
|
||||||
/>
|
/>
|
||||||
: null
|
: null
|
||||||
: <Checkbox
|
: <SimpleCheckbox
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'text-white',
|
'text-white',
|
||||||
// Required to prevent Safari jitter
|
// Required to prevent Safari jitter
|
||||||
|
|||||||
@ -1,28 +0,0 @@
|
|||||||
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,
|
|
||||||
),
|
|
||||||
}}
|
|
||||||
/>;
|
|
||||||
}
|
|
||||||
@ -4,7 +4,7 @@ import { ImCheckboxUnchecked, ImCheckboxChecked } from 'react-icons/im';
|
|||||||
|
|
||||||
const ICON_CLASS_NAME = 'text-[1rem]';
|
const ICON_CLASS_NAME = 'text-[1rem]';
|
||||||
|
|
||||||
export default function Checkbox(props: {
|
export default function SimpleCheckbox(props: {
|
||||||
children?: ReactNode
|
children?: ReactNode
|
||||||
} & InputHTMLAttributes<HTMLInputElement>) {
|
} & InputHTMLAttributes<HTMLInputElement>) {
|
||||||
const {
|
const {
|
||||||
Loading…
Reference in New Issue
Block a user