Move EditableListItem component to EditableList file + cleanup

This commit is contained in:
hensm
2020-07-23 18:58:22 +01:00
parent ab6fe6de7e
commit 1fad5fce22
3 changed files with 132 additions and 136 deletions

View File

@@ -2,7 +2,6 @@
"use strict";
import React, { Component } from "react";
import EditableListItem from "./EditableListItem";
const _ = browser.i18n.getMessage;
@@ -50,9 +49,7 @@ export default class EditableList extends Component<
{ this.state.rawView
? (
<textarea className="editable-list__raw-view"
rows={ this.props.data.length > 10
? this.props.data.length
: 10 }
rows={ Math.min(this.props.data.length, 10) }
value={ this.state.rawViewValue }
onChange={ this.handleRawViewTextAreaChange }
ref={ el => { this.rawViewTextArea = el; }}>
@@ -66,6 +63,7 @@ export default class EditableList extends Component<
onRemove={ this.handleItemRemove }
onEdit={ this.handleItemEdit }
key={ i } /> )}
{ this.state.addingNewItem &&
<EditableListItem text=""
itemPattern={ this.props.itemPattern }
@@ -73,7 +71,6 @@ export default class EditableList extends Component<
onRemove={ this.handleNewItemRemove }
onEdit={ this.handleNewItemEdit }
editing={ true } /> }
</ul>
)}
<hr />
@@ -85,12 +82,14 @@ export default class EditableList extends Component<
type="button">
<img src="assets/photon_new.svg" alt="icon, add" />
</button> }
{ this.state.rawView &&
<button className="editable-list__save-raw-button"
onClick={ this.handleSaveRaw }
type="button">
{ _("optionsUserAgentWhitelistSaveRaw") }
</button> }
<button className="editable-list__view-button"
onClick={ this.handleSwitchView }
type="button">
@@ -113,8 +112,8 @@ export default class EditableList extends Component<
private handleItemEdit (item: string, newValue: string) {
this.props.onChange(this.props.data.map(
currentItem => currentItem === item
? newValue
: currentItem));
? newValue
: currentItem));
}
private handleSwitchView () {
@@ -188,3 +187,128 @@ export default class EditableList extends Component<
});
}
}
interface EditableListItemProps {
text: string;
itemPattern: RegExp;
editing?: boolean;
itemPatternError (err?: string): string;
onRemove (item: string): void;
onEdit (item: string, newValue: string): void;
}
interface EditableListItemState {
editing: boolean;
editValue: string;
}
class EditableListItem extends Component<
EditableListItemProps, EditableListItemState> {
private input: (HTMLInputElement | null) = null;
constructor (props: EditableListItemProps) {
super(props);
this.state = {
editing: this.props.editing || false
, editValue: ""
};
this.handleRemove = this.handleRemove.bind(this);
this.handleEditBegin = this.handleEditBegin.bind(this);
this.handleEditEnd = this.handleEditEnd.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
this.handleInputKeyPress = this.handleInputKeyPress.bind(this);
}
public render () {
const selected = this.state.editing
? "editable-list__item--selected" : "";
return (
<li className={`editable-list__item ${selected}`}>
<div className="editable-list__title"
onDoubleClick={ this.handleEditBegin }>
{ this.state.editing
? <input className="editable-list__edit-field"
type="text"
ref={ input => this.input = input }
value={ this.state.editValue }
onBlur={ this.handleEditEnd }
onChange={ this.handleInputChange }
onKeyPress={ this.handleInputKeyPress }/>
: this.props.text }
</div>
<button className="editable-list__edit-button ghost"
title={ _("optionsUserAgentWhitelistEditItem") }
onClick={ this.handleEditBegin }
type="button">
<img src="assets/photon_edit.svg" alt="icon, edit" />
</button>
<button className="editable-list__remove-button ghost"
title={ _("optionsUserAgentWhitelistRemoveItem") }
onClick={ this.handleRemove }
type="button">
<img src="assets/photon_delete.svg" alt="icon, remove" />
</button>
</li>
);
}
private stopEditing (input: HTMLInputElement) {
if (this.props.editing
&& !this.props.itemPattern.test(this.state.editValue)) {
input.setCustomValidity(this.props.itemPatternError());
}
if (!input.validity.valid) {
return;
}
this.props.onEdit(this.props.text, this.state.editValue);
this.setState({
editing: false
, editValue: ""
});
}
private handleRemove () {
this.props.onRemove(this.props.text);
}
private handleEditBegin () {
this.setState({
editing: true
, editValue: this.props.text
}, () => {
this.input?.focus();
this.input?.select();
});
}
private handleEditEnd (ev: React.FocusEvent<HTMLInputElement>) {
this.stopEditing(ev.target);
}
private handleInputChange (ev: React.ChangeEvent<HTMLInputElement>) {
this.setState({
editValue: ev.target.value
});
// If invalid, set custom error from parent
ev.target.setCustomValidity(!this.props.itemPattern.test(ev.target.value)
? this.props.itemPatternError()
: "");
}
private handleInputKeyPress (ev: React.KeyboardEvent<HTMLInputElement>) {
if (ev.key === "Enter") {
this.stopEditing(ev.target as HTMLInputElement);
}
}
}

