Skip to content

Commit 4c9b91c

Browse files
committed
feat: add basic statusBar
1 parent 9345916 commit 4c9b91c

File tree

7 files changed

+234
-40
lines changed

7 files changed

+234
-40
lines changed

src/extensions/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { ExtendActivityBar } from './activityBar';
22
import { ExtendExplore } from './explore';
33
import { ExtendSearch } from './search';
4+
import { ExtendStatusBar } from './statusBar';
5+
46
const Themes = require('./theme-defaults/package.json');
57

68
/**
@@ -10,5 +12,6 @@ export const defaultExtensions = [
1012
ExtendActivityBar,
1113
ExtendExplore,
1214
ExtendSearch,
15+
ExtendStatusBar,
1316
Themes,
1417
];

src/extensions/statusBar/index.tsx

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import * as React from 'react';
2+
import { IExtensionService, IStatusBarItem } from 'mo';
3+
import { IExtension } from 'mo/model/extension';
4+
import { statusBarService } from 'mo/services';
5+
import { Icon } from 'mo/components/icon';
6+
7+
function init() {
8+
const problems: IStatusBarItem = {
9+
id: 'MoProblems',
10+
sortIndex: 1,
11+
name: 'Problems',
12+
};
13+
14+
const notifications: IStatusBarItem = {
15+
id: 'MoNotification',
16+
sortIndex: 1,
17+
name: 'Notification',
18+
render: () => <Icon type="bell" />,
19+
};
20+
21+
statusBarService.appendLeftItem(problems);
22+
statusBarService.appendRightItem(notifications);
23+
24+
statusBarService.onClick(function (e, item) {
25+
console.log('statusBarService:', e, item);
26+
});
27+
}
28+
29+
export const ExtendStatusBar: IExtension = {
30+
activate(extensionCtx: IExtensionService) {
31+
init();
32+
},
33+
};

src/model/workbench/statusBar.ts

+31-14
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,45 @@
1+
import { EventBus } from 'mo/common/event';
12
import { observable } from 'mo/common/observable';
2-
import { container, inject, injectable } from 'tsyringe';
3+
import * as React from 'react';
4+
import { injectable } from 'tsyringe';
35

4-
export interface IStatusBarItem {}
6+
export interface IStatusBarItem extends HTMLElementProps {
7+
id: string;
8+
sortIndex: number;
9+
onClick?(e: React.MouseEvent, item?: IStatusBarItem);
10+
render?: () => ReactNode;
11+
name?: string;
12+
}
513

614
export interface IStatusBar {
7-
data: IStatusBarItem[];
8-
onClick: (event: React.MouseEvent<any, any>) => void;
9-
render?: () => React.ReactNode | JSX.Element;
15+
rightItems: IStatusBarItem[];
16+
leftItems: IStatusBarItem[];
17+
onClick?: (e: React.MouseEvent, item: IStatusBarItem) => void;
18+
}
19+
20+
/**
21+
* The activity bar event definition
22+
*/
23+
export enum StatusBarEvent {
24+
/**
25+
* Selected an activity bar
26+
*/
27+
onClick = 'statusBar.onClick',
28+
/**
29+
* Activity bar data changed
30+
*/
31+
DataChanged = 'statusBar.data',
1032
}
1133

