Move Sections Plugin to OS folder

Summary: as title; we'd like to reuse this for the Litho sections plugin but not having it in OS makes setting up deps more difficult than it should be.

Reviewed By: danielbuechele

Differential Revision: D16052298

fbshipit-source-id: cd965688eff4fedbe57264e6676b6ca09b9deb45
This commit is contained in:
Mihaela Ogrezeanu
2019-07-01 07:00:50 -07:00
committed by Facebook Github Bot
parent 468468a3bc
commit 353f65cd7f
12 changed files with 10736 additions and 1 deletions

View File

@@ -7,7 +7,7 @@
.*/static/.* .*/static/.*
<PROJECT_ROOT>/src/fb/plugins/relaydevtools/relay-devtools/DevtoolsUI.js$ <PROJECT_ROOT>/src/fb/plugins/relaydevtools/relay-devtools/DevtoolsUI.js$
.*/website/.* .*/website/.*
<PROJECT_ROOT>/src/fb/plugins/sections/d3/d3.js$ <PROJECT_ROOT>/src/plugins/sections/d3/d3.js$
[libs] [libs]
flow-typed flow-typed

View File

@@ -0,0 +1,88 @@
/**
* Copyright 2018-present Facebook.
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* @format
*/
import type {UpdateTreeGenerationChangesetApplicationPayload} from './Models.js';
import React from 'react';
import {
MarkerTimeline,
Component,
styled,
FlexBox,
ManagedDataInspector,
Panel,
colors,
} from 'flipper';
const NoContent = styled(FlexBox)({
alignItems: 'center',
justifyContent: 'center',
flexGrow: 1,
fontWeight: '500',
color: colors.light30,
});
type Props = {|
changeSets: ?Array<UpdateTreeGenerationChangesetApplicationPayload>,
eventUserInfo: ?Object,
focusedChangeSet: ?UpdateTreeGenerationChangesetApplicationPayload,
onFocusChangeSet: (
focusedChangeSet: ?UpdateTreeGenerationChangesetApplicationPayload,
) => void,
|};
export default class DetailsPanel extends Component<Props> {
render() {
const {changeSets, eventUserInfo} = this.props;
const firstChangeSet =
(changeSets || []).reduce(
(min, cs) => Math.min(min, cs.timestamp),
Infinity,
) || 0;
return (
<React.Fragment>
{eventUserInfo && (
<Panel
collapsable={false}
floating={false}
heading={'Event User Info'}>
<ManagedDataInspector data={eventUserInfo} expandRoot={true} />
</Panel>
)}
{changeSets && changeSets.length > 0 ? (
<Panel collapsable={false} floating={false} heading={'Changesets'}>
<MarkerTimeline
points={changeSets.map(p => ({
label:
p.type === 'CHANGESET_GENERATED' ? 'Generated' : 'Rendered',
time: Math.round((p.timestamp || 0) - firstChangeSet),
color:
p.type === 'CHANGESET_GENERATED' ? colors.lemon : colors.teal,
key: p.identifier,
}))}
onClick={ids =>
this.props.onFocusChangeSet(
changeSets.find(c => c.identifier === ids[0]),
)
}
selected={this.props.focusedChangeSet?.identifier}
/>
</Panel>
) : (
<NoContent>No changes sets available</NoContent>
)}
{this.props.focusedChangeSet && (
<Panel floating={false} heading="Changeset Details">
<ManagedDataInspector
data={this.props.focusedChangeSet.changeset}
expandRoot={true}
/>
</Panel>
)}
</React.Fragment>
);
}
}

View File

