Skip to content

Commit 443d4da

Browse files
authored
fix: improve editor actions (#234)
* refactor: rename update actions and create a new update actions function * fix: improve editor actions * fix: improve interface name * fix: rename ellipsisActions
1 parent 1dfee56 commit 443d4da

File tree

10 files changed

+181
-59
lines changed

10 files changed

+181
-59
lines changed

src/controller/editor.tsx

+26-4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ import {
99
EDITOR_MENU_CLOSE_TO_RIGHT,
1010
EDITOR_MENU_CLOSE_TO_LEFT,
1111
EDITOR_MENU_CLOSE_ALL,
12+
IEditorActionsProps,
13+
EDITOR_MENU_SHOW_OPENEDITORS,
14+
EDITOR_MENU_SPILIT,
1215
} from 'mo/model/workbench/editor';
1316
import { undoRedoMenu } from 'mo/model/workbench/menuBar';
1417
import { Controller } from 'mo/react/controller';
@@ -47,7 +50,7 @@ export interface IEditorController {
4750
) => void;
4851
onMoveTab?: <T = any>(updateTabs: IEditorTab<T>[], group: number) => void;
4952
onSelectTab?: (tabId: string, group: number) => void;
50-
onSplitEditorRight?: () => void;
53+
onClickActions: (action: IEditorActionsProps) => void;
5154
onUpdateEditorIns?: (editorInstance: any, groupId: number) => void;
5255
onPaneSizeChange?: (newSize: number) => void;
5356
}
@@ -190,9 +193,28 @@ export class EditorController extends Controller implements IEditorController {
190193
});
191194
};
192195

