Skip to content

Commit

Permalink
Merge pull request #13 from markerikson/practical-redux-part-10-final
Browse files Browse the repository at this point in the history
Practical Redux Part 10 - final
  • Loading branch information
markerikson committed Jul 25, 2017
2 parents 047aaf9 + 08af948 commit bc478ca
Show file tree
Hide file tree
Showing 40 changed files with 619 additions and 17 deletions.
Binary file removed offline-mirror/error-stack-parser-1.3.6.tgz
Binary file not shown.
Binary file added offline-mirror/material-colors-1.2.5.tgz
Binary file not shown.
Binary file added offline-mirror/react-color-2.13.1.tgz
Binary file not shown.
Binary file added offline-mirror/react-portal-3.1.0.tgz
Binary file not shown.
Binary file added offline-mirror/reactcss-1.2.2.tgz
Binary file not shown.
Binary file removed offline-mirror/redbox-react-1.4.3.tgz
Binary file not shown.
Binary file removed offline-mirror/sourcemapped-stacktrace-1.1.6.tgz
Binary file not shown.
Binary file removed offline-mirror/stackframe-0.3.1.tgz
Binary file not shown.
Binary file added offline-mirror/tinycolor2-1.4.1.tgz
Binary file not shown.
2 changes: 2 additions & 0 deletions package.json
Expand Up @@ -10,7 +10,9 @@
"lodash": "^4.17.4",
"prop-types": "^15.5.10",
"react": "^15.6.1",
"react-color": "^2.13.1",
"react-dom": "^15.6.1",
"react-portal": "^3.1.0",
"react-redux": "^5.0.5",
"react-toggle-display": "^2.2.0",
"redux": "^3.7.1",
Expand Down
4 changes: 4 additions & 0 deletions src/app/layout/App.js
Expand Up @@ -10,6 +10,8 @@ import Pilots from "features/pilots/Pilots";
import Mechs from "features/mechs/Mechs";
import UnitOrganization from "features/unitOrganization/UnitOrganization";
import Tools from "features/tools/Tools";
import ModalManager from "features/modals/ModalManager";
import ContextMenuManager from "features/contextMenus/ContextMenuManager";

import './App.css';

Expand All @@ -25,6 +27,8 @@ class App extends Component {

return (
<div className="App">
<ModalManager/>
<ContextMenuManager />
<div className="App-header">
<Header inverted as="h1">Project Mini-Mek</Header>
</div>
Expand Down
6 changes: 5 additions & 1 deletion src/app/reducers/rootReducer.js
Expand Up @@ -8,6 +8,8 @@ import tabReducer from "features/tabs/tabReducer";
import unitInfoReducer from "features/unitInfo/unitInfoReducer";
import pilotsReducer from "features/pilots/pilotsReducer";
import mechsReducer from "features/mechs/mechsReducer";
import modalsReducer from "features/modals/modalReducer";
import contextMenuReducer from "features/contextMenus/contextMenuReducer";

import entityCrudReducer from "features/entities/entityReducer";
import editingFeatureReducer from "features/editing/editingReducer";
Expand All @@ -20,6 +22,8 @@ const combinedReducer = combineReducers({
pilots : pilotsReducer,
mechs : mechsReducer,
tabs : tabReducer,
modals : modalsReducer,
contextMenu : contextMenuReducer
});


Expand All @@ -29,4 +33,4 @@ const rootReducer = reduceReducers(
editingFeatureReducer,
);

export default rootReducer;
export default rootReducer;
31 changes: 31 additions & 0 deletions src/common/components/AbsolutePosition.jsx
@@ -0,0 +1,31 @@
import React from "react";
import PropTypes from "prop-types";

const AbsolutePosition = (props) => {
const {children, nodeRef} = props;
const style = {
position: 'absolute',
top: props.top,
bottom : props.bottom,
left: props.left,
right : props.right,
width: props.width,
};


return (
<div style={style} className={props.className} ref={nodeRef}>
{children}
</div>
);
}

AbsolutePosition.propTypes = {
top: PropTypes.number,
bottom : PropTypes.number,
left: PropTypes.number,
width: PropTypes.number,
nodeRef : PropTypes.func,
};

export default AbsolutePosition;
24 changes: 24 additions & 0 deletions src/common/components/ColorPicker/ColorPickerButton.jsx
@@ -0,0 +1,24 @@
import React from "react";

import {Button} from "semantic-ui-react";

const ColorPickerButton = ({value, onClick, disabled=false}) => {
return (
<Button
type="button"
style={{padding: "4px", margin: 0}}
disabled={disabled}
onClick={onClick}
>
<div
style={{
width : 30,
height : 15,
backgroundColor : value
}}
/>
</Button>
)
}

export default ColorPickerButton;
64 changes: 64 additions & 0 deletions src/common/components/ColorPicker/ColorPickerDialog.jsx
@@ -0,0 +1,64 @@
import React, {Component} from "react";
import {connect} from "react-redux";
import {
Modal,
Button,
} from "semantic-ui-react";

import {SketchPicker} from "react-color";

import {closeModal} from "features/modals/modalActions";
import {colorSelected} from "./colorPickerActions";

const actions = {closeModal, colorSelected};

export class ColorPickerDialog extends Component {
constructor(props) {
super();
this.state = {
color : props.color
}
}

onSelectClicked = () => {
this.props.colorSelected(this.state.color, this.props.onColorPicked);

this.props.closeModal();
}

onSelectedColorChanged = (colorEvent) => {
this.setState({color : colorEvent.hex});
}

render() {
const {closeModal} = this.props;

return (
<Modal
closeIcon="close"
open={true}
onClose={closeModal}
size="small"
>
<Modal.Header>Select Color</Modal.Header>
<Modal.Content>
<SketchPicker
color={this.state.color}
onChangeComplete={this.onSelectedColorChanged}
/>
</Modal.Content>
<Modal.Actions>
<Button positive onClick={this.onSelectClicked}>Select</Button>
<Button secondary onClick={closeModal}>Cancel</Button>
</Modal.Actions>
</Modal>
)
}
}

ColorPickerDialog.defaultProps = {
color : "red"
};


export default connect(null, actions)(ColorPickerDialog);
27 changes: 27 additions & 0 deletions src/common/components/ColorPicker/colorPickerActions.js
@@ -0,0 +1,27 @@
import _ from "lodash";

import {
openModal
} from "features/modals/modalActions";

export function showColorPicker(initialColor, onColorPickedAction) {
// Define props that we want to "pass" to the ColorPicker dialog,
// including the body of the action that should be dispatched when
// the dialog is actually used to select a color.
const colorPickerProps = {
color : initialColor,
onColorPicked : onColorPickedAction
};
return openModal("ColorPickerDialog", colorPickerProps);
}

export function colorSelected(color, actionToDispatch) {
return (dispatch) => {
if(actionToDispatch) {
const newAction = _.cloneDeep(actionToDispatch);
newAction.payload.color = color;

dispatch(newAction);
}
}
}
4 changes: 3 additions & 1 deletion src/common/utils/clientUtils.js
Expand Up @@ -16,4 +16,6 @@ export function getValueFromEvent(e) {
}

return newValues;
}
}