1234
@observable()
1335
@injectable()
1436
export class StatusBarModel implements IStatusBar {
15-
public data: IStatusBarItem[] = [];
16-
17-
constructor(@inject('StatusBarData') data: IStatusBarItem[] = []) {
18-
this.data = data;
19-
}
37+
public leftItems: IStatusBarItem[] = [];
38+
public rightItems: IStatusBarItem[] = [];
2039

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

23-
public onClick = (event: React.MouseEvent) => {
24-
console.log('onClick:', event);
42+
public onClick = (e: React.MouseEvent, item: IStatusBarItem) => {
43+
EventBus.emit(StatusBarEvent.onClick, e, item);
2544
};
2645
}
27-
28-
container.register('StatusBarData', { useValue: [] });
+59-22
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,39 @@
11
import {
22
IStatusBar,
33
IStatusBarItem,
4+
StatusBarEvent,
45
StatusBarModel,
56
} from 'mo/model/workbench/statusBar';
67
import { Component } from 'mo/react';
7-
import { emit } from 'mo/common/event';
88
import { container, singleton } from 'tsyringe';
99

10-
/**
11-
* The activity bar event definition
12-
*/
13-
export enum StatusBarEvent {
10+
export interface IStatusBarService extends Component<IStatusBar> {
11+
appendLeftItem(item: IStatusBarItem): void;
12+
appendRightItem(item: IStatusBarItem): void;
13+
updateItem(item: IStatusBarItem): void;
14+
findById(id: string): IStatusBarItem | null;
15+
/**
16+
* Remove the left item of StatusBar,
17+
* return the removed item.
18+
* @param id
19+
*/
20+
removeLeftItem(id: string): IStatusBarItem;
1421
/**
15-
* Selected an activity bar
22+
* Remove the right item of StatusBar,
23+
* return the removed item.
24+
* @param id
1625
*/
17-
onClick = 'statusBar.onClick',
26+
removeRightItem(id: string): IStatusBarItem;
1827
/**
19-
* Activity bar data changed
28+
* Listen to the statusbar onclick event
29+
* @param callback
2030
*/
21-
DataChanged = 'statusBar.data',
31+
onClick(callback: (e: MouseEvent, item: IStatusBarItem) => void);
2232
}
2333

24-
export interface IStatusBarService extends Component<IStatusBar> {
25-
push(data: IStatusBarItem | IStatusBarItem[]): void;
26-
remove(index: number): void;
34+
function searchById(id: string) {
35+
return (item: IStatusBarItem) => item.id === id;
2736
}
28-
2937
@singleton()
3038
export class StatusBarService
3139
extends Component<IStatusBar>
@@ -37,17 +45,46 @@ export class StatusBarService
3745
this.state = container.resolve(StatusBarModel);
3846
}
3947

40-
@emit(StatusBarEvent.DataChanged)
41-
public push(data: IStatusBarItem | IStatusBarItem[]) {
42-
let original = this.state.data;
43-
if (Array.isArray(data)) {
44-
original = original.concat(data);
45-
} else {
46-
original.push(data);
48+
onClick(callback: (e: MouseEvent, item: IStatusBarItem) => void) {
49+
this.subscribe(StatusBarEvent.onClick, callback);
50+
}
51+
52+
private remove(id: string, arr: IStatusBarItem[]): IStatusBarItem {
53+
const index = arr.findIndex(searchById(id));
54+
const result = arr.splice(index, 1);
55+
return result[0];
56+
}
57+
58+
removeLeftItem(id: string): IStatusBarItem {
59+
return this.remove(id, this.state.leftItems);
60+
}
61+
62+
removeRightItem(id: string): IStatusBarItem {
63+
return this.remove(id, this.state.rightItems);
64+
}
65+
66+
findById(id: string): IStatusBarItem {
67+
let result;
68+
const { leftItems, rightItems } = this.state;
69+
result = leftItems.find(searchById(id));
70+
if (!result) {
71+
result = rightItems.find(searchById(id));
4772
}
73+
return result;
4874
}
4975

50-
public remove(index: number) {
51-
this.state.data.splice(index, 1);
76+
appendLeftItem(item: IStatusBarItem): void {
77+
this.state.leftItems.push(item);
78+
}
79+
80+
appendRightItem(item: IStatusBarItem): void {
81+
this.state.rightItems.push(item);
82+
}
83+
84+
updateItem(item: IStatusBarItem): void {
85+
const original = this.findById(item.id);
86+
if (original) {
87+
Object.assign(original, item);
88+
}
5289
}
5390
}

src/workbench/statusBar/item.tsx

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { classNames, getBEMElement } from 'mo/common/className';
2+
import { IStatusBarItem } from 'mo/model/workbench/statusBar';
3+
import * as React from 'react';
4+
import { memo } from 'react';
5+
import { statusBarClassName } from './statusBar';
6+
7+
function StatusItem(props: IStatusBarItem) {
8+
const itemClassName = getBEMElement(statusBarClassName, 'item');
9+
10+
const { className, onClick, name, render, ...extra } = props;
11+
12+
const clsName = classNames(itemClassName, className);
13+
const events = {
14+
onClick: function (e: React.MouseEvent) {
15+
onClick?.(e, props);
16+
},
17+
};
18+
19+
return (
20+
<div className={clsName} {...extra}>
21+
<a tabIndex={-1} title={name} {...events}>
22+
{render ? render() : name}
23+
</a>
24+
</div>
25+
);
26+
}
27+
28+
export default memo(StatusItem);

src/workbench/statusBar/statusBar.tsx

+31-4
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,40 @@ import 'mo/workbench/statusBar/style.scss';
33
import * as React from 'react';
44
import { memo } from 'react';
55

6-
import { prefixClaName } from 'mo/common/className';
7-
import { IStatusBar } from 'mo/model/workbench/statusBar';
6+
import { getBEMElement, prefixClaName } from 'mo/common/className';
7+
import { IStatusBar, IStatusBarItem } from 'mo/model/workbench/statusBar';
8+
import StatusItem from './item';
9+
import { mergeFunctions } from 'mo/common/utils';
810

9-
const defaultClassName = prefixClaName('statusBar');
11+
export const statusBarClassName = prefixClaName('statusBar');
12+
const leftItemsClassName = getBEMElement(statusBarClassName, 'left-items');
13+
const rightItemsClassName = getBEMElement(statusBarClassName, 'right-items');
14+
15+
function sortByIndex(a: IStatusBarItem, b: IStatusBarItem) {
16+
return a.sortIndex - b.sortIndex;
17+
}
1018

1119
function StatusBar(props: IStatusBar) {
12-
return <div className={defaultClassName}>StatusBar</div>;
20+
const { leftItems = [], onClick, rightItems = [] } = props;
21+
22+
const renderItems = (data: IStatusBarItem[]) => {
23+
return data
24+
.sort(sortByIndex)
25+
.map((item: IStatusBarItem) => (
26+
<StatusItem
27+
key={item.id}
28+
{...item}
29+
onClick={mergeFunctions(item.onClick, onClick)}
30+
/>
31+
));
32+
};
33+
34+
return (
35+
<div className={statusBarClassName}>
36+
<div className={leftItemsClassName}>{renderItems(leftItems)}</div>
37+
<div className={rightItemsClassName}>{renderItems(rightItems)}</div>
38+
</div>
39+
);
1340
}
1441

1542
export default memo(StatusBar);

src/workbench/statusBar/style.scss

+49
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,59 @@
11
@import 'mo/style/common';
22

33
#{$statusBar} {
4+
align-items: center;
45
bottom: 0;
6+
display: flex;
57
height: 22px;
8+
justify-content: center;
69
position: absolute;
710
text-align: center;
811
width: 100%;
912
z-index: 1;
13+
14+
&__left-items,
15+
&__right-items {
16+
display: flex;
17+
height: 100%;
18+
}
19+
20+
&__left-items {
21+
flex-grow: 1;
22+
23+
> :first-child {
24+
margin-left: 7px;
25+
}
26+
}
27+
28+
&__right-items {
29+
direction: rtl;
30+
31+
> :last-child {
32+
margin-right: 7px;
33+
}
34+
}
35+
36+
&__item {
37+
height: 100%;
38+
39+
a {
40+
align-items: center;
41+
cursor: pointer;
42+
display: flex;
43+
height: 100%;
44+
outline-width: 0;
45+
overflow: hidden;
46+
padding: 0 5px;
47+
text-overflow: ellipsis;
48+
white-space: pre;
49+
}
50+
51+
&:hover {
52+
background-color: rgba(255, 255, 255, 0.12);
53+
}
54+
55+
.codicon {
56+
font-size: 14px;
57+
}
58+
}
1059
}

0 commit comments

Comments
 (0)