@@ -0,0 +1,249 @@
/**
* Copyright 2018-present Facebook.
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* @format
*/
import type {TreeGeneration} from './Models.js';
import {
FlexColumn,
FlexRow,
Component,
Tooltip,
Glyph,
styled,
colors,
} from 'flipper';
import React from 'react';
const PADDING = 15;
const WIDTH = 70;
const LABEL_WIDTH = 140;
const Container = styled(FlexRow)({
flexShrink: 0,
flexGrow: 1,
});
const SurfaceContainer = styled(FlexColumn)(props => ({
position: 'relative',
'::after': {
display: props.scrolled ? 'block' : 'none',
content: '""',
top: 0,
bottom: 0,
right: -15,
width: 15,
background: `linear-gradient(90deg, ${
colors.macOSTitleBarBackgroundBlur
} 0%, transparent 100%)`,
zIndex: 3,
position: 'absolute',
},
}));
const TimeContainer = styled(FlexColumn)({
overflow: 'scroll',
flexGrow: 1,
flexShrink: 1,
});
const Row = styled(FlexRow)(props => ({
alignItems: 'center',
paddingBottom: 3,
marginTop: 3,
flexGrow: 1,
flexShrink: 0,
maxHeight: 75,
position: 'relative',
minWidth: '100%',
alignSelf: 'flex-start',
'::before': {
display: props.showTimeline ? 'block' : 'none',
zIndex: 1,
content: '""',
position: 'absolute',
borderTop: `1px dotted ${colors.light15}`,
height: 1,
top: '50%',
left: 0,
right: 0,
},
}));
const Label = styled('div')({
width: LABEL_WIDTH,
paddingLeft: 10,
paddingRight: 10,
fontWeight: 'bold',
textOverflow: 'ellipsis',
overflow: 'hidden',
whiteSpace: 'nowrap',
textAlign: 'right',
flexShrink: 0,
position: 'sticky',
left: 0,
zIndex: 2,
});
const Content = styled('div')({
textOverflow: 'ellipsis',
overflow: 'hidden',
fontSize: 11,
textAlign: 'center',
textTransform: 'uppercase',
fontWeight: '500',
color: colors.light50,
});
const Record = styled('div')(({highlighted}) => ({
border: `1px solid ${colors.light15}`,
boxShadow: highlighted
? `inset 0 0 0 2px ${colors.macOSTitleBarIconSelected}`
: 'none',
borderRadius: 5,
padding: 5,
marginRight: PADDING,
backgroundColor: colors.white,
zIndex: 2,
position: 'relative',
width: WIDTH,
flexShrink: 0,
alignSelf: 'stretch',
display: 'flex',
alignItems: 'center',
}));
const Empty = styled('div')({
width: WIDTH,
padding: '10px 5px',
marginRight: PADDING,
flexShrink: 0,
position: 'relative',
});
const Icon = styled(Glyph)({
position: 'absolute',
right: 5,
top: 5,
});
type Props = {|
generations: Array<TreeGeneration>,
focusedGenerationId: ?string,
onClick: (id: string) => mixed,
|};
type State = {
scrolled: boolean,
};
export default class extends Component<Props, State> {
state = {
scrolled: false,
};
componentDidMount() {
document.addEventListener('keydown', this.onKeyDown);
}
componentWillUnmount() {
document.removeEventListener('keydown', this.onKeyDown);
}
componentDidUpdate(prevProps: Props) {
const {focusedGenerationId} = this.props;
if (
focusedGenerationId &&
focusedGenerationId !== prevProps.focusedGenerationId
) {
const node = document.querySelector(`[data-id="${focusedGenerationId}"]`);
if (node) {
// $FlowFixMe scrollIntoViewIfNeeded exists
node.scrollIntoViewIfNeeded();
}
}
}
onKeyDown = (e: KeyboardEvent) => {
if (e.key !== 'ArrowRight' && e.key !== 'ArrowLeft') {
return;
}
e.preventDefault();
let nextGenerationId = null;
const index = this.props.generations.findIndex(
g => g.id === this.props.focusedGenerationId,
);
const direction = e.key === 'ArrowRight' ? 1 : -1;
const bound = e.key === 'ArrowRight' ? this.props.generations.length : -1;
for (let i = index + direction; i !== bound; i += direction) {
if (
this.props.generations[i].surface_key ===
this.props.generations[index].surface_key
) {
nextGenerationId = this.props.generations[i].id;
break;
}
}
if (nextGenerationId) {
this.props.onClick(nextGenerationId);
}
};
onScroll = (e: SyntheticUIEvent<HTMLElement>) =>
this.setState({scrolled: e.currentTarget.scrollLeft > 0});
render() {
const surfaces = this.props.generations.reduce(
(acc, cv) => acc.add(cv.surface_key),
new Set(),
);
return (
<Container>
<SurfaceContainer scrolled={this.state.scrolled}>
{[...surfaces].map(surface => (
<Row key={surface}>
<Label title={surface}>{surface}</Label>
</Row>
))}
</SurfaceContainer>
<TimeContainer onScroll={this.onScroll}>
{[...surfaces].map(surface => (
<Row key={surface} showTimeline>
{this.props.generations.map((record: TreeGeneration) =>
record.surface_key === surface ? (
<Record
key={`${surface}${record.id}`}
data-id={record.id}
highlighted={record.id === this.props.focusedGenerationId}
onClick={() => this.props.onClick(record.id)}>
<Content>{record.reason}</Content>
{record.reentrant_count > 0 && (
<Tooltip
title={'Reentrant count ' + record.reentrant_count}>
<Icon
color={colors.red}
name="caution-circle"
variant="filled"
size={12}
/>
</Tooltip>
)}
</Record>
) : (
<Empty key={`${surface}${record.id}`} />
),
)}
</Row>
))}
</TimeContainer>
</Container>
);
}
}