export const noop = () => {};
1 change: 1 addition & 0 deletions src/data/sampleData.js
Expand Up @@ -2,6 +2,7 @@ const sampleData = {
unit : {
name : "Black Widow Company",
affiliation : "wd",
color : "black"
},
pilots : [
{
Expand Down
42 changes: 42 additions & 0 deletions src/features/contextMenus/ContextMenu.jsx
@@ -0,0 +1,42 @@
import React, {Component} from "react";
import {connect} from "react-redux";

import AbsolutePosition from "common/components/AbsolutePosition";

import {hideContextMenu} from "./contextMenuActions";

const actions = {hideContextMenu};

export class ContextMenu extends Component {

componentDidMount() {
document.addEventListener('click', this.handleClickOutside, true);
}

componentWillUnmount() {
document.removeEventListener('click', this.handleClickOutside, true);
}

handleClickOutside = (e) => {
if (!this.node || !this.node.contains(e.target) ) {
this.props.hideContextMenu();
}
}

render() {
const {location} = this.props;

return (
<AbsolutePosition
left={location.x + 2}
top={location.y}
className="contextMenu"
nodeRef={node => this.node = node}
>
{this.props.children}
</AbsolutePosition>
)
}
}

export default connect(null, actions)(ContextMenu);
51 changes: 51 additions & 0 deletions src/features/contextMenus/ContextMenuManager.jsx
@@ -0,0 +1,51 @@
import React, {Component} from "react";
import {connect} from "react-redux";
import Portal from 'react-portal';

import ContextMenu from "./ContextMenu";

import TestContextMenu from "./TestContextMenu";
import PilotsListItemMenu from "features/pilots/PilotsList/PilotsListItemMenu";

import {selectContextMenu} from "./contextMenuSelectors";

const menuTypes = {
TestContextMenu,
PilotsListItemMenu
};


export function contextMenuManagerMapState(state) {
return {
contextMenu : selectContextMenu(state)
};
}


export class ContextMenuManager extends Component {

render() {
const {contextMenu} = this.props;
const {show, location, type, menuArgs = {}} = contextMenu;

let menu = null;

if(show) {
let MenuComponent = menuTypes[type];

if(MenuComponent) {
menu = (
<Portal isOpened={true}>
<ContextMenu location={location}>
<MenuComponent {...menuArgs} />
</ContextMenu>
</Portal>
)
}
}

return menu;
}
}

export default connect(contextMenuManagerMapState)(ContextMenuManager);
19 changes: 19 additions & 0 deletions src/features/contextMenus/TestContextMenu.jsx
@@ -0,0 +1,19 @@
import React, { Component } from 'react'
import { Menu } from 'semantic-ui-react'

export default class TestContextMenu extends Component {
render() {
return (
<Menu vertical>
<Menu.Item>
<Menu.Header>Menu Header: {this.props.text} </Menu.Header>

<Menu.Menu>
<Menu.Item>First Menu Item</Menu.Item>
<Menu.Item>Second Menu Item</Menu.Item>
</Menu.Menu>
</Menu.Item>
</Menu>
)
}
}
22 changes: 22 additions & 0 deletions src/features/contextMenus/contextMenuActions.js
@@ -0,0 +1,22 @@
import {CONTEXT_MENU_HIDE, CONTEXT_MENU_SHOW} from "./contextMenuConstants";


export function showContextMenu(x, y, type, menuArgs) {
return {
type : CONTEXT_MENU_SHOW,
payload : {
location : {
x,
y
},
type,
menuArgs
}
}
}

export function hideContextMenu() {
return {
type : CONTEXT_MENU_HIDE
}
}
2 changes: 2 additions & 0 deletions src/features/contextMenus/contextMenuConstants.js
@@ -0,0 +1,2 @@
export const CONTEXT_MENU_SHOW = "CONTEXT_MENU_SHOW";
export const CONTEXT_MENU_HIDE = "CONTEXT_MENU_HIDE";

0 comments on commit bc478ca

Please sign in to comment.