Skip to content

Commit

Permalink
modals, removed conditional as distinct entity
Browse files Browse the repository at this point in the history
  • Loading branch information
William Nadeau committed May 15, 2018
1 parent c2eb91a commit 3211e45
Show file tree
Hide file tree
Showing 16 changed files with 346 additions and 83 deletions.
1 change: 1 addition & 0 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="modal-root"></div>
<div id="root"></div>
<!--
This HTML file is a template.
Expand Down
1 change: 1 addition & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ interface AppProps {
}

class App extends React.Component<AppProps, AppState> {
modal: HTMLElement;
render() {
return (
<div className="sheet-main">
Expand Down
34 changes: 34 additions & 0 deletions src/controls/EditForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as React from 'react';

const EditForm: React.StatelessComponent<{
header: string,
onSave?: () => void,
onCancel?: () => void
}> = ({ header, children, onSave, onCancel }) => {

return (
<div className="sheet-panel card p-4">
<h1>{header}</h1>
<form className="form-inline">
{children}
</form>
<div>
{onSave ? (
<button
onClick={event => { event.preventDefault(); onSave(); }}
className="btn btn-small btn-secondary d-inline float-right"
>Cancel</button>
) : null}
{onCancel ? (
<button
onClick={event => { event.preventDefault(); onCancel(); }}
className="btn btn-small btn-primary d-inline float-right"
>Save</button>
) : null}
</div>
</div>
);

};

export default EditForm;
44 changes: 44 additions & 0 deletions src/controls/Modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';

const modalRoot = document.getElementById('modal-root')!;

class Modal extends React.Component<{}> {
modalElement: HTMLElement;
modalRoot: HTMLElement;

constructor(props: {}) {
super(props);
// Create a div that we'll render the modal into. Because each
// Modal component has its own element, we can render multiple
// modal components into the modal container.
this.modalElement = document.createElement('div');
}

componentDidMount() {
// Append the element into the DOM on mount. We'll render
// into the modal container element (see the HTML tab).
modalRoot.appendChild(this.modalElement);
}

componentWillUnmount() {
// Remove the element from the DOM when we unmount
modalRoot.removeChild(this.modalElement);
}

render() {
// Use a portal to render the children into the element
return ReactDOM.createPortal(
// Any valid React child: JSX, strings, arrays, etc.
(
<div className="modal">
{this.props.children}
</div>
),
// A DOM element
this.modalElement,
);
}
}

export default Modal;
2 changes: 1 addition & 1 deletion src/controls/TextInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const TextInput: React.StatelessComponent<TextInputProps> =

return (
<div className={classes}>
<label className="mr-2" htmlFor={name}>{label}</label>
{label && <label className="mr-2" htmlFor={name}>{label}</label>}
<div className="field">
<input
type="text"
Expand Down
11 changes: 8 additions & 3 deletions src/sheet/SheetApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ export class MockSheetApi implements SheetApi {
name: 'Roland of Gilead',
statistics: [
{ name: 'fighter level', modifiers: [{ formula: '5' }] },
{
name: 'example base with conditional',
modifiers: [
{ formula: '5' },
{ source: 'example', condition: 'long is kranky', formula: '5' }
]
},
{ name: 'wizard level', modifiers: [{ formula: '3' }] },
{ name: 'favored class bonus', modifiers: [{ source: 'race', formula: '[fighter level]' }] },
{ name: 'total level', modifiers: [{ formula: '[fighter level] + [wizard level]' }] },
Expand Down Expand Up @@ -51,9 +58,7 @@ export class MockSheetApi implements SheetApi {
name: 'gun attack bonus', modifiers: [
{ source: 'weapon focus', formula: '1' },
{ formula: '[base attack bonus]' },
{ formula: '[dexterity modifier]' }
],
conditionals: [
{ formula: '[dexterity modifier]' },
{ source: 'point blank shot', condition: 'target is within 30ft.', formula: '1' },
{ condition: 'target is behind cover', formula: '-2' }
]
Expand Down
27 changes: 22 additions & 5 deletions src/sheet/SheetModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,27 @@ export function selectResources(sheet: Sheet): { resource: Resource, maximum: nu
return resources;
}

export function modifierIsBase(modifier: Modifier) {
return !isNaN(Number(modifier.formula)) && (modifier.condition ? modifier.condition === '' : true);
}

export function statisticIsBase(sheet: Sheet, statistic: Statistic) {
const onlyHasBaseModifiers = statistic.modifiers && statistic
.modifiers
.map(modifierIsBase)
.reduce((l, r) => l && r) || false;

return onlyHasBaseModifiers;
}

export function statisticHasConditionals(statistic: Statistic) {
return statistic.modifiers && statistic.modifiers.filter(isConditional).length > 0;
}

export function isConditional(modifier: Modifier) {
return modifier.condition ? modifier.condition !== '' : false;
}

export function calculateValue(sheet: Sheet, statistic: Statistic): number {
return statistic.name ? statisticValueCache.getFromCache(statistic.name!, key => {
if (!statistic.name || statistic.name === 'unknown') {
Expand Down Expand Up @@ -109,14 +130,10 @@ export type Statistic = {
name: string;
modifiers?: Modifier[];
resource?: Resource;
conditionals?: Conditional[];
};

export type Conditional = Modifier & {
condition?: string;
};

export type Modifier = {
condition?: string,
formula?: string,
source?: string
};
Expand Down
2 changes: 1 addition & 1 deletion src/sheetManage/ActionsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const ActionsPanel: React.StatelessComponent<{ className?: string, sheet: Sheet
const actions = selectActions(sheet);
const flattenedByCost = actions
.map(a => a.actionCost!.map(c => ({ cost: c, name: a.name, description: a.description })))
.reduce((l, r) => l.concat(r));
.reduce((l, r) => l.concat(r), []);
const costGroups = groupBy(flattenedByCost, a => a.cost);

return (
Expand Down
42 changes: 0 additions & 42 deletions src/sheetManage/ConditionalTable.tsx

This file was deleted.

36 changes: 34 additions & 2 deletions src/sheetManage/ManageSheetPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,60 @@ import * as React from 'react';
import RootState from '../core/RootState';
import { RouteComponentProps } from 'react-router-dom';
import SheetForm from './SheetForm';
import Modal from '../controls/Modal';

type ManageSheetPageProps = {
sheet: Sheet;
} & RouteComponentProps<{ id: string }>;

export class ManageSheetPage extends React.Component<ManageSheetPageProps> {
type ManageSheetPageState = {
modal?: JSX.Element;
};

export class ManageSheetPage extends React.Component<ManageSheetPageProps, ManageSheetPageState> {
constructor(props: ManageSheetPageProps) {
super(props);

this.onSave = this.onSave.bind(this);
this.openModal = this.openModal.bind(this);
this.closeModal = this.closeModal.bind(this);

this.state = {};
}

goBack() {
this.props.history.push('/sheets');
}

openModal(modalElement: JSX.Element) {
this.setState({
modal: (
<Modal>
{modalElement}
</Modal>
)
});
}

closeModal() {
this.setState({ modal: undefined });
}

onSave(event: React.FormEvent<HTMLInputElement>) {
alert('saved!');
}

render() {
return <SheetForm sheet={this.props.sheet} onSave={this.onSave} />;
return (
<div>
<SheetForm
sheet={this.props.sheet}
onSave={this.onSave}
showModal={this.openModal}
closeModal={this.closeModal} />
{this.state.modal}
</div>
);
}
}

Expand Down
15 changes: 7 additions & 8 deletions src/sheetManage/ModifierTable.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import * as React from 'react';
import Sheet, { Modifier, calculateFormula } from '../sheet/SheetModel';
import Sheet, { Modifier, calculateFormula, modifierIsBase } from '../sheet/SheetModel';
import combineClasses from '../controls/combineClasses';

const toRow = (key: string, sheet: Sheet, modifier: Modifier) => {
var calculatedValue = calculateFormula(sheet, modifier.formula).toString();
let calculatedValue = calculateFormula(sheet, modifier.formula).toString();
let isBase = modifierIsBase(modifier);

return (
<tr key={key}>
<td>{modifier.source}</td>
<td>{modifier.source ? modifier.source : isBase ? 'base' : ''}</td>
<td className="text-center">
{modifier.formula === calculatedValue
? modifier.formula
{isBase
? modifier.formula
: `"${modifier.formula}" => ${calculatedValue}`}</td>
<td>
<button className="fill-cell m-0 btn btn-light">...</button>
</td>
<td>{modifier.condition}</td>
</tr>
);
};
Expand Down
11 changes: 9 additions & 2 deletions src/sheetManage/SheetForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import TextInput from '../controls/TextInput';

type SheetFormProps = {
sheet: Sheet;
showModal: (modalElement: JSX.Element) => void;
closeModal: () => void;
onSave: (event: React.FormEvent<HTMLInputElement>) => void;
};

const SheetForm: React.StatelessComponent<SheetFormProps> = (props) => {
const sheet = props.sheet;
const { sheet, showModal, closeModal } = props;

return sheet ?
(
Expand All @@ -34,7 +36,12 @@ const SheetForm: React.StatelessComponent<SheetFormProps> = (props) => {
</div>

<div className="row">
<StatisticsPanel className="col-4" sheet={sheet} />
<StatisticsPanel
className="col-4"
sheet={sheet}
showModal={showModal}
closeModal={closeModal}
/>
<ResourcesPanel className="col-4" sheet={sheet} />
<ActionsPanel className="col-4" sheet={sheet} />
</div>
Expand Down
16 changes: 12 additions & 4 deletions src/sheetManage/SheetPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
import * as React from 'react';
import combineClasses from '../controls/combineClasses';

const SheetPanel: React.StatelessComponent<{ title: string, className?: string }> =
({ title, className, children }) => {
const SheetPanel: React.StatelessComponent<{ title: string, className?: string, onAdd?: () => void }> =
({ title, className, children, onAdd }) => {
const classes = combineClasses(className, 'sheet-panel card pt-4');

return (
<div className={classes}>
<h2>{title}</h2>
<div className={classes}><h2>
{title}
{onAdd
? (
<button
className="btn btn-outline-primary btn-small float-right"
onClick={event => { event.preventDefault(); onAdd(); }}
>+</button>
)
: null}</h2>
{children}
</div>
);
Expand Down
Loading

0 comments on commit 3211e45

Please sign in to comment.