Skip to content

Commit

Permalink
feat: add basic statusBar
Browse files Browse the repository at this point in the history
  • Loading branch information
wewoor committed Dec 11, 2020
1 parent 9345916 commit 4c9b91c
Show file tree
Hide file tree
Showing 7 changed files with 234 additions and 40 deletions.
3 changes: 3 additions & 0 deletions src/extensions/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { ExtendActivityBar } from './activityBar';
import { ExtendExplore } from './explore';
import { ExtendSearch } from './search';
import { ExtendStatusBar } from './statusBar';

const Themes = require('./theme-defaults/package.json');

/**
Expand All @@ -10,5 +12,6 @@ export const defaultExtensions = [
ExtendActivityBar,
ExtendExplore,
ExtendSearch,
ExtendStatusBar,
Themes,
];
33 changes: 33 additions & 0 deletions src/extensions/statusBar/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import * as React from 'react';
import { IExtensionService, IStatusBarItem } from 'mo';
import { IExtension } from 'mo/model/extension';
import { statusBarService } from 'mo/services';
import { Icon } from 'mo/components/icon';

function init() {
const problems: IStatusBarItem = {
id: 'MoProblems',
sortIndex: 1,
name: 'Problems',
};

const notifications: IStatusBarItem = {
id: 'MoNotification',
sortIndex: 1,
name: 'Notification',
render: () => <Icon type="bell" />,
};

statusBarService.appendLeftItem(problems);
statusBarService.appendRightItem(notifications);

statusBarService.onClick(function (e, item) {
console.log('statusBarService:', e, item);
});
}

export const ExtendStatusBar: IExtension = {
activate(extensionCtx: IExtensionService) {
init();
},
};
45 changes: 31 additions & 14 deletions src/model/workbench/statusBar.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,45 @@
import { EventBus } from 'mo/common/event';
import { observable } from 'mo/common/observable';
import { container, inject, injectable } from 'tsyringe';
import * as React from 'react';
import { injectable } from 'tsyringe';

export interface IStatusBarItem {}
export interface IStatusBarItem extends HTMLElementProps {
id: string;
sortIndex: number;
onClick?(e: React.MouseEvent, item?: IStatusBarItem);
render?: () => ReactNode;
name?: string;
}

export interface IStatusBar {
data: IStatusBarItem[];
onClick: (event: React.MouseEvent<any, any>) => void;
render?: () => React.ReactNode | JSX.Element;
rightItems: IStatusBarItem[];
leftItems: IStatusBarItem[];
onClick?: (e: React.MouseEvent, item: IStatusBarItem) => void;
}

/**
* The activity bar event definition
*/
export enum StatusBarEvent {
/**
* Selected an activity bar
*/
onClick = 'statusBar.onClick',
/**
* Activity bar data changed
*/
DataChanged = 'statusBar.data',
}

