Skip to content
This repository was archived by the owner on Jun 26, 2020. It is now read-only.

Commit 03c5e05

Browse files
committed
Merge pull request #186 from kassens/tabs
Add a tabbed pane [updated]
2 parents e4c55f4 + 3f63d11 commit 03c5e05

File tree

4 files changed

+152
-30
lines changed

4 files changed

+152
-30
lines changed

frontend/Container.js

Lines changed: 39 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@ var PropState = require('./PropState');
1515
var React = require('react');
1616
var SearchPane = require('./SearchPane');
1717
var SplitPane = require('./SplitPane');
18+
var TabbedPane = require('./TabbedPane');
1819

1920
import type MenuItem from './ContextMenu';
2021

2122
class Container extends React.Component {
2223
props: {
2324
reload: () => void,
2425
extraPanes: Array<(node: Object) => ReactElement>,
26+
extraTabs: ?{[key: string]: () => ReactElement},
2527
menuItems: {
2628
tree?: (id: string, node: Object, store: Object) => ?Array<MenuItem>,
2729
attr?: (
@@ -36,45 +38,53 @@ class Container extends React.Component {
3638
};
3739

3840
render(): ReactElement {
39-
var defaultItems = {
40-
tree: (id, node, store) => {
41-
var items = [];
42-
if (node.get('name')) {
43-
items.push({
44-
title: 'Show all ' + node.get('name'),
45-
action: () => store.changeSearch(node.get('name')),
46-
});
47-
}
48-
if (store.capabilities.scroll) {
49-
items.push({
50-
title: 'Scroll to node',
51-
action: () => store.scrollToNode(id),
52-
});
53-
}
54-
return items;
55-
},
56-
attr: (id, node, val, path, name, store) => {
57-
var items = [{
58-
title: 'Store as global variable',
59-
action: () => store.makeGlobal(id, path),
60-
}];
61-
return items;
62-
},
63-
};
64-
65-
return (
66-
<div style={styles.container}>
41+
var tabs = {
42+
Elements: () => (
6743
<SplitPane
6844
initialWidth={300}
6945
left={() => <SearchPane reload={this.props.reload} />}
7046
right={() => <PropState extraPanes={this.props.extraPanes} />}
7147
/>
72-
<ContextMenu itemSources={[defaultItems, this.props.menuItems]} />
48+
),
49+
...this.props.extraTabs,
50+
};
51+
return (
52+
<div style={styles.container}>
53+
<TabbedPane
54+
tabs={tabs}
55+
/>
56+
<ContextMenu itemSources={[DEFAULT_MENU_ITEMS, this.props.menuItems]} />
7357
</div>
7458
);
7559
}
7660
}
7761

62+
var DEFAULT_MENU_ITEMS = {
63+
tree: (id, node, store) => {
64+
var items = [];
65+
if (node.get('name')) {
66+
items.push({
67+
title: 'Show all ' + node.get('name'),
68+
action: () => store.changeSearch(node.get('name')),
69+
});
70+
}
71+
if (store.capabilities.scroll) {
72+
items.push({
73+
title: 'Scroll to Node',
74+
action: () => store.scrollToNode(id),
75+
});
76+
}
77+
return items;
78+
},
79+
attr: (id, node, val, path, name, store) => {
80+
var items = [{
81+
title: 'Store as global variable',
82+
action: () => store.makeGlobal(id, path),
83+
}];
84+
return items;
85+
},
86+
};
87+
7888
var styles = {
7989
container: {
8090
flex: 1,

frontend/Store.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ class Store extends EventEmitter {
9191
roots: List;
9292
searchRoots: ?List;
9393
searchText: string;
94+
selectedTab: string;
9495
selected: ?ElementID;
9596
breadcrumbHead: ?ElementID;
9697
// an object describing the capabilities of the inspected runtime.
@@ -111,6 +112,7 @@ class Store extends EventEmitter {
111112
this.searchRoots = null;
112113
this.hovered = null;
113114
this.selected = null;
115+
this.selectedTab = 'Elements';
114116
this.breadcrumbHead = null;
115117
this.isBottomTagSelected = false;
116118
this.searchText = '';
@@ -140,6 +142,7 @@ class Store extends EventEmitter {
140142
this._bridge.on('select', ({id, quiet}) => {
141143
this._revealDeep(id);
142144
this.selectTop(this.skipWrapper(id), quiet);
145+
this.setSelectedTab('Elements');
143146
});
144147

145148
this._establishConnection();
@@ -170,6 +173,14 @@ class Store extends EventEmitter {
170173
this._bridge.send('scrollToNode', id);
171174
}
172175

176+
setSelectedTab(name: string): void {
177+
if (this.selectedTab === name) {
178+
return;
179+
}
180+
this.selectedTab = name;
181+
this.emit('selectedTab');
182+
}
183+
173184
// TODO(jared): get this working for react native
174185
changeTextContent(id: ElementID, text: string): void {
175186
this._bridge.send('changeTextContent', {id, text});

frontend/TabbedPane.js

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*
9+
* @flow
10+
*/
11+
'use strict';
12+
13+
var React = require('react');
14+
var assign = require('object-assign');
15+
var decorate = require('./decorate');
16+
17+
class TabbedPane {
18+
props: {
19+
tabs: {[key: string]: () => ReactElement},
20+
selected: string,
21+
setSelectedTab: (name: string) => void,
22+
};
23+
24+
render() {
25+
var tabs = Object.keys(this.props.tabs);
26+
if (tabs.length === 1) {
27+
return this.props.tabs[tabs[0]]();
28+
}
29+
return (
30+
<div style={styles.container}>
31+
<ul style={styles.tabs}>
32+
{tabs.map((name, i) => {
33+
var style = styles.tab;
34+
if (name === this.props.selected) {
35+
style = assign({}, style, styles.selectedTab);
36+
}
37+
if (i === tabs.length - 1) {
38+
style = assign({}, style, styles.lastTab);
39+
}
40+
return (
41+
<li style={style} onClick={() => this.props.setSelectedTab(name)}>
42+
{name}
43+
</li>
44+
);
45+
})}
46+
</ul>
47+
<div style={styles.body}>
48+
{this.props.tabs[this.props.selected]()}
49+
</div>
50+
</div>
51+
);
52+
}
53+
}
54+
55+
var styles = {
56+
container:{
57+
flex: 1,
58+
display: 'flex',
59+
flexDirection: 'column',
60+
},
61+
tabs: {
62+
display: 'flex',
63+
flexShrink: 0,
64+
listStyle: 'none',
65+
backgroundColor: '#eee',
66+
borderBottom: '1px solid rgb(163, 163, 163)',
67+
margin: 0,
68+
padding: '0 2px',
69+
},
70+
tab: {
71+
padding: '2px 4px',
72+
lineHeight: '15px',
73+
fontSize: 12,
74+
fontFamily: "'Lucida Grande', sans-serif",
75+
cursor: 'pointer',
76+
borderLeft: '1px solid rgb(163, 163, 163)',
77+
},
78+
lastTab: {
79+
borderRight: '1px solid rgb(163, 163, 163)',
80+
},
81+
selectedTab: {
82+
backgroundColor: 'white',
83+
},
84+
body: {
85+
flex: 1,
86+
display: 'flex',
87+
minHeight: 0,
88+
},
89+
};
90+
91+
module.exports = decorate({
92+
listeners: () => ['selectedTab'],
93+
props(store) {
94+
return {
95+
selected: store.selectedTab,
96+
setSelectedTab: name => store.setSelectedTab(name),
97+
};
98+
},
99+
}, TabbedPane);

shells/plain/index.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
}
1212
#container {
1313
display: flex;
14-
height: 500px;
14+
height: 400px;
1515
}
1616
body {
1717
margin: 0;
@@ -22,6 +22,8 @@
2222
left: 0;
2323
right: 0;
2424
bottom: 0;
25+
margin: 0;
26+
padding: 0;
2527
}
2628
</style>
2729
</head>

0 commit comments

Comments
 (0)