mirror of
https://github.com/Hopiu/wagtail.git
synced 2026-05-15 18:53:18 +00:00
Make explorer tab-navigable
* use focus-trap-react * switch clickable span to button * let focus trap handle outside click
This commit is contained in:
parent
81c6f3e3b1
commit
3f85c5fed5
6 changed files with 94 additions and 93 deletions
|
|
@ -6,6 +6,7 @@ const PageCount = ({ id, count, title }) => (
|
|||
<a
|
||||
href={`${ADMIN_URLS.PAGES}${id}/`}
|
||||
className="c-explorer__see-more"
|
||||
tabIndex={0}
|
||||
>
|
||||
{STRINGS.EXPLORE_ALL_IN}{' '}
|
||||
<span className="c-explorer__see-more__title">{title}</span>{' '}
|
||||
|
|
|
|||
|
|
@ -16,10 +16,14 @@ const ExplorerHeader = ({ page, depth, onPop, transName }) => {
|
|||
className: 'c-explorer__rel',
|
||||
};
|
||||
|
||||
// TODO Do not use a span for a clickable element.
|
||||
return (
|
||||
<div className="c-explorer__header">
|
||||
<span className={`c-explorer__trigger${depth > 1 ? ' c-explorer__trigger--enabled' : ''}`} onClick={onPop}>
|
||||
<button
|
||||
role="button"
|
||||
className={`c-explorer__trigger${depth > 1 ? ' c-explorer__trigger--enabled' : ''}`}
|
||||
onClick={onPop}
|
||||
tabIndex={depth === 1 ? -1 : 0}
|
||||
>
|
||||
<span className="u-overflow c-explorer__overflow">
|
||||
<CSSTransitionGroup {...transitionProps}>
|
||||
<span className="c-explorer__parent-name" key={depth}>
|
||||
|
|
@ -32,7 +36,7 @@ const ExplorerHeader = ({ page, depth, onPop, transName }) => {
|
|||
</span>
|
||||
</CSSTransitionGroup>
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -21,23 +21,23 @@ const ExplorerItem = ({ title, typeName, data, onItemClick }) => {
|
|||
const hasChildren = count > 0;
|
||||
|
||||
return (
|
||||
<Button href={`${ADMIN_URLS.PAGES}${id}`} className="c-explorer__item">
|
||||
<div className="c-explorer__item">
|
||||
{hasChildren ? (
|
||||
<span
|
||||
role="button"
|
||||
className="c-explorer__children"
|
||||
<button
|
||||
type="button"
|
||||
className="c-explorer__item__children"
|
||||
onClick={onItemClick.bind(null, id)}
|
||||
>
|
||||
<Icon name="arrow-right" title={STRINGS.SEE_CHILDREN} />
|
||||
</span>
|
||||
</button>
|
||||
) : null}
|
||||
|
||||
<h3 className="c-explorer__title">{title}</h3>
|
||||
|
||||
<p className="c-explorer__meta">
|
||||
<span className="c-explorer__meta__type">{typeName}</span> | <AbsoluteDate time={time} /> | <PublicationStatus status={status} />
|
||||
</p>
|
||||
</Button>
|
||||
<Button href={`${ADMIN_URLS.PAGES}${id}`} className="c-explorer__item__link">
|
||||
<h3 className="c-explorer__title">{title}</h3>
|
||||
<p className="c-explorer__meta">
|
||||
<span className="c-explorer__meta__type">{typeName}</span> | <AbsoluteDate time={time} /> | <PublicationStatus status={status} />
|
||||
</p>
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import React from 'react';
|
||||
import CSSTransitionGroup from 'react-addons-css-transition-group';
|
||||
import FocusTrap from 'focus-trap-react'
|
||||
|
||||
import { EXPLORER_ANIM_DURATION } from '../../config/config';
|
||||
import { STRINGS } from '../../config/wagtail';
|
||||
|
|
@ -13,7 +14,6 @@ import PageCount from './PageCount';
|
|||
export default class ExplorerPanel extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.clickOutside = this.clickOutside.bind(this);
|
||||
this.onItemClick = this.onItemClick.bind(this);
|
||||
|
||||
this.state = {
|
||||
|
|
@ -51,34 +51,11 @@ export default class ExplorerPanel extends React.Component {
|
|||
|
||||
componentDidMount() {
|
||||
this.props.init();
|
||||
|
||||
document.body.classList.add('explorer-open');
|
||||
document.addEventListener('click', this.clickOutside);
|
||||
var Anchors = document.getElementsByTagName("a");
|
||||
for (var i = 0; i < Anchors.length ; i++) {
|
||||
Anchors[i].addEventListener("click", this.clickOutside)
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.body.classList.remove('explorer-open');
|
||||
document.removeEventListener('click', this.clickOutside);
|
||||
var Anchors = document.getElementsByTagName("a");
|
||||
for (var i = 0; i < Anchors.length ; i++) {
|
||||
Anchors[i].removeEventListener("click", this.clickOutside)
|
||||
}
|
||||
}
|
||||
|
||||
clickOutside(e) {
|
||||
const { explorer } = this.refs;
|
||||
|
||||
if (!explorer) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!explorer.contains(e.target)) {
|
||||
this.props.onClose();
|
||||
}
|
||||
}
|
||||
|
||||
getClass() {
|
||||
|
|
@ -189,26 +166,36 @@ export default class ExplorerPanel extends React.Component {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className={this.getClass()} ref="explorer">
|
||||
<ExplorerHeader {...headerProps} transName={this.state.animation} />
|
||||
<div className="c-explorer__drawer">
|
||||
<CSSTransitionGroup {...transitionProps}>
|
||||
<div {...transitionTargetProps}>
|
||||
<CSSTransitionGroup {...innerTransitionProps}>
|
||||
{page.isFetching ? <LoadingSpinner key={1} /> : (
|
||||
<div key={0}>
|
||||
{this.getContents()}
|
||||
{(page.children.count > page.children.items.length) && (
|
||||
<PageCount id={page.id} count={page.meta.children.count} title={page.title} />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</CSSTransitionGroup>
|
||||
<FocusTrap
|
||||
paused={page.isFetching}
|
||||
focusTrapOptions={{
|
||||
onDeactivate: this.props.onClose,
|
||||
clickOutsideDeactivates: true,
|
||||
}}
|
||||
>
|
||||
{/* FocusTrap gets antsy while the page is loading, so we give it something to focus on. */}
|
||||
{page.isFetching && <div tabIndex={0} />}
|
||||
<div className={this.getClass()} tabIndex={-1}>
|
||||
<ExplorerHeader {...headerProps} transName={this.state.animation} />
|
||||
<div className="c-explorer__drawer">
|
||||
<CSSTransitionGroup {...transitionProps}>
|
||||
<div {...transitionTargetProps}>
|
||||
<CSSTransitionGroup {...innerTransitionProps}>
|
||||
{page.isFetching ? <LoadingSpinner key={1} /> : (
|
||||
<div key={0}>
|
||||
{this.getContents()}
|
||||
{(page.children.count > page.children.items.length) && (
|
||||
<PageCount id={page.id} count={page.meta.children.count} title={page.title} />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</CSSTransitionGroup>
|
||||
|
||||
</div>
|
||||
</CSSTransitionGroup>
|
||||
</div>
|
||||
</CSSTransitionGroup>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</FocusTrap>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,15 +32,21 @@ $c-explorer-easing: cubic-bezier(0.075, 0.820, 0.165, 1.000);
|
|||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
float: left;
|
||||
background: none;
|
||||
border: none;
|
||||
text-align: left;
|
||||
color: $c-explorer-secondary;
|
||||
line-height: inherit;
|
||||
font: inherit;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.c-explorer__trigger--enabled {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
&:hover, &:focus {
|
||||
color: $color-white;
|
||||
background: rgba(0,0,0,0.2);
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -70,16 +76,6 @@ $c-explorer-easing: cubic-bezier(0.075, 0.820, 0.165, 1.000);
|
|||
padding: 1rem;
|
||||
}
|
||||
|
||||
.c-explorer__item {
|
||||
padding: 1rem;
|
||||
cursor: pointer;
|
||||
border-bottom: solid 1px #676767;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.c-explorer__placeholder {
|
||||
padding: 1rem;
|
||||
color: $color-white;
|
||||
|
|
@ -99,10 +95,42 @@ $c-explorer-easing: cubic-bezier(0.075, 0.820, 0.165, 1.000);
|
|||
.c-explorer__item {
|
||||
display: block;
|
||||
position: relative;
|
||||
border-bottom: solid 1px #676767;
|
||||
&:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.c-explorer__item__link {
|
||||
display: block;
|
||||
padding: 1rem;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover, &:focus {
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
color: $color-white;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.c-explorer__item__children {
|
||||
display: inline-block;
|
||||
color: $color-white;
|
||||
line-height: 1;
|
||||
padding: .7em .3em .7em .7em;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
font-size: 2em;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
&:hover, &:focus {
|
||||
background: rgba(0,0,0,0.5);
|
||||
color: $color-white;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -112,9 +140,10 @@ $c-explorer-easing: cubic-bezier(0.075, 0.820, 0.165, 1.000);
|
|||
color: $c-explorer-secondary;
|
||||
display: block;
|
||||
|
||||
&:hover {
|
||||
&:hover, &:focus {
|
||||
color: $c-explorer-secondary;
|
||||
background: rgba(0,0,0,0.4);
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -122,27 +151,6 @@ $c-explorer-easing: cubic-bezier(0.075, 0.820, 0.165, 1.000);
|
|||
color: $color-white;
|
||||
}
|
||||
|
||||
.c-explorer__children {
|
||||
display: inline-block;
|
||||
color: $color-white;
|
||||
line-height: 1;
|
||||
padding: .7em .3em .7em .7em;
|
||||
float: right;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
font-size: 2em;
|
||||
|
||||
|
||||
&:hover {
|
||||
background: rgba(0,0,0,0.5);
|
||||
color: $color-white;
|
||||
}
|
||||
}
|
||||
|
||||
.c-status {
|
||||
background: $color-grey-1;
|
||||
color: $c-explorer-secondary;
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@
|
|||
"webpack": "^1.12.14"
|
||||
},
|
||||
"dependencies": {
|
||||
"focus-trap-react": "^3.0.2",
|
||||
"lodash": "^4.17.4",
|
||||
"moment": "^2.17.1",
|
||||
"react": "^15.4.2",
|
||||
|
|
|
|||
Loading…
Reference in a new issue