View File

@@ -0,0 +1,83 @@
/**
* Copyright 2018-present Facebook.
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* @format
*/
export type SectionComponentHierarchy = {|
type: string,
children: Array<SectionComponentHierarchy>,
|};
export type AddEventPayload = {|
id: string,
reason: string,
stack_trace: Array<string>,
surface_key: string,
event_timestamp: number,
update_mode: number,
reentrant_count: number,
payload: ?Object,
|};
export type UpdateTreeGenerationHierarchyGenerationPayload = {|
hierarchy_generation_timestamp: number,
id: string,
reason: string,
tree?: Array<{
didTriggerStateUpdate: boolean,
identifier: string,
isDirty: boolean,
isReused: boolean,
name: string,
parent: string | 0,
}>,
|};
export type UpdateTreeGenerationChangesetGenerationPayload = {|
timestamp: number,
tree_generation_id: string,
identifier: string,
type: string,
changeset: {
inserted_items: {},
inserted_sections: {},
moved_items: {},
removed_sections: [],
updated_items: {
[key: string]: {
context: string,
model: string,
},
},
},
|};
export type UpdateTreeGenerationChangesetApplicationPayload = {|
changeset: {
inserted_items: {},
inserted_sections: {},
moved_items: {},
removed_sections: [],
updated_items: {
[key: string]: {
context: string,
model: string,
},
},
},
type: string,
identifier: string,
timestamp: number,
section_component_hierarchy: SectionComponentHierarchy,
tree_generation_id: string,
payload: ?Object,
|};
export type TreeGeneration = {|
...AddEventPayload,
...$Shape<UpdateTreeGenerationHierarchyGenerationPayload>,
...$Shape<UpdateTreeGenerationChangesetGenerationPayload>,
changeSets: Array<UpdateTreeGenerationChangesetApplicationPayload>,
|};

View File

@@ -0,0 +1,47 @@
/**
* Copyright 2018-present Facebook.
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* @format
*/
import React from 'react';
import {colors, StackTrace} from 'flipper';
const FacebookLibraries = ['Facebook'];
const REGEX = new RegExp(
'(?<library>[A-Za-z0-9]*) *(?<address>0x[A-Za-z0-9]*) (?<caller>(.*)) \\+ (?<lineNumber>[0-9]*)',
);
function isSystemLibrary(libraryName: ?string): boolean {
return !FacebookLibraries.includes(libraryName);
}
type Props = {
data: Array<string>,
};
export default class extends React.Component<Props> {
render() {
return (
<StackTrace backgroundColor={colors.white}>
{/* We need to filter out from the stack trace any reference to the plugin such that the information is more coincised and focused */}
{this.props.data
.filter(stack_trace_line => {
return !stack_trace_line.includes('FlipperKitSectionsPlugin');
})
.map(stack_trace_line => {
const trace = REGEX.exec(stack_trace_line)?.groups;
return {
bold: !isSystemLibrary(trace?.library),
library: trace?.library,
address: trace?.address,
caller: trace?.caller,
lineNumber: trace?.lineNumber,
};
})}
</StackTrace>
);
}
}

View File

