Skip to content

Commit 325eea0

Browse files
authored
feat: editor actions & actionBar support tooltip (#260)
* feat: add tooltip * feat: editor actions & actionBar support tooltip * fix: update actionBar snapshot * test: improve actionBar unit test * fix: improve docs api * fix: extract color/background-color into color theme
1 parent 3d45c87 commit 325eea0

File tree

19 files changed

+173
-30
lines changed

19 files changed

+173
-30
lines changed

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"monaco-editor": "^0.23.0",
4545
"rc-dialog": "8.2.1",
4646
"rc-textarea": "~0.3.1",
47+
"rc-tooltip": "^5.1.1",
4748
"rc-tree": "~3.10.0",
4849
"rc-util": "~5.5.0",
4950
"react-dnd": "^9.3.4",

src/components/actionBar/__tests__/__snapshots__/actionBar.test.tsx.snap

+3-2
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@ exports[`Test ActionBar Component Test the ActionBar Snapshot 1`] = `
99
>
1010
<li
1111
className="mo-action-bar__item"
12-
id="1"
12+
id="m1"
1313
onClick={[Function]}
1414
>
1515
<a
1616
className="mo-action-bar__label codicon codicon-add"
17-
title="mockDataTitle"
17+
onMouseEnter={[Function]}
18+
onMouseLeave={[Function]}
1819
/>
1920
</li>
2021
</ul>

src/components/actionBar/__tests__/actionBar.test.tsx

+8-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import * as React from 'react';
2-
import { render, screen } from '@testing-library/react';
2+
import { render } from '@testing-library/react';
33
import renderer from 'react-test-renderer';
44

55
import { ActionBar } from '../index';
66

77
const mockData = [
88
{
9-
id: '1',
9+
id: 'm1',
1010
title: 'mockDataTitle',
1111
iconName: 'codicon-add',
1212
},
@@ -20,7 +20,11 @@ describe('Test ActionBar Component', () => {
2020
});
2121

2222
test('Test the ActionBar by the data Props', () => {
23-
render(<ActionBar data={mockData} />);
24-
expect(screen.getByTitle(/mockDataTitle/)).not.toBeNull();
23+
const wrapper = render(<ActionBar data={mockData} />);
24+
const liDom = wrapper.container.querySelector('#m1');
25+
const iconDom = liDom?.querySelector('a.codicon-add');
26+
27+
expect(liDom).not.toBeNull();
28+
expect(iconDom).not.toBeNull();
2529
});
2630
});

src/components/actionBar/index.tsx

+4-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
import { useContextMenu } from 'mo/components/contextMenu';
1010
import { IMenuItemProps, Menu } from 'mo/components/menu';
1111
import { mergeFunctions } from 'mo/common/utils';
12+
import Tooltip from '../tooltip';
1213

1314
export interface IActionBarItemProps<T = any> {
1415
id?: string;
@@ -113,9 +114,9 @@ export function ActionBarItem(props: IActionBarItemProps) {
113114
onClick={onClickItem}
114115
data-id={data.id}
115116
>
116-
<a className={claNames} title={title}>
117-
{name}
118-
</a>
117+
<Tooltip overlay={<span>{title}</span>}>
118+
<a className={claNames}>{name}</a>
119+
</Tooltip>
119120
</li>
120121
);
121122
}

src/components/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,8 @@ export type { ITabProps } from './tabs/tab';
6565
export { Toolbar } from './toolbar';
6666
export type { IToolbarProps } from './toolbar';
6767

68+
export { default as Tooltip } from './tooltip';
69+
export type { IToolTipProps } from './tooltip';
70+
6871
export { default as TreeView } from './tree';
6972
export type { ITreeProps, ITreeNodeItemProps } from './tree';

src/components/menu/menu.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ export function Menu(props: React.PropsWithChildren<IMenuProps>) {
6565
children,
6666
onClick,
6767
trigger = 'hover',
68+
title,
6869
...custom
6970
} = props;
7071
const menuRef = React.useRef<HTMLUListElement>(null);
@@ -197,6 +198,8 @@ export function Menu(props: React.PropsWithChildren<IMenuProps>) {
197198
<ul
198199
className={claNames}
199200
ref={menuRef}
201+
// prevent JSX render in HTMLElement
202+
{...(typeof title === 'string' ? { title } : {})}
200203
{...getEventListener()}
201204
{...custom}
202205
>

src/components/menu/menuItem.tsx

+8-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ import {
99
labelClassName,
1010
menuContentClassName,
1111
} from './base';
12-
export interface IMenuItemProps extends HTMLElementProps {
12+
13+
export interface IMenuItemProps extends Omit<HTMLElementProps, 'title'> {
1314
/**
1415
* The name of icon
1516
*/
@@ -19,6 +20,7 @@ export interface IMenuItemProps extends HTMLElementProps {
1920
* Item Name
2021
*/
2122
name?: ReactNode;
23+
title?: React.ReactNode;
2224
disabled?: boolean;
2325
/**
2426
* The description of keybinding
@@ -45,20 +47,25 @@ export function MenuItem(props: React.PropsWithChildren<IMenuItemProps>) {
4547
render,
4648
children,
4749
name,
50+
title,
4851
...custom
4952
} = props;
53+
5054
const events = {
5155
onClick: function (e: React.MouseEvent) {
5256
onClick?.(e, props);
5357
},
5458
};
59+
5560
return (
5661
<li
5762
className={classNames(
5863
defaultMenuItemClassName,
5964
className,
6065
disabled ? disabledClassName : null
6166
)}
67+
// prevent render JSX title in HTMLElement
68+
{...(typeof title === 'string' ? { title } : {})}
6269
{...events}
6370
{...custom}
6471
>

src/components/menu/subMenu.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export function SubMenu(props: React.PropsWithChildren<ISubMenuProps>) {
4949
disabled = false,
5050
children,
5151
onClick,
52+
title,
5253
...custom
5354
} = props;
5455
const cNames = classNames(defaultSubMenuClassName, className);
@@ -81,6 +82,8 @@ export function SubMenu(props: React.PropsWithChildren<ISubMenuProps>) {
8182
disabled ? disabledClassName : null
8283
)}
8384
data-submenu
85+
// prevent render JSX title in HTMLElement
86+
{...(typeof title === 'string' ? { title } : {})}
8487
{...custom}
8588
>
8689
<a className={menuContentClassName}>

src/components/tooltip/index.tsx

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import React from 'react';
2+
import { default as RcTooltip } from 'rc-tooltip';
3+
import 'rc-tooltip/assets/bootstrap.css';
4+
import { TooltipProps } from 'rc-tooltip/lib/Tooltip';
5+
6+
export interface IToolTipProps extends TooltipProps {}
7+
8+
const Tooltip = ({
9+
overlay,
10+
children,
11+
placement = 'bottom',
12+
trigger = 'hover',
13+
...rest
14+
}: IToolTipProps) => {
15+
if (overlay) {
16+
return (
17+
<RcTooltip
18+
placement={placement}
19+
trigger={trigger}
20+
overlay={overlay}
21+
mouseEnterDelay={0.1}
22+
{...rest}
23+
>
24+
{children}
25+
</RcTooltip>
26+
);
27+
} else {
28+
return children || null;
29+
}
30+
};
31+
32+
export default Tooltip;

src/components/tooltip/style.scss

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
@import 'mo/style/common';
2+
3+
#{$rcTooltip} {
4+
box-sizing: border-box;
5+
color: var(--tooltip-color);
6+
margin: 0;
7+
opacity: 1;
8+
padding: 0;
9+
10+
&-inner {
11+
background-color: var(--tooltip-background);
12+
border-radius: 2px;
13+
box-shadow: 0 2px 8px rgb(0 0 0 / 15%);
14+
box-sizing: border-box;
15+
min-width: 30px;
16+
padding: 6px 8px;
17+
}
18+
}
19+
20+
#{$rcTooltip}-placement-bottom #{$rcTooltip}-arrow {
21+
border-bottom-color: var(--tooltip-background);
22+
top: -5px;
23+
}

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@
4444
"tab.activeForeground": "#fff",
4545
"diffEditor.insertedTextBackground": "#9bb95533",
4646
"diffEditor.removedTextBackground": "#ff000033",
47-
"actionBar.checkedBackground": "rgba(0, 127, 212, 0.4)"
47+
"actionBar.checkedBackground": "rgba(0, 127, 212, 0.4)",
48+
"tooltip.color": "#333",
49+
"tooltip.background": "rgba(0, 0, 0, 0.75)"
4850
},
4951
"semanticHighlighting": true
5052
}

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@
4242
"tab.inactiveBackground": "rgb(236, 236, 236)",
4343
"tab.inactiveForeground": "rgba(51, 51, 51, 0.7)",
4444
"tab.activeBackground": "#fffffe",
45-
"tab.activeForeground": "#333333"
45+
"tab.activeForeground": "#333333",
46+
"tooltip.color": "#333",
47+
"tooltip.background": "rgba(0, 0, 0, 0.75)"
4648
},
4749
"semanticHighlighting": true
4850
}

src/i18n/source/en.ts

+1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export default {
6262
'editor.closeSaved': 'Close Saved',
6363
'editor.closeOthers': 'Close Others',
6464
'editor.close': 'Close',
65+
'editor.actions.splitRight': 'Split Editor Right',
6566
'editor.showOpenEditors': 'Show Opened Editors',
6667
},
6768
};

src/i18n/source/zh-CN.json

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
"editor.closeOthers": "关闭其他",
6060
"editor.closeSaved": "关闭已保存",
6161
"editor.close": "关闭",
62+
"editor.actions.splitRight": "向右拆分编辑器",
6263
"editor.showOpenEditors": "展示已打开的编辑器"
6364
}
6465
}

src/model/workbench/editor.ts

+1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ export function getEditorInitialActions(): IEditorActionsProps[] {
8181
{
8282
id: EDITOR_MENU_SPILIT,
8383
name: 'Split Editor Right',
84+
title: localize('editor.actions.splitRight', 'Split Editor Right'),
8485
icon: 'split-horizontal',
8586
place: 'outer',
8687
},

src/style/common.scss

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ $modal: prefix('modal');
2828
$confirm: prefix('confirm');
2929
$breadcrumb: prefix('breadcrumb');
3030
$search: prefix('search');
31+
$rcTooltip: prefix('tooltip', 'rc');
3132

3233
// ============= The Workbench core component =============
3334
$compositeBar: prefix('compositeBar');

src/style/mo.scss

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
@import 'mo/components/toolbar/style';
2121
@import 'mo/components/tree/style';
2222
@import 'mo/components/search/style';
23+
@import 'mo/components/tooltip/style';
2324

2425
// ============= The Workbench core component =============
2526
@import 'mo/workbench/style';

src/workbench/editor/action.tsx

+21-18
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
} from './base';
1212
import { IEditorController } from 'mo/controller/editor';
1313
import { classNames } from 'mo/common/className';
14+
import Tooltip from 'mo/components/tooltip';
1415

1516
export interface IEditorActionProps extends IEditorAction {
1617
isActiveGroup: boolean;
@@ -79,25 +80,27 @@ function EditorAction(props: IEditorActionProps & IEditorController) {
7980
<div className={groupActionsClassName}>
8081
{isActiveGroup &&
8182
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-
typeof action.icon === 'string' ? (
93-
<Icon type={action.icon} />
83+
<Tooltip overlay={action.title}>
84+
<div
85+
key={action.id}
86+
onClick={() => handleActionsClick(action)}
87+
className={classNames(
88+
groupActionsItemClassName,
89+
action.disabled &&
90+
groupActionItemDisabledClassName
91+
)}
92+
>
93+
{action.icon ? (
94+
typeof action.icon === 'string' ? (
95+
<Icon type={action.icon} />
96+
) : (
97+
action.icon
98+
)
9499
) : (
95-
action.icon
96-
)
97-
) : (
98-
action.name
99-
)}
100-
</div>
100+
action.name
101+
)}
102+
</div>
103+
</Tooltip>
101104
))}
102105
{Boolean(inner.length) && (
103106
<DropDown

0 commit comments

Comments
 (0)