View File

@@ -1,129 +0,0 @@
/* tslint:disable:max-line-length */
"use strict";
import React, { Component } from "react";
const _ = browser.i18n.getMessage;
interface EditableListItemProps {
text: string;
itemPattern: RegExp;
editing?: boolean;
itemPatternError (err?: string): string;
onRemove (item: string): void;
onEdit (item: string, newValue: string): void;
}
interface EditableListItemState {
editing: boolean;
editValue: string;
}
export default class EditableListItem extends Component<
EditableListItemProps, EditableListItemState> {
private input: (HTMLInputElement | null) = null;
constructor (props: EditableListItemProps) {
super(props);
this.state = {
editing: this.props.editing || false
, editValue: ""
};
this.handleRemove = this.handleRemove.bind(this);
this.handleEditBegin = this.handleEditBegin.bind(this);
this.handleEditEnd = this.handleEditEnd.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
this.handleInputKeyPress = this.handleInputKeyPress.bind(this);
}
public render () {
const selected = this.state.editing
? "editable-list__item--selected" : "";
return (
<li className={`editable-list__item ${selected}`}>
<div className="editable-list__title"
onDoubleClick={ this.handleEditBegin }>
{ this.state.editing
? <input className="editable-list__edit-field"
type="text"
ref={ input => this.input = input }
value={ this.state.editValue }
onBlur={ this.handleEditEnd }
onChange={ this.handleInputChange }
onKeyPress={ this.handleInputKeyPress }/>
: this.props.text }
</div>
<button className="editable-list__edit-button ghost"
title={ _("optionsUserAgentWhitelistEditItem") }
onClick={ this.handleEditBegin }
type="button">
<img src="assets/photon_edit.svg" alt="icon, edit" />
</button>
<button className="editable-list__remove-button ghost"
title={ _("optionsUserAgentWhitelistRemoveItem") }
onClick={ this.handleRemove }
type="button">
<img src="assets/photon_delete.svg" alt="icon, remove" />
</button>
</li>
);
}
private stopEditing (input: HTMLInputElement) {
if (this.props.editing
&& !this.props.itemPattern.test(this.state.editValue)) {
input.setCustomValidity(this.props.itemPatternError());
}
if (!input.validity.valid) {
return;
}
this.props.onEdit(this.props.text, this.state.editValue);
this.setState({
editing: false
, editValue: ""
});
}
private handleRemove () {
this.props.onRemove(this.props.text);
}
private handleEditBegin () {
this.setState({
editing: true
, editValue: this.props.text
}, () => {
this.input?.focus();
this.input?.select();
});
}
private handleEditEnd (ev: React.FocusEvent<HTMLInputElement>) {
this.stopEditing(ev.target);
}
private handleInputChange (ev: React.ChangeEvent<HTMLInputElement>) {
this.setState({
editValue: ev.target.value
});
if (!this.props.itemPattern.test(ev.target.value)) {
ev.target.setCustomValidity(this.props.itemPatternError());
} else {
ev.target.setCustomValidity("");
}
}
private handleInputKeyPress (ev: React.KeyboardEvent<HTMLInputElement>) {
if (ev.key === "Enter") {
this.stopEditing(ev.target as HTMLInputElement);
}
}
}

View File

@@ -409,6 +409,7 @@ button.ghost:not(:hover) {
.editable-list__raw-view {
max-height: 300px;
overflow-y: auto;
resize: vertical;
width: 100%;
}