typescriptify Popupbase

focus textfield on filterclick
This commit is contained in:
2021-01-22 21:05:21 +00:00
parent 66eb72d7fb
commit 6c7cc11038
29 changed files with 303 additions and 190 deletions

View File

@ -3,7 +3,7 @@ import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faUser} from '@fortawesome/free-solid-svg-icons';
import React from 'react';
import {Link} from 'react-router-dom';
import {ActorType} from '../../api/VideoTypes';
import {ActorType} from '../../types/VideoTypes';
interface props {
actor: ActorType;

View File

@ -2,14 +2,23 @@ import React from 'react';
import style from './InfoHeaderItem.module.css';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {Spinner} from 'react-bootstrap';
import {IconDefinition} from '@fortawesome/fontawesome-common-types';
interface props {
onClick?: () => void
backColor: string
icon: IconDefinition
text: string | number
subtext: string | number
}
/**
* a component to display one of the short quickinfo tiles on dashboard
*/
class InfoHeaderItem extends React.Component {
render() {
class InfoHeaderItem extends React.Component<props> {
render(): JSX.Element {
return (
<div onClick={() => {
<div onClick={(): void => {
// call clicklistener if defined
if (this.props.onClick != null) this.props.onClick();
}} className={style.infoheaderitem} style={{backgroundColor: this.props.backColor}}>

View File

@ -4,8 +4,8 @@ import ActorTile from '../../ActorTile/ActorTile';
import style from './AddActorPopup.module.css';
import {NewActorPopupContent} from '../NewActorPopup/NewActorPopup';
import {callAPI} from '../../../utils/Api';
import {ActorType} from '../../../api/VideoTypes';
import {GeneralSuccess} from '../../../api/GeneralTypes';
import {ActorType} from '../../../types/VideoTypes';
import {GeneralSuccess} from '../../../types/GeneralTypes';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faFilter, faTimes} from '@fortawesome/free-solid-svg-icons';
import {Button} from '../../GPElements/Button';
@ -26,6 +26,9 @@ interface state {
* Popup for Adding a new Actor to a Video
*/
class AddActorPopup extends React.Component<props, state> {
// filterfield anchor, needed to focus after filter btn click
private filterfield: HTMLInputElement | null | undefined;
constructor(props: props) {
super(props);
@ -89,7 +92,8 @@ class AddActorPopup extends React.Component<props, state> {
type='text' placeholder='Filter' value={this.state.filter}
onChange={(e): void => {
this.setState({filter: e.target.value});
}}/>
}}
ref={(input): void => {this.filterfield = input;}}/>
<Button title={<FontAwesomeIcon style={{
verticalAlign: 'middle',
lineHeight: '130px'
@ -101,7 +105,10 @@ class AddActorPopup extends React.Component<props, state> {
verticalAlign: 'middle',
lineHeight: '130px'
}} icon={faFilter} size='1x'/></span>} color={{backgroundColor: 'cornflowerblue', color: 'white'}} onClick={(): void => {
this.setState({filtervisible: true});
this.setState({filtervisible: true}, () => {
// focus filterfield after state update
this.filterfield?.focus();
});
}}/>
}
</div>

View File

@ -2,19 +2,31 @@ import React from 'react';
import Tag from '../../Tag/Tag';
import PopupBase from '../PopupBase';
import {callAPI} from '../../../utils/Api';
import {TagType} from '../../../types/VideoTypes';
import {GeneralSuccess} from '../../../types/GeneralTypes';
interface props {
onHide: () => void;
submit: (tagId: number, tagName: string) => void;
movie_id: number;
}
interface state {
items: TagType[];
}
/**
* component creates overlay to add a new tag to a video
*/
class AddTagPopup extends React.Component {
constructor(props, context) {
super(props, context);
class AddTagPopup extends React.Component<props, state> {
constructor(props: props) {
super(props);
this.state = {items: []};
}
componentDidMount() {
callAPI('tags.php', {action: 'getAllTags'}, (result) => {
componentDidMount(): void {
callAPI('tags.php', {action: 'getAllTags'}, (result: TagType[]) => {
console.log(result);
this.setState({
items: result
@ -22,13 +34,13 @@ class AddTagPopup extends React.Component {
});
}
render() {
render(): JSX.Element {
return (
<PopupBase title='Add a Tag to this Video:' onHide={this.props.onHide}>
{this.state.items ?
this.state.items.map((i) => (
<Tag tagInfo={i}
onclick={() => {
onclick={(): void => {
this.addTag(i.tag_id, i.tag_name);
}}/>
)) : null}
@ -41,8 +53,8 @@ class AddTagPopup extends React.Component {
* @param tagid tag id to add
* @param tagname tag name to add
*/
addTag(tagid, tagname) {
callAPI('tags.php', {action: 'addTag', id: tagid, movieid: this.props.movie_id}, result => {
addTag(tagid: number, tagname: string): void {
callAPI('tags.php', {action: 'addTag', id: tagid, movieid: this.props.movie_id}, (result: GeneralSuccess) => {
if (result.result !== 'success') {
console.log('error occured while writing to db -- todo error handling');
console.log(result.result);

View File

@ -2,7 +2,7 @@ import React from 'react';
import PopupBase from '../PopupBase';
import style from './NewActorPopup.module.css';
import {callAPI} from '../../../utils/Api';
import {GeneralSuccess} from '../../../api/GeneralTypes';
import {GeneralSuccess} from '../../../types/GeneralTypes';
interface NewActorPopupProps {
onHide: () => void;

View File

@ -2,24 +2,25 @@ import React from 'react';
import PopupBase from '../PopupBase';
import style from './NewTagPopup.module.css';
import {callAPI} from '../../../utils/Api';
import {GeneralSuccess} from '../../../types/GeneralTypes';
interface props {
onHide: () => void
}
/**
* creates modal overlay to define a new Tag
*/
class NewTagPopup extends React.Component {
constructor(props, context) {
super(props, context);
class NewTagPopup extends React.Component<props> {
private value: string = '';
this.props = props;
}
render() {
render(): JSX.Element {
return (
<PopupBase title='Add new Tag' onHide={this.props.onHide} height='200px' width='400px'>
<div><input type='text' placeholder='Tagname' onChange={(v) => {
<div><input type='text' placeholder='Tagname' onChange={(v): void => {
this.value = v.target.value;
}}/></div>
<button className={style.savebtn} onClick={() => this.storeselection()}>Save</button>
<button className={style.savebtn} onClick={(): void => this.storeselection()}>Save</button>
</PopupBase>
);
}
@ -27,8 +28,8 @@ class NewTagPopup extends React.Component {
/**
* store the filled in form to the backend
*/
storeselection() {
callAPI('tags.php', {action: 'createTag', tagname: this.value}, result => {
storeselection(): void {
callAPI('tags.php', {action: 'createTag', tagname: this.value}, (result: GeneralSuccess) => {
if (result.result !== 'success') {
console.log('error occured while writing to db -- todo error handling');
console.log(result.result);

View File

@ -1,13 +1,24 @@
import GlobalInfos from '../../utils/GlobalInfos';
import style from './PopupBase.module.css';
import {Line} from '../PageTitle/PageTitle';
import React from 'react';
import React, {RefObject} from 'react';
interface props {
width?: string;
height?: string;
banner?: JSX.Element;
title: string;
onHide: () => void
}
/**
* wrapper class for generic types of popups
*/
class PopupBase extends React.Component {
constructor(props) {
class PopupBase extends React.Component<props> {
private wrapperRef: RefObject<HTMLDivElement>;
private framedimensions: { minHeight: string | undefined; width: string | undefined; height: string | undefined };
constructor(props: props) {
super(props);
this.state = {items: []};
@ -25,7 +36,7 @@ class PopupBase extends React.Component {
};
}
componentDidMount() {
componentDidMount(): void {
document.addEventListener('mousedown', this.handleClickOutside);
document.addEventListener('keyup', this.keypress);
@ -35,13 +46,13 @@ class PopupBase extends React.Component {
}
}
componentWillUnmount() {
componentWillUnmount(): void {
// remove the appended listeners
document.removeEventListener('mousedown', this.handleClickOutside);
document.removeEventListener('keyup', this.keypress);
}
render() {
render(): JSX.Element {
const themeStyle = GlobalInfos.getThemeStyle();
return (
<div style={this.framedimensions} className={[style.popup, themeStyle.thirdbackground].join(' ')} ref={this.wrapperRef}>
@ -61,8 +72,8 @@ class PopupBase extends React.Component {
/**
* Alert if clicked on outside of element
*/
handleClickOutside(event) {
if (this.wrapperRef && !this.wrapperRef.current.contains(event.target)) {
handleClickOutside(event: MouseEvent): void {
if (this.wrapperRef && this.wrapperRef.current && !this.wrapperRef.current.contains(event.target as Node)) {
this.props.onHide();
}
}
@ -71,7 +82,7 @@ class PopupBase extends React.Component {
* key event handling
* @param event keyevent
*/
keypress(event) {
keypress(event: KeyboardEvent): void {
// hide if escape is pressed
if (event.key === 'Escape') {
this.props.onHide();
@ -81,16 +92,17 @@ class PopupBase extends React.Component {
/**
* make the element drag and droppable
*/
dragElement() {
dragElement(): void {
let xOld = 0, yOld = 0;
const elmnt = this.wrapperRef.current;
if (elmnt === null) return;
if (elmnt.firstChild === null) return;
elmnt.firstChild.onmousedown = dragMouseDown;
(elmnt.firstChild as HTMLDivElement).onmousedown = dragMouseDown;
function dragMouseDown(e) {
function dragMouseDown(e: MouseEvent): void {
e.preventDefault();
// get the mouse cursor position at startup:
xOld = e.clientX;
@ -100,7 +112,7 @@ class PopupBase extends React.Component {
document.onmousemove = elementDrag;
}
function elementDrag(e) {
function elementDrag(e: MouseEvent): void {
e.preventDefault();
// calculate the new cursor position:
const dx = xOld - e.clientX;
@ -108,11 +120,12 @@ class PopupBase extends React.Component {
xOld = e.clientX;
yOld = e.clientY;
// set the element's new position:
if (elmnt === null) return;
elmnt.style.top = (elmnt.offsetTop - dy) + 'px';
elmnt.style.left = (elmnt.offsetLeft - dx) + 'px';
}
function closeDragElement() {
function closeDragElement(): void {
// stop moving when mouse button is released:
document.onmouseup = null;
document.onmousemove = null;

View File

@ -5,35 +5,42 @@ import {Link} from 'react-router-dom';
import GlobalInfos from '../../utils/GlobalInfos';
import {callAPIPlain} from '../../utils/Api';
interface PreviewProps{
name: string;
movie_id: number;
}
interface PreviewState {
previewpicture: string | null;
}
/**
* Component for single preview tile
* floating side by side
*/
class Preview extends React.Component {
constructor(props, context) {
super(props, context);
class Preview extends React.Component<PreviewProps, PreviewState> {
constructor(props: PreviewProps) {
super(props);
this.state = {
previewpicture: null,
name: null
previewpicture: null
};
}
componentDidMount() {
componentDidMount(): void {
callAPIPlain('video.php', {action: 'readThumbnail', movieid: this.props.movie_id}, (result) => {
this.setState({
previewpicture: result,
name: this.props.name
previewpicture: result
});
});
}
render() {
render(): JSX.Element {
const themeStyle = GlobalInfos.getThemeStyle();
return (
<Link to={'/player/' + this.props.movie_id}>
<div className={style.videopreview + ' ' + themeStyle.secbackground + ' ' + themeStyle.preview}>
<div className={style.previewtitle + ' ' + themeStyle.lighttextcolor}>{this.state.name}</div>
<div className={style.previewtitle + ' ' + themeStyle.lighttextcolor}>{this.props.name}</div>
<div className={style.previewpic}>
{this.state.previewpicture !== null ?
<img className={style.previewimage}
@ -55,8 +62,8 @@ class Preview extends React.Component {
/**
* Component for a Tag-name tile (used in category page)
*/
export class TagPreview extends React.Component {
render() {
export class TagPreview extends React.Component<{name: string}> {
render(): JSX.Element {
const themeStyle = GlobalInfos.getThemeStyle();
return (
<div

View File

@ -2,7 +2,7 @@ import React from 'react';
import styles from './Tag.module.css';
import {Link} from 'react-router-dom';
import {TagType} from '../../api/VideoTypes';
import {TagType} from '../../types/VideoTypes';
interface props {
onclick?: (_: string) => void

View File

@ -1,14 +1,14 @@
import React from 'react';
import Preview from '../Preview/Preview';
import style from './VideoContainer.module.css';
import {VideoUnloadedType} from '../../api/VideoTypes';
import {VideoTypes} from '../../types/ApiTypes';
interface props {
data: VideoUnloadedType[]
data: VideoTypes.VideoUnloadedType[]
}
interface state {
loadeditems: VideoUnloadedType[];
loadeditems: VideoTypes.VideoUnloadedType[];
selectionnr: number;
}