mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
feature(code/frontend): main page structure tree (#27838)
This commit is contained in:
parent
274ad11caf
commit
1c001af11c
9 changed files with 151 additions and 61 deletions
|
@ -15,3 +15,6 @@ export interface SymbolsPayload {
|
|||
export const loadStructure = createAction<string>('LOAD STRUCTURE');
|
||||
export const loadStructureSuccess = createAction<SymbolsPayload>('LOAD STRUCTURE SUCCESS');
|
||||
export const loadStructureFailed = createAction<Error>('LOAD STRUCTURE FAILED');
|
||||
|
||||
export const openSymbolPath = createAction<string>('OPEN SYMBOL PATH');
|
||||
export const closeSymbolPath = createAction<string>('CLOSE SYMBOL PATH');
|
||||
|
|
|
@ -80,7 +80,7 @@ exports[`render correctly 1`] = `
|
|||
role="button"
|
||||
>
|
||||
<span
|
||||
className="sc-bdVaJa eSuqza"
|
||||
className="sc-bdVaJa fsxEnI"
|
||||
/>
|
||||
<svg
|
||||
className="euiIcon euiIcon--medium"
|
||||
|
@ -111,7 +111,7 @@ exports[`render correctly 1`] = `
|
|||
role="button"
|
||||
>
|
||||
<span
|
||||
className="sc-bdVaJa eSuqza"
|
||||
className="sc-bdVaJa fsxEnI"
|
||||
/>
|
||||
<svg
|
||||
className="euiIcon euiIcon--medium"
|
||||
|
@ -142,7 +142,7 @@ exports[`render correctly 1`] = `
|
|||
role="button"
|
||||
>
|
||||
<span
|
||||
className="sc-bwzfXH gzqToY"
|
||||
className="sc-bwzfXH kVxRUe sc-bdVaJa fsxEnI"
|
||||
/>
|
||||
<svg
|
||||
className="euiIcon euiIcon--medium"
|
||||
|
@ -175,7 +175,7 @@ exports[`render correctly 1`] = `
|
|||
role="button"
|
||||
>
|
||||
<span
|
||||
className="sc-bdVaJa eSuqza"
|
||||
className="sc-bdVaJa fsxEnI"
|
||||
/>
|
||||
<svg
|
||||
className="euiIcon euiIcon--medium"
|
||||
|
@ -241,7 +241,7 @@ exports[`render correctly 1`] = `
|
|||
role="button"
|
||||
>
|
||||
<span
|
||||
className="sc-bwzfXH gzqToY"
|
||||
className="sc-bwzfXH kVxRUe sc-bdVaJa fsxEnI"
|
||||
/>
|
||||
<svg
|
||||
className="euiIcon euiIcon--medium"
|
||||
|
@ -274,7 +274,7 @@ exports[`render correctly 1`] = `
|
|||
role="button"
|
||||
>
|
||||
<span
|
||||
className="sc-bdVaJa eSuqza"
|
||||
className="sc-bdVaJa fsxEnI"
|
||||
/>
|
||||
<svg
|
||||
className="euiIcon euiIcon--medium"
|
||||
|
@ -305,7 +305,7 @@ exports[`render correctly 1`] = `
|
|||
role="button"
|
||||
>
|
||||
<span
|
||||
className="sc-bdVaJa eSuqza"
|
||||
className="sc-bdVaJa fsxEnI"
|
||||
/>
|
||||
<svg
|
||||
className="euiIcon euiIcon--medium"
|
||||
|
@ -375,7 +375,7 @@ exports[`render correctly 1`] = `
|
|||
role="button"
|
||||
>
|
||||
<span
|
||||
className="sc-bdVaJa eSuqza"
|
||||
className="sc-bdVaJa fsxEnI"
|
||||
/>
|
||||
<svg
|
||||
className="euiIcon euiIcon--medium"
|
||||
|
@ -406,7 +406,7 @@ exports[`render correctly 1`] = `
|
|||
role="button"
|
||||
>
|
||||
<span
|
||||
className="sc-bdVaJa eSuqza"
|
||||
className="sc-bdVaJa fsxEnI"
|
||||
/>
|
||||
<svg
|
||||
className="euiIcon euiIcon--medium"
|
||||
|
@ -437,7 +437,7 @@ exports[`render correctly 1`] = `
|
|||
role="button"
|
||||
>
|
||||
<span
|
||||
className="sc-bdVaJa eSuqza"
|
||||
className="sc-bdVaJa fsxEnI"
|
||||
/>
|
||||
<svg
|
||||
className="euiIcon euiIcon--medium"
|
||||
|
@ -468,7 +468,7 @@ exports[`render correctly 1`] = `
|
|||
role="button"
|
||||
>
|
||||
<span
|
||||
className="sc-bdVaJa eSuqza"
|
||||
className="sc-bdVaJa fsxEnI"
|
||||
/>
|
||||
<svg
|
||||
className="euiIcon euiIcon--medium"
|
||||
|
@ -499,7 +499,7 @@ exports[`render correctly 1`] = `
|
|||
role="button"
|
||||
>
|
||||
<span
|
||||
className="sc-bdVaJa eSuqza"
|
||||
className="sc-bdVaJa fsxEnI"
|
||||
/>
|
||||
<svg
|
||||
className="euiIcon euiIcon--medium"
|
||||
|
|
|
@ -15,29 +15,7 @@ import { FileTree as Tree, FileTreeItemType } from '../../../model';
|
|||
import { closeTreePath, fetchRepoTree, FetchRepoTreePayload } from '../../actions';
|
||||
import { EuiSideNavItem, MainRouteParams, PathTypes } from '../../common/types';
|
||||
import { RootState } from '../../reducers';
|
||||
|
||||
const FolderClosedTriangle = styled.span`
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin-right: 4px;
|
||||
border: 6px solid transparent;
|
||||
border-left: 6px solid grey;
|
||||
border-right: 6px solid transparent;
|
||||
vertical-align: middle;
|
||||
`;
|
||||
|
||||
const FolderOpenTriangle = styled.span`
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin-right: 4px;
|
||||
margin-top: 3px;
|
||||
border: 6px solid transparent;
|
||||
border-top: 6px solid grey;
|
||||
border-bottom: none;
|
||||
vertical-align: middle;
|
||||
`;
|
||||
import { FolderClosedTriangle, FolderOpenTriangle } from '../shared';
|
||||
|
||||
const DirectoryNode = styled.span`
|
||||
margin-left: 4px;
|
||||
|
|
|
@ -44,7 +44,12 @@ class CodeSideTabs extends React.PureComponent<RouteComponentProps<MainRoutePara
|
|||
};
|
||||
|
||||
public get sideTab(): Tabs {
|
||||
const tab = parseQuery(this.props.location.search).tab;
|
||||
const { search } = this.props.location;
|
||||
let qs = search;
|
||||
if (search.charAt(0) === '?') {
|
||||
qs = search.substr(1);
|
||||
}
|
||||
const tab = parseQuery(qs).tab;
|
||||
return tab === Tabs.structure ? Tabs.structure : Tabs.file;
|
||||
}
|
||||
|
||||
|
|
23
x-pack/plugins/code/public/components/shared.tsx
Normal file
23
x-pack/plugins/code/public/components/shared.tsx
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const FolderClosedTriangle = styled.span`
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin-right: 4px;
|
||||
border: 6px solid transparent;
|
||||
border-left: 6px solid grey;
|
||||
vertical-align: middle;
|
||||
`;
|
||||
|
||||
export const FolderOpenTriangle = styled(FolderClosedTriangle)`
|
||||
margin-top: 3px;
|
||||
border-top: 6px solid grey;
|
||||
border-left: 6px solid transparent;
|
||||
`;
|
|
@ -1,3 +0,0 @@
|
|||
.symbolLinkContainer {
|
||||
padding: 0.5rem 0;
|
||||
}
|
|
@ -4,19 +4,38 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiSideNav } from '@elastic/eui';
|
||||
import { EuiFlexGroup, EuiSideNav, EuiText, EuiToken } from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Location } from 'vscode-languageserver-types/lib/esm/main';
|
||||
import styled from 'styled-components';
|
||||
import { Location, SymbolKind } from 'vscode-languageserver-types/lib/esm/main';
|
||||
import { RepositoryUtils } from '../../../common/repository_utils';
|
||||
import { closeSymbolPath, openSymbolPath } from '../../actions';
|
||||
import { EuiSideNavItem } from '../../common/types';
|
||||
import { RootState } from '../../reducers';
|
||||
import { SymbolWithMembers } from '../../reducers/symbol';
|
||||
import { structureSelector } from '../../selectors';
|
||||
import { FolderClosedTriangle, FolderOpenTriangle } from '../shared';
|
||||
|
||||
const Root = styled(EuiSideNav)`
|
||||
padding: 20px 16px;
|
||||
overflow: auto;
|
||||
`;
|
||||
|
||||
const Symbol = styled(EuiFlexGroup)`
|
||||
margin-bottom: 8px;
|
||||
`;
|
||||
|
||||
const Token = styled(EuiToken)`
|
||||
margin-right: 6px;
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
structureTree: SymbolWithMembers[];
|
||||
openPaths: string[];
|
||||
openSymbolPath: (p: string) => void;
|
||||
closeSymbolPath: (p: string) => void;
|
||||
}
|
||||
|
||||
const sortSymbol = (a: SymbolWithMembers, b: SymbolWithMembers) => {
|
||||
|
@ -28,10 +47,30 @@ const sortSymbol = (a: SymbolWithMembers, b: SymbolWithMembers) => {
|
|||
}
|
||||
};
|
||||
class CodeSymbolTree extends React.PureComponent<Props> {
|
||||
public getStructureTreeItemRenderer = (location: Location, name: string) => () => (
|
||||
<div className="symbolLinkContainer">
|
||||
<Link to={RepositoryUtils.locationToUrl(location)}>{name}</Link>
|
||||
</div>
|
||||
public getStructureTreeItemRenderer = (
|
||||
location: Location,
|
||||
name: string,
|
||||
kind: SymbolKind,
|
||||
isContainer: boolean = false,
|
||||
forceOpen: boolean = false,
|
||||
path: string = ''
|
||||
) => () => (
|
||||
<Symbol gutterSize="none" alignItems="center">
|
||||
{isContainer &&
|
||||
(forceOpen ? (
|
||||
<FolderOpenTriangle onClick={() => this.props.closeSymbolPath(path)} />
|
||||
) : (
|
||||
<FolderClosedTriangle onClick={() => this.props.openSymbolPath(path)} />
|
||||
))}
|
||||
{/*
|
||||
// @ts-ignore */}
|
||||
<Token iconType={`token${Object.keys(SymbolKind).find(k => SymbolKind[k] === kind)}`} />
|
||||
<Link to={RepositoryUtils.locationToUrl(location)}>
|
||||
<EuiText>
|
||||
<strong>{name}</strong>
|
||||
</EuiText>
|
||||
</Link>
|
||||
</Symbol>
|
||||
);
|
||||
|
||||
public symbolsToSideNavItems = (symbolsWithMembers: SymbolWithMembers[]): EuiSideNavItem[] => {
|
||||
|
@ -39,12 +78,27 @@ class CodeSymbolTree extends React.PureComponent<Props> {
|
|||
const item: EuiSideNavItem = {
|
||||
name: s.name,
|
||||
id: `${s.name}_${index}`,
|
||||
renderItem: this.getStructureTreeItemRenderer(s.location, s.name),
|
||||
onClick: () => void 0,
|
||||
};
|
||||
if (s.members) {
|
||||
item.items = this.symbolsToSideNavItems(Array.from(s.members));
|
||||
item.forceOpen = true;
|
||||
item.forceOpen = this.props.openPaths.includes(s.path!);
|
||||
item.renderItem = this.getStructureTreeItemRenderer(
|
||||
s.location,
|
||||
s.name,
|
||||
s.kind,
|
||||
true,
|
||||
item.forceOpen,
|
||||
s.path
|
||||
);
|
||||
} else {
|
||||
item.renderItem = this.getStructureTreeItemRenderer(
|
||||
s.location,
|
||||
s.name,
|
||||
s.kind,
|
||||
false,
|
||||
false
|
||||
);
|
||||
}
|
||||
return item;
|
||||
});
|
||||
|
@ -54,12 +108,21 @@ class CodeSymbolTree extends React.PureComponent<Props> {
|
|||
const items = [
|
||||
{ name: '', id: '', items: this.symbolsToSideNavItems(this.props.structureTree) },
|
||||
];
|
||||
return <EuiSideNav items={items} style={{ overflow: 'auto' }} className="sideNavTree" />;
|
||||
return <Root items={items} />;
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: RootState) => {
|
||||
return { structureTree: structureSelector(state) };
|
||||
const mapStateToProps = (state: RootState) => ({
|
||||
structureTree: structureSelector(state),
|
||||
openPaths: state.symbol.openPaths,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = {
|
||||
openSymbolPath,
|
||||
closeSymbolPath,
|
||||
};
|
||||
|
||||
export const SymbolTree = connect(mapStateToProps)(CodeSymbolTree);
|
||||
export const SymbolTree = connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(CodeSymbolTree);
|
||||
|
|
|
@ -10,9 +10,11 @@ import { Action, handleActions } from 'redux-actions';
|
|||
|
||||
import { Location, SymbolInformation } from 'vscode-languageserver-types/lib/esm/main';
|
||||
import {
|
||||
closeSymbolPath,
|
||||
loadStructure,
|
||||
loadStructureFailed,
|
||||
loadStructureSuccess,
|
||||
openSymbolPath,
|
||||
SymbolsPayload,
|
||||
} from '../actions';
|
||||
|
||||
|
@ -20,7 +22,8 @@ const SPECIAL_SYMBOL_NAME = '{...}';
|
|||
const SPECIAL_CONTAINER_NAME = '';
|
||||
|
||||
export interface SymbolWithMembers extends SymbolInformation {
|
||||
members?: Set<SymbolInformation>;
|
||||
members?: Set<SymbolWithMembers>;
|
||||
path?: string;
|
||||
}
|
||||
|
||||
type Container = SymbolWithMembers | undefined;
|
||||
|
@ -31,12 +34,14 @@ export interface SymbolState {
|
|||
error?: Error;
|
||||
loading: boolean;
|
||||
lastRequestPath?: string;
|
||||
openPaths: string[];
|
||||
}
|
||||
|
||||
const initialState: SymbolState = {
|
||||
symbols: {},
|
||||
loading: false,
|
||||
structureTree: {},
|
||||
openPaths: [],
|
||||
};
|
||||
|
||||
const generateStructureTree: (symbols: SymbolInformation[]) => any = symbols => {
|
||||
|
@ -48,13 +53,13 @@ const generateStructureTree: (symbols: SymbolInformation[]) => any = symbols =>
|
|||
character: oneLocationStartCharacter,
|
||||
} = oneLocation.range.start;
|
||||
const {
|
||||
line: anotherLocationEndLine,
|
||||
character: anotherLocationEndCharacter,
|
||||
} = anotherLocation.range.end;
|
||||
line: anotherLocationStartLine,
|
||||
character: anotherLocationStartCharacter,
|
||||
} = anotherLocation.range.start;
|
||||
return (
|
||||
oneLocationStartLine > anotherLocationEndLine ||
|
||||
(oneLocationStartLine === anotherLocationEndLine &&
|
||||
oneLocationStartCharacter >= anotherLocationEndCharacter)
|
||||
oneLocationStartLine > anotherLocationStartLine ||
|
||||
(oneLocationStartLine === anotherLocationStartLine &&
|
||||
oneLocationStartCharacter >= anotherLocationStartCharacter)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -79,10 +84,13 @@ const generateStructureTree: (symbols: SymbolInformation[]) => any = symbols =>
|
|||
container = findContainer(s.containerName || '', s.location);
|
||||
}
|
||||
if (container) {
|
||||
if (!container.path) {
|
||||
container.path = container.name;
|
||||
}
|
||||
if (container.members) {
|
||||
container.members.add(s);
|
||||
container.members.add({ ...s, path: `${container.path}/${s.name}` });
|
||||
} else {
|
||||
container.members = new Set([s]);
|
||||
container.members = new Set([{ ...s, path: `${container.path}/${s.name}` }]);
|
||||
}
|
||||
} else {
|
||||
structureTree.push(s);
|
||||
|
@ -119,6 +127,20 @@ export const symbol = handleActions(
|
|||
return state;
|
||||
}
|
||||
},
|
||||
[String(openSymbolPath)]: (state: SymbolState, action: any) =>
|
||||
produce<SymbolState>(state, draft => {
|
||||
const path = action.payload!;
|
||||
if (!state.openPaths.includes(path)) {
|
||||
draft.openPaths.push(path);
|
||||
}
|
||||
}),
|
||||
[String(closeSymbolPath)]: (state: SymbolState, action: any) =>
|
||||
produce<SymbolState>(state, draft => {
|
||||
const idx = state.openPaths.indexOf(action.payload!);
|
||||
if (idx >= 0) {
|
||||
draft.openPaths.splice(idx, 1);
|
||||
}
|
||||
}),
|
||||
},
|
||||
initialState
|
||||
);
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
@import "./components/file_tree/file_tree.scss";
|
||||
@import "./components/file_tree/override_eui_side_nav_styles.scss";
|
||||
@import "./components/admin_page/admin.scss";
|
||||
@import "./components/symbol_tree/symbol_tree.scss";
|
||||
@import "./components/editor/references_panel.scss";
|
||||
@import "./monaco/monaco.scss";
|
||||
@import "./monaco/override_monaco_styles.scss";
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue