Skip to content
This repository has been archived by the owner on Jun 19, 2018. It is now read-only.

Relations app #50

Merged
merged 7 commits into from
Apr 3, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion react/scss/_common.scss
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
@import '~normalize.css';
@import '~normalize.css';
@import '~bootstrap/dist/css/bootstrap.css';

2 changes: 1 addition & 1 deletion react/scss/composers-app.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@import '~bootstrap/dist/css/bootstrap.css';
@import 'common';

.pagination__header, .pagination__search, .pagination__suggestions, .pagination__content, .pagination__footer {
margin: 20px 0 20px 0;
Expand Down
44 changes: 44 additions & 0 deletions react/scss/relations-app.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
@import 'common';

.application__relations {
margin-top: 30px;
}

.application__search__field {
margin: 20px 0 20px 0;
}

.entity__title {
margin-bottom: 0;
}

.entity, .relation {
text-align: center;
font-size: 14px;
}

.entity-list__suggestions, .relation-list__suggestions {
text-align: center;
}

.entity-list__suggestions__title, .relation-list__suggestions__title {
border-bottom: 1px solid #999999;
padding-bottom: 10px;
}
.entity-list__suggestions__suggestion-list__item, .relation-list__suggestions__suggestion-list__item {
cursor: pointer;
margin-bottom: 10px;
font-size: 14px;
}

.entity-suggestion__title, .relation-suggestion__title {
margin-bottom: 0;
}

.entity-list__entity-list__item, .relation-list__entity-list__item {
margin-bottom: 10px;
}

.badge {
font-size: 9px;
}
166 changes: 166 additions & 0 deletions react/src/components/relations-app/Application.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import React from 'react';
import { Container, Row, Col } from 'reactstrap';

import Entity from './Entity';
import EntityList from './EntityList';
import Relation from './Relation';
import RelationList from './RelationList';
import SearchField from './SearchField';
import loadSuggestions from './remote/loadSuggestions';
import loadRelations from './remote/loadRelations';
import loadEntities from './remote/loadEntities';
import '../../../scss/relations-app.scss';

class Application extends React.Component {
constructor(props) {
super(props);

this.state = {
entity: {
title: 'Mozart',
type: 'composer',
},
relationSuggestion: null,
entitySuggestion: null,
query: '',
entities: [],
relations: [],
entitiesSuggestions: [],
relationsSuggestions: [],
showSuggestions: false,
loadingSuggestions: false,
loadingEntities: false,
loadingRelations: false,
};
}

onSearchChange(query) {
this.setState({
query,
showSuggestions: true,
loadingSuggestions: true,
loadingEntities: false,
loadingRelations: false,
relationSuggestion: null,
entitySuggestion: null,
entities: [],
relations: [],
});

loadSuggestions(query).then(suggestions => {
this.setState({
...suggestions,
loadingSuggestions: false,
});
});
}

onEntitySuggestionClick(entitySuggestion) {
this.setState({
showSuggestions: false,
loadingRelations: true,
entitySuggestion,
});

loadRelations(entitySuggestion).then(result => {
this.setState({
...result,
loadingRelations: false,
});
});
}

onRelationSuggestionClick(relationSuggestion) {
this.setState({
showSuggestions: false,
loadingEntities: true,
relationSuggestion,
});

loadEntities(relationSuggestion).then(result => {
this.setState({
...result,
loadingEntities: false,
});
});
}

render() {
const {
query,
entity,
entities,
relations,
entitiesSuggestions,
relationsSuggestions,
showSuggestions,
loadingSuggestions,
loadingEntities,
loadingRelations,
relationSuggestion,
entitySuggestion,
} = this.state;

return (
<Container className="application">
<Row>
<Col>
<SearchField
query={query}
className="application__search__field"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename to application__search-field

handleSearchChange={(_query) => this.onSearchChange(_query)}
handleSearchClick={() => 3}
/>
</Col>
</Row>
<Row>
<Col sm={!!query ? '4' : '12'} xs="12">
<Entity
entity={entity}
/>
</Col>
{!!query && (
<Col sm="4" xs="6">
{!!relationSuggestion && (
<Relation
relation={relationSuggestion}
/>
)}
{!relationSuggestion && (
<RelationList
relations={relations}
relationsSuggestions={relationsSuggestions}
showSuggestions={showSuggestions}
loadingSuggestions={loadingSuggestions}
loadingRelations={loadingRelations}
onRelationSuggestionClick={(_relationSuggestion) => this.onRelationSuggestionClick(_relationSuggestion)}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

split lines

/>
)}
</Col>
)}
{!!query && (
<Col sm="4" xs="6">
{!!entitySuggestion && (
<Entity
entity={entitySuggestion}
/>
)}
{!entitySuggestion && (
<EntityList
entities={entities}
entitiesSuggestions={entitiesSuggestions}
showSuggestions={showSuggestions}
loadingSuggestions={loadingSuggestions}
loadingEntities={loadingEntities}
onEntitySuggestionClick={(_entitySuggestion) => this.onEntitySuggestionClick(_entitySuggestion)}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

split lines

/>
)}
</Col>
)}
</Row>
</Container>
);
}
}

export default Application;
26 changes: 26 additions & 0 deletions react/src/components/relations-app/Entity.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';
import { Badge } from 'reactstrap';


class Entity extends React.Component {
render() {
const { entity, className } = this.props;

return (
<div className={`entity ${className}`}>
<p className="entity__title"> {entity.title} </p>
<Badge className="badge"> {entity.type} </Badge>
</div>
);
}
}

Entity.propTypes = {
entity: React.PropTypes.shape({
title: React.PropTypes.string.isRequired,
type: React.PropTypes.string.isRequired,
}).isRequired,
className: React.PropTypes.string,
};

export default Entity;
107 changes: 107 additions & 0 deletions react/src/components/relations-app/EntityList.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import _ from 'lodash';
import React from 'react';
import { Progress, Pagination, PaginationLink, PaginationItem } from 'reactstrap';

import Entity from './Entity';
import EntitySuggestion from './EntitySuggestion';


const LIMIT = 5;
const MAX_PAGES = 5;

class EntityList extends React.Component {
constructor(props) {
super(props);

this.state = {
page: 1,
};
}

render() {
const {
entities,
showSuggestions,
entitiesSuggestions,
loadingSuggestions,
onEntitySuggestionClick,
loadingEntities,
} = this.props;

const { page } = this.state;

const numberPages = Math.min(MAX_PAGES, Math.ceil(entities.length / LIMIT));
const displayEntitiesSuggestions = entitiesSuggestions.slice(0, LIMIT);
const displayEntities = entities.slice((page - 1) * LIMIT, page * LIMIT);

return (
<div>
{!!showSuggestions && (
<div className="entity-list__suggestions">
<h6 className="entity-list__suggestions__title"> Suggestions... </h6>
{!loadingSuggestions && (
<div className="entity-list__suggestions__suggestion-list">
{_.map(displayEntitiesSuggestions, entitySuggestion =>
<EntitySuggestion
key={`${entitySuggestion.title}.${entitySuggestion.type}`}
className="entity-list__suggestions__suggestion-list__item"
entitySuggestion={entitySuggestion}
onEntitySuggestionClick={onEntitySuggestionClick}
/>
)}
</div>
)}
{!!loadingSuggestions && (
<Progress value={75} />
)}
</div>
)}

{!showSuggestions && (
<div>
{!loadingEntities && (
<div className="entity-list__entity-list">
{_.map(displayEntities, entity =>
<Entity
key={`${entity.title}.${entity.type}`}
className="entity-list__entity-list__item"
entity={entity}
/>
)}
<Pagination style={{ display: 'flex', justifyContent: 'center' }}>
{new Array(numberPages).fill(undefined).map((____, index) =>
<PaginationItem key={index}>
<PaginationLink href="#" onClick={() => this.setState({ page: index + 1 })}>
{index + 1}
</PaginationLink>
</PaginationItem>
)}
</Pagination>
</div>
)}
{!!loadingEntities && (
<Progress value={55}> Loading ... </Progress>
)}
</div>
)}
</div>
);
}
}

EntityList.propTypes = {
entities: React.PropTypes.arrayOf(React.PropTypes.shape({
title: React.PropTypes.string,
type: React.PropTypes.string,
})).isRequired,
entitiesSuggestions: React.PropTypes.arrayOf(React.PropTypes.shape({
title: React.PropTypes.string,
type: React.PropTypes.string,
})).isRequired,
onEntitySuggestionClick: React.PropTypes.func.isRequired,
showSuggestions: React.PropTypes.bool.isRequired,
loadingSuggestions: React.PropTypes.bool.isRequired,
loadingEntities: React.PropTypes.bool.isRequired,
};

export default EntityList;
30 changes: 30 additions & 0 deletions react/src/components/relations-app/EntitySuggestion.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react';
import { Badge } from 'reactstrap';


class EntitySuggestion extends React.Component {
render() {
const {
className,
entitySuggestion,
onEntitySuggestionClick,
} = this.props;

return (
<div className={`entity-suggestion ${className}`} onClick={() => onEntitySuggestionClick(entitySuggestion)}>
<p className="entity-suggestion__title"> {entitySuggestion.title} </p>
<Badge className="badge"> {entitySuggestion.type} </Badge>
</div>
);
}
}

EntitySuggestion.propTypes = {
entitySuggestion: React.PropTypes.shape({
title: React.PropTypes.string,
}).isRequired,
onEntitySuggestionClick: React.PropTypes.func.isRequired,
className: React.PropTypes.string,
};

export default EntitySuggestion;
Loading