Skip to content

Commit

Permalink
feat: Add select all action #831
Browse files Browse the repository at this point in the history
  • Loading branch information
estruyf committed Jul 18, 2024
1 parent ced7e41 commit 86de4fa
Show file tree
Hide file tree
Showing 9 changed files with 66 additions and 19 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions l10n/bundle.l10n.json
Original file line number Diff line number Diff line change
Expand Up @@ -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?",

Expand Down
7 changes: 6 additions & 1 deletion src/dashboardWebView/components/Contents/Overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down
55 changes: 39 additions & 16 deletions src/dashboardWebView/components/Header/ActionsBar.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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) {
Expand Down Expand Up @@ -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[] = [];

Expand Down Expand Up @@ -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>
Expand All @@ -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>
Expand All @@ -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>
Expand All @@ -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
Expand All @@ -266,7 +288,8 @@ export const ActionsBar: React.FunctionComponent<IActionsBarProps> = ({
dismiss={() => setShowAlert(false)}
trigger={onDeleteConfirm}
/>
)}
)
}
</>
);
};
5 changes: 4 additions & 1 deletion src/dashboardWebView/components/Header/ActionsBarItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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}
Expand Down
5 changes: 4 additions & 1 deletion src/dashboardWebView/components/Media/Media.tsx
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -126,6 +128,7 @@ export const Media: React.FunctionComponent<IMediaProps> = (
});
}

setPagedItems(mediaFiles.map((m) => m.fsPath));
return mediaFiles;
}, [media, viewData, currentStaticFolder, settings?.staticFolder]);

Expand Down
6 changes: 6 additions & 0 deletions src/dashboardWebView/state/atom/PagedItems.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { atom } from 'recoil';

export const PagedItems = atom<string[]>({
key: 'PagedItems',
default: []
});
1 change: 1 addition & 0 deletions src/dashboardWebView/state/atom/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
4 changes: 4 additions & 0 deletions src/localization/localization.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,10 @@ export enum LocalizationKey {
* {0} selected
*/
dashboardHeaderActionsBarItemsSelected = 'dashboard.header.actionsBar.itemsSelected',
/**
* Select all
*/
dashboardHeaderActionsBarSelectAll = 'dashboard.header.actionsBar.selectAll',
/**
* Delete selected files
*/
Expand Down

0 comments on commit 86de4fa

Please sign in to comment.