generalized the boards and the libraries views.

Signed-off-by: Akos Kitta <kittaakos@typefox.io>
This commit is contained in:
Akos Kitta
2019-07-30 16:45:04 +02:00
committed by jbicker
parent b24d440e22
commit 3fcf5a6ac9
24 changed files with 303 additions and 443 deletions

View File

@@ -1,50 +0,0 @@
import * as React from 'react';
import { ComponentListItem } from '../components/component-list/component-list-item';
export class LibraryComponentListItem extends ComponentListItem {
render(): React.ReactNode {
const { item } = this.props;
const name = <span className={'name'}>{item.name}</span>;
const author = <span className={'author'}>by {item.author}</span>;
const installedVersion = !!item.installedVersion && <div className={'version-info'}>
<span className={'version'}>Version {item.installedVersion}</span>
<span className={'installed'}>INSTALLED</span>
</div>;
const summary = <div className={'summary'}>{item.summary}</div>;
const moreInfo = !!item.moreInfoLink && <a href={item.moreInfoLink} onClick={this.onClick}>More info</a>;
const install = this.props.install && item.installable && !item.installedVersion &&
<button className={'install'} onClick={this.install.bind(this, item)}>INSTALL</button>;
const versions = (() => {
const { availableVersions } = item;
if (availableVersions.length === 0) {
return undefined;
} else if (availableVersions.length === 1) {
return <label>{availableVersions[0]}</label>
} else {
return <select>{item.availableVersions.map(version => <option value={version} key={version}>{version}</option>)}</select>;
}
})();
return <div className={'component-list-item noselect'}>
<div className={'header'}>
<span>{name} {author}</span>
{installedVersion}
</div>
<div className={'content'}>
{summary}
</div>
<div className={'info'}>
{moreInfo}
</div>
<div className={'footer'}>
{install}
{versions}
</div>
</div>;
}
}

View File

@@ -1,17 +0,0 @@
import * as React from 'react';
import { ArduinoComponent } from '../../common/protocol/arduino-component';
import { ComponentList } from '../components/component-list/component-list';
import { LibraryComponentListItem } from './library-component-list-item';
export class LibraryComponentList extends ComponentList {
createItem(item: ArduinoComponent): React.ReactNode {
return <LibraryComponentListItem
key={item.name}
item={item}
windowService={this.props.windowService}
install={this.props.install}
/>
}
}

View File

@@ -1,110 +0,0 @@
import * as React from 'react';
import { FilterableListContainer } from '../components/component-list/filterable-list-container';
import { LibraryComponentList } from './library-component-list';
export class LibraryFilterableListContainer extends FilterableListContainer {
constructor(props: Readonly<FilterableListContainer.Props>) {
super(props);
this.state = {
filterText: '',
items: [],
props: {
topic: this.topics[0],
type: this.types[0]
}
};
}
protected renderSearchFilter(): React.ReactNode {
const types = this.types.map(type => <option value={type} key={type}>{type}</option>);
let type = this.types[0];
if (this.state.props) {
const currentType = this.types.find(t => t === this.state.props!.type) || this.types[0];
if (currentType) {
type = currentType;
}
}
const topics = this.topics.map(topic => <option value={topic} key={topic}>{topic}</option>);
let topic = this.topics[0];
if (this.state.props) {
const currentTopic = this.topics.find(t => t === this.state.props!.topic) || this.topics[0];
if (currentTopic) {
topic = currentTopic;
}
}
return <div className={'search-filters'}>
<div className={'filter'}>
<div className={'title'} style={{ minWidth: '32.088px' }}>Type</div> {/** TODO: do `minWidth` better! */}
<select
value={type}
onChange={this.onTypeChange}>
{types}
</select>
</div>
<div className={'filter'}>
<div className={'title'}>Topic</div>
<select
value={topic}
onChange={this.onTopicChange}>
{topics}
</select>
</div>
</div>
}
protected onTypeChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
const type = event.target.value;
const props = { ...(this.state.props || {}), ...{ type } };
this.setState({
props
});
}
protected onTopicChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
const topic = event.target.value;
const props = { ...(this.state.props || {}), ...{ topic } };
this.setState({
props
});
}
protected renderComponentList(): React.ReactNode {
return <LibraryComponentList
items={this.state.items}
install={this.install.bind(this)}
windowService={this.props.windowService}
resolveContainer={this.props.resolveContainer}
/>
}
private get topics(): string[] {
return [
'All',
'Communication',
'Data Processing',
'Data Storage',
'Device Control',
'Display',
'Other',
'Sensor',
'Signal Input/Output',
'Timing',
'Uncategorized'
];
}
private get types(): string[] {
return [
'All',
'Updatable',
'Installed',
'Arduino',
'Partner',
'Recommended',
'Contributed',
'Retired'
];
}
}

View File