@@ -0,0 +1,236 @@
/**
* Copyright 2018-present Facebook.
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* @format
*/
import type {SectionComponentHierarchy} from './Models';
import {PureComponent, styled, Toolbar, Spacer, colors} from 'flipper';
import {Tree} from 'react-d3-tree';
import {Fragment} from 'react';
const Legend = styled('div')(props => ({
color: colors.dark50,
marginLeft: 20,
'&::before': {
content: '""',
display: 'inline-block',
width: 10,
height: 10,
borderRadius: 6,
backgroundColor: props.color,
border: `1px solid rgba(0,0,0,0.2)`,
marginRight: 4,
marginBottom: -1,
},
}));
const Label = styled('div')({
position: 'relative',
top: -7,
left: 7,
maxWidth: 270,
overflow: 'hidden',
fontWeight: '500',
textOverflow: 'ellipsis',
paddingLeft: 5,
paddingRight: 5,
background: colors.white,
display: 'inline-block',
});
const Container = styled('div')({
width: '100%',
height: '100%',
overflow: 'hidden',
background:
'linear-gradient(-90deg,rgba(0,0,0,.02) 1px,transparent 0),linear-gradient(rgba(0,0,0,.02) 1px,transparent 0),linear-gradient(-90deg,rgba(0,0,0,.03) 1px,transparent 0),linear-gradient(rgba(0,0,0,.03) 1px,transparent 0)',
backgroundSize:
'10px 10px,10px 10px,100px 100px,100px 100px,100px 100px,100px 100px,100px 100px,100px 100px',
});
type TreeData = Array<{
identifier: string,
name: string,
parent: string | 0,
didTriggerStateUpdate: boolean,
isReused: boolean,
isDirty: boolean,
}>;
type Props = {
data: TreeData | SectionComponentHierarchy,
};
type State = {
translate: {
x: number,
y: number,
},
tree: ?Object,
zoom: number,
};
const NodeLabel = (props: {
nodeData: {
name: string,
},
}) => {
const name = props?.nodeData?.name;
return <Label title={name}>{name}</Label>;
};
export default class extends PureComponent<Props, State> {
treeFromFlatArray = (data: TreeData) => {
const tree = data.map(n => {
let fill = colors.blueGreyTint70;
if (n.didTriggerStateUpdate) {
fill = colors.lemon;
} else if (n.isReused) {
fill = colors.teal;
} else if (n.isDirty) {
fill = colors.grape;
}
return {
name: n.name,
children: [],
attributes: {...n},
nodeSvgShape: {
shapeProps: {
fill,
r: 6,
strokeWidth: 1,
stroke: 'rgba(0,0,0,0.2)',
},
},
};
});
const parentMap: Map<string, Array<Object>> = tree.reduce((acc, cv) => {
const {parent} = cv.attributes;
if (typeof parent !== 'string') {
return acc;
}
const children = acc.get(parent);
if (children) {
return acc.set(parent, children.concat(cv));
} else {
return acc.set(parent, [cv]);
}
}, new Map());
tree.forEach(n => {
n.children = parentMap.get(n.attributes.identifier) || [];
});
// find the root node
return tree.find(node => !node.attributes.parent);
};
treeFromHierarchy = (data: SectionComponentHierarchy): Object => {
return {
name: data.type,
children: data.children ? data.children.map(this.treeFromHierarchy) : [],
};
};
state = {
translate: {
x: 0,
y: 0,
},
tree: Array.isArray(this.props.data)
? this.treeFromFlatArray(this.props.data)
: this.treeFromHierarchy(this.props.data),
zoom: 1,
};
treeContainer: any = null;
componentWillReceiveProps(props: Props) {
if (this.props.data === props.data) {
return;
}
this.setState({
tree: Array.isArray(props.data)
? this.treeFromFlatArray(props.data)
: this.treeFromHierarchy(props.data),
});
}
componentDidMount() {
if (this.treeContainer) {
const dimensions = this.treeContainer.getBoundingClientRect();
this.setState({
translate: {
x: 50,
y: dimensions.height / 2,
},
});
}
}
onZoom = (e: SyntheticInputEvent<HTMLInputElement>) => {
this.setState({zoom: e.target.valueAsNumber});
};
render() {
return (
<Fragment>
<Container
innerRef={ref => {
this.treeContainer = ref;
}}>
<style>
{'.rd3t-tree-container foreignObject {overflow: visible;}'}
</style>
{this.state.tree && (
<Tree
transitionDuration={0}
separation={{siblings: 0.5, nonSiblings: 0.5}}
data={this.state.tree}
translate={this.state.translate}
zoom={this.state.zoom}
nodeLabelComponent={{
// $FlowFixMe props are passed in by react-d3-tree
render: <NodeLabel />,
}}
allowForeignObjects
nodeSvgShape={{
shape: 'circle',
shapeProps: {
stroke: 'rgba(0,0,0,0.2)',
strokeWidth: 1,
},
}}
styles={{
links: {
stroke: '#b3b3b3',
},
}}
nodeSize={{x: 300, y: 100}}
/>
)}
</Container>
<Toolbar position="bottom" compact>
<input
type="range"
onChange={this.onZoom}
value={this.state.zoom}
min="0.1"
max="1"
step="0.01"
/>
<Spacer />
<Legend color={colors.lemon}>triggered state update</Legend>
<Legend color={colors.teal}>is reused</Legend>
<Legend color={colors.grape}>is dirty</Legend>
</Toolbar>
</Fragment>
);
}
}

