New simplfied collection list when adding a sketch
This commit is contained in:
parent
18af6aed3a
commit
08fd6b826d
10 changed files with 362 additions and 66 deletions
11
client/images/check_encircled.svg
Normal file
11
client/images/check_encircled.svg
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="check-encircled" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
|
||||
y="0px" viewBox="0 0 100 100" style="enable-background:new 0 0 100 100;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<path id="circle" d="M50,26.5c-14.4,0-26,11.5-25.9,25.9c0,14.5,11.6,26,26,26c14.5,0,25.9-11.5,25.9-26C76,38,64.5,26.5,50,26.5z
|
||||
M47.6,66.6L34,53.4l5.6-5.5l7.1,6.8l14-15.4l6.1,5.5L47.6,66.6z"/>
|
||||
<polygon id="check" class="st0 counter-form" points="46.7,54.7 39.6,47.9 34,53.4 47.6,66.6 66.8,44.8 60.7,39.3 "/>
|
||||
</svg>
|
After Width: | Height: | Size: 727 B |
12
client/images/close.svg
Normal file
12
client/images/close.svg
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="close" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 100 100" style="enable-background:new 0 0 100 100;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<path id="circle" d="M50,26.5c-14.4,0-26,11.5-25.9,25.9c0,14.5,11.6,26,26,26c14.5,0,25.9-11.5,25.9-26C76,38,64.5,26.5,50,26.5z
|
||||
M63.4,60.2L58,65.6l-7.9-8L42,65.7l-5.4-5.4l8.1-8l-8.1-8l5.4-5.4l8,8.1l8-8l5.4,5.4l-8,7.8L63.4,60.2z"/>
|
||||
<polygon id="x" class="st0 counter-form" points="58,39 50,47 42,38.9 36.6,44.3 44.7,52.3 36.6,60.3 42,65.7 50.1,57.6 58,65.6 63.4,60.2
|
||||
55.4,52.2 63.4,44.4 "/>
|
||||
</svg>
|
After Width: | Height: | Size: 801 B |
169
client/modules/IDE/components/AddToCollectionList.jsx
Normal file
169
client/modules/IDE/components/AddToCollectionList.jsx
Normal file
|
@ -0,0 +1,169 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import InlineSVG from 'react-inlinesvg';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import * as ProjectActions from '../actions/project';
|
||||
import * as ProjectsActions from '../actions/projects';
|
||||
import * as CollectionsActions from '../actions/collections';
|
||||
import * as ToastActions from '../actions/toast';
|
||||
import * as SortingActions from '../actions/sorting';
|
||||
import getSortedCollections from '../selectors/collections';
|
||||
import Loader from '../../App/components/loader';
|
||||
import QuickAddList from './QuickAddList/QuickAddList';
|
||||
|
||||
const projectInCollection = (project, collection) => collection.items.find(item => item.project.id === project.id) != null;
|
||||
|
||||
class CollectionList extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
if (props.projectId) {
|
||||
props.getProject(props.projectId);
|
||||
}
|
||||
|
||||
this.props.getCollections(this.props.username);
|
||||
|
||||
this.state = {
|
||||
hasLoadedData: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (prevProps.loading === true && this.props.loading === false) {
|
||||
// eslint-disable-next-line react/no-did-update-set-state
|
||||
this.setState({
|
||||
hasLoadedData: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getTitle() {
|
||||
if (this.props.username === this.props.user.username) {
|
||||
return 'p5.js Web Editor | My collections';
|
||||
}
|
||||
return `p5.js Web Editor | ${this.props.username}'s collections`;
|
||||
}
|
||||
|
||||
handleCollectionAdd = (collection) => {
|
||||
this.props.addToCollection(collection.id, this.props.project.id);
|
||||
}
|
||||
|
||||
handleCollectionRemove = (collection) => {
|
||||
this.props.removeFromCollection(collection.id, this.props.project.id);
|
||||
}
|
||||
|
||||
handleAddRemove = (collection) => {
|
||||
if (projectInCollection(this.props.project, collection)) {
|
||||
this.handleCollectionRemove(collection);
|
||||
} else {
|
||||
this.handleCollectionAdd(collection);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const username = this.props.username !== undefined ? this.props.username : this.props.user.username;
|
||||
const { collections, project } = this.props;
|
||||
const hasCollections = collections.length > 0;
|
||||
const collectionWithSketchStatus = collections.map(collection => ({
|
||||
...collection,
|
||||
url: `/${collection.owner.username}/collections/${collection.id}`,
|
||||
isAdded: projectInCollection(project, collection),
|
||||
}));
|
||||
|
||||
let content = null;
|
||||
|
||||
if (this.props.loading && !this.state.hasLoadedData) {
|
||||
content = <Loader />;
|
||||
} else if (hasCollections) {
|
||||
content = <QuickAddList items={collectionWithSketchStatus} onSelect={this.handleAddRemove} />;
|
||||
} else {
|
||||
content = 'No collections';
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="sketches-table-container">
|
||||
<Helmet>
|
||||
<title>{this.getTitle()}</title>
|
||||
</Helmet>
|
||||
|
||||
{content}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const ProjectShape = PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
createdAt: PropTypes.string.isRequired,
|
||||
updatedAt: PropTypes.string.isRequired,
|
||||
user: PropTypes.shape({
|
||||
username: PropTypes.string.isRequired
|
||||
}).isRequired,
|
||||
});
|
||||
|
||||
const ItemsShape = PropTypes.shape({
|
||||
createdAt: PropTypes.string.isRequired,
|
||||
updatedAt: PropTypes.string.isRequired,
|
||||
project: ProjectShape
|
||||
});
|
||||
|
||||
CollectionList.propTypes = {
|
||||
user: PropTypes.shape({
|
||||
username: PropTypes.string,
|
||||
authenticated: PropTypes.bool.isRequired
|
||||
}).isRequired,
|
||||
projectId: PropTypes.string.isRequired,
|
||||
getCollections: PropTypes.func.isRequired,
|
||||
getProject: PropTypes.func.isRequired,
|
||||
addToCollection: PropTypes.func.isRequired,
|
||||
removeFromCollection: PropTypes.func.isRequired,
|
||||
collections: PropTypes.arrayOf(PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
description: PropTypes.string,
|
||||
createdAt: PropTypes.string.isRequired,
|
||||
updatedAt: PropTypes.string.isRequired,
|
||||
items: PropTypes.arrayOf(ItemsShape),
|
||||
})).isRequired,
|
||||
username: PropTypes.string,
|
||||
loading: PropTypes.bool.isRequired,
|
||||
project: PropTypes.shape({
|
||||
id: PropTypes.string,
|
||||
owner: PropTypes.shape({
|
||||
id: PropTypes.string
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
CollectionList.defaultProps = {
|
||||
project: {
|
||||
id: undefined,
|
||||
owner: undefined
|
||||
},
|
||||
username: undefined
|
||||
};
|
||||
|
||||
function mapStateToProps(state, ownProps) {
|
||||
return {
|
||||
user: state.user,
|
||||
collections: getSortedCollections(state),
|
||||
sorting: state.sorting,
|
||||
loading: state.loading,
|
||||
project: state.project,
|
||||
projectId: ownProps && ownProps.params ? ownProps.prams.project_id : null,
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return bindActionCreators(
|
||||
Object.assign({}, CollectionsActions, ProjectsActions, ProjectActions, ToastActions, SortingActions),
|
||||
dispatch
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(CollectionList);
|
27
client/modules/IDE/components/QuickAddList/Icons.jsx
Normal file
27
client/modules/IDE/components/QuickAddList/Icons.jsx
Normal file
|
@ -0,0 +1,27 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import InlineSVG from 'react-inlinesvg';
|
||||
|
||||
const check = require('../../../../images/check_encircled.svg');
|
||||
const close = require('../../../../images/close.svg');
|
||||
|
||||
const Icons = ({ isAdded }) => {
|
||||
const classes = [
|
||||
'quick-add__icon',
|
||||
isAdded ? 'quick-add__icon--in-collection' : 'quick-add__icon--not-in-collection'
|
||||
].join(' ');
|
||||
|
||||
return (
|
||||
<div className={classes}>
|
||||
<InlineSVG className="quick-add__remove-icon" src={close} alt="Remove from collection" />
|
||||
<InlineSVG className="quick-add__in-icon" src={check} alt="In collection" />
|
||||
<InlineSVG className="quick-add__add-icon" src={close} alt="Add to collection" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Icons.propTypes = {
|
||||
isAdded: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
export default Icons;
|
46
client/modules/IDE/components/QuickAddList/QuickAddList.jsx
Normal file
46
client/modules/IDE/components/QuickAddList/QuickAddList.jsx
Normal file
|
@ -0,0 +1,46 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Link } from 'react-router';
|
||||
|
||||
import Icons from './Icons';
|
||||
|
||||
const Item = ({
|
||||
isAdded, onSelect, name, url
|
||||
}) => (
|
||||
<li className="quick-add__item">
|
||||
<button className="quick-add__item-toggle" onClick={onSelect}>
|
||||
<Icons isAdded={isAdded} />
|
||||
{name}
|
||||
</button>
|
||||
<Link className="quick-add__item-view" to={url}>View</Link>
|
||||
</li>
|
||||
);
|
||||
|
||||
const ItemType = PropTypes.shape({
|
||||
name: PropTypes.string.isRequired,
|
||||
url: PropTypes.string.isRequired,
|
||||
isAdded: PropTypes.bool.isRequired,
|
||||
});
|
||||
|
||||
Item.propTypes = {
|
||||
...ItemType,
|
||||
onSelect: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const QuickAddList = ({ items, onSelect }) => (
|
||||
<ul className="quick-add">{items.map(item => (<Item
|
||||
key={item.id}
|
||||
{...item}
|
||||
onSelect={
|
||||
() => onSelect(item)
|
||||
}
|
||||
/>))}
|
||||
</ul>
|
||||
);
|
||||
|
||||
QuickAddList.propTypes = {
|
||||
items: PropTypes.arrayOf(ItemType).isRequired,
|
||||
onSelect: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default QuickAddList;
|
1
client/modules/IDE/components/QuickAddList/index.js
Normal file
1
client/modules/IDE/components/QuickAddList/index.js
Normal file
|
@ -0,0 +1 @@
|
|||
export { default } from './QuickAddList.jsx';
|
|
@ -30,7 +30,7 @@ import * as ConsoleActions from '../actions/console';
|
|||
import { getHTMLFile } from '../reducers/files';
|
||||
import Overlay from '../../App/components/Overlay';
|
||||
import About from '../components/About';
|
||||
import CollectionList from '../components/CollectionList';
|
||||
import CollectionList from '../components/AddToCollectionList';
|
||||
import Feedback from '../components/Feedback';
|
||||
|
||||
class IDEView extends React.Component {
|
||||
|
@ -392,7 +392,6 @@ class IDEView extends React.Component {
|
|||
previousPath={this.props.ide.previousPath}
|
||||
>
|
||||
<CollectionList
|
||||
addMode
|
||||
projectId={this.props.params.project_id}
|
||||
username={this.props.params.username}
|
||||
user={this.props.user}
|
||||
|
|
94
client/styles/components/_quick-add.scss
Normal file
94
client/styles/components/_quick-add.scss
Normal file
|
@ -0,0 +1,94 @@
|
|||
.quick-add {
|
||||
padding: #{24 / $base-font-size}rem;
|
||||
width: #{600 / $base-font-size}rem;
|
||||
}
|
||||
|
||||
.quick-add__item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: #{64 / $base-font-size}rem;
|
||||
padding-right: #{24 / $base-font-size}rem;
|
||||
|
||||
button, a {
|
||||
@include themify() {
|
||||
color: getThemifyVariable('primary-text-color');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.quick-add__item:nth-child(odd) {
|
||||
@include themify() {
|
||||
background: getThemifyVariable('table-row-stripe-color');
|
||||
}
|
||||
}
|
||||
|
||||
.quick-add__item-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.quick-add__icon {
|
||||
display: inline-block;
|
||||
margin-right:#{15 / $base-font-size}rem;
|
||||
width:#{35 / $base-font-size}rem;
|
||||
height:#{35 / $base-font-size}rem;
|
||||
@include icon();
|
||||
@include themify() {
|
||||
& path {
|
||||
fill: getThemifyVariable('dropdown-color');
|
||||
}
|
||||
|
||||
& svg {
|
||||
width:#{35 / $base-font-size}rem;
|
||||
height:#{35 / $base-font-size}rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.quick-add__icon > * {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.quick-add__in-icon {
|
||||
display: inline-block;
|
||||
|
||||
& svg {
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
.quick-add__icon--in-collection .quick-add__in-icon svg {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.quick-add__add-icon {
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.quick-add__item-toggle:hover {
|
||||
.quick-add__in-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.quick-add__icon--in-collection {
|
||||
.quick-add__remove-icon {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.quick-add__add-icon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.quick-add__icon--not-in-collection {
|
||||
.quick-add__add-icon {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.quick-add__remove-icon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -118,70 +118,6 @@
|
|||
width: #{35 / $base-font-size}rem;
|
||||
}
|
||||
|
||||
.sketch-list__icon {
|
||||
display: inline-block;
|
||||
margin-right:#{15 / $base-font-size}rem;
|
||||
width:#{35 / $base-font-size}rem;
|
||||
height:#{35 / $base-font-size}rem;
|
||||
@include icon();
|
||||
@include themify() {
|
||||
& path {
|
||||
fill: getThemifyVariable('dropdown-color');
|
||||
}
|
||||
|
||||
& svg {
|
||||
width:#{35 / $base-font-size}rem;
|
||||
height:#{35 / $base-font-size}rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sketch-list__icon > * {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sketch-list__in-icon {
|
||||
display: inline-block;
|
||||
|
||||
& svg {
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
.sketch-list__icon--in-collection .sketch-list__in-icon svg {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.sketch-list__add-icon {
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.sketches-table__row:hover {
|
||||
.sketch-list__in-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sketch-list__icon--in-collection {
|
||||
.sketch-list__remove-icon {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.sketch-list__add-icon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.sketch-list__icon--not-in-collection {
|
||||
.sketch-list__add-icon {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.sketch-list__remove-icon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sketch-list__action-dialogue {
|
||||
@extend %dropdown-open-right;
|
||||
top: 63%;
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
@import 'components/collection';
|
||||
@import 'components/collection-create';
|
||||
@import 'components/collection-popover';
|
||||
@import 'components/quick-add';
|
||||
|
||||
@import 'layout/dashboard';
|
||||
@import 'layout/ide';
|
||||
|
|
Loading…
Reference in a new issue