-
-
Notifications
You must be signed in to change notification settings - Fork 781
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add StoredVars tab #220
base: trunk
Are you sure you want to change the base?
Add StoredVars tab #220
Changes from 110 commits
7397715
52f8bcb
5fcca39
07329b0
360e63e
20d66a5
22bf8cb
7d5018c
ddbe71e
91dccdf
cfa2a65
1bf9642
0a37fb7
bcd0fac
b8c9152
c49ddb1
592edd7
5361e90
97091de
a2ac3bb
f8bd438
01ae49f
99793d2
2f1ffb8
5b02588
a039510
80560e6
f22421f
e7fbea7
22ceeb6
215df3e
4d8a91e
5084b98
34ab9b5
c83c71c
9f5ecfe
e2f7f86
32fe96d
8cc2ab8
f1d4f7c
b6e30ba
b5857a8
053507d
382f301
6b8c95b
afd3f3a
2fbec41
2d3c2ff
6c77ea7
04d7c5e
b5c03de
be6b8af
5c824ba
90ca479
4c051e5
f1d44e5
8fc4eb0
124cf5b
14668c4
dc383bb
8c8a1e2
73af006
b8bf1be
fcb0695
f078116
e4b61c3
c1a3541
595ea29
63f2056
0479728
ec2a3db
ff0bc3d
fb8fac2
08703dc
64ab9cc
cd0dd7d
2c974a2
6c157ad
cd30364
34c200a
3400bac
95eb981
0944601
90282de
d8f027c
e0b8b8a
364d03e
fd9aa81
c338056
ca3d734
31bd3ec
e7463a2
3e1487a
45c1f76
ee9d96b
bae774a
91a3e05
57c263a
a2aa1d7
d4057ed
e7afab8
11a27c4
ac14f23
9542ed2
3922396
219a869
27cb4d7
f79aa49
156e804
6178d3c
5003d93
cd88f7e
c3b7459
58a139c
8012491
43146ef
5c90bb2
35fd7a3
90e6cf4
c1dce17
9014d69
0fb9ed9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// Licensed to the Software Freedom Conservancy (SFC) under one | ||
// or more contributor license agreements. See the NOTICE file | ||
// distributed with this work for additional information | ||
// regarding copyright ownership. The SFC licenses this file | ||
// to you under the Apache License, Version 2.0 (the | ||
// "License"); you may not use this file except in compliance | ||
// with the License. You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, | ||
// software distributed under the License is distributed on an | ||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
// KIND, either express or implied. See the License for the | ||
// specific language governing permissions and limitations | ||
// under the License. | ||
|
||
import React from "react"; | ||
import ActionButton from "../ActionButton"; | ||
import classNames from "classnames"; | ||
|
||
export default class DeleteButton extends React.Component { | ||
render() { | ||
return ( | ||
<ActionButton data-tip="<p>Delete</p>" {...this.props} className={classNames("si-delete", this.props.className)} />// eslint-disable-line react/prop-types | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
// Licensed to the Software Freedom Conservancy (SFC) under one | ||
// or more contributor license agreements. See the NOTICE file | ||
// distributed with this work for additional information | ||
// regarding copyright ownership. The SFC licenses this file | ||
// to you under the Apache License, Version 2.0 (the | ||
// "License"); you may not use this file except in compliance | ||
// with the License. You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, | ||
// software distributed under the License is distributed on an | ||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
// KIND, either express or implied. See the License for the | ||
// specific language governing permissions and limitations | ||
// under the License. | ||
|
||
import React from "react"; | ||
import PropTypes from "prop-types"; | ||
import classNames from "classnames"; | ||
import DeleteButton from "../ActionButtons/Delete"; | ||
import { observer } from "mobx-react"; | ||
import "./style.css"; | ||
|
||
@observer | ||
export default class Variable extends React.Component { | ||
constructor(props){ | ||
super(props); | ||
this.handleKeyDown = this.handleKeyDown.bind(this); | ||
this.handleChanged = this.handleChanged.bind(this); | ||
this.keyChanged = this.keyChanged.bind(this); | ||
this.valueChanged = this.valueChanged.bind(this); | ||
this.delete = this.delete.bind(this); | ||
this.edit = this.edit.bind(this); | ||
this.state = { key: "", value: "" }; | ||
} | ||
componentDidMount() { | ||
if (this.input) | ||
this.input.focus(); | ||
} | ||
handleKeyDown(e) { | ||
if (e.key === "Enter") { | ||
e.preventDefault(); | ||
const isValidKey = this.state.key || this.props.keyVar; | ||
const isValidValue = this.state.value || this.props.value; | ||
if (isValidKey && isValidValue) { | ||
this.edit(isValidKey, isValidValue); | ||
} | ||
} | ||
} | ||
handleChanged() { | ||
const isValidKey = this.state.key || this.props.keyVar; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The naming convention is misleading, variables that begin with |
||
const isValidValue = this.state.value || this.props.value; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is empty string considered invalid? users can definitely store an empty string. |
||
if (isValidKey && isValidValue) { | ||
this.edit(isValidKey, isValidValue); | ||
} else if (!(isValidKey || isValidValue)) { | ||
this.props.setIsAdding(false); | ||
} | ||
} | ||
delete() { | ||
this.props.delete(this.props.keyVar); | ||
} | ||
edit(key, value){ | ||
this.delete(); | ||
this.props.add(key, value); | ||
} | ||
keyChanged(e) { | ||
this.setState({ key: e.target.value }); | ||
} | ||
valueChanged(e) { | ||
this.setState({ value: e.target.value }); | ||
} | ||
render() { | ||
return ( | ||
<li className="variable"> | ||
{this.props.isAdding ? | ||
<input | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the reason to use the default |
||
ref={(input) => { this.input = input; }} | ||
className="name isAdding" | ||
onChange={this.keyChanged} | ||
onKeyDown={this.handleKeyDown} | ||
onBlur={this.handleChanged}/> | ||
: | ||
<input | ||
className={classNames("name", { "editable": this.props.isStop })} | ||
disabled={this.props.isStop ? false : true} | ||
onChange={this.keyChanged} | ||
value={this.state.key || this.props.keyVar} | ||
onKeyDown={this.handleKeyDown} | ||
onBlur={this.handleChanged}/>} | ||
{this.props.isAdding ? | ||
<input | ||
className="value isAdding" | ||
onChange={this.valueChanged} | ||
onKeyDown={this.handleKeyDown} | ||
onBlur={this.handleChanged} /> | ||
: | ||
<input | ||
className={classNames("value", { "editable": this.props.isStop })} | ||
disabled={this.props.isStop ? false : true} | ||
onChange={this.valueChanged} | ||
value={this.state.value || this.props.value} | ||
onKeyDown={this.handleKeyDown} | ||
onBlur={this.handleChanged} />} | ||
<DeleteButton className="deleteBtn" data-place="left" onClick={this.delete} disabled={!this.props.isStop}/> | ||
</li> | ||
); | ||
} | ||
static propTypes = { | ||
keyVar: PropTypes.string, | ||
value: PropTypes.string, | ||
delete: PropTypes.func, | ||
add: PropTypes.func, | ||
isAdding: PropTypes.bool, | ||
isStop: PropTypes.bool, | ||
setIsAdding: PropTypes.func | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
.variable { | ||
font-size: 12px; | ||
word-break: break-all; | ||
display: flex; | ||
} | ||
|
||
.variable .name, | ||
.variable .value { | ||
float: left; | ||
padding: 4px 5px; | ||
width: 48%; | ||
text-align: left; | ||
border: 1px transparent solid; | ||
} | ||
|
||
.variable:not(.value-header):hover { | ||
background-color: #f3f3f3; | ||
} | ||
|
||
.variable .deleteBtn { | ||
width: 4%; | ||
font-size: 15px; | ||
opacity: 0; | ||
height: 20px; | ||
} | ||
|
||
.variable:hover .deleteBtn { | ||
opacity: 1; | ||
} | ||
|
||
.variable input, | ||
.variable input:disabled { | ||
background-color: transparent; | ||
} | ||
|
||
.variable input:not(.isAdding) { | ||
cursor: initial; | ||
outline: 0; | ||
} | ||
|
||
.variable .isAdding { | ||
margin: 0 2px; | ||
} | ||
|
||
.variable .editable:focus { | ||
border: 1px #4ea1ff solid; | ||
outline: 0; | ||
background-color: white; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
// Licensed to the Software Freedom Conservancy (SFC) under one | ||
// or more contributor license agreements. See the NOTICE file | ||
// distributed with this work for additional information | ||
// regarding copyright ownership. The SFC licenses this file | ||
// to you under the Apache License, Version 2.0 (the | ||
// "License"); you may not use this file except in compliance | ||
// with the License. You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, | ||
// software distributed under the License is distributed on an | ||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
// KIND, either express or implied. See the License for the | ||
// specific language governing permissions and limitations | ||
// under the License. | ||
|
||
import React from "react"; | ||
import PropTypes from "prop-types"; | ||
import Variable from "../Variable"; | ||
import { observer } from "mobx-react"; | ||
import "./style.css"; | ||
|
||
@observer | ||
export default class VariableList extends React.Component { | ||
constructor(props){ | ||
super(props); | ||
this.deleteVariable = this.deleteVariable.bind(this); | ||
this.addVariable = this.addVariable.bind(this); | ||
} | ||
deleteVariable(key){ | ||
this.props.variables.deleteVariable(key); | ||
this.props.setIsAdding(false); | ||
} | ||
addVariable(key, value){ | ||
this.props.variables.addVariable(key, value); | ||
this.props.setIsAdding(false); | ||
} | ||
|
||
render() { | ||
const variables = this.props.variables; | ||
return ( | ||
<form onSubmit={(e) => { e.preventDefault(); }}> | ||
<ul className="value-list"> | ||
{variables.storedVars.size == 0 ? null : | ||
<li className="value-header variable"> | ||
<strong className="name">Name</strong> | ||
<strong className="value">Value</strong> | ||
<div className="deleteBtn"/> | ||
</li>} | ||
{variables.storedVars.entries().sort().map((storedMap) => ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Keep them sorted via a computed property, either in the store, or in the component. |
||
<Variable | ||
key={Math.random()} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This beats the purpose of having a |
||
keyVar={storedMap[0]} | ||
value={storedMap[1]} | ||
add={this.addVariable} | ||
delete={this.deleteVariable} | ||
isStop={variables.isStop} | ||
/> | ||
))} | ||
{ this.props.isAdding ? | ||
<Variable | ||
add={this.addVariable} | ||
delete={this.deleteVariable} | ||
isAdding={this.props.isAdding} | ||
setIsAdding={this.props.setIsAdding} | ||
isStop={variables.isStop} | ||
/> : null} | ||
</ul> | ||
</form> | ||
); | ||
} | ||
static propTypes = { | ||
variables: PropTypes.object, | ||
isAdding: PropTypes.bool, | ||
setIsAdding: PropTypes.func | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
.value-list { | ||
text-align: left; | ||
overflow-y: auto; | ||
overflow-x: hidden; | ||
display: flex; | ||
flex-direction: column; | ||
} | ||
|
||
.value-list li:last-child { | ||
margin-bottom: 3px; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,26 +17,28 @@ | |
|
||
import React from "react"; | ||
import PropTypes from "prop-types"; | ||
import { observer } from "mobx-react"; | ||
import { observe } from "mobx"; | ||
import TabBar from "../../components/TabBar"; | ||
import LogList from "../../components/LogList"; | ||
import VariableList from "../../components/VariableList"; | ||
import ClearButton from "../../components/ActionButtons/Clear"; | ||
import AddButton from "../../components/ActionButtons/Add"; | ||
import { output } from "../../stores/view/Logs"; | ||
import PlaybackLogger from "../../side-effects/playback-logging"; | ||
import "./style.css"; | ||
import CommandReference from "../../components/CommandReference"; | ||
import variables from "../../stores/view/Variables"; | ||
import UiState from "../../stores/view/UiState"; | ||
import { observer } from "mobx-react"; | ||
import { observe } from "mobx"; | ||
import { Commands } from "../../models/Command"; | ||
import "./style.css"; | ||
|
||
@observer | ||
export default class Console extends React.Component { | ||
constructor(props) { | ||
super(props); | ||
this.state = { | ||
tab: "Log", | ||
logsUnread: false | ||
}; | ||
this.state = { tab: "Log", logsUnread: false, isAddingVariable: false }; | ||
this.tabClicked = this.tabClicked.bind(this); | ||
this.addVariableClicked = this.addVariableClicked.bind(this); | ||
this.playbackLogger = new PlaybackLogger(); | ||
this.loggerDisposer = observe(output.logs, () => { | ||
this.setState({ logsUnread: this.state.tab === "Log" ? false : true }); | ||
|
@@ -58,24 +60,40 @@ export default class Console extends React.Component { | |
logsUnread: tab === "Log" ? false : this.state.logsUnread | ||
}); | ||
} | ||
tabClicked(){ | ||
this.props.restoreSize(); | ||
//create different object which stores name and read status (e.g., unread boolean) | ||
tabClicked(tab) { | ||
this.setState({ | ||
tab | ||
}); | ||
if(this.props.restoreSize) this.props.restoreSize(); | ||
} | ||
addVariableClicked(isAdding) { | ||
this.setState({ | ||
isAddingVariable: isAdding | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't like this state, it is also inconsistent with the experience we give with the command table, |
||
}); | ||
} | ||
scroll(to) { | ||
this.viewport.scrollTo(0, to); | ||
} | ||
render() { | ||
const command = UiState.selectedCommand ? Commands.list.get(UiState.selectedCommand.command) : undefined; | ||
const tabs = [{ name: "Log", unread: this.state.logsUnread }, { name: "Reference", unread: false }]; | ||
const tabs = [{ name: "Log", unread: this.state.logsUnread }, { name: "Reference", unread: false }, { name: "Variables", unread: false }]; | ||
return ( | ||
<footer className="console" style={{ | ||
height: this.props.height ? `${this.props.height}px` : "initial" | ||
}}> | ||
<TabBar tabs={tabs} tabWidth={90} buttonsMargin={0} tabChanged={this.tabChangedHandler}> | ||
<ClearButton onClick={output.clear} /> | ||
<TabBar tabs={tabs} tabWidth={90} buttonsMargin={0} tabChanged={this.tabChangedHandler} tabClicked={this.tabClicked}> | ||
{this.state.tab === "Log" && <ClearButton data-tip="<p>Clear log</p>" onClick={output.clear} /> } | ||
{this.state.tab === "Variables" && | ||
<div> | ||
<AddButton data-tip="<p>Add Variable</p>" onClick={() => this.addVariableClicked(true)} disabled={!variables.isStop} /> | ||
<ClearButton data-tip="<p>Clear Variable</p>" onClick={variables.clearVariables} disabled={!variables.isStop}/> | ||
</div> } | ||
{this.state.tab === "Reference" && <ClearButton onClick={output.clear} /> } | ||
</TabBar> | ||
<div className="viewport" ref={this.setViewportRef}> | ||
{this.state.tab === "Log" && <LogList output={output} scrollTo={this.scroll}/> } | ||
{this.state.tab === "Variables" && <VariableList variables={variables} isAdding={this.state.isAddingVariable} setIsAdding={this.addVariableClicked}/> } | ||
{this.state.tab === "Reference" && <CommandReference currentCommand={command}/> } | ||
</div> | ||
</footer> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use a form's
onSubmit
event, instead of keydown and checking for the key enter.It is bypassing the browser default behavior.