View File

@@ -0,0 +1,26 @@
Copyright (c) 2010-2016, Michael Bostock
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* The name Michael Bostock may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL MICHAEL BOSTOCK BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

9553
src/plugins/sections/d3/d3.js vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,69 @@
{
"name": "d3",
"version": "3.5.17",
"description": "A JavaScript visualization library for HTML and SVG.",
"bugs": {
"email": "danielbuechele@fb.com"
},
"keywords": [
"dom",
"w3c",
"visualization",
"svg",
"animation",
"canvas"
],
"homepage": "http://d3js.org",
"author": {
"name": "Mike Bostock",
"url": "http://bost.ocks.org/mike"
},
"contributors": [
{
"name": "Jason Davies",
"url": "http://jasondavies.com"
}
],
"repository": {
"type": "git",
"url": "https://github.com/mbostock/d3.git"
},
"main": "d3.js",
"browser": "d3.js",
"jspm": {
"main": "d3",
"shim": {
"d3": {
"exports": "d3"
}
},
"files": [
"d3.js"
],
"buildConfig": {
"uglify": true
}
},
"jam": {
"main": "d3.js",
"shim": {
"exports": "d3"
}
},
"spm": {
"main": "d3.js"
},
"devDependencies": {
"jsdom": "3",
"seedrandom": "2",
"smash": "0.0",
"uglify-js": "2.6.2",
"vows": "0.8"
},
"scripts": {
"test": "vows && echo",
"prepublish": "npm test && rm -f package.js src/start.js d3.js d3.min.js d3.zip && bin/start > src/start.js && bin/meteor > package.js && smash src/d3.js | uglifyjs - -b indent-level=2 -o d3.js && bin/uglify d3.js > d3.min.js && chmod a-w d3.js d3.min.js package.js && zip d3.zip LICENSE d3.js d3.min.js",
"postpublish": "VERSION=`node -e 'console.log(require(\"./package.json\").version)'`; git push && git push --tags && cp -v README.md LICENSE d3.js d3.min.js ../d3-bower && cd ../d3-bower && git add README.md LICENSE d3.js d3.min.js && git commit -m \"Release $VERSION.\" && git tag -am \"Release $VERSION.\" v${VERSION} && git push && git push --tags && cd - && cp -v d3.js ../d3.github.com/d3.v3.js && cp -v d3.min.js ../d3.github.com/d3.v3.min.js && cd ../d3.github.com && git add d3.v3.js d3.v3.min.js && git commit -m \"d3 ${VERSION}\" && git push"
},
"license": "BSD-3-Clause"
}

View File

