summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJules Laplace <julescarbon@gmail.com>2020-06-06 16:30:51 +0200
committerJules Laplace <julescarbon@gmail.com>2020-06-06 16:30:51 +0200
commit434e53dea597f61ad59e14012f528ceac58ead85 (patch)
treef4dc6bae69f4c46dad9cfce1f74684cccfc26388
parenta42008b2d8c051ec2110d866c2da288a66a1d989 (diff)
tile list. drag items to sort them
-rw-r--r--cli/app/controllers/crud_controller.py2
-rw-r--r--cli/app/controllers/page_controller.py19
-rw-r--r--cli/app/sql/models/page.py2
-rw-r--r--cli/app/sql/models/tile.py2
-rw-r--r--cli/app/sql/versions/202006041801_add_sort_order_to_tiles.py29
-rw-r--r--frontend/store.js2
-rw-r--r--frontend/types.js2
-rw-r--r--frontend/views/graph/graph.css7
-rw-r--r--frontend/views/page/components/page.header.js1
-rw-r--r--frontend/views/page/components/tile.form.js8
-rw-r--r--frontend/views/page/components/tile.list.js73
-rw-r--r--frontend/views/page/components/tile.new.js1
-rw-r--r--frontend/views/page/page.actions.js24
-rw-r--r--frontend/views/page/page.container.js2
-rw-r--r--frontend/views/page/page.css29
-rw-r--r--frontend/views/page/page.reducer.js46
-rw-r--r--package-lock.json347
-rw-r--r--package.json4
18 files changed, 359 insertions, 241 deletions
diff --git a/cli/app/controllers/crud_controller.py b/cli/app/controllers/crud_controller.py
index 7692d26..c1bde8d 100644
--- a/cli/app/controllers/crud_controller.py
+++ b/cli/app/controllers/crud_controller.py
@@ -108,7 +108,7 @@ class CrudView(FlaskView):
if item:
raw_form = MultiDict(request.json) if request.json is not None else request.form
form = self.form(raw_form, obj=item)
- print(item.toJSON())
+ # print(item.toJSON())
if form.validate():
form.populate_obj(item)
self.on_update(session, raw_form, item)
diff --git a/cli/app/controllers/page_controller.py b/cli/app/controllers/page_controller.py
index b6bfaa8..3da2068 100644
--- a/cli/app/controllers/page_controller.py
+++ b/cli/app/controllers/page_controller.py
@@ -28,6 +28,25 @@ class PageView(CrudView):
def on_destroy(self, session, item):
session.query(Tile).filter(Tile.page_id == item.id).delete(synchronize_session=False)
+ @route('/sort/<int:page_id>', methods=['POST'])
+ def set_sort_order(self, page_id):
+ page = session.query(Page).get(page_id)
+ if not page:
+ session.close()
+ return jsonify({
+ 'status': 'error',
+ 'error': 'page not found'
+ })
+
+ print(request.json)
+
+ result = {
+ 'status': 'ok',
+ # 'res': page.toFullJSON() if hasattr(page, 'toFullJSON') else page.toJSON(),
+ }
+ session.close()
+ return jsonify(result)
+
@route('/name/<graph_path>/<page_path>', methods=['GET'])
def get_name(self, graph_path: str, page_path: str):
"""
diff --git a/cli/app/sql/models/page.py b/cli/app/sql/models/page.py
index 2e6bbad..960ffd7 100644
--- a/cli/app/sql/models/page.py
+++ b/cli/app/sql/models/page.py
@@ -22,7 +22,7 @@ class Page(Base):
created_at = Column(UtcDateTime(), default=utcnow())
updated_at = Column(UtcDateTime(), onupdate=utcnow())
- tiles = relationship("Tile", foreign_keys="Tile.page_id", lazy='dynamic')
+ tiles = relationship("Tile", foreign_keys="Tile.page_id", lazy='dynamic', order_by="asc(Tile.sort_order)")
def toJSON(self):
return {
diff --git a/cli/app/sql/models/tile.py b/cli/app/sql/models/tile.py
index 7dfa80b..3f6ce31 100644
--- a/cli/app/sql/models/tile.py
+++ b/cli/app/sql/models/tile.py
@@ -20,6 +20,7 @@ class Tile(Base):
page_id = Column(Integer, ForeignKey('page.id'), nullable=True)
target_page_id = Column(Integer, ForeignKey('page.id'), nullable=True)
type = Column(String(16, convert_unicode=True), nullable=False)
+ sort_order = Column(Integer, default=0)
settings = Column(JSON, default={}, nullable=True)
created_at = Column(UtcDateTime(), default=utcnow())
updated_at = Column(UtcDateTime(), onupdate=utcnow())
@@ -31,6 +32,7 @@ class Tile(Base):
'page_id': self.page_id,
'target_page_id': self.target_page_id,
'type': self.type,
+ 'sort_order': self.sort_order,
'settings': self.settings,
'created_at': self.created_at,
'updated_at': self.updated_at,
diff --git a/cli/app/sql/versions/202006041801_add_sort_order_to_tiles.py b/cli/app/sql/versions/202006041801_add_sort_order_to_tiles.py
new file mode 100644
index 0000000..2c670a8
--- /dev/null
+++ b/cli/app/sql/versions/202006041801_add_sort_order_to_tiles.py
@@ -0,0 +1,29 @@
+"""add sort order to tiles
+
+Revision ID: 91a729020584
+Revises: 453787357254
+Create Date: 2020-06-04 18:01:33.482300
+
+"""
+from alembic import op
+import sqlalchemy as sa
+import sqlalchemy_utc
+
+
+# revision identifiers, used by Alembic.
+revision = '91a729020584'
+down_revision = '453787357254'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.add_column('tile', sa.Column('sort_order', sa.Integer(), nullable=True))
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_column('tile', 'sort_order')
+ # ### end Alembic commands ###
diff --git a/frontend/store.js b/frontend/store.js
index 40c8d9c..9af2b96 100644
--- a/frontend/store.js
+++ b/frontend/store.js
@@ -1,6 +1,7 @@
import { applyMiddleware, compose, combineReducers, createStore } from 'redux'
import { connectRouter, routerMiddleware } from 'connected-react-router'
import { createBrowserHistory } from 'history'
+import createDebounce from 'redux-debounced';
import thunk from 'redux-thunk'
// import { login } from './util'
@@ -33,6 +34,7 @@ const configureStore = (initialState = {}, history) => {
composeEnhancers(
applyMiddleware(
thunk,
+ createDebounce(),
routerMiddleware(history)
),
),
diff --git a/frontend/types.js b/frontend/types.js
index 98c18cb..10fb411 100644
--- a/frontend/types.js
+++ b/frontend/types.js
@@ -12,6 +12,8 @@ export const page = crud_type('page', [
'show_add_tile_form', 'hide_add_tile_form', 'toggle_add_tile_form',
'show_edit_tile_form', 'hide_edit_tile_form', 'toggle_edit_tile_form',
'update_page_tile',
+ 'set_tile_sort_order', 'update_tile_sort_order',
+ 'show_tile_list', 'hide_tile_list', 'toggle_tile_list',
])
export const tile = crud_type('tile', [
diff --git a/frontend/views/graph/graph.css b/frontend/views/graph/graph.css
index d6a0ff0..60a4c81 100644
--- a/frontend/views/graph/graph.css
+++ b/frontend/views/graph/graph.css
@@ -17,8 +17,11 @@
.sidebar {
position: absolute;
- top: 1rem;
- right: 1rem;
+ top: 0;
+ right: 0;
+ padding: 1rem;
+ overflow: auto;
+ max-height: 100%;
z-index: 2000;
}
.box {
diff --git a/frontend/views/page/components/page.header.js b/frontend/views/page/components/page.header.js
index 4170f8b..2898f86 100644
--- a/frontend/views/page/components/page.header.js
+++ b/frontend/views/page/components/page.header.js
@@ -13,6 +13,7 @@ function PageHeader(props) {
</div>
<div>
<button onClick={() => props.pageActions.toggleAddTileForm()}>+ Add tile</button>
+ <button onClick={() => props.pageActions.toggleTileList()}>+ Sort tiles</button>
</div>
</header>
)
diff --git a/frontend/views/page/components/tile.form.js b/frontend/views/page/components/tile.form.js
index 31d49e3..6cf9474 100644
--- a/frontend/views/page/components/tile.form.js
+++ b/frontend/views/page/components/tile.form.js
@@ -77,7 +77,7 @@ class TileForm extends Component {
}
componentDidMount() {
- const { graph, page, isNew, initialData } = this.props
+ const { graph, page, isNew, initialData, sortOrder } = this.props
const title = isNew ? 'new tile' : 'editing tile'
const submitTitle = isNew ? "Create Tile" : "Save Changes"
this.setState({
@@ -86,11 +86,13 @@ class TileForm extends Component {
errorFields: new Set([]),
})
if (isNew) {
- this.props.tileActions.updateTemporaryTile(newImage({
+ const newTile = newImage({
id: "new",
graph_id: graph.show.res.id,
page_id: page.show.res.id,
- }))
+ sort_order: sortOrder,
+ })
+ this.props.tileActions.updateTemporaryTile(newTile)
} else {
this.props.tileActions.updateTemporaryTile({ ...initialData })
}
diff --git a/frontend/views/page/components/tile.list.js b/frontend/views/page/components/tile.list.js
new file mode 100644
index 0000000..232516d
--- /dev/null
+++ b/frontend/views/page/components/tile.list.js
@@ -0,0 +1,73 @@
+import React, { Component } from 'react'
+// import { Link } from 'react-router-dom'
+import { bindActionCreators } from 'redux'
+import { connect } from 'react-redux'
+import { ReactSortable } from "react-sortablejs"
+
+// import actions from '../../../actions'
+import * as tileActions from '../../tile/tile.actions'
+import * as pageActions from '../../page/page.actions'
+
+class TileList extends Component {
+ state = {
+ tiles: [],
+ }
+
+ componentDidMount(prevProps) {
+ const { tiles } = this.props.page.show.res
+ this.setState({ tiles })
+ // this.props.pageActions.setTileSortOrder(list)
+ }
+
+ componentDidUpdate(prevProps, prevState) {
+ const { tiles } = this.state
+ const { tiles: oldTiles } = prevState
+ // const { tiles } = this.props.page.show.res
+ // const { tiles: oldTiles } = prevProps.page.show.res
+ if (tiles !== oldTiles) {
+ this.props.pageActions.setTileSortOrder(tiles)
+ }
+ }
+
+ render() {
+ const { tiles } = this.state
+ return (
+ <div className='box tileList'>
+ <ReactSortable
+ list={tiles}
+ setList={newTiles => this.setState({ tiles: newTiles })}
+ >
+ {tiles.map(tile => (
+ tile.type === 'image'
+ ? <TileListImage key={tile.id} tile={tile} />
+ : <TileListText key={tile.id} tile={tile} />
+ ))}
+ </ReactSortable>
+ </div>
+ )
+ }
+}
+
+const TileListImage = ({ tile }) => (
+ <div className='row'>
+ <div className='thumb' style={{ backgroundImage: 'url(' + tile.settings.url + ')' }} />
+ </div>
+)
+
+const TileListText = ({ tile }) => (
+ <div className='row'>
+ <span className='snippet'>{tile.settings.content.substr(0, 100)}</span>
+ </div>
+)
+
+const mapStateToProps = state => ({
+ graph: state.graph,
+ page: state.page,
+})
+
+const mapDispatchToProps = dispatch => ({
+ tileActions: bindActionCreators({ ...tileActions }, dispatch),
+ pageActions: bindActionCreators({ ...pageActions }, dispatch),
+})
+
+export default connect(mapStateToProps, mapDispatchToProps)(TileList)
diff --git a/frontend/views/page/components/tile.new.js b/frontend/views/page/components/tile.new.js
index 3c97c31..c5ebeab 100644
--- a/frontend/views/page/components/tile.new.js
+++ b/frontend/views/page/components/tile.new.js
@@ -35,6 +35,7 @@ class TileNew extends Component {
graph={this.props.graph.show.res}
page={this.props.page.show.res}
initialData={null}
+ sortOrder={this.props.page.show.res.tiles.length}
onSubmit={this.handleSubmit.bind(this)}
/>
)
diff --git a/frontend/views/page/page.actions.js b/frontend/views/page/page.actions.js
index bd38683..dd94936 100644
--- a/frontend/views/page/page.actions.js
+++ b/frontend/views/page/page.actions.js
@@ -26,6 +26,18 @@ export const toggleEditTileForm = () => dispatch => {
dispatch({ type: types.page.toggle_edit_tile_form })
}
+export const showTileList = () => dispatch => {
+ dispatch({ type: types.page.show_tile_list })
+}
+
+export const hideTileList = () => dispatch => {
+ dispatch({ type: types.page.hide_tile_list })
+}
+
+export const toggleTileList = () => dispatch => {
+ dispatch({ type: types.page.toggle_tile_list })
+}
+
export const updatePageTile = tile => dispatch => {
dispatch({ type: types.page.update_page_tile, tile })
}
@@ -52,4 +64,14 @@ export const showGraphIfUnloaded = ({ graph_name }) => dispatch => (
.then(resolve)
.catch(reject)
})
-) \ No newline at end of file
+)
+
+export const setTileSortOrder = tiles => dispatch => {
+ dispatch({ type: types.page.set_tile_sort_order, tiles })
+ updateTileSortOrder(tiles)(dispatch)
+}
+
+export const updateTileSortOrder = tiles => dispatch => {
+
+}
+
diff --git a/frontend/views/page/page.container.js b/frontend/views/page/page.container.js
index 0c366ab..5da41d6 100644
--- a/frontend/views/page/page.container.js
+++ b/frontend/views/page/page.container.js
@@ -14,6 +14,7 @@ import * as pageActions from './page.actions'
import PageEdit from '../graph/components/page.edit'
import TileNew from './components/tile.new'
import TileEdit from './components/tile.edit'
+import TileList from './components/tile.list'
import PageHeader from './components/page.header'
import PageEditor from './components/page.editor'
@@ -72,6 +73,7 @@ class PageContainer extends Component {
{this.props.graph.editor.editingPage && <PageEdit />}
{this.props.page.editor.addingTile && <TileNew />}
{this.props.page.editor.editingTile && <TileEdit />}
+ {this.props.page.editor.tileList && <TileList />}
</div>
</div>
</div>
diff --git a/frontend/views/page/page.css b/frontend/views/page/page.css
index 67e35df..d828b82 100644
--- a/frontend/views/page/page.css
+++ b/frontend/views/page/page.css
@@ -27,3 +27,32 @@
.tile.top_right { top: 0; right: 0; }
.tile.center_right { top: 50%; right: 0; }
.tile.bottom_right { bottom: 0; right: 0; }
+
+.tileList .row {
+ justify-content: flex-start;
+ align-items: center;
+ min-height: 2.5rem;
+ margin-bottom: 0.5rem;
+ box-shadow: 4px 4px 6px rgba(0,0,0,0.5);
+}
+.tileList .row:nth-child(odd) {
+ background: rgba(0,0,0,0.2);
+}
+.tileList .row:nth-child(even) {
+ background: rgba(255,255,255,0.2);
+}
+.tileList span {
+ display: block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ width: 100%;
+ white-space: nowrap;
+ padding: 0.25rem;
+}
+.tileList .thumb {
+ width: 100%;
+ height: 2.5rem;
+ background-position: center center;
+ background-size: cover;
+ background-repeat: no-repeat;
+} \ No newline at end of file
diff --git a/frontend/views/page/page.reducer.js b/frontend/views/page/page.reducer.js
index 2b0d102..063e22d 100644
--- a/frontend/views/page/page.reducer.js
+++ b/frontend/views/page/page.reducer.js
@@ -7,6 +7,7 @@ const initialState = crudState('page', {
editor: {
addingTile: false,
editingTile: false,
+ tileList: false,
},
options: {
}
@@ -49,6 +50,7 @@ export default function pageReducer(state = initialState, action) {
}
}
+ // add tile UI
case types.page.show_add_tile_form:
return {
...state,
@@ -77,6 +79,7 @@ export default function pageReducer(state = initialState, action) {
}
}
+ // edit tile UI
case types.page.show_edit_tile_form:
return {
...state,
@@ -96,6 +99,49 @@ export default function pageReducer(state = initialState, action) {
}
}
+ // tile list UI
+ case types.page.show_tile_list:
+ return {
+ ...state,
+ editor: {
+ ...state.editor,
+ addingTile: false,
+ editingTile: false,
+ tileList: true,
+ }
+ }
+
+ case types.page.hide_tile_list:
+ return {
+ ...state,
+ editor: {
+ ...state.editor,
+ tileList: false,
+ }
+ }
+
+ case types.page.toggle_tile_list:
+ return {
+ ...state,
+ editor: {
+ ...state.editor,
+ tileList: !state.editor.tileList,
+ }
+ }
+
+ case types.page.set_tile_sort_order:
+ return {
+ ...state,
+ show: {
+ ...state.show,
+ res: {
+ ...state.res,
+ tiles: action.tiles,
+ }
+ }
+ }
+
+
default:
return state
}
diff --git a/package-lock.json b/package-lock.json
index d4bf57f..0956159 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3287,6 +3287,11 @@
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
"dev": true
},
+ "@types/sortablejs": {
+ "version": "1.10.4",
+ "resolved": "https://registry.npmjs.org/@types/sortablejs/-/sortablejs-1.10.4.tgz",
+ "integrity": "sha512-iXdJUEM09Hc0RacStDvkEi5BBdHuuwdys0L5/GtsRiEht69Df4hapA3FwFIeXn2STicqJjBAkowyD1OA3jGgwA=="
+ },
"@webassemblyjs/ast": {
"version": "1.8.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz",
@@ -3456,9 +3461,9 @@
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ=="
},
"acorn": {
- "version": "5.7.3",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz",
- "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==",
+ "version": "5.7.4",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz",
+ "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==",
"dev": true
},
"acorn-jsx": {
@@ -3467,11 +3472,6 @@
"integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==",
"dev": true
},
- "after": {
- "version": "0.8.2",
- "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
- "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
- },
"aggregate-error": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz",
@@ -3607,11 +3607,6 @@
"es-abstract": "^1.17.0-next.1"
}
},
- "arraybuffer.slice": {
- "version": "0.0.7",
- "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
- "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog=="
- },
"asap": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
@@ -3674,11 +3669,6 @@
"resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz",
"integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0="
},
- "async-limiter": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
- "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
- },
"atob": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
@@ -4378,11 +4368,6 @@
"resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz",
"integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ=="
},
- "backo2": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
- "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
- },
"bail": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz",
@@ -4459,24 +4444,11 @@
"integrity": "sha512-V6YHUbjLxN1ymqNLb1DPHoU1CpfdL7d2YTIp5W3U4hhoG4hhxNmsFDs66M9EXxBiSEke5Bt5dwdfMwwZF70iLA==",
"dev": true
},
- "base64-arraybuffer": {
- "version": "0.1.5",
- "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
- "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg="
- },
"base64-js": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz",
"integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw=="
},
- "better-assert": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
- "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=",
- "requires": {
- "callsite": "1.0.0"
- }
- },
"big.js": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz",
@@ -4496,11 +4468,6 @@
"file-uri-to-path": "1.0.0"
}
},
- "blob": {
- "version": "0.0.5",
- "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
- "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig=="
- },
"bluebird": {
"version": "3.7.2",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
@@ -4737,11 +4704,6 @@
}
}
},
- "callsite": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
- "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA="
- },
"callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -4858,6 +4820,11 @@
}
}
},
+ "classnames": {
+ "version": "2.2.6",
+ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz",
+ "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q=="
+ },
"clean-stack": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
@@ -4983,21 +4950,11 @@
}
}
},
- "component-bind": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
- "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
- },
"component-emitter": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
"integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
},
- "component-inherit": {
- "version": "0.0.3",
- "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
- "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
- },
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -5522,51 +5479,6 @@
"once": "^1.4.0"
}
},
- "engine.io-client": {
- "version": "3.4.0",
- "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.0.tgz",
- "integrity": "sha512-a4J5QO2k99CM2a0b12IznnyQndoEvtA4UAldhGzKqnHf42I3Qs2W5SPnDvatZRcMaNZs4IevVicBPayxYt6FwA==",
- "requires": {
- "component-emitter": "1.2.1",
- "component-inherit": "0.0.3",
- "debug": "~4.1.0",
- "engine.io-parser": "~2.2.0",
- "has-cors": "1.1.0",
- "indexof": "0.0.1",
- "parseqs": "0.0.5",
- "parseuri": "0.0.5",
- "ws": "~6.1.0",
- "xmlhttprequest-ssl": "~1.5.4",
- "yeast": "0.1.2"
- },
- "dependencies": {
- "debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
- }
- }
- },
- "engine.io-parser": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.0.tgz",
- "integrity": "sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w==",
- "requires": {
- "after": "0.8.2",
- "arraybuffer.slice": "~0.0.7",
- "base64-arraybuffer": "0.1.5",
- "blob": "0.0.5",
- "has-binary2": "~1.0.2"
- }
- },
"enhanced-resolve": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz",
@@ -6391,6 +6303,14 @@
"readable-stream": "^2.3.6"
}
},
+ "flux-standard-action": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/flux-standard-action/-/flux-standard-action-0.6.1.tgz",
+ "integrity": "sha1-bzQhG5SDTqHDzDD056+tPQ+/caI=",
+ "requires": {
+ "lodash.isplainobject": "^3.2.0"
+ }
+ },
"for-in": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
@@ -7060,26 +6980,6 @@
"ansi-regex": "^2.0.0"
}
},
- "has-binary2": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
- "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
- "requires": {
- "isarray": "2.0.1"
- },
- "dependencies": {
- "isarray": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
- "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
- }
- }
- },
- "has-cors": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
- "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
- },
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@@ -7317,11 +7217,6 @@
"resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz",
"integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc="
},
- "indexof": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
- "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
- },
"infer-owner": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
@@ -7880,11 +7775,63 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
},
+ "lodash._basefor": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash._basefor/-/lodash._basefor-3.0.3.tgz",
+ "integrity": "sha1-dVC06SGO8J+tJDQ7YSAhx5tMIMI="
+ },
"lodash.camelcase": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
"integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY="
},
+ "lodash.debounce": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+ "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168="
+ },
+ "lodash.isarguments": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
+ "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo="
+ },
+ "lodash.isarray": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz",
+ "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U="
+ },
+ "lodash.isplainobject": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-3.2.0.tgz",
+ "integrity": "sha1-moI4rhayAEMpYM1zRlEtASP79MU=",
+ "requires": {
+ "lodash._basefor": "^3.0.0",
+ "lodash.isarguments": "^3.0.0",
+ "lodash.keysin": "^3.0.0"
+ }
+ },
+ "lodash.keysin": {
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/lodash.keysin/-/lodash.keysin-3.0.8.tgz",
+ "integrity": "sha1-IsRJPrvtsUJ5YqVLRFssinZ/tH8=",
+ "requires": {
+ "lodash.isarguments": "^3.0.0",
+ "lodash.isarray": "^3.0.0"
+ }
+ },
+ "lodash.mapvalues": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz",
+ "integrity": "sha1-G6+lAF3p3W9PJmaMMMo3IwzJaJw="
+ },
+ "lodash.throttle": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.0.1.tgz",
+ "integrity": "sha1-syEWYu6dgvVpA9BzEmKqqoc6YzA=",
+ "requires": {
+ "lodash.debounce": "^4.0.0"
+ }
+ },
"loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -8351,11 +8298,6 @@
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
- "object-component": {
- "version": "0.0.3",
- "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz",
- "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE="
- },
"object-copy": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
@@ -8626,22 +8568,6 @@
"resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
"integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY="
},
- "parseqs": {
- "version": "0.0.5",
- "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
- "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=",
- "requires": {
- "better-assert": "~1.0.0"
- }
- },
- "parseuri": {
- "version": "0.0.5",
- "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz",
- "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=",
- "requires": {
- "better-assert": "~1.0.0"
- }
- },
"pascalcase": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
@@ -9125,6 +9051,17 @@
"tiny-warning": "^1.0.0"
}
},
+ "react-sortablejs": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/react-sortablejs/-/react-sortablejs-2.0.11.tgz",
+ "integrity": "sha512-Id44yygU6H/fNRp0uWkGZnKGuBF8GF/Ts6gKX5NfwmzceRjH0e0XHsmBuQ6WXhBIVnMM+XgkEPub851mdti0TA==",
+ "requires": {
+ "@types/sortablejs": "^1.10.0",
+ "classnames": "^2.2.6",
+ "sortablejs": "1.10.1",
+ "tiny-invariant": "^1.0.6"
+ }
+ },
"read-pkg": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
@@ -9493,6 +9430,29 @@
"symbol-observable": "^1.2.0"
}
},
+ "redux-debounce": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/redux-debounce/-/redux-debounce-1.0.1.tgz",
+ "integrity": "sha1-dqcJU/uz+J6f1Aw5BnSs7vQA0QU=",
+ "requires": {
+ "flux-standard-action": "^0.6.1",
+ "lodash.debounce": "^4.0.6",
+ "lodash.mapvalues": "^4.3.0"
+ }
+ },
+ "redux-debounced": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/redux-debounced/-/redux-debounced-0.5.0.tgz",
+ "integrity": "sha512-O2anhB0A6yQZH19uLETFtajcUQLcyiJcgC0hHSoFr5T3hWGtt0C5s6KNnb2RX51MwCh5VCl9ehZTv91F/rsZww=="
+ },
+ "redux-throttle": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/redux-throttle/-/redux-throttle-0.1.1.tgz",
+ "integrity": "sha1-QwcFc/AzHRKuFvrmVyrZ1/y3Ya4=",
+ "requires": {
+ "lodash.throttle": "4.0.1"
+ }
+ },
"redux-thunk": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz",
@@ -10009,66 +9969,10 @@
"kind-of": "^3.2.0"
}
},
- "socket.io-client": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz",
- "integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==",
- "requires": {
- "backo2": "1.0.2",
- "base64-arraybuffer": "0.1.5",
- "component-bind": "1.0.0",
- "component-emitter": "1.2.1",
- "debug": "~4.1.0",
- "engine.io-client": "~3.4.0",
- "has-binary2": "~1.0.2",
- "has-cors": "1.1.0",
- "indexof": "0.0.1",
- "object-component": "0.0.3",
- "parseqs": "0.0.5",
- "parseuri": "0.0.5",
- "socket.io-parser": "~3.3.0",
- "to-array": "0.1.4"
- },
- "dependencies": {
- "debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
- "requires": {
- "ms": "^2.1.1"
- }
- },
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
- }
- }
- },
- "socket.io-parser": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz",
- "integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==",
- "requires": {
- "component-emitter": "1.2.1",
- "debug": "~3.1.0",
- "isarray": "2.0.1"
- },
- "dependencies": {
- "debug": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
- "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
- "requires": {
- "ms": "2.0.0"
- }
- },
- "isarray": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
- "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
- }
- }
+ "sortablejs": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.10.1.tgz",
+ "integrity": "sha512-N6r7GrVmO8RW1rn0cTdvK3JR0BcqecAJ0PmYMCL3ZuqTH3pY+9QyqkmJSkkLyyDvd+AJnwaxTP22Ybr/83V9hQ=="
},
"source-list-map": {
"version": "2.0.0",
@@ -10791,11 +10695,6 @@
"os-tmpdir": "~1.0.2"
}
},
- "to-array": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
- "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
- },
"to-arraybuffer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
@@ -11527,14 +11426,6 @@
"mkdirp": "^0.5.1"
}
},
- "ws": {
- "version": "6.1.4",
- "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz",
- "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==",
- "requires": {
- "async-limiter": "~1.0.0"
- }
- },
"x-is-string": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz",
@@ -11560,11 +11451,6 @@
"integrity": "sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ==",
"optional": true
},
- "xmlhttprequest-ssl": {
- "version": "1.5.5",
- "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
- "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4="
- },
"xtend": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
@@ -11619,18 +11505,13 @@
}
},
"yargs-parser": {
- "version": "13.1.1",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz",
- "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==",
+ "version": "13.1.2",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
+ "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
}
- },
- "yeast": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
- "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
}
}
}
diff --git a/package.json b/package.json
index a2844ab..b3cfada 100644
--- a/package.json
+++ b/package.json
@@ -55,7 +55,11 @@
"react-redux": "^7.2.0",
"react-router": "^5.1.2",
"react-router-dom": "^5.1.2",
+ "react-sortablejs": "^2.0.11",
"redux": "^4.0.5",
+ "redux-debounce": "^1.0.1",
+ "redux-debounced": "^0.5.0",
+ "redux-throttle": "^0.1.1",
"redux-thunk": "^2.3.0",
"store2": "^2.10.0",
"style-loader": "^1.1.3",