mirror of https://github.com/onivim/oni.git
Feature/ switch explorer rendering to use react-virtualized (#2182)
* experiment with changing scrollIntoView fn in explorer * switch to using react virtualized to render explorer * add react virtualised list and typescript Styled components plugin * add missing style item to rowrenderer which fixes scrolling * add key as per docs example * update snapshot test for explorer * add ability to dynamically calculate row with inputs * remove overscan rows * re-add overscan re-trigger build * use ui.fontSize for row sizing * destructure vars (primarily to trigger re-test) * switch scroll to alignment to end as center causes scroll lag * add font size in px function switch to cell measurer * add the definition file for units-css * update jest mocks * remove shadowed variable in explorer * update react-virtualized and its types to fix type errors * prettier lint fix for performance config * lint fix * merge upstream * fix double border in sidebar item component also recalculate height when component yanrk status changes * separate should measure calculation into function add iss selected check to it * restructure comparison function to be more readable and work hopefully * use cache clear and recompute row height for list resizing * add is or was selected to props checked component did update check * remove outline from List, separate out should measure * switch to using _oni in explorer split * prettier fix * fix missing space on left edge of container component add helper for quickly adding types for styled component oni props * remove unnecessary type annotation * use pixel function from common in sidebar items * fix lint error * remove unnecessary dependency and associated utility use more thorough method for checking if props changed simplify sidebar items components * fix lint error * remove references to unused fontsize prop * remove measurement from node entirely switch to invalidating the cache if yanking, pasting etc occur
This commit is contained in:
parent
f5ca695205
commit
c06df641ff
|
@ -79,7 +79,7 @@ const DragCollect = (connect: DND.DragSourceConnector, monitor: DND.DragSourceMo
|
|||
*
|
||||
* @name props
|
||||
* @function
|
||||
* @param {String | String[]} >props.target The target Type that responds to the drop
|
||||
* @param {String | String[]} props.target The target Type that responds to the drop
|
||||
* @param {Object} DragSource Object with a beginDrag which return the dragged props
|
||||
* @param {React.Component} A component which is dragged onto another
|
||||
* @returns {React.Component<P>} A react class component
|
||||
|
|
|
@ -84,9 +84,9 @@ export class ExplorerSplit {
|
|||
onCompleteCreate={this._completeCreation}
|
||||
onCompleteRename={this._completeRename}
|
||||
onCancelRename={this._cancelRename}
|
||||
onSelectionChanged={id => this._onSelectionChanged(id)}
|
||||
onClick={id => this._onOpenItem(id)}
|
||||
moveFileOrFolder={this.moveFileOrFolder}
|
||||
onSelectionChanged={id => this._onSelectionChanged(id)}
|
||||
/>
|
||||
</Provider>
|
||||
)
|
||||
|
|
|
@ -7,17 +7,20 @@ import * as React from "react"
|
|||
import * as DND from "react-dnd"
|
||||
import HTML5Backend from "react-dnd-html5-backend"
|
||||
import { connect } from "react-redux"
|
||||
import { AutoSizer, CellMeasurer, CellMeasurerCache, List } from "react-virtualized"
|
||||
import { compose } from "redux"
|
||||
|
||||
import { CSSTransition, TransitionGroup } from "react-transition-group"
|
||||
|
||||
import { css, styled } from "./../../UI/components/common"
|
||||
import { css, enableMouse, styled } from "./../../UI/components/common"
|
||||
import { TextInputView } from "./../../UI/components/LightweightText"
|
||||
import { SidebarEmptyPaneView } from "./../../UI/components/SidebarEmptyPaneView"
|
||||
import { SidebarContainerView, SidebarItemView } from "./../../UI/components/SidebarItemView"
|
||||
import { Sneakable } from "./../../UI/components/Sneakable"
|
||||
import { VimNavigator } from "./../../UI/components/VimNavigator"
|
||||
import { DragAndDrop, Droppeable } from "./../DragAndDrop"
|
||||
|
||||
import { commandManager } from "./../CommandManager"
|
||||
import { FileIcon } from "./../FileIcon"
|
||||
|
||||
import * as ExplorerSelectors from "./ExplorerSelectors"
|
||||
|
@ -38,20 +41,9 @@ export interface INodeViewProps {
|
|||
updated?: string[]
|
||||
isRenaming: Node
|
||||
isCreating: boolean
|
||||
children?: React.ReactNode
|
||||
}
|
||||
|
||||
export const NodeWrapper = styled.div`
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
`
|
||||
|
||||
// tslint:disable-next-line
|
||||
const noop = (elem: HTMLElement) => {}
|
||||
const scrollIntoViewIfNeeded = (elem: HTMLElement) => {
|
||||
// tslint:disable-next-line
|
||||
elem && elem["scrollIntoViewIfNeeded"] && elem["scrollIntoViewIfNeeded"]()
|
||||
}
|
||||
const stopPropagation = (fn: () => void) => {
|
||||
return (e?: React.MouseEvent<HTMLElement>) => {
|
||||
if (e) {
|
||||
|
@ -75,6 +67,13 @@ interface IMoveNode {
|
|||
}
|
||||
}
|
||||
|
||||
export const NodeWrapper = styled.div`
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
`
|
||||
|
||||
const NodeTransitionWrapper = styled.div`
|
||||
transition: all 400ms 50ms ease-in-out;
|
||||
|
||||
|
@ -94,12 +93,6 @@ interface ITransitionProps {
|
|||
updated: boolean
|
||||
}
|
||||
|
||||
const Transition = ({ children, updated }: ITransitionProps) => (
|
||||
<CSSTransition in={updated} classNames="move" timeout={1000}>
|
||||
<NodeTransitionWrapper className={updated && "move"}>{children}</NodeTransitionWrapper>
|
||||
</CSSTransition>
|
||||
)
|
||||
|
||||
const renameStyles = css`
|
||||
width: 100%;
|
||||
background-color: inherit;
|
||||
|
@ -116,7 +109,13 @@ const createStyles = css`
|
|||
margin-top: 0.2em;
|
||||
`
|
||||
|
||||
export class NodeView extends React.PureComponent<INodeViewProps, {}> {
|
||||
const Transition = ({ children, updated }: ITransitionProps) => (
|
||||
<CSSTransition in={updated} classNames="move" timeout={1000}>
|
||||
<NodeTransitionWrapper className={updated && "move"}>{children}</NodeTransitionWrapper>
|
||||
</CSSTransition>
|
||||
)
|
||||
|
||||
export class NodeView extends React.PureComponent<INodeViewProps> {
|
||||
public moveFileOrFolder = ({ drag, drop }: IMoveNode) => {
|
||||
this.props.moveFileOrFolder(drag.node, drop.node)
|
||||
}
|
||||
|
@ -125,15 +124,12 @@ export class NodeView extends React.PureComponent<INodeViewProps, {}> {
|
|||
return !(drag.node.name === drop.node.name)
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
public render() {
|
||||
const { isCreating, isRenaming, isSelected, node } = this.props
|
||||
const renameInProgress = isRenaming.name === node.name && isSelected && !isCreating
|
||||
const creationInProgress = isCreating && isSelected && !renameInProgress
|
||||
return (
|
||||
<NodeWrapper
|
||||
style={{ cursor: "pointer" }}
|
||||
innerRef={this.props.isSelected ? scrollIntoViewIfNeeded : noop}
|
||||
>
|
||||
<NodeWrapper>
|
||||
{renameInProgress ? (
|
||||
<TextInputView
|
||||
styles={renameStyles}
|
||||
|
@ -159,7 +155,7 @@ export class NodeView extends React.PureComponent<INodeViewProps, {}> {
|
|||
public hasUpdated = (path: string) =>
|
||||
!!this.props.updated && this.props.updated.some(nodePath => nodePath === path)
|
||||
|
||||
public getElement(): JSX.Element {
|
||||
public getElement() {
|
||||
const { node } = this.props
|
||||
const yanked = this.props.yanked.includes(node.id)
|
||||
|
||||
|
@ -270,13 +266,67 @@ export interface IExplorerViewProps extends IExplorerViewContainerProps {
|
|||
idToSelect: string
|
||||
}
|
||||
|
||||
import { SidebarEmptyPaneView } from "./../../UI/components/SidebarEmptyPaneView"
|
||||
interface ISneakableNode extends IExplorerViewProps {
|
||||
node: Node
|
||||
selectedId: string
|
||||
}
|
||||
|
||||
import { commandManager } from "./../CommandManager"
|
||||
const SneakableNode = ({ node, selectedId, ...props }: ISneakableNode) => (
|
||||
<Sneakable callback={() => props.onClick(node.id)}>
|
||||
<NodeView
|
||||
node={node}
|
||||
isSelected={node.id === selectedId}
|
||||
isCreating={props.isCreating}
|
||||
onCancelCreate={props.onCancelCreate}
|
||||
onCompleteCreate={props.onCompleteCreate}
|
||||
onCompleteRename={props.onCompleteRename}
|
||||
isRenaming={props.isRenaming}
|
||||
onCancelRename={props.onCancelRename}
|
||||
updated={props.updated}
|
||||
yanked={props.yanked}
|
||||
moveFileOrFolder={props.moveFileOrFolder}
|
||||
onClick={() => props.onClick(node.id)}
|
||||
/>
|
||||
</Sneakable>
|
||||
)
|
||||
|
||||
export class ExplorerView extends React.PureComponent<IExplorerViewProps, {}> {
|
||||
public render(): JSX.Element {
|
||||
const ExplorerContainer = styled.div`
|
||||
height: 100%;
|
||||
${enableMouse};
|
||||
`
|
||||
|
||||
export class ExplorerView extends React.PureComponent<IExplorerViewProps> {
|
||||
private _list = React.createRef<List>()
|
||||
|
||||
private _cache = new CellMeasurerCache({
|
||||
defaultHeight: 30,
|
||||
fixedWidth: true,
|
||||
})
|
||||
|
||||
public openWorkspaceFolder = () => {
|
||||
commandManager.executeCommand("workspace.openFolder")
|
||||
}
|
||||
|
||||
public getSelectedNode = (selectedId: string) => {
|
||||
return this.props.nodes.findIndex(n => selectedId === n.id)
|
||||
}
|
||||
|
||||
public propsChanged(keys: Array<keyof IExplorerViewProps>, prevProps: IExplorerViewProps) {
|
||||
return keys.some(prop => this.props[prop] !== prevProps[prop])
|
||||
}
|
||||
|
||||
public componentDidUpdate(prevProps: IExplorerViewProps) {
|
||||
if (this.propsChanged(["isCreating", "isRenaming", "yanked"], prevProps)) {
|
||||
// TODO: if we could determine which nodes actually were involved
|
||||
// in the change this could potentially be optimised
|
||||
this._cache.clearAll()
|
||||
this._list.current.recomputeRowHeights()
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
const ids = this.props.nodes.map(node => node.id)
|
||||
const isActive = this.props.isActive && !this.props.isRenaming && !this.props.isCreating
|
||||
|
||||
if (!this.props.nodes || !this.props.nodes.length) {
|
||||
return (
|
||||
|
@ -284,43 +334,57 @@ export class ExplorerView extends React.PureComponent<IExplorerViewProps, {}> {
|
|||
active={this.props.isActive}
|
||||
contentsText="Nothing to show here, yet!"
|
||||
actionButtonText="Open a Folder"
|
||||
onClickButton={() => commandManager.executeCommand("workspace.openFolder")}
|
||||
onClickButton={this.openWorkspaceFolder}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<TransitionGroup>
|
||||
<TransitionGroup style={{ height: "100%" }}>
|
||||
<VimNavigator
|
||||
ids={ids}
|
||||
active={this.props.isActive && !this.props.isRenaming && !this.props.isCreating}
|
||||
active={isActive}
|
||||
style={{ height: "100%" }}
|
||||
idToSelect={this.props.idToSelect}
|
||||
onSelectionChanged={this.props.onSelectionChanged}
|
||||
onSelected={id => this.props.onClick(id)}
|
||||
render={(selectedId: string) => {
|
||||
const nodes = this.props.nodes.map(node => (
|
||||
<Sneakable callback={() => this.props.onClick(node.id)} key={node.id}>
|
||||
<NodeView
|
||||
node={node}
|
||||
isSelected={node.id === selectedId}
|
||||
isCreating={this.props.isCreating}
|
||||
onCancelCreate={this.props.onCancelCreate}
|
||||
onCompleteCreate={this.props.onCompleteCreate}
|
||||
onCompleteRename={this.props.onCompleteRename}
|
||||
isRenaming={this.props.isRenaming}
|
||||
onCancelRename={this.props.onCancelRename}
|
||||
updated={this.props.updated}
|
||||
yanked={this.props.yanked}
|
||||
moveFileOrFolder={this.props.moveFileOrFolder}
|
||||
onClick={() => this.props.onClick(node.id)}
|
||||
/>
|
||||
</Sneakable>
|
||||
))
|
||||
|
||||
onSelectionChanged={this.props.onSelectionChanged}
|
||||
render={selectedId => {
|
||||
return (
|
||||
<div className="explorer enable-mouse">
|
||||
<div className="items">{nodes}</div>
|
||||
</div>
|
||||
<ExplorerContainer className="explorer">
|
||||
<AutoSizer>
|
||||
{measurements => (
|
||||
<List
|
||||
{...measurements}
|
||||
ref={this._list}
|
||||
scrollToAlignment="end"
|
||||
style={{ outline: "none" }}
|
||||
rowCount={this.props.nodes.length}
|
||||
rowHeight={this._cache.rowHeight}
|
||||
scrollToIndex={this.getSelectedNode(selectedId)}
|
||||
rowRenderer={({ index, style, key, parent }) => (
|
||||
<CellMeasurer
|
||||
key={key}
|
||||
cache={this._cache}
|
||||
columnIndex={0}
|
||||
parent={parent}
|
||||
rowIndex={index}
|
||||
>
|
||||
<div
|
||||
style={style}
|
||||
key={this.props.nodes[index].id}
|
||||
>
|
||||
<SneakableNode
|
||||
{...this.props}
|
||||
selectedId={selectedId}
|
||||
node={this.props.nodes[index]}
|
||||
/>
|
||||
</div>
|
||||
</CellMeasurer>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</AutoSizer>
|
||||
</ExplorerContainer>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
|
@ -329,6 +393,19 @@ export class ExplorerView extends React.PureComponent<IExplorerViewProps, {}> {
|
|||
}
|
||||
}
|
||||
|
||||
const getIdToSelect = (fileToSelect: string, nodes: ExplorerSelectors.ExplorerNode[]) => {
|
||||
// If parent has told us to select a file, attempt to convert the file path into a node ID.
|
||||
if (fileToSelect) {
|
||||
const [nodeToSelect] = nodes.filter(node => {
|
||||
const nodePath = getPathForNode(node)
|
||||
return nodePath === fileToSelect
|
||||
})
|
||||
|
||||
return nodeToSelect ? nodeToSelect.id : null
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const mapStateToProps = (
|
||||
state: IExplorerState,
|
||||
containerProps: IExplorerViewContainerProps,
|
||||
|
@ -341,25 +418,13 @@ const mapStateToProps = (
|
|||
|
||||
const nodes: ExplorerSelectors.ExplorerNode[] = ExplorerSelectors.mapStateToNodeList(state)
|
||||
|
||||
let idToSelect: string = null
|
||||
// If parent has told us to select a file, attempt to convert the file path into a node ID.
|
||||
if (fileToSelect) {
|
||||
const [nodeToSelect] = nodes.filter((node: ExplorerSelectors.ExplorerNode) => {
|
||||
const nodePath: string = getPathForNode(node)
|
||||
return nodePath === fileToSelect
|
||||
})
|
||||
if (nodeToSelect) {
|
||||
idToSelect = nodeToSelect.id
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...containerProps,
|
||||
isActive: state.hasFocus,
|
||||
nodes,
|
||||
updated,
|
||||
yanked,
|
||||
idToSelect,
|
||||
idToSelect: getIdToSelect(fileToSelect, nodes),
|
||||
isCreating: state.register.create.active,
|
||||
isRenaming: rename.active && rename.target,
|
||||
}
|
||||
|
|
|
@ -127,8 +127,8 @@ export class SidebarHeaderView extends React.PureComponent<ISidebarHeaderProps,
|
|||
|
||||
export const SidebarInnerPaneWrapper = withProps<{}>(styled.div)`
|
||||
flex: 1 1 auto;
|
||||
overflow-y: auto;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
`
|
||||
|
||||
export class SidebarContentView extends React.PureComponent<
|
||||
|
|
|
@ -6,34 +6,53 @@
|
|||
|
||||
import * as React from "react"
|
||||
|
||||
import { styled, withProps } from "./common"
|
||||
import { OniStyledProps, pixel, styled, withProps } from "./common"
|
||||
|
||||
import Caret from "./../../UI/components/Caret"
|
||||
import { Sneakable } from "./../../UI/components/Sneakable"
|
||||
|
||||
export interface ISidebarItemViewProps {
|
||||
interface IItemProps {
|
||||
yanked?: boolean
|
||||
updated?: boolean
|
||||
isOver?: boolean
|
||||
canDrop?: boolean
|
||||
didDrop?: boolean
|
||||
text: string | JSX.Element
|
||||
isFocused: boolean
|
||||
isContainer?: boolean
|
||||
indentationLevel: number
|
||||
icon?: JSX.Element
|
||||
text: string | JSX.Element
|
||||
onClick: (e?: React.MouseEvent<HTMLElement>) => void
|
||||
}
|
||||
|
||||
const px = (num: number): string => num.toString() + "px"
|
||||
export interface ISidebarItemViewProps extends IItemProps {
|
||||
indentationLevel: number
|
||||
}
|
||||
|
||||
export interface ISidebarContainerViewProps extends IItemProps {
|
||||
isExpanded: boolean
|
||||
indentationLevel?: number
|
||||
}
|
||||
|
||||
type SidebarStyleProps = OniStyledProps<ISidebarItemViewProps>
|
||||
|
||||
const INDENT_AMOUNT = 12
|
||||
|
||||
const getLeftBorder = (props: SidebarStyleProps) => {
|
||||
switch (true) {
|
||||
case props.isFocused:
|
||||
return `4px solid ${props.theme["highlight.mode.normal.background"]}`
|
||||
case !props.isContainer:
|
||||
return "4px solid transparent"
|
||||
case props.isContainer:
|
||||
return `4px solid rgba(0, 0, 0, 0.2)`
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
const SidebarItemStyleWrapper = withProps<ISidebarItemViewProps>(styled.div)`
|
||||
padding-left: ${props => px(INDENT_AMOUNT * props.indentationLevel)};
|
||||
border-left: ${props =>
|
||||
props.isFocused
|
||||
? `4px solid ${props.theme["highlight.mode.normal.background"]}`
|
||||
: "4px solid transparent"};
|
||||
|
||||
padding-left: ${props => pixel(INDENT_AMOUNT * props.indentationLevel)};
|
||||
border-left: ${getLeftBorder};
|
||||
${p =>
|
||||
(p.isOver || p.yanked) &&
|
||||
`border: 3px solid ${p.theme["highlight.mode.insert.background"]};`};
|
||||
|
@ -44,7 +63,6 @@ const SidebarItemStyleWrapper = withProps<ISidebarItemViewProps>(styled.div)`
|
|||
padding-top: 4px;
|
||||
padding-bottom: 3px;
|
||||
position: relative;
|
||||
|
||||
cursor: pointer;
|
||||
pointer-events: all;
|
||||
|
||||
|
@ -63,18 +81,19 @@ const SidebarItemStyleWrapper = withProps<ISidebarItemViewProps>(styled.div)`
|
|||
}
|
||||
`
|
||||
|
||||
const SidebarItemBackground = withProps<ISidebarItemViewProps>(styled.div)`
|
||||
background-color: ${props => {
|
||||
if (props.isFocused && !props.isContainer) {
|
||||
return props.theme["highlight.mode.normal.background"]
|
||||
} else if (props.isContainer) {
|
||||
return "rgb(0, 0, 0)"
|
||||
} else {
|
||||
return "transparent"
|
||||
}
|
||||
}};
|
||||
opacity: ${props => (props.isContainer || props.isFocused ? "0.2" : "0")};
|
||||
const getSidebarBackground = (props: SidebarStyleProps) => {
|
||||
if (props.isFocused && !props.isContainer) {
|
||||
return props.theme["highlight.mode.normal.background"]
|
||||
} else if (props.isContainer) {
|
||||
return "rgb(0, 0, 0)"
|
||||
} else {
|
||||
return "transparent"
|
||||
}
|
||||
}
|
||||
|
||||
const SidebarItemBackground = withProps<ISidebarItemViewProps>(styled.div)`
|
||||
background-color: ${getSidebarBackground};
|
||||
opacity: ${props => (props.isContainer || props.isFocused ? "0.2" : "0")};
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
|
@ -82,76 +101,38 @@ const SidebarItemBackground = withProps<ISidebarItemViewProps>(styled.div)`
|
|||
bottom: 0px;
|
||||
`
|
||||
|
||||
const INDENT_AMOUNT = 12
|
||||
|
||||
export class SidebarItemView extends React.PureComponent<ISidebarItemViewProps, {}> {
|
||||
public render(): JSX.Element {
|
||||
const icon = this.props.icon ? <div className="icon">{this.props.icon}</div> : null
|
||||
return (
|
||||
<Sneakable callback={this.props.onClick}>
|
||||
<SidebarItemStyleWrapper
|
||||
{...this.props}
|
||||
className="item"
|
||||
onClick={this.props.onClick}
|
||||
>
|
||||
<SidebarItemBackground {...this.props} />
|
||||
{icon}
|
||||
<div className="name">{this.props.text}</div>
|
||||
</SidebarItemStyleWrapper>
|
||||
</Sneakable>
|
||||
)
|
||||
}
|
||||
export const SidebarItemView: React.SFC<ISidebarItemViewProps> = props => {
|
||||
const icon = props.icon ? <div className="icon">{props.icon}</div> : null
|
||||
return (
|
||||
<Sneakable callback={props.onClick}>
|
||||
<SidebarItemStyleWrapper {...props} className="item" onClick={props.onClick}>
|
||||
<SidebarItemBackground {...props} />
|
||||
{icon}
|
||||
<div className="name">{props.text}</div>
|
||||
</SidebarItemStyleWrapper>
|
||||
</Sneakable>
|
||||
)
|
||||
}
|
||||
const SidebarContainer = styled.div``
|
||||
|
||||
export interface ISidebarContainerViewProps extends IContainerProps {
|
||||
yanked?: boolean
|
||||
updated?: boolean
|
||||
didDrop?: boolean
|
||||
text: string
|
||||
isExpanded: boolean
|
||||
isFocused: boolean
|
||||
indentationLevel?: number
|
||||
isContainer?: boolean
|
||||
onClick: (e: React.MouseEvent<HTMLElement>) => void
|
||||
}
|
||||
|
||||
interface IContainerProps {
|
||||
isOver?: boolean
|
||||
canDrop?: boolean
|
||||
yanked?: boolean
|
||||
updated?: boolean
|
||||
}
|
||||
|
||||
const SidebarContainer = withProps<IContainerProps>(styled.div)`
|
||||
${p =>
|
||||
(p.isOver || p.yanked) &&
|
||||
`border: 3px solid ${p.theme["highlight.mode.insert.background"]};`};
|
||||
`
|
||||
|
||||
export class SidebarContainerView extends React.PureComponent<ISidebarContainerViewProps, {}> {
|
||||
public render(): JSX.Element {
|
||||
const indentationlevel = this.props.indentationLevel || 0
|
||||
|
||||
return (
|
||||
<SidebarContainer
|
||||
updated={this.props.updated}
|
||||
yanked={this.props.yanked}
|
||||
canDrop={this.props.canDrop}
|
||||
isOver={this.props.isOver}
|
||||
>
|
||||
<SidebarItemView
|
||||
yanked={this.props.yanked}
|
||||
updated={this.props.updated}
|
||||
didDrop={this.props.didDrop}
|
||||
indentationLevel={indentationlevel}
|
||||
icon={<Caret active={this.props.isExpanded} />}
|
||||
text={this.props.text}
|
||||
isFocused={this.props.isFocused}
|
||||
isContainer={this.props.isContainer}
|
||||
onClick={this.props.onClick}
|
||||
/>
|
||||
{this.props.isExpanded ? this.props.children : null}
|
||||
</SidebarContainer>
|
||||
)
|
||||
}
|
||||
export const SidebarContainerView: React.SFC<ISidebarContainerViewProps> = ({
|
||||
indentationLevel = 0,
|
||||
...props
|
||||
}) => {
|
||||
return (
|
||||
<SidebarContainer>
|
||||
<SidebarItemView
|
||||
yanked={props.yanked}
|
||||
updated={props.updated}
|
||||
didDrop={props.didDrop}
|
||||
indentationLevel={indentationLevel}
|
||||
icon={<Caret active={props.isExpanded} />}
|
||||
text={props.text}
|
||||
isFocused={props.isFocused}
|
||||
isContainer={props.isContainer}
|
||||
onClick={props.onClick}
|
||||
/>
|
||||
{props.isExpanded ? props.children : null}
|
||||
</SidebarContainer>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ import { Event } from "oni-types"
|
|||
import { KeyboardInputView } from "./../../Input/KeyboardInput"
|
||||
import { getInstance, IMenuBinding } from "./../../neovim/SharedNeovimInstance"
|
||||
|
||||
import styled from "./../../UI/components/common"
|
||||
|
||||
import { CallbackCommand, commandManager } from "./../../Services/CommandManager"
|
||||
|
||||
export interface IVimNavigatorProps {
|
||||
|
@ -42,6 +44,10 @@ export interface IVimNavigatorState {
|
|||
selectedId: string
|
||||
}
|
||||
|
||||
const NavigatorContainer = styled.div`
|
||||
height: 100%;
|
||||
`
|
||||
|
||||
export class VimNavigator extends React.PureComponent<IVimNavigatorProps, IVimNavigatorState> {
|
||||
private _activeBinding: IMenuBinding = null
|
||||
private _activateEvent = new Event<void>()
|
||||
|
@ -89,9 +95,9 @@ export class VimNavigator extends React.PureComponent<IVimNavigatorProps, IVimNa
|
|||
|
||||
return (
|
||||
<div style={this.props.style}>
|
||||
<div className="items">
|
||||
<NavigatorContainer>
|
||||
{this.props.render(this.state.selectedId, this.updateSelection)}
|
||||
</div>
|
||||
</NavigatorContainer>
|
||||
{this.props.active ? inputElement : null}
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -162,6 +162,9 @@ const fallBackFonts = `
|
|||
sans-serif
|
||||
`.trim()
|
||||
|
||||
export type OniThemeProps = ThemeProps<IThemeColors>
|
||||
export type OniStyledProps<T> = OniThemeProps & T
|
||||
|
||||
export {
|
||||
css,
|
||||
injectGlobal,
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
declare module "units-css"
|
|
@ -1,6 +1,9 @@
|
|||
var path = require("path")
|
||||
var webpack = require("webpack")
|
||||
|
||||
const createStyledComponentsTransformer = require("typescript-plugin-styled-components").default
|
||||
const styledComponentsTransformer = createStyledComponentsTransformer()
|
||||
|
||||
module.exports = {
|
||||
mode: "development",
|
||||
entry: [path.join(__dirname, "src/index.tsx")],
|
||||
|
@ -48,7 +51,12 @@ module.exports = {
|
|||
},
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
use: "ts-loader",
|
||||
use: {
|
||||
loader: "ts-loader",
|
||||
options: {
|
||||
getCustomTransformers: () => ({ before: [styledComponentsTransformer] }),
|
||||
},
|
||||
},
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
],
|
||||
|
|
|
@ -919,7 +919,7 @@
|
|||
"@types/react-redux": "5.0.12",
|
||||
"@types/react-test-renderer": "^16.0.0",
|
||||
"@types/react-transition-group": "^2.0.11",
|
||||
"@types/react-virtualized": "^9.7.10",
|
||||
"@types/react-virtualized": "^9.18.3",
|
||||
"@types/redux-batched-subscribe": "^0.1.2",
|
||||
"@types/redux-mock-store": "^1.0.0",
|
||||
"@types/rimraf": "^2.0.2",
|
||||
|
@ -980,7 +980,7 @@
|
|||
"react-redux": "5.0.6",
|
||||
"react-test-renderer": "^16.2.0",
|
||||
"react-transition-group": "2.2.1",
|
||||
"react-virtualized": "^9.18.0",
|
||||
"react-virtualized": "^9.19.1",
|
||||
"redux": "3.7.2",
|
||||
"redux-mock-store": "^1.5.3",
|
||||
"redux-observable": "0.17.0",
|
||||
|
@ -995,6 +995,7 @@
|
|||
"ts-jest": "^23.0.0",
|
||||
"ts-loader": "^4.2.0",
|
||||
"tslint": "5.9.1",
|
||||
"typescript-plugin-styled-components": "^0.0.6",
|
||||
"vscode-snippet-parser": "0.0.5",
|
||||
"wcwidth": "1.0.1",
|
||||
"webdriverio": "4.8.0",
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
// For more information on customizing Oni,
|
||||
// check out our wiki page:
|
||||
// https://github.com/onivim/oni/wiki/Configuration
|
||||
|
||||
// tslint:disable
|
||||
module.exports = {
|
||||
"sidebar.enabled": false,
|
||||
"statusbar.enabled": false,
|
||||
"tabs.mode": "hidden",
|
||||
"ui.animations.enabled": false,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { mount, shallow } from "enzyme"
|
||||
import { shallow } from "enzyme"
|
||||
import { shallowToJson } from "enzyme-to-json"
|
||||
import * as React from "react"
|
||||
|
||||
|
@ -20,6 +20,8 @@ describe("<NodeView />", () => {
|
|||
const Node = (
|
||||
<NodeView
|
||||
yanked={[] as string[]}
|
||||
measure={() => null}
|
||||
isCreating={false}
|
||||
isRenaming={testNode}
|
||||
moveFileOrFolder={() => ({})}
|
||||
node={testNode}
|
||||
|
@ -38,6 +40,8 @@ describe("<NodeView />", () => {
|
|||
const wrapper = shallow(
|
||||
<NodeView
|
||||
yanked={[] as string[]}
|
||||
measure={() => null}
|
||||
isCreating={false}
|
||||
isRenaming={testNode}
|
||||
moveFileOrFolder={() => ({})}
|
||||
node={testNode}
|
||||
|
|
|
@ -1,14 +1,7 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<NodeView /> Should match the snapshot 1`] = `
|
||||
<styled.div
|
||||
innerRef={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"cursor": "pointer",
|
||||
}
|
||||
}
|
||||
>
|
||||
<styled.div>
|
||||
<div>
|
||||
<DropTarget(DragSource(DragAndDrop))
|
||||
accepts={
|
||||
|
|
29
yarn.lock
29
yarn.lock
|
@ -220,9 +220,9 @@
|
|||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-virtualized@^9.7.10":
|
||||
version "9.7.10"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-virtualized/-/react-virtualized-9.7.10.tgz#f55ee6138cef17a4adf2893494d32b8407f0103b"
|
||||
"@types/react-virtualized@^9.18.3":
|
||||
version "9.18.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-virtualized/-/react-virtualized-9.18.3.tgz#777b3c28bd2b972d0a402f2f9d11bacef550be1c"
|
||||
dependencies:
|
||||
"@types/prop-types" "*"
|
||||
"@types/react" "*"
|
||||
|
@ -8607,6 +8607,10 @@ react-lifecycles-compat@^2.0.0:
|
|||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-2.0.2.tgz#00a23160eec17a43b94dd74f95d44a1a2c3c5ec1"
|
||||
|
||||
react-lifecycles-compat@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
|
||||
|
||||
react-motion@0.5.2:
|
||||
version "0.5.2"
|
||||
resolved "https://registry.yarnpkg.com/react-motion/-/react-motion-0.5.2.tgz#0dd3a69e411316567927917c6626551ba0607316"
|
||||
|
@ -8654,15 +8658,16 @@ react-transition-group@2.2.1:
|
|||
prop-types "^15.5.8"
|
||||
warning "^3.0.0"
|
||||
|
||||
react-virtualized@^9.18.0:
|
||||
version "9.18.0"
|
||||
resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-9.18.0.tgz#d95ccdcfe82c7791da661be3ebc59f4bbc224f07"
|
||||
react-virtualized@^9.19.1:
|
||||
version "9.19.1"
|
||||
resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-9.19.1.tgz#84b53253df2d9df61c85ce037141edccc70a73fd"
|
||||
dependencies:
|
||||
babel-runtime "^6.26.0"
|
||||
classnames "^2.2.3"
|
||||
dom-helpers "^2.4.0 || ^3.0.0"
|
||||
loose-envify "^1.3.0"
|
||||
prop-types "^15.5.4"
|
||||
prop-types "^15.6.0"
|
||||
react-lifecycles-compat "^3.0.4"
|
||||
|
||||
react@^16.3.2:
|
||||
version "16.3.2"
|
||||
|
@ -10328,6 +10333,10 @@ tryer@^1.0.0:
|
|||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.0.tgz#027b69fa823225e551cace3ef03b11f6ab37c1d7"
|
||||
|
||||
ts-is-kind@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ts-is-kind/-/ts-is-kind-1.0.0.tgz#f9ca9c1af07ddb82d7a06744d8928952d4bd2426"
|
||||
|
||||
ts-jest@^23.0.0:
|
||||
version "23.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-23.0.0.tgz#3216310fb51e40789c6e4aa20bfe80198d250e9b"
|
||||
|
@ -10416,6 +10425,12 @@ typedarray@^0.0.6, typedarray@~0.0.5:
|
|||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||
|
||||
typescript-plugin-styled-components@^0.0.6:
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/typescript-plugin-styled-components/-/typescript-plugin-styled-components-0.0.6.tgz#e8489b72436aa1780e859243b519230b2c6b53d1"
|
||||
dependencies:
|
||||
ts-is-kind "^1.0.0"
|
||||
|
||||
typescript@^2.8.1:
|
||||
version "2.8.1"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.8.1.tgz#6160e4f8f195d5ba81d4876f9c0cc1fbc0820624"
|
||||
|
|
Loading…
Reference in New Issue