make nav keyboard accessible
This commit is contained in:
parent
7d449e63b9
commit
087f319a8a
2 changed files with 108 additions and 16 deletions
|
@ -12,10 +12,15 @@ class Nav extends React.PureComponent {
|
||||||
this.state = {
|
this.state = {
|
||||||
dropdownOpen: 'none'
|
dropdownOpen: 'none'
|
||||||
};
|
};
|
||||||
|
this.handleFocus = this.handleFocus.bind(this);
|
||||||
|
this.handleBlur = this.handleBlur.bind(this);
|
||||||
|
this.clearHideTimeout = this.clearHideTimeout.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
isUserOwner() {
|
setDropdown(dropdown) {
|
||||||
return this.props.project.owner && this.props.project.owner.id === this.props.user.id;
|
this.setState({
|
||||||
|
dropdownOpen: dropdown
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleDropdown(dropdown) {
|
toggleDropdown(dropdown) {
|
||||||
|
@ -30,6 +35,26 @@ class Nav extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isUserOwner() {
|
||||||
|
return this.props.project.owner && this.props.project.owner.id === this.props.user.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleFocus(dropdown) {
|
||||||
|
this.clearHideTimeout();
|
||||||
|
this.setDropdown(dropdown);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearHideTimeout() {
|
||||||
|
if (this.timer) {
|
||||||
|
clearTimeout(this.timer);
|
||||||
|
this.timer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleBlur() {
|
||||||
|
this.timer = setTimeout(this.setDropdown.bind(this, 'none'), 10);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const navDropdownState = {
|
const navDropdownState = {
|
||||||
file: classNames({
|
file: classNames({
|
||||||
|
@ -62,7 +87,8 @@ class Nav extends React.PureComponent {
|
||||||
<li className={navDropdownState.file}>
|
<li className={navDropdownState.file}>
|
||||||
<button
|
<button
|
||||||
onClick={this.toggleDropdown.bind(this, 'file')}
|
onClick={this.toggleDropdown.bind(this, 'file')}
|
||||||
onBlur={this.toggleDropdown.bind(this, 'none')}
|
onBlur={this.handleBlur}
|
||||||
|
onFocus={this.clearHideTimeout}
|
||||||
>
|
>
|
||||||
<span className="nav__item-header">File</span>
|
<span className="nav__item-header">File</span>
|
||||||
<InlineSVG src={triangleUrl} />
|
<InlineSVG src={triangleUrl} />
|
||||||
|
@ -81,6 +107,8 @@ class Nav extends React.PureComponent {
|
||||||
this.props.newProject();
|
this.props.newProject();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
onFocus={this.handleFocus.bind(this, 'file')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
>
|
>
|
||||||
New
|
New
|
||||||
</button>
|
</button>
|
||||||
|
@ -95,37 +123,57 @@ class Nav extends React.PureComponent {
|
||||||
this.props.showErrorModal('forceAuthentication');
|
this.props.showErrorModal('forceAuthentication');
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
onFocus={this.handleFocus.bind(this, 'file')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</button>
|
</button>
|
||||||
</li> }
|
</li> }
|
||||||
{ this.props.project.id && this.props.user.authenticated &&
|
{ this.props.project.id && this.props.user.authenticated &&
|
||||||
<li className="nav__dropdown-item">
|
<li className="nav__dropdown-item">
|
||||||
<button onClick={this.props.cloneProject}>
|
<button
|
||||||
|
onClick={this.props.cloneProject}
|
||||||
|
onFocus={this.handleFocus.bind(this, 'file')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
>
|
||||||
Duplicate
|
Duplicate
|
||||||
</button>
|
</button>
|
||||||
</li> }
|
</li> }
|
||||||
{ this.props.project.id &&
|
{ this.props.project.id &&
|
||||||
<li className="nav__dropdown-item">
|
<li className="nav__dropdown-item">
|
||||||
<button onClick={this.props.showShareModal}>
|
<button
|
||||||
|
onClick={this.props.showShareModal}
|
||||||
|
onFocus={this.handleFocus.bind(this, 'file')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
>
|
||||||
Share
|
Share
|
||||||
</button>
|
</button>
|
||||||
</li> }
|
</li> }
|
||||||
{ this.props.project.id &&
|
{ this.props.project.id &&
|
||||||
<li className="nav__dropdown-item">
|
<li className="nav__dropdown-item">
|
||||||
<button onClick={() => this.props.exportProjectAsZip(this.props.project.id)}>
|
<button
|
||||||
|
onClick={() => this.props.exportProjectAsZip(this.props.project.id)}
|
||||||
|
onFocus={this.handleFocus.bind(this, 'file')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
>
|
||||||
Download
|
Download
|
||||||
</button>
|
</button>
|
||||||
</li> }
|
</li> }
|
||||||
{ this.props.user.authenticated &&
|
{ this.props.user.authenticated &&
|
||||||
<li className="nav__dropdown-item">
|
<li className="nav__dropdown-item">
|
||||||
<Link to={`/${this.props.user.username}/sketches`}>
|
<Link
|
||||||
|
to={`/${this.props.user.username}/sketches`}
|
||||||
|
onFocus={this.handleFocus.bind(this, 'file')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
>
|
||||||
Open
|
Open
|
||||||
</Link>
|
</Link>
|
||||||
</li> }
|
</li> }
|
||||||
<li className="nav__dropdown-item">
|
<li className="nav__dropdown-item">
|
||||||
<Link
|
<Link
|
||||||
to="/p5/sketches"
|
to="/p5/sketches"
|
||||||
|
onFocus={this.handleFocus.bind(this, 'file')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
>
|
>
|
||||||
Examples
|
Examples
|
||||||
</Link>
|
</Link>
|
||||||
|
@ -133,7 +181,11 @@ class Nav extends React.PureComponent {
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li className={navDropdownState.edit}>
|
<li className={navDropdownState.edit}>
|
||||||
<button onClick={this.toggleDropdown.bind(this, 'edit')}>
|
<button
|
||||||
|
onClick={this.toggleDropdown.bind(this, 'edit')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
onFocus={this.clearHideTimeout}
|
||||||
|
>
|
||||||
<span className="nav__item-header">Edit</span>
|
<span className="nav__item-header">Edit</span>
|
||||||
<InlineSVG src={triangleUrl} />
|
<InlineSVG src={triangleUrl} />
|
||||||
</button>
|
</button>
|
||||||
|
@ -151,7 +203,11 @@ class Nav extends React.PureComponent {
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li className={navDropdownState.sketch}>
|
<li className={navDropdownState.sketch}>
|
||||||
<button onClick={this.toggleDropdown.bind(this, 'sketch')}>
|
<button
|
||||||
|
onClick={this.toggleDropdown.bind(this, 'sketch')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
onFocus={this.clearHideTimeout}
|
||||||
|
>
|
||||||
<span className="nav__item-header">Sketch</span>
|
<span className="nav__item-header">Sketch</span>
|
||||||
<InlineSVG src={triangleUrl} />
|
<InlineSVG src={triangleUrl} />
|
||||||
</button>
|
</button>
|
||||||
|
@ -169,7 +225,11 @@ class Nav extends React.PureComponent {
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li className={navDropdownState.help}>
|
<li className={navDropdownState.help}>
|
||||||
<button onClick={this.toggleDropdown.bind(this, 'help')}>
|
<button
|
||||||
|
onClick={this.toggleDropdown.bind(this, 'help')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
onFocus={this.clearHideTimeout}
|
||||||
|
>
|
||||||
<span className="nav__item-header">Help</span>
|
<span className="nav__item-header">Help</span>
|
||||||
<InlineSVG src={triangleUrl} />
|
<InlineSVG src={triangleUrl} />
|
||||||
</button>
|
</button>
|
||||||
|
@ -186,10 +246,16 @@ class Nav extends React.PureComponent {
|
||||||
href="https://p5js.org/reference/"
|
href="https://p5js.org/reference/"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
|
onFocus={this.handleFocus.bind(this, 'help')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
>Reference</a>
|
>Reference</a>
|
||||||
</li>
|
</li>
|
||||||
<li className="nav__dropdown-item">
|
<li className="nav__dropdown-item">
|
||||||
<Link to="/about">
|
<Link
|
||||||
|
to="/about"
|
||||||
|
onFocus={this.handleFocus.bind(this, 'help')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
>
|
||||||
About
|
About
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
|
@ -216,6 +282,8 @@ class Nav extends React.PureComponent {
|
||||||
<button
|
<button
|
||||||
className="nav__item-header"
|
className="nav__item-header"
|
||||||
onClick={this.toggleDropdown.bind(this, 'account')}
|
onClick={this.toggleDropdown.bind(this, 'account')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
onFocus={this.clearHideTimeout}
|
||||||
>
|
>
|
||||||
My Account
|
My Account
|
||||||
</button>
|
</button>
|
||||||
|
@ -226,22 +294,38 @@ class Nav extends React.PureComponent {
|
||||||
<InlineSVG src={triangleUrl} />
|
<InlineSVG src={triangleUrl} />
|
||||||
</li>
|
</li>
|
||||||
<li className="nav__dropdown-item">
|
<li className="nav__dropdown-item">
|
||||||
<Link to={`/${this.props.user.username}/sketches`}>
|
<Link
|
||||||
|
to={`/${this.props.user.username}/sketches`}
|
||||||
|
onFocus={this.handleFocus.bind(this, 'account')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
>
|
||||||
My sketches
|
My sketches
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li className="nav__dropdown-item">
|
<li className="nav__dropdown-item">
|
||||||
<Link to={`/${this.props.user.username}/assets`}>
|
<Link
|
||||||
|
to={`/${this.props.user.username}/assets`}
|
||||||
|
onFocus={this.handleFocus.bind(this, 'account')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
>
|
||||||
My assets
|
My assets
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li className="nav__dropdown-item">
|
<li className="nav__dropdown-item">
|
||||||
<Link to={`/${this.props.user.username}/account`}>
|
<Link
|
||||||
|
to={`/${this.props.user.username}/account`}
|
||||||
|
onFocus={this.handleFocus.bind(this, 'account')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
>
|
||||||
Settings
|
Settings
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li className="nav__dropdown-item">
|
<li className="nav__dropdown-item">
|
||||||
<button onClick={this.props.logoutUser} >
|
<button
|
||||||
|
onClick={this.props.logoutUser}
|
||||||
|
onFocus={this.handleFocus.bind(this, 'account')}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
>
|
||||||
Log out
|
Log out
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -152,6 +152,14 @@ class PreviewFrame extends React.Component {
|
||||||
doc.srcDoc = '';
|
doc.srcDoc = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addLoopProtect(sketchDoc) {
|
||||||
|
const scriptsInHTML = sketchDoc.getElementsByTagName('script');
|
||||||
|
const scriptsInHTMLArray = Array.prototype.slice.call(scriptsInHTML);
|
||||||
|
scriptsInHTMLArray.forEach((script) => {
|
||||||
|
script.innerHTML = loopProtect(script.innerHTML); // eslint-disable-line
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
injectLocalFiles() {
|
injectLocalFiles() {
|
||||||
const htmlFile = this.props.htmlFile.content;
|
const htmlFile = this.props.htmlFile.content;
|
||||||
let scriptOffs = [];
|
let scriptOffs = [];
|
||||||
|
@ -227,7 +235,7 @@ class PreviewFrame extends React.Component {
|
||||||
scriptOffs = getAllScriptOffsets(sketchDocString);
|
scriptOffs = getAllScriptOffsets(sketchDocString);
|
||||||
const consoleErrorsScript = sketchDoc.createElement('script');
|
const consoleErrorsScript = sketchDoc.createElement('script');
|
||||||
consoleErrorsScript.innerHTML = hijackConsoleErrorsScript(JSON.stringify(scriptOffs));
|
consoleErrorsScript.innerHTML = hijackConsoleErrorsScript(JSON.stringify(scriptOffs));
|
||||||
// sketchDoc.head.appendChild(consoleErrorsScript);
|
this.addLoopProtect(sketchDoc);
|
||||||
sketchDoc.head.insertBefore(consoleErrorsScript, sketchDoc.head.firstElement);
|
sketchDoc.head.insertBefore(consoleErrorsScript, sketchDoc.head.firstElement);
|
||||||
|
|
||||||
return `<!DOCTYPE HTML>\n${sketchDoc.documentElement.outerHTML}`;
|
return `<!DOCTYPE HTML>\n${sketchDoc.documentElement.outerHTML}`;
|
||||||
|
|
Loading…
Reference in a new issue