mirror of
https://github.com/movie-web/movie-web.git
synced 2024-12-25 07:11:50 +01:00
Merge pull request #51 from sammce/accessibility
Feature - add keyboard navigation support
This commit is contained in:
commit
9cf4e77194
@ -60,6 +60,15 @@
|
|||||||
transform: translateX(.3rem) translateY(.1rem);
|
transform: translateX(.3rem) translateY(.1rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.movieRow:focus-visible {
|
||||||
|
border: 1px solid #fff;
|
||||||
|
background-color: var(--content-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.movieRow:focus-visible .watch .arrow {
|
||||||
|
transform: translateX(.3rem) translateY(.1rem);
|
||||||
|
}
|
||||||
|
|
||||||
.attribute {
|
.attribute {
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
background-color: var(--theme-color);
|
background-color: var(--theme-color);
|
||||||
|
@ -19,8 +19,14 @@ export function MovieRow(props) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleKeyPress(event){
|
||||||
|
if ((event.code === 'Enter' || event.code === 'Space') && props.onClick){
|
||||||
|
props.onClick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="movieRow" onClick={() => props.onClick && props.onClick()}>
|
<div className="movieRow" tabIndex={0} onKeyPress={handleKeyPress} onClick={() => props.onClick && props.onClick()}>
|
||||||
|
|
||||||
{ props.source === "lookmovie" && (
|
{ props.source === "lookmovie" && (
|
||||||
<div className="subtitleIcon">
|
<div className="subtitleIcon">
|
||||||
|
@ -39,10 +39,15 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.numberSelector .choice:hover {
|
.numberSelector .choice:hover,
|
||||||
|
.numberSelector .choiceWrapper:focus-visible .choice {
|
||||||
background-color: var(--choice-hover);
|
background-color: var(--choice-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.numberSelector .choiceWrapper:focus-visible {
|
||||||
|
border: 1px solid #fff;
|
||||||
|
}
|
||||||
|
|
||||||
.numberSelector .choice.selected {
|
.numberSelector .choice.selected {
|
||||||
color: var(--choice-active-text, var(--text));
|
color: var(--choice-active-text, var(--text));
|
||||||
background-color: var(--choice-active);
|
background-color: var(--choice-active);
|
||||||
|
@ -7,10 +7,15 @@ import { PercentageOverlay } from './PercentageOverlay';
|
|||||||
// choices: { label: string, value: string }[]
|
// choices: { label: string, value: string }[]
|
||||||
// selected: string
|
// selected: string
|
||||||
export function NumberSelector({ setType, choices, selected }) {
|
export function NumberSelector({ setType, choices, selected }) {
|
||||||
|
const handleKeyPress = choice => event => {
|
||||||
|
if (event.code === 'Space' || event.code === 'Enter'){
|
||||||
|
setType(choice);
|
||||||
|
}
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div className="numberSelector">
|
<div className="numberSelector">
|
||||||
{choices.map(v=>(
|
{choices.map(v=>(
|
||||||
<div key={v.value} className="choiceWrapper">
|
<div key={v.value} className="choiceWrapper" tabIndex={0} onKeyPress={handleKeyPress(v.value)}>
|
||||||
<div className={`choice ${selected&&selected===v.value?'selected':''}`} onClick={() => setType(v.value)}>
|
<div className={`choice ${selected&&selected===v.value?'selected':''}`} onClick={() => setType(v.value)}>
|
||||||
{v.label}
|
{v.label}
|
||||||
<PercentageOverlay percentage={v.percentage} />
|
<PercentageOverlay percentage={v.percentage} />
|
||||||
|
@ -8,6 +8,10 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.select-box:focus-visible .selected {
|
||||||
|
border: 1px solid #fff;
|
||||||
|
}
|
||||||
|
|
||||||
.select-box > * {
|
.select-box > * {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { useRef, useState, useEffect } from "react"
|
import { useRef, useState, useEffect } from "react"
|
||||||
import "./SelectBox.css"
|
import "./SelectBox.css"
|
||||||
|
|
||||||
function Option({ option, onClick }) {
|
function Option({ option, ...props }) {
|
||||||
return (
|
return (
|
||||||
<div className="option" onClick={onClick}>
|
<div className="option" {...props}>
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
className="radio"
|
className="radio"
|
||||||
@ -53,18 +53,30 @@ export function SelectBox({ options, selectedItem, setSelectedItem }) {
|
|||||||
closeDropdown()
|
closeDropdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleSelectedKeyPress = event => {
|
||||||
|
if (event.code === 'Enter' || event.code === 'Space'){
|
||||||
|
setActive(a => !a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOptionKeyPress = (option, i) => event => {
|
||||||
|
if (event.code === 'Enter' || event.code === 'Space'){
|
||||||
|
onOptionClick(event, option, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="select-box" ref={containerRef} onClick={() => setActive(a => !a)}>
|
<div className="select-box" ref={containerRef} onClick={() => setActive(a => !a)} >
|
||||||
<div className={"options-container" + (active ? " active" : "")}>
|
<div className="selected" tabIndex={0} onKeyPress={handleSelectedKeyPress}>
|
||||||
{options.map((opt, i) => (
|
|
||||||
<Option option={opt} key={i} onClick={(e) => onOptionClick(e, opt, i)} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div className="selected">
|
|
||||||
{options ? (
|
{options ? (
|
||||||
<Option option={options[selectedItem]} />
|
<Option option={options[selectedItem]} />
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
<div className={"options-container" + (active ? " active" : "")}>
|
||||||
|
{options.map((opt, i) => (
|
||||||
|
<Option option={opt} key={i} onClick={(e) => onOptionClick(e, opt, i)} tabIndex={active ? 0 : undefined} onKeyPress={active ? handleOptionKeyPress(opt, i) : undefined} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,15 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.title.accent.title-accent-link:focus-visible {
|
||||||
|
border: 1px solid #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title.accent.title-accent-link:focus-visible .arrow {
|
||||||
|
transform: translateY(.1rem) translateX(-.5rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.title-accent.title-accent-link .arrow {
|
.title-accent.title-accent-link .arrow {
|
||||||
transition: transform 100ms ease-in-out;
|
transition: transform 100ms ease-in-out;
|
||||||
transform: translateY(.1rem);
|
transform: translateY(.1rem);
|
||||||
@ -39,3 +48,5 @@
|
|||||||
transform: translateY(.1rem) translateX(-.5rem);
|
transform: translateY(.1rem) translateX(-.5rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,15 +15,23 @@ export function Title(props) {
|
|||||||
const accentLink = props.accentLink || "";
|
const accentLink = props.accentLink || "";
|
||||||
const accent = props.accent || "";
|
const accent = props.accent || "";
|
||||||
|
|
||||||
|
function handleAccentClick(){
|
||||||
|
if (accentLink.length > 0) {
|
||||||
|
history.push(`/${streamData.type}`);
|
||||||
|
resetStreamData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleKeyPress(event){
|
||||||
|
if (event.code === 'Enter' || event.code === 'Space'){
|
||||||
|
handleAccentClick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{accent.length > 0 ? (
|
{accent.length > 0 ? (
|
||||||
<p onClick={() => {
|
<p onClick={handleAccentClick} className={`title-accent ${accentLink.length > 0 ? 'title-accent-link' : ''}`} tabIndex={accentLink.length > 0 ? 0 : undefined} onKeyPress={handleKeyPress}>
|
||||||
if (accentLink.length > 0) {
|
|
||||||
history.push(`/${streamData.type}`);
|
|
||||||
resetStreamData();
|
|
||||||
}
|
|
||||||
}} className={`title-accent ${accentLink.length > 0 ? 'title-accent-link' : ''}`}>
|
|
||||||
{accentLink.length > 0 ? (<Arrow left/>) : null}{accent}
|
{accentLink.length > 0 ? (<Arrow left/>) : null}{accent}
|
||||||
</p>
|
</p>
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -39,6 +39,11 @@
|
|||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.typeSelector .choice:focus-visible {
|
||||||
|
border: 1px solid #fff;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
.typeSelector .choice.selected {
|
.typeSelector .choice.selected {
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,36 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import './TypeSelector.css'
|
import './TypeSelector.css';
|
||||||
|
|
||||||
// setType: (txt: string) => void
|
// setType: (txt: string) => void
|
||||||
// choices: { label: string, value: string }[]
|
// choices: { label: string, value: string }[]
|
||||||
// selected: string
|
// selected: string
|
||||||
export function TypeSelector({ setType, choices, selected, noWrap = false }) {
|
export function TypeSelector({ setType, choices, selected, noWrap = false }) {
|
||||||
const selectedIndex = choices.findIndex(v=>v.value===selected);
|
const selectedIndex = choices.findIndex(v => v.value === selected);
|
||||||
const transformStyles = {
|
const transformStyles = {
|
||||||
opacity: selectedIndex!==-1?1:0,
|
opacity: selectedIndex !== -1 ? 1 : 0,
|
||||||
transform: `translateX(${selectedIndex!==-1?selectedIndex*7:0}rem)`
|
transform: `translateX(${selectedIndex !== -1 ? selectedIndex * 7 : 0}rem)`,
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeyPress = choice => event => {
|
||||||
|
if (event.code === 'Enter' || event.code === 'Space') {
|
||||||
|
setType(choice);
|
||||||
}
|
}
|
||||||
return (
|
};
|
||||||
<div className={`typeSelector ${noWrap ? 'nowrap' : ''}`}>
|
|
||||||
{choices.map(v=>(
|
return (
|
||||||
<div key={v.value} className={`choice ${selected===v.value?'selected':''}`} onClick={() => setType(v.value)}>
|
<div className={`typeSelector ${noWrap ? 'nowrap' : ''}`}>
|
||||||
{v.label}
|
{choices.map(v => (
|
||||||
</div>
|
<div
|
||||||
))}
|
key={v.value}
|
||||||
<div className="selectedBar" style={transformStyles}/>
|
className={`choice ${selected === v.value ? 'selected' : ''}`}
|
||||||
|
onClick={() => setType(v.value)}
|
||||||
|
onKeyPress={handleKeyPress(v.value)}
|
||||||
|
tabIndex={0}
|
||||||
|
>
|
||||||
|
{v.label}
|
||||||
</div>
|
</div>
|
||||||
)
|
))}
|
||||||
|
<div className="selectedBar" style={transformStyles} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,11 @@
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cardView nav span:focus-visible {
|
||||||
|
border: 1px solid #fff;
|
||||||
|
}
|
||||||
|
|
||||||
.cardView nav span:not(.selected-link) {
|
.cardView nav span:not(.selected-link) {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
@ -207,6 +207,12 @@ export function SearchView() {
|
|||||||
return <Redirect to="/movie" />
|
return <Redirect to="/movie" />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleKeyPress = page => event => {
|
||||||
|
if (event.code === 'Enter' || event.code === 'Space'){
|
||||||
|
setPage(page);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="cardView">
|
<div className="cardView">
|
||||||
<Helmet>
|
<Helmet>
|
||||||
@ -215,9 +221,9 @@ export function SearchView() {
|
|||||||
|
|
||||||
{/* Nav */}
|
{/* Nav */}
|
||||||
<nav>
|
<nav>
|
||||||
<span className={page === 'search' ? 'selected-link' : ''} onClick={() => setPage('search')}>Search</span>
|
<span className={page === 'search' ? 'selected-link' : ''} onClick={() => setPage('search')} onKeyPress={handleKeyPress('search')} tabIndex={0}>Search</span>
|
||||||
{continueWatching.length > 0 ?
|
{continueWatching.length > 0 ?
|
||||||
<span className={page === 'watching' ? 'selected-link' : ''} onClick={() => setPage('watching')}>Continue watching</span>
|
<span className={page === 'watching' ? 'selected-link' : ''} onClick={() => setPage('watching')} onKeyPress={handleKeyPress('watching')} tabIndex={0}>Continue watching</span>
|
||||||
: ''}
|
: ''}
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user