Ugly code inc, only a draft; timeline-view and horizontal scrolling

This commit is contained in:
Benjamin Kniffler 2015-08-05 02:06:31 +02:00
parent dffe84ec04
commit dba48a6128
3 changed files with 345 additions and 75 deletions

View File

@ -1,6 +1,9 @@
import React, { PropTypes, findDOMNode } from 'react';
import LogMonitorEntry from './LogMonitorEntry';
if(BROWSER){
require("./timeline.less");
}
export default class LogMonitor {
constructor() {
window.addEventListener('keydown', ::this.handleKeyPress);
@ -135,7 +138,9 @@ export default class LogMonitor {
</a>
</div>
</div>
{elements}
<ul className="timeline">
{elements}
</ul>
<div>
{computedStates.length > 1 &&
<a onClick={::this.handleRollback}

View File

@ -1,82 +1,142 @@
import React, { PropTypes } from 'react';
import React, { PropTypes, Component } from 'react';
function hsvToRgb(h, s, v) {
const i = Math.floor(h);
const f = h - i;
const p = v * (1 - s);
const q = v * (1 - f * s);
const t = v * (1 - (1 - f) * s);
const mod = i % 6;
const r = [v, q, p, p, t, v][mod];
const g = [t, v, v, q, p, p][mod];
const b = [p, p, t, v, v, q][mod];
const i = Math.floor(h);
const f = h - i;
const p = v * (1 - s);
const q = v * (1 - f * s);
const t = v * (1 - (1 - f) * s);
const mod = i % 6;
const r = [v, q, p, p, t, v][mod];
const g = [t, v, v, q, p, p][mod];
const b = [p, p, t, v, v, q][mod];
return {
r: Math.round(r * 255),
g: Math.round(g * 255),
b: Math.round(b * 255)
};
return {
r: Math.round(r * 255),
g: Math.round(g * 255),
b: Math.round(b * 255)
};
}
function colorFromString(token) {
const splitToken = token.split('');
const finalToken = splitToken.concat(splitToken.reverse());
const splitToken = token.split('');
const finalToken = splitToken.concat(splitToken.reverse());
const number = finalToken.reduce(
(sum, char) => sum + char.charCodeAt(0),
0
) * Math.abs(Math.sin(token.length));
const number = finalToken.reduce(
(sum, char) => sum + char.charCodeAt(0),
0
) * Math.abs(Math.sin(token.length));
const h = Math.round((number * (180 / Math.PI) * token.length) % 360);
const s = number % 100 / 100;
const v = 1;
const h = Math.round((number * (180 / Math.PI) * token.length) % 360);
const s = number % 100 / 100;
const v = 1;
return hsvToRgb(h, s, v);
return hsvToRgb(h, s, v);
}
export default class LogMonitorEntry {
static propTypes = {
index: PropTypes.number.isRequired,
state: PropTypes.object.isRequired,
action: PropTypes.object.isRequired,
select: PropTypes.func.isRequired,
error: PropTypes.string,
onActionClick: PropTypes.func.isRequired,
collapsed: PropTypes.bool
};
printState(state, error) {
let errorText = error;
if (!errorText) {
try {
return JSON.stringify(this.props.select(state), null, 2);
} catch (err) {
errorText = 'Error selecting state.';
export default class LogMonitorEntry extends Component {
constructor(props) {
super(props);
this.state = {
maximized: false,
time: new Date()
}
}
}
return (
<span style={{
static propTypes = {
index: PropTypes.number.isRequired,
state: PropTypes.object.isRequired,
action: PropTypes.object.isRequired,
select: PropTypes.func.isRequired,
error: PropTypes.string,
onActionClick: PropTypes.func.isRequired,
collapsed: PropTypes.bool
};
printState(state, error) {
let errorText = error;
if (!errorText) {
try {
return JSON.stringify(this.props.select(state), null, 2);
} catch (err) {
errorText = 'Error selecting state.';
}
}
return (
<span style={{
fontStyle: 'italic'
}}>
({errorText})
</span>
);
}
);
}
handleActionClick() {
const { index, onActionClick } = this.props;
if (index > 0) {
onActionClick(index);
}
}
handleCollapseClick() {
const { index, onActionClick } = this.props;
if (index > 0) {
onActionClick(index);
}
}
render() {
const { index, error, action, state, collapsed } = this.props;
const { r, g, b } = colorFromString(action.type);
handleMaximizeClick() {
const { maximized } = this.state;
this.setState({maximized: !maximized});
}
return (
<pre style={{
render() {
const { index, error, action, state, collapsed } = this.props;
const { maximized, time } = this.state;
if (!action.type) {
return null;
}
const { r, g, b } = colorFromString(action.type);
return (
<li className="timeline-inverted">
<a className="timeline-badge" onClick={::this.handleCollapseClick} href="javascript:;">
<i className={"fa fa-" + (collapsed ? "ban" : "check")}></i>
</a>
{maximized ?
<div className="timeline-panel" style={{whiteSpace: "nowrap", overflow: "auto"}}>
<div style={{display: "table", tableLayout: "fixed", width: "100%"}}>
<div style={{ width: "400px", display: "table-cell"}}>
<a className="timeline-heading" onClick={::this.handleMaximizeClick} href="javascript:;">
<pre className="timeline-pre">
<a style={{color: `rgb(${r}, ${g}, ${b})`,}}>
<i className="fa fa-minus-square"></i>
{JSON.stringify(action, null, 2)}
</a>
</pre>
</a>
<div className="timeline-body">
<pre className="timeline-pre">
{this.printState(state, error)}
</pre>
</div>
</div>
</div>
</div> :
<div className="timeline-panel">
<a class="timeline-heading" onClick={::this.handleMaximizeClick} href="javascript:;">
<h4 class="timeline-title" style={{color: `rgb(${r}, ${g}, ${b})`,}}>
<i className="fa fa-plus-square"></i>
{" " + action.type}
</h4>
<p>
<small className="text-muted">
<i className="fa fa-clock-o"></i>
{" " + getTimestring(time)}
</small>
</p>
</a>
</div>
}
</li>
);
return (
<pre style={{
textDecoration: collapsed ? 'line-through' : 'none',
backgroundColor: "transparent",
border: "0px"
@ -92,34 +152,48 @@ export default class LogMonitorEntry {
cursor: (index > 0) ? 'pointer' : 'default',
WebkitUserSelect: 'none'
}}>
{JSON.stringify(action, null, 2)}
{JSON.stringify(action, null, 2)}
</a>
{!collapsed &&
<p style={{
{!collapsed &&
<p style={{
textAlign: 'center',
transform: 'rotate(180deg)',
color: 'lightyellow',
fontSize: "2em"
}}>
</p>
}
</p>
}
{!collapsed &&
<div style={{
{!collapsed &&
<div style={{
paddingBottom: '1em',
paddingTop: '1em',
color: 'lightyellow'
}}>
{this.printState(state, error)}
</div>
}
{this.printState(state, error)}
</div>
}
<hr style={{
<hr style={{
marginBottom: '2em'
}} />
}}/>
</pre>
);
}
);
}
}
function getTimestring(d) {
var x = document.getElementById("demo");
var h = addZero(d.getHours(), 2);
var m = addZero(d.getMinutes(), 2);
var s = addZero(d.getSeconds(), 2);
var ms = addZero(d.getMilliseconds(), 3);
return h + ":" + m + ":" + s + ":" + ms;
}
function addZero(x, n) {
if (x.toString().length < n) {
x = "0" + x;
}
return x;
}

191
src/react/timeline.less Normal file
View File

@ -0,0 +1,191 @@
.timeline {
list-style: none;
padding: 20px 0 20px;
position: relative;
}
.timeline-pre {
background-color: transparent;
color: white;
border: 0;
margin: 0;
padding: 0;
}
.timeline:before {
top: 0;
bottom: 0;
position: absolute;
content: " ";
width: 3px;
background-color: #eeeeee;
left: 20px;
margin-left: -1.5px;
}
.timeline > li {
margin-bottom: 20px;
position: relative;
}
.timeline > li:before,
.timeline > li:after {
content: " ";
display: table;
}
.timeline > li:after {
clear: both;
}
.timeline > li:before,
.timeline > li:after {
content: " ";
display: table;
}
.timeline > li:after {
clear: both;
}
.timeline > li > .timeline-panel {
width: 80%;
float: left;
border: 1px solid #d4d4d4;
border-radius: 2px;
padding: 5px;
position: relative;
-webkit-box-shadow: 0 1px 6px rgba(0, 0, 0, 0.175);
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.175);
}
.timeline > li > .timeline-panel:before {
position: absolute;
top: 26px;
right: -15px;
display: inline-block;
border-top: 15px solid transparent;
border-left: 15px solid #ccc;
border-right: 0 solid #ccc;
border-bottom: 15px solid transparent;
content: " ";
}
.timeline > li > .timeline-panel:after {
position: absolute;
top: 27px;
right: -14px;
display: inline-block;
border-top: 14px solid transparent;
border-left: 14px solid #fff;
border-right: 0 solid #fff;
border-bottom: 14px solid transparent;
content: " ";
}
.timeline > li > .timeline-badge {
color: #fff;
width: 50px;
height: 50px;
line-height: 50px;
font-size: 1.4em;
text-align: center;
position: absolute;
top: 16px;
left: 20px;
margin-left: -25px;
background-color: #999999;
z-index: 100;
border-top-right-radius: 50%;
border-top-left-radius: 50%;
border-bottom-right-radius: 50%;
border-bottom-left-radius: 50%;
}
.timeline > li.timeline-inverted > .timeline-panel {
float: right;
}
.timeline > li.timeline-inverted > .timeline-panel:before {
border-left-width: 0;
border-right-width: 15px;
left: -15px;
right: auto;
}
.timeline > li.timeline-inverted > .timeline-panel:after {
border-left-width: 0;
border-right-width: 14px;
left: -14px;
right: auto;
}
.timeline-badge.primary {
background-color: #2e6da4 !important;
}
.timeline-badge.success {
background-color: #3f903f !important;
}
.timeline-badge.warning {
background-color: #f0ad4e !important;
}
.timeline-badge.danger {
background-color: #d9534f !important;
}
.timeline-badge.info {
background-color: #5bc0de !important;
}
.timeline-title {
margin-top: 0;
//color: inherit;
}
.timeline-body > p,
.timeline-body > ul {
margin-bottom: 0;
}
.timeline-body > p + p {
margin-top: 5px;
}
@media (max-width: 767px) {
ul.timeline:before {
left: 40px;
}
ul.timeline > li > .timeline-panel {
width: calc(100% - 90px);
width: -moz-calc(100% - 90px);
width: -webkit-calc(100% - 90px);
}
ul.timeline > li > .timeline-badge {
left: 15px;
margin-left: 0;
top: 16px;
}
ul.timeline > li > .timeline-panel {
float: right;
}
ul.timeline > li > .timeline-panel:before {
border-left-width: 0;
border-right-width: 15px;
left: -15px;
right: auto;
}
ul.timeline > li > .timeline-panel:after {
border-left-width: 0;
border-right-width: 14px;
left: -14px;
right: auto;
}
}