mirror of
https://github.com/estruyf/vscode-front-matter.git
synced 2026-03-28 17:42:40 +01:00
feat: Add select all action #831
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
- [#821](https://github.com/estruyf/vscode-front-matter/issues/821): Added URI handler to support command links from the documentation
|
||||
- [#822](https://github.com/estruyf/vscode-front-matter/issues/822): Added docs to the panel & dashboard views
|
||||
- [#829](https://github.com/estruyf/vscode-front-matter/issues/829): UI extensibility is now generally available
|
||||
- [#831](https://github.com/estruyf/vscode-front-matter/issues/831): Added "select all" action bar button to the content and media dashboards
|
||||
|
||||
### 🐞 Fixes
|
||||
|
||||
|
||||
@@ -154,6 +154,7 @@
|
||||
"dashboard.filters.languageFilter.all": "All",
|
||||
|
||||
"dashboard.header.actionsBar.itemsSelected": "{0} selected",
|
||||
"dashboard.header.actionsBar.selectAll": "Select all",
|
||||
"dashboard.header.actionsBar.alertDelete.title": "Delete selected files",
|
||||
"dashboard.header.actionsBar.alertDelete.description": "Are you sure you want to delete the selected files?",
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { groupBy } from '../../../helpers/GroupBy';
|
||||
import { FrontMatterIcon } from '../../../panelWebView/components/Icons/FrontMatterIcon';
|
||||
import { GroupOption } from '../../constants/GroupOption';
|
||||
import { GroupingSelector, PageAtom, ViewSelector } from '../../state';
|
||||
import { GroupingSelector, PageAtom, PagedItems, ViewSelector } from '../../state';
|
||||
import { Item } from './Item';
|
||||
import { List } from './List';
|
||||
import usePagination from '../../hooks/usePagination';
|
||||
@@ -34,6 +34,7 @@ export const Overview: React.FunctionComponent<IOverviewProps> = ({
|
||||
const page = useRecoilValue(PageAtom);
|
||||
const { pageSetNr } = usePagination(settings?.dashboardState.contents.pagination);
|
||||
const view = useRecoilValue(ViewSelector);
|
||||
const [, setPagedItems] = useRecoilState(PagedItems);
|
||||
|
||||
const pagedPages = useMemo(() => {
|
||||
if (pageSetNr) {
|
||||
@@ -100,6 +101,10 @@ export const Overview: React.FunctionComponent<IOverviewProps> = ({
|
||||
return { groupKeys, groupedPages };
|
||||
}, [pages, grouping, settings?.draftField]);
|
||||
|
||||
React.useEffect(() => {
|
||||
setPagedItems(pagedPages.map((page) => page.fmFilePath));
|
||||
}, [pagedPages]);
|
||||
|
||||
React.useEffect(() => {
|
||||
messageHandler.request<string[]>(DashboardMessage.getPinnedItems).then((items) => {
|
||||
setIsReady(true);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import * as React from 'react';
|
||||
import { NavigationType, Page } from '../../models';
|
||||
import { CommandLineIcon, PencilIcon, TrashIcon, ChevronDownIcon, XMarkIcon, EyeIcon, LanguageIcon } from '@heroicons/react/24/outline';
|
||||
import { CommandLineIcon, PencilIcon, TrashIcon, ChevronDownIcon, XMarkIcon, EyeIcon, LanguageIcon, CheckIcon } from '@heroicons/react/24/outline';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { MultiSelectedItemsAtom, SelectedItemActionAtom, SelectedMediaFolderSelector, SettingsSelector } from '../../state';
|
||||
import { MultiSelectedItemsAtom, PagedItems, SelectedItemActionAtom, SelectedMediaFolderSelector, SettingsSelector } from '../../state';
|
||||
import { ActionsBarItem } from './ActionsBarItem';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../../localization';
|
||||
@@ -29,6 +29,7 @@ export const ActionsBar: React.FunctionComponent<IActionsBarProps> = ({
|
||||
const selectedFolder = useRecoilValue(SelectedMediaFolderSelector);
|
||||
const settings = useRecoilValue(SettingsSelector);
|
||||
const { files } = useFilesContext();
|
||||
const pagedItems = useRecoilValue(PagedItems);
|
||||
|
||||
const viewFile = React.useCallback(() => {
|
||||
if (selectedFiles.length === 1) {
|
||||
@@ -66,6 +67,10 @@ export const ActionsBar: React.FunctionComponent<IActionsBarProps> = ({
|
||||
}
|
||||
}, [selectedFiles]);
|
||||
|
||||
const selectAllItems = React.useCallback(() => {
|
||||
setSelectedFiles([...pagedItems]);
|
||||
}, [pagedItems]);
|
||||
|
||||
const languageActions = React.useMemo(() => {
|
||||
const actions: React.ReactNode[] = [];
|
||||
|
||||
@@ -192,6 +197,7 @@ export const ActionsBar: React.FunctionComponent<IActionsBarProps> = ({
|
||||
<ActionsBarItem
|
||||
disabled={selectedFiles.length === 0 || selectedFiles.length > 1}
|
||||
onClick={viewFile}
|
||||
title={l10n.t(LocalizationKey.commonView)}
|
||||
>
|
||||
<EyeIcon className="w-4 h-4 mr-2" aria-hidden="true" />
|
||||
<span>{l10n.t(LocalizationKey.commonView)}</span>
|
||||
@@ -205,6 +211,7 @@ export const ActionsBar: React.FunctionComponent<IActionsBarProps> = ({
|
||||
messageHandler.send(DashboardMessage.rename, selectedFiles[0]);
|
||||
setSelectedFiles([]);
|
||||
}}
|
||||
title={l10n.t(LocalizationKey.commonRename)}
|
||||
>
|
||||
<RenameIcon className="w-4 h-4 mr-2" aria-hidden="true" />
|
||||
<span>{l10n.t(LocalizationKey.commonRename)}</span>
|
||||
@@ -221,6 +228,7 @@ export const ActionsBar: React.FunctionComponent<IActionsBarProps> = ({
|
||||
path: selectedFiles[0],
|
||||
action: 'edit'
|
||||
})}
|
||||
title={l10n.t(LocalizationKey.commonEdit)}
|
||||
>
|
||||
<PencilIcon className="w-4 h-4 mr-2" aria-hidden="true" />
|
||||
<span>{l10n.t(LocalizationKey.commonEdit)}</span>
|
||||
@@ -237,25 +245,39 @@ export const ActionsBar: React.FunctionComponent<IActionsBarProps> = ({
|
||||
className='hover:text-[var(--vscode-statusBarItem-errorBackground)]'
|
||||
disabled={selectedFiles.length === 0}
|
||||
onClick={() => setShowAlert(true)}
|
||||
title={l10n.t(LocalizationKey.commonDelete)}
|
||||
>
|
||||
<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>{l10n.t(LocalizationKey.dashboardHeaderActionsBarItemsSelected, selectedFiles.length)}</span>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<div className='flex gap-4'>
|
||||
{
|
||||
selectedFiles.length > 0 && (
|
||||
<ActionsBarItem
|
||||
className='flex items-center hover:text-[var(--vscode-statusBarItem-warningBackground)]'
|
||||
onClick={() => setSelectedFiles([])}
|
||||
title={l10n.t(LocalizationKey.dashboardHeaderActionsBarItemsSelected, selectedFiles.length)}
|
||||
>
|
||||
<XMarkIcon className="w-4 h-4 mr-1" aria-hidden="true" />
|
||||
<span>{l10n.t(LocalizationKey.dashboardHeaderActionsBarItemsSelected, selectedFiles.length)}</span>
|
||||
</ActionsBarItem>
|
||||
)
|
||||
}
|
||||
|
||||
<ActionsBarItem
|
||||
disabled={selectedFiles.length === pagedItems.length}
|
||||
onClick={selectAllItems}
|
||||
title={l10n.t(LocalizationKey.dashboardHeaderActionsBarSelectAll)}
|
||||
>
|
||||
<div className='w-4 h-4 inline-flex items-center justify-center border border-[var(--vscode-sideBar-foreground)] group-hover:border-[var(--vscode-statusBarItem-warningBackground)] rounded mr-1'>
|
||||
<CheckIcon className="w-3 h-3" aria-hidden="true" />
|
||||
</div>
|
||||
<span>{l10n.t(LocalizationKey.dashboardHeaderActionsBarSelectAll)}</span>
|
||||
</ActionsBarItem>
|
||||
</div>
|
||||
</div >
|
||||
|
||||
{showAlert && (
|
||||
<Alert
|
||||
@@ -266,7 +288,8 @@ export const ActionsBar: React.FunctionComponent<IActionsBarProps> = ({
|
||||
dismiss={() => setShowAlert(false)}
|
||||
trigger={onDeleteConfirm}
|
||||
/>
|
||||
)}
|
||||
)
|
||||
}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -2,6 +2,7 @@ import * as React from 'react';
|
||||
import { cn } from '../../../utils/cn';
|
||||
|
||||
export interface IActionsBarItemProps {
|
||||
title?: string;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
onClick?: () => void;
|
||||
@@ -11,11 +12,13 @@ export const ActionsBarItem: React.FunctionComponent<IActionsBarItemProps> = ({
|
||||
children,
|
||||
className,
|
||||
disabled,
|
||||
onClick
|
||||
onClick,
|
||||
title
|
||||
}: React.PropsWithChildren<IActionsBarItemProps>) => {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
title={title || ''}
|
||||
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}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
import { ArrowUpTrayIcon } from '@heroicons/react/24/outline';
|
||||
import * as React from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import {
|
||||
LoadingAtom,
|
||||
MediaFoldersAtom,
|
||||
PagedItems,
|
||||
SelectedMediaFolderAtom,
|
||||
SettingsSelector,
|
||||
ViewDataSelector
|
||||
@@ -41,6 +42,7 @@ export const Media: React.FunctionComponent<IMediaProps> = (
|
||||
const selectedFolder = useRecoilValue(SelectedMediaFolderAtom);
|
||||
const folders = useRecoilValue(MediaFoldersAtom);
|
||||
const loading = useRecoilValue(LoadingAtom);
|
||||
const [, setPagedItems] = useRecoilState(PagedItems);
|
||||
|
||||
const currentStaticFolder = useMemo(() => {
|
||||
if (settings?.staticFolder) {
|
||||
@@ -126,6 +128,7 @@ export const Media: React.FunctionComponent<IMediaProps> = (
|
||||
});
|
||||
}
|
||||
|
||||
setPagedItems(mediaFiles.map((m) => m.fsPath));
|
||||
return mediaFiles;
|
||||
}, [media, viewData, currentStaticFolder, settings?.staticFolder]);
|
||||
|
||||
|
||||
6
src/dashboardWebView/state/atom/PagedItems.ts
Normal file
6
src/dashboardWebView/state/atom/PagedItems.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
export const PagedItems = atom<string[]>({
|
||||
key: 'PagedItems',
|
||||
default: []
|
||||
});
|
||||
@@ -16,6 +16,7 @@ export * from './MediaTotalAtom';
|
||||
export * from './ModeAtom';
|
||||
export * from './MultiSelectedItemsAtom';
|
||||
export * from './PageAtom';
|
||||
export * from './PagedItems';
|
||||
export * from './PinnedItems';
|
||||
export * from './SearchAtom';
|
||||
export * from './SearchReadyAtom';
|
||||
|
||||
@@ -515,6 +515,10 @@ export enum LocalizationKey {
|
||||
* {0} selected
|
||||
*/
|
||||
dashboardHeaderActionsBarItemsSelected = 'dashboard.header.actionsBar.itemsSelected',
|
||||
/**
|
||||
* Select all
|
||||
*/
|
||||
dashboardHeaderActionsBarSelectAll = 'dashboard.header.actionsBar.selectAll',
|
||||
/**
|
||||
* Delete selected files
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user