From 8eabd5d898e92751d60384e8a36464444d1d907d Mon Sep 17 00:00:00 2001 From: Laksh Singla Date: Sat, 2 Feb 2019 16:44:43 +0530 Subject: [PATCH 1/7] parent b3c3efcec96b5e5bb4e00be742e8f17a025db409 author Laksh Singla 1549106083 +0530 committer Cassie Tarakajian 1560540243 -0400 parent b3c3efcec96b5e5bb4e00be742e8f17a025db409 author Laksh Singla 1549106083 +0530 committer Cassie Tarakajian 1560540198 -0400 parent b3c3efcec96b5e5bb4e00be742e8f17a025db409 author Laksh Singla 1549106083 +0530 committer Cassie Tarakajian 1560539667 -0400 Created initial html structure and styling for new SketchList design Final styling of ActionDialogueBox commplete Dropdown menu disappearing while clicking anywhere on the table Fixed linting issues and renamed variables Minor tweaks in the SketchList dropdown dialogue UI Themifyed the dropdown Made changes in the dropdown: Arrow positioned slightly updwards, Removed blank space and added box-shadow in dropdown, themifyed dropdowns dashed border color Added Delete and Share functionality to Dialog box Added Duplicate functionality to Dialog box Added download functionality to Dialog box SketchList does not open a sketch if dialogue box is opened SketchList Rename initial UI completed Enter key handled for rename project option [WIP] Updating rename functionality Download option now working for all the sketches Duplicate functionality extended for non opened sketches too Modified overlay behaviour to close only the last overlay Share modal can now display different projects Dropdown closes when Share and Delete are closing for a more natural UX fix broken files from rebasing Created initial html structure and styling for new SketchList design Final styling of ActionDialogueBox commplete Added Delete and Share functionality to Dialog box Added Duplicate functionality to Dialog box [WIP] Updating rename functionality Duplicate functionality extended for non opened sketches too Modified overlay behaviour to close only the last overlay Share modal can now display different projects Final styling of ActionDialogueBox commplete Fixed linting issues and renamed variables Minor tweaks in the SketchList dropdown dialogue UI Themifyed the dropdown Added Delete and Share functionality to Dialog box [WIP] Updating rename functionality Modified overlay behaviour to close only the last overlay Share modal can now display different projects Dropdown closes when Share and Delete are closing for a more natural UX fix broken files from rebasing Final styling of ActionDialogueBox commplete Minor tweaks in the SketchList dropdown dialogue UI Themifyed the dropdown [WIP] Updating rename functionality Duplicate functionality extended for non opened sketches too Modified overlay behaviour to close only the last overlay Share modal can now display different projects Dropdown closes when Share and Delete are closing for a more natural UX --- client/constants.js | 1 + client/images/btn-up-triangle-divot.svg | 6 + client/modules/App/components/Overlay.jsx | 6 +- client/modules/IDE/actions/ide.js | 14 +- client/modules/IDE/actions/project.js | 117 ++++++--- client/modules/IDE/components/SketchList.jsx | 242 ++++++++++++++++--- client/modules/IDE/pages/IDEView.jsx | 10 +- client/modules/IDE/reducers/ide.js | 10 +- client/modules/IDE/reducers/projects.js | 8 + client/styles/components/_sketch-list.scss | 170 ++++++++++++- server/controllers/project.controller.js | 2 +- 11 files changed, 501 insertions(+), 85 deletions(-) create mode 100644 client/images/btn-up-triangle-divot.svg diff --git a/client/constants.js b/client/constants.js index 4c5d1bf1e1..aaf7637c92 100644 --- a/client/constants.js +++ b/client/constants.js @@ -20,6 +20,7 @@ export const AUTH_ERROR = 'AUTH_ERROR'; export const SETTINGS_UPDATED = 'SETTINGS_UPDATED'; export const SET_PROJECT_NAME = 'SET_PROJECT_NAME'; +export const RENAME_PROJECT = 'RENAME_PROJECT'; export const PROJECT_SAVE_SUCCESS = 'PROJECT_SAVE_SUCCESS'; export const PROJECT_SAVE_FAIL = 'PROJECT_SAVE_FAIL'; diff --git a/client/images/btn-up-triangle-divot.svg b/client/images/btn-up-triangle-divot.svg new file mode 100644 index 0000000000..b988b6822a --- /dev/null +++ b/client/images/btn-up-triangle-divot.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/client/modules/App/components/Overlay.jsx b/client/modules/App/components/Overlay.jsx index 486225f462..3a70242d11 100644 --- a/client/modules/App/components/Overlay.jsx +++ b/client/modules/App/components/Overlay.jsx @@ -33,7 +33,7 @@ class Overlay extends React.Component { return; } - this.handleClickOutside(); + this.handleClickOutside(e); } handleClickOutside() { @@ -49,6 +49,10 @@ class Overlay extends React.Component { } close() { + // Only close if it is the last (and therefore the topmost overlay) + const overlays = document.getElementsByClassName('overlay'); + if (this.node.parentElement.parentElement !== overlays[overlays.length - 1]) return; + if (!this.props.closeOverlay) { browserHistory.push(this.props.previousPath); } else { diff --git a/client/modules/IDE/actions/ide.js b/client/modules/IDE/actions/ide.js index 64226b73ce..8a2b45d344 100644 --- a/client/modules/IDE/actions/ide.js +++ b/client/modules/IDE/actions/ide.js @@ -134,9 +134,17 @@ export function closeNewFolderModal() { }; } -export function showShareModal() { - return { - type: ActionTypes.SHOW_SHARE_MODAL +export function showShareModal(projectId, projectName, ownerUsername) { + return (dispatch, getState) => { + const { project, user } = getState(); + dispatch({ + type: ActionTypes.SHOW_SHARE_MODAL, + payload: { + shareModalProjectId: projectId || project.id, + shareModalProjectName: projectName || project.name, + shareModalOwnerUsername: ownerUsername || user.username + } + }); }; } diff --git a/client/modules/IDE/actions/project.js b/client/modules/IDE/actions/project.js index 0fa2d3444a..e24c7bc5da 100644 --- a/client/modules/IDE/actions/project.js +++ b/client/modules/IDE/actions/project.js @@ -246,49 +246,62 @@ function generateNewIdsForChildren(file, files) { file.children = newChildren; // eslint-disable-line } -export function cloneProject() { +export function cloneProject(id) { return (dispatch, getState) => { dispatch(setUnsavedChanges(false)); - const state = getState(); - const newFiles = state.files.map((file) => { // eslint-disable-line - return { ...file }; - }); + new Promise((resolve, reject) => { + if (!id) { + resolve(getState()); + } else { + fetch(`${ROOT_URL}/projects/${id}`) + .then(res => res.json()) + .then(data => resolve({ + files: data.files, + project: { + name: data.name + } + })); + } + }).then((state) => { + const newFiles = state.files.map((file) => { // eslint-disable-line + return { ...file }; + }); - // generate new IDS for all files - const rootFile = newFiles.find(file => file.name === 'root'); - const newRootFileId = objectID().toHexString(); - rootFile.id = newRootFileId; - rootFile._id = newRootFileId; - generateNewIdsForChildren(rootFile, newFiles); + // generate new IDS for all files + const rootFile = newFiles.find(file => file.name === 'root'); + const newRootFileId = objectID().toHexString(); + rootFile.id = newRootFileId; + rootFile._id = newRootFileId; + generateNewIdsForChildren(rootFile, newFiles); - // duplicate all files hosted on S3 - each(newFiles, (file, callback) => { - if (file.url && file.url.includes('amazonaws')) { - const formParams = { - url: file.url - }; - axios.post(`${ROOT_URL}/S3/copy`, formParams, { withCredentials: true }) + // duplicate all files hosted on S3 + each(newFiles, (file, callback) => { + if (file.url && file.url.includes('amazonaws')) { + const formParams = { + url: file.url + }; + axios.post(`${ROOT_URL}/S3/copy`, formParams, { withCredentials: true }) + .then((response) => { + file.url = response.data.url; + callback(null); + }); + } else { + callback(null); + } + }, (err) => { + // if not errors in duplicating the files on S3, then duplicate it + const formParams = Object.assign({}, { name: `${state.project.name} copy` }, { files: newFiles }); + axios.post(`${ROOT_URL}/projects`, formParams, { withCredentials: true }) .then((response) => { - file.url = response.data.url; - callback(null); - }); - } else { - callback(null); - } - }, (err) => { - // if not errors in duplicating the files on S3, then duplicate it - const formParams = Object.assign({}, { name: `${state.project.name} copy` }, { files: newFiles }); - axios.post(`${ROOT_URL}/projects`, formParams, { withCredentials: true }) - .then((response) => { - browserHistory.push(`/${response.data.user.username}/sketches/${response.data.id}`); - dispatch(setNewProject(response.data)); - }) - .catch(response => dispatch({ - type: ActionTypes.PROJECT_SAVE_FAIL, - error: response.data - })); - }); - }; + browserHistory.push(`/${response.data.user.username}/sketches/${response.data.id}`); + dispatch(setNewProject(response.data)); + }) + .catch(response => dispatch({ + type: ActionTypes.PROJECT_SAVE_FAIL, + error: response.data + })); + }); + }); } export function showEditProjectName() { @@ -309,3 +322,31 @@ export function setProjectSavedTime(updatedAt) { value: updatedAt }; } + +export function changeProjectName(id, newName) { + return (dispatch, getState) => { + const state = getState(); + axios.put(`${ROOT_URL}/projects/${id}`, { name: newName }, { withCredentials: true }) + .then((response) => { + if (response.status === 200) { + dispatch({ + type: ActionTypes.RENAME_PROJECT, + payload: { id: response.data.id, name: response.data.name } + }); + if (state.project.id === response.data.id) { + dispatch({ + type: ActionTypes.SET_PROJECT_NAME, + name: response.data.name + }); + } + } + }) + .catch((response) => { + console.log(response); + dispatch({ + type: ActionTypes.PROJECT_SAVE_FAIL, + error: response.data + }); + }); + }; +} diff --git a/client/modules/IDE/components/SketchList.jsx b/client/modules/IDE/components/SketchList.jsx index f51a815a69..c98d2cfb0a 100644 --- a/client/modules/IDE/components/SketchList.jsx +++ b/client/modules/IDE/components/SketchList.jsx @@ -14,9 +14,11 @@ import * as SortingActions from '../actions/sorting'; import getSortedSketches from '../selectors/projects'; import Loader from '../../App/components/loader'; -const trashCan = require('../../../images/trash-can.svg'); +// const trashCan = require('../../../images/trash-can.svg'); const arrowUp = require('../../../images/sort-arrow-up.svg'); const arrowDown = require('../../../images/sort-arrow-down.svg'); +const downFilledTriangle = require('../../../images/down-filled-triangle.svg'); +const btnUpTriangleDivot = require('../../../images/btn-up-triangle-divot.svg'); class SketchList extends React.Component { constructor(props) { @@ -24,6 +26,21 @@ class SketchList extends React.Component { this.props.getProjects(this.props.username); this.props.resetSorting(); this._renderFieldHeader = this._renderFieldHeader.bind(this); + this.state = { + actionDialogueDisplayed: new Array(this.props.sketches.length).fill(false), + renameBoxDisplayed: new Array(this.props.sketches.length).fill(false), + renameBoxContent: this.props.sketches.map(({ name }) => name) + }; + + this.closeAllDropdowns = this.closeAllDropdowns.bind(this); + this.closeAllRenameBoxes = this.closeAllRenameBoxes.bind(this); + this.restoreRenameBoxContent = this.restoreRenameBoxContent.bind(this); + } + + componentWillReceiveProps(props) { + this.setState({ + renameBoxContent: props.sketches.map(({ name }) => name) + }); } getSketchesTitle() { @@ -70,10 +87,35 @@ class SketchList extends React.Component { ); } + closeAllDropdowns() { + this.setState({ + actionDialogueDisplayed: new Array(this.props.sketches.length).fill(false) + }); + } + + closeAllRenameBoxes() { + this.setState({ + renameBoxDisplayed: new Array(this.props.sketches.length).fill(false) + }); + } + + restoreRenameBoxContent() { + this.setState({ + renameBoxContent: this.props.sketches.map(({ name }) => name) + }); + } + render() { const username = this.props.username !== undefined ? this.props.username : this.props.user.username; return ( -
+
{ + this.closeAllDropdowns(); + this.closeAllRenameBoxes(); + }} + > {this.getSketchesTitle()} @@ -83,42 +125,170 @@ class SketchList extends React.Component { - {this._renderFieldHeader('name', 'Sketch')} {this._renderFieldHeader('createdAt', 'Date Created')} {this._renderFieldHeader('updatedAt', 'Date Updated')} + - {this.props.sketches.map(sketch => + {this.props.sketches.map((sketch, i) => // eslint-disable-next-line browserHistory.push(`/${username}/sketches/${sketch.id}`)} + onClick={() => { + if (this.state.actionDialogueDisplayed.some(el => el)) { + this.closeAllDropdowns(); + return; + } + if (this.state.renameBoxDisplayed.some(el => el)) { + this.closeAllRenameBoxes(); + return; + } + browserHistory.push(`/${username}/sketches/${sketch.id}`); + }} > - - + + )}
- {(() => { // eslint-disable-line - if (this.props.username === this.props.user.username || this.props.username === undefined) { - return ( - - ); - } - })()} - {sketch.name} + + {this.state.renameBoxDisplayed[i] ? '' : sketch.name} + + {this.state.renameBoxDisplayed[i] + && + { + const renameBoxContent = [...this.state.renameBoxContent]; + renameBoxContent[i] = e.target.value; + this.setState({ + renameBoxContent + }); + }} + onKeyUp={(e) => { + // Enter pressed + if (e.key === 'Enter') { + this.props.changeProjectName(sketch.id, this.state.renameBoxContent[i]); + this.restoreRenameBoxContent(); + this.closeAllRenameBoxes(); + } + }} + onBlur={this.restoreRenameBoxContent} + onClick={e => e.stopPropagation()} + autoFocus //eslint-disable-line + /> + } + {format(new Date(sketch.createdAt), 'MMM D, YYYY h:mm A')} {format(new Date(sketch.updatedAt), 'MMM D, YYYY h:mm A')} + + {this.state.actionDialogueDisplayed[i] && +
e.stopPropagation()} + > + +
{ + const actionDialogueDisplayed = [...this.state.actionDialogueDisplayed]; + actionDialogueDisplayed[i] = false; + this.setState({ + actionDialogueDisplayed + }); + }} + role="presentation" + > + Sketch actions +
+
+
{ + this.closeAllRenameBoxes(); + this.closeAllDropdowns(); + const renameBoxDisplayed = new Array(this.props.sketches.length).fill(false); + renameBoxDisplayed[i] = true; + this.setState({ + renameBoxDisplayed + }); + }} + > + Rename +
+
{ + this.props.exportProjectAsZip(sketch.id); + }} + > + Download +
+ {this.props.user.authenticated && +
{ + this.closeAllDropdowns(); + this.props.cloneProject(sketch.id); + }} + > + Duplicate +
} +
{ + this.closeAllDropdowns(); + this.props.showShareModal(sketch.id, sketch.name); + }} + > + Share +
+
{ + e.stopPropagation(); + this.closeAllDropdowns(); + if (window.confirm(`Are you sure you want to delete "${sketch.name}"?`)) { + this.props.deleteProject(sketch.id); + } + }} + > + Delete +
+
} +
} @@ -129,7 +299,8 @@ class SketchList extends React.Component { SketchList.propTypes = { user: PropTypes.shape({ - username: PropTypes.string + username: PropTypes.string, + authenticated: PropTypes.bool.isRequired }).isRequired, getProjects: PropTypes.func.isRequired, sketches: PropTypes.arrayOf(PropTypes.shape({ @@ -147,9 +318,23 @@ SketchList.propTypes = { field: PropTypes.string.isRequired, direction: PropTypes.string.isRequired }).isRequired, + showShareModal: PropTypes.func.isRequired, + project: PropTypes.shape({ + id: PropTypes.string, + owner: PropTypes.shape({ + id: PropTypes.string + }) + }), + cloneProject: PropTypes.func.isRequired, + exportProjectAsZip: PropTypes.func.isRequired, + changeProjectName: PropTypes.func.isRequired }; SketchList.defaultProps = { + project: { + id: undefined, + owner: undefined + }, username: undefined }; @@ -158,7 +343,8 @@ function mapStateToProps(state) { user: state.user, sketches: getSortedSketches(state), sorting: state.sorting, - loading: state.loading + loading: state.loading, + project: state.project }; } diff --git a/client/modules/IDE/pages/IDEView.jsx b/client/modules/IDE/pages/IDEView.jsx index ed19bb8bd1..9ebc329312 100644 --- a/client/modules/IDE/pages/IDEView.jsx +++ b/client/modules/IDE/pages/IDEView.jsx @@ -407,6 +407,7 @@ class IDEView extends React.Component { } + aa { this.props.ide.shareModalVisible && } @@ -481,6 +482,9 @@ IDEView.propTypes = { projectOptionsVisible: PropTypes.bool.isRequired, newFolderModalVisible: PropTypes.bool.isRequired, shareModalVisible: PropTypes.bool.isRequired, + shareModalProjectId: PropTypes.string.isRequired, + shareModalProjectName: PropTypes.string.isRequired, + shareModalProjectUsername: PropTypes.string.isRequired, editorOptionsVisible: PropTypes.bool.isRequired, keyboardShortcutVisible: PropTypes.bool.isRequired, unsavedChanges: PropTypes.bool.isRequired, diff --git a/client/modules/IDE/reducers/ide.js b/client/modules/IDE/reducers/ide.js index 379237bc70..1396241600 100644 --- a/client/modules/IDE/reducers/ide.js +++ b/client/modules/IDE/reducers/ide.js @@ -10,6 +10,9 @@ const initialState = { projectOptionsVisible: false, newFolderModalVisible: false, shareModalVisible: false, + shareModalProjectId: null, + shareModalProjectName: null, + shareModalOwnerUsername: null, editorOptionsVisible: false, keyboardShortcutVisible: false, unsavedChanges: false, @@ -61,7 +64,12 @@ const ide = (state = initialState, action) => { case ActionTypes.CLOSE_NEW_FOLDER_MODAL: return Object.assign({}, state, { newFolderModalVisible: false }); case ActionTypes.SHOW_SHARE_MODAL: - return Object.assign({}, state, { shareModalVisible: true }); + return Object.assign({}, state, { + shareModalVisible: true, + shareModalProjectId: action.payload.shareModalProjectId, + shareModalProjectName: action.payload.shareModalProjectName, + shareModalOwnerUsername: action.payload.shareModalOwnerUsername, + }); case ActionTypes.CLOSE_SHARE_MODAL: return Object.assign({}, state, { shareModalVisible: false }); case ActionTypes.SHOW_EDITOR_OPTIONS: diff --git a/client/modules/IDE/reducers/projects.js b/client/modules/IDE/reducers/projects.js index ba3bb4c9f9..ff06c5c720 100644 --- a/client/modules/IDE/reducers/projects.js +++ b/client/modules/IDE/reducers/projects.js @@ -7,6 +7,14 @@ const sketches = (state = [], action) => { case ActionTypes.DELETE_PROJECT: return state.filter(sketch => sketch.id !== action.id); + case ActionTypes.RENAME_PROJECT: { + return state.map((sketch) => { + if (sketch.id === action.payload.id) { + return { ...sketch, name: action.payload.name }; + } + return { ...sketch }; + }); + } default: return state; } diff --git a/client/styles/components/_sketch-list.scss b/client/styles/components/_sketch-list.scss index 496a2b1c13..6ca1a8a83c 100644 --- a/client/styles/components/_sketch-list.scss +++ b/client/styles/components/_sketch-list.scss @@ -10,8 +10,11 @@ padding: #{10 / $base-font-size}rem #{20 / $base-font-size}rem; max-height: 100%; border-spacing: 0; + & .sketch-list__dropdown-column { + width: #{60 / $base-font-size}rem; & .sketch-list__trash-column { - width: #{23 / $base-font-size}rem; + width: #{36 / $base-font-size}rem; + position: relative; } } @@ -44,6 +47,10 @@ } } +.sketches-table thead th:nth-child(1){ + padding-left: #{12 / $base-font-size}rem; +} + .sketches-table__row { margin: #{10 / $base-font-size}rem; height: #{72 / $base-font-size}rem; @@ -57,6 +64,10 @@ } } +.sketches-table__row > th:nth-child(1) { + padding-left: #{12 / $base-font-size}rem; +} + .sketches-table__row a { @include themify() { color: getThemifyVariable('primary-text-color'); @@ -75,14 +86,11 @@ font-weight: normal; } -.visibility-toggle .sketch-list__trash-button { - @extend %hidden-element; +.sketch-list__dropdown-button { width:#{20 / $base-font-size}rem; height:#{20 / $base-font-size}rem; -} - -.visibility-toggle:hover .sketch-list__trash-button { - @include themify() { + transform: translateY(-3px); + @include themify() { background-color: transparent; border: none; cursor: pointer; @@ -90,13 +98,155 @@ position: initial; left: 0; top: 0; - & g { - opacity: 1; - fill: getThemifyVariable('icon-hover-color'); + + & polygon { + fill: getThemifyVariable('dropdown-color'); + } + + & polygon { + fill: getThemifyVariable('dropdown-color'); + } + + & polygon { + fill: getThemifyVariable('dropdown-color'); } } } +.sketch-list__action-dialogue { + position: absolute; + width: #{162 / $base-font-size}rem; + @include themify(){ + background-color: getThemifyVariable('modal-background-color'); + box-shadow: 0 0 18px 0 getThemifyVariable('shadow-color'); + } + border-radius: #{6 / $base-font-size}rem; + border: 1px solid #0f9dd7; + top: 22%; + right: 55%; + z-index: 100; + button { + width: 20px; + } +} + +.sketch-list__action-label { + @include themify(){ + color: getThemifyVariable('dropdown-color'); + } + padding-left: #{14 / $base-font-size}rem; + width: 100%; + height: #{36 / $base-font-size}rem; + font-family: Montserrat; + font-size: #{12 / $base-font-size}rem; + font-weight: bold; + font-style: normal; + font-stretch: normal; + line-height: 3; + letter-spacing: normal; + text-align: left; + margin-top: #{2 / $base-font-size}rem; + /*&:hover { + @include themify(){ + color: getThemifyVariable('nav-hover-color'); + path:nth-child(2) { + fill: getThemifyVariable('nav-hover-color'); + } + } + }*/ +} + +.sketch-list__action-dashed-line { + width: #{133 / $base-font-size}rem; + @include themify(){ + border-bottom: 1px dashed map-get($theme-map, 'inactive-text-color'); + } + // color: #ffffff; + margin: 0 auto; +} + +.sketch-list__action-option { + @include themify(){ + color: getThemifyVariable('dropdown-color'); + } + width: #{53 / $base-font-size}rem; + height: #{32 / $base-font-size}rem; + font-family: Montserrat; + font-size: #{12 / $base-font-size}rem; + font-weight: bold; + font-style: normal; + font-stretch: normal; + line-height: 2.67; + letter-spacing: normal; + text-align: left; + margin: #{4 / $base-font-size}rem; + margin-left: 0; + padding-left: #{14 / $base-font-size}rem; + width: 100%; + + &:hover { + @include themify(){ + background-color: getThemifyVariable('nav-hover-color'); + color: getThemifyVariable('button-active-color'); + } + } + + &:nth-last-child(1) { + border-bottom-left-radius: #{6 / $base-font-size}rem; + border-bottom-right-radius: #{6 / $base-font-size}rem; + margin-bottom: 0; + } +} + +.sketch-list__action-close { + @include themify(){ + & path:nth-child(2) { + fill: getThemifyVariable('dropdown-color'); + } + } + position: absolute; + top: #{11.604 / $base-font-size}rem; + right: #{14 / $base-font-size}rem; + width: #{22 / $base-font-size}rem; + height: #{18 / $base-font-size}rem; +} + +.sketch-list__action-close { + position: absolute; + top: #{14 / $base-font-size}rem; + right: #{14 / $base-font-size}rem; + width: #{18 / $base-font-size}rem; + height: #{18 / $base-font-size}rem; +} + +.sketch-list__action-close { + @include themify(){ + & path:nth-child(2) { + fill: getThemifyVariable('dropdown-color'); + } + } + position: absolute; + top: #{11.604 / $base-font-size}rem; + right: #{14 / $base-font-size}rem; + width: #{22 / $base-font-size}rem; + height: #{18 / $base-font-size}rem; +} + +.sketch-list__action-close { + @include themify(){ + & path:nth-child(2) { + fill: getThemifyVariable('dropdown-color'); + } + } + position: absolute; + top: #{11.604 / $base-font-size}rem; + right: #{14 / $base-font-size}rem; + width: #{22 / $base-font-size}rem; + height: #{18 / $base-font-size}rem; +} + + + .sketches-table__empty { text-align: center; font-size: #{16 / $base-font-size}rem; diff --git a/server/controllers/project.controller.js b/server/controllers/project.controller.js index 527ac64ab2..ae02991865 100644 --- a/server/controllers/project.controller.js +++ b/server/controllers/project.controller.js @@ -40,7 +40,7 @@ export function updateProject(req, res) { res.json({ success: false }); return; } - if (updatedProject.files.length !== req.body.files.length) { + if (req.body.files && updatedProject.files.length !== req.body.files.length) { const oldFileIds = updatedProject.files.map(file => file.id); const newFileIds = req.body.files.map(file => file.id); const staleIds = oldFileIds.filter(id => newFileIds.indexOf(id) === -1); From 22ee7622917e12249837c0a39f5e5d14353a8cd3 Mon Sep 17 00:00:00 2001 From: Cassie Tarakajian Date: Fri, 14 Jun 2019 15:31:57 -0400 Subject: [PATCH 2/7] fix bugs in merge commit --- client/modules/IDE/actions/project.js | 3 ++- client/styles/components/_sketch-list.scss | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/client/modules/IDE/actions/project.js b/client/modules/IDE/actions/project.js index e24c7bc5da..3a977bcdca 100644 --- a/client/modules/IDE/actions/project.js +++ b/client/modules/IDE/actions/project.js @@ -301,7 +301,8 @@ export function cloneProject(id) { error: response.data })); }); - }); + }); + }; } export function showEditProjectName() { diff --git a/client/styles/components/_sketch-list.scss b/client/styles/components/_sketch-list.scss index 6ca1a8a83c..ba05f777c1 100644 --- a/client/styles/components/_sketch-list.scss +++ b/client/styles/components/_sketch-list.scss @@ -12,6 +12,7 @@ border-spacing: 0; & .sketch-list__dropdown-column { width: #{60 / $base-font-size}rem; + } & .sketch-list__trash-column { width: #{36 / $base-font-size}rem; position: relative; From 0d65e1a069671f01f0766ef94029a1163f4d0cad Mon Sep 17 00:00:00 2001 From: Cassie Tarakajian Date: Tue, 18 Jun 2019 17:20:18 -0400 Subject: [PATCH 3/7] move sketch list dialogue to ul/li --- client/modules/IDE/components/SketchList.jsx | 149 ++++++++----------- client/modules/IDE/pages/IDEView.jsx | 1 - client/styles/abstracts/_placeholders.scss | 16 +- 3 files changed, 72 insertions(+), 94 deletions(-) diff --git a/client/modules/IDE/components/SketchList.jsx b/client/modules/IDE/components/SketchList.jsx index c98d2cfb0a..fd2ac0691d 100644 --- a/client/modules/IDE/components/SketchList.jsx +++ b/client/modules/IDE/components/SketchList.jsx @@ -198,96 +198,75 @@ class SketchList extends React.Component { {this.state.actionDialogueDisplayed[i] && -
e.stopPropagation()} > - -
{ - const actionDialogueDisplayed = [...this.state.actionDialogueDisplayed]; - actionDialogueDisplayed[i] = false; - this.setState({ - actionDialogueDisplayed - }); - }} - role="presentation" - > - Sketch actions -
-
-
{ - this.closeAllRenameBoxes(); - this.closeAllDropdowns(); - const renameBoxDisplayed = new Array(this.props.sketches.length).fill(false); - renameBoxDisplayed[i] = true; - this.setState({ - renameBoxDisplayed - }); - }} - > - Rename -
-
{ - this.props.exportProjectAsZip(sketch.id); - }} - > - Download -
+
  • + +
  • +
  • + +
  • {this.props.user.authenticated && -
    { - this.closeAllDropdowns(); - this.props.cloneProject(sketch.id); - }} - > - Duplicate -
    } -
    { - this.closeAllDropdowns(); - this.props.showShareModal(sketch.id, sketch.name); - }} - > - Share -
    -
    { - e.stopPropagation(); - this.closeAllDropdowns(); - if (window.confirm(`Are you sure you want to delete "${sketch.name}"?`)) { - this.props.deleteProject(sketch.id); - } - }} - > - Delete -
    -
    } +
  • + +
  • } +
  • + +
  • +
  • + +
  • + } )} diff --git a/client/modules/IDE/pages/IDEView.jsx b/client/modules/IDE/pages/IDEView.jsx index 9ebc329312..b2ae1ebb2f 100644 --- a/client/modules/IDE/pages/IDEView.jsx +++ b/client/modules/IDE/pages/IDEView.jsx @@ -407,7 +407,6 @@ class IDEView extends React.Component { } - aa { this.props.ide.shareModalVisible && Date: Wed, 19 Jun 2019 12:16:41 -0400 Subject: [PATCH 4/7] update sketch option dropdown to use dropdown placeholder, remove unused css --- client/styles/abstracts/_placeholders.scss | 11 +- client/styles/components/_sketch-list.scss | 133 +-------------------- 2 files changed, 6 insertions(+), 138 deletions(-) diff --git a/client/styles/abstracts/_placeholders.scss b/client/styles/abstracts/_placeholders.scss index d85c427b63..d252a08c9b 100644 --- a/client/styles/abstracts/_placeholders.scss +++ b/client/styles/abstracts/_placeholders.scss @@ -214,6 +214,9 @@ height: auto; z-index: 9999; border-radius: #{6 / $base-font-size}rem; + & li:first-child { + border-radius: #{5 / $base-font-size}rem #{5 / $base-font-size}rem 0 0; + } & li:last-child { border-radius: 0 0 #{5 / $base-font-size}rem #{5 / $base-font-size}rem; } @@ -248,17 +251,9 @@ %dropdown-open-left { @extend %dropdown-open; left: 0; - // border-top-left-radius: 0px; - // & li:first-child { - // border-radius: 0 #{5 / $base-font-size}rem 0 0; - // } } %dropdown-open-right { @extend %dropdown-open; right: 0; - // border-top-right-radius: 0px; - // & li:first-child { - // border-radius: #{5 / $base-font-size}rem 0 0 0; - // } } diff --git a/client/styles/components/_sketch-list.scss b/client/styles/components/_sketch-list.scss index ba05f777c1..dec4f79d34 100644 --- a/client/styles/components/_sketch-list.scss +++ b/client/styles/components/_sketch-list.scss @@ -12,9 +12,6 @@ border-spacing: 0; & .sketch-list__dropdown-column { width: #{60 / $base-font-size}rem; - } - & .sketch-list__trash-column { - width: #{36 / $base-font-size}rem; position: relative; } } @@ -115,139 +112,15 @@ } .sketch-list__action-dialogue { - position: absolute; - width: #{162 / $base-font-size}rem; - @include themify(){ - background-color: getThemifyVariable('modal-background-color'); - box-shadow: 0 0 18px 0 getThemifyVariable('shadow-color'); - } - border-radius: #{6 / $base-font-size}rem; - border: 1px solid #0f9dd7; - top: 22%; - right: 55%; - z-index: 100; - button { - width: 20px; - } -} - -.sketch-list__action-label { - @include themify(){ - color: getThemifyVariable('dropdown-color'); - } - padding-left: #{14 / $base-font-size}rem; - width: 100%; - height: #{36 / $base-font-size}rem; - font-family: Montserrat; - font-size: #{12 / $base-font-size}rem; - font-weight: bold; - font-style: normal; - font-stretch: normal; - line-height: 3; - letter-spacing: normal; - text-align: left; - margin-top: #{2 / $base-font-size}rem; - /*&:hover { - @include themify(){ - color: getThemifyVariable('nav-hover-color'); - path:nth-child(2) { - fill: getThemifyVariable('nav-hover-color'); - } - } - }*/ -} - -.sketch-list__action-dashed-line { - width: #{133 / $base-font-size}rem; - @include themify(){ - border-bottom: 1px dashed map-get($theme-map, 'inactive-text-color'); - } - // color: #ffffff; - margin: 0 auto; + @extend %dropdown-open-right; + top: 63%; + right: calc(100% - 26px); } .sketch-list__action-option { - @include themify(){ - color: getThemifyVariable('dropdown-color'); - } - width: #{53 / $base-font-size}rem; - height: #{32 / $base-font-size}rem; - font-family: Montserrat; - font-size: #{12 / $base-font-size}rem; - font-weight: bold; - font-style: normal; - font-stretch: normal; - line-height: 2.67; - letter-spacing: normal; - text-align: left; - margin: #{4 / $base-font-size}rem; - margin-left: 0; - padding-left: #{14 / $base-font-size}rem; - width: 100%; - &:hover { - @include themify(){ - background-color: getThemifyVariable('nav-hover-color'); - color: getThemifyVariable('button-active-color'); - } - } - - &:nth-last-child(1) { - border-bottom-left-radius: #{6 / $base-font-size}rem; - border-bottom-right-radius: #{6 / $base-font-size}rem; - margin-bottom: 0; - } -} - -.sketch-list__action-close { - @include themify(){ - & path:nth-child(2) { - fill: getThemifyVariable('dropdown-color'); - } - } - position: absolute; - top: #{11.604 / $base-font-size}rem; - right: #{14 / $base-font-size}rem; - width: #{22 / $base-font-size}rem; - height: #{18 / $base-font-size}rem; -} - -.sketch-list__action-close { - position: absolute; - top: #{14 / $base-font-size}rem; - right: #{14 / $base-font-size}rem; - width: #{18 / $base-font-size}rem; - height: #{18 / $base-font-size}rem; -} - -.sketch-list__action-close { - @include themify(){ - & path:nth-child(2) { - fill: getThemifyVariable('dropdown-color'); - } - } - position: absolute; - top: #{11.604 / $base-font-size}rem; - right: #{14 / $base-font-size}rem; - width: #{22 / $base-font-size}rem; - height: #{18 / $base-font-size}rem; } -.sketch-list__action-close { - @include themify(){ - & path:nth-child(2) { - fill: getThemifyVariable('dropdown-color'); - } - } - position: absolute; - top: #{11.604 / $base-font-size}rem; - right: #{14 / $base-font-size}rem; - width: #{22 / $base-font-size}rem; - height: #{18 / $base-font-size}rem; -} - - - .sketches-table__empty { text-align: center; font-size: #{16 / $base-font-size}rem; From 2f0ba88a8b42ba8e34f81d4a5cb93471274c25db Mon Sep 17 00:00:00 2001 From: Cassie Tarakajian Date: Wed, 19 Jun 2019 15:44:11 -0400 Subject: [PATCH 5/7] major refactor of sketchlist component, fix showShareModal action, minor updates ot icon sizing --- client/modules/IDE/actions/ide.js | 2 +- client/modules/IDE/actions/project.js | 30 +- client/modules/IDE/actions/projects.js | 30 +- client/modules/IDE/components/SketchList.jsx | 426 +++++++++++-------- client/modules/IDE/reducers/ide.js | 4 +- client/styles/components/_sidebar.scss | 7 +- client/styles/components/_sketch-list.scss | 22 +- 7 files changed, 281 insertions(+), 240 deletions(-) diff --git a/client/modules/IDE/actions/ide.js b/client/modules/IDE/actions/ide.js index 8a2b45d344..1d7c299803 100644 --- a/client/modules/IDE/actions/ide.js +++ b/client/modules/IDE/actions/ide.js @@ -142,7 +142,7 @@ export function showShareModal(projectId, projectName, ownerUsername) { payload: { shareModalProjectId: projectId || project.id, shareModalProjectName: projectName || project.name, - shareModalOwnerUsername: ownerUsername || user.username + shareModalProjectUsername: ownerUsername || user.username } }); }; diff --git a/client/modules/IDE/actions/project.js b/client/modules/IDE/actions/project.js index 3a977bcdca..e4abb9d5ff 100644 --- a/client/modules/IDE/actions/project.js +++ b/client/modules/IDE/actions/project.js @@ -9,7 +9,8 @@ import { setUnsavedChanges, justOpenedProject, resetJustOpenedProject, - showErrorModal + showErrorModal, + setPreviousPath } from './ide'; import { clearState, saveState } from '../../../persistState'; @@ -351,3 +352,30 @@ export function changeProjectName(id, newName) { }); }; } + +export function deleteProject(id) { + return (dispatch, getState) => { + axios.delete(`${ROOT_URL}/projects/${id}`, { withCredentials: true }) + .then(() => { + const state = getState(); + if (id === state.project.id) { + dispatch(resetProject()); + dispatch(setPreviousPath('/')); + } + dispatch({ + type: ActionTypes.DELETE_PROJECT, + id + }); + }) + .catch((response) => { + if (response.status === 403) { + dispatch(showErrorModal('staleSession')); + } else { + dispatch({ + type: ActionTypes.ERROR, + error: response.data + }); + } + }); + }; +} diff --git a/client/modules/IDE/actions/projects.js b/client/modules/IDE/actions/projects.js index 77dfbe5252..446c50ccc0 100644 --- a/client/modules/IDE/actions/projects.js +++ b/client/modules/IDE/actions/projects.js @@ -1,12 +1,11 @@ import axios from 'axios'; import * as ActionTypes from '../../../constants'; -import { showErrorModal, setPreviousPath } from './ide'; -import { resetProject } from './project'; import { startLoader, stopLoader } from './loader'; const __process = (typeof global !== 'undefined' ? global : window).process; const ROOT_URL = __process.env.API_URL; +// eslint-disable-next-line export function getProjects(username) { return (dispatch) => { dispatch(startLoader()); @@ -33,30 +32,3 @@ export function getProjects(username) { }); }; } - -export function deleteProject(id) { - return (dispatch, getState) => { - axios.delete(`${ROOT_URL}/projects/${id}`, { withCredentials: true }) - .then(() => { - const state = getState(); - if (id === state.project.id) { - dispatch(resetProject()); - dispatch(setPreviousPath('/')); - } - dispatch({ - type: ActionTypes.DELETE_PROJECT, - id - }); - }) - .catch((response) => { - if (response.status === 403) { - dispatch(showErrorModal('staleSession')); - } else { - dispatch({ - type: ActionTypes.ERROR, - error: response.data - }); - } - }); - }; -} diff --git a/client/modules/IDE/components/SketchList.jsx b/client/modules/IDE/components/SketchList.jsx index fd2ac0691d..2ac937bfa0 100644 --- a/client/modules/IDE/components/SketchList.jsx +++ b/client/modules/IDE/components/SketchList.jsx @@ -4,45 +4,261 @@ import React from 'react'; import { Helmet } from 'react-helmet'; import InlineSVG from 'react-inlinesvg'; import { connect } from 'react-redux'; -import { browserHistory, Link } from 'react-router'; +import { Link } from 'react-router'; import { bindActionCreators } from 'redux'; import classNames from 'classnames'; import * as ProjectActions from '../actions/project'; -import * as SketchActions from '../actions/projects'; +import * as ProjectsActions from '../actions/projects'; import * as ToastActions from '../actions/toast'; import * as SortingActions from '../actions/sorting'; +import * as IdeActions from '../actions/ide'; import getSortedSketches from '../selectors/projects'; import Loader from '../../App/components/loader'; -// const trashCan = require('../../../images/trash-can.svg'); const arrowUp = require('../../../images/sort-arrow-up.svg'); const arrowDown = require('../../../images/sort-arrow-down.svg'); const downFilledTriangle = require('../../../images/down-filled-triangle.svg'); -const btnUpTriangleDivot = require('../../../images/btn-up-triangle-divot.svg'); -class SketchList extends React.Component { +class SketchListRowBase extends React.Component { constructor(props) { super(props); - this.props.getProjects(this.props.username); - this.props.resetSorting(); - this._renderFieldHeader = this._renderFieldHeader.bind(this); this.state = { - actionDialogueDisplayed: new Array(this.props.sketches.length).fill(false), - renameBoxDisplayed: new Array(this.props.sketches.length).fill(false), - renameBoxContent: this.props.sketches.map(({ name }) => name) + optionsOpen: false, + renameOpen: false, + renameValue: props.sketch.name, + isFocused: false }; + } + + onFocusComponent = () => { + this.setState({ isFocused: true }); + } + + onBlurComponent = () => { + this.setState({ isFocused: false }); + setTimeout(() => { + if (!this.state.isFocused) { + this.closeAll(); + } + }, 200); + } + + openOptions = () => { + this.setState({ + optionsOpen: true + }); + } + + closeOptions = () => { + this.setState({ + optionsOpen: false + }); + } + + toggleOptions = () => { + if (this.state.optionsOpen) { + this.closeOptions(); + } else { + this.openOptions(); + } + } + + openRename = () => { + this.setState({ + renameOpen: true + }); + } + + closeRename = () => { + this.setState({ + renameOpen: false + }); + } - this.closeAllDropdowns = this.closeAllDropdowns.bind(this); - this.closeAllRenameBoxes = this.closeAllRenameBoxes.bind(this); - this.restoreRenameBoxContent = this.restoreRenameBoxContent.bind(this); + closeAll = () => { + this.setState({ + renameOpen: false, + optionsOpen: false + }); } - componentWillReceiveProps(props) { + handleRenameChange = (e) => { this.setState({ - renameBoxContent: props.sketches.map(({ name }) => name) + renameValue: e.target.value }); } + handleRenameEnter = (e) => { + if (e.key === 'Enter') { + // TODO pass this func + this.props.changeProjectName(this.props.sketch.id, this.state.renameValue); + this.closeAll(); + } + } + + resetSketchName = () => { + this.setState({ + renameValue: this.props.sketch.name + }); + } + + handleDropdownOpen = () => { + this.closeAll(); + this.openOptions(); + } + + handleRenameOpen = () => { + this.closeAll(); + this.openRename(); + } + + handleSketchDownload = () => { + this.props.exportProjectAsZip(this.props.sketch.id); + } + + handleSketchDuplicate = () => { + this.closeAll(); + this.props.cloneProject(this.props.sketch.id); + } + + handleSketchShare = () => { + this.closeAll(); + this.props.showShareModal(this.props.sketch.id, this.props.sketch.name, this.props.username); + } + + handleSketchDelete = () => { + this.closeAll(); + if (window.confirm(`Are you sure you want to delete "${this.props.sketch.name}"?`)) { + this.props.deleteProject(this.props.sketch.id); + } + } + + render() { + const { sketch, username } = this.props; + const { renameOpen, optionsOpen, renameValue } = this.state; + return ( + + + + {renameOpen ? '' : sketch.name} + + {renameOpen + && + e.stopPropagation()} + /> + } + + {format(new Date(sketch.createdAt), 'MMM D, YYYY h:mm A')} + {format(new Date(sketch.updatedAt), 'MMM D, YYYY h:mm A')} + + + {optionsOpen && +
      +
    • + +
    • +
    • + +
    • + {this.props.user.authenticated && +
    • + +
    • } + { /*
    • + +
    • */ } +
    • + +
    • +
    } + + ); + } +} + +SketchListRowBase.propTypes = { + sketch: PropTypes.shape({ + id: PropTypes.string.isRequired, + name: PropTypes.string.isRequired + }).isRequired, + username: PropTypes.string.isRequired, + user: PropTypes.shape({ + username: PropTypes.string, + authenticated: PropTypes.bool.isRequired + }).isRequired, + deleteProject: PropTypes.func.isRequired, + showShareModal: PropTypes.func.isRequired, + cloneProject: PropTypes.func.isRequired, + exportProjectAsZip: PropTypes.func.isRequired, + changeProjectName: PropTypes.func.isRequired +}; + +function mapDispatchToPropsSketchListRow(dispatch) { + return bindActionCreators(Object.assign({}, ProjectActions, IdeActions), dispatch); +} + +const SketchListRow = connect(null, mapDispatchToPropsSketchListRow)(SketchListRowBase); + +class SketchList extends React.Component { + constructor(props) { + super(props); + this.props.getProjects(this.props.username); + this.props.resetSorting(); + this._renderFieldHeader = this._renderFieldHeader.bind(this); + } + getSketchesTitle() { if (this.props.username === this.props.user.username) { return 'p5.js Web Editor | My sketches'; @@ -87,35 +303,10 @@ class SketchList extends React.Component { ); } - closeAllDropdowns() { - this.setState({ - actionDialogueDisplayed: new Array(this.props.sketches.length).fill(false) - }); - } - - closeAllRenameBoxes() { - this.setState({ - renameBoxDisplayed: new Array(this.props.sketches.length).fill(false) - }); - } - - restoreRenameBoxContent() { - this.setState({ - renameBoxContent: this.props.sketches.map(({ name }) => name) - }); - } - render() { const username = this.props.username !== undefined ? this.props.username : this.props.user.username; return ( -
    { - this.closeAllDropdowns(); - this.closeAllRenameBoxes(); - }} - > +
    {this.getSketchesTitle()} @@ -132,143 +323,13 @@ class SketchList extends React.Component { - {this.props.sketches.map((sketch, i) => - // eslint-disable-next-line - + ( { - if (this.state.actionDialogueDisplayed.some(el => el)) { - this.closeAllDropdowns(); - return; - } - if (this.state.renameBoxDisplayed.some(el => el)) { - this.closeAllRenameBoxes(); - return; - } - browserHistory.push(`/${username}/sketches/${sketch.id}`); - }} - > - - - {this.state.renameBoxDisplayed[i] ? '' : sketch.name} - - {this.state.renameBoxDisplayed[i] - && - { - const renameBoxContent = [...this.state.renameBoxContent]; - renameBoxContent[i] = e.target.value; - this.setState({ - renameBoxContent - }); - }} - onKeyUp={(e) => { - // Enter pressed - if (e.key === 'Enter') { - this.props.changeProjectName(sketch.id, this.state.renameBoxContent[i]); - this.restoreRenameBoxContent(); - this.closeAllRenameBoxes(); - } - }} - onBlur={this.restoreRenameBoxContent} - onClick={e => e.stopPropagation()} - autoFocus //eslint-disable-line - /> - } - - {format(new Date(sketch.createdAt), 'MMM D, YYYY h:mm A')} - {format(new Date(sketch.updatedAt), 'MMM D, YYYY h:mm A')} - - - {this.state.actionDialogueDisplayed[i] && -
      e.stopPropagation()} - > -
    • - -
    • -
    • - -
    • - {this.props.user.authenticated && -
    • - -
    • } -
    • - -
    • -
    • - -
    • -
    } - - )} + sketch={sketch} + user={this.props.user} + username={username} + />))} }
    @@ -290,23 +351,18 @@ SketchList.propTypes = { })).isRequired, username: PropTypes.string, loading: PropTypes.bool.isRequired, - deleteProject: PropTypes.func.isRequired, toggleDirectionForField: PropTypes.func.isRequired, resetSorting: PropTypes.func.isRequired, sorting: PropTypes.shape({ field: PropTypes.string.isRequired, direction: PropTypes.string.isRequired }).isRequired, - showShareModal: PropTypes.func.isRequired, project: PropTypes.shape({ id: PropTypes.string, owner: PropTypes.shape({ id: PropTypes.string }) - }), - cloneProject: PropTypes.func.isRequired, - exportProjectAsZip: PropTypes.func.isRequired, - changeProjectName: PropTypes.func.isRequired + }) }; SketchList.defaultProps = { @@ -328,7 +384,7 @@ function mapStateToProps(state) { } function mapDispatchToProps(dispatch) { - return bindActionCreators(Object.assign({}, SketchActions, ProjectActions, ToastActions, SortingActions), dispatch); + return bindActionCreators(Object.assign({}, ProjectsActions, ToastActions, SortingActions), dispatch); } export default connect(mapStateToProps, mapDispatchToProps)(SketchList); diff --git a/client/modules/IDE/reducers/ide.js b/client/modules/IDE/reducers/ide.js index 1396241600..f95592347d 100644 --- a/client/modules/IDE/reducers/ide.js +++ b/client/modules/IDE/reducers/ide.js @@ -12,7 +12,7 @@ const initialState = { shareModalVisible: false, shareModalProjectId: null, shareModalProjectName: null, - shareModalOwnerUsername: null, + shareModalProjectUsername: null, editorOptionsVisible: false, keyboardShortcutVisible: false, unsavedChanges: false, @@ -68,7 +68,7 @@ const ide = (state = initialState, action) => { shareModalVisible: true, shareModalProjectId: action.payload.shareModalProjectId, shareModalProjectName: action.payload.shareModalProjectName, - shareModalOwnerUsername: action.payload.shareModalOwnerUsername, + shareModalProjectUsername: action.payload.shareModalProjectUsername, }); case ActionTypes.CLOSE_SHARE_MODAL: return Object.assign({}, state, { shareModalVisible: false }); diff --git a/client/styles/components/_sidebar.scss b/client/styles/components/_sidebar.scss index 9c852e9da0..345d0b40a8 100644 --- a/client/styles/components/_sidebar.scss +++ b/client/styles/components/_sidebar.scss @@ -25,6 +25,8 @@ } .sidebar__add { + width: #{20 / $base-font-size}rem; + height: #{20 / $base-font-size}rem; @include icon(); .sidebar--contracted & { display: none; @@ -121,10 +123,11 @@ } .sidebar__file-item-show-options { + width: #{20 / $base-font-size}rem; + height: #{20 / $base-font-size}rem; @include icon(); @include themify() { - padding: #{4 / $base-font-size}rem 0; - padding-right: #{6 / $base-font-size}rem; + margin-right: #{5 / $base-font-size}rem; } display: none; position: absolute; diff --git a/client/styles/components/_sketch-list.scss b/client/styles/components/_sketch-list.scss index dec4f79d34..f49bf828e4 100644 --- a/client/styles/components/_sketch-list.scss +++ b/client/styles/components/_sketch-list.scss @@ -53,7 +53,6 @@ margin: #{10 / $base-font-size}rem; height: #{72 / $base-font-size}rem; font-size: #{16 / $base-font-size}rem; - cursor: pointer; } .sketches-table__row:nth-child(odd) { @@ -85,26 +84,9 @@ } .sketch-list__dropdown-button { - width:#{20 / $base-font-size}rem; - height:#{20 / $base-font-size}rem; - transform: translateY(-3px); + width:#{25 / $base-font-size}rem; + height:#{25 / $base-font-size}rem; @include themify() { - background-color: transparent; - border: none; - cursor: pointer; - padding: 0; - position: initial; - left: 0; - top: 0; - - & polygon { - fill: getThemifyVariable('dropdown-color'); - } - - & polygon { - fill: getThemifyVariable('dropdown-color'); - } - & polygon { fill: getThemifyVariable('dropdown-color'); } From 7c22139823d9ba68593cd4f2ae0fa24e21c4b7ac Mon Sep 17 00:00:00 2001 From: Cassie Tarakajian Date: Wed, 19 Jun 2019 15:56:36 -0400 Subject: [PATCH 6/7] fix broken links on asset list --- client/modules/IDE/components/AssetList.jsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/modules/IDE/components/AssetList.jsx b/client/modules/IDE/components/AssetList.jsx index 42513fbaac..ab54832234 100644 --- a/client/modules/IDE/components/AssetList.jsx +++ b/client/modules/IDE/components/AssetList.jsx @@ -17,13 +17,14 @@ class AssetList extends React.Component { } getAssetsTitle() { - if (this.props.username === this.props.user.username) { + if (!this.props.username || this.props.username === this.props.user.username) { return 'p5.js Web Editor | My assets'; } return `p5.js Web Editor | ${this.props.username}'s assets`; } render() { + const username = this.props.username !== undefined ? this.props.username : this.props.user.username; return (
    @@ -49,7 +50,7 @@ class AssetList extends React.Component { {asset.name} {prettyBytes(asset.size)} View - {asset.sketchName} + {asset.sketchName} ))} From 4a8d8c29d4485c2c405dd97225e9e1e7bae6d4fe Mon Sep 17 00:00:00 2001 From: Cassie Tarakajian Date: Wed, 19 Jun 2019 16:12:11 -0400 Subject: [PATCH 7/7] remove unused image, fix options for different users in sketch list --- client/images/btn-up-triangle-divot.svg | 6 ----- client/modules/IDE/components/SketchList.jsx | 27 +++++++++++--------- 2 files changed, 15 insertions(+), 18 deletions(-) delete mode 100644 client/images/btn-up-triangle-divot.svg diff --git a/client/images/btn-up-triangle-divot.svg b/client/images/btn-up-triangle-divot.svg deleted file mode 100644 index b988b6822a..0000000000 --- a/client/images/btn-up-triangle-divot.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/client/modules/IDE/components/SketchList.jsx b/client/modules/IDE/components/SketchList.jsx index 2ac937bfa0..bcfdf15fd4 100644 --- a/client/modules/IDE/components/SketchList.jsx +++ b/client/modules/IDE/components/SketchList.jsx @@ -136,6 +136,7 @@ class SketchListRowBase extends React.Component { render() { const { sketch, username } = this.props; const { renameOpen, optionsOpen, renameValue } = this.state; + const userIsOwner = this.props.user.username === this.props.username; return ( + {userIsOwner &&
  • -
  • + }
  • {this.props.user.authenticated && -
  • - -
  • } +
  • + +
  • } { /*
  • */ } + {userIsOwner &&
  • -
  • + } } );