feat(UI/Roles): add category sorting

This commit is contained in:
Katie Thornhill 2019-09-06 23:48:45 -04:00
parent b4ce19eb41
commit 2a91fa74f9
No known key found for this signature in database
GPG key ID: F76EDC6541A99644
9 changed files with 100 additions and 8 deletions

View file

@ -42,7 +42,7 @@
"not ie <= 11", "not ie <= 11",
"not op_mini all" "not op_mini all"
], ],
"proxy": "http://localhost:6768", "proxy": "http://localhost:6769",
"devDependencies": { "devDependencies": {
"@babel/plugin-proposal-decorators": "^7.4.0", "@babel/plugin-proposal-decorators": "^7.4.0",
"customize-cra": "^0.2.12", "customize-cra": "^0.2.12",

View file

@ -33,7 +33,6 @@ class Category extends Component {
</div> </div>
{ {
category.get('roles_map') category.get('roles_map')
.sortBy(r => r.get('position'))
.reverse() .reverse()
.map((r, k) => <Role key={k} role={r} categoryId={name} />) .map((r, k) => <Role key={k} role={r} categoryId={name} />)
.toArray() .toArray()

View file

@ -24,6 +24,28 @@ export default class CategoryEditor extends Component {
<div className="uk-form-controls"> <div className="uk-form-controls">
<input type="text" className="uk-input" placeholder='' value={category.get('name')} onChange={this.props.onEdit('name', Symbol.for('edit: text'))} /> <input type="text" className="uk-input" placeholder='' value={category.get('name')} onChange={this.props.onEdit('name', Symbol.for('edit: text'))} />
</div> </div>
<div className="role-editor__bumpers">
<div
onClick={this.props.onBump(-1)}
className={
`role-editor__bumpers-bump
${category.get('position') === 0 ? 'yeet' : ''}
`}
uk-tooltip="delay: 1s"
title="Move category up">
<i uk-icon="icon: chevron-up"></i>
</div>
<div
onClick={this.props.onBump(1)}
className={
`role-editor__bumpers-bump
${category.get('position') === this.props.arrMax - 1 ? 'yeet' : ''}
`}
uk-tooltip="delay: 1s"
title="Move category down">
<i uk-icon="icon: chevron-down"></i>
</div>
</div>
</div> </div>
<div style={{ marginTop: 10 }}> <div style={{ marginTop: 10 }}>
<div className="uk-form-controls"> <div className="uk-form-controls">

View file

@ -36,7 +36,16 @@
color: var(--c-white) color: var(--c-white)
.role-editor__bumpers
position: absolute
top: 10px
right: 10px
display: flex
&-bump
transition: color 0.15s ease-in-out
&:hover
color: var(--c-white)
.role-editor__category .role-editor__category
box-sizing: border-box box-sizing: border-box

View file

@ -9,7 +9,7 @@ export const constructView = id => async (dispatch, getState) => {
const server = getState().servers.get(id) const server = getState().servers.get(id)
let { viewMap, hasSafeRoles } = getViewMap(server) let { viewMap, hasSafeRoles } = getViewMap(server)
viewMap = viewMap.map(c => c.set('mode', Symbol.for('drop'))) viewMap = viewMap.map((c, idx) => c.set('mode', Symbol.for('drop')))
dispatch({ dispatch({
type: Symbol.for('re: setup'), type: Symbol.for('re: setup'),
@ -115,7 +115,8 @@ export const createCategory = (dispatch, getState) => {
let name = 'New Category' let name = 'New Category'
let idx = 1 let idx = 1
while (vm.find(c => c.get('name') === name) !== undefined) { const pred = c => c.get('name') === name
while (vm.find(pred) !== undefined) {
idx++ idx++
name = `New Category ${idx}` name = `New Category ${idx}`
} }
@ -131,16 +132,57 @@ export const createCategory = (dispatch, getState) => {
roles_map: Set([]), roles_map: Set([]),
hidden: true, hidden: true,
type: 'multi', type: 'multi',
position: idx,
mode: Symbol.for('edit') mode: Symbol.for('edit')
} }
}) })
} }
export const bumpCategory = (category, name) => move => async (dispatch, getState) => {
const { roleEditor } = getState()
const vm = roleEditor.get('viewMap')
const position = category.get('position')
const nextPos = position + move
const replaceThisOne = vm.findKey(category => category.get('position') === nextPos)
dispatch({
type: Symbol.for('re: edit category'),
data: {
id: name,
key: 'position',
value: nextPos
}
})
if (!!replaceThisOne) {
dispatch({
type: Symbol.for('re: edit category'),
data: {
id: replaceThisOne,
key: 'position',
value: position
}
})
}
}
export const saveServer = id => async (dispatch, getState) => { export const saveServer = id => async (dispatch, getState) => {
const viewMap = getState().roleEditor.get('viewMap') const viewMap = getState().roleEditor.get('viewMap')
.filterNot((_, k) => k === 'Uncategorized') .filterNot((_, k) => k === 'Uncategorized')
.map(v => v.delete('roles_map').delete('mode').delete('id')) .map(v => v.delete('roles_map').delete('mode').delete('id'))
viewMap.map((v, idx) => {
if (v.has('position')) {
return v
}
console.warn('category position wasnt set, so fake ones are being made', {cat: v.toJS(), idx, position: viewMap.count()+idx})
return v.set('position', viewMap.count()+idx)
})
await superagent.patch(`/api/server/${id}`).send({ categories: viewMap.toJS() }) await superagent.patch(`/api/server/${id}`).send({ categories: viewMap.toJS() })
dispatch({ type: Symbol.for('re: swap original state') }) dispatch({ type: Symbol.for('re: swap original state') })
} }

View file

@ -107,6 +107,8 @@ class RoleEditor extends Component {
dispatch(Actions.saveServer(server)) dispatch(Actions.saveServer(server))
} }
onBump = (category, name) => (move) => () => this.props.dispatch(Actions.bumpCategory(category, name)(move))
get hasChanged () { get hasChanged () {
return this.props.editor.get('originalSnapshot').hashCode() !== this.props.editor.get('viewMap').hashCode() return this.props.editor.get('originalSnapshot').hashCode() !== this.props.editor.get('viewMap').hashCode()
} }
@ -143,16 +145,19 @@ class RoleEditor extends Component {
{ {
vm vm
.filter((_, k) => k !== 'Uncategorized') .filter((_, k) => k !== 'Uncategorized')
.map((c, name) => <Category .sortBy(c => c.get('position'))
.map((c, name, arr) => <Category
key={name} key={name}
name={name} name={name}
category={c} category={c}
arrMax={arr.count()}
mode={c.get('mode')} mode={c.get('mode')}
onDrop={this.dropRole(c, name)} onDrop={this.dropRole(c, name)}
onEdit={this.editCategory(c, name)} onEdit={this.editCategory(c, name)}
onEditOpen={this.openEditor(c, name)} onEditOpen={this.openEditor(c, name)}
onSave={this.saveCategory(c, name)} onSave={this.saveCategory(c, name)}
onDelete={this.deleteCategory(c, name)} onDelete={this.deleteCategory(c, name)}
onBump={this.onBump(c, name)}
/>) />)
.toArray() .toArray()
} }

View file

@ -19,6 +19,7 @@ export const setup = id => async dispatch => {
export const getViewMap = server => { export const getViewMap = server => {
const roles = server.get('roles') const roles = server.get('roles')
const categories = server.get('categories') const categories = server.get('categories')
const categoriesIds = server.get('categories').keySeq()
const allRoles = server.get('roles').filter(v => v.get('safe')).map(r => r.get('id')).toSet() const allRoles = server.get('roles').filter(v => v.get('safe')).map(r => r.get('id')).toSet()
const accountedRoles = categories.map(c => c.get('roles')).toSet().flatten() const accountedRoles = categories.map(c => c.get('roles')).toSet().flatten()
@ -31,7 +32,17 @@ export const getViewMap = server => {
hidden: true, hidden: true,
type: 'multi', type: 'multi',
name: 'Uncategorized' name: 'Uncategorized'
})).map(c => { }))
.map(
(cat, idx) =>
cat.set(
'position',
cat.get('position', categoriesIds.findIndex(v => v === idx)
)
)
)
// .sortBy(cat => cat.get('position'))
.map(c => {
const roles = c.get('roles') const roles = c.get('roles')
// fill in roles_map // fill in roles_map
.map(r => .map(r =>

View file

@ -134,7 +134,7 @@ class RolePicker extends Component {
</div> </div>
<div className="role-picker__categories"> <div className="role-picker__categories">
{ {
vm.map((c, name) => <Category key={name} name={name} category={c} isSelected={this.isSelected} onChange={(roles) => dispatch(Actions.updateRoles(roles))} />).toArray() vm.sortBy(v => v.get('position')).map((c, name) => <Category key={name} name={name} category={c} isSelected={this.isSelected} onChange={(roles) => dispatch(Actions.updateRoles(roles))} />).toArray()
} }
</div> </div>
</section> </section>

View file

@ -63,3 +63,7 @@ h1,h2,h3,h4,h5,h6 {
.fade { .fade {
opacity: 0; opacity: 0;
} }
.yeet {
display: none;
}