@@ -0,0 +1,266 @@
/**
* Copyright 2018-present Facebook.
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* @format
* @flow
*/
import type {
TreeGeneration,
AddEventPayload,
UpdateTreeGenerationHierarchyGenerationPayload,
UpdateTreeGenerationChangesetGenerationPayload,
UpdateTreeGenerationChangesetApplicationPayload,
} from './Models.js';
import {FlipperPlugin} from 'flipper';
import React from 'react';
import Tree from './Tree.js';
import StackTrace from './StackTrace.js';
import EventTable from './EventsTable.js';
import DetailsPanel from './DetailsPanel.js';
import {
Toolbar,
Glyph,
Sidebar,
FlexBox,
styled,
Button,
Spacer,
colors,
DetailSidebar,
} from 'flipper';
const Waiting = styled(FlexBox)(props => ({
width: '100%',
height: '100%',
flexGrow: 1,
background: colors.light02,
alignItems: 'center',
justifyContent: 'center',
textAlign: 'center',
}));
const InfoText = styled('div')(props => ({
marginTop: 10,
marginBottom: 10,
fontWeight: '500',
color: colors.light30,
}));
const InfoBox = styled('div')(props => ({
maxWidth: 400,
margin: 'auto',
textAlign: 'center',
}));
type State = {
focusedChangeSet: ?UpdateTreeGenerationChangesetApplicationPayload,
userSelectedGenerationId: ?string,
};
type PersistedState = {
generations: {
[id: string]: TreeGeneration,
},
focusedGenerationId: ?string,
recording: boolean,
};
export default class extends FlipperPlugin<State, *, PersistedState> {
static title = 'Sections';
static id = 'Sections';
static icon = 'tree';
static defaultPersistedState = {
generations: {},
focusedGenerationId: null,
recording: true,
};
static persistedStateReducer = (
persistedState: PersistedState,
method: string,
payload: Object,
): $Shape<PersistedState> => {
if (!persistedState.recording) {
return persistedState;
}
const addEvent = (data: AddEventPayload) => ({
...persistedState,
generations: {
...persistedState.generations,
[data.id]: {
...data,
changeSets: [],
},
},
focusedGenerationId: persistedState.focusedGenerationId || data.id,
});
const updateTreeGenerationHierarchyGeneration = (
data: UpdateTreeGenerationHierarchyGenerationPayload,
) => ({
...persistedState,
generations: {
...persistedState.generations,
[data.id]: {
...persistedState.generations[data.id],
...data,
},
},
});
const updateTreeGenerationChangeset = (
data:
| UpdateTreeGenerationChangesetGenerationPayload
| UpdateTreeGenerationChangesetApplicationPayload,
) => ({
...persistedState,
generations: {
...persistedState.generations,
[data.tree_generation_id]: {
...persistedState.generations[data.tree_generation_id],
changeSets: [
...persistedState.generations[data.tree_generation_id].changeSets,
data,
],
},
},
});
if (method === 'addEvent') {
return addEvent(payload);
} else if (method === 'updateTreeGenerationHierarchyGeneration') {
return updateTreeGenerationHierarchyGeneration(payload);
} else if (
method === 'updateTreeGenerationChangesetApplication' ||
method === 'updateTreeGenerationChangesetGeneration'
) {
return updateTreeGenerationChangeset(payload);
} else {
return persistedState;
}
};
state = {
focusedChangeSet: null,
userSelectedGenerationId: null,
};
onTreeGenerationFocused = (focusedGenerationId: ?string) => {
this.setState({
focusedChangeSet: null,
userSelectedGenerationId: focusedGenerationId,
});
};
onFocusChangeSet = (
focusedChangeSet: ?UpdateTreeGenerationChangesetApplicationPayload,
) => {
this.setState({
focusedChangeSet,
});
};
renderTreeHierarchy = (generation: ?TreeGeneration) => {
if (generation && generation.tree && generation.tree.length > 0) {
// Display component tree hierarchy, if any
return <Tree data={generation.tree} />;
} else if (
this.state.focusedChangeSet &&
this.state.focusedChangeSet.section_component_hierarchy
) {
// Display section component hierarchy for specific changeset
return (
<Tree data={this.state.focusedChangeSet.section_component_hierarchy} />
);
} else {
return this.renderWaiting();
}
};
renderWaiting = () => (
<Waiting>
<InfoBox>
<Glyph
name="face-unhappy"
variant="outline"
size={24}
color={colors.light30}
/>
<InfoText>No data available...</InfoText>
</InfoBox>
</Waiting>
);
clear = () => {
this.props.setPersistedState({
...this.constructor.defaultPersistedState,
});
};
render() {
const {generations} = this.props.persistedState;
if (Object.values(this.props.persistedState.generations).length === 0) {
return this.renderWaiting();
}
const focusedGenerationId =
this.state.userSelectedGenerationId ||
this.props.persistedState.focusedGenerationId;
const focusedTreeGeneration: ?TreeGeneration = focusedGenerationId
? generations[focusedGenerationId]
: null;
return (
<React.Fragment>
<Toolbar>
<Spacer />
{this.props.persistedState.recording ? (
<Button
onClick={() =>
this.props.setPersistedState({
recording: false,
})
}
iconVariant="filled"
icon="stop-playback">
Stop
</Button>
) : (
<Button onClick={this.clear} icon="trash" iconVariant="outline">
Clear
</Button>
)}
</Toolbar>
<Sidebar position="top" minHeight={80} height={80}>
<EventTable
// $FlowFixMe Object.values returns Array<mixed>: https://github.com/facebook/flow/issues/2221
generations={Object.values(generations)}
focusedGenerationId={focusedGenerationId}
onClick={this.onTreeGenerationFocused}
/>
</Sidebar>
{this.renderTreeHierarchy(focusedTreeGeneration)}
{focusedTreeGeneration && (
<Sidebar position="bottom" minHeight={100} height={250}>
<StackTrace data={focusedTreeGeneration.stack_trace} />
</Sidebar>
)}
<DetailSidebar>
<DetailsPanel
eventUserInfo={focusedTreeGeneration?.payload}
changeSets={focusedTreeGeneration?.changeSets}
onFocusChangeSet={this.onFocusChangeSet}
focusedChangeSet={this.state.focusedChangeSet}
/>
</DetailSidebar>
</React.Fragment>
);
}
}

