Improved accessibility by adding focus styles
This commit is contained in:
parent
d1eb97f9b6
commit
9c1074061e
16 changed files with 71 additions and 44 deletions
|
|
@ -68,51 +68,52 @@ export function FileExplorer({ startPath }) {
|
|||
return (
|
||||
<div className={styles.Container}>
|
||||
<div className={styles.Header}>
|
||||
<button title="Back" className={styles["Icon-button"]}>
|
||||
<button title="Back" tabIndex={0} className={styles["Icon-button"]}>
|
||||
<FontAwesomeIcon icon={faCaretLeft}/>
|
||||
</button>
|
||||
<button title="Forward" className={styles["Icon-button"]}>
|
||||
<button title="Forward" tabIndex={0} className={styles["Icon-button"]}>
|
||||
<FontAwesomeIcon icon={faCaretRight}/>
|
||||
</button>
|
||||
<button title="Up" className={styles["Icon-button"]} onClick={() => { changeDirectory(".."); }}>
|
||||
<button title="Up" tabIndex={0} className={styles["Icon-button"]} onClick={() => { changeDirectory(".."); }}>
|
||||
<FontAwesomeIcon icon={faArrowUp}/>
|
||||
</button>
|
||||
<input
|
||||
value={path}
|
||||
type="text"
|
||||
className={styles["Path-input"]}
|
||||
tabIndex={0}
|
||||
onChange={onPathChange}
|
||||
onKeyDown={onKeyDown}
|
||||
/>
|
||||
<button title="Search" className={styles["Icon-button"]}>
|
||||
<button title="Search" tabIndex={0} className={styles["Icon-button"]}>
|
||||
<FontAwesomeIcon icon={faSearch}/>
|
||||
</button>
|
||||
<button title="Settings" className={styles["Icon-button"]}>
|
||||
<button title="Settings" tabIndex={0} className={styles["Icon-button"]}>
|
||||
<FontAwesomeIcon icon={faCog}/>
|
||||
</button>
|
||||
</div>
|
||||
<div className={styles.Body}>
|
||||
<div className={styles.Sidebar}>
|
||||
<button title="Home" className={styles["Nav-button"]} onClick={() => { changeDirectory("~"); }}>
|
||||
<button title="Home" tabIndex={0} className={styles["Nav-button"]} onClick={() => { changeDirectory("~"); }}>
|
||||
<FontAwesomeIcon icon={faHouse}/>
|
||||
Home
|
||||
</button>
|
||||
<button title="Desktop" className={styles["Nav-button"]} onClick={() => { changeDirectory("~/Desktop"); }}>
|
||||
<button title="Desktop" tabIndex={0} className={styles["Nav-button"]} onClick={() => { changeDirectory("~/Desktop"); }}>
|
||||
<FontAwesomeIcon icon={faDesktop}/>
|
||||
Desktop
|
||||
</button>
|
||||
<button title="Documents" className={styles["Nav-button"]} onClick={() => { changeDirectory("~/Documents"); }}>
|
||||
<button title="Documents" tabIndex={0} className={styles["Nav-button"]} onClick={() => { changeDirectory("~/Documents"); }}>
|
||||
<FontAwesomeIcon icon={faFileLines}/>
|
||||
Documents
|
||||
</button>
|
||||
<button title="Images" className={styles["Nav-button"]} onClick={() => { changeDirectory("~/Images"); }}>
|
||||
<button title="Images" tabIndex={0} className={styles["Nav-button"]} onClick={() => { changeDirectory("~/Images"); }}>
|
||||
<FontAwesomeIcon icon={faImage}/>
|
||||
Images
|
||||
</button>
|
||||
</div>
|
||||
<div className={styles.Main}>
|
||||
{currentDirectory?.getFiles(showHidden)?.map((file, index) =>
|
||||
<button key={index} title={file.id} className={styles["File-button"]} onClick={(event) => {
|
||||
<button key={index} title={file.id} tabIndex={0} className={styles["File-button"]} onClick={(event) => {
|
||||
event.preventDefault();
|
||||
windowsManager.openFile(file);
|
||||
}}>
|
||||
|
|
@ -121,7 +122,7 @@ export function FileExplorer({ startPath }) {
|
|||
</button>
|
||||
)}
|
||||
{currentDirectory?.getSubFolders(showHidden)?.map(({ name }, index) =>
|
||||
<button key={index} title={name} className={styles["Folder-button"]} onClick={() => {
|
||||
<button key={index} title={name} tabIndex={0} className={styles["Folder-button"]} onClick={() => {
|
||||
changeDirectory(name);
|
||||
}}>
|
||||
<FontAwesomeIcon icon={faFolder}/>
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@
|
|||
transition: all 200ms ease-in-out;
|
||||
}
|
||||
|
||||
.Icon-button:hover::after {
|
||||
.Icon-button:hover::after, .Icon-button:focus::after {
|
||||
background-color: rgba(255, 255, 255, 10%);
|
||||
transform: scale(150%);
|
||||
}
|
||||
|
|
@ -102,7 +102,7 @@
|
|||
transition: background-color 100ms ease-in-out;
|
||||
}
|
||||
|
||||
.Nav-button:hover {
|
||||
.Nav-button:hover, .Nav-button:focus {
|
||||
background-color: rgba(255, 255, 255, 10%);
|
||||
}
|
||||
|
||||
|
|
@ -138,7 +138,8 @@
|
|||
transition: background-color 100ms ease-in-out;
|
||||
}
|
||||
|
||||
.File-button:hover, .Folder-button:hover {
|
||||
.File-button:hover, .Folder-button:hover,
|
||||
.File-button:focus, .Folder-button:focus {
|
||||
background-color: rgba(255, 255, 255, 10%);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
transition: background-color 100ms ease-in-out;
|
||||
}
|
||||
|
||||
.Tab-button:hover {
|
||||
.Tab-button:hover, .Tab-button:focus {
|
||||
background-color: rgba(255, 255, 255, 10%);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ function AppButton({ app }) {
|
|||
|
||||
return (
|
||||
<button
|
||||
tabIndex={0}
|
||||
className={classNames.join(" ")}
|
||||
onClick={() => { windowsManager.open(app.id); }}
|
||||
title={app.name}
|
||||
|
|
@ -102,7 +103,9 @@ export function Taskbar() {
|
|||
<div className={styles["Menu-icons"]}>
|
||||
<div className={styles["Home-container"]}>
|
||||
<OutsideClickListener onOutsideClick={() => { updateShowHome(false); }}>
|
||||
<button title="Home"
|
||||
<button
|
||||
title="Home"
|
||||
tabIndex={0}
|
||||
className={`${styles["Menu-button"]} ${styles["Home-button"]}`}
|
||||
onClick={() => { updateShowHome(!showHome); }}
|
||||
>
|
||||
|
|
@ -113,7 +116,9 @@ export function Taskbar() {
|
|||
</div>
|
||||
<div className={styles["Search-container"]}>
|
||||
<OutsideClickListener onOutsideClick={() => { updateShowSearch(false); }}>
|
||||
<button title="Search"
|
||||
<button
|
||||
title="Search"
|
||||
tabIndex={0}
|
||||
className={`${styles["Menu-button"]} ${styles["Search-button"]}`}
|
||||
onClick={() => { updateShowSearch(!showSearch); }}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
z-index: -1;
|
||||
}
|
||||
|
||||
.Task-bar button:hover {
|
||||
.Task-bar button:hover, .Task-bar button:focus {
|
||||
background-color: var(--task-bar-button-hover-color);
|
||||
}
|
||||
|
||||
|
|
@ -95,7 +95,7 @@
|
|||
-webkit-transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.App-icon:hover::after {
|
||||
.App-icon:hover::after, .App-icon:focus::after {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ export function Battery() {
|
|||
battery.removeEventListener("levelchange", updatePercentage);
|
||||
// battery.removeEventListener("chargingtimechange", updateChargingTime);
|
||||
// battery.removeEventListener("dischargingtimechange", updateDischargingTime);
|
||||
}
|
||||
};
|
||||
});
|
||||
}, []);
|
||||
|
||||
|
|
@ -58,7 +58,7 @@ export function Battery() {
|
|||
}
|
||||
|
||||
return (
|
||||
<button className={styles.Button} title="Battery">
|
||||
<button className={styles.Button} title="Battery" tabIndex={0}>
|
||||
{!isCharging
|
||||
? <FontAwesomeIcon className={styles["Charging-indicator"]} icon={faMinus}/>
|
||||
: null
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ export function Calendar() {
|
|||
}, []);
|
||||
|
||||
return (
|
||||
<button className={styles.Button} title="Date & Time" style={{ userSelect: "none" }}>
|
||||
<button className={styles.Button} title="Date & Time" style={{ userSelect: "none" }} tabIndex={0}>
|
||||
{date.toLocaleString("en-US", {
|
||||
hour: "numeric",
|
||||
minute: "numeric",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|||
|
||||
export function Network() {
|
||||
return (
|
||||
<button title="Wifi">
|
||||
<button title="Wifi" tabIndex={0}>
|
||||
<FontAwesomeIcon icon={faWifi}/>
|
||||
</button>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|||
|
||||
export function Volume() {
|
||||
return (
|
||||
<button title="Volume">
|
||||
<button title="Volume" tabIndex={0}>
|
||||
<FontAwesomeIcon icon={faVolumeHigh}/>
|
||||
</button>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { ReactSVG } from "react-svg";
|
|||
import { closeTab } from "../../../features/utils/browser.js";
|
||||
import { useKeyboardListener } from "../../../hooks/utils/keyboard.js";
|
||||
import { useVirtualRoot } from "../../../hooks/virtual-drive/VirtualRootContext.js";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
/**
|
||||
* @param {object} props
|
||||
|
|
@ -18,6 +19,11 @@ import { useVirtualRoot } from "../../../hooks/virtual-drive/VirtualRootContext.
|
|||
export function HomeMenu({ active, setActive, search }) {
|
||||
const windowsManager = useWindowsManager();
|
||||
const virtualRoot = useVirtualRoot();
|
||||
const [tabIndex, setTabIndex] = useState(active ? 0 : -1);
|
||||
|
||||
useEffect(() => {
|
||||
setTabIndex(active ? 0 : -1);
|
||||
}, [active]);
|
||||
|
||||
const classNames = [styles["Container-outer"]];
|
||||
if (active)
|
||||
|
|
@ -35,7 +41,7 @@ export function HomeMenu({ active, setActive, search }) {
|
|||
search(event.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onKeyUp = (event) => {
|
||||
if (event.key === "Alt" && onlyAltKey) {
|
||||
|
|
@ -45,7 +51,7 @@ export function HomeMenu({ active, setActive, search }) {
|
|||
} else {
|
||||
onlyAltKey = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useKeyboardListener({ onKeyDown, onKeyUp });
|
||||
|
||||
|
|
@ -53,16 +59,16 @@ export function HomeMenu({ active, setActive, search }) {
|
|||
<div className={classNames.join(" ")}>
|
||||
<div className={styles["Container-inner"]}>
|
||||
<div className={styles.Buttons}>
|
||||
<button title="Power" onClick={() => { closeTab(); }}>
|
||||
<button title="Power" tabIndex={tabIndex} onClick={() => { closeTab(); }}>
|
||||
<FontAwesomeIcon icon={faPowerOff}/>
|
||||
</button>
|
||||
<button title="Settings" onClick={() => {
|
||||
<button title="Settings" tabIndex={tabIndex} onClick={() => {
|
||||
setActive(false);
|
||||
windowsManager.open("settings");
|
||||
}}>
|
||||
<FontAwesomeIcon icon={faGear}/>
|
||||
</button>
|
||||
<button title="Info" onClick={() => {
|
||||
<button title="Info" tabIndex={tabIndex} onClick={() => {
|
||||
setActive(false);
|
||||
windowsManager.open("text-editor", {
|
||||
mode: "view",
|
||||
|
|
@ -71,13 +77,13 @@ export function HomeMenu({ active, setActive, search }) {
|
|||
}}>
|
||||
<FontAwesomeIcon icon={faCircleInfo}/>
|
||||
</button>
|
||||
<button title="Images" onClick={() => {
|
||||
<button title="Images" tabIndex={tabIndex} onClick={() => {
|
||||
setActive(false);
|
||||
windowsManager.open("file-explorer", { startPath: "~/Images" });
|
||||
}}>
|
||||
<FontAwesomeIcon icon={faImage}/>
|
||||
</button>
|
||||
<button title="Documents" onClick={() => {
|
||||
<button title="Documents" tabIndex={tabIndex} onClick={() => {
|
||||
setActive(false);
|
||||
windowsManager.open("file-explorer", { startPath: "~/Documents" }); }
|
||||
}>
|
||||
|
|
@ -91,6 +97,7 @@ export function HomeMenu({ active, setActive, search }) {
|
|||
<button
|
||||
key={id}
|
||||
className={appStyles["App-button"]}
|
||||
tabIndex={tabIndex}
|
||||
onClick={() => {
|
||||
setActive(false);
|
||||
windowsManager.open(id);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@ import { useState } from "react";
|
|||
export function SearchMenu({ active, setActive, searchQuery, setSearchQuery, inputRef }) {
|
||||
const windowsManager = useWindowsManager();
|
||||
const [apps, setApps] = useState(null);
|
||||
const [tabIndex, setTabIndex] = useState(active ? 0 : -1);
|
||||
|
||||
useEffect(() => {
|
||||
setTabIndex(active ? 0 : -1);
|
||||
}, [active]);
|
||||
|
||||
useEffect(() => {
|
||||
if (inputRef.current)
|
||||
|
|
@ -32,7 +37,7 @@ export function SearchMenu({ active, setActive, searchQuery, setSearchQuery, inp
|
|||
const onChange = (event) => {
|
||||
const value = event.target.value;
|
||||
setSearchQuery(value);
|
||||
}
|
||||
};
|
||||
|
||||
const classNames = [styles["Container-outer"]];
|
||||
if (active && apps)
|
||||
|
|
@ -44,6 +49,7 @@ export function SearchMenu({ active, setActive, searchQuery, setSearchQuery, inp
|
|||
<input
|
||||
ref={inputRef}
|
||||
className={styles.Input}
|
||||
tabIndex={tabIndex}
|
||||
value={searchQuery}
|
||||
onChange={onChange}
|
||||
spellCheck={false}
|
||||
|
|
@ -54,6 +60,7 @@ export function SearchMenu({ active, setActive, searchQuery, setSearchQuery, inp
|
|||
<button
|
||||
key={id}
|
||||
className={appStyles["App-button"]}
|
||||
tabIndex={tabIndex}
|
||||
onClick={() => {
|
||||
setActive(false);
|
||||
windowsManager.open(id);
|
||||
|
|
|
|||
|
|
@ -12,16 +12,20 @@ import { formatShortcut } from "../../features/utils/string.js";
|
|||
export function DropdownButton({ label, options, shortcuts }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const tabIndex = () => {
|
||||
return open ? 0 : -1;
|
||||
};
|
||||
|
||||
return (
|
||||
<OutsideClickListener onOutsideClick={() => { setOpen(false); }}>
|
||||
<div className={styles.Container}>
|
||||
<button title={label} className={styles.Button} onClick={() => { setOpen(!open); }}>
|
||||
<button title={label} className={styles.Button} tabIndex={0} onClick={() => { setOpen(!open); }}>
|
||||
{label}
|
||||
</button>
|
||||
{open && options
|
||||
? (<div className={styles.Dropdown}>
|
||||
{Object.entries(options).map(([label, callback]) =>
|
||||
<button title={label} key={label} onClick={() => {
|
||||
<button title={label} key={label} tabIndex={tabIndex} onClick={() => {
|
||||
setOpen(false);
|
||||
callback();
|
||||
}}>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.Button:hover {
|
||||
.Button:hover, .Button:focus {
|
||||
background-color: rgba(255, 255, 255, 5%);
|
||||
}
|
||||
|
||||
|
|
@ -42,7 +42,7 @@
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.Dropdown > button:hover {
|
||||
.Dropdown > button:hover, .Dropdown > button:focus {
|
||||
background-color: rgba(255, 255, 255, 5%);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ export function Window({ id, app, size, position, focused = false, onInteract, o
|
|||
});
|
||||
|
||||
const close = (event) => {
|
||||
event.preventDefault();
|
||||
event?.preventDefault();
|
||||
windowsManager.close(id);
|
||||
};
|
||||
|
||||
|
|
@ -89,15 +89,18 @@ export function Window({ id, app, size, position, focused = false, onInteract, o
|
|||
onClick={focus}
|
||||
>
|
||||
<div className={`${styles.Header} Handle`}>
|
||||
<ReactSVG className={styles["Window-icon"]} src={process.env.PUBLIC_URL + `/media/applications/icons/${app.id}.svg`}/>
|
||||
<ReactSVG
|
||||
className={styles["Window-icon"]}
|
||||
src={process.env.PUBLIC_URL + `/media/applications/icons/${app.id}.svg`}
|
||||
/>
|
||||
<p>{title}</p>
|
||||
<button title="Minimize" onClick={() => setMinimized(!minimized)}>
|
||||
<button title="Minimize" tabIndex={0} onClick={() => setMinimized(!minimized)}>
|
||||
<FontAwesomeIcon icon={faMinus}/>
|
||||
</button>
|
||||
<button title="Maximize" onClick={() => setMaximized(!maximized)}>
|
||||
<button title="Maximize" tabIndex={0} onClick={() => setMaximized(!maximized)}>
|
||||
<FontAwesomeIcon icon={faSquare}/>
|
||||
</button>
|
||||
<button title="Close" id="close-window" onClick={close}>
|
||||
<button title="Close" tabIndex={0} id="close-window" onClick={close}>
|
||||
<FontAwesomeIcon icon={faXmark}/>
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@
|
|||
outline: none;
|
||||
}
|
||||
|
||||
.Header button:hover {
|
||||
.Header button:hover, .Header button:focus {
|
||||
background-color: var(--header-button-hover-color);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable eqeqeq */
|
||||
import Application from "./application.js";
|
||||
import { FileExplorer } from "../../components/applications/file-explorer/FileExplorer.jsx";
|
||||
import { MediaViewer } from "../../components/applications/media-viewer/MediaViewer.jsx";
|
||||
|
|
@ -29,7 +28,7 @@ export default class ApplicationsManager {
|
|||
let application = null;
|
||||
|
||||
this.APPLICATIONS.forEach((app) => {
|
||||
if (app.id == id) {
|
||||
if (app.id === id) {
|
||||
application = app;
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue