Cleanup, Lib Updates, etc.. (#12)
* Break apart bookmark component into a few low-level renderings * Improve drag-and-drop * Update dragKey proptype * Update libraries, correct linting and tests * Add skeleton-css dep; remove index.html, use webpack plugin * Upgrade to webpack 2 * Switch to Jest * Lib Updates * Unconnect the top-level History component, allow tthe createHistoryContainer function to wire in redux action bindings * Extend the redux-dag-history configuration class so that we can inject UI configuration * Add clean task, make some tasks parallel
This commit is contained in:
Родитель
3b4be2e16c
Коммит
90e5ff223e
|
@ -13,7 +13,14 @@
|
|||
// workaround for incomplete types
|
||||
"dot-notation": "off",
|
||||
|
||||
// due to TypeScript
|
||||
//
|
||||
// TypeScript
|
||||
//
|
||||
// ctors with field initializers
|
||||
"no-useless-constructor": "off",
|
||||
"no-empty-function": "off",
|
||||
"brace-style": "off",
|
||||
|
||||
"no-undef": "off",
|
||||
"import/no-unresolved": "off",
|
||||
"import/extensions": "off",
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
node_modules/
|
||||
lib/
|
||||
dist/
|
||||
storybook-static/
|
||||
coverage/
|
||||
.nyc_output/
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
.eslintrc
|
||||
.babelrc
|
||||
.npmignore
|
||||
node_modules/
|
||||
src/
|
||||
test/
|
||||
dist/
|
||||
variations/
|
||||
scripts/
|
||||
storybook-static/
|
||||
coverage/
|
||||
webpack.conf.js
|
||||
gulpfile.babel.js
|
||||
.eslintrc
|
||||
.babelrc
|
||||
.nycrc
|
||||
server.js
|
||||
.npmignore
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import store from './state/store';
|
||||
import 'skeleton-css/css/normalize.css';
|
||||
import 'skeleton-css/css/skeleton.css';
|
||||
import './app.scss';
|
||||
import store from './state/store';
|
||||
import Application from './components/Application';
|
||||
|
||||
ReactDOM.render(<Application store={store} />, document.getElementById('root'));
|
||||
const root = document.createElement('div');
|
||||
document.body.appendChild(root);
|
||||
|
||||
ReactDOM.render(<Application store={store} />, root);
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { save, load } from '../persister';
|
||||
import { IBookmark } from '../../src/interfaces';
|
||||
import '../../src/daghistory.scss';
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
<!doctype html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link href="https://cdn.jsdelivr.net/skeleton/2.0.4/css/skeleton.css" type="text/css" rel="stylesheet">
|
||||
<title>DAG History Demo</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root" class="app-root"></div>
|
||||
<script src="appbundle.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,6 +1,6 @@
|
|||
import * as redux from 'redux';
|
||||
import dagHistory from '@essex/redux-dag-history/lib/reducer';
|
||||
import Configuration from '@essex/redux-dag-history/lib/Configuration';
|
||||
import Configuration from '../../../src/state/Configuration';
|
||||
import app from './app';
|
||||
import history from '../../../src/state/reducers';
|
||||
|
||||
|
@ -46,11 +46,17 @@ function stateKeyGenerator(state) {
|
|||
}
|
||||
|
||||
const DAG_HISTORY_CONFIG = new Configuration({
|
||||
// Middleware Config
|
||||
debug: false,
|
||||
actionName: state => state.metadata.name,
|
||||
actionFilter: actionType => EXCLUDED_ACTION_NAMES.indexOf(actionType) === -1,
|
||||
stateEqualityPredicate,
|
||||
stateKeyGenerator,
|
||||
|
||||
// UI Config
|
||||
initialViewState: {
|
||||
branchContainerExpanded: false,
|
||||
},
|
||||
});
|
||||
|
||||
export default redux.combineReducers({
|
||||
|
|
152
package.json
152
package.json
|
@ -4,23 +4,24 @@
|
|||
"description": "A React Component for Dag-History Visualization",
|
||||
"main": "lib/index.js",
|
||||
"scripts": {
|
||||
"webpack:server": "webpack-dev-server --host 0.0.0.0 --hot --inline --history-api-fallback --publicPath ./",
|
||||
"clean": "rimraf lib/ storybook-static/",
|
||||
"serve:webpack": "webpack-dev-server --host 0.0.0.0 --hot --inline --history-api-fallback",
|
||||
"serve:storybook": "start-storybook -p 6006",
|
||||
"build:tsc": "tsc",
|
||||
"build:sass": "node-sass src/daghistory.scss --output lib",
|
||||
"build:assets": "cpx 'src/**/*.scss' lib",
|
||||
"build:storybook": "build-storybook",
|
||||
"build": "npm-run-all --parallel 'build:*'",
|
||||
"lint": "eslint '{src,test,stories}/**/*.ts'",
|
||||
"tsc": "tsc",
|
||||
"sass": "node-sass src/daghistory.scss --output lib",
|
||||
"copysass": "cpx 'src/**/*.scss' lib",
|
||||
"mocha:coverage": "nyc ./node_modules/.bin/_mocha",
|
||||
"mocha": "mocha",
|
||||
"test": "npm-run-all mocha:coverage lint --parallel tsc sass copysass",
|
||||
"watch:tsc": "npm run tsc -- -w",
|
||||
"watch:sass": "npm run sass -- --watch",
|
||||
"watch:mocha": "npm run mocha -- -w",
|
||||
"watch:copysass": "npm run copysass -- --watch",
|
||||
"watch": "npm-run-all --parallel 'watch:*'",
|
||||
"develop": "npm-run-all --parallel watch webpack:server storybook",
|
||||
"storybook": "start-storybook -p 6006",
|
||||
"build-storybook": "build-storybook",
|
||||
"start": "npm run develop"
|
||||
"unit_test": "jest",
|
||||
"unit_test:coverage": "jest --coverage",
|
||||
"watch:tsc": "tsc -w",
|
||||
"watch:unit_test": "jest --watch",
|
||||
"watch:sass": "npm run build:sass -- --watch",
|
||||
"watch:copysass": "npm run build:assets -- --watch",
|
||||
"verify": "npm-run-all --parallel lint unit_test:coverage",
|
||||
"test": "npm-run-all clean --parallel verify build",
|
||||
"start": "npm-run-all --parallel 'serve:*' 'watch:*'"
|
||||
},
|
||||
"author": "Chris Trevino <chris.trevino@atsid.com>",
|
||||
"license": "MIT",
|
||||
|
@ -31,111 +32,96 @@
|
|||
"lib/"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@kadira/storybook": "^2.33.0",
|
||||
"@kadira/storybook": "^2.35.3",
|
||||
"@types/bluebird": "^3.0.37",
|
||||
"@types/chai": "^3.4.34",
|
||||
"@types/enzyme": "^2.7.1",
|
||||
"@types/enzyme": "^2.7.2",
|
||||
"@types/jest": "^18.1.1",
|
||||
"@types/jsdom": "^2.0.29",
|
||||
"@types/mocha": "^2.2.36",
|
||||
"@types/node": "^6.0.58",
|
||||
"@types/react-dom": "^0.14.20",
|
||||
"@types/react-router": "^2.0.41",
|
||||
"@types/react-router-redux": "^4.0.36",
|
||||
"@types/redux-logger": "^2.6.32",
|
||||
"@types/react-dom": "^0.14.22",
|
||||
"@types/react-router": "^3.0.0",
|
||||
"@types/react-router-redux": "^4.0.39",
|
||||
"@types/redux-thunk": "^2.1.32",
|
||||
"@types/sinon": "^1.16.34",
|
||||
"autoprefixer": "^6.6.1",
|
||||
"babel-polyfill": "^6.16.0",
|
||||
"autoprefixer": "^6.7.2",
|
||||
"babel-polyfill": "^6.22.0",
|
||||
"bluebird": "^3.4.7",
|
||||
"chai": "^3.5.0",
|
||||
"cpx": "^1.3.1",
|
||||
"css-loader": "^0.26.1",
|
||||
"electron": "^1.4.10",
|
||||
"enzyme": "^2.7.0",
|
||||
"eslint": "^3.13.0",
|
||||
"eslint-config-airbnb": "^14.0.0",
|
||||
"electron": "^1.4.15",
|
||||
"enzyme": "^2.7.1",
|
||||
"eslint": "^3.15.0",
|
||||
"eslint-config-airbnb": "^14.1.0",
|
||||
"eslint-plugin-import": "^2.2.0",
|
||||
"eslint-plugin-jsx-a11y": "^3.0.2",
|
||||
"eslint-plugin-jsx-a11y": "^4.0.0",
|
||||
"eslint-plugin-react": "^6.9.0",
|
||||
"file-loader": "^0.9.0",
|
||||
"filesaver.js": "^0.2.0",
|
||||
"html-webpack-plugin": "^2.28.0",
|
||||
"imports-loader": "^0.7.0",
|
||||
"jsdom": "^9.5.0",
|
||||
"jest": "^18.1.0",
|
||||
"jsdom": "^9.10.0",
|
||||
"json-loader": "^0.5.4",
|
||||
"mocha": "^3.0.2",
|
||||
"mocha-junit-reporter": "^1.13.0",
|
||||
"mocha-multi": "^0.10.0",
|
||||
"mocha-osx-reporter": "^0.1.2",
|
||||
"node-sass": "^4.2.0",
|
||||
"npm-run-all": "^4.0.0",
|
||||
"nyc": "^10.0.0",
|
||||
"postcss-loader": "^1.0.0",
|
||||
"node-sass": "^4.5.0",
|
||||
"npm-run-all": "^4.0.1",
|
||||
"nyc": "^10.1.2",
|
||||
"postcss-loader": "^1.2.2",
|
||||
"react-addons-test-utils": "^15.4.2",
|
||||
"react-dnd-html5-backend": "^2.1.2",
|
||||
"react-dnd-html5-backend": "^2.2.3",
|
||||
"react-dom": "^15.4.2",
|
||||
"react-hot-loader": "^1.3.0",
|
||||
"react-router": "^3.0.0",
|
||||
"react-router": "^3.0.2",
|
||||
"react-router-redux": "^4.0.2",
|
||||
"redux-logger": "^2.6.1",
|
||||
"redux-thunk": "^2.0.1",
|
||||
"redux-thunk": "^2.2.0",
|
||||
"rimraf": "^2.5.4",
|
||||
"sass-lint": "^1.8.2",
|
||||
"sass-loader": "^4.1.1",
|
||||
"sass-loader": "^5.0.0",
|
||||
"sinon": "2.0.0-pre",
|
||||
"skeleton-css": "^2.0.4",
|
||||
"style-loader": "^0.13.1",
|
||||
"ts-loader": "^1.2.2",
|
||||
"ts-loader": "^2.0.0",
|
||||
"ts-node": "^2.0.0",
|
||||
"typescript": "^2.1.4",
|
||||
"typescript-eslint-parser": "^1.0.1",
|
||||
"wallaby-webpack": "^0.0.30",
|
||||
"webpack": "^1.13.1",
|
||||
"webpack-dev-middleware": "^1.6.1",
|
||||
"webpack-dev-server": "^1.14.1",
|
||||
"webpack-hot-middleware": "^2.12.0"
|
||||
"typescript": "^2.1.5",
|
||||
"typescript-eslint-parser": "^1.0.2",
|
||||
"wallaby-webpack": "^0.0.32",
|
||||
"webpack": "^2.2.1",
|
||||
"webpack-dev-server": "^2.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@essex/redux-dag-history": "^2.0.0",
|
||||
"@essex/redux-dag-history": "^2.0.1",
|
||||
"@types/classnames": "^0.0.32",
|
||||
"@types/react": "^0.0.0",
|
||||
"@types/react-dnd": "^2.0.31",
|
||||
"@types/react-redux": "^4.4.33",
|
||||
"@types/react-tabs": "^0.5.21",
|
||||
"@types/react": "^15.0.6",
|
||||
"@types/react-dnd": "^2.0.32",
|
||||
"@types/react-redux": "^4.4.36",
|
||||
"@types/react-tabs": "^0.5.22",
|
||||
"@types/redux": "^3.6.31",
|
||||
"@types/redux-actions": "^1.2.2",
|
||||
"classnames": "^2.2.5",
|
||||
"debug": "^2.6.0",
|
||||
"lodash": "^4.17.4",
|
||||
"react": "^15.4.2",
|
||||
"react-dnd": "^2.1.4",
|
||||
"react-dnd": "^2.2.3",
|
||||
"react-icons": "^2.2.3",
|
||||
"react-keydown": "^1.6.2",
|
||||
"react-redux": "^5.0.1",
|
||||
"react-redux": "^5.0.2",
|
||||
"react-simple-dropdown": "^1.1.4",
|
||||
"react-tabs": "^0.8.2",
|
||||
"redux": "^3.3.1",
|
||||
"redux-actions": "^1.2.0"
|
||||
"redux-actions": "^1.2.1"
|
||||
},
|
||||
"nyc": {
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx"
|
||||
"jest": {
|
||||
"moduleFileExtensions": [
|
||||
"ts",
|
||||
"tsx",
|
||||
"js"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules/",
|
||||
"lib/",
|
||||
"stories/"
|
||||
],
|
||||
"extension": [
|
||||
".ts",
|
||||
".tsx"
|
||||
],
|
||||
"require": [
|
||||
"ts-node/register"
|
||||
],
|
||||
"reporter": [
|
||||
"text-summary",
|
||||
"html"
|
||||
],
|
||||
"sourceMap": true,
|
||||
"instrument": true
|
||||
"moduleNameMapper": {
|
||||
"\\.(css|scss)$": "<rootDir>/scripts/stub.js"
|
||||
},
|
||||
"transform": {
|
||||
"^.+\\.(ts|tsx)$": "<rootDir>/scripts/preprocessor.js"
|
||||
},
|
||||
"testRegex": ".*/test/.*/.*\\.spec\\.(ts|tsx|js)$"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
// From: https://github.com/facebook/jest/blob/master/examples/typescript/preprocessor.js
|
||||
const tsc = require('typescript');
|
||||
const tsConfig = require('../tsconfig.json');
|
||||
module.exports = {
|
||||
process(src, path) {
|
||||
if (path.endsWith('.ts') || path.endsWith('.tsx')) {
|
||||
return tsc.transpile(
|
||||
src,
|
||||
tsConfig.compilerOptions,
|
||||
path,
|
||||
[]
|
||||
);
|
||||
}
|
||||
return src;
|
||||
},
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
module.exports = {};
|
|
@ -0,0 +1,73 @@
|
|||
import * as React from "react";
|
||||
import * as classnames from "classnames";
|
||||
import './Bookmark.scss';
|
||||
import DiscoveryTrail from '../DiscoveryTrail';
|
||||
|
||||
const { PropTypes } = React;
|
||||
|
||||
export interface IBookmarkProps {
|
||||
name: string;
|
||||
active?: boolean;
|
||||
numLeadInStates?: number;
|
||||
onClick?: Function;
|
||||
onClickEdit?: Function;
|
||||
annotation: string;
|
||||
onDiscoveryTrailIndexClicked?: (index: number) => void;
|
||||
}
|
||||
|
||||
const determineHighlight = (props) => {
|
||||
const { selectedDepth } = props;
|
||||
if (selectedDepth === undefined && props.active) {
|
||||
return Math.max(0, (props.shortestCommitPath || []).length - 1);
|
||||
}
|
||||
return selectedDepth;
|
||||
}
|
||||
|
||||
const Bookmark: React.StatelessComponent<IBookmarkProps> = (props) => {
|
||||
const {
|
||||
name,
|
||||
active,
|
||||
onClick,
|
||||
onClickEdit,
|
||||
onDiscoveryTrailIndexClicked,
|
||||
numLeadInStates,
|
||||
annotation,
|
||||
} = props;
|
||||
const highlight = determineHighlight(props);
|
||||
const isDiscoveryTrailVisible = active && numLeadInStates > 0;
|
||||
const discoveryTrail = isDiscoveryTrailVisible ? (
|
||||
<DiscoveryTrail
|
||||
fullWidth
|
||||
depth={this.commitPathLength - 1}
|
||||
highlight={highlight}
|
||||
leadIn={numLeadInStates}
|
||||
active={active}
|
||||
onIndexClicked={idx => onDiscoveryTrailIndexClicked(idx)}
|
||||
/>
|
||||
) : null;
|
||||
return (
|
||||
<div
|
||||
className={`history-bookmark ${active ? 'selected' : ''}`}
|
||||
>
|
||||
<div className="bookmark-details-container">
|
||||
<div className="bookmark-details" onClick={onClick ? () => onClick() : undefined}>
|
||||
<div
|
||||
className={classnames('bookmark-title', { active })}
|
||||
onClick={() => onClickEdit('title')}
|
||||
>
|
||||
{name}
|
||||
</div>
|
||||
<div
|
||||
className="bookmark-annotation"
|
||||
onClick={() => onClickEdit('annotation')}
|
||||
>
|
||||
{annotation}
|
||||
</div>
|
||||
</div>
|
||||
{discoveryTrail}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Bookmark;
|
|
@ -0,0 +1,44 @@
|
|||
import * as React from "react";
|
||||
import {default as Bookmark, IEditableBookmarkProps} from "./EditableBookmark";
|
||||
const flow = require('lodash/flow');
|
||||
|
||||
export interface IDragDropBookmarkProps extends IEditableBookmarkProps {
|
||||
// Injected by React DnD:
|
||||
isDragging?: boolean;
|
||||
connectDragSource?: Function;
|
||||
connectDropTarget?: Function;
|
||||
dragIndex?: number;
|
||||
hoverIndex?: number;
|
||||
dragKey?: string;
|
||||
dispatch: Function;
|
||||
stateId: string;
|
||||
}
|
||||
|
||||
export interface IDragDropBookmarkState {}
|
||||
|
||||
export default class DrapDropBookmark extends React.Component<IDragDropBookmarkProps, IDragDropBookmarkState> {
|
||||
private renderBookmark() {
|
||||
if (this.props.isDragging) {
|
||||
return (<div className="bookmark-dragged" />);
|
||||
} else {
|
||||
return (
|
||||
<div className="bookmark-dragdrop-wrapper">
|
||||
<Bookmark {...this.props} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {
|
||||
connectDragSource,
|
||||
connectDropTarget,
|
||||
} = this.props;
|
||||
return flow(
|
||||
connectDragSource,
|
||||
connectDropTarget,
|
||||
)(
|
||||
this.renderBookmark(),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -26,7 +26,6 @@ export interface IEditBookmarkProps {
|
|||
|
||||
// Injected by React DnD:
|
||||
isDragging?: boolean;
|
||||
connectDragSource?: Function;
|
||||
}
|
||||
export interface IEditBookmarkState {}
|
||||
|
||||
|
@ -119,14 +118,13 @@ export default class EditBookmark extends React.Component<IEditBookmarkProps, IE
|
|||
selectedDepth,
|
||||
numLeadInStates,
|
||||
onDiscoveryTrailIndexClicked,
|
||||
connectDragSource,
|
||||
} = this.props;
|
||||
|
||||
const leadInStatesValue = numLeadInStates !== undefined ? `${numLeadInStates}` : 'all';
|
||||
const isIntroSet = numLeadInStates !== undefined;
|
||||
|
||||
log('rendering commitPathLength=%s, selectedDepth=%s', this.props.commitPathLength, this.props.selectedDepth);
|
||||
return connectDragSource(
|
||||
return (
|
||||
<div
|
||||
className={`history-bookmark ${active ? 'selected' : ''}`}
|
||||
data-index={index}
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
import * as React from "react";
|
||||
import * as classnames from "classnames";
|
||||
import './Bookmark.scss';
|
||||
import EditBookmark from './EditBookmark';
|
||||
import {default as Bookmark, IBookmarkProps } from './Bookmark';
|
||||
import DiscoveryTrail from '../DiscoveryTrail';
|
||||
|
||||
const { PropTypes } = React;
|
||||
|
||||
export interface IEditableBookmarkProps extends IBookmarkProps {
|
||||
index: number;
|
||||
numLeadInStates?: number;
|
||||
onBookmarkChange?: Function;
|
||||
shortestCommitPath?: number[];
|
||||
selectedDepth?: number;
|
||||
onSelectBookmarkDepth?: Function;
|
||||
}
|
||||
|
||||
export interface IEditableBookmarkState {
|
||||
editMode: boolean;
|
||||
focusOn?: string;
|
||||
}
|
||||
|
||||
export default class EditableBookmark extends React.PureComponent<IEditableBookmarkProps, IEditableBookmarkState> {
|
||||
public static propTypes = {
|
||||
index: PropTypes.number,
|
||||
name: PropTypes.string.isRequired,
|
||||
annotation: PropTypes.string.isRequired,
|
||||
numLeadInStates: PropTypes.number,
|
||||
active: PropTypes.bool,
|
||||
onClick: PropTypes.func,
|
||||
onBookmarkChange: PropTypes.func,
|
||||
onDiscoveryTrailIndexClicked: PropTypes.func,
|
||||
shortestCommitPath: PropTypes.arrayOf(PropTypes.number),
|
||||
selectedDepth: PropTypes.number,
|
||||
onSelectBookmarkDepth: PropTypes.func,
|
||||
};
|
||||
|
||||
public static defaultProps = {
|
||||
shortestCommitPath: [],
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.state = { editMode: false };
|
||||
}
|
||||
|
||||
onClickEdit(focusOn) {
|
||||
this.setState({ editMode: true, focusOn });
|
||||
}
|
||||
|
||||
onDoneEditing() {
|
||||
this.setState({ editMode: false });
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
editMode,
|
||||
focusOn,
|
||||
} = this.state;
|
||||
|
||||
if (editMode) {
|
||||
return (
|
||||
<EditBookmark
|
||||
{...this.props}
|
||||
focusOn={focusOn}
|
||||
onDoneEditing={() => this.onDoneEditing()}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Bookmark
|
||||
{...this.props}
|
||||
onClickEdit={t => this.onClickEdit(t)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
import * as classnames from "classnames";
|
||||
import './Bookmark.scss';
|
||||
import EditBookmark from './EditBookmark';
|
||||
import DiscoveryTrail from '../DiscoveryTrail';
|
||||
import * as state from '../../state';
|
||||
import {default as DragDropBookmark, IDragDropBookmarkProps} from "./DragDropBookmark";
|
||||
import { connect } from "react-redux";
|
||||
import {
|
||||
bookmarkDragStart,
|
||||
bookmarkDragHover,
|
||||
|
@ -16,75 +16,18 @@ const {
|
|||
DragSource,
|
||||
DropTarget,
|
||||
} = require('react-dnd');
|
||||
const { connect } = require('react-redux');
|
||||
|
||||
const { PropTypes } = React;
|
||||
|
||||
export interface IBookmarkDragProps {
|
||||
// Injected by React DnD:
|
||||
isDragging?: boolean;
|
||||
connectDragSource?: Function;
|
||||
connectDropTarget?: Function;
|
||||
hoverIndex?: number,
|
||||
}
|
||||
|
||||
export interface IBookmarkProps extends IBookmarkDragProps {
|
||||
name: string;
|
||||
onClick?: Function;
|
||||
active?: boolean;
|
||||
onDiscoveryTrailIndexClicked?: (index: number) => void;
|
||||
onSelectBookmarkDepth?: Function;
|
||||
index: number;
|
||||
numLeadInStates?: number;
|
||||
annotation: string;
|
||||
onBookmarkChange?: Function;
|
||||
shortestCommitPath?: number[];
|
||||
selectedDepth?: number;
|
||||
}
|
||||
|
||||
export interface IBookmarkState {
|
||||
editMode: boolean;
|
||||
focusOn?: string;
|
||||
}
|
||||
|
||||
const fireHoverEvent = debounce((dispatch, index) => dispatch(bookmarkDragHover({ index })));
|
||||
|
||||
const dropTargetSpec = {
|
||||
drop(props, monitor, component) {
|
||||
const { index } = props;
|
||||
return { index };
|
||||
},
|
||||
hover(props, monitor, component) {
|
||||
if (!monitor.isOver()) {
|
||||
return;
|
||||
}
|
||||
const { dispatch, index } = props;
|
||||
const domNode = ReactDOM.findDOMNode(component);
|
||||
const { clientWidth: width, clientHeight: height } = domNode;
|
||||
|
||||
const rect = domNode.getBoundingClientRect();
|
||||
const clientY = monitor.getClientOffset().y;
|
||||
const midline = rect.top + ((rect.bottom - rect.top) / 2);
|
||||
|
||||
if (clientY < midline) {
|
||||
// insert hover into the hover slot, pushing the hovered item forward
|
||||
fireHoverEvent(dispatch, index);
|
||||
} else {
|
||||
// insert hover into next slot
|
||||
fireHoverEvent(dispatch, index + 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
const flow = require('lodash/flow');
|
||||
|
||||
const dragSource = {
|
||||
beginDrag(props) {
|
||||
const { index, dispatch } = props;
|
||||
dispatch(bookmarkDragStart({ index }));
|
||||
beginDrag(props: IDragDropBookmarkProps) {
|
||||
const { index, dispatch, stateId } = props;
|
||||
dispatch(bookmarkDragStart({ index, key: stateId }));
|
||||
return { index };
|
||||
},
|
||||
endDrag(props, monitor, component) {
|
||||
endDrag(props: IDragDropBookmarkProps, monitor, component) {
|
||||
const { dispatch } = props;
|
||||
const item = monitor.getItem();
|
||||
// TODO: use key instead for this?
|
||||
dispatch(bookmarkDragDrop({
|
||||
index: item.index,
|
||||
droppedOn: props.hoverIndex,
|
||||
|
@ -92,150 +35,53 @@ const dragSource = {
|
|||
},
|
||||
};
|
||||
|
||||
@connect()
|
||||
@DragSource("BOOKMARK", dragSource, (connect, monitor) => ({
|
||||
const fireHoverEvent = debounce((dispatch, index) => dispatch(bookmarkDragHover({ index })));
|
||||
|
||||
const dropTargetSpec = {
|
||||
drop(props: IDragDropBookmarkProps, monitor, component) {
|
||||
const { index } = props;
|
||||
return { index };
|
||||
},
|
||||
hover(props: IDragDropBookmarkProps, monitor, component) {
|
||||
if (!monitor.isOver()) {
|
||||
return;
|
||||
}
|
||||
const {
|
||||
dispatch,
|
||||
index,
|
||||
hoverIndex,
|
||||
dragIndex,
|
||||
dragKey,
|
||||
stateId,
|
||||
} = props;
|
||||
|
||||
if (dragKey === stateId) {
|
||||
return;
|
||||
}
|
||||
const domNode = ReactDOM.findDOMNode(component);
|
||||
const { clientWidth: width, clientHeight: height } = domNode;
|
||||
const rect = domNode.getBoundingClientRect();
|
||||
const clientY = monitor.getClientOffset().y;
|
||||
const midline = rect.top + ((rect.bottom - rect.top) / 2);
|
||||
const newHoverIndex = clientY < midline ? index : index + 1;
|
||||
|
||||
if (newHoverIndex !== hoverIndex) {
|
||||
fireHoverEvent(dispatch, newHoverIndex);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const connectDragSource = (connect, monitor) => ({
|
||||
connectDragSource: connect.dragSource(),
|
||||
isDragging: monitor.isDragging(),
|
||||
}))
|
||||
@DropTarget("BOOKMARK", dropTargetSpec, (connect, monitor) => {
|
||||
return { connectDropTarget: connect.dropTarget() };
|
||||
})
|
||||
export default class Bookmark extends React.PureComponent<IBookmarkProps, IBookmarkState> {
|
||||
public static propTypes = {
|
||||
index: PropTypes.number,
|
||||
name: PropTypes.string.isRequired,
|
||||
annotation: PropTypes.string.isRequired,
|
||||
numLeadInStates: PropTypes.number,
|
||||
active: PropTypes.bool,
|
||||
onClick: PropTypes.func,
|
||||
onBookmarkChange: PropTypes.func,
|
||||
onDiscoveryTrailIndexClicked: PropTypes.func,
|
||||
shortestCommitPath: PropTypes.arrayOf(PropTypes.number),
|
||||
selectedDepth: PropTypes.number,
|
||||
onSelectBookmarkDepth: PropTypes.func,
|
||||
});
|
||||
|
||||
// Injected by React DnD:
|
||||
isDragging: PropTypes.bool.isRequired,
|
||||
connectDragSource: PropTypes.func.isRequired,
|
||||
connectDropTarget: PropTypes.func,
|
||||
const connectDropTarget = (connect, monitor) => ({
|
||||
connectDropTarget: connect.dropTarget(),
|
||||
});
|
||||
|
||||
// D&D Related
|
||||
hoverIndex: PropTypes.number,
|
||||
};
|
||||
|
||||
public static defaultProps = {
|
||||
shortestCommitPath: [],
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.state = { editMode: false };
|
||||
}
|
||||
|
||||
onClickEdit(focusOn) {
|
||||
this.setState({ editMode: true, focusOn });
|
||||
}
|
||||
|
||||
onDoneEditing() {
|
||||
this.setState({ editMode: false });
|
||||
}
|
||||
|
||||
onDiscoveryTrailIndexClicked(index) {
|
||||
if (this.props.onDiscoveryTrailIndexClicked) {
|
||||
this.props.onDiscoveryTrailIndexClicked(index);
|
||||
}
|
||||
}
|
||||
|
||||
onBookmarkChangeDone(payload) {
|
||||
if (this.props.onBookmarkChange) {
|
||||
this.props.onBookmarkChange(payload);
|
||||
}
|
||||
}
|
||||
|
||||
private get commitPathLength() {
|
||||
const { shortestCommitPath } = this.props;
|
||||
return shortestCommitPath.length;
|
||||
}
|
||||
|
||||
private get highlight() {
|
||||
const { selectedDepth } = this.props;
|
||||
if (selectedDepth === undefined && this.props.active) {
|
||||
return this.commitPathLength - 1;
|
||||
}
|
||||
return selectedDepth;
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
name,
|
||||
onClick,
|
||||
active,
|
||||
index,
|
||||
isDragging,
|
||||
annotation,
|
||||
onBookmarkChange,
|
||||
shortestCommitPath,
|
||||
selectedDepth,
|
||||
numLeadInStates,
|
||||
connectDragSource,
|
||||
connectDropTarget,
|
||||
} = this.props;
|
||||
const {
|
||||
editMode,
|
||||
focusOn,
|
||||
} = this.state;
|
||||
const { highlight } = this;
|
||||
|
||||
const isDiscoveryTrailVisible = active && numLeadInStates > 0;
|
||||
const discoveryTrail = isDiscoveryTrailVisible ? (
|
||||
<DiscoveryTrail
|
||||
fullWidth
|
||||
depth={this.commitPathLength - 1}
|
||||
highlight={highlight}
|
||||
leadIn={numLeadInStates}
|
||||
active={active}
|
||||
onIndexClicked={idx => this.onDiscoveryTrailIndexClicked(idx)}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
if (isDragging) {
|
||||
return <div className="bookmark-dragged" />
|
||||
} else if (editMode) {
|
||||
return (
|
||||
<EditBookmark
|
||||
{...this.props}
|
||||
commitPathLength={this.commitPathLength - 1}
|
||||
selectedDepth={this.highlight}
|
||||
focusOn={focusOn}
|
||||
onDoneEditing={() => this.onDoneEditing()}
|
||||
onBookmarkChange={p => this.onBookmarkChangeDone(p)}
|
||||
onDiscoveryTrailIndexClicked={idx => this.onDiscoveryTrailIndexClicked(idx)}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return connectDropTarget(connectDragSource(
|
||||
<div
|
||||
className={`history-bookmark ${active ? 'selected' : ''}`}
|
||||
>
|
||||
<div className="bookmark-details-container">
|
||||
<div className="bookmark-details" onClick={onClick ? () => onClick() : undefined}>
|
||||
<div
|
||||
className={classnames('bookmark-title', { active })}
|
||||
onClick={() => this.onClickEdit('title')}
|
||||
>
|
||||
{name}
|
||||
</div>
|
||||
<div
|
||||
className="bookmark-annotation"
|
||||
onClick={() => this.onClickEdit('annotation')}
|
||||
>
|
||||
{annotation}
|
||||
</div>
|
||||
</div>
|
||||
{ discoveryTrail }
|
||||
</div>
|
||||
</div>
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
export default flow(
|
||||
DragSource("BOOKMARK", dragSource, connectDragSource),
|
||||
DropTarget("BOOKMARK", dropTargetSpec, connectDropTarget),
|
||||
connect(),
|
||||
)(DragDropBookmark);
|
||||
|
|
|
@ -12,10 +12,10 @@ export interface IBookmarkListProps {
|
|||
onBookmarkClick?: Function;
|
||||
onSelectState?: Function;
|
||||
onSelectBookmarkDepth?: Function;
|
||||
connectDropTarget?: Function;
|
||||
|
||||
dragIndex?: number;
|
||||
hoverIndex?: number;
|
||||
dragKey?: number;
|
||||
}
|
||||
|
||||
export default class BookmarkList extends React.PureComponent<IBookmarkListProps, {}> {
|
||||
|
@ -40,17 +40,20 @@ export default class BookmarkList extends React.PureComponent<IBookmarkListProps
|
|||
onBookmarkClick,
|
||||
onSelectState,
|
||||
onSelectBookmarkDepth,
|
||||
connectDropTarget,
|
||||
dragIndex,
|
||||
hoverIndex,
|
||||
dragKey,
|
||||
} = this.props;
|
||||
|
||||
let bookmarkViews = bookmarks.map((s, index) => (
|
||||
<Bookmark
|
||||
{...s}
|
||||
hoverIndex={hoverIndex}
|
||||
dragIndex={dragIndex}
|
||||
dragKey={dragKey}
|
||||
key={`bookmark::${s.stateId}`}
|
||||
index={index}
|
||||
stateId={s.stateId}
|
||||
onSelectBookmarkDepth={onSelectBookmarkDepth}
|
||||
onClick={() => this.onBookmarkClick(index, s.stateId)}
|
||||
onDiscoveryTrailIndexClicked={selectedIndex => {
|
||||
|
@ -61,7 +64,7 @@ export default class BookmarkList extends React.PureComponent<IBookmarkListProps
|
|||
/>
|
||||
));
|
||||
|
||||
if (hoverIndex >= 0 && hoverIndex !== dragIndex) {
|
||||
if (dragKey && hoverIndex >= 0 && hoverIndex !== dragIndex) {
|
||||
const dragged = bookmarkViews[dragIndex];
|
||||
const adjustedHoverIndex = hoverIndex < dragIndex ? hoverIndex : hoverIndex - 1;
|
||||
bookmarkViews.splice(dragIndex, 1);
|
||||
|
|
|
@ -13,6 +13,7 @@ const log = require('debug')('dag-history-component:components:StoryboardingView
|
|||
export interface IBookmarkListContainerStateProps {
|
||||
dragIndex?: number;
|
||||
hoverIndex?: number;
|
||||
dragKey?: number;
|
||||
}
|
||||
|
||||
export interface IBookmarkListContainerDispatchProps {
|
||||
|
@ -49,6 +50,7 @@ const BookmarkListContainer: React.StatelessComponent<IBookmarkListContainerProp
|
|||
selectedBookmarkDepth: selectedBookmarkDepthIndex,
|
||||
dragIndex,
|
||||
hoverIndex,
|
||||
dragKey,
|
||||
} = props;
|
||||
const historyGraph = new DagGraph(graph);
|
||||
const { currentStateId } = historyGraph;
|
||||
|
@ -82,6 +84,7 @@ const BookmarkListContainer: React.StatelessComponent<IBookmarkListContainerProp
|
|||
<BookmarkList
|
||||
dragIndex={dragIndex}
|
||||
hoverIndex={hoverIndex}
|
||||
dragKey={dragKey}
|
||||
bookmarks={bookmarkData}
|
||||
onBookmarkClick={(index, state) => onSelectBookmark(index, state)}
|
||||
onSelectState={onSelectState}
|
||||
|
@ -98,6 +101,7 @@ BookmarkListContainer.propTypes = {
|
|||
selectedBookmark: React.PropTypes.number,
|
||||
selectedBookmarkDepth: React.PropTypes.number,
|
||||
dragIndex: React.PropTypes.number,
|
||||
dragKey: React.PropTypes.number,
|
||||
hoverIndex: React.PropTypes.number,
|
||||
};
|
||||
|
||||
|
|
|
@ -79,6 +79,7 @@ StoryboardingView.propTypes = {
|
|||
selectedBookmark: PropTypes.number,
|
||||
selectedBookmarkDepth: PropTypes.number,
|
||||
dragIndex: PropTypes.number,
|
||||
dragKey: PropTypes.number,
|
||||
hoverIndex: PropTypes.number,
|
||||
|
||||
/* User Interaction Handlers - loaded by redux */
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import * as DagHistoryActions from '@essex/redux-dag-history/lib/ActionCreators';
|
||||
import * as Actions from '../../state/actions/creators';
|
||||
import { IDagHistory } from '@essex/redux-dag-history/lib/interfaces';
|
||||
import DagGraph from '@essex/redux-dag-history/lib/DagGraph';
|
||||
import HistoryTabs from './HistoryTabs';
|
||||
|
@ -24,25 +20,27 @@ const log = require('debug')('dag-history-component:components:History');
|
|||
|
||||
export interface IHistoryStateProps {}
|
||||
export interface IHistoryDispatchProps {
|
||||
onLoad: Function;
|
||||
onClear: Function;
|
||||
onSelectMainView: Function;
|
||||
onToggleBranchContainer: Function;
|
||||
onStartPlayback: Function;
|
||||
onStopPlayback: Function;
|
||||
onSelectBookmarkDepth: Function;
|
||||
onSelectState: Function;
|
||||
onLoad?: Function;
|
||||
onClear?: Function;
|
||||
onSelectMainView?: Function;
|
||||
onToggleBranchContainer?: Function;
|
||||
onStartPlayback?: Function;
|
||||
onStopPlayback?: Function;
|
||||
onSelectBookmarkDepth?: Function;
|
||||
onSelectState?: Function;
|
||||
}
|
||||
export interface IHistoryOwnProps extends IHistoryContainerSharedProps {
|
||||
}
|
||||
|
||||
export interface IHistoryProps extends IHistoryStateProps, IHistoryDispatchProps, IHistoryOwnProps {}
|
||||
|
||||
export class History extends React.Component<IHistoryProps, {}> {
|
||||
export default class History extends React.Component<IHistoryProps, {}> {
|
||||
public static propTypes = {
|
||||
bookmarks: PropTypes.array.isRequired,
|
||||
dragIndex: PropTypes.number,
|
||||
dragKey: PropTypes.number,
|
||||
hoverIndex: PropTypes.number,
|
||||
isPlayingBack: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* The Dag-History Object
|
||||
|
@ -221,19 +219,3 @@ export class History extends React.Component<IHistoryProps, {}> {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default connect<IHistoryStateProps, IHistoryDispatchProps, IHistoryOwnProps>(
|
||||
() => ({}),
|
||||
dispatch => bindActionCreators({
|
||||
onClear: DagHistoryActions.clear,
|
||||
onLoad: DagHistoryActions.load,
|
||||
onSelectMainView: Actions.selectMainView,
|
||||
onSelectState: DagHistoryActions.jumpToState,
|
||||
onToggleBranchContainer: Actions.toggleBranchContainer,
|
||||
onStartPlayback: Actions.startPlayback,
|
||||
onStopPlayback: Actions.stopPlayback,
|
||||
onSelectBookmarkDepth: Actions.selectBookmarkDepth,
|
||||
}, dispatch)
|
||||
)(History);
|
||||
|
||||
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import * as React from 'react';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { IBookmark } from '../interfaces';
|
||||
import HistoryComponent from './History';
|
||||
import {default as HistoryComponent, IHistoryOwnProps, IHistoryDispatchProps } from './History';
|
||||
import '../daghistory.scss';
|
||||
import * as DagHistoryActions from '@essex/redux-dag-history/lib/ActionCreators';
|
||||
import * as Actions from '../state/actions/creators';
|
||||
|
||||
const { PropTypes } = React;
|
||||
|
||||
|
@ -17,79 +20,46 @@ export interface IHistoryContainerStateProps {
|
|||
bookmarks?: IBookmark[];
|
||||
}
|
||||
|
||||
export interface IHistoryContainerDispatchProps {
|
||||
export interface IHistoryContainerDispatchProps extends IHistoryDispatchProps {
|
||||
}
|
||||
|
||||
export interface IHistoryContainerOwnProps extends IHistoryContainerStateProps {
|
||||
controlBar: any;
|
||||
export interface IHistoryContainerOwnProps {
|
||||
bookmarksEnabled?: boolean;
|
||||
|
||||
/**
|
||||
* ControlBar Configuration Properties
|
||||
*/
|
||||
controlBar?: {
|
||||
/**
|
||||
* A handler to save the history tree out. This is handled by clients.
|
||||
*/
|
||||
onSaveHistory: Function;
|
||||
|
||||
/**
|
||||
* A handler to retrieve the history tree. This is handled by clients
|
||||
*/
|
||||
onLoadHistory: Function;
|
||||
|
||||
/**
|
||||
* A function that emits a Promise<boolean> that confirms the clear-history operation.
|
||||
*/
|
||||
onConfirmClear: Function;
|
||||
};
|
||||
|
||||
getSourceFromState: Function;
|
||||
}
|
||||
|
||||
export interface IHistoryContainerProps extends IHistoryContainerStateProps, IHistoryContainerDispatchProps, IHistoryContainerOwnProps {
|
||||
export interface IHistoryContainerProps extends
|
||||
IHistoryContainerStateProps,
|
||||
IHistoryContainerDispatchProps,
|
||||
IHistoryContainerOwnProps {
|
||||
}
|
||||
|
||||
const HistoryContainer: React.StatelessComponent<IHistoryContainerProps> = (props) => {
|
||||
return (<HistoryComponent {...props} />);
|
||||
};
|
||||
|
||||
HistoryContainer.propTypes = {
|
||||
/**
|
||||
* ControlBar Configuration Properties
|
||||
*/
|
||||
controlBar: PropTypes.shape({
|
||||
/**
|
||||
* A handler to save the history tree out. This is handled by clients.
|
||||
*/
|
||||
onSaveHistory: PropTypes.func,
|
||||
|
||||
/**
|
||||
* A handler to retrieve the history tree. This is handled by clients
|
||||
*/
|
||||
onLoadHistory: PropTypes.func,
|
||||
|
||||
/**
|
||||
* A function that emits a Promise<boolean> that confirms the clear-history operation.
|
||||
*/
|
||||
onConfirmClear: PropTypes.func,
|
||||
}),
|
||||
|
||||
/**
|
||||
* Bookbark Configuration Properties
|
||||
*/
|
||||
bookmarksEnabled: PropTypes.bool,
|
||||
getSourceFromState: PropTypes.func.isRequired,
|
||||
|
||||
// Props injected from redux state
|
||||
history: PropTypes.object,
|
||||
highlightSuccessorsOf: PropTypes.number,
|
||||
mainView: PropTypes.string,
|
||||
historyType: PropTypes.string,
|
||||
dragIndex: PropTypes.number,
|
||||
hoverIndex: PropTypes.number,
|
||||
branchContainerExpanded: PropTypes.bool,
|
||||
selectedBookmark: PropTypes.number,
|
||||
selectedBookmarkDepth: PropTypes.number,
|
||||
isPlayingBack: PropTypes.bool,
|
||||
};
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
// State from the redux-dag-history middleware
|
||||
history: state.app,
|
||||
highlightSuccessorsOf: state.app.pinnedStateId,
|
||||
|
||||
// State from the dag-history-component
|
||||
bookmarks: state.component.bookmarks,
|
||||
mainView: state.component.views.mainView,
|
||||
historyType: state.component.views.historyType,
|
||||
dragIndex: state.component.dragDrop.sourceIndex,
|
||||
hoverIndex: state.component.dragDrop.hoverIndex,
|
||||
branchContainerExpanded: state.component.views.branchContainerExpanded,
|
||||
selectedBookmark: state.component.playback.bookmark,
|
||||
selectedBookmarkDepth: state.component.playback.depth,
|
||||
isPlayingBack: state.component.playback.isPlayingBack,
|
||||
};
|
||||
...HistoryComponent.propTypes,
|
||||
};
|
||||
|
||||
export default function createHistoryContainer(getMiddlewareState: Function, getComponentState: Function) {
|
||||
|
@ -106,6 +76,7 @@ export default function createHistoryContainer(getMiddlewareState: Function, get
|
|||
mainView: component.views.mainView,
|
||||
historyType: component.views.historyType,
|
||||
dragIndex: component.dragDrop.sourceIndex,
|
||||
dragKey: state.component.dragDrop.sourceKey,
|
||||
hoverIndex: component.dragDrop.hoverIndex,
|
||||
branchContainerExpanded: component.views.branchContainerExpanded,
|
||||
selectedBookmark: component.playback.bookmark,
|
||||
|
@ -113,5 +84,18 @@ export default function createHistoryContainer(getMiddlewareState: Function, get
|
|||
isPlayingBack: component.playback.isPlayingBack,
|
||||
};
|
||||
};
|
||||
return connect<IHistoryContainerStateProps, IHistoryContainerDispatchProps, IHistoryContainerOwnProps>(mapStateToProps)(HistoryContainer);
|
||||
const mapDispatchToProps = (dispatch) => bindActionCreators({
|
||||
onClear: DagHistoryActions.clear,
|
||||
onLoad: DagHistoryActions.load,
|
||||
onSelectMainView: Actions.selectMainView,
|
||||
onSelectState: DagHistoryActions.jumpToState,
|
||||
onToggleBranchContainer: Actions.toggleBranchContainer,
|
||||
onStartPlayback: Actions.startPlayback,
|
||||
onStopPlayback: Actions.stopPlayback,
|
||||
onSelectBookmarkDepth: Actions.selectBookmarkDepth,
|
||||
}, dispatch);
|
||||
return connect<IHistoryContainerStateProps, IHistoryContainerDispatchProps, IHistoryContainerOwnProps>(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
)(HistoryContainer);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
import Configuration from '@essex/redux-dag-history/lib/configuration';
|
||||
import { IComponentConfiguration } from './interfaces'; // eslint-disable-line no-unused-vars
|
||||
|
||||
export default class ComponentConfiguration<T>
|
||||
extends Configuration<T>
|
||||
implements IComponentConfiguration<T> {
|
||||
constructor(rawConfig: IComponentConfiguration<T>) {
|
||||
super(rawConfig);
|
||||
}
|
||||
|
||||
private get config() {
|
||||
return this.rawConfig as IComponentConfiguration<T>;
|
||||
}
|
||||
|
||||
public get initialViewState() {
|
||||
return this.config.initialViewState || {};
|
||||
}
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
import { StateId } from '@essex/redux-dag-history/lib/interfaces';
|
||||
import * as DagHistoryActions from '@essex/redux-dag-history/lib/ActionCreators';
|
||||
import { createAction } from 'redux-actions';
|
||||
import * as ReduxActions from 'redux-actions';
|
||||
import * as Types from './types';
|
||||
|
||||
const { createAction } = ReduxActions;
|
||||
|
||||
// Simple Action Creators
|
||||
const doSelectBookmarkDepth = createAction<BookmarkDepthSelection>(Types.SELECT_BOOKMARK_DEPTH);
|
||||
|
@ -66,6 +67,7 @@ export interface AddBookmarkPayload {
|
|||
|
||||
export interface BookmarkDragStartPayload {
|
||||
index: number;
|
||||
key: string;
|
||||
}
|
||||
|
||||
export interface BookmarkDragHoverPayload {
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
import {
|
||||
IConfiguration, // eslint-disable-line no-unused-vars
|
||||
} from '@essex/redux-dag-history/lib/interfaces';
|
||||
|
||||
export interface IComponentConfiguration<T> extends IConfiguration<T> {
|
||||
initialViewState?: {
|
||||
mainView?: string;
|
||||
historyType?: string;
|
||||
branchContainerExpanded?: boolean;
|
||||
};
|
||||
}
|
|
@ -10,6 +10,7 @@ import {
|
|||
|
||||
export const INITIAL_STATE = {
|
||||
sourceIndex: undefined,
|
||||
sourceKey: undefined,
|
||||
hoverIndex: undefined,
|
||||
};
|
||||
|
||||
|
@ -20,31 +21,20 @@ export default function makeReducer(config: IConfiguration<any>) {
|
|||
result = {
|
||||
...state,
|
||||
sourceIndex: action.payload.index,
|
||||
sourceKey: action.payload.key,
|
||||
};
|
||||
} else if (action.type === BOOKMARK_DRAG_HOVER) {
|
||||
const hoverIndex = action.payload.index;
|
||||
result = {
|
||||
...state,
|
||||
hoverIndex: action.payload.index,
|
||||
hoverIndex,
|
||||
};
|
||||
} else if (action.type === BOOKMARK_DRAG_DROP) {
|
||||
result = {
|
||||
...state,
|
||||
sourceIndex: undefined,
|
||||
hoverIndex: undefined,
|
||||
};
|
||||
result = INITIAL_STATE;
|
||||
} else if (action.type === BOOKMARK_DRAG_CANCEL) {
|
||||
result = {
|
||||
...state,
|
||||
sourceIndex: undefined,
|
||||
hoverIndex: undefined,
|
||||
};
|
||||
result = INITIAL_STATE;
|
||||
} else if (action.type.indexOf('DAG_HISTORY_') !== 0 && config.actionFilter(action.type)) {
|
||||
// Insertable actions clear the pinned state
|
||||
result = {
|
||||
...state,
|
||||
sourceIndex: undefined,
|
||||
hoverIndex: undefined,
|
||||
};
|
||||
result = INITIAL_STATE;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
import {
|
||||
IConfiguration, // eslint-disable-line no-unused-vars
|
||||
} from '@essex/redux-dag-history/lib/interfaces';
|
||||
import * as redux from 'redux';
|
||||
import dragDrop from './dragDrop';
|
||||
import views from './views';
|
||||
import playback from './playback';
|
||||
import bookmarks from './bookmarks';
|
||||
import { IComponentConfiguration } from '../interfaces'; // eslint-disable-line no-unused-vars
|
||||
|
||||
export default function createReducer(config: IConfiguration<any>) {
|
||||
export default function createReducer<T>(config: IComponentConfiguration<T>) {
|
||||
return redux.combineReducers({
|
||||
dragDrop: dragDrop(config),
|
||||
views: views(config),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import {
|
||||
IConfiguration, // eslint-disable-line no-unused-vars
|
||||
} from '@essex/redux-dag-history/lib/interfaces';
|
||||
IComponentConfiguration, // eslint-disable-line no-unused-vars
|
||||
} from '../interfaces';
|
||||
import {
|
||||
SELECT_MAIN_VIEW,
|
||||
SELECT_HISTORY_TYPE,
|
||||
|
@ -13,8 +13,12 @@ export const INITIAL_STATE = {
|
|||
branchContainerExpanded: true,
|
||||
};
|
||||
|
||||
export default function (config: IConfiguration<any>) {
|
||||
return function reduce(state = INITIAL_STATE, action) {
|
||||
export default function makeReducer<T>(config: IComponentConfiguration<T>) {
|
||||
const initialState = {
|
||||
...INITIAL_STATE,
|
||||
...config.initialViewState,
|
||||
};
|
||||
return function reduce(state = initialState, action) {
|
||||
let result = state;
|
||||
if (action.type === SELECT_MAIN_VIEW) {
|
||||
result = {
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
const log = require('debug')('dag-history-component:SpanCalculator');
|
||||
|
||||
export class Span {
|
||||
public start: number;
|
||||
public end: number;
|
||||
public type: string;
|
||||
|
||||
constructor(start, end, type) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.type = type;
|
||||
constructor(
|
||||
public start: number,
|
||||
public end: number,
|
||||
public type: string, // eslint-disable-line no-unused-vars
|
||||
) {
|
||||
}
|
||||
|
||||
toString() {
|
||||
|
|
|
@ -1,24 +1,20 @@
|
|||
import * as React from 'react';
|
||||
import { storiesOf, action } from '@kadira/storybook';
|
||||
import Bookmark from '../../../src/components/Bookmark';
|
||||
import Bookmark from '../../../src/components/Bookmark/Bookmark';
|
||||
|
||||
storiesOf('Bookmark', module)
|
||||
.add('Inactive', () => (
|
||||
<Bookmark
|
||||
name="Some Bookmark Name"
|
||||
annotation="Some Bookmark Annotation Text. Derp Depr Derp Derp"
|
||||
index={1}
|
||||
onClick={action('click')}
|
||||
onBookmarkChange={action('bookmarkChange')}
|
||||
/>
|
||||
))
|
||||
.add('Active', () => (
|
||||
<Bookmark
|
||||
name="Some Bookmark Name"
|
||||
annotation="Some Bookmark Annotation Text. Derp Depr Derp Derp"
|
||||
index={1}
|
||||
onClick={action('click')}
|
||||
onBookmarkChange={action('bookmarkChange')}
|
||||
active
|
||||
/>
|
||||
));
|
||||
|
|
|
@ -2,5 +2,7 @@ import { expect } from 'chai';
|
|||
import * as History from '../src';
|
||||
|
||||
describe('The Top-Level Entry Point', () => {
|
||||
expect(History).to.be.ok;
|
||||
it('exists', () => {
|
||||
expect(History).to.be.ok;
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
--reporter mocha-multi
|
||||
--reporter-options spec=-,mocha-junit-reporter=-,mocha-osx-reporter=-
|
||||
--require ts-node/register
|
||||
--require test/setup
|
||||
test/**/*.spec.*
|
|
@ -1,5 +1,8 @@
|
|||
import { expect } from 'chai';
|
||||
import makeReducer from '../../../src/state/reducers/dragDrop';
|
||||
import {
|
||||
default as makeReducer,
|
||||
INITIAL_STATE,
|
||||
} from '../../../src/state/reducers/dragDrop';
|
||||
|
||||
import {
|
||||
bookmarkDragStart,
|
||||
|
@ -14,20 +17,18 @@ const defaultConfig = {
|
|||
describe('The DragDrop reducer', () => {
|
||||
it('will emit an initial dragDrop state', () => {
|
||||
const state = makeReducer(defaultConfig)(undefined, { type: 'derp' });
|
||||
expect(state).to.deep.equal({
|
||||
sourceIndex: undefined,
|
||||
hoverIndex: undefined,
|
||||
});
|
||||
expect(state).to.deep.equal(INITIAL_STATE);
|
||||
});
|
||||
|
||||
it('can handle a dragStart event', () => {
|
||||
let state;
|
||||
const reduce = makeReducer(defaultConfig);
|
||||
state = reduce(state, { type: 'derp' });
|
||||
state = reduce(state, bookmarkDragStart({ index: 3 }));
|
||||
state = reduce(state, bookmarkDragStart({ index: 3, key: 2 }));
|
||||
expect(state).to.deep.equal({
|
||||
...INITIAL_STATE,
|
||||
sourceIndex: 3,
|
||||
hoverIndex: undefined,
|
||||
sourceKey: 2,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -38,6 +39,7 @@ describe('The DragDrop reducer', () => {
|
|||
state = reduce(state, bookmarkDragStart({ index: 3 }));
|
||||
state = reduce(state, bookmarkDragHover({ index: 4 }));
|
||||
expect(state).to.deep.equal({
|
||||
...INITIAL_STATE,
|
||||
sourceIndex: 3,
|
||||
hoverIndex: 4,
|
||||
});
|
||||
|
@ -50,11 +52,7 @@ describe('The DragDrop reducer', () => {
|
|||
state = reduce(state, bookmarkDragStart({ index: 3 }));
|
||||
state = reduce(state, bookmarkDragHover({ index: 4 }));
|
||||
state = reduce(state, { type: types.BOOKMARK_DRAG_DROP });
|
||||
|
||||
expect(state).to.deep.equal({
|
||||
sourceIndex: undefined,
|
||||
hoverIndex: undefined,
|
||||
});
|
||||
expect(state).to.deep.equal(INITIAL_STATE);
|
||||
});
|
||||
|
||||
it('can handle a dragCancel event', () => {
|
||||
|
@ -64,10 +62,6 @@ describe('The DragDrop reducer', () => {
|
|||
state = reduce(state, bookmarkDragStart({ index: 3 }));
|
||||
state = reduce(state, bookmarkDragHover({ index: 4 }));
|
||||
state = reduce(state, { type: types.BOOKMARK_DRAG_CANCEL });
|
||||
|
||||
expect(state).to.deep.equal({
|
||||
sourceIndex: undefined,
|
||||
hoverIndex: undefined,
|
||||
});
|
||||
expect(state).to.deep.equal(INITIAL_STATE);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
/*
|
||||
import { expect } from 'chai';
|
||||
import {
|
||||
INITIAL_STATE,
|
||||
default as reduceFactory, // eslint-disable-line
|
||||
} from '../src/reducer';
|
||||
|
||||
const reducer = () => reduceFactory({
|
||||
actionFilter: () => false,
|
||||
});
|
||||
|
||||
describe('The Dag-History Component Reducer', () => {
|
||||
it('is a function that returns a function', () => {
|
||||
expect(reduceFactory).to.be.a('function');
|
||||
expect(reduceFactory({} as any)).to.be.a('function');
|
||||
});
|
||||
|
||||
it('can generate an initial state', () => {
|
||||
const state = reducer()(undefined, { type: 'DERP' });
|
||||
expect(state).to.deep.equal(INITIAL_STATE);
|
||||
});
|
||||
|
||||
it('can respond to a SELECT_MAIN_VIEW action', () => {
|
||||
const state = reducer()(undefined, { type: 'SELECT_MAIN_VIEW', payload: 'abc123' });
|
||||
expect(state).to.deep.equal({
|
||||
...INITIAL_STATE,
|
||||
mainView: 'abc123',
|
||||
});
|
||||
});
|
||||
|
||||
it('can respond to a SELECT_HISTORY_TYPE action', () => {
|
||||
const state = reducer()(undefined, { type: 'SELECT_HISTORY_TYPE', payload: 'derp' });
|
||||
expect(state).to.deep.equal({
|
||||
...INITIAL_STATE,
|
||||
mainView: 'history',
|
||||
historyType: 'derp',
|
||||
});
|
||||
});
|
||||
|
||||
it('can respond to a TOGGLE_BRANCH_CONTAINER action', () => {
|
||||
let state = reducer()(undefined, { type: 'TOGGLE_BRANCH_CONTAINER' });
|
||||
expect(state).to.deep.equal({
|
||||
...INITIAL_STATE,
|
||||
branchContainerExpanded: false,
|
||||
});
|
||||
state = reducer()(state, { type: 'TOGGLE_BRANCH_CONTAINER' });
|
||||
expect(state).to.deep.equal({
|
||||
...INITIAL_STATE,
|
||||
branchContainerExpanded: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('will not reset the main view to history if DAG_HISTORY_* actions are taken', () => {
|
||||
const initialState = {
|
||||
...INITIAL_STATE,
|
||||
mainView: 'bookmarks',
|
||||
};
|
||||
const state = reducer()(initialState, { type: 'DAG_HISTORY_DERP' });
|
||||
expect(state).to.deep.equal(initialState);
|
||||
});
|
||||
|
||||
it('will reset the main view to history if an insertable action occurs', () => {
|
||||
const initialState = {
|
||||
...INITIAL_STATE,
|
||||
mainView: 'bookmarks',
|
||||
};
|
||||
const reduce = reduceFactory({
|
||||
actionFilter: () => true,
|
||||
});
|
||||
const state = reduce(initialState, { type: 'DERP' });
|
||||
expect(state.mainView).to.equal('history');
|
||||
});
|
||||
});
|
||||
*/
|
|
@ -1,41 +1,40 @@
|
|||
const webpack = require('webpack');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const path = require('path');
|
||||
|
||||
const CSS_LOADERS = ['style-loader', 'css-loader', 'postcss-loader'];
|
||||
|
||||
module.exports = {
|
||||
devtool: 'source-map',
|
||||
context: path.join(__dirname),
|
||||
devtool: 'inline-source-map',
|
||||
entry: {
|
||||
javascript: './example/app.tsx',
|
||||
html: './example/index.html',
|
||||
},
|
||||
output: {
|
||||
filename: './appbundle.js',
|
||||
path: path.join(__dirname, 'dist'),
|
||||
filename: 'appbundle.js',
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['', '.js', '.jsx', '.ts', '.tsx'],
|
||||
modulesDirectories: [path.join(__dirname, 'node_modules')],
|
||||
fallback: path.join(__dirname, 'node_modules'),
|
||||
extensions: ['.js', '.jsx', '.ts', '.tsx'],
|
||||
alias: {
|
||||
'react/lib/ReactMount': 'react-dom/lib/ReactMount',
|
||||
sinon: 'sinon/pkg/sinon',
|
||||
},
|
||||
},
|
||||
resolveLoader: {
|
||||
modulesDirectories: [path.join(__dirname, 'node_modules')],
|
||||
fallback: path.join(__dirname, 'node_modules'),
|
||||
},
|
||||
plugins: [
|
||||
new webpack.NoErrorsPlugin(),
|
||||
new HtmlWebpackPlugin({
|
||||
title: 'DAG History Component Example',
|
||||
}),
|
||||
new webpack.NoEmitOnErrorsPlugin(),
|
||||
new webpack.ProvidePlugin({
|
||||
saveAs: 'imports?this=>global!exports?global.saveAs!filesaver.js',
|
||||
}),
|
||||
],
|
||||
module: {
|
||||
loaders: [
|
||||
{ test: /\.html$/, loader: 'file-loader?name=[name].[ext]' },
|
||||
{ test: /\.css$/, loader: 'style-loader!css-loader!postcss-loader' },
|
||||
{ test: /\.scss$/, loader: 'style-loader!css-loader!postcss-loader!sass-loader' },
|
||||
{ test: /\.ts(x|)/, loaders: ['ts-loader'], exclude: /node_modules/ },
|
||||
rules: [
|
||||
{ test: /\.css$/, use: CSS_LOADERS },
|
||||
{ test: /\.scss$/, use: [...CSS_LOADERS, 'sass-loader'] },
|
||||
{ test: /\.ts(x|)/, use: ['ts-loader'], exclude: /node_modules/ },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
|
1720
yarn.lock
1720
yarn.lock
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Загрузка…
Ссылка в новой задаче