|
// pages/tags.js |
|
// At 127.0.0.1:3000/tags on a next dev run |
|
import React from "react"; |
|
|
|
const ButtonStyle = { |
|
border: "none", |
|
backgroundColor: "#c3e2e9", |
|
display: "inline-block", |
|
textDecoration: "none", |
|
padding: ".25em 0.5em", |
|
margin: "0em 0em 0em .2em", |
|
borderRadius: "5px 5px 5px 5px", |
|
font: "Monospace", |
|
fontFamily: "Monospace", |
|
verticalAlign:"middle", |
|
fontSize: ".75em" |
|
} |
|
const textStyle ={ |
|
fontFamily: "Monospace", |
|
font: "Monospace", |
|
} |
|
|
|
const spanStyle ={ |
|
verticalAlign:"middle", |
|
listStyleType:"none", |
|
backgroundColor: "#93b7bf", |
|
border: "none", |
|
padding: ".25em 0.2em", |
|
borderRadius: "3px 3px 3px 3px", |
|
margin: "0.5em 0.5em 0.5em 0.5em", |
|
textOverflow:"ellipsis", |
|
maxWidth:"50px", |
|
overflow:"hidden" |
|
} |
|
|
|
|
|
function dropTag(tags, dropTag) { |
|
return tags.filter((tag) => tag !== dropTag); |
|
} |
|
|
|
function appendTag(tags, newTag) { |
|
// Since arrays are mutable, we use `concat` to return a copy of the original |
|
// array with our new tag |
|
// Question: is the reason to not just change it mutably that that isn't clean |
|
// and against the ethos of React? |
|
|
|
// NOTE: We're now using Set to guarantee uniqueness |
|
// Question: why do we need to transform it back into an array for this to work? |
|
var newtags = [].concat(tags, newTag); |
|
var uniqtags = new Set(newtags); |
|
// I'm assuming we can do something more interesting than alphabetical sort later on |
|
return [...uniqtags].sort() |
|
} |
|
|
|
// Heavily based on the template provided in |
|
// https://facebook.github.io/react/docs/forms.html#controlled-components |
|
class TagForm extends React.Component{ |
|
constructor(props){ |
|
super(props); |
|
this.state = {value: ""}; |
|
this.handleChange = this.handleChange.bind(this) |
|
this.handleSubmit = this.handleSubmit.bind(this) |
|
|
|
} |
|
|
|
handleChange(event){ |
|
this.setState({value: event.target.value}); |
|
} |
|
|
|
handleSubmit(event){ |
|
event.preventDefault(); |
|
this.props.onChange(appendTag(this.props.tags, this.state.value )); |
|
this.state.value = ""; |
|
} |
|
|
|
render() { |
|
return ( |
|
// Q: if you uncomment line 42, this will work; why doesn't line 43? |
|
// <form onSubmit={(e) => {this.handleSubmit(e); e.preventDefault();}}> |
|
<form onSubmit={this.handleSubmit}> |
|
<label> |
|
Tag: |
|
<input type="text" value={this.state.value} onChange={this.handleChange}/> |
|
</label> |
|
</form> |
|
); |
|
} |
|
} |
|
// Filled this in with my real implementation (mostly above in TagForm) |
|
const Tags = props => ( |
|
<div> |
|
{/* |
|
Question: I need to pass this down to TagForm in order for it to bubble up |
|
correctly. Is there a more idiomatic way to approach this? */} |
|
<TagForm tags={props.tags} onChange={props.onChange}/> |
|
<p> |
|
{props.tags.map( |
|
tag => |
|
<span |
|
style={ |
|
{...spanStyle, |
|
...textStyle} |
|
} |
|
key={tag} |
|
> |
|
{tag} |
|
<Remover |
|
tags={props.tags} |
|
tag={tag} |
|
onChange={props.onChange} |
|
/> |
|
</span>) |
|
} |
|
</p> |
|
</div> |
|
); |
|
|
|
|
|
const Remover = props => ( |
|
<button style={ButtonStyle} onClick={ |
|
() => props.onChange(dropTag(props.tags, props.tag))} |
|
> |
|
× |
|
</button> |
|
); |
|
|
|
|
|
export default class TagPage extends React.Component { |
|
tagUpdate = tags => this.setState({ tags }); |
|
|
|
constructor(props) { |
|
super(props); |
|
this.state = { |
|
tags: ["test"] |
|
}; |
|
} |
|
|
|
render() { |
|
return <Tags tags={this.state.tags} onChange={this.tagUpdate} />; |
|
} |
|
} |
@willingc