Support for keyboard navigation for Apps. Small refactor to pinApp action

This commit is contained in:
unknown 2021-05-22 18:10:12 +02:00
parent 8813bf6181
commit 5c948e1a68
4 changed files with 43 additions and 10 deletions

View File

@ -1,4 +1,4 @@
import { useState, useEffect, ChangeEvent, SyntheticEvent } from 'react'; import { useState, useEffect, useRef, ChangeEvent, SyntheticEvent } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { addApp, updateApp } from '../../../store/actions'; import { addApp, updateApp } from '../../../store/actions';
import { App, NewApp } from '../../../interfaces/App'; import { App, NewApp } from '../../../interfaces/App';
@ -20,6 +20,14 @@ const AppForm = (props: ComponentProps): JSX.Element => {
icon: '' icon: ''
}); });
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
if (inputRef.current) {
inputRef.current.focus();
}
}, [inputRef])
useEffect(() => { useEffect(() => {
if (props.app) { if (props.app) {
console.log('app'); console.log('app');
@ -75,6 +83,7 @@ const AppForm = (props: ComponentProps): JSX.Element => {
required required
value={formData.name} value={formData.name}
onChange={(e) => inputChangeHandler(e)} onChange={(e) => inputChangeHandler(e)}
ref={inputRef}
/> />
</div> </div>
<div className={classes.InputGroup}> <div className={classes.InputGroup}>

View File

@ -1,3 +1,4 @@
import { KeyboardEvent } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { App, GlobalState } from '../../../interfaces'; import { App, GlobalState } from '../../../interfaces';
import { pinApp, deleteApp } from '../../../store/actions'; import { pinApp, deleteApp } from '../../../store/actions';
@ -7,7 +8,7 @@ import Icon from '../../UI/Icons/Icon/Icon';
interface ComponentProps { interface ComponentProps {
apps: App[]; apps: App[];
pinApp: (id: number, isPinned: boolean) => void; pinApp: (app: App) => void;
deleteApp: (id: number) => void; deleteApp: (id: number) => void;
updateAppHandler: (app: App) => void; updateAppHandler: (app: App) => void;
} }
@ -21,6 +22,12 @@ const AppTable = (props: ComponentProps): JSX.Element => {
} }
} }
const keyboardActionHandler = (e: KeyboardEvent, app: App, handler: Function) => {
if (e.key === 'Enter') {
handler(app);
}
}
return ( return (
<div className={classes.TableContainer}> <div className={classes.TableContainer}>
<table className={classes.Table}> <table className={classes.Table}>
@ -42,16 +49,27 @@ const AppTable = (props: ComponentProps): JSX.Element => {
<td className={classes.TableActions}> <td className={classes.TableActions}>
<div <div
className={classes.TableAction} className={classes.TableAction}
onClick={() => deleteAppHandler(app)}> onClick={() => deleteAppHandler(app)}
onKeyDown={(e) => keyboardActionHandler(e, app, deleteAppHandler)}
tabIndex={0}>
<Icon icon='mdiDelete' /> <Icon icon='mdiDelete' />
</div> </div>
<div <div
className={classes.TableAction} className={classes.TableAction}
onClick={() => props.updateAppHandler(app)}> onClick={() => props.updateAppHandler(app)}
onKeyDown={(e) => keyboardActionHandler(e, app, props.updateAppHandler)}
tabIndex={0}>
<Icon icon='mdiPencil' /> <Icon icon='mdiPencil' />
</div> </div>
<div className={classes.TableAction} onClick={() => props.pinApp(app.id, app.isPinned)}> <div
{app.isPinned? <Icon icon='mdiPinOff' color='var(--color-accent)' /> : <Icon icon='mdiPin' />} className={classes.TableAction}
onClick={() => props.pinApp(app)}
onKeyDown={(e) => keyboardActionHandler(e, app, props.pinApp)}
tabIndex={0}>
{app.isPinned
? <Icon icon='mdiPinOff' color='var(--color-accent)' />
: <Icon icon='mdiPin' />
}
</div> </div>
</td> </td>
</tr> </tr>

View File

@ -1,4 +1,4 @@
import { useEffect, useState } from 'react'; import { Fragment, useEffect, useState } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
// Redux // Redux
@ -25,7 +25,6 @@ import AppTable from './AppTable/AppTable';
interface ComponentProps { interface ComponentProps {
getApps: Function; getApps: Function;
pinApp: (id: number, isPinned: boolean) => void;
addApp: (formData: NewApp) => void; addApp: (formData: NewApp) => void;
apps: App[]; apps: App[];
loading: boolean; loading: boolean;
@ -92,6 +91,12 @@ const Apps = (props: ComponentProps): JSX.Element => {
icon='mdiPencil' icon='mdiPencil'
handler={toggleEdit} handler={toggleEdit}
/> />
{isInEdit && <Fragment>
<ActionButton
name='Pin All'
icon='mdiPin'
/>
</Fragment>}
</div> </div>
<div className={classes.Apps}> <div className={classes.Apps}>
@ -115,4 +120,4 @@ const mapStateToProps = (state: GlobalState) => {
} }
} }
export default connect(mapStateToProps, { getApps, pinApp, addApp })(Apps); export default connect(mapStateToProps, { getApps, addApp })(Apps);

View File

@ -34,8 +34,9 @@ export interface PinAppAction {
payload: App; payload: App;
} }
export const pinApp = (id: number, isPinned: boolean) => async (dispatch: Dispatch) => { export const pinApp = (app: App) => async (dispatch: Dispatch) => {
try { try {
const { id, isPinned} = app;
const res = await axios.put<ApiResponse<App>>(`/api/apps/${id}`, { isPinned: !isPinned }); const res = await axios.put<ApiResponse<App>>(`/api/apps/${id}`, { isPinned: !isPinned });
dispatch<PinAppAction>({ dispatch<PinAppAction>({