Web.Files: add GroupButtonMenu: selectors, actions, handling events, fake callbacks

This commit is contained in:
Daniil Senkiv 2020-03-17 08:58:59 +03:00
parent 4c09c402f4
commit 9d7d454655
8 changed files with 288 additions and 59 deletions

View File

@ -13,7 +13,8 @@ import {
import EmptyFolderContainer from "./EmptyFolderContainer";
import FilesRowContent from "./FilesRowContent";
import { api } from 'asc-web-common';
import { fetchFiles, deleteFile, deleteFolder, fetchFolder } from '../../../../../store/files/actions';
import { fetchFiles, deleteFile, deleteFolder, fetchFolder, selectFile, deselectFile } from '../../../../../store/files/actions';
import { isFileSelected } from '../../../../../store/files/selectors';
import store from "../../../../../store/store";
import { getFilterByLocation } from "../../../../../helpers/converters";
import config from "../../../../../../package.json";
@ -172,8 +173,17 @@ class SectionBodyContent extends React.PureComponent {
return false;
};
onContentRowSelect = (checked, file) => {
if (checked) {
this.props.selectFile(file);
} else {
this.props.deselectFile(file);
}
};
render() {
const { files, folders, viewer, parentId, folderId, settings } = this.props;
const { files, folders, viewer, parentId, folderId, settings, selection } = this.props;
const { editingId, isEdit, isCreating } = this.state;
let items = [...folders, ...files];
@ -194,7 +204,7 @@ class SectionBodyContent extends React.PureComponent {
const contextOptionsProps = !contextOptions.length || item.id === -2
? {}
: { contextOptions };
const checked = false; //isUserSelected(selection, user.id);
const checked = isFileSelected(selection, item.id);
const checkedProps = /* isAdmin(viewer) */ item.id !== -2 && true ? { checked } : {};
const element = (isEdit || isCreating) && (item.id === editingId || item.id === -2)
? <Loader type='oval' color="black" size='24px' label="Editing..." />
@ -207,7 +217,7 @@ class SectionBodyContent extends React.PureComponent {
key={item.id}
data={item}
element={element}
onSelect={() => { }}
onSelect={this.onContentRowSelect}
editing={editingId}
{...checkedProps}
{...contextOptionsProps}
@ -244,5 +254,5 @@ const mapStateToProps = state => {
export default connect(
mapStateToProps,
{ fetchFiles, deleteFile, deleteFolder }
{ fetchFiles, deleteFile, deleteFolder, selectFile, deselectFile }
)(withRouter(withTranslation()(SectionBodyContent)));

View File

@ -1,27 +1,33 @@
import React, { useCallback } from "react";
import styled, { css } from "styled-components";
import { withRouter } from "react-router";
import { Headline, store } from 'asc-web-common';
import { Headline, store, constants } from 'asc-web-common';
import { connect } from "react-redux";
import { withTranslation } from "react-i18next";
import {
toastr,
ContextMenuButton
ContextMenuButton,
GroupButtonsMenu,
DropDownItem
} from "asc-web-components";
const { isAdmin } = store.auth.selectors;
const { FilterType } = constants;
const StyledContainer = styled.div`
@media (min-width: 1024px) {
${props => props.isHeaderVisible &&
css`width: calc(100% + 76px);`}
}
.header-container {
position: relative;
display: flex;
align-items: center;
max-width: calc(100vw - 32px);
@media (min-width: 1024px) {
${props => props.isHeaderVisible && css`width: calc(100% + 76px);`}
}
.action-button {
margin-bottom: -1px;
margin-left: 16px;
@ -38,11 +44,41 @@ const StyledContainer = styled.div`
}
}
}
}
.group-button-menu-container {
margin: 0 -16px;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
padding-bottom: 56px;
@media (max-width: 1024px) {
& > div:first-child {
${props =>
props.isArticlePinned &&
css`
width: calc(100% - 240px);
`}
position: absolute;
top: 56px;
z-index: 180;
}
}
@media (min-width: 1024px) {
margin: 0 -24px;
}
}
`;
const SectionHeaderContent = props => {
const { t, folder, title, onCreate } = props;
const { t, folder, title,
onCreate, onCheck, onSelect,
onClose,
isHeaderVisible,
isHeaderChecked,
isHeaderIndeterminate,
selection } = props;
const createDocument = useCallback(
() => onCreate('docx'),
@ -133,6 +169,11 @@ const SectionHeaderContent = props => {
[]
);
const downloadAsAction = useCallback(
() => toastr.info("downloadAsAction click"),
[]
);
const renameAction = useCallback(
() => toastr.info("renameAction click"),
[]
@ -201,10 +242,83 @@ const SectionHeaderContent = props => {
deleteAction
]);
const isItemsSelected = selection.length;
const isOnlyFolderSelected = selection.every(selected => !selected.fileType);
const menuItems = [
{
label: t("LblSelect"),
isDropdown: true,
isSeparator: true,
isSelect: true,
fontWeight: "bold",
children: [
<DropDownItem key='all' label={t("All")} />,
<DropDownItem key={FilterType.FoldersOnly} label={t("Folders")} />,
<DropDownItem key={FilterType.DocumentsOnly} label={t("Documents")} />,
<DropDownItem key={FilterType.PresentationsOnly} label={t("Presentations")} />,
<DropDownItem key={FilterType.SpreadsheetsOnly} label={t("Spreadsheets")} />,
<DropDownItem key={FilterType.ImagesOnly} label={t("Images")} />,
<DropDownItem key={FilterType.MediaOnly} label={t("Media")} />,
<DropDownItem key={FilterType.ArchiveOnly} label={t("Archives")} />,
<DropDownItem key={FilterType.FilesOnly} label={t("AllFiles")} />,
],
onSelect: item => onSelect(item.key)
},
{
label: t("Share"),
disabled: !isItemsSelected,
onClick: openSharingSettings
},
{
label: t("Download"),
disabled: !isItemsSelected,
onClick: downloadAction
},
{
label: t("DownloadAs"),
disabled: !isItemsSelected || isOnlyFolderSelected,
onClick: downloadAsAction
},
{
label: t("MoveTo"),
disabled: !isItemsSelected,
onClick: moveAction
},
{
label: t("Copy"),
disabled: !isItemsSelected,
onClick: copyAction
},
{
label: t("Delete"),
disabled: !isItemsSelected,
onClick: deleteAction
}
];
return (
<StyledContainer isHeaderVisible={true}>
<StyledContainer isHeaderVisible={isHeaderVisible}>
{isHeaderVisible ? (
<div className="group-button-menu-container">
<GroupButtonsMenu
checked={isHeaderChecked}
isIndeterminate={isHeaderIndeterminate}
onChange={onCheck}
menuItems={menuItems}
visible={isHeaderVisible}
moreLabel={t("More")}
closeTitle={t("CloseButton")}
onClose={onClose}
selected={menuItems[0].label}
/>
</div>
)
: (
<div className='header-container'>
<Headline className='headline-header' type="content" truncate={true}>{title}</Headline>
{folder ? (
{folder ?
<>
<ContextMenuButton
className="action-button"
@ -226,8 +340,7 @@ const SectionHeaderContent = props => {
isDisabled={false}
/>
</>
) : (
<>
:
<ContextMenuButton
className="action-button"
directionX="right"
@ -237,7 +350,8 @@ const SectionHeaderContent = props => {
getData={getContextOptionsPlus}
isDisabled={false}
/>
</>
}
</div>
)}
</StyledContainer>
);
@ -247,7 +361,8 @@ const mapStateToProps = state => {
return {
isAdmin: isAdmin(state.auth.user),
title: state.files.selectedFolder.title,
folder: state.files.selectedFolder.parentId !== 0
folder: state.files.selectedFolder.parentId !== 0,
selection: state.files.selection
};
};

View File

@ -35,12 +35,12 @@ class PureHome extends React.Component {
}
renderGroupButtonMenu = () => {
const { files, selection, selected, setSelected } = this.props;
const { files, selection, selected, setSelected, folders } = this.props;
const headerVisible = selection.length > 0;
const headerIndeterminate =
headerVisible && selection.length > 0 && selection.length < files.length;
const headerChecked = headerVisible && selection.length === files.length;
headerVisible && selection.length > 0 && selection.length < files.length + folders.length;
const headerChecked = headerVisible && selection.length === files.length + folders.length;
/*console.log(`renderGroupButtonMenu()
headerVisible=${headerVisible}
@ -182,6 +182,7 @@ Home.propTypes = {
function mapStateToProps(state) {
return {
files: state.files.files,
folders: state.files.folders,
selection: state.files.selection,
selected: state.files.selected,
isLoaded: state.auth.isLoaded

View File

@ -45,5 +45,11 @@
"TitleModified": "Updated",
"TitleRemoved": "Removed",
"TitleSubfolders": "Flds",
"TitleDocuments": "Dcs"
"TitleDocuments": "Dcs",
"Share": "Share",
"DownloadAs": "Download as",
"More": "More",
"CloseButton": "Close",
"All": "All",
"Files": "Files"
}

View File

@ -39,12 +39,17 @@
"NextPage": "Следующая",
"DefaultOptionLabel": "Я",
"LblSelect": "Выберите",
"AuthorMe": "Я",
"TitleCreated": "Создана",
"TitleUploaded": "Загружен",
"TitleModified": "Обновлён",
"TitleRemoved": "Удалён",
"TitleSubfolders": "Flds",
"TitleDocuments": "Dcs"
"TitleDocuments": "Dcs",
"Share": "Общий доступ",
"DownloadAs": "Скачать как",
"More": "Больше",
"CloseButton": "Закрыть",
"All": "Все",
"Files": "Файлы"
}

View File

@ -24,6 +24,8 @@ export const SET_SELECTED_FOLDER = "SET_SELECTED_FOLDER";
export const SET_ROOT_FOLDERS = "SET_ROOT_FOLDERS";
export const SET_FILES_FILTER = "SET_FILES_FILTER";
export const SET_FILTER = "SET_FILTER";
export const SELECT_FILE = "SELECT_FILE";
export const DESELECT_FILE = "DESELECT_FILE";
export function setFile(file) {
return {
@ -93,6 +95,20 @@ export function setFilter(filter) {
type: SET_FILTER,
filter
};
};
export function selectFile(file) {
return {
type: SELECT_FILE,
file
};
}
export function deselectFile(file) {
return {
type: DESELECT_FILE,
file
};
}
export function setFilterUrl(filter) {

View File

@ -8,9 +8,12 @@ import {
SET_ROOT_FOLDERS,
SET_SELECTED_FOLDER,
SET_SELECTED,
SET_SELECTION
SET_SELECTION,
SELECT_FILE,
DESELECT_FILE
} from "./actions";
import { api } from "asc-web-common";
import { isFileSelected, skipFile, getFilesBySelected } from "./selectors";
const { FilesFilter } = api;
const initialState = {
@ -51,7 +54,8 @@ const filesReducer = (state = initialState, action) => {
});
case SET_SELECTED:
return Object.assign({}, state, {
selected: action.selected
selected: action.selected,
selection: getFilesBySelected(state.files.concat(state.folders), action.selected)
});
case SET_SELECTED_FOLDER:
return Object.assign({}, state, {
@ -69,6 +73,18 @@ const filesReducer = (state = initialState, action) => {
return Object.assign({}, state, {
filter: action.filter
});
case SELECT_FILE:
if (!isFileSelected(state.selection, action.file.id)) {
return Object.assign({}, state, {
selection: [...state.selection, action.file]
});
} else return state;
case DESELECT_FILE:
if (isFileSelected(state.selection, action.file.id)) {
return Object.assign({}, state, {
selection: skipFile(state.selection, action.file.id)
});
} else return state;
default:
return state;
}

View File

@ -1,3 +1,8 @@
import { find, filter } from "lodash";
import { constants } from 'asc-web-common';
const { FileType, FilterType } = constants;
export const getRootFolders = files => {
const { my, share, common, project, trash } = files;
@ -48,3 +53,58 @@ export const canConvert = fileExst => {
const result = convertedDocs.findIndex(item => item === fileExst);
return result === -1 ? false : true;
}
export function getSelectedFile(selection, fileId) {
return find(selection, function (obj) {
return obj.id === fileId;
});
};
export function isFileSelected(selection, fileId) {
return getSelectedFile(selection, fileId) !== undefined;
};
export function skipFile(selection, fileId) {
return filter(selection, function (obj) {
return obj.id !== fileId;
});
};
export function getFilesBySelected(files, selected) {
let newSelection = [];
files.forEach(file => {
const checked = getFilesChecked(file, selected);
if (checked)
newSelection.push(file);
});
return newSelection;
};
const getFilesChecked = (file, selected) => {
const type = file.fileType;
switch (selected) {
case "all":
return true;
case FilterType.FoldersOnly.toString():
return !type;
case FilterType.DocumentsOnly.toString():
return type === FileType.Document;
case FilterType.PresentationsOnly.toString():
return type === FileType.Presentation;
case FilterType.SpreadsheetsOnly.toString():
return type === FileType.Spreadsheet;
case FilterType.ImagesOnly.toString():
return type === FileType.Image;
case FilterType.MediaOnly.toString():
return type === FileType.Video || type === FileType.Audio;
case FilterType.ArchiveOnly.toString():
return type === FileType.Archive;
case FilterType.FilesOnly.toString():
return type;
default:
return false;
}
};