@observable()
@injectable()
export class StatusBarModel implements IStatusBar {
public data: IStatusBarItem[] = [];

constructor(@inject('StatusBarData') data: IStatusBarItem[] = []) {
this.data = data;
}
public leftItems: IStatusBarItem[] = [];
public rightItems: IStatusBarItem[] = [];

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

public onClick = (event: React.MouseEvent) => {
console.log('onClick:', event);
public onClick = (e: React.MouseEvent, item: IStatusBarItem) => {
EventBus.emit(StatusBarEvent.onClick, e, item);
};
}

container.register('StatusBarData', { useValue: [] });
81 changes: 59 additions & 22 deletions src/services/workbench/statusBarService.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,39 @@
import {
IStatusBar,
IStatusBarItem,
StatusBarEvent,
StatusBarModel,
} from 'mo/model/workbench/statusBar';
import { Component } from 'mo/react';
import { emit } from 'mo/common/event';
import { container, singleton } from 'tsyringe';

/**
* The activity bar event definition
*/
export enum StatusBarEvent {
export interface IStatusBarService extends Component<IStatusBar> {
appendLeftItem(item: IStatusBarItem): void;
appendRightItem(item: IStatusBarItem): void;
updateItem(item: IStatusBarItem): void;
findById(id: string): IStatusBarItem | null;
/**
* Remove the left item of StatusBar,
* return the removed item.
* @param id
*/
removeLeftItem(id: string): IStatusBarItem;
/**
* Selected an activity bar
* Remove the right item of StatusBar,
* return the removed item.
* @param id
*/
onClick = 'statusBar.onClick',
removeRightItem(id: string): IStatusBarItem;
/**
* Activity bar data changed
* Listen to the statusbar onclick event
* @param callback
*/
DataChanged = 'statusBar.data',
onClick(callback: (e: MouseEvent, item: IStatusBarItem) => void);
}

export interface IStatusBarService extends Component<IStatusBar> {
push(data: IStatusBarItem | IStatusBarItem[]): void;
remove(index: number): void;
function searchById(id: string) {
return (item: IStatusBarItem) => item.id === id;
}

@singleton()
export class StatusBarService
extends Component<IStatusBar>
Expand All @@ -37,17 +45,46 @@ export class StatusBarService
this.state = container.resolve(StatusBarModel);
}

@emit(StatusBarEvent.DataChanged)
public push(data: IStatusBarItem | IStatusBarItem[]) {
let original = this.state.data;
if (Array.isArray(data)) {
original = original.concat(data);
} else {
original.push(data);
onClick(callback: (e: MouseEvent, item: IStatusBarItem) => void) {
this.subscribe(StatusBarEvent.onClick, callback);
}

private remove(id: string, arr: IStatusBarItem[]): IStatusBarItem {
const index = arr.findIndex(searchById(id));
const result = arr.splice(index, 1);
return result[0];
}

removeLeftItem(id: string): IStatusBarItem {
return this.remove(id, this.state.leftItems);
}

removeRightItem(id: string): IStatusBarItem {
return this.remove(id, this.state.rightItems);
}

findById(id: string): IStatusBarItem {
let result;
const { leftItems, rightItems } = this.state;
result = leftItems.find(searchById(id));
if (!result) {
result = rightItems.find(searchById(id));
}
return result;
}

public remove(index: number) {
this.state.data.splice(index, 1);
appendLeftItem(item: IStatusBarItem): void {
this.state.leftItems.push(item);
}

appendRightItem(item: IStatusBarItem): void {
this.state.rightItems.push(item);
}

updateItem(item: IStatusBarItem): void {
const original = this.findById(item.id);
if (original) {
Object.assign(original, item);
}
}
}
28 changes: 28 additions & 0 deletions src/workbench/statusBar/item.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { classNames, getBEMElement } from 'mo/common/className';
import { IStatusBarItem } from 'mo/model/workbench/statusBar';
import * as React from 'react';
import { memo } from 'react';
import { statusBarClassName } from './statusBar';

function StatusItem(props: IStatusBarItem) {
const itemClassName = getBEMElement(statusBarClassName, 'item');

const { className, onClick, name, render, ...extra } = props;

const clsName = classNames(itemClassName, className);
const events = {
onClick: function (e: React.MouseEvent) {
onClick?.(e, props);
},
};

return (
<div className={clsName} {...extra}>
<a tabIndex={-1} title={name} {...events}>
{render ? render() : name}
</a>
</div>
);
}

export default memo(StatusItem);
35 changes: 31 additions & 4 deletions src/workbench/statusBar/statusBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,40 @@ import 'mo/workbench/statusBar/style.scss';
import * as React from 'react';
import { memo } from 'react';

import { prefixClaName } from 'mo/common/className';
import { IStatusBar } from 'mo/model/workbench/statusBar';
import { getBEMElement, prefixClaName } from 'mo/common/className';
import { IStatusBar, IStatusBarItem } from 'mo/model/workbench/statusBar';
import StatusItem from './item';
import { mergeFunctions } from 'mo/common/utils';

const defaultClassName = prefixClaName('statusBar');
export const statusBarClassName = prefixClaName('statusBar');
const leftItemsClassName = getBEMElement(statusBarClassName, 'left-items');
const rightItemsClassName = getBEMElement(statusBarClassName, 'right-items');

function sortByIndex(a: IStatusBarItem, b: IStatusBarItem) {
return a.sortIndex - b.sortIndex;
}

function StatusBar(props: IStatusBar) {
return <div className={defaultClassName}>StatusBar</div>;
const { leftItems = [], onClick, rightItems = [] } = props;

const renderItems = (data: IStatusBarItem[]) => {
return data
.sort(sortByIndex)
.map((item: IStatusBarItem) => (
<StatusItem
key={item.id}
{...item}
onClick={mergeFunctions(item.onClick, onClick)}
/>
));
};

return (
<div className={statusBarClassName}>
<div className={leftItemsClassName}>{renderItems(leftItems)}</div>
<div className={rightItemsClassName}>{renderItems(rightItems)}</div>
</div>
);
}

export default memo(StatusBar);
49 changes: 49 additions & 0 deletions src/workbench/statusBar/style.scss
Original file line number Diff line number Diff line change
@@ -1,10 +1,59 @@
@import 'mo/style/common';

#{$statusBar} {
align-items: center;
bottom: 0;
display: flex;
height: 22px;
justify-content: center;
position: absolute;
text-align: center;
width: 100%;
z-index: 1;

&__left-items,
&__right-items {
display: flex;
height: 100%;
}

&__left-items {
flex-grow: 1;

> :first-child {
margin-left: 7px;
}
}

&__right-items {
direction: rtl;

> :last-child {
margin-right: 7px;
}
}

&__item {
height: 100%;

a {
align-items: center;
cursor: pointer;
display: flex;
height: 100%;
outline-width: 0;
overflow: hidden;
padding: 0 5px;
text-overflow: ellipsis;
white-space: pre;
}

&:hover {
background-color: rgba(255, 255, 255, 0.12);
}

.codicon {
font-size: 14px;
}
}
}

0 comments on commit 4c9b91c

Please sign in to comment.