View File

@@ -0,0 +1,18 @@
{
"name": "flipper-plugin-sections",
"title": "Sections",
"bugs": {
"email": "oncall+ios_componentkit@xmail.facebook.com",
"url": "https://fb.workplace.com/groups/componentkit/"
},
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"gatekeeper": "flipper_sections_plugin",
"dependencies": {
"react-d3-tree": "^1.12.1"
},
"resolutions": {
"react-d3-tree/d3": "file:./d3"
}
}

View File

@@ -0,0 +1,100 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@babel/runtime@^7.1.2":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.2.0.tgz#b03e42eeddf5898e00646e4c840fa07ba8dcad7f"
integrity sha512-oouEibCbHMVdZSDlJBO6bZmID/zA/G/Qx3H1d3rSNPTD+L8UNKvCat7aKWSJ74zYbm5zWGh0GQN0hKj8zYFTCg==
dependencies:
regenerator-runtime "^0.12.0"
chain-function@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/chain-function/-/chain-function-1.0.1.tgz#c63045e5b4b663fb86f1c6e186adaf1de402a1cc"
integrity sha512-SxltgMwL9uCko5/ZCLiyG2B7R9fY4pDZUw7hJ4MhirdjBLosoDqkWABi3XMucddHdLiFJMb7PD2MZifZriuMTg==
clone@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f"
integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=
d3@3.5.17, "d3@file:./d3":
version "3.5.17"
deep-equal@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5"
integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=
dom-helpers@^3.2.0:
version "3.4.0"
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8"
integrity sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==
dependencies:
"@babel/runtime" "^7.1.2"
"js-tokens@^3.0.0 || ^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
loose-envify@^1.0.0, loose-envify@^1.3.1:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
dependencies:
js-tokens "^3.0.0 || ^4.0.0"
object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
prop-types@^15.5.10, prop-types@^15.5.6:
version "15.6.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102"
integrity sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==
dependencies:
loose-envify "^1.3.1"
object-assign "^4.1.1"
react-d3-tree@^1.12.1:
version "1.12.1"
resolved "https://registry.yarnpkg.com/react-d3-tree/-/react-d3-tree-1.12.1.tgz#30ca3ff727bc6c43cf2d0f0e1796c3b0907f6a3f"
integrity sha512-4yRCXccmkTbMPHNr2gcVrYwYaYzZOWItqRqxItNFfozpKfaymWvwepC9sIm5RAdWOIGnsoK1SR+X67rB4th2Xg==
dependencies:
clone "^2.1.1"
d3 "3.5.17"
deep-equal "^1.0.1"
prop-types "^15.5.10"
react-transition-group "^1.1.3"
uuid "^3.0.1"
react-transition-group@^1.1.3:
version "1.2.1"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-1.2.1.tgz#e11f72b257f921b213229a774df46612346c7ca6"
integrity sha512-CWaL3laCmgAFdxdKbhhps+c0HRGF4c+hdM4H23+FI1QBNUyx/AMeIJGWorehPNSaKnQNOAxL7PQmqMu78CDj3Q==
dependencies:
chain-function "^1.0.0"
dom-helpers "^3.2.0"
loose-envify "^1.3.1"
prop-types "^15.5.6"
warning "^3.0.0"
regenerator-runtime@^0.12.0:
version "0.12.1"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de"
integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==
uuid@^3.0.1:
version "3.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==
warning@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/warning/-/warning-3.0.0.tgz#32e5377cb572de4ab04753bdf8821c01ed605b7c"
integrity sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=
dependencies:
loose-envify "^1.0.0"