@@ -0,0 +1,52 @@
import * as React from 'react';
import { injectable } from 'inversify';
import { Library } from '../../common/protocol/library-service';
import { ListItemRenderer } from '../components/component-list/list-item-renderer';
@injectable()
export class LibraryItemRenderer extends ListItemRenderer<Library> {
renderItem(item: Library, install: (item: Library) => Promise<void>): React.ReactNode {
const name = <span className='name'>{item.name}</span>;
const author = <span className='author'>by {item.author}</span>;
const installedVersion = !!item.installedVersion && <div className='version-info'>
<span className='version'>Version {item.installedVersion}</span>
<span className='installed'>INSTALLED</span>
</div>;
const summary = <div className='summary'>{item.summary}</div>;
const moreInfo = !!item.moreInfoLink && <a href={item.moreInfoLink} onClick={this.onClick}>More info</a>;
const installButton = item.installable && !item.installedVersion &&
<button className='install' onClick={install.bind(this, item)}>INSTALL</button>;
const versions = (() => {
const { availableVersions } = item;
if (availableVersions.length === 0) {
return undefined;
} else if (availableVersions.length === 1) {
return <label>{availableVersions[0]}</label>
} else {
return <select>{item.availableVersions.map(version => <option value={version} key={version}>{version}</option>)}</select>;
}
})();
return <div className='component-list-item noselect'>
<div className='header'>
<span>{name} {author}</span>
{installedVersion}
</div>
<div className='content'>
{summary}
</div>
<div className='info'>
{moreInfo}
</div>
<div className='footer'>
{installButton}
{versions}
</div>
</div>;
}
}

View File

@@ -0,0 +1,27 @@
import { inject, injectable } from 'inversify';
import { Library, LibraryService } from '../../common/protocol/library-service';
import { ListWidget } from '../components/component-list/list-widget';
import { LibraryItemRenderer } from './library-item-renderer';
@injectable()
export class LibraryListWidget extends ListWidget<Library> {
static WIDGET_ID = 'library-list-widget';
static WIDGET_LABEL = 'Library Manager';
constructor(
@inject(LibraryService) protected service: LibraryService,
@inject(LibraryItemRenderer) protected itemRenderer: LibraryItemRenderer) {
super({
id: LibraryListWidget.WIDGET_ID,
label: LibraryListWidget.WIDGET_LABEL,
iconClass: 'library-tab-icon',
searchable: service,
installable: service,
itemLabel: (item: Library) => item.name,
itemRenderer
});
}
}

View File

@@ -1,88 +0,0 @@
import * as React from 'react';
import { inject, injectable, postConstruct } from 'inversify';
import { Message } from '@phosphor/messaging';
import { Deferred } from '@theia/core/lib/common/promise-util';
import { MaybePromise } from '@theia/core/lib/common/types';
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
import { WindowService } from '@theia/core/lib/browser/window/window-service';
import { LibraryFilterableListContainer } from './library-filterable-list-container';
import { LibraryService } from '../../common/protocol/library-service';
@injectable()
export class LibraryListWidget extends ReactWidget {
static WIDGET_ID = 'library-list-widget';
static WIDGET_LABEL = 'Library Manager';
@inject(LibraryService)
protected readonly libraryService: LibraryService;
@inject(WindowService)
protected readonly windowService: WindowService;
/**
* Do not touch or use it. It is for setting the focus on the `input` after the widget activation.
*/
protected focusNode: HTMLElement | undefined;
protected readonly deferredContainer = new Deferred<HTMLElement>();
constructor() {
super();
this.id = LibraryListWidget.WIDGET_ID
this.title.label = LibraryListWidget.WIDGET_LABEL;
this.title.caption = LibraryListWidget.WIDGET_LABEL
this.title.iconClass = 'library-tab-icon';
this.title.closable = true;
this.addClass('arduino-list-widget');
this.node.tabIndex = 0; // To be able to set the focus on the widget.
this.scrollOptions = {
suppressScrollX: true
}
}
@postConstruct()
protected init(): void {
this.update();
}
protected getScrollContainer(): MaybePromise<HTMLElement> {
return this.deferredContainer.promise;
}
protected onActivateRequest(msg: Message): void {
super.onActivateRequest(msg);
(this.focusNode || this.node).focus();
}
protected onUpdateRequest(msg: Message): void {
super.onUpdateRequest(msg);
this.render();
}
protected onFocusResolved = (element: HTMLElement | undefined) => {
this.focusNode = element;
}
render(): React.ReactNode {
return <LibraryFilterableListContainer
resolveContainer={this.deferredContainer.resolve}
resolveFocus={this.onFocusResolved}
service={this.libraryService}
windowService={this.windowService}
/>;
}
}
export namespace ListWidget {
/**
* Props for customizing the abstract list widget.
*/
export interface Props {
readonly id: string;
readonly title: string;
readonly iconClass: string;
}
}