Skip to content

Commit

Permalink
fix: merge tabs and collapse
Browse files Browse the repository at this point in the history
  • Loading branch information
wewoor committed Nov 20, 2020
2 parents 1eff91a + d04c152 commit c3e1451
Show file tree
Hide file tree
Showing 10 changed files with 435 additions and 45 deletions.
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"main": "dist/index.js",
"scripts": {
"test": "jest --coverage",
"dev": "tsc --skipLibCheck && start-storybook -s ./public -p 6006",
"dev": "start-storybook -s ./public -p 6006",
"build-storybook": "build-storybook",
"check-types": "tsc --skipLibCheck",
"build": "tsc --project tsconfig.build.json",
Expand All @@ -28,11 +28,15 @@
"@types/react-dom": "^16.9.9",
"core-js": "^3.6.5",
"dt-utils": "^1.0.1",
"immutability-helper": "^3.1.1",
"loadsh": "^0.0.4",
"monaco-editor": "^0.21.2",
"rc-collapse": "^2.0.1",
"rc-tree": "^3.10.0",
"react": "^16.13.1",
"react-dnd": "^9.3.4",
"react-dnd-html5-backend": "^9.3.4",
"rc-tabs": "11.7.0",
"react-dom": "^16.13.1",
"react-split-pane": "^0.1.92",
"reflect-metadata": "^0.1.13",
Expand Down
65 changes: 65 additions & 0 deletions src/components/tabs/Tab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import * as React from 'react'
import { useRef } from 'react';
import { findDOMNode } from 'react-dom';
import { DragSourceMonitor, DropTargetMonitor, useDrag, useDrop } from 'react-dnd';

interface ITabProps {
active?: string;
content?: React.ReactNode;
index?: number;
id?: number | string;
name?: any;
moveTab: (dragIndex?: number, hoverIndex?: number | string) => void;
}

const WrapTabNode: React.FC<ITabProps> = (props) => {
const { id, index, moveTab, children } = props
const ref = useRef<HTMLDivElement>(null)

const [, drag] = useDrag({
collect: (monitor: DragSourceMonitor) => ({
isDragging: monitor.isDragging(),
}),
item: { type: 'DND_NODE', id, index }
});

const [, drop] = useDrop({
accept: 'DND_NODE',
hover (item: { type: string; index: number }, monitor: DropTargetMonitor) {
debugger
if (!ref.current) return
let hoverIndex
const component = ref.current
const dragIndex = monitor.getItem().index
hoverIndex = index
// Don't replace items with themselves
if (dragIndex === hoverIndex) {
return;
}
const hoverBoundingRect = (findDOMNode(component) as Element)?.getBoundingClientRect();
const hoverMiddleX = (hoverBoundingRect.right - hoverBoundingRect.left) / 2;
const clientOffset = monitor.getClientOffset();
const hoverClientX = (clientOffset as { x: number; y: number; }).x - hoverBoundingRect.left;
// 往下拖动
if (dragIndex < hoverIndex && hoverClientX < hoverMiddleX) {
return;
}
// 往上拖动
if (dragIndex > hoverIndex && hoverClientX > hoverMiddleX) {
return;
}
moveTab(dragIndex, hoverIndex);
monitor.getItem().index = hoverIndex;
}
})
drag(drop(ref))

return (
<div ref={ref} >
{children}
</div>
)
}

export default WrapTabNode

78 changes: 60 additions & 18 deletions src/components/tabs/index.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,75 @@
import * as React from 'react';
import { useCallback } from 'react'
import update from 'immutability-helper';
import { DndProvider } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';

import Tabs, { TabPane } from 'rc-tabs';

import WrapTabNode from './Tab';
import { prefixClaName } from 'mo/common/className';
import './style.scss'

export interface ITab<T = any, K = any> {
export interface ITab {
active?: string;
id?: number;
name?: string;
mode?: string;
data?: T;
options?: K;
data?: [];
value?: string;
renderPane?: () => React.ReactElement;
renderPane?: string | React.ReactNode;
}

interface ITabsProps {
data: ITab[];
onClose?: (item: ITab, index: number) => void;
closeTab?: (index: number) => void;
changeTab?: (tabs: ITab[]) => void;
selectTab: (index: number) => void;
children: React.ReactNode | JSX.Element
}

const Tabs: React.FunctionComponent<ITabsProps> = (props: ITabsProps) => {
const { data, onClose } = props;
const tabs = data.map((tab: ITab, index: number) => {
return (
<a key={tab.id}>
{tab.name}{' '}
<button onClick={(e) => onClose!(tab, index)}>Close</button>
</a>
);
});
return <div className={prefixClaName('tabs')}>{tabs}</div>;
const DraggleTabs: React.FC<ITabsProps> = (props: ITabsProps) => {

const { data, changeTab, selectTab } = props;

const moveTab = useCallback((dragIndex, hoverIndex) => {
const dragTab = data[dragIndex]
changeTab?.(update(data, {
$splice: [[dragIndex, 1], [ hoverIndex, 0, dragTab]],
}))
}, [data])

const onTabClick = key => {
console.log(`onTabClick ${key}`)
selectTab(key)
}

const renderTabBar = (props, DefaultTabBar) => {
return ( <DefaultTabBar {...props}>
{node => {
return (<WrapTabNode key={node.key} index={node.key} moveTab={moveTab}>{node}
</WrapTabNode>)
}}
</DefaultTabBar>
)
}

return (
<div className={prefixClaName('tabs-container')}>
<DndProvider backend={HTML5Backend}>
<Tabs
renderTabBar={renderTabBar}
onChange={onTabClick}
editable={{ showAdd: false, onEdit: () => { console.log(1)} }}
>
{data?.map(({ active, id, name }: ITab, index) => {
return (
<TabPane tab={`${name}`} key={index}/>
)
})}
</Tabs>
</DndProvider>
</div>
);
};

export default Tabs;
export default DraggleTabs
79 changes: 76 additions & 3 deletions src/components/tabs/style.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,77 @@
.scroll {
pointer-events: none;
width: 10px;
.mo-tabs-container {
display: flex;
overflow: hidden;
font-size: 14px;
box-sizing: border-box;
.rc-tabs-nav {
display: flex;
align-items: center;
.rc-tabs-nav-wrap {
position: relative;
display: inline-block;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-flex: 1;
-ms-flex: auto;
flex: auto;
-ms-flex-item-align: stretch;
align-self: stretch;
overflow: hidden;
white-space: nowrap;
}
.rc-tabs-nav-operations {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-ms-flex-item-align: stretch;
align-self: stretch;
}
}
}
.rc-tabs-nav-list {
height: 35px;
background-color: #001f27;
border-color: #002b36;
display: flex;
flex-flow: row nowrap;
justify-content: flex-start;
align-items: center;
overflow: hidden;
}

.rc-tabs-tab {
position: relative;
display: -webkit-inline-box;
display: -ms-inline-flexbox;
display: inline-flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
margin: 0 32px 0 0;
padding: 12px 0;
font-size: 14px;
background: transparent;
border: 0;
outline: none;
cursor: pointer;
&:hover {
.rc-tabs-tab-remove {
visibility: visible;
}
}
}
.rc-tabs-tab-btn {
outline: none;
}
.rc-tabs-tab-remove {
margin-right: -4px;
margin-left: 8px;
color: #999999;
font-size: 16px;
background: transparent;
border: none;
outline: none;
cursor: pointer;
visibility: hidden;
}
9 changes: 7 additions & 2 deletions src/extensions/explore/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { activityBarService, IActivityBarItem, sidebarService } from 'mo';
import { activityBarService, IActivityBarItem, sidebarService, editorService } from 'mo';

import { Explorer } from './explore';
import { ExtensionService } from 'mo/services/extensionService';
Expand All @@ -19,7 +19,12 @@ function init(extensionCtx: ExtensionService) {
selected: exploreActiveItem.id,
data: [...state.data, exploreActiveItem],
});

editorService.changeTab((data ) => {
console.log(data)
})
editorService.selectTab(tab =>{
console.log(`selected tabs${tab}`)
})
const explorePane = {
id: 'explore',
title: 'EXPLORER',
Expand Down
20 changes: 20 additions & 0 deletions src/model/workbench/editor.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
/* eslint-disable no-invalid-this */
import 'reflect-metadata';
import { EventBus } from 'mo/common/event';
import { observable } from 'mo/common/observable';
import { ITab } from 'mo/components/tabs';
import { container, inject, injectable } from 'tsyringe';

export enum EditorEvent {
CloseTab = 'editor.close',
ChangeTab = 'editor.changeTab',
OpenTab = 'editor.openTab',
SelectTab = 'editor.selectTab'
}
export interface IEditor {
current: IEditorGroup | undefined;
groups: IEditorGroup[];
closeAll?: () => void;
onClose?: () => void;
render?: () => React.ReactNode;
changeTab: (tabs: ITab[], group?: number) => void;
selectTab: (tab: ITab) => void;
}

export interface IEditorGroup<E = any> {
Expand Down Expand Up @@ -64,6 +74,16 @@ export class EditorModel implements IEditor {
}

public render!: () => React.ReactNode;

public readonly selectTab = (tab: ITab) => {
EventBus.emit(EditorEvent.ChangeTab, tab);
}
public readonly changeTab = (
updateTabs: ITab[],
groupId?: number
) => {
EventBus.emit(EditorEvent.ChangeTab, updateTabs, groupId);
}
}

container.register('CurrentEditorGroup', { useValue: '' });
Expand Down
32 changes: 22 additions & 10 deletions src/services/workbench/editorService.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
import { Component } from 'mo/react';

import { ITab } from 'mo/components/tabs';
import { emit } from 'mo/common/event';
import { singleton, container } from 'tsyringe';
import { EditorModel, EditorGroupModel, IEditor, IEditorGroup } from 'mo/model';

export enum EditorEvent {
OpenTab = 'editor.openTab',
CloseTab = 'editor.close',
}
import { EditorEvent, EditorModel, EditorGroupModel, IEditor, IEditorGroup } from 'mo/model';

export interface IEditorService extends Component<IEditor> {
/**
* Open a new tab in indicated group instance
* @param tab Tab data
* @param groupId group ID
*/
open<T = any>(tab: ITab<T>, groupId?: number): void;
open<T = any>(tab: ITab, groupId?: number): void;
close(index: number, callback: () => void): void;
changeTab(callback: (tabs: ITab[]) => void);
selectTab(callback: (tab: ITab) => void)
}

@singleton()
Expand All @@ -28,9 +26,8 @@ export class EditorService
super();
this.state = container.resolve(EditorModel);
}

@emit(EditorEvent.OpenTab)
public open<T>(tab: ITab<T>, groupId?: number) {
public open<T>(tab: ITab, groupId?: number) {
let { current, groups } = this.state;
let group: IEditorGroup | undefined = current;
if (groupId) {
Expand All @@ -45,7 +42,22 @@ export class EditorService
current = group;
}
}

public changeTab(
callback: (data) => void
) {
this.subscribe(EditorEvent.ChangeTab, (args) => {
let { groups } = this.state;
let group
if (!args?.[1]) return
const groupId = args?.[1]
group = groups?.find((group: IEditorGroup) => group.id === groupId)
group.tabs = args?.[0]
callback?.(args?.[0])
})
}
public selectTab(callback: Function) {
this.subscribe(EditorEvent.SelectTab, callback);
}
public closeAll() {}

@emit(EditorEvent.CloseTab)
Expand Down
Loading

0 comments on commit c3e1451

Please sign in to comment.