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