Akos Kitta 5f5193932f ATL-374: Refactored the Output services.
Signed-off-by: Akos Kitta <kittaakos@typefox.io>
2020-10-12 16:28:07 +02:00

194 lines
6.7 KiB
TypeScript

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { CommandRegistry } from '@theia/core/lib/common/command';
import { DisposableCollection } from '@theia/core/lib/common/disposable';
import { Port } from '../../common/protocol';
import { BoardsConfig } from './boards-config';
import { ArduinoCommands } from '../arduino-commands';
import { BoardsServiceProvider, AvailableBoard } from './boards-service-provider';
export interface BoardsDropDownListCoords {
readonly top: number;
readonly left: number;
readonly width: number;
readonly paddingTop: number;
}
export namespace BoardsDropDown {
export interface Props {
readonly coords: BoardsDropDownListCoords | 'hidden';
readonly items: Array<AvailableBoard & { onClick: () => void, port: Port }>;
readonly openBoardsConfig: () => void;
}
}
export class BoardsDropDown extends React.Component<BoardsDropDown.Props> {
protected dropdownElement: HTMLElement;
constructor(props: BoardsDropDown.Props) {
super(props);
let list = document.getElementById('boards-dropdown-container');
if (!list) {
list = document.createElement('div');
list.id = 'boards-dropdown-container';
document.body.appendChild(list);
this.dropdownElement = list;
}
}
render(): React.ReactNode {
return ReactDOM.createPortal(this.renderNode(), this.dropdownElement);
}
protected renderNode(): React.ReactNode {
const { coords, items } = this.props;
if (coords === 'hidden') {
return '';
}
return <div className='arduino-boards-dropdown-list'
style={{
position: 'absolute',
...coords
}}>
{this.renderItem({
label: 'Select Other Board & Port',
onClick: () => this.props.openBoardsConfig()
})}
{items.map(({ name, port, selected, onClick }) => ({ label: `${name} at ${Port.toString(port)}`, selected, onClick })).map(this.renderItem)}
</div>
}
protected renderItem({ label, selected, onClick }: { label: string, selected?: boolean, onClick: () => void }): React.ReactNode {
return <div key={label} className={`arduino-boards-dropdown-item ${selected ? 'selected' : ''}`} onClick={onClick}>
<div>
{label}
</div>
{selected ? <span className='fa fa-check' /> : ''}
</div>
}
}
export class BoardsToolBarItem extends React.Component<BoardsToolBarItem.Props, BoardsToolBarItem.State> {
static TOOLBAR_ID: 'boards-toolbar';
protected readonly toDispose: DisposableCollection = new DisposableCollection();
constructor(props: BoardsToolBarItem.Props) {
super(props);
const { availableBoards } = props.boardsServiceClient;
this.state = {
availableBoards,
coords: 'hidden'
};
document.addEventListener('click', () => {
this.setState({ coords: 'hidden' });
});
}
componentDidMount() {
this.props.boardsServiceClient.onAvailableBoardsChanged(availableBoards => this.setState({ availableBoards }));
}
componentWillUnmount(): void {
this.toDispose.dispose();
}
protected readonly show = (event: React.MouseEvent<HTMLElement>) => {
const { currentTarget: element } = event;
if (element instanceof HTMLElement) {
if (this.state.coords === 'hidden') {
const rect = element.getBoundingClientRect();
this.setState({
coords: {
top: rect.top,
left: rect.left,
width: rect.width,
paddingTop: rect.height
}
});
} else {
this.setState({ coords: 'hidden' });
}
}
event.stopPropagation();
event.nativeEvent.stopImmediatePropagation();
};
render(): React.ReactNode {
const { coords, availableBoards } = this.state;
const boardsConfig = this.props.boardsServiceClient.boardsConfig;
const title = BoardsConfig.Config.toString(boardsConfig, { default: 'no board selected' });
const decorator = (() => {
const selectedBoard = availableBoards.find(({ selected }) => selected);
if (!selectedBoard || !selectedBoard.port) {
return 'fa fa-times notAttached'
}
if (selectedBoard.state === AvailableBoard.State.guessed) {
return 'fa fa-exclamation-triangle guessed'
}
return ''
})();
return <React.Fragment>
<div className='arduino-boards-toolbar-item-container'>
<div className='arduino-boards-toolbar-item' title={title}>
<div className='inner-container' onClick={this.show}>
<span className={decorator} />
<div className='label noWrapInfo'>
<div className='noWrapInfo noselect'>
{title}
</div>
</div>
<span className='fa fa-caret-down caret' />
</div>
</div>
</div>
<BoardsDropDown
coords={coords}
items={availableBoards.filter(AvailableBoard.hasPort).map(board => ({
...board,
onClick: () => {
if (board.state === AvailableBoard.State.incomplete) {
this.props.boardsServiceClient.boardsConfig = {
selectedPort: board.port
};
this.openDialog();
} else {
this.props.boardsServiceClient.boardsConfig = {
selectedBoard: board,
selectedPort: board.port
}
}
}
}))}
openBoardsConfig={this.openDialog}>
</BoardsDropDown>
</React.Fragment>;
}
protected openDialog = () => {
this.props.commands.executeCommand(ArduinoCommands.OPEN_BOARDS_DIALOG.id);
this.setState({ coords: 'hidden' });
};
}
export namespace BoardsToolBarItem {
export interface Props {
readonly boardsServiceClient: BoardsServiceProvider;
readonly commands: CommandRegistry;
}
export interface State {
availableBoards: AvailableBoard[];
coords: BoardsDropDownListCoords | 'hidden';
}
}