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 { getHTMLFile } from '../reducers/files';
|
||||||
import Overlay from '../../App/components/Overlay';
|
import Overlay from '../../App/components/Overlay';
|
||||||
import About from '../components/About';
|
import About from '../components/About';
|
||||||
import CollectionList from '../components/CollectionList';
|
import CollectionList from '../components/AddToCollectionList';
|
||||||
import Feedback from '../components/Feedback';
|
import Feedback from '../components/Feedback';
|
||||||
|
|
||||||
class IDEView extends React.Component {
|
class IDEView extends React.Component {
|
||||||
|
@ -392,7 +392,6 @@ class IDEView extends React.Component {
|
||||||
previousPath={this.props.ide.previousPath}
|
previousPath={this.props.ide.previousPath}
|
||||||
>
|
>
|
||||||
<CollectionList
|
<CollectionList
|
||||||
addMode
|
|
||||||
projectId={this.props.params.project_id}
|
projectId={this.props.params.project_id}
|
||||||
username={this.props.params.username}
|
username={this.props.params.username}
|
||||||
user={this.props.user}
|
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;
|
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 {
|
.sketch-list__action-dialogue {
|
||||||
@extend %dropdown-open-right;
|
@extend %dropdown-open-right;
|
||||||
top: 63%;
|
top: 63%;
|
||||||
|
|
|
@ -52,6 +52,7 @@
|
||||||
@import 'components/collection';
|
@import 'components/collection';
|
||||||
@import 'components/collection-create';
|
@import 'components/collection-create';
|
||||||
@import 'components/collection-popover';
|
@import 'components/collection-popover';
|
||||||
|
@import 'components/quick-add';
|
||||||
|
|
||||||
@import 'layout/dashboard';
|
@import 'layout/dashboard';
|
||||||
@import 'layout/ide';
|
@import 'layout/ide';
|
||||||
|
|
Loading…
Reference in a new issue