1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-08 18:30:53 +00:00

[a11y] added 'keyboard access' for 'dir view - dirent' & fixed bugs

This commit is contained in:
llj
2021-09-29 15:23:02 +08:00
parent b5d3a6cf6b
commit aef44a8605
8 changed files with 70 additions and 111 deletions

View File

@@ -9,10 +9,6 @@ const propTypes = {
class AboutDialog extends React.Component { class AboutDialog extends React.Component {
toggle = () => {
this.props.onCloseAboutDialog();
}
renderExternalAboutLinks = () => { renderExternalAboutLinks = () => {
if (additionalAboutDialogLinks && (typeof additionalAboutDialogLinks) === 'object') { if (additionalAboutDialogLinks && (typeof additionalAboutDialogLinks) === 'object') {
let keys = Object.keys(additionalAboutDialogLinks); let keys = Object.keys(additionalAboutDialogLinks);
@@ -26,21 +22,22 @@ class AboutDialog extends React.Component {
render() { render() {
let href = lang === lang == 'zh-cn' ? 'http://seafile.com/about/' : 'http://seafile.com/en/about/'; let href = lang === lang == 'zh-cn' ? 'http://seafile.com/about/' : 'http://seafile.com/en/about/';
const { onCloseAboutDialog: toggleDialog } = this.props;
if (aboutDialogCustomHtml) { if (aboutDialogCustomHtml) {
return ( return (
<Modal isOpen={true} toggle={this.toggle}> <Modal isOpen={true} toggle={toggleDialog}>
<ModalBody> <ModalBody>
<button type="button" className="close" onClick={this.toggle}><span aria-hidden="true">×</span></button> <button type="button" className="close" onClick={toggleDialog}><span aria-hidden="true">×</span></button>
<div className="about-content" dangerouslySetInnerHTML={{__html: aboutDialogCustomHtml}}></div> <div className="about-content" dangerouslySetInnerHTML={{__html: aboutDialogCustomHtml}}></div>
</ModalBody> </ModalBody>
</Modal> </Modal>
); );
} else { } else {
return ( return (
<Modal isOpen={true} toggle={this.toggle}> <Modal isOpen={true} toggle={toggleDialog}>
<ModalBody> <ModalBody>
<button type="button" className="close" onClick={this.toggle}><span aria-hidden="true">×</span></button> <button type="button" className="close" onClick={toggleDialog}><span aria-hidden="true">×</span></button>
<div className="about-content"> <div className="about-content">
<p><img src={mediaUrl + logoPath} height={logoHeight} width={logoWidth} title={siteTitle} alt="logo" /></p> <p><img src={mediaUrl + logoPath} height={logoHeight} width={logoWidth} title={siteTitle} alt="logo" /></p>
<p>{gettext('Server Version: ')}{seafileVersion}<br />© {(new Date()).getFullYear()} {gettext('Seafile')}</p> <p>{gettext('Server Version: ')}{seafileVersion}<br />© {(new Date()).getFullYear()} {gettext('Seafile')}</p>

View File

@@ -26,10 +26,6 @@ class DeleteRepoDialog extends Component {
} }
} }
toggle = () => {
this.props.toggle();
}
onDeleteRepo = () => { onDeleteRepo = () => {
this.setState({isRequestSended: true}, () => { this.setState({isRequestSended: true}, () => {
this.props.onDeleteRepo(this.props.repo); this.props.onDeleteRepo(this.props.repo);
@@ -44,14 +40,16 @@ class DeleteRepoDialog extends Component {
let message = gettext('Are you sure you want to delete %s ?'); let message = gettext('Are you sure you want to delete %s ?');
message = message.replace('%s', repoName); message = message.replace('%s', repoName);
const { toggle: toggleDialog } = this.props;
return ( return (
<Modal isOpen={true} toggle={this.toggle}> <Modal isOpen={true} toggle={toggleDialog}>
<ModalHeader toggle={this.toggle}>{gettext('Delete Library')}</ModalHeader> <ModalHeader toggle={toggleDialog}>{gettext('Delete Library')}</ModalHeader>
<ModalBody> <ModalBody>
<p dangerouslySetInnerHTML={{__html: message}}></p> <p dangerouslySetInnerHTML={{__html: message}}></p>
</ModalBody> </ModalBody>
<ModalFooter> <ModalFooter>
<Button color="secondary" onClick={this.toggle}>{gettext('Cancel')}</Button> <Button color="secondary" onClick={toggleDialog}>{gettext('Cancel')}</Button>
<Button color="primary" disabled={isRequestSended} onClick={this.onDeleteRepo}>{gettext('Delete')}</Button> <Button color="primary" disabled={isRequestSended} onClick={this.onDeleteRepo}>{gettext('Delete')}</Button>
</ModalFooter> </ModalFooter>
</Modal> </Modal>

View File

@@ -154,11 +154,13 @@ class DirentListItem extends React.Component {
this.props.onItemSelected(this.props.dirent); this.props.onItemSelected(this.props.dirent);
} }
onItemStarred = () => { onItemStarred = (e) => {
let dirent = this.props.dirent; let dirent = this.props.dirent;
let repoID = this.props.repoID; let repoID = this.props.repoID;
let filePath = this.getDirentPath(dirent); let filePath = this.getDirentPath(dirent);
e.preventDefault();
if (dirent.starred) { if (dirent.starred) {
seafileAPI.unstarItem(repoID, filePath).then(() => { seafileAPI.unstarItem(repoID, filePath).then(() => {
this.props.updateDirent(this.props.dirent, 'starred', false); this.props.updateDirent(this.props.dirent, 'starred', false);
@@ -209,11 +211,13 @@ class DirentListItem extends React.Component {
} }
onItemDelete = (e) => { onItemDelete = (e) => {
e.preventDefault();
e.nativeEvent.stopImmediatePropagation(); //for document event e.nativeEvent.stopImmediatePropagation(); //for document event
this.props.onItemDelete(this.props.dirent); this.props.onItemDelete(this.props.dirent);
} }
onItemShare = (e) => { onItemShare = (e) => {
e.preventDefault();
e.nativeEvent.stopImmediatePropagation(); //for document event e.nativeEvent.stopImmediatePropagation(); //for document event
this.setState({isShareDialogShow: !this.state.isShareDialogShow}); this.setState({isShareDialogShow: !this.state.isShareDialogShow});
} }
@@ -382,6 +386,7 @@ class DirentListItem extends React.Component {
} }
onItemDownload = (e) => { onItemDownload = (e) => {
e.preventDefault();
e.nativeEvent.stopImmediatePropagation(); e.nativeEvent.stopImmediatePropagation();
let dirent = this.props.dirent; let dirent = this.props.dirent;
let repoID = this.props.repoID; let repoID = this.props.repoID;
@@ -570,88 +575,48 @@ class DirentListItem extends React.Component {
<Fragment> <Fragment>
{this.state.isOperationShow && !dirent.isSelected && {this.state.isOperationShow && !dirent.isSelected &&
<div className="operations"> <div className="operations">
<ul className="operation-group"> {(dirent.permission === 'rw' || dirent.permission === 'r' || (isCustomPermission && canDownload)) && (
{(dirent.permission === 'rw' || dirent.permission === 'r') && ( <a href="#" className="op-icon sf2-icon-download" title={gettext('Download')} role="button" aria-label={gettext('Download')} onClick={this.onItemDownload}></a>
<li className="operation-group-item"> )}
<i className="op-icon sf2-icon-download" title={gettext('Download')} onClick={this.onItemDownload}></i> {showShareBtn && (
</li> <a href="#" className="op-icon sf2-icon-share" title={gettext('Share')} role="button" aria-label={gettext('Share')} onClick={this.onItemShare}></a>
)} )}
{(isCustomPermission && canDownload) && ( {(dirent.permission === 'rw' || (isCustomPermission && canDelete)) && (
<li className="operation-group-item"> <a href="#" className="op-icon sf2-icon-delete" title={gettext('Delete')} role="button" aria-label={gettext('Delete')} onClick={this.onItemDelete}></a>
<i className="op-icon sf2-icon-download" title={gettext('Download')} onClick={this.onItemDownload}></i> )}
</li> <ItemDropdownMenu
)} item={this.props.dirent}
{showShareBtn && ( toggleClass={'sf2-icon-caret-down'}
<li className="operation-group-item"> isHandleContextMenuEvent={true}
<i className="op-icon sf2-icon-share" title={gettext('Share')} onClick={this.onItemShare}></i> getMenuList={this.props.getDirentItemMenuList}
</li> onMenuItemClick={this.onMenuItemClick}
)} unfreezeItem={this.unfreezeItem}
{dirent.permission === 'rw' && ( freezeItem={this.props.freezeItem}
<li className="operation-group-item"> />
<i className="op-icon sf2-icon-delete" title={gettext('Delete')} onClick={this.onItemDelete}></i>
</li>
)}
{(isCustomPermission && canDelete) && (
<li className="operation-group-item">
<i className="op-icon sf2-icon-delete" title={gettext('Delete')} onClick={this.onItemDelete}></i>
</li>
)}
<li className="operation-group-item">
<ItemDropdownMenu
item={this.props.dirent}
toggleClass={'sf2-icon-caret-down'}
isHandleContextMenuEvent={true}
getMenuList={this.props.getDirentItemMenuList}
onMenuItemClick={this.onMenuItemClick}
unfreezeItem={this.unfreezeItem}
freezeItem={this.props.freezeItem}
/>
</li>
</ul>
</div> </div>
} }
</Fragment> : </Fragment> :
<Fragment> <Fragment>
{this.state.isOperationShow && {this.state.isOperationShow &&
<div className="operations"> <div className="operations">
<ul className="operation-group"> {(dirent.permission === 'rw' || dirent.permission === 'r' || (isCustomPermission && canDownload)) && (
{(dirent.permission === 'rw' || dirent.permission === 'r') && ( <a href="#" className="op-icon sf2-icon-download" title={gettext('Download')} role="button" aria-label={gettext('Download')} onClick={this.onItemDownload}></a>
<li className="operation-group-item"> )}
<i className="op-icon sf2-icon-download" title={gettext('Download')} onClick={this.onItemDownload}></i> {showShareBtn && (
</li> <a href="#" className="op-icon sf2-icon-share" title={gettext('Share')} role="button" aria-label={gettext('Share')} onClick={this.onItemShare}></a>
)} )}
{(isCustomPermission && canDownload) && ( {(dirent.permission === 'rw' || (isCustomPermission && canDelete)) && (
<li className="operation-group-item"> <a href="#" className="op-icon sf2-icon-delete" title={gettext('Delete')} role="button" aria-label={gettext('Delete')} onClick={this.onItemDelete}></a>
<i className="op-icon sf2-icon-download" title={gettext('Download')} onClick={this.onItemDownload}></i> )}
</li> <ItemDropdownMenu
)} item={this.props.dirent}
{showShareBtn && ( toggleClass={'sf2-icon-caret-down'}
<li className="operation-group-item"> isHandleContextMenuEvent={true}
<i className="op-icon sf2-icon-share" title={gettext('Share')} onClick={this.onItemShare}></i> getMenuList={this.props.getDirentItemMenuList}
</li> onMenuItemClick={this.onMenuItemClick}
)} unfreezeItem={this.unfreezeItem}
{(dirent.permission === 'rw') && ( freezeItem={this.props.freezeItem}
<li className="operation-group-item"> />
<i className="op-icon sf2-icon-delete" title={gettext('Delete')} onClick={this.onItemDelete}></i>
</li>
)}
{(isCustomPermission && canDelete) && (
<li className="operation-group-item">
<i className="op-icon sf2-icon-delete" title={gettext('Delete')} onClick={this.onItemDelete}></i>
</li>
)}
<li className="operation-group-item">
<ItemDropdownMenu
item={this.props.dirent}
toggleClass={'sf2-icon-caret-down'}
isHandleContextMenuEvent={true}
getMenuList={this.props.getDirentItemMenuList}
onMenuItemClick={this.onMenuItemClick}
unfreezeItem={this.unfreezeItem}
freezeItem={this.props.freezeItem}
/>
</li>
</ul>
</div> </div>
} }
</Fragment> </Fragment>
@@ -690,6 +655,7 @@ class DirentListItem extends React.Component {
<tr <tr
className={trClass} className={trClass}
draggable={this.canDrag} draggable={this.canDrag}
onFocus={this.onMouseEnter}
onMouseEnter={this.onMouseEnter} onMouseEnter={this.onMouseEnter}
onMouseOver={this.onMouseOver} onMouseOver={this.onMouseOver}
onMouseLeave={this.onMouseLeave} onMouseLeave={this.onMouseLeave}
@@ -706,8 +672,11 @@ class DirentListItem extends React.Component {
<input type="checkbox" className="vam" onChange={this.onItemSelected} checked={dirent.isSelected}/> <input type="checkbox" className="vam" onChange={this.onItemSelected} checked={dirent.isSelected}/>
</td> </td>
<td className="pl10"> <td className="pl10">
{dirent.starred !== undefined && !dirent.starred && <i className="far fa-star star-empty cursor-pointer" onClick={this.onItemStarred}></i>} {dirent.starred !== undefined &&
{dirent.starred !== undefined && dirent.starred && <i className="fas fa-star cursor-pointer" onClick={this.onItemStarred}></i>} <a href="#" role="button" aria-label={dirent.starred ? gettext('Unstar') : gettext('Star')} onClick={this.onItemStarred}>
<i className={`fa-star ${dirent.starred ? 'fas' : 'far star-empty'}`}></i>
</a>
}
</td> </td>
<td className="pl10"> <td className="pl10">
<div className="dir-icon"> <div className="dir-icon">
@@ -796,6 +765,8 @@ class DirentListItem extends React.Component {
return ( return (
<DropdownItem className="mobile-menu-item" key={index} data-op={item.key} onClick={this.onMobileMenuItemClick}>{item.value}</DropdownItem> <DropdownItem className="mobile-menu-item" key={index} data-op={item.key} onClick={this.onMobileMenuItemClick}>{item.value}</DropdownItem>
); );
} else {
return null;
} }
})} })}
</div> </div>

View File

@@ -589,7 +589,7 @@ class DirentListView extends React.Component {
<thead onMouseDown={this.onThreadMouseDown} onContextMenu={this.onThreadContextMenu}> <thead onMouseDown={this.onThreadMouseDown} onContextMenu={this.onThreadContextMenu}>
<tr> <tr>
<th width="3%" className="pl10"> <th width="3%" className="pl10">
<input type="checkbox" className="vam" onChange={this.props.onAllItemSelected} checked={this.props.isAllItemSelected}/> <input type="checkbox" className="vam" onChange={this.props.onAllItemSelected} checked={this.props.isAllItemSelected} />
</th> </th>
<th width="3%" className="pl10">{/*icon */}</th> <th width="3%" className="pl10">{/*icon */}</th>
<th width="5%" className="pl10">{/*star */}</th> <th width="5%" className="pl10">{/*star */}</th>

View File

@@ -146,6 +146,8 @@ class ItemDropdownMenu extends React.Component {
<Dropdown isOpen={this.state.isItemMenuShow} toggle={this.onDropdownToggleClick}> <Dropdown isOpen={this.state.isItemMenuShow} toggle={this.onDropdownToggleClick}>
<DropdownToggle <DropdownToggle
tag={tagName || 'i'} tag={tagName || 'i'}
role="button"
tabIndex="0"
className={toggleClass} className={toggleClass}
title={gettext('More Operations')} title={gettext('More Operations')}
data-toggle="dropdown" data-toggle="dropdown"

View File

@@ -13,9 +13,7 @@ class SideNavFooter extends React.Component {
} }
onAboutDialogToggle = (e) => { onAboutDialogToggle = (e) => {
if (e) { e.preventDefault();
e.preventDefault();
}
this.setState({isAboutDialogShow: !this.state.isAboutDialogShow}); this.setState({isAboutDialogShow: !this.state.isAboutDialogShow});
} }

View File

@@ -1,15 +1,6 @@
.tr-drop-effect { .tr-drop-effect {
background-color: #f8f8f8; background-color: #f8f8f8;
} }
.operations {
display: flex;
}
.operations .operation-group {
list-style: none;
}
.operation-group .operation-group-item {
display: inline-block;
}
.star-empty { .star-empty {
color: #d0d0d0; color: #d0d0d0;
@@ -31,4 +22,4 @@
.tag-list-title { .tag-list-title {
overflow: hidden; overflow: hidden;
} }

View File

@@ -146,13 +146,15 @@ class MylibRepoMenu extends React.Component {
return ( return (
<Dropdown isOpen={this.state.isItemMenuShow} toggle={this.toggleOperationMenu}> <Dropdown isOpen={this.state.isItemMenuShow} toggle={this.toggleOperationMenu}>
<DropdownToggle <DropdownToggle
className="sf-dropdown-toggle sf2-icon-caret-down border-0 p-0" tag="i"
role="button"
tabIndex="0"
className="sf-dropdown-toggle sf2-icon-caret-down"
title={gettext('More Operations')} title={gettext('More Operations')}
aria-label={gettext('More Operations')}
onClick={this.onDropdownToggleClick} onClick={this.onDropdownToggleClick}
onKeyDown={this.onDropdownToggleKeyDown} onKeyDown={this.onDropdownToggleKeyDown}
data-toggle="dropdown" data-toggle="dropdown"
aria-haspopup={true}
style={{'minWidth': '0'}}
/> />
<DropdownMenu> <DropdownMenu>
{operations.map((item, index)=> { {operations.map((item, index)=> {