mirror of
https://github.com/estruyf/vscode-front-matter.git
synced 2026-03-28 17:42:40 +01:00
Multi-select actions
This commit is contained in:
@@ -229,6 +229,9 @@
|
||||
"dashboard.media.folderCreation.hexo.create": "Create post asset folder",
|
||||
"dashboard.media.folderCreation.folder.create": "Create new folder",
|
||||
|
||||
"dashboard.media.folderItem.contentDirectory": "Content directory",
|
||||
"dashboard.media.folderItem.publicDirectory": "Public directory",
|
||||
|
||||
"dashboard.media.item.buttom.insert.image": "Insert image",
|
||||
"dashboard.media.item.buttom.insert.snippet": "Insert snippet",
|
||||
|
||||
|
||||
8
package-lock.json
generated
8
package-lock.json
generated
@@ -85,7 +85,7 @@
|
||||
"react-quill": "^2.0.0",
|
||||
"react-router-dom": "^6.8.0",
|
||||
"react-sortable-hoc": "^2.0.0",
|
||||
"recoil": "^0.4.1",
|
||||
"recoil": "^0.7.7",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"semver": "^7.3.8",
|
||||
@@ -10193,9 +10193,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/recoil": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/recoil/-/recoil-0.4.1.tgz",
|
||||
"integrity": "sha512-vp6KPwlHOjJ4bJofmdDchmgI9ilMTCoUisK8/WYLl8dThH7e7KmtZttiLgvDb2Em99dUfTEsk8vT8L1nUMgqXQ==",
|
||||
"version": "0.7.7",
|
||||
"resolved": "https://registry.npmjs.org/recoil/-/recoil-0.7.7.tgz",
|
||||
"integrity": "sha512-8Og5KPQW9LwC577Vc7Ug2P0vQshkv1y3zG3tSSkWMqkWSwHmE+by06L8JtnGocjW6gcCvfwB3YtrJG6/tWivNQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"hamt_plus": "1.0.2"
|
||||
|
||||
@@ -2816,7 +2816,7 @@
|
||||
"react-quill": "^2.0.0",
|
||||
"react-router-dom": "^6.8.0",
|
||||
"react-sortable-hoc": "^2.0.0",
|
||||
"recoil": "^0.4.1",
|
||||
"recoil": "^0.7.7",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"semver": "^7.3.8",
|
||||
|
||||
159
src/dashboardWebView/components/Header/ActionsBar.tsx
Normal file
159
src/dashboardWebView/components/Header/ActionsBar.tsx
Normal file
@@ -0,0 +1,159 @@
|
||||
import * as React from 'react';
|
||||
import { NavigationType } from '../../models';
|
||||
import { CommandLineIcon, PencilIcon, TrashIcon, ChevronDownIcon, XMarkIcon } from '@heroicons/react/24/outline';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { MultiSelectedItemsAtom, SelectedItemActionAtom, SelectedMediaFolderSelector, SettingsSelector } from '../../state';
|
||||
import { ActionsBarItem } from './ActionsBarItem';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import { Alert } from '../Modals/Alert';
|
||||
import { messageHandler } from '@estruyf/vscode/dist/client';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
import { CustomScript, ScriptType } from '../../../models';
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '../../../components/shadcn/Dropdown';
|
||||
|
||||
export interface IActionsBarProps {
|
||||
view: NavigationType;
|
||||
}
|
||||
|
||||
export const ActionsBar: React.FunctionComponent<IActionsBarProps> = ({
|
||||
view
|
||||
}: React.PropsWithChildren<IActionsBarProps>) => {
|
||||
const [selectedFiles, setSelectedFiles] = useRecoilState(MultiSelectedItemsAtom);
|
||||
const [, setSelectedItemAction] = useRecoilState(SelectedItemActionAtom);
|
||||
const [showAlert, setShowAlert] = React.useState(false);
|
||||
const selectedFolder = useRecoilValue(SelectedMediaFolderSelector);
|
||||
const settings = useRecoilValue(SettingsSelector);
|
||||
|
||||
const onDeleteConfirm = React.useCallback(() => {
|
||||
for (const file of selectedFiles) {
|
||||
if (file) {
|
||||
if (view === NavigationType.Contents) {
|
||||
messageHandler.send(DashboardMessage.deleteFile, file);
|
||||
} else if (view === NavigationType.Media) {
|
||||
messageHandler.send(DashboardMessage.deleteMedia, {
|
||||
file: file,
|
||||
folder: selectedFolder
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
setSelectedFiles([]);
|
||||
setShowAlert(false);
|
||||
}, [selectedFiles]);
|
||||
|
||||
const runCustomScript = React.useCallback((script: CustomScript) => {
|
||||
for (const file of selectedFiles) {
|
||||
messageHandler.send(DashboardMessage.runCustomScript, {
|
||||
script,
|
||||
path: file
|
||||
});
|
||||
}
|
||||
|
||||
setSelectedFiles([]);
|
||||
}, [selectedFiles]);
|
||||
|
||||
const customScriptActions = React.useMemo(() => {
|
||||
if (!settings?.scripts) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { scripts } = settings;
|
||||
if (view === NavigationType.Media) {
|
||||
const mediaScripts = (scripts || [])
|
||||
.filter((script) => script.type === ScriptType.MediaFile && !script.hidden);
|
||||
|
||||
if (mediaScripts.length > 0) {
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
className='flex items-center text-[var(--vscode-tab-inactiveForeground)] hover:text-[var(--vscode-tab-activeForeground)] disabled:opacity-50 disabled:hover:text-[var(--vscode-tab-inactiveForeground)]'
|
||||
disabled={selectedFiles.length === 0}
|
||||
>
|
||||
<CommandLineIcon className="mr-2 h-4 w-4" aria-hidden={true} />
|
||||
<span>Scripts</span>
|
||||
<ChevronDownIcon className="ml-2 h-4 w-4" aria-hidden={true} />
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
<DropdownMenuContent align='start'>
|
||||
{
|
||||
mediaScripts.map((script) => (
|
||||
<DropdownMenuItem
|
||||
key={script.id || script.title}
|
||||
onClick={() => runCustomScript(script)}
|
||||
>
|
||||
<CommandLineIcon className="mr-2 h-4 w-4" aria-hidden={true} />
|
||||
<span>{script.title}</span>
|
||||
</DropdownMenuItem>
|
||||
))
|
||||
}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}, [view, settings?.scripts, selectedFiles]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={`w-full flex items-center justify-between py-2 px-4 border-b bg-[var(--vscode-sideBar-background)] text-[var(--vscode-sideBar-foreground)] border-[var(--frontmatter-border)]`}
|
||||
aria-label="Item actions"
|
||||
>
|
||||
{
|
||||
view === NavigationType.Media && (
|
||||
<div className='flex items-center space-x-6'>
|
||||
<ActionsBarItem
|
||||
disabled={selectedFiles.length === 0 || selectedFiles.length > 1}
|
||||
onClick={() => setSelectedItemAction({
|
||||
path: selectedFiles[0],
|
||||
action: 'edit'
|
||||
})}
|
||||
>
|
||||
<PencilIcon className="w-4 h-4 mr-2" aria-hidden="true" />
|
||||
<span>{l10n.t(LocalizationKey.commonEdit)}</span>
|
||||
</ActionsBarItem>
|
||||
|
||||
{customScriptActions}
|
||||
|
||||
<ActionsBarItem
|
||||
className='hover:text-[var(--vscode-statusBarItem-errorBackground)]'
|
||||
disabled={selectedFiles.length === 0}
|
||||
onClick={() => setShowAlert(true)}
|
||||
>
|
||||
<TrashIcon className="w-4 h-4 mr-2" aria-hidden="true" />
|
||||
<span>{l10n.t(LocalizationKey.commonDelete)}</span>
|
||||
</ActionsBarItem>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
selectedFiles.length > 0 && (
|
||||
<button
|
||||
type="button"
|
||||
className='flex items-center hover:text-[var(--vscode-statusBarItem-warningBackground)]'
|
||||
onClick={() => setSelectedFiles([])}
|
||||
>
|
||||
<XMarkIcon className="w-4 h-4 mr-1" aria-hidden="true" />
|
||||
<span>{selectedFiles.length} selected</span>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
{showAlert && (
|
||||
<Alert
|
||||
title={`${l10n.t(LocalizationKey.commonDelete)}`}
|
||||
description={`Are you sure you want to delete the selected files?`}
|
||||
okBtnText={l10n.t(LocalizationKey.commonDelete)}
|
||||
cancelBtnText={l10n.t(LocalizationKey.commonCancel)}
|
||||
dismiss={() => setShowAlert(false)}
|
||||
trigger={onDeleteConfirm}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
26
src/dashboardWebView/components/Header/ActionsBarItem.tsx
Normal file
26
src/dashboardWebView/components/Header/ActionsBarItem.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import * as React from 'react';
|
||||
import { cn } from '../../../utils/cn';
|
||||
|
||||
export interface IActionsBarItemProps {
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
export const ActionsBarItem: React.FunctionComponent<IActionsBarItemProps> = ({
|
||||
children,
|
||||
className,
|
||||
disabled,
|
||||
onClick
|
||||
}: React.PropsWithChildren<IActionsBarItemProps>) => {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={cn(`flex items-center text-[var(--vscode-tab-inactiveForeground)] hover:text-[var(--vscode-tab-activeForeground)] disabled:opacity-50 disabled:hover:text-[var(--vscode-tab-inactiveForeground)]`, className)}
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
@@ -4,24 +4,25 @@ import * as React from 'react';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { HOME_PAGE_NAVIGATION_ID } from '../../../constants';
|
||||
import { parseWinPath } from '../../../helpers/parseWinPath';
|
||||
import { SearchAtom, SelectedMediaFolderAtom, SettingsAtom } from '../../state';
|
||||
import { SearchAtom, SettingsAtom } from '../../state';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import useMediaFolder from '../../hooks/useMediaFolder';
|
||||
|
||||
export interface IBreadcrumbProps { }
|
||||
|
||||
export const Breadcrumb: React.FunctionComponent<IBreadcrumbProps> = (
|
||||
_: React.PropsWithChildren<IBreadcrumbProps>
|
||||
) => {
|
||||
const [selectedFolder, setSelectedFolder] = useRecoilState(SelectedMediaFolderAtom);
|
||||
const { selectedFolder, updateFolder } = useMediaFolder();
|
||||
const [, setSearchValue] = useRecoilState(SearchAtom);
|
||||
const [folders, setFolders] = React.useState<string[]>([]);
|
||||
const settings = useRecoilValue(SettingsAtom);
|
||||
|
||||
const updateFolder = (folder: string) => {
|
||||
const updateMediaFolder = React.useCallback((folder: string) => {
|
||||
setSearchValue('');
|
||||
setSelectedFolder(folder);
|
||||
};
|
||||
updateFolder(folder);
|
||||
}, [updateFolder, setSearchValue]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!settings) {
|
||||
@@ -79,11 +80,11 @@ export const Breadcrumb: React.FunctionComponent<IBreadcrumbProps> = (
|
||||
}, [selectedFolder, settings]);
|
||||
|
||||
return (
|
||||
<ol role="list" className="flex space-x-4 px-5 flex-1">
|
||||
<ol role="list" className="flex space-x-2 px-4 flex-1">
|
||||
<li className="flex">
|
||||
<div className="flex items-center">
|
||||
<button
|
||||
onClick={() => setSelectedFolder(HOME_PAGE_NAVIGATION_ID)}
|
||||
onClick={() => updateMediaFolder(HOME_PAGE_NAVIGATION_ID)}
|
||||
className={`text-[var(--vscode-tab-inactiveForeground)] hover:text-[var(--vscode-tab-activeForeground)]`}
|
||||
>
|
||||
<HomeIcon className="flex-shrink-0 h-5 w-5" aria-hidden="true" />
|
||||
@@ -106,8 +107,8 @@ export const Breadcrumb: React.FunctionComponent<IBreadcrumbProps> = (
|
||||
</svg>
|
||||
|
||||
<button
|
||||
onClick={() => updateFolder(folder)}
|
||||
className={`ml-4 text-sm font-medium text-[var(--vscode-tab-inactiveForeground)] hover:text-[var(--vscode-tab-activeForeground)]`}
|
||||
onClick={() => updateMediaFolder(folder)}
|
||||
className={`ml-2 text-sm font-medium text-[var(--vscode-tab-inactiveForeground)] hover:text-[var(--vscode-tab-activeForeground)]`}
|
||||
>
|
||||
{basename(folder)}
|
||||
</button>
|
||||
|
||||
@@ -6,7 +6,7 @@ import { DashboardMessage } from '../../DashboardMessage';
|
||||
import { Grouping } from '.';
|
||||
import { ViewSwitch } from './ViewSwitch';
|
||||
import { useRecoilValue, useResetRecoilState } from 'recoil';
|
||||
import { GroupingSelector, SortingAtom } from '../../state';
|
||||
import { GroupingSelector, MultiSelectedItemsAtom, SortingAtom } from '../../state';
|
||||
import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
import { ClearFilters } from './ClearFilters';
|
||||
import { MediaHeaderTop } from '../Media/MediaHeaderTop';
|
||||
@@ -18,8 +18,7 @@ import { ArrowTopRightOnSquareIcon, BoltIcon, PlusIcon } from '@heroicons/react/
|
||||
import { HeartIcon } from '@heroicons/react/24/solid';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import { routePaths } from '../..';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { SyncButton } from './SyncButton';
|
||||
import { useMemo } from 'react';
|
||||
import { Pagination } from './Pagination';
|
||||
import { GroupOption } from '../../constants/GroupOption';
|
||||
import usePagination from '../../hooks/usePagination';
|
||||
@@ -32,6 +31,7 @@ import { SettingsLink } from '../SettingsView/SettingsLink';
|
||||
import { Link } from '../Common/Link';
|
||||
import { SPONSOR_LINK } from '../../../constants';
|
||||
import { Filters } from './Filters';
|
||||
import { ActionsBar } from './ActionsBar';
|
||||
|
||||
export interface IHeaderProps {
|
||||
header?: React.ReactNode;
|
||||
@@ -51,6 +51,7 @@ export const Header: React.FunctionComponent<IHeaderProps> = ({
|
||||
}: React.PropsWithChildren<IHeaderProps>) => {
|
||||
const grouping = useRecoilValue(GroupingSelector);
|
||||
const resetSorting = useResetRecoilState(SortingAtom);
|
||||
const resetSelectedItems = useResetRecoilState(MultiSelectedItemsAtom);
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const { pageSetNr } = usePagination(settings?.dashboardState.contents.pagination);
|
||||
@@ -70,6 +71,7 @@ export const Header: React.FunctionComponent<IHeaderProps> = ({
|
||||
const updateView = (view: NavigationType) => {
|
||||
navigate(routePaths[view]);
|
||||
resetSorting();
|
||||
resetSelectedItems();
|
||||
};
|
||||
|
||||
const runBulkScript = (script: CustomScript) => {
|
||||
@@ -216,6 +218,8 @@ export const Header: React.FunctionComponent<IHeaderProps> = ({
|
||||
<MediaHeaderTop />
|
||||
|
||||
<MediaHeaderBottom />
|
||||
|
||||
<ActionsBar view={NavigationType.Media} />
|
||||
</>
|
||||
)}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import { DashboardMessage } from '../../DashboardMessage';
|
||||
import {
|
||||
AllContentFoldersAtom,
|
||||
AllStaticFoldersAtom,
|
||||
SelectedMediaFolderAtom,
|
||||
SettingsSelector,
|
||||
ViewDataSelector
|
||||
} from '../../state';
|
||||
@@ -18,13 +17,14 @@ import { extname } from 'path';
|
||||
import { parseWinPath } from '../../../helpers/parseWinPath';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import useMediaFolder from '../../hooks/useMediaFolder';
|
||||
|
||||
export interface IFolderCreationProps { }
|
||||
|
||||
export const FolderCreation: React.FunctionComponent<IFolderCreationProps> = (
|
||||
props: React.PropsWithChildren<IFolderCreationProps>
|
||||
_: React.PropsWithChildren<IFolderCreationProps>
|
||||
) => {
|
||||
const selectedFolder = useRecoilValue(SelectedMediaFolderAtom);
|
||||
const { selectedFolder } = useMediaFolder();
|
||||
const settings = useRecoilValue(SettingsSelector);
|
||||
const allStaticFolders = useRecoilValue(AllStaticFoldersAtom);
|
||||
const allContentFolders = useRecoilValue(AllContentFoldersAtom);
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { FolderIcon } from '@heroicons/react/24/solid';
|
||||
import { basename, join } from 'path';
|
||||
import * as React from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { SelectedMediaFolderAtom } from '../../state';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
import useMediaFolder from '../../hooks/useMediaFolder';
|
||||
|
||||
export interface IFolderItemProps {
|
||||
folder: string;
|
||||
@@ -15,7 +16,7 @@ export const FolderItem: React.FunctionComponent<IFolderItemProps> = ({
|
||||
wsFolder,
|
||||
staticFolder
|
||||
}: React.PropsWithChildren<IFolderItemProps>) => {
|
||||
const [, setSelectedFolder] = useRecoilState(SelectedMediaFolderAtom);
|
||||
const { updateFolder } = useMediaFolder();
|
||||
|
||||
const relFolderPath = wsFolder ? folder.replace(wsFolder, '') : folder;
|
||||
|
||||
@@ -29,9 +30,9 @@ export const FolderItem: React.FunctionComponent<IFolderItemProps> = ({
|
||||
className={`group relative hover:bg-[var(--vscode-list-hoverBackground)] text-[var(--vscode-editor-foreground)] hover:text-[var(--vscode-list-activeSelectionForeground)]`}
|
||||
>
|
||||
<button
|
||||
title={isContentFolder ? 'Content directory folder' : 'Public directory folder'}
|
||||
title={isContentFolder ? l10n.t(LocalizationKey.dashboardMediaFolderItemContentDirectory) : l10n.t(LocalizationKey.dashboardMediaFolderItemPublicDirectory)}
|
||||
className={`p-4 w-full flex flex-row items-center h-full`}
|
||||
onClick={() => setSelectedFolder(folder)}
|
||||
onClick={() => updateFolder(folder)}
|
||||
>
|
||||
<div className="relative mr-4">
|
||||
<FolderIcon className={`h-12 w-12`} />
|
||||
|
||||
@@ -17,6 +17,8 @@ import { MediaInfo } from '../../../models/MediaPaths';
|
||||
import { DashboardMessage } from '../../DashboardMessage';
|
||||
import {
|
||||
LightboxAtom,
|
||||
MultiSelectedItemsAtom,
|
||||
SelectedItemActionAtom,
|
||||
SelectedMediaFolderSelector,
|
||||
SettingsSelector,
|
||||
ViewDataSelector
|
||||
@@ -40,6 +42,8 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
media,
|
||||
}: React.PropsWithChildren<IItemProps>) => {
|
||||
const [, setLightbox] = useRecoilState(LightboxAtom);
|
||||
const [selectedFiles, setSelectedFiles] = useRecoilState(MultiSelectedItemsAtom);
|
||||
const [selectedItemAction, setSelectedItemAction] = useRecoilState(SelectedItemActionAtom);
|
||||
const [showAlert, setShowAlert] = useState(false);
|
||||
const [showForm, setShowForm] = useState(false);
|
||||
const [showSnippetSelection, setShowSnippetSelection] = useState(false);
|
||||
@@ -191,14 +195,14 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
});
|
||||
};
|
||||
|
||||
const getDimensions = () => {
|
||||
const dimensions = useMemo(() => {
|
||||
if (media.dimensions) {
|
||||
return `${media.dimensions.width} x ${media.dimensions.height}`;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
}, [media]);
|
||||
|
||||
const getSize = () => {
|
||||
const size = useMemo(() => {
|
||||
if (media?.size) {
|
||||
const size = media.size / (1024 * 1024);
|
||||
if (size > 1) {
|
||||
@@ -209,23 +213,21 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
}, [media]);
|
||||
|
||||
const getMediaDetails = () => {
|
||||
const mediaDetails = useMemo(() => {
|
||||
let sizeDetails = [];
|
||||
|
||||
const dimensions = getDimensions();
|
||||
if (dimensions) {
|
||||
sizeDetails.push(dimensions);
|
||||
}
|
||||
|
||||
const size = getSize();
|
||||
if (size) {
|
||||
sizeDetails.push(size);
|
||||
}
|
||||
|
||||
return sizeDetails.join(' - ');
|
||||
};
|
||||
}, [media, dimensions, size]);
|
||||
|
||||
const openLightbox = useCallback(() => {
|
||||
if (isImageFile) {
|
||||
@@ -314,12 +316,29 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
return null;
|
||||
}, [media]);
|
||||
|
||||
const onMultiSelect = useCallback(() => {
|
||||
if (selectedFiles.includes(media.fsPath)) {
|
||||
setSelectedFiles(selectedFiles.filter((file) => file !== media.fsPath));
|
||||
} else {
|
||||
setSelectedFiles([...selectedFiles, media.fsPath]);
|
||||
}
|
||||
}, [selectedFiles]);
|
||||
|
||||
const clearFormData = () => {
|
||||
setShowSnippetFormDialog(false);
|
||||
setSnippet(undefined);
|
||||
setMediaData(undefined);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedItemAction && selectedItemAction.path === media.fsPath) {
|
||||
if (selectedItemAction.action === 'edit') {
|
||||
updateMetadata();
|
||||
setSelectedItemAction(undefined);
|
||||
}
|
||||
}
|
||||
}, [media, selectedItemAction])
|
||||
|
||||
useEffect(() => {
|
||||
const name = basename(parseWinPath(media.fsPath) || '');
|
||||
if (name !== filename) {
|
||||
@@ -351,11 +370,13 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
{renderMedia}
|
||||
</div>
|
||||
|
||||
<div className='hidden group-hover:block absolute top-2 left-2 white'>
|
||||
<div className={`${selectedFiles.includes(media.fsPath) ? 'block' : 'hidden'} group-hover:block absolute top-2 left-2 white`}>
|
||||
<VSCodeCheckbox
|
||||
onClick={(e: any) => {
|
||||
onClick={(e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
||||
e.stopPropagation();
|
||||
}} />
|
||||
onMultiSelect();
|
||||
}}
|
||||
checked={selectedFiles.includes(media.fsPath)} />
|
||||
</div>
|
||||
|
||||
{hasViewData && (
|
||||
@@ -439,7 +460,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
{l10n.t(LocalizationKey.dashboardMediaCommonSize)}:
|
||||
</b>
|
||||
<span className={`block mt-1 text-xs text-[var(--vscode-foreground)]`}>
|
||||
{getMediaDetails()}
|
||||
{mediaDetails}
|
||||
</span>
|
||||
</p>
|
||||
)}
|
||||
@@ -471,8 +492,8 @@ export const Item: React.FunctionComponent<IItemProps> = ({
|
||||
{showDetails && (
|
||||
<DetailsSlideOver
|
||||
imgSrc={media.vsPath || ''}
|
||||
size={getSize()}
|
||||
dimensions={getDimensions()}
|
||||
size={size}
|
||||
dimensions={dimensions}
|
||||
folder={getFolder()}
|
||||
media={media}
|
||||
showForm={showForm}
|
||||
|
||||
@@ -12,12 +12,12 @@ import {
|
||||
MediaTotalAtom,
|
||||
PageAtom,
|
||||
SearchAtom,
|
||||
SelectedMediaFolderAtom,
|
||||
SettingsAtom
|
||||
} from '../state';
|
||||
import Fuse from 'fuse.js';
|
||||
import usePagination from './usePagination';
|
||||
import { usePrevious } from '../../panelWebView/hooks/usePrevious';
|
||||
import useMediaFolder from './useMediaFolder';
|
||||
|
||||
const fuseOptions: Fuse.IFuseOptions<MediaInfo> = {
|
||||
keys: [
|
||||
@@ -35,7 +35,7 @@ export default function useMedia() {
|
||||
// const page = useRecoilValue(PageAtom);
|
||||
const [page, setPage] = useRecoilState(PageAtom);
|
||||
const [searchedMedia, setSearchedMedia] = useState<MediaInfo[]>([]);
|
||||
const [, setSelectedFolder] = useRecoilState(SelectedMediaFolderAtom);
|
||||
const { updateFolder } = useMediaFolder();
|
||||
const [, setTotal] = useRecoilState(MediaTotalAtom);
|
||||
const [, setFolders] = useRecoilState(MediaFoldersAtom);
|
||||
const [, setAllContentFolders] = useRecoilState(AllContentFoldersAtom);
|
||||
@@ -79,7 +79,7 @@ export default function useMedia() {
|
||||
setMedia(payload.media);
|
||||
setTotal(payload.total);
|
||||
setFolders(payload.folders);
|
||||
setSelectedFolder(payload.selectedFolder);
|
||||
updateFolder(payload.selectedFolder);
|
||||
if (search) {
|
||||
searchMedia(search, payload.media);
|
||||
} else {
|
||||
|
||||
17
src/dashboardWebView/hooks/useMediaFolder.tsx
Normal file
17
src/dashboardWebView/hooks/useMediaFolder.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { MultiSelectedItemsAtom, SelectedMediaFolderAtom } from '../state';
|
||||
|
||||
export default function useMediaFolder() {
|
||||
const [selectedFolder, setSelectedFolder] = useRecoilState(SelectedMediaFolderAtom);
|
||||
const [, setSelectedFiles] = useRecoilState(MultiSelectedItemsAtom);
|
||||
|
||||
const updateFolder = (folder: string) => {
|
||||
setSelectedFolder(folder);
|
||||
setSelectedFiles([]);
|
||||
};
|
||||
|
||||
return {
|
||||
selectedFolder,
|
||||
updateFolder
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
export const MultiSelectedItemsAtom = atom<string[]>({
|
||||
key: 'MultiSelectedItemsAtom',
|
||||
default: []
|
||||
});
|
||||
12
src/dashboardWebView/state/atom/SelectedItemActionAtom.ts
Normal file
12
src/dashboardWebView/state/atom/SelectedItemActionAtom.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
export const SelectedItemActionAtom = atom<
|
||||
| {
|
||||
path: string;
|
||||
action: 'edit';
|
||||
}
|
||||
| undefined
|
||||
>({
|
||||
key: 'SelectedItemActionAtom',
|
||||
default: undefined
|
||||
});
|
||||
@@ -14,10 +14,12 @@ export * from './LocalesAtom';
|
||||
export * from './MediaFoldersAtom';
|
||||
export * from './MediaTotalAtom';
|
||||
export * from './ModeAtom';
|
||||
export * from './MultiSelectedItemsAtom';
|
||||
export * from './PageAtom';
|
||||
export * from './PinnedItems';
|
||||
export * from './SearchAtom';
|
||||
export * from './SearchReadyAtom';
|
||||
export * from './SelectedItemActionAtom';
|
||||
export * from './SelectedMediaFolderAtom';
|
||||
export * from './SettingsAtom';
|
||||
export * from './SortingAtom';
|
||||
|
||||
@@ -739,6 +739,14 @@ export enum LocalizationKey {
|
||||
* Create new folder
|
||||
*/
|
||||
dashboardMediaFolderCreationFolderCreate = 'dashboard.media.folderCreation.folder.create',
|
||||
/**
|
||||
* Content directory
|
||||
*/
|
||||
dashboardMediaFolderItemContentDirectory = 'dashboard.media.folderItem.contentDirectory',
|
||||
/**
|
||||
* Public directory
|
||||
*/
|
||||
dashboardMediaFolderItemPublicDirectory = 'dashboard.media.folderItem.publicDirectory',
|
||||
/**
|
||||
* Insert image
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user