193-
public onSplitEditorRight = () => {
194-
this.editorService.cloneGroup();
195-
this.emit(EditorEvent.OnSplitEditorRight);
196+
public onClickActions = (action: IEditorActionsProps) => {
197+
const { current } = this.editorService.getState();
198+
if (!current) return;
199+
200+
switch (action.id) {
201+
case EDITOR_MENU_CLOSE_ALL: {
202+
this.onCloseAll(current.id!);
203+
break;
204+
}
205+
case EDITOR_MENU_SHOW_OPENEDITORS: {
206+
// TODO
207+
break;
208+
}
209+
case EDITOR_MENU_SPILIT: {
210+
this.editorService.cloneGroup();
211+
this.emit(EditorEvent.OnSplitEditorRight);
212+
break;
213+
}
214+
default: {
215+
this.emit(EditorEvent.onActionsClick, action.id, current);
216+
}
217+
}
196218
};
197219

198220
public onPaneSizeChange = (newSize) => {

src/extensions/theme-defaults/themes/dark_defaults.json

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"inputValidation.warningBorder": "#B89500",
2727
"inputValidation.errorBackground": "#5A1D1D",
2828
"inputValidation.errorBorder": "#BE1100",
29+
"icon.foreground": "#C5C5C5",
2930
"settings.textInputBackground": "#292929",
3031
"settings.numberInputBackground": "#292929",
3132
"menu.background": "#252526",

src/extensions/theme-defaults/themes/light_defaults.json

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"inputValidation.warningBorder": "#B89500",
2727
"inputValidation.errorBackground": "#F2DEDE",
2828
"inputValidation.errorBorder": "#BE1100",
29+
"icon.foreground": "#424242",
2930
"searchEditor.textInputBorder": "#CECECE",
3031
"diffEditor.insertedTextBackground": "#9bb95533",
3132
"diffEditor.removedTextBackground": "#ff000033",

src/model/workbench/editor.ts

+20-5
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,19 @@ interface BuiltInEditorTabDataType {
2424
modified?: boolean;
2525
}
2626

27+
export interface IEditorActionsProps extends IMenuItemProps {
28+
id: string;
29+
/**
30+
* Mark the action placed in More menus or outer
31+
*/
32+
place?: 'outer';
33+
}
34+
2735
export interface IEditorTab<T = BuiltInEditorTabDataType> extends ITabProps<T> {
2836
breadcrumb?: IBreadcrumbItemProps[];
2937
}
3038
export interface IEditorAction {
31-
actions?: IMenuItemProps[];
39+
actions?: IEditorActionsProps[];
3240
menu?: IMenuItemProps[];
3341
}
3442
export interface IEditorGroup<E = any, T = any> extends ITabsProps<T> {
@@ -37,7 +45,7 @@ export interface IEditorGroup<E = any, T = any> extends ITabsProps<T> {
3745
* Current editor group tab
3846
*/
3947
tab?: IEditorTab<T>;
40-
actions?: IMenuItemProps[];
48+
actions?: IEditorActionsProps[];
4149
menu?: IMenuItemProps[];
4250
editorInstance?: E;
4351
}
@@ -57,6 +65,7 @@ export const EDITOR_MENU_CLOSE_OTHERS = 'editor.closeOthers';
5765
export const EDITOR_MENU_CLOSE_SAVED = 'editor.closeSaved';
5866
export const EDITOR_MENU_CLOSE = 'editor.close';
5967
export const EDITOR_MENU_SHOW_OPENEDITORS = 'editor.showOpenEditors';
68+
export const EDITOR_MENU_SPILIT = 'editor.split';
6069

6170
export function getBaseMenu() {
6271
return [
@@ -67,8 +76,14 @@ export function getBaseMenu() {
6776
];
6877
}
6978

70-
export function getEditorInitialActions(): IMenuItemProps[] {
79+
export function getEditorInitialActions(): IEditorActionsProps[] {
7180
return [
81+
{
82+
id: EDITOR_MENU_SPILIT,
83+
name: 'Split Editor Right',
84+
icon: 'split-horizontal',
85+
place: 'outer',
86+
},
7287
{
7388
id: EDITOR_MENU_SHOW_OPENEDITORS,
7489
name: 'Show Opened Editors',
@@ -103,15 +118,15 @@ export class EditorGroupModel<E = any, T = any> implements IEditorGroup<E, T> {
103118
id: number;
104119
tab: IEditorTab<T>;
105120
data: IEditorTab<T>[];
106-
actions: IMenuItemProps[];
121+
actions: IEditorActionsProps[];
107122
menu: IMenuItemProps[];
108123
editorInstance: E | undefined;
109124

110125
constructor(
111126
id: number,
112127
tab: IEditorTab<T>,
113128
data: IEditorTab<T>[],
114-
actions: IMenuItemProps[] = getEditorInitialActions(),
129+
actions: IEditorActionsProps[] = getEditorInitialActions(),
115130
menu: IMenuItemProps[] = getEditorInitialMenu(),
116131
editorInstance?: E
117132
) {

src/services/workbench/editorService.ts

+36-7
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
IEditorTab,
1111
EditorEvent,
1212
getEditorInitialActions,
13+
IEditorActionsProps,
1314
} from 'mo/model';
1415
import { searchById } from '../helper';
1516
import { editor as monacoEditor, Uri } from 'mo/monaco';
@@ -71,9 +72,13 @@ export interface IEditorService extends Component<IEditor> {
7172
setActive(groupId: number, tabId: string);
7273
updateGroup(groupId, groupValues: IEditorGroup): void;
7374
/**
74-
* Update actions in group
75+
* Set default actions when create a new group
7576
*/
76-
updateGroupActions(actions: IMenuItemProps[]): void;
77+
setDefaultActions(actions: IEditorActionsProps[]): void;
78+
/**
79+
* Update actions in specific group
80+
*/
81+
updateActions(actions: IMenuItemProps[], groupId?: number): void;
7782
updateCurrentGroup(currentValues): void;
7883
/**
7984
* The Instance of Editor
@@ -85,11 +90,11 @@ export class EditorService
8590
extends Component<IEditor>
8691
implements IEditorService {
8792
protected state: IEditor;
88-
protected groupActions: IMenuItemProps[];
93+
protected defaultActions: IEditorActionsProps[];
8994
constructor() {
9095
super();
9196
this.state = container.resolve(EditorModel);
92-
this.groupActions = getEditorInitialActions();
97+
this.defaultActions = getEditorInitialActions();
9398
}
9499

95100
private disposeModel(tabs: IEditorTab | IEditorTab[]) {
@@ -104,8 +109,8 @@ export class EditorService
104109
return groups.some((group) => this.getTabById(tabId, group));
105110
}
106111

107-
public updateGroupActions(actions: IMenuItemProps[]): void {
108-
this.groupActions = actions;
112+
public setDefaultActions(actions: IEditorActionsProps[]): void {
113+
this.defaultActions = actions;
109114
}
110115

111116
public setEntry(component: React.ReactNode) {
@@ -114,6 +119,30 @@ export class EditorService
114119
});
115120
}
116121

122+
public updateActions = (actions: IMenuItemProps[], groupId?: number) => {
123+
const { current, groups: rawGroups } = this.getState();
124+
if (!current) return;
125+
126+
const groups = rawGroups?.concat() || [];
127+
const targetGroup = groups.find(searchById(groupId || current.id));
128+
129+
if (targetGroup) {
130+
const newActions = targetGroup.actions?.concat() || [];
131+
newActions.forEach((action) => {
132+
const target = actions.find((item) => item.id === action.id);
133+
if (target) {
134+
Object.assign(action, target);
135+
}
136+
});
137+
targetGroup.actions = newActions;
138+
139+
this.setState({
140+
current: targetGroup.id === current.id ? targetGroup : current,
141+
groups,
142+
});
143+
}
144+
};
145+
117146
public getTabById<T>(
118147
tabId: string,
119148
group: IEditorGroup
@@ -378,7 +407,7 @@ export class EditorService
378407
groups.length + 1,
379408
tab,
380409
[tab],
381-
this.groupActions
410+
this.defaultActions
382411
);
383412
groups.push(group);
384413
}

src/workbench/editor/action.tsx

+73-37
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,67 @@
11
import * as React from 'react';
2-
import { memo, useCallback } from 'react';
2+
import { memo } from 'react';
33
import { Icon } from 'mo/components/icon';
44
import { Menu } from 'mo/components/menu';
55
import { DropDown, DropDownRef } from 'mo/components/dropdown';
6-
import { IEditorAction } from 'mo/model';
7-
import { groupActionsClassName, groupActionsItemClassName } from './base';
6+
import { IEditorActionsProps, IEditorAction } from 'mo/model';
7+
import {
8+
groupActionItemDisabledClassName,
9+
groupActionsClassName,
10+
groupActionsItemClassName,
11+
} from './base';
812
import { IEditorController } from 'mo/controller/editor';
13+
import { classNames } from 'mo/common/className';
914

1015
export interface IEditorActionProps extends IEditorAction {
1116
isActiveGroup: boolean;
1217
}
1318

19+
const MAX_ACTIONS_LENGTH = 6;
20+
21+
function splitActions(actions: IEditorActionsProps[]) {
22+
const outerActions: IEditorActionsProps[] = [];
23+
const innerActions: IEditorActionsProps[] = [];
24+
25+
actions.forEach((action) => {
26+
if (action.place === 'outer') {
27+
outerActions.push(action);
28+
} else {
29+
innerActions.push(action);
30+
}
31+
});
32+
33+
if (outerActions.length > MAX_ACTIONS_LENGTH) {
34+
const surplusActions = outerActions.splice(
35+
0,
36+
MAX_ACTIONS_LENGTH - outerActions.length
37+
);
38+
39+
innerActions.concat(surplusActions);
40+
}
41+
42+
return [outerActions, innerActions];
43+
}
44+
1445
function EditorAction(props: IEditorActionProps & IEditorController) {
15-
const {
16-
actions = [],
17-
isActiveGroup = false,
18-
onClickContextMenu,
19-
onSplitEditorRight,
20-
} = props;
46+
const { actions = [], isActiveGroup = false, onClickActions } = props;
47+
const [outer, inner] = splitActions(actions);
2148

2249
const childRef = React.useRef<DropDownRef>(null);
2350

24-
const handleOnMenuClick = (e: React.MouseEvent, item) => {
25-
onClickContextMenu?.(e, item);
26-
(childRef.current as any)!.dispose();
51+
const handleOnMenuClick = (_, item) => {
52+
onClickActions(item);
53+
childRef.current?.dispose();
54+
};
55+
56+
const handleActionsClick = (action) => {
57+
onClickActions(action);
2758
};
2859

2960
const overlay =
30-
actions.length > 0 ? (
61+
inner.length > 0 ? (
3162
<Menu
3263
style={{ width: 200 }}
33-
data={actions}
64+
data={inner}
3465
onClick={handleOnMenuClick}
3566
/>
3667
) : (
@@ -44,33 +75,38 @@ function EditorAction(props: IEditorActionProps & IEditorController) {
4475
</span>
4576
);
4677

47-
const handleSplitEditor = useCallback(
48-
(e: React.MouseEvent) => {
49-
onSplitEditorRight?.();
50-
},
51-
[actions]
52-
);
5378
return (
5479
<div className={groupActionsClassName}>
55-
{isActiveGroup ? (
56-
<div
57-
onClick={handleSplitEditor}
80+
{isActiveGroup &&
81+
outer.map((action) => (
82+
<div
83+
key={action.id}
84+
onClick={() => handleActionsClick(action)}
85+
className={classNames(
86+
groupActionsItemClassName,
87+
action.disabled && groupActionItemDisabledClassName
88+
)}
89+
title={action.name?.toString()}
90+
>
91+
{action.icon ? (
92+
<Icon type={action.icon} />
93+
) : (
94+
action.name
95+
)}
96+
</div>
97+
))}
98+
{Boolean(inner.length) && (
99+
<DropDown
100+
ref={childRef}
101+
placement="bottom"
58102
className={groupActionsItemClassName}
59-
title="Split Editor Right"
103+
trigger="click"
104+
title="More Actions..."
105+
overlay={overlay}
60106
>
61-
<Icon type="split-horizontal" />
62-
</div>
63-
) : null}
64-
<DropDown
65-
ref={childRef}
66-
placement="bottom"
67-
className={groupActionsItemClassName}
68-
trigger="click"
69-
title="More Actions..."
70-
overlay={overlay}
71-
>
72-
<Icon type="ellipsis" />
73-
</DropDown>
107+
<Icon type="ellipsis" />
108+
</DropDown>
109+
)}
74110
</div>
75111
);
76112
}

src/workbench/editor/base.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { getBEMElement, prefixClaName } from 'mo/common/className';
1+
import {
2+
getBEMElement,
3+
getBEMModifier,
4+
prefixClaName,
5+
} from 'mo/common/className';
26

37
export const defaultEditorClassName = prefixClaName('editor');
48
export const groupClassName = getBEMElement(defaultEditorClassName, 'group');
@@ -23,6 +27,11 @@ export const groupActionsItemClassName = getBEMElement(
2327
'group-actions-item'
2428
);
2529

30+
export const groupActionItemDisabledClassName = getBEMModifier(
31+
groupActionsItemClassName,
32+
'disabled'
33+
);
34+
2635
export const groupBreadcrumbClassName = getBEMElement(
2736
defaultEditorClassName,
2837
'group-breadcrumb'

0 commit comments

Comments
 (0)