This blog is to (1) implement a simple TodoList in React and (2) test the JS embedding framework. Previously, the CKeditor (and Jinja2) will block and consume the in-text Javascript in a terrible way, which makes it difficult to embed JS script in the text when editing.
In the updated design, an additional field is added especially for the script. I kept the original design where administrators can share some common scripts (like code-highlighting scripts) among different blogs by adding common scripts to the dataset and simply selecting them when editing.
Update2:
Using ReactCSSTransitionGroup to implement a dynamic adding/deleting. https://reactjs.org/docs/animation.html
TodoList:
Source code:
<style>
.todoItem-enter {
opacity: 0.01;
max-height: 0px;
overflow: hidden;
}
.todoItem-enter.todoItem-enter-active {
opacity: 1;
max-height: 200px;
transition: all 300ms ease-in;
}
.todoItem-leave {
opacity: 1;
max-height: 200px;
}
.todoItem-leave.todoItem-leave-active {
opacity: 0.01;
max-height: 0;
transition: all 300ms ease-out;
}
</style>
<script type="text/babel">
class TodoItem extends React.Component {
constructor(props) {
super(props);
this.state = {checked: false};
this.toggle = this.toggle.bind(this);
this.onDelete = this.onDelete.bind(this);
}
toggle(e) {
if (e.target.classList.contains('btn')) return;
this.setState((prevState) => ({ checked: !prevState.checked }) );
}
onDelete() {
this.props.onDelete(this.props.index);
}
render() {
return (
<div className={"m-2 d-flex flex-row align-items-center border rounded "+
(this.state.checked?"border-success":"border-danger")}
onClick={this.toggle}>
<div className="pl-2 p-1" >
{this.state.checked?
(<i className="far fa-check-square fa-lg"></i>)
:
(<i className="far fa-square fa-lg"></i>)
}
</div>
<div className="p-1 w-100">
{this.state.checked?
(<del>{this.props.text}</del>)
:
this.props.text
}
</div>
<div className="ml-auto p-1">
<button className="btn btn-sm btn-outline-warning"
onClick={this.onDelete}
>delete</button>
</div>
</div>
);
}
}
const ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
class TodoList extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<ReactCSSTransitionGroup
transitionName="todoItem"
transitionEnterTimeout={300}
transitionLeaveTimeout={300}>
{ this.props.todos.map((todo, index) =>
<TodoItem key={todo.addTime} index={index} text={todo.text}
onDelete={this.props.deleteTodoItem}/>
)}
</ReactCSSTransitionGroup>
</div>
);
}
}
class TodoInput extends React.Component {
constructor(props) {
super(props);
this.state = {text:''};
this.onTextChange = this.onTextChange.bind(this);
this.onAdd = this.onAdd.bind(this);
this.onKeyPress = this.onKeyPress.bind(this);
}
onKeyPress(e) {
if (e.key=='Enter')
this.onAdd();
}
onTextChange(e) {
this.setState({ text: e.target.value });
}
onAdd() {
if (this.state.text.length == 0) return;
this.props.addTodoItem(
(new Date()).getTime().toString(),
this.state.text
);
this.setState({text:''});
}
render() {
return (
<div className="m-2 d-flex flex-row align-items-center border rounded border-success">
<div className="p-1 pl-2 w-100">
<input className="form-control" value={this.state.text}
onKeyPress={this.onKeyPress}
onChange={this.onTextChange} />
</div>
<div className="ml-auto p-1">
<button className="btn btn-outline-success btn-sm"
onClick={this.onAdd} >Add</button>
</div>
</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = { todos: this.props.todos };
this.deleteTodoItem = this.deleteTodoItem.bind(this);
this.addTodoItem = this.addTodoItem.bind(this);
}
deleteTodoItem(index) {
this.setState(
(prevState) => ({
todos:
[...prevState.todos.slice(0, index),
...prevState.todos.slice(index+1)]
})
);
}
addTodoItem(addTime, text) {
this.setState(
(prevState) => ({
todos: [...prevState.todos, {addTime, text}]
}) );
}
render() {
return (
<div>
<TodoList todos={this.state.todos}
deleteTodoItem={this.deleteTodoItem}/>
<TodoInput addTodoItem={this.addTodoItem} />
</div>
)
}
}
const items = [{addTime:1, text:"1st thing"}
, {addTime:2, text:"2nd thing"}
, {addTime:3, text:"3rd thing"}];
ReactDOM.render(
// <TodoItem text={"asdf"} />,
// <TodoList todos={items} />,
<App todos={items} />,
document.getElementById("react-root")
);
</script>