From bd9076c4ffecd6397993e516d14f9c9674a86de6 Mon Sep 17 00:00:00 2001 From: orangemug Date: Tue, 22 May 2018 21:16:46 +0100 Subject: [PATCH 1/9] Added additional menu in This is to make the following options accessible to keyboard users - reorder layers - duplicate layer - delete layer - hide/show layer --- package-lock.json | 25 +++++++++ package.json | 2 + src/components/App.jsx | 66 ++++++++++++++++++++++ src/components/layers/LayerEditor.jsx | 75 +++++++++++++++++++++++++ src/components/layers/LayerList.jsx | 49 ++-------------- src/components/layers/LayerListItem.jsx | 10 ++-- src/styles/_layer.scss | 50 ++++++++++++++++- 7 files changed, 227 insertions(+), 50 deletions(-) diff --git a/package-lock.json b/package-lock.json index 35c46c9..4667ddc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5523,6 +5523,11 @@ "readable-stream": "2.3.5" } }, + "focus-group": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/focus-group/-/focus-group-0.3.1.tgz", + "integrity": "sha1-4PMu2GsNq91v/OvfiY7LMuR/7c4=" + }, "focus-trap": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-2.4.4.tgz", @@ -8365,6 +8370,11 @@ "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", "integrity": "sha1-+CbJtOKoUR2E46yinbBeGk87cqk=" }, + "lodash.clamp": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/lodash.clamp/-/lodash.clamp-4.0.3.tgz", + "integrity": "sha1-XCS+3u7vB1NWDcK0y0Zx+Qpt36o=" + }, "lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", @@ -11479,6 +11489,16 @@ "object-assign": "4.1.1" } }, + "react-aria-menubutton": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/react-aria-menubutton/-/react-aria-menubutton-5.1.1.tgz", + "integrity": "sha512-ceBjPvuqwM2jnRFsfMlpfPdyqQ5cz4STNH7NlKpxStr2uETB/zQ2sHiIUMTuqSuOszU1kgUB2vm3aVn3xdjhcA==", + "requires": { + "focus-group": "0.3.1", + "prop-types": "15.6.1", + "teeny-tap": "0.2.0" + } + }, "react-aria-modal": { "version": "2.12.1", "resolved": "https://registry.npmjs.org/react-aria-modal/-/react-aria-modal-2.12.1.tgz", @@ -14298,6 +14318,11 @@ "xtend": "4.0.1" } }, + "teeny-tap": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/teeny-tap/-/teeny-tap-0.2.0.tgz", + "integrity": "sha1-Fn5kUYLQasIi1iuyq2eUenClimg=" + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", diff --git a/package.json b/package.json index 0184727..575c9ab 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "github-api": "^3.0.0", "jsonlint": "github:josdejong/jsonlint#85a19d7", "lodash.capitalize": "^4.2.1", + "lodash.clamp": "^4.0.3", "lodash.clonedeep": "^4.5.0", "lodash.isequal": "^4.5.0", "lodash.throttle": "^4.1.1", @@ -42,6 +43,7 @@ "prop-types": "^15.6.0", "react": "^16.3.2", "react-addons-pure-render-mixin": "^15.6.2", + "react-aria-menubutton": "^5.1.1", "react-aria-modal": "^2.12.1", "react-autocomplete": "^1.7.2", "react-codemirror2": "^4.2.1", diff --git a/src/components/App.jsx b/src/components/App.jsx index 61b2ea7..857931f 100644 --- a/src/components/App.jsx +++ b/src/components/App.jsx @@ -1,5 +1,8 @@ import React from 'react' import Mousetrap from 'mousetrap' +import cloneDeep from 'lodash.clonedeep' +import clamp from 'lodash.clamp' +import {arrayMove} from 'react-sortable-hoc'; import MapboxGlMap from './map/MapboxGlMap' import OpenLayers3Map from './map/OpenLayers3Map' @@ -164,6 +167,24 @@ export default class App extends React.Component { }) } + onSortEnd(move) { + let { oldIndex, newIndex } = move; + let layers = this.state.mapStyle.layers; + oldIndex = clamp(oldIndex, 0, layers.length-1); + newIndex = clamp(newIndex, 0, layers.length-1); + if(oldIndex === newIndex) return; + + if (oldIndex === this.state.selectedLayerIndex) { + this.setState({ + selectedLayerIndex: newIndex + }); + } + + layers = layers.slice(0); + layers = arrayMove(layers, oldIndex, newIndex); + this.onLayersChange(layers); + } + onLayersChange(changedLayers) { const changedStyle = { ...this.state.mapStyle, @@ -172,6 +193,40 @@ export default class App extends React.Component { this.onStyleChanged(changedStyle) } + onLayerDestroy(layerId) { + let layers = this.state.mapStyle.layers; + const remainingLayers = layers.slice(0); + const idx = style.indexOfLayer(remainingLayers, layerId) + remainingLayers.splice(idx, 1); + this.onLayersChange(remainingLayers); + } + + onLayerCopy(layerId) { + let layers = this.state.mapStyle.layers; + const changedLayers = layers.slice(0) + const idx = style.indexOfLayer(changedLayers, layerId) + + const clonedLayer = cloneDeep(changedLayers[idx]) + clonedLayer.id = clonedLayer.id + "-copy" + changedLayers.splice(idx, 0, clonedLayer) + this.onLayersChange(changedLayers) + } + + onLayerVisibilityToggle(layerId) { + let layers = this.state.mapStyle.layers; + const changedLayers = layers.slice(0) + const idx = style.indexOfLayer(changedLayers, layerId) + + const layer = { ...changedLayers[idx] } + const changedLayout = 'layout' in layer ? {...layer.layout} : {} + changedLayout.visibility = changedLayout.visibility === 'none' ? 'visible' : 'none' + + layer.layout = changedLayout + changedLayers[idx] = layer + this.onLayersChange(changedLayers) + } + + onLayerIdChange(oldId, newId) { const changedLayers = this.state.mapStyle.layers.slice(0) const idx = style.indexOfLayer(changedLayers, oldId) @@ -297,6 +352,10 @@ export default class App extends React.Component { /> const layerList = : null diff --git a/src/components/layers/LayerEditor.jsx b/src/components/layers/LayerEditor.jsx index d205640..16c8d28 100644 --- a/src/components/layers/LayerEditor.jsx +++ b/src/components/layers/LayerEditor.jsx @@ -1,5 +1,7 @@ import React from 'react' import PropTypes from 'prop-types' +import { Wrapper, Button, Menu, MenuItem } from 'react-aria-menubutton' +import classnames from 'classnames' import JSONEditor from './JSONEditor' import FilterEditor from '../filter/FilterEditor' @@ -13,6 +15,8 @@ import CommentBlock from './CommentBlock' import LayerSourceBlock from './LayerSourceBlock' import LayerSourceLayerBlock from './LayerSourceLayerBlock' +import MoreVertIcon from 'react-icons/lib/md/more-vert' + import InputBlock from '../inputs/InputBlock' import MultiButtonInput from '../inputs/MultiButtonInput' @@ -176,6 +180,13 @@ export default class LayerEditor extends React.Component { } } + moveLayer(offset) { + this.props.onSortEnd({ + oldIndex: this.props.layerIndex, + newIndex: this.props.layerIndex+offset + }) + } + render() { const layerType = this.props.layer.type const groups = layoutGroups(layerType).filter(group => { @@ -192,8 +203,72 @@ export default class LayerEditor extends React.Component { }) + + const items = { + delete: { + text: "Delete", + handler: () => this.props.onLayerDestroy(this.props.layer.id) + }, + duplicate: { + text: "Duplicate", + handler: () => this.props.onLayerCopy(this.props.layer.id) + }, + hide: { + text: "Hide", + handler: () => this.props.onLayerVisibilityToggle(this.props.layer.id) + }, + moveLayerUp: { + text: "Move layer up", + // Not actually used... + disabled: this.props.isFirstLayer, + handler: () => this.moveLayer(-1) + }, + moveLayerDown: { + text: "Move layer down", + // Not actually used... + disabled: this.props.isLastLayer, + handler: () => this.moveLayer(+1) + } + } + + function handleSelection(id, event) { + event.stopPropagation; + items[id].handler(); + } + return
+
+
+

+ Layer: {this.props.layer.id} +

+
+ + + +
    + {Object.keys(items).map((id, idx) => { + const item = items[id]; + return
  • + + {item.text} + +
  • + })} +
+
+
+
+
+ +
{groups}
} diff --git a/src/components/layers/LayerList.jsx b/src/components/layers/LayerList.jsx index a9b144f..25312f6 100644 --- a/src/components/layers/LayerList.jsx +++ b/src/components/layers/LayerList.jsx @@ -1,7 +1,6 @@ import React from 'react' import PropTypes from 'prop-types' import classnames from 'classnames' -import cloneDeep from 'lodash.clonedeep' import Button from '../Button' import LayerListGroup from './LayerListGroup' @@ -10,7 +9,7 @@ import AddIcon from 'react-icons/lib/md/add-circle-outline' import AddModal from '../modals/AddModal' import style from '../../libs/style.js' -import {SortableContainer, SortableHandle, arrayMove} from 'react-sortable-hoc'; +import {SortableContainer, SortableHandle} from 'react-sortable-hoc'; const layerListPropTypes = { layers: PropTypes.array.isRequired, @@ -57,36 +56,6 @@ class LayerListContainer extends React.Component { } } - onLayerDestroy(layerId) { - const remainingLayers = this.props.layers.slice(0) - const idx = style.indexOfLayer(remainingLayers, layerId) - remainingLayers.splice(idx, 1); - this.props.onLayersChange(remainingLayers) - } - - onLayerCopy(layerId) { - const changedLayers = this.props.layers.slice(0) - const idx = style.indexOfLayer(changedLayers, layerId) - - const clonedLayer = cloneDeep(changedLayers[idx]) - clonedLayer.id = clonedLayer.id + "-copy" - changedLayers.splice(idx, 0, clonedLayer) - this.props.onLayersChange(changedLayers) - } - - onLayerVisibilityToggle(layerId) { - const changedLayers = this.props.layers.slice(0) - const idx = style.indexOfLayer(changedLayers, layerId) - - const layer = { ...changedLayers[idx] } - const changedLayout = 'layout' in layer ? {...layer.layout} : {} - changedLayout.visibility = changedLayout.visibility === 'none' ? 'visible' : 'none' - - layer.layout = changedLayout - changedLayers[idx] = layer - this.props.onLayersChange(changedLayers) - } - toggleModal(modalName) { this.setState({ isOpen: { @@ -186,9 +155,9 @@ class LayerListContainer extends React.Component { visibility={(layer.layout || {}).visibility} isSelected={idx === this.props.selectedLayerIndex} onLayerSelect={this.props.onLayerSelect} - onLayerDestroy={this.onLayerDestroy.bind(this)} - onLayerCopy={this.onLayerCopy.bind(this)} - onLayerVisibilityToggle={this.onLayerVisibilityToggle.bind(this)} + onLayerDestroy={this.props.onLayerDestroy.bind(this)} + onLayerCopy={this.props.onLayerCopy.bind(this)} + onLayerVisibilityToggle={this.props.onLayerVisibilityToggle.bind(this)} /> listItems.push(listItem) idx += 1 @@ -236,18 +205,10 @@ class LayerListContainer extends React.Component { export default class LayerList extends React.Component { static propTypes = {...layerListPropTypes} - onSortEnd(move) { - const { oldIndex, newIndex } = move - if(oldIndex === newIndex) return - let layers = this.props.layers.slice(0) - layers = arrayMove(layers, oldIndex, newIndex) - this.props.onLayersChange(layers) - } - render() { return } diff --git a/src/components/layers/LayerListItem.jsx b/src/components/layers/LayerListItem.jsx index 91c9217..5c65188 100644 --- a/src/components/layers/LayerListItem.jsx +++ b/src/components/layers/LayerListItem.jsx @@ -38,7 +38,7 @@ class IconAction extends React.Component { renderIcon() { switch(this.props.action) { - case 'copy': return + case 'duplicate': return case 'show': return case 'hide': return case 'delete': return @@ -46,13 +46,15 @@ class IconAction extends React.Component { } render() { - return {this.renderIcon()} - + } } @@ -109,7 +111,7 @@ class LayerListItem extends React.Component { /> this.props.onLayerCopy(this.props.layerId)} /> Date: Tue, 22 May 2018 21:26:11 +0100 Subject: [PATCH 2/9] Remove empty scss blocks --- src/styles/_layer.scss | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/styles/_layer.scss b/src/styles/_layer.scss index d4f27d0..8013f3a 100644 --- a/src/styles/_layer.scss +++ b/src/styles/_layer.scss @@ -183,9 +183,6 @@ .more-menu { position: relative; - &__button { - } - &__menu { position: absolute; z-index: 9999; From c71fbcf436ac38e0f19cf0bba12c4046e6bd72b2 Mon Sep 17 00:00:00 2001 From: orangemug Date: Mon, 28 May 2018 11:15:16 +0100 Subject: [PATCH 3/9] Tidy --- src/components/App.jsx | 6 +++--- src/components/layers/LayerEditor.jsx | 5 ++--- src/components/layers/LayerList.jsx | 2 +- src/styles/_layer.scss | 5 +++++ 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/components/App.jsx b/src/components/App.jsx index 857931f..7817503 100644 --- a/src/components/App.jsx +++ b/src/components/App.jsx @@ -167,7 +167,7 @@ export default class App extends React.Component { }) } - onSortEnd(move) { + onMoveLayer(move) { let { oldIndex, newIndex } = move; let layers = this.state.mapStyle.layers; oldIndex = clamp(oldIndex, 0, layers.length-1); @@ -352,7 +352,7 @@ export default class App extends React.Component { /> const layerList =
    diff --git a/src/components/layers/LayerList.jsx b/src/components/layers/LayerList.jsx index 25312f6..1b50b89 100644 --- a/src/components/layers/LayerList.jsx +++ b/src/components/layers/LayerList.jsx @@ -208,7 +208,7 @@ export default class LayerList extends React.Component { render() { return } diff --git a/src/styles/_layer.scss b/src/styles/_layer.scss index 8013f3a..c5a3a9a 100644 --- a/src/styles/_layer.scss +++ b/src/styles/_layer.scss @@ -192,6 +192,11 @@ min-width: 120px; } + &__button__svg { + width: 24px; + height: 24px; + } + &__menu__item { padding: 4px; } From d59d9cde9577da3bd5b63b2212e7ce5a01267d1e Mon Sep 17 00:00:00 2001 From: orangemug Date: Mon, 28 May 2018 11:19:04 +0100 Subject: [PATCH 4/9] Fixed OSX working directory if CircleCI config. --- .circleci/config.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9d16b64..bd9e5d8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -71,7 +71,7 @@ jobs: dependencies: override: - brew install node@6 - working_directory: ~/repo-linux-node-v6 + working_directory: ~/repo-osx-node-v6 steps: *build-steps build-osx-node-v8: macos: @@ -79,7 +79,7 @@ jobs: dependencies: override: - brew install node@8 - working_directory: ~/repo-linux-node-v8 + working_directory: ~/repo-osx-node-v8 steps: *build-steps build-osx-node-v10: macos: @@ -87,7 +87,7 @@ jobs: dependencies: override: - brew install node@10 - working_directory: ~/repo-linux-node-v10 + working_directory: ~/repo-osx-node-v10 steps: *build-steps workflows: From fc7395df962ec79a42ef869ced4597a90d8a6b43 Mon Sep 17 00:00:00 2001 From: orangemug Date: Mon, 28 May 2018 11:34:12 +0100 Subject: [PATCH 5/9] Fixed CircleCI cache to include `{{arch}}` --- .circleci/config.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index bd9e5d8..68ffeeb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,14 +7,14 @@ templates: name: "Create artifacts directory" command: mkdir /tmp/artifacts - restore_cache: - key: v1-dependencies-{{ checksum "package.json" }} + key: v1-dependencies-{{ arch }}-{{ checksum "package.json" }} - run: npm install - save_cache: paths: - node_modules - key: v1-dependencies-{{ checksum "package.json" }} + key: v1-dependencies-{{ arch }}-{{ checksum "package.json" }} - run: mkdir -p /tmp/artifacts/logs - run: npm run build @@ -30,14 +30,14 @@ templates: name: "Create artifacts directory" command: mkdir /tmp/artifacts - restore_cache: - key: v1-dependencies-{{ checksum "package.json" }} + key: v1-dependencies-{{ arch }}-{{ checksum "package.json" }} - run: npm install - save_cache: paths: - node_modules - key: v1-dependencies-{{ checksum "package.json" }} + key: v1-dependencies-{{ arch }}-{{ checksum "package.json" }} - run: mkdir -p /tmp/artifacts/logs - run: npm run build From 019428a241bf816c0fa4afbc2eef66cb85d9d244 Mon Sep 17 00:00:00 2001 From: orangemug Date: Mon, 28 May 2018 12:06:22 +0100 Subject: [PATCH 6/9] Added missing prop-types. --- src/components/layers/LayerEditor.jsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/components/layers/LayerEditor.jsx b/src/components/layers/LayerEditor.jsx index 6f6f44c..906ac9c 100644 --- a/src/components/layers/LayerEditor.jsx +++ b/src/components/layers/LayerEditor.jsx @@ -48,6 +48,13 @@ export default class LayerEditor extends React.Component { spec: PropTypes.object.isRequired, onLayerChanged: PropTypes.func, onLayerIdChange: PropTypes.func, + onMoveLayer: PropTypes.func, + onLayerDestroy: PropTypes.func, + onLayerCopy: PropTypes.func, + onLayerVisibilityToggle: PropTypes.func, + isFirstLayer: PropTypes.bool, + isLastLayer: PropTypes.bool, + layerIndex: PropTypes.number, } static defaultProps = { From f5e8d473ad0cd68792cebcec5a0701fd4fb4cc99 Mon Sep 17 00:00:00 2001 From: orangemug Date: Thu, 31 May 2018 20:40:21 +0100 Subject: [PATCH 7/9] Changed toggle visibility text from hide to show/hide --- src/components/layers/LayerEditor.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/layers/LayerEditor.jsx b/src/components/layers/LayerEditor.jsx index 906ac9c..4965774 100644 --- a/src/components/layers/LayerEditor.jsx +++ b/src/components/layers/LayerEditor.jsx @@ -220,7 +220,7 @@ export default class LayerEditor extends React.Component { handler: () => this.props.onLayerCopy(this.props.layer.id) }, hide: { - text: "Hide", + text: (this.props.layer.layout.visibility === "visible") ? "Hide" : "Show", handler: () => this.props.onLayerVisibilityToggle(this.props.layer.id) }, moveLayerUp: { From 13fc699d4a30f20f54cf1a3f96809d5c01524275 Mon Sep 17 00:00:00 2001 From: orangemug Date: Thu, 31 May 2018 21:09:31 +0100 Subject: [PATCH 8/9] Styling fixes. --- src/styles/_layer.scss | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/styles/_layer.scss b/src/styles/_layer.scss index c5a3a9a..391c66a 100644 --- a/src/styles/_layer.scss +++ b/src/styles/_layer.scss @@ -137,6 +137,7 @@ user-select: none; padding: $margin-2; line-height: 20px; + border-top: solid 1px #36383e; @include flex-row; @@ -206,10 +207,11 @@ display: flex; padding: 6px; background: $color-black; - border-bottom: solid 1px $color-midgray; &__title { flex: 1; + margin: 0; + line-height: 24px; } &__info { From 32edb48e161942c04bd1fd940004cc4c9b23e4f6 Mon Sep 17 00:00:00 2001 From: orangemug Date: Sun, 3 Jun 2018 09:31:02 +0100 Subject: [PATCH 9/9] Fix for when 'layout.visibility' is undefined --- src/components/layers/LayerEditor.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/layers/LayerEditor.jsx b/src/components/layers/LayerEditor.jsx index 4965774..7d7291d 100644 --- a/src/components/layers/LayerEditor.jsx +++ b/src/components/layers/LayerEditor.jsx @@ -209,6 +209,7 @@ export default class LayerEditor extends React.Component { }) + const layout = this.props.layer.layout || {} const items = { delete: { @@ -220,7 +221,7 @@ export default class LayerEditor extends React.Component { handler: () => this.props.onLayerCopy(this.props.layer.id) }, hide: { - text: (this.props.layer.layout.visibility === "visible") ? "Hide" : "Show", + text: (layout.visibility === "none") ? "Show" : "Hide", handler: () => this.props.onLayerVisibilityToggle(this.props.layer.id) }, moveLayerUp: {