Skip to content

Commit 3bf957b

Browse files
committed
feat(editor): add split window and some features for editor
1 parent 1ea93c1 commit 3bf957b

File tree

13 files changed

+250
-81
lines changed

13 files changed

+250
-81
lines changed

src/components/monaco/index.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as React from 'react';
2-
import { Component } from 'react';
2+
import { PureComponent } from 'react';
33
import * as monaco from 'monaco-editor';
44
import { APP_PREFIX } from 'mo/common/const';
55

@@ -21,7 +21,7 @@ export interface IMonacoEditorProps extends React.ComponentProps<any> {
2121
editorInstanceRef?: (instance: monaco.editor.IStandaloneCodeEditor) => void;
2222
}
2323

24-
export default class MonacoEditor extends Component<IMonacoEditorProps> {
24+
export default class MonacoEditor extends PureComponent<IMonacoEditorProps> {
2525
/**
2626
* The instance of monaco
2727
*/
@@ -55,7 +55,7 @@ export default class MonacoEditor extends Component<IMonacoEditorProps> {
5555

5656
render() {
5757
const { style } = this.props;
58-
let renderStyle: any = {
58+
let renderStyle: React.CSSProperties = {
5959
position: 'relative',
6060
minHeight: '400px',
6161
height: '100%',

src/components/tabs/index.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export interface ITabs<T> extends React.ComponentProps<any> {
1818
data?: ITab<T>[];
1919
activeTab?: string;
2020
type?: TabsType;
21+
style?: React.CSSProperties;
2122
onCloseTab?: (key?: string) => void;
2223
onMoveTab?: (tabs: ITab<T>[]) => void;
2324
onSelectTab?: (key?: string) => void;
@@ -34,7 +35,7 @@ export function Tabs<T>(props: ITabs<T>) {
3435
activeTab,
3536
data = [],
3637
type = 'line',
37-
closable,
38+
style,
3839
onMoveTab,
3940
...resetProps
4041
} = props;
@@ -57,11 +58,11 @@ export function Tabs<T>(props: ITabs<T>) {
5758
return (
5859
<DragAndDrop>
5960
<div
61+
style={style}
6062
className={classNames(
6163
tabsClassName,
6264
getBEMModifier(tabsClassName, type as string)
6365
)}
64-
{...resetProps}
6566
>
6667
<div className={tabsHeader}>
6768
{data?.map((tab: ITab<T>, index: number) => {

src/components/tabs/style.scss

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
height: 36px;
1717
justify-content: flex-start;
1818
overflow: hidden;
19-
overflow-x: auto;
19+
overflow-x: inherit;
2020
overflow-y: hidden;
2121
width: 100%;
2222
}

src/components/tabs/tab.tsx

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as React from 'react';
2-
import { useRef, useState } from 'react';
2+
import { useCallback, useRef, useState } from 'react';
33
import { findDOMNode } from 'react-dom';
44
import {
55
DragSourceMonitor,
@@ -31,6 +31,7 @@ export interface ITabEvent {
3131
onMoveTab?: (dragIndex: number, hoverIndex: number) => void;
3232
onCloseTab?: (key?: string) => void;
3333
onSelectTab?: (key?: string) => void;
34+
onContextMenu?: <T = any>(event: React.MouseEvent, tab: ITab<T>) => void;
3435
}
3536
export const tabClassName = prefixClaName('tab');
3637
export const tabItemClassName = getBEMElement(tabClassName, 'item');
@@ -46,13 +47,20 @@ export function Tab<T>(props: ITab<T> & ITabEvent) {
4647
onCloseTab,
4748
onMoveTab,
4849
onSelectTab,
50+
onContextMenu,
4951
...resetProps
5052
} = props;
5153
const ref = useRef<HTMLDivElement>(null);
5254

5355
const [hover, setHover] = useState(false);
5456
const handleMouseOver = () => setHover(true);
5557
const handleMouseOut = () => setHover(false);
58+
const handleOnContextMenu = useCallback(
59+
(event: React.MouseEvent) => {
60+
onContextMenu?.(event, props);
61+
},
62+
[props]
63+
);
5664

5765
const [, drag] = useDrag({
5866
collect: (monitor: DragSourceMonitor) => ({
@@ -107,6 +115,7 @@ export function Tab<T>(props: ITab<T> & ITabEvent) {
107115
onClick={(event: React.MouseEvent) => onSelectTab?.(id)}
108116
onMouseOver={handleMouseOver}
109117
onMouseOut={handleMouseOut}
118+
onContextMenu={handleOnContextMenu}
110119
>
111120
{label || name}
112121
{closable && (

src/controller/editor.ts

+65-3
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,28 @@ import { EditorEvent, IEditorTab } from 'mo/model/workbench/editor';
22
import { Controller } from 'mo/react/controller';
33
import { editorService } from 'mo/services';
44
import { singleton } from 'tsyringe';
5-
65
export interface IEditorController {
6+
groupSplitPos?: string[];
77
onCloseAll?: (group: number) => void;
88
onCloseTab?: (tabKey: string, group: number) => void;
99
onMoveTab?: <T = any>(updateTabs: IEditorTab<T>[], group: number) => void;
1010
onSelectTab?: (tabKey: string, group: number) => void;
1111
onSplitEditorRight?: () => void;
1212
onUpdateEditorIns?: (editorInstance: any, groupId: number) => void;
13+
onPaneSizeChange?: (newSize: number) => void;
14+
onTabContextMenu?: (e: React.MouseEvent, tab: IEditorTab) => void;
1315
}
1416

1517
@singleton()
1618
export class EditorController extends Controller implements IEditorController {
19+
private editorInstance;
20+
// Group Pos locate here temporary, we can move it to state or localStorage if you need
21+
public groupSplitPos: string[] = [];
22+
23+
constructor() {
24+
super();
25+
}
26+
1727
public onCloseAll = (groupId: number) => {
1828
editorService.closeAll(groupId);
1929
this.emit(EditorEvent.OnCloseAll, groupId);
@@ -34,12 +44,18 @@ export class EditorController extends Controller implements IEditorController {
3444
};
3545

3646
public onSelectTab = (tabKey: string, groupId: number) => {
37-
editorService.updateCurrent(groupId, tabKey);
47+
editorService.setActive(groupId, tabKey);
48+
const { current } = editorService.getState();
49+
current?.editorInstance.setValue(current.tab?.data.value);
3850
this.emit(EditorEvent.OnSelectTab, tabKey, groupId);
3951
};
4052

41-
public onUpdateEditorIns = (editorInstance: any, groupId: number) => {
53+
public onUpdateEditorIns = (
54+
editorInstance: IStandaloneCodeEditor,
55+
groupId: number
56+
) => {
4257
if (editorInstance) {
58+
this.initEditorEvents(editorInstance, groupId);
4359
editorService.updateGroup(groupId, {
4460
editorInstance: editorInstance,
4561
});
@@ -50,4 +66,50 @@ export class EditorController extends Controller implements IEditorController {
5066
editorService.cloneGroup();
5167
this.emit(EditorEvent.OnSplitEditorRight);
5268
};
69+
70+
public onPaneSizeChange = (newSize) => {
71+
this.groupSplitPos = newSize;
72+
};
73+
74+
public onTabContextMenu = (e: React.MouseEvent, tab: IEditorTab) => {
75+
console.log('onTabContextMenu', e, tab);
76+
};
77+
78+
private initEditorEvents(
79+
editorInstance: IStandaloneCodeEditor,
80+
groupId: number
81+
) {
82+
this.editorInstance = editorInstance;
83+
if (editorInstance) {
84+
editorInstance.onDidChangeModelContent((event: any) => {
85+
const newValue = editorInstance.getValue();
86+
const { current } = editorService.getState();
87+
const tab = current?.tab;
88+
if (tab) {
89+
editorService.updateTab(
90+
{
91+
id: tab.id,
92+
data: {
93+
...tab.data,
94+
value: newValue,
95+
},
96+
modified: true,
97+
},
98+
groupId
99+
);
100+
}
101+
});
102+
103+
editorInstance.onDidFocusEditorText(() => {
104+
const group = editorService.getGroupById(groupId);
105+
if (group?.tab!.id) {
106+
editorService.setActive(groupId, group.tab.id);
107+
}
108+
});
109+
}
110+
}
111+
112+
getEditorInstance() {
113+
return this.editorInstance;
114+
}
53115
}

src/extensions/search/searchPane.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ export default class SearchPane extends React.Component<
130130
breadcrumb: [{ id: `${key}`, name: 'editor.js' }],
131131
};
132132
console.log('open editor:', tabData);
133-
editorService.open(tabData, 1);
133+
editorService.open(tabData);
134134
};
135135

136136
const openCommand = function () {};

src/model/workbench/editor.ts

+40-14
Original file line numberDiff line numberDiff line change
@@ -23,26 +23,17 @@ export interface IEditorTab<T = BuiltInEditorTabDataType> extends ITab<T> {
2323
modified?: boolean;
2424
breadcrumb?: IBreadcrumbItem[];
2525
}
26-
27-
export interface IEditorActionItem {
28-
id?: number;
29-
icon?: ReactNode;
30-
title?: string;
31-
onClick?: (e: React.MouseEvent) => void;
32-
}
33-
3426
export interface IEditorAction {
35-
actions?: IEditorActionItem[];
27+
actions?: IMenuItem[];
3628
menu?: IMenuItem[];
3729
}
38-
3930
export interface IEditorGroup<E = any, T = any> extends ITabs<T> {
4031
id?: number;
4132
/**
4233
* Current editor group tab
4334
*/
4435
tab?: IEditorTab<T>;
45-
actions?: IEditorActionItem[];
36+
actions?: IMenuItem[];
4637
menu?: IMenuItem[];
4738
editorInstance?: E;
4839
}
@@ -54,20 +45,55 @@ export interface IEditor {
5445
groups?: IEditorGroup[];
5546
}
5647

48+
const baseMenu = [
49+
{
50+
id: 'closeToRight',
51+
name: 'Close To Right',
52+
},
53+
{
54+
id: 'closeSaved',
55+
name: 'Close Saved',
56+
},
57+
];
58+
59+
const initialActions: IMenuItem[] = [
60+
{
61+
id: 'showOpenEditors',
62+
name: 'Show Opened Editors',
63+
},
64+
...baseMenu,
65+
];
66+
67+
const initialMenu: IMenuItem[] = [
68+
{
69+
id: 'close',
70+
name: 'Close',
71+
},
72+
{
73+
id: 'closeOther',
74+
name: 'Close Others',
75+
},
76+
{
77+
id: 'closeToRight',
78+
name: 'Close To Right',
79+
},
80+
...baseMenu,
81+
];
82+
5783
export class EditorGroupModel<E = any, T = any> implements IEditorGroup<E, T> {
5884
id: number;
5985
tab: IEditorTab<T>;
6086
data: IEditorTab<T>[];
61-
actions: IEditorActionItem[];
87+
actions: IMenuItem[];
6288
menu: IMenuItem[];
6389
editorInstance: E | undefined;
6490

6591
constructor(
6692
id: number,
6793
tab: IEditorTab<T>,
6894
data: IEditorTab<T>[],
69-
actions: IEditorActionItem[] = [],
70-
menu: IMenuItem[] = [],
95+
actions: IMenuItem[] = initialActions,
96+
menu: IMenuItem[] = initialMenu,
7197
editorInstance?: E
7298
) {
7399
this.id = id;

src/services/helper.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function searchById(id) {
2+
return (item) => item.id === id;
3+
}

0 commit comments

Comments
 (0)