add/edit list better design

This commit is contained in:
2018-06-01 20:18:18 +03:00
parent 6df56dc0b4
commit 7cabfffd35
9 changed files with 224 additions and 43 deletions

View File

@@ -8,6 +8,8 @@ export const REQUEST_LISTS = 'REQUEST_LISTS';
export const INVALIDATE_LISTS = 'INVALIDATE_LISTS';
export const VALIDATE_LISTS = 'VALIDATE_LISTS';
export const CHANGE_LIST = 'CHANGE_LIST';
export const START_CREATE_LIST = 'START_CREATE_LIST';
export const START_EDIT_LIST = 'START_EDIT_LIST';
function requestLists() {
return { type: REQUEST_LISTS };
@@ -26,6 +28,12 @@ export function changeList(list) {
return { type: CHANGE_LIST, list };
}
export function startCreateList() {
return { type: START_CREATE_LIST };
}
export function startEditList() {
return { type: START_EDIT_LIST };
}
function addListToState(list) {
return { type: ADD_LIST, list };
}
@@ -53,10 +61,12 @@ function removeListFromState(id) {
return { type: REMOVE_LIST, id };
}
export function removeList(id) {
export function removeList() {
return async (dispatch, getState) => {
let state = getState();
const { list } = state.lists;
dispatch(invalidateLists());
const response = await fetch(`${API_ROOT}/lists/${id}`, {
const response = await fetch(`${API_ROOT}/lists/${list}`, {
headers: {
Authorization: `Bearer ${getToken()}`,
'content-type': 'application/json',
@@ -65,12 +75,12 @@ export function removeList(id) {
});
const json = await response.json();
if (json.success) {
dispatch(removeListFromState(id));
const state = getState();
const list = state.lists.lists[Object.keys(state.lists.lists)[0]]
dispatch(removeListFromState(list));
state = getState();
const newList = state.lists.lists[Object.keys(state.lists.lists)[0]]
? state.lists.lists[Object.keys(state.lists.lists)[0]].id
: '';
dispatch(changeList(list));
dispatch(changeList(newList));
}
dispatch(validateLists());
};
@@ -80,10 +90,12 @@ function editListNameInState(id, name) {
return { type: EDIT_LIST_NAME, id, name };
}
export function editList(id, name) {
return async (dispatch) => {
export function editList(name) {
return async (dispatch, getState) => {
const state = getState();
const { list } = state.lists;
dispatch(invalidateLists());
const response = await fetch(`${API_ROOT}/lists/${id}`, {
const response = await fetch(`${API_ROOT}/lists/${list}`, {
body: JSON.stringify({ name }),
headers: {
Authorization: `Bearer ${getToken()}`,
@@ -93,7 +105,7 @@ export function editList(id, name) {
});
const json = await response.json();
if (json.success) {
dispatch(editListNameInState(id, name));
dispatch(editListNameInState(list, name));
}
dispatch(validateLists());
};

View File

@@ -7,6 +7,8 @@
#lists {
display: flex;
flex-direction: row;
flex-grow: 0;
max-width: 50%;
}
#listactions {
@@ -32,21 +34,7 @@
#filters {
margin-right: 0.75rem;
align-self: center;
}
#listselector {
margin-left: 0.2rem;
align-self: center;
background-color: #fbfbfb;
}
#listselector select {
color: black;
font-size: 1.5rem;
font-weight: 400;
outline: none;
border: none;
background-color: #fbfbfb;
flex-shrink: 0;
}
#inputs {

View File

@@ -4,21 +4,24 @@ import React from 'react';
import PropTypes from 'prop-types';
export default function ListActions({
addList, removeList, editList, list,
startCreateList,
removeList,
startEditList,
list,
}) {
const editRemoveButtons = list
? [
<button onClick={() => removeList(list)}>
<button onClick={() => removeList()}>
<FontAwesomeIcon icon={faTrash} />
</button>,
<button onClick={() => editList(list, prompt('List name?'))}>
<button onClick={() => startEditList()}>
<FontAwesomeIcon icon={faEdit} />
</button>,
]
: null;
return (
<div id="listactions">
<button onClick={() => addList(prompt('List name?'))}>
<button onClick={() => startCreateList()}>
<FontAwesomeIcon icon={faPlus} />
</button>
{editRemoveButtons}
@@ -31,8 +34,8 @@ ListActions.defaultProps = {
};
ListActions.propTypes = {
addList: PropTypes.func.isRequired,
startCreateList: PropTypes.func.isRequired,
removeList: PropTypes.func.isRequired,
editList: PropTypes.func.isRequired,
startEditList: PropTypes.func.isRequired,
list: PropTypes.string,
};

View File

@@ -4,11 +4,33 @@ import Selector from './Selector';
import ListActions from './ListActions';
export default function Lists({
list, lists, onChange, addList, removeList, editList,
list,
lists,
onChange,
addList,
creating,
editing,
removeList,
startCreateList,
startEditList,
editList,
listObjs,
}) {
const selectorProps = { list, lists, onChange };
const selectorProps = {
list,
lists,
creating,
editing,
onChange,
editList,
addList,
listObjs,
};
const actionsProps = {
addList, removeList, editList, list,
startCreateList,
removeList,
startEditList,
list,
};
return (
<div id="lists">
@@ -31,4 +53,9 @@ Lists.propTypes = {
addList: PropTypes.func.isRequired,
removeList: PropTypes.func.isRequired,
editList: PropTypes.func.isRequired,
startCreateList: PropTypes.func.isRequired,
startEditList: PropTypes.func.isRequired,
creating: PropTypes.bool.isRequired,
editing: PropTypes.bool.isRequired,
listObjs: PropTypes.any.isRequired,
};

View File

@@ -0,0 +1,50 @@
#listselector {
display: flex;
margin-left: 0.2rem;
overflow: hidden;
align-self: center;
background-color: #fbfbfb;
flex-grow: 1;
}
#listselector input {
padding: 0;
color: black;
font-size: 1.5rem;
font-weight: 400;
outline: none;
border: none;
background-color: #fbfbfb;
border-bottom: 1px solid #888888;
width: 80%;
}
#listselector button {
width: 20%;
font-size: 0.9rem;
color: #1b881b;
background: none;
border: none;
margin: 0.1rem 0.3rem;
padding: 0.3rem 0.7em;
}
#listselector select {
max-width: 100%;
color: black;
font-size: 1.5rem;
font-weight: 400;
outline: none;
border: none;
background-color: #fbfbfb;
}
#listselector select option {
max-width: 100%;
color: black;
font-size: 1.5rem;
font-weight: 400;
outline: none;
border: none;
background-color: #fbfbfb;
}

View File

@@ -1,16 +1,64 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faPlus, faCheck } from '@fortawesome/free-solid-svg-icons';
export default function Selector(props) {
const lists = props.lists.map(list => (
<option key={list.id} value={list.id}>
{list.name}
import './Selector.css';
export default function Selector({
lists,
list,
onChange,
editing,
creating,
addList,
editList,
listObjs,
}) {
const listElements = lists.map(elem => (
<option key={elem.id} value={elem.id}>
{elem.name}
</option>
));
if (creating) {
let input = null;
return (
<div id="listselector">
<input
ref={(node) => {
input = node;
}}
id="input"
type="text"
/>
<button onClick={() => addList(input.value)}>
<FontAwesomeIcon icon={faPlus} />
</button>
</div>
);
}
if (editing) {
let input = null;
return (
<div id="listselector">
<input
ref={(node) => {
input = node;
}}
defaultValue={listObjs.lists[list].name}
id="input"
type="text"
/>
<button onClick={() => editList(input.value)}>
<FontAwesomeIcon icon={faCheck} />
</button>
</div>
);
}
return (
<div id="listselector">
<select value={props.list} onChange={e => props.onChange(e.target.value)}>
{lists}
<select value={list} onChange={e => onChange(e.target.value)}>
{listElements}
</select>
</div>
);
@@ -25,5 +73,10 @@ Selector.propTypes = {
id: PropTypes.string.isRequired,
})).isRequired,
list: PropTypes.string,
editing: PropTypes.bool.isRequired,
creating: PropTypes.bool.isRequired,
onChange: PropTypes.func.isRequired,
editList: PropTypes.func.isRequired,
addList: PropTypes.func.isRequired,
listObjs: PropTypes.func.isRequired,
};

View File

@@ -1,11 +1,24 @@
import { connect } from 'react-redux';
import Lists from '../components/Lists';
import { changeList, removeList, addList, editList } from '../actions/lists';
import {
changeList,
removeList,
addList,
editList,
startCreateList,
startEditList,
} from '../actions/lists';
function mapStateToProps(state) {
const editing = state.lists.lists[state.lists.list]
? state.lists.lists[state.lists.list].editing
: false;
return {
lists: Object.values(state.lists.lists),
listObjs: state.lists,
list: state.lists.list,
editing,
creating: state.lists.creating,
dirty: state.lists.dirty,
};
}
@@ -16,6 +29,8 @@ function mapDispatchToProps(dispatch) {
addList: name => dispatch(addList(name)),
removeList: id => dispatch(removeList(id)),
editList: (id, name) => dispatch(editList(id, name)),
startCreateList: () => dispatch(startCreateList()),
startEditList: () => dispatch(startEditList()),
};
}

View File

@@ -10,7 +10,12 @@ import {
} from '../actions/todos';
export default function todos(
state = { dirty: true, fetching: false, todos: [] },
state = {
dirty: true,
fetching: false,
todos: [],
editing: false,
},
action,
) {
switch (action.type) {

View File

@@ -7,6 +7,8 @@ import {
ADD_LIST,
REMOVE_LIST,
EDIT_LIST_NAME,
START_CREATE_LIST,
START_EDIT_LIST,
} from '../actions/lists';
import {
ADD_TODO,
@@ -25,6 +27,7 @@ export default function lists(
dirty: true,
fetching: false,
lists: {},
creating: false,
list: '',
},
action,
@@ -39,9 +42,15 @@ export default function lists(
fetching: false,
lists: action.lists,
};
case START_CREATE_LIST:
return {
...state,
creating: true,
};
case ADD_LIST:
return {
...state,
creating: false,
lists: { ...state.lists, [action.list.id]: action.list },
};
case REMOVE_LIST: {
@@ -52,10 +61,29 @@ export default function lists(
lists: newLists,
};
}
case START_EDIT_LIST: {
return {
...state,
lists: {
...state.lists,
[state.list]: {
...state.lists[state.list],
editing: true,
},
},
};
}
case EDIT_LIST_NAME: {
return {
...state,
lists: { ...state.lists, [action.id]: { ...state.lists[action.id], name: action.name } },
lists: {
...state.lists,
[action.id]: {
...state.lists[action.id],
name: action.name,
editing: false,
},
},
};
}
case INVALIDATE_LISTS: