Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show quick actions on narrow screens #4580

Merged
merged 11 commits into from
Sep 4, 2023
20 changes: 18 additions & 2 deletions jsapp/js/projects/customViewRoute.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ import styles from './projectViews.module.scss';
import {toJS} from 'mobx';
import {ROOT_URL} from 'js/constants';
import {fetchPostUrl} from 'js/api';
import ProjectQuickActionsEmpty from './projectsTable/projectQuickActionsEmpty';
import ProjectQuickActions from './projectsTable/projectQuickActions';
import ProjectBulkActions from './projectsTable/projectBulkActions';

function CustomViewRoute() {
const {viewUid} = useParams();
Expand Down Expand Up @@ -100,18 +102,32 @@ function CustomViewRoute() {
onClick={exportAllData}
/>

{selectedAssets.length === 0 && (
<div className={styles.actions}>
<ProjectQuickActionsEmpty />
</div>
)}

{selectedAssets.length === 1 && (
<div className={styles.quickActions}>
<div className={styles.actions}>
<ProjectQuickActions asset={selectedAssets[0]} />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder… when I select a project, and do some bulk action (e.g. archive) and it goes through successfuly, the projects list updated, but the project is still selected. Do you think we should unselect all projects whenever the list is being updated?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's more normal to retain the selection after a bulk action. e.g. Gmail bulk "mark read", Finder bulk rename or tag – both retain the selection.

More:

  • For us it's a little moot right now since we only support delete. But if we supported >1 archive it might be nice to retain the selection so you can see where they all went.
  • I definitely want to retain the selection if the list is 'updated' due to pagination infinite scroll

This selection UI in general feels a little incomplete, as we don't support ranged selection or deselect-all (clear selection button) yet. If we add bulk archive / unarchive it would be nice to have an undo. And it might be nice to have a "X projects selected" indicator somewhere.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could add the indicator here:
Screenshot 2023-08-25 at 22 58 08

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel it would be nice to have it (not saying we should add it right now), as after I archived a project, it went outside the visible area. By mistake user could think that there is nothing selected, and could select another project and got confused or did some action unknowingly.

</div>
)}

{selectedAssets.length > 1 && (
<div className={styles.actions}>
<ProjectBulkActions assets={selectedAssets} />
magicznyleszek marked this conversation as resolved.
Show resolved Hide resolved
</div>
)}
</header>

<ProjectsTable
assets={customView.assets}
isLoading={!customView.isFirstLoadComplete}
highlightedFields={getFilteredFieldsNames()}
visibleFields={toJS(customView.fields) || customView.defaultVisibleFields}
visibleFields={
toJS(customView.fields) || customView.defaultVisibleFields
}
orderableFields={DEFAULT_ORDERABLE_FIELDS}
order={customView.order}
onChangeOrderRequested={customView.setOrder.bind(customView)}
Expand Down
7 changes: 7 additions & 0 deletions jsapp/js/projects/myProjectsRoute.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import styles from './projectViews.module.scss';
import routeStyles from './myProjectsRoute.module.scss';
import {toJS} from 'mobx';
import {COMMON_QUERIES, ROOT_URL} from 'js/constants';
import ProjectQuickActionsEmpty from './projectsTable/projectQuickActionsEmpty';
import ProjectQuickActions from './projectsTable/projectQuickActions';
import ProjectBulkActions from './projectsTable/projectBulkActions';
import Dropzone from 'react-dropzone';
Expand Down Expand Up @@ -125,6 +126,12 @@ function MyProjectsRoute() {
excludedFields={HOME_EXCLUDED_FIELDS}
/>

{selectedAssets.length === 0 && (
<div className={styles.actions}>
<ProjectQuickActionsEmpty />
</div>
)}

{selectedAssets.length === 1 && (
<div className={styles.actions}>
<ProjectQuickActions asset={selectedAssets[0]} />
Expand Down
3 changes: 2 additions & 1 deletion jsapp/js/projects/projectViews.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@

.header {
@include mixins.centerRowFlex;
gap: sizes.$x30;
padding: sizes.$x30 sizes.$x30 sizes.$x40;
gap: sizes.$x10 sizes.$x30;
flex-wrap: wrap;
}

.actions {
Expand Down
12 changes: 0 additions & 12 deletions jsapp/js/projects/projectsTable/projectActions.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,3 @@
@include mixins.centerRowFlex;
gap: sizes.$x10;
}

.menu {
@include mixins.floatingRoundedBox;
padding: sizes.$x6;
min-width: sizes.$x180;

// There is a `isFullWidth` property on Button component, but it also has text
// centering styles on it, so we can't use it.
:global .k-button {
width: 100%;
}
}
63 changes: 51 additions & 12 deletions jsapp/js/projects/projectsTable/projectBulkActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,68 @@ import type {AssetResponse, ProjectViewAsset} from 'js/dataInterface';
import Button from 'js/components/common/button';
import actionsStyles from './projectActions.module.scss';
import BulkDeletePrompt from './bulkActions/bulkDeletePrompt';
import {userCan} from 'js/components/permissions/utils';

interface ProjectBulkActionsProps {
/** A list of selected assets for bulk operations. */
assets: Array<AssetResponse | ProjectViewAsset>;
}

function userCanDeleteAssets(assets: Array<AssetResponse | ProjectViewAsset>) {
return assets.every((asset) => userCan('manage_asset', asset));
}
magicznyleszek marked this conversation as resolved.
Show resolved Hide resolved

/**
* "Bulk" Quick Actions buttons. Use these when two or more projects are
* selected in the Project Table.
*/
export default function ProjectBulkActions(props: ProjectBulkActionsProps) {
p2edwards marked this conversation as resolved.
Show resolved Hide resolved
const [isDeletePromptOpen, setIsDeletePromptOpen] = useState(false);
const canBulkDelete = userCanDeleteAssets(props.assets);

let tooltipForDelete = t('Delete projects');
if (canBulkDelete) {
tooltipForDelete = t('Delete ##count## projects').replace(
'##count##',
String(props.assets.length)
);
}

return (
<div className={actionsStyles.root}>
<Button
type='bare'
color='storm'
size='s'
startIcon='trash'
tooltip={t('Delete ##count## projects').replace(
'##count##',
String(props.assets.length)
)}
onClick={() => setIsDeletePromptOpen(true)}
classNames={['right-tooltip']}
/>
{/* Archive / Unarchive - Bulk action not supported yet */}
<span data-tip={t('Archive/Unarchive')} className='right-tooltip'>
<Button
isDisabled
type='bare'
color='storm'
size='s'
startIcon='archived'
/>
</span>

{/* Share - Bulk action not supported yet */}
<span data-tip={t('Share projects')} className='right-tooltip'>
<Button
isDisabled
type='bare'
color='storm'
size='s'
startIcon='user-share'
/>
</span>

{/* Delete */}
<span data-tip={tooltipForDelete} className='right-tooltip'>
<Button
isDisabled={!canBulkDelete}
type='bare'
color='storm'
size='s'
startIcon='trash'
onClick={() => setIsDeletePromptOpen(true)}
/>
</span>

{isDeletePromptOpen && (
<BulkDeletePrompt
Expand Down