Moved sections plugin into fb/
Summary: See https://github.com/facebook/flipper/issues/1680, the sections plugin was exposed publicly, even though we don't have a publicly available client plugin Reviewed By: passy Differential Revision: D24993552 fbshipit-source-id: 788ecc29ec64048b3077dea89e492ddbf1ea7d84
This commit is contained in:
committed by
Facebook GitHub Bot
parent
acd2cf25ec
commit
c6e51fe73c
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"$schema": "https://fbflipper.com/schemas/plugin-package/v2.json",
|
||||
"name": "flipper-plugin-sections",
|
||||
"id": "Sections",
|
||||
"icon": "tree",
|
||||
"title": "Sections",
|
||||
"bugs": {
|
||||
"email": "oncall+ios_componentkit@xmail.facebook.com",
|
||||
"url": "https://fb.workplace.com/groups/componentkit/"
|
||||
},
|
||||
"version": "0.65.0",
|
||||
"main": "dist/bundle.js",
|
||||
"flipperBundlerEntry": "src/index.tsx",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"flipper-plugin"
|
||||
],
|
||||
"gatekeeper": "flipper_sections_plugin",
|
||||
"dependencies": {
|
||||
"react-d3-tree": "^1.16.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"flipper": "0.65.0",
|
||||
"flipper-plugin": "0.65.0"
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* 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';
|
||||
|
||||
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>
|
||||
| null
|
||||
| undefined;
|
||||
eventUserInfo: any;
|
||||
focusedChangeSet:
|
||||
| UpdateTreeGenerationChangesetApplicationPayload
|
||||
| null
|
||||
| undefined;
|
||||
onFocusChangeSet: (
|
||||
focusedChangeSet:
|
||||
| UpdateTreeGenerationChangesetApplicationPayload
|
||||
| null
|
||||
| undefined,
|
||||
) => void;
|
||||
selectedNodeInfo: any;
|
||||
};
|
||||
|
||||
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
|
||||
key="eventUserInfo"
|
||||
collapsable={false}
|
||||
floating={false}
|
||||
heading={'Event User Info'}>
|
||||
<ManagedDataInspector data={eventUserInfo} expandRoot={true} />
|
||||
</Panel>
|
||||
)}
|
||||
{changeSets && changeSets.length > 0 ? (
|
||||
<Panel
|
||||
key="Changesets"
|
||||
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
|
||||
key="Changeset Details"
|
||||
floating={false}
|
||||
heading="Changeset Details">
|
||||
<ManagedDataInspector
|
||||
data={this.props.focusedChangeSet.changeset}
|
||||
expandRoot={true}
|
||||
/>
|
||||
</Panel>
|
||||
)}
|
||||
{this.props.selectedNodeInfo && (
|
||||
<Panel
|
||||
key="Selected Node Info"
|
||||
floating={false}
|
||||
heading="Selected Node Info">
|
||||
<ManagedDataInspector
|
||||
data={this.props.selectedNodeInfo}
|
||||
expandRoot={true}
|
||||
/>
|
||||
</Panel>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,248 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* 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';
|
||||
|
||||
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: {scrolled: boolean}) => ({
|
||||
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: {showTimeline?: boolean}) => ({
|
||||
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((props: {highlighted: boolean}) => ({
|
||||
border: `1px solid ${colors.light15}`,
|
||||
boxShadow: props.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 | null | undefined;
|
||||
onClick: (id: string) => any;
|
||||
};
|
||||
|
||||
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) {
|
||||
node.scrollIntoView();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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: React.UIEvent<HTMLDivElement>) =>
|
||||
this.setState({scrolled: e.currentTarget.scrollLeft > 0});
|
||||
|
||||
render() {
|
||||
const surfaces: Set<string> = this.props.generations.reduce(
|
||||
(acc, cv) => acc.add(cv.surface_key),
|
||||
new Set<string>(),
|
||||
);
|
||||
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>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* 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>;
|
||||
skip_stack_trace_format?: boolean;
|
||||
surface_key: string;
|
||||
event_timestamp: number;
|
||||
update_mode: number;
|
||||
reentrant_count: number;
|
||||
payload: any;
|
||||
};
|
||||
|
||||
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 | '';
|
||||
inserted?: boolean;
|
||||
removed?: boolean;
|
||||
updated?: boolean;
|
||||
unchanged?: boolean;
|
||||
isSection?: boolean;
|
||||
isDataModel?: boolean;
|
||||
}>;
|
||||
};
|
||||
|
||||
export type UpdateTreeGenerationChangesetGenerationPayload = {
|
||||
timestamp: number;
|
||||
tree_generation_id: string;
|
||||
identifier: string;
|
||||
type: string;
|
||||
changeset: {
|
||||
section_key: {
|
||||
changesets: {
|
||||
id: {
|
||||
count: number;
|
||||
index: number;
|
||||
toIndex?: number;
|
||||
type: string;
|
||||
render_infos?: Array<String>;
|
||||
prev_data?: Array<String>;
|
||||
next_data?: Array<String>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export type UpdateTreeGenerationChangesetApplicationPayload = {
|
||||
changeset: {
|
||||
section_key: {
|
||||
changesets: {
|
||||
id: {
|
||||
count: number;
|
||||
index: number;
|
||||
toIndex?: number;
|
||||
type: string;
|
||||
render_infos?: Array<String>;
|
||||
prev_data?: Array<String>;
|
||||
next_data?: Array<String>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
type: string;
|
||||
identifier: string;
|
||||
timestamp: number;
|
||||
section_component_hierarchy?: SectionComponentHierarchy;
|
||||
tree_generation_id: string;
|
||||
payload?: any;
|
||||
};
|
||||
|
||||
export type TreeGeneration = {
|
||||
changeSets: Array<UpdateTreeGenerationChangesetApplicationPayload>;
|
||||
} & AddEventPayload &
|
||||
Partial<UpdateTreeGenerationHierarchyGenerationPayload> &
|
||||
Partial<UpdateTreeGenerationChangesetGenerationPayload>;
|
||||
@@ -1,60 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* 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 = /\d+\s+(?<library>[\s\w\.]+\w)\s+(?<address>0x\w+?)\s+(?<caller>.+) \+ (?<lineNumber>\d+)/;
|
||||
|
||||
function isSystemLibrary(libraryName: string | null | undefined): boolean {
|
||||
return libraryName ? !FacebookLibraries.includes(libraryName) : false;
|
||||
}
|
||||
|
||||
type Props = {
|
||||
data: Array<string>;
|
||||
skipStackTraceFormat?: boolean | undefined;
|
||||
};
|
||||
|
||||
export default class extends React.Component<Props> {
|
||||
render() {
|
||||
if (this.props.skipStackTraceFormat) {
|
||||
return (
|
||||
<StackTrace backgroundColor={colors.white}>
|
||||
{this.props.data.map((stack_trace_line) => {
|
||||
return {
|
||||
caller: stack_trace_line,
|
||||
};
|
||||
})}
|
||||
</StackTrace>
|
||||
);
|
||||
}
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,328 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* 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 {Glyph, PureComponent, styled, Toolbar, Spacer, colors} from 'flipper';
|
||||
import {Tree} from 'react-d3-tree';
|
||||
import React from 'react';
|
||||
|
||||
const Container = styled.div({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
});
|
||||
|
||||
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 TreeContainer = styled.div({
|
||||
flexGrow: 1,
|
||||
width: '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',
|
||||
});
|
||||
|
||||
const LabelContainer = styled.div({
|
||||
display: 'flex',
|
||||
});
|
||||
|
||||
const IconButton = styled.div({
|
||||
position: 'relative',
|
||||
left: 5,
|
||||
top: -8,
|
||||
background: colors.white,
|
||||
});
|
||||
|
||||
type TreeData = Array<{
|
||||
identifier: string;
|
||||
name: string;
|
||||
parent: string | '';
|
||||
didTriggerStateUpdate?: boolean;
|
||||
isReused?: boolean;
|
||||
isDirty?: boolean;
|
||||
inserted?: boolean;
|
||||
removed?: boolean;
|
||||
updated?: boolean;
|
||||
unchanged?: boolean;
|
||||
isSection?: boolean;
|
||||
isDataModel?: boolean;
|
||||
}>;
|
||||
|
||||
type Props = {
|
||||
data: TreeData | SectionComponentHierarchy;
|
||||
nodeClickHandler: (node: any) => void;
|
||||
selectedNodeIndexPath?: number[];
|
||||
};
|
||||
|
||||
type State = {
|
||||
translate: {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
tree: Object | null | undefined;
|
||||
zoom: number;
|
||||
};
|
||||
|
||||
class NodeLabel extends PureComponent<
|
||||
{onLabelClicked: (node: any) => void; nodeData?: any},
|
||||
{collapsed: boolean}
|
||||
> {
|
||||
state = {
|
||||
collapsed: false,
|
||||
};
|
||||
|
||||
showNodeData = (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
this.props.onLabelClicked(this.props?.nodeData);
|
||||
};
|
||||
|
||||
toggleClicked = () => {
|
||||
this.setState({
|
||||
collapsed: !this.state.collapsed,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const name = this.props?.nodeData?.name;
|
||||
const isSection = this.props?.nodeData?.attributes.isSection;
|
||||
const chevron = this.state.collapsed ? 'chevron-right' : 'chevron-left';
|
||||
|
||||
return (
|
||||
<LabelContainer>
|
||||
<Label title={name} onClick={this.showNodeData}>
|
||||
{name}
|
||||
</Label>
|
||||
{isSection && (
|
||||
<IconButton onClick={this.toggleClicked}>
|
||||
<Glyph
|
||||
color={colors.blueGreyTint70}
|
||||
name={chevron}
|
||||
variant={'filled'}
|
||||
size={12}
|
||||
/>
|
||||
</IconButton>
|
||||
)}
|
||||
</LabelContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (n.removed) {
|
||||
fill = colors.light20;
|
||||
} else if (n.inserted) {
|
||||
fill = colors.pinkDark1;
|
||||
} else if (n.updated) {
|
||||
fill = colors.orangeTint15;
|
||||
} else if (n.unchanged) {
|
||||
fill = colors.teal;
|
||||
}
|
||||
|
||||
return {
|
||||
name: n.name,
|
||||
children: [] as Array<any>,
|
||||
attributes: {...n},
|
||||
nodeSvgShape: {
|
||||
shapeProps: {
|
||||
fill,
|
||||
r: 6,
|
||||
strokeWidth: 1,
|
||||
stroke: 'rgba(0,0,0,0.2)',
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const parentMap: Map<string, Array<any>> = 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
|
||||
const root = tree.find((node) => !node.attributes.parent);
|
||||
|
||||
// Highlight the selected node
|
||||
if (this.props.selectedNodeIndexPath && root) {
|
||||
let cursor = root;
|
||||
this.props.selectedNodeIndexPath.forEach((idx) => {
|
||||
cursor = cursor.children[idx];
|
||||
});
|
||||
cursor.nodeSvgShape.shapeProps.strokeWidth = 2;
|
||||
cursor.nodeSvgShape.shapeProps.stroke = colors.red;
|
||||
cursor.nodeSvgShape.shapeProps.fill = colors.redTint;
|
||||
}
|
||||
|
||||
return root;
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
UNSAFE_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: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({zoom: e.target.valueAsNumber});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Container>
|
||||
<TreeContainer
|
||||
ref={(ref) => {
|
||||
this.treeContainer = ref;
|
||||
}}>
|
||||
{this.state.tree && (
|
||||
<>
|
||||
<style>
|
||||
{'.rd3t-tree-container foreignObject {overflow: visible;}'}
|
||||
</style>
|
||||
<Tree
|
||||
transitionDuration={0}
|
||||
separation={{siblings: 0.5, nonSiblings: 0.5}}
|
||||
data={this.state.tree}
|
||||
translate={this.state.translate}
|
||||
zoom={this.state.zoom}
|
||||
nodeLabelComponent={{
|
||||
render: (
|
||||
<NodeLabel onLabelClicked={this.props.nodeClickHandler} />
|
||||
),
|
||||
}}
|
||||
allowForeignObjects
|
||||
nodeSvgShape={{
|
||||
shape: 'circle',
|
||||
shapeProps: {
|
||||
stroke: 'rgba(0,0,0,0.2)',
|
||||
strokeWidth: 1,
|
||||
},
|
||||
}}
|
||||
styles={{
|
||||
links: {
|
||||
stroke: '#b3b3b3',
|
||||
},
|
||||
}}
|
||||
nodeSize={{x: 300, y: 100}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</TreeContainer>
|
||||
<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.light20}>Item removed</Legend>
|
||||
<Legend color={colors.pinkDark1}>Item inserted</Legend>
|
||||
<Legend color={colors.orangeTint15}>Item updated</Legend>
|
||||
<Legend color={colors.teal}>Item/Section Reused</Legend>
|
||||
<Legend color={colors.lemon}>Section triggered state update</Legend>
|
||||
<Legend color={colors.grape}>Section is dirty</Legend>
|
||||
</Toolbar>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
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.
|
||||
9560
desktop/plugins/sections/src/d3/d3.js
vendored
9560
desktop/plugins/sections/src/d3/d3.js
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,70 +0,0 @@
|
||||
{
|
||||
"title": "sections-d3",
|
||||
"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": "^16.0.1",
|
||||
"seedrandom": "^3.0.5",
|
||||
"smash": "0.0",
|
||||
"uglify-js": "^3.7.5",
|
||||
"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"
|
||||
}
|
||||
@@ -1,389 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {
|
||||
TreeGeneration,
|
||||
AddEventPayload,
|
||||
UpdateTreeGenerationHierarchyGenerationPayload,
|
||||
UpdateTreeGenerationChangesetGenerationPayload,
|
||||
UpdateTreeGenerationChangesetApplicationPayload,
|
||||
} from './Models';
|
||||
|
||||
import Tree from './Tree';
|
||||
import StackTrace from './StackTrace';
|
||||
import EventTable from './EventsTable';
|
||||
import DetailsPanel from './DetailsPanel';
|
||||
import React, {useState, useMemo} from 'react';
|
||||
|
||||
import {
|
||||
Toolbar,
|
||||
Glyph,
|
||||
Sidebar,
|
||||
FlexBox,
|
||||
styled,
|
||||
Button,
|
||||
Spacer,
|
||||
colors,
|
||||
DetailSidebar,
|
||||
SearchInput,
|
||||
SearchBox,
|
||||
SearchIcon,
|
||||
Layout,
|
||||
} from 'flipper';
|
||||
|
||||
import {PluginClient, createState, usePlugin, useValue} from 'flipper-plugin';
|
||||
|
||||
const Waiting = styled(FlexBox)({
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
flexGrow: 1,
|
||||
background: colors.light02,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
textAlign: 'center',
|
||||
});
|
||||
|
||||
const InfoText = styled.div({
|
||||
marginTop: 10,
|
||||
marginBottom: 10,
|
||||
fontWeight: 500,
|
||||
color: colors.light30,
|
||||
});
|
||||
|
||||
const InfoBox = styled.div({
|
||||
maxWidth: 400,
|
||||
margin: 'auto',
|
||||
textAlign: 'center',
|
||||
});
|
||||
|
||||
const TreeContainer = styled.div({
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
overflow: 'hidden',
|
||||
});
|
||||
|
||||
type Events = {
|
||||
addEvent: AddEventPayload;
|
||||
updateTreeGenerationHierarchyGeneration: UpdateTreeGenerationHierarchyGenerationPayload;
|
||||
updateTreeGenerationChangesetApplication: UpdateTreeGenerationChangesetApplicationPayload;
|
||||
updateTreeGenerationChangesetGeneration: UpdateTreeGenerationChangesetGenerationPayload;
|
||||
};
|
||||
|
||||
type FocusInfo = {
|
||||
generationId: string;
|
||||
treeNodeIndexPath?: number[];
|
||||
};
|
||||
|
||||
export function plugin(client: PluginClient<Events, {}>) {
|
||||
const generations = createState<{[id: string]: TreeGeneration}>(
|
||||
{},
|
||||
{persist: 'generations'},
|
||||
);
|
||||
const focusInfo = createState<FocusInfo | undefined>(undefined);
|
||||
const recording = createState<boolean>(true);
|
||||
|
||||
client.onMessage('addEvent', (data) => {
|
||||
if (!recording.get()) {
|
||||
return;
|
||||
}
|
||||
generations.update((draft) => {
|
||||
draft[data.id] = {...data, changeSets: []};
|
||||
});
|
||||
focusInfo.set({
|
||||
generationId: focusInfo.get()?.generationId || data.id,
|
||||
treeNodeIndexPath: undefined,
|
||||
});
|
||||
});
|
||||
client.onMessage('updateTreeGenerationHierarchyGeneration', (data) => {
|
||||
generations.update((draft) => {
|
||||
draft[data.id] = {...draft[data.id], ...data};
|
||||
});
|
||||
});
|
||||
function updateTreeGenerationChangeset(
|
||||
data:
|
||||
| UpdateTreeGenerationChangesetGenerationPayload
|
||||
| UpdateTreeGenerationChangesetApplicationPayload,
|
||||
) {
|
||||
generations.update((draft) => {
|
||||
draft[data.tree_generation_id].changeSets.push(data);
|
||||
});
|
||||
}
|
||||
client.onMessage(
|
||||
'updateTreeGenerationChangesetApplication',
|
||||
updateTreeGenerationChangeset,
|
||||
);
|
||||
client.onMessage(
|
||||
'updateTreeGenerationChangesetGeneration',
|
||||
updateTreeGenerationChangeset,
|
||||
);
|
||||
client.onDeepLink((payload) => {
|
||||
if (typeof payload === 'string') {
|
||||
handleDeepLinkPayload(payload);
|
||||
}
|
||||
});
|
||||
|
||||
function handleDeepLinkPayload(payload: string) {
|
||||
// Payload expected to be something like
|
||||
// 1.1.FBAnimatingComponent[0].CKFlexboxComponent[2].CKComponent where path components are separated by '.'
|
||||
// - The first '1' is the scope root ID.
|
||||
// - The numbers in square brackets are the indexes of the following component in the children array
|
||||
// of the preceding component. In this example, the last CKComponent is the 2nd child of CKFlexboxComponent.
|
||||
const pathComponents = payload.split('.');
|
||||
const rootId = pathComponents[0];
|
||||
|
||||
const generationValues = Object.values(generations.get());
|
||||
const mostRecentTreeBuild = generationValues.reverse().find((g) => {
|
||||
return g.surface_key == rootId && g.reason == 'Tree Build';
|
||||
});
|
||||
if (mostRecentTreeBuild) {
|
||||
const regex = /\w+\[(\d+)\]/;
|
||||
const indexPath = pathComponents.reduce((acc, component) => {
|
||||
const match = regex.exec(component);
|
||||
if (match) {
|
||||
acc.push(+match[1]);
|
||||
}
|
||||
return acc;
|
||||
}, [] as number[]);
|
||||
focusInfo.set({
|
||||
generationId: mostRecentTreeBuild.id,
|
||||
treeNodeIndexPath: indexPath,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function setRecording(value: boolean) {
|
||||
recording.set(value);
|
||||
}
|
||||
function clear() {
|
||||
generations.set({});
|
||||
focusInfo.set(undefined);
|
||||
recording.set(true);
|
||||
}
|
||||
|
||||
return {
|
||||
generations,
|
||||
focusInfo,
|
||||
recording,
|
||||
setRecording,
|
||||
clear,
|
||||
};
|
||||
}
|
||||
|
||||
export function Component() {
|
||||
const instance = usePlugin(plugin);
|
||||
const generations = useValue(instance.generations);
|
||||
const focusInfo = useValue(instance.focusInfo);
|
||||
const recording = useValue(instance.recording);
|
||||
|
||||
const [userSelectedGenerationId, setUserSelectedGenerationId] = useState<
|
||||
string | undefined
|
||||
>();
|
||||
const [searchString, setSearchString] = useState<string>('');
|
||||
const [focusedChangeSet, setFocusedChangeSet] = useState<
|
||||
UpdateTreeGenerationChangesetApplicationPayload | null | undefined
|
||||
>(null);
|
||||
const [selectedTreeNode, setSelectedTreeNode] = useState<any>();
|
||||
|
||||
const focusedTreeGeneration: TreeGeneration | null = useMemo(() => {
|
||||
const id = userSelectedGenerationId || focusInfo?.generationId;
|
||||
if (id === undefined) {
|
||||
return null;
|
||||
}
|
||||
return generations[id];
|
||||
}, [userSelectedGenerationId, focusInfo, generations]);
|
||||
const filteredGenerations: Array<TreeGeneration> = useMemo(() => {
|
||||
const generationValues = Object.values(generations);
|
||||
if (searchString.length <= 0) {
|
||||
return generationValues;
|
||||
}
|
||||
|
||||
const matchesCurrentSearchString = (s: string): boolean => {
|
||||
return s.toLowerCase().includes(searchString.toLowerCase());
|
||||
};
|
||||
const matchingKeys: Array<string> = generationValues
|
||||
.filter((g) => {
|
||||
if (g.payload) {
|
||||
const componentClassName: string | null | undefined =
|
||||
g.payload.component_class_name;
|
||||
if (componentClassName) {
|
||||
return matchesCurrentSearchString(componentClassName);
|
||||
}
|
||||
}
|
||||
return g.tree?.some((node) => {
|
||||
return matchesCurrentSearchString(node.name);
|
||||
});
|
||||
})
|
||||
.map((g) => {
|
||||
return g.surface_key;
|
||||
});
|
||||
|
||||
return generationValues.filter((g) => matchingKeys.includes(g.surface_key));
|
||||
}, [generations, searchString]);
|
||||
|
||||
return (
|
||||
<Layout.Right>
|
||||
<Layout.Top>
|
||||
<Toolbar>
|
||||
<SearchBox tabIndex={-1}>
|
||||
<SearchIcon
|
||||
name="magnifying-glass"
|
||||
color={colors.macOSTitleBarIcon}
|
||||
size={16}
|
||||
/>
|
||||
<SearchInput
|
||||
placeholder={'Search'}
|
||||
onChange={(e) => setSearchString(e.target.value)}
|
||||
value={searchString}
|
||||
/>
|
||||
</SearchBox>
|
||||
<Spacer />
|
||||
{recording ? (
|
||||
<Button
|
||||
onClick={() => instance.setRecording(false)}
|
||||
iconVariant="filled"
|
||||
icon="stop-playback">
|
||||
Stop
|
||||
</Button>
|
||||
) : (
|
||||
<Button onClick={instance.clear} icon="trash" iconVariant="outline">
|
||||
Clear
|
||||
</Button>
|
||||
)}
|
||||
</Toolbar>
|
||||
<Layout.Top>
|
||||
<Sidebar position="top" minHeight={80} height={80}>
|
||||
<EventTable
|
||||
generations={filteredGenerations}
|
||||
focusedGenerationId={
|
||||
userSelectedGenerationId || focusInfo?.generationId
|
||||
}
|
||||
onClick={(id?: string) => {
|
||||
setFocusedChangeSet(null);
|
||||
setUserSelectedGenerationId(id);
|
||||
setSelectedTreeNode(null);
|
||||
}}
|
||||
/>
|
||||
</Sidebar>
|
||||
<Layout.Top>
|
||||
<Sidebar position="top" minHeight={400} height={400}>
|
||||
<TreeContainer>
|
||||
<TreeHierarchy
|
||||
generation={focusedTreeGeneration}
|
||||
focusedChangeSet={focusedChangeSet}
|
||||
setSelectedTreeNode={setSelectedTreeNode}
|
||||
selectedNodeIndexPath={focusInfo?.treeNodeIndexPath}
|
||||
/>
|
||||
</TreeContainer>
|
||||
</Sidebar>
|
||||
{focusedTreeGeneration && (
|
||||
<Layout.ScrollContainer>
|
||||
<StackTrace
|
||||
data={focusedTreeGeneration.stack_trace}
|
||||
skipStackTraceFormat={
|
||||
focusedTreeGeneration.skip_stack_trace_format
|
||||
}
|
||||
/>
|
||||
</Layout.ScrollContainer>
|
||||
)}
|
||||
</Layout.Top>
|
||||
</Layout.Top>
|
||||
</Layout.Top>
|
||||
<DetailSidebar>
|
||||
<DetailsPanel
|
||||
eventUserInfo={focusedTreeGeneration?.payload}
|
||||
changeSets={focusedTreeGeneration?.changeSets}
|
||||
onFocusChangeSet={(
|
||||
focusedChangeSet:
|
||||
| UpdateTreeGenerationChangesetApplicationPayload
|
||||
| null
|
||||
| undefined,
|
||||
) => {
|
||||
setFocusedChangeSet(focusedChangeSet);
|
||||
setSelectedTreeNode(null);
|
||||
}}
|
||||
focusedChangeSet={focusedChangeSet}
|
||||
selectedNodeInfo={selectedTreeNode}
|
||||
/>
|
||||
</DetailSidebar>
|
||||
</Layout.Right>
|
||||
);
|
||||
}
|
||||
|
||||
function TreeHierarchy({
|
||||
generation,
|
||||
focusedChangeSet,
|
||||
setSelectedTreeNode,
|
||||
selectedNodeIndexPath,
|
||||
}: {
|
||||
generation: TreeGeneration | null;
|
||||
focusedChangeSet:
|
||||
| UpdateTreeGenerationChangesetApplicationPayload
|
||||
| null
|
||||
| undefined;
|
||||
setSelectedTreeNode: (node: any) => void;
|
||||
selectedNodeIndexPath?: number[];
|
||||
}) {
|
||||
const onNodeClicked = useMemo(
|
||||
() => (targetNode: any) => {
|
||||
if (targetNode.attributes.isSection) {
|
||||
const sectionData: any = {};
|
||||
sectionData.global_key = targetNode.attributes.identifier;
|
||||
setSelectedTreeNode({sectionData});
|
||||
return;
|
||||
}
|
||||
|
||||
let dataModel;
|
||||
// Not all models can be parsed.
|
||||
if (targetNode.attributes.isDataModel) {
|
||||
try {
|
||||
dataModel = JSON.parse(targetNode.attributes.identifier);
|
||||
} catch (e) {
|
||||
dataModel = targetNode.attributes.identifier;
|
||||
}
|
||||
}
|
||||
|
||||
setSelectedTreeNode({dataModel});
|
||||
},
|
||||
[setSelectedTreeNode],
|
||||
);
|
||||
|
||||
if (generation && generation.tree && generation.tree.length > 0) {
|
||||
// Display component tree hierarchy, if any
|
||||
return (
|
||||
<Tree
|
||||
data={generation.tree}
|
||||
nodeClickHandler={onNodeClicked}
|
||||
selectedNodeIndexPath={selectedNodeIndexPath}
|
||||
/>
|
||||
);
|
||||
} else if (focusedChangeSet && focusedChangeSet.section_component_hierarchy) {
|
||||
// Display section component hierarchy for specific changeset
|
||||
return (
|
||||
<Tree
|
||||
data={focusedChangeSet.section_component_hierarchy}
|
||||
nodeClickHandler={onNodeClicked}
|
||||
selectedNodeIndexPath={selectedNodeIndexPath}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Waiting>
|
||||
<InfoBox>
|
||||
<Glyph
|
||||
name="face-unhappy"
|
||||
variant="outline"
|
||||
size={24}
|
||||
color={colors.light30}
|
||||
/>
|
||||
<InfoText>No data available...</InfoText>
|
||||
</InfoBox>
|
||||
</Waiting>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3958,11 +3958,6 @@ caseless@~0.12.0:
|
||||
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
|
||||
integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
|
||||
|
||||
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==
|
||||
|
||||
chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.1, chalk@^2.4.2:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
|
||||
@@ -4171,11 +4166,6 @@ clone@2.1.1:
|
||||
resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.1.tgz#d217d1e961118e3ac9a4b8bba3285553bf647cdb"
|
||||
integrity sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=
|
||||
|
||||
clone@^2.1.1:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f"
|
||||
integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=
|
||||
|
||||
co@^4.6.0:
|
||||
version "4.6.0"
|
||||
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
|
||||
@@ -4663,11 +4653,6 @@ d3-time@1:
|
||||
resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.1.0.tgz#b1e19d307dae9c900b7e5b25ffc5dcc249a8a0f1"
|
||||
integrity sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==
|
||||
|
||||
d3@3.5.17:
|
||||
version "3.5.17"
|
||||
resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.17.tgz#bc46748004378b21a360c9fc7cf5231790762fb8"
|
||||
integrity sha1-vEZ0gAQ3iyGjYMn8fPUjF5B2L7g=
|
||||
|
||||
damerau-levenshtein@^1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz#143c1641cb3d85c60c32329e26899adea8701791"
|
||||
@@ -4812,18 +4797,6 @@ decompress@^4.2.1:
|
||||
pify "^2.3.0"
|
||||
strip-dirs "^2.0.0"
|
||||
|
||||
deep-equal@^1.0.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a"
|
||||
integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==
|
||||
dependencies:
|
||||
is-arguments "^1.0.4"
|
||||
is-date-object "^1.0.1"
|
||||
is-regex "^1.0.4"
|
||||
object-is "^1.0.1"
|
||||
object-keys "^1.1.1"
|
||||
regexp.prototype.flags "^1.2.0"
|
||||
|
||||
deep-equal@^2.0.1:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.0.4.tgz#6b0b407a074666033169df3acaf128e1c6f3eab6"
|
||||
@@ -5017,7 +4990,7 @@ dom-align@^1.7.0:
|
||||
resolved "https://registry.yarnpkg.com/dom-align/-/dom-align-1.12.0.tgz#56fb7156df0b91099830364d2d48f88963f5a29c"
|
||||
integrity sha512-YkoezQuhp3SLFGdOlr5xkqZ640iXrnHAwVYcDg8ZKRUtO7mSzSC2BA5V0VuyAwPSJA4CLIc6EDDJh4bEsD2+zA==
|
||||
|
||||
dom-helpers@^3.2.0, dom-helpers@^3.4.0:
|
||||
dom-helpers@^3.4.0:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8"
|
||||
integrity sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==
|
||||
@@ -7274,7 +7247,7 @@ is-potential-custom-element-name@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz#0c52e54bcca391bb2c494b21e8626d7336c6e397"
|
||||
integrity sha1-DFLlS8yjkbssSUsh6GJtczbG45c=
|
||||
|
||||
is-regex@^1.0.4, is-regex@^1.1.1:
|
||||
is-regex@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9"
|
||||
integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==
|
||||
@@ -8699,7 +8672,7 @@ lolex@^5.0.0:
|
||||
dependencies:
|
||||
"@sinonjs/commons" "^1.7.0"
|
||||
|
||||
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
|
||||
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
|
||||
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==
|
||||
@@ -9521,7 +9494,7 @@ object-inspect@^1.8.0:
|
||||
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0"
|
||||
integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==
|
||||
|
||||
object-is@^1.0.1, object-is@^1.1.3:
|
||||
object-is@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.3.tgz#2e3b9e65560137455ee3bd62aec4d90a2ea1cc81"
|
||||
integrity sha512-teyqLvFWzLkq5B9ki8FVWA902UER2qkxmdA4nLf+wjOLAWgxzCWZNCxpDq9MvE8MmhWNr+I8w3BN49Vx36Y6Xg==
|
||||
@@ -10158,7 +10131,7 @@ prompts@^2.0.1:
|
||||
kleur "^3.0.3"
|
||||
sisteransi "^1.0.4"
|
||||
|
||||
prop-types@^15.0.0, prop-types@^15.5.10, prop-types@^15.5.6, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2:
|
||||
prop-types@^15.0.0, prop-types@^15.5.10, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2:
|
||||
version "15.7.2"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
|
||||
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
|
||||
@@ -10690,19 +10663,6 @@ react-color@^2.18.1:
|
||||
reactcss "^1.2.0"
|
||||
tinycolor2 "^1.4.1"
|
||||
|
||||
react-d3-tree@^1.16.1:
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/react-d3-tree/-/react-d3-tree-1.16.1.tgz#fca2f1096fd3040841406f0d4efbf4e752479ee9"
|
||||
integrity sha512-LybzIZM4f4A+/ao6U5OEAZVQbQ08w0wmMYA7TZvPRYQ0QZnSImK9PFTiGR0PW8hVWQqOVJtSZyXlYyJ+M+Uh+A==
|
||||
dependencies:
|
||||
clone "^2.1.1"
|
||||
d3 "3.5.17"
|
||||
deep-equal "^1.0.1"
|
||||
prop-types "^15.5.10"
|
||||
react-lifecycles-compat "^3.0.4"
|
||||
react-transition-group "^1.1.3"
|
||||
uuid "^3.0.1"
|
||||
|
||||
react-debounce-render@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/react-debounce-render/-/react-debounce-render-7.0.0.tgz#4e36df3a867b298ef64b6ab3b8bffb3d46d3055d"
|
||||
@@ -10850,17 +10810,6 @@ react-test-renderer@^17.0.1:
|
||||
react-shallow-renderer "^16.13.1"
|
||||
scheduler "^0.20.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"
|
||||
|
||||
react-transition-group@^2.5.0:
|
||||
version "2.9.0"
|
||||
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.9.0.tgz#df9cdb025796211151a436c69a8f3b97b5b07c8d"
|
||||
@@ -11135,7 +11084,7 @@ regex-not@^1.0.0, regex-not@^1.0.2:
|
||||
extend-shallow "^3.0.2"
|
||||
safe-regex "^1.1.0"
|
||||
|
||||
regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.3.0:
|
||||
regexp.prototype.flags@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75"
|
||||
integrity sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==
|
||||
@@ -12996,7 +12945,7 @@ uuid@3.2.1:
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14"
|
||||
integrity sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==
|
||||
|
||||
uuid@^3.0.1, uuid@^3.3.2:
|
||||
uuid@^3.3.2:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
|
||||
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
|
||||
@@ -13090,13 +13039,6 @@ walker@^1.0.7, walker@~1.0.5:
|
||||
dependencies:
|
||||
makeerror "1.0.x"
|
||||
|
||||
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"
|
||||
|
||||
warning@^4.0.1, warning@^4.0.3:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
|
||||
|
||||
Reference in